Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.8', '3.9', '3.10']
python-version: ['3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v2
Expand Down
1 change: 1 addition & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ max-attributes=9

# we're going to support as many arguments as the API calls have
disable=too-many-arguments,
too-many-positional-arguments,
# unfortunately this has to be disabled because of similar
# import statements across different methods (which are
# separated by different files by design)
Expand Down
1,559 changes: 762 additions & 797 deletions poetry.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,20 @@ packages = [
]

[tool.poetry.dependencies]
python = "^3.8"
python = "^3.10"
requests = "^2.20"
semantic-version = "^2.8.5"
pandas = {version = "^1.3.4", optional = true}
pandas = {version = "^2.0.0", optional = true}

[tool.poetry.extras]
data_science = ["pandas"]

[tool.poetry.group.dev.dependencies]
pytest = "^6.2.5"
pytest = "^7.0.0"
pytest-cov = "^3.0.0"
pytest-black = "^0.3.12"
pytest-mypy = "^0.10.3"
pytest-pylint = "^0.18.0"
pytest-pylint = "^0.20.0"
responses = "^0.14.0"
pytest-mock = "^3.6.1"
types-requests = "^2.26.1"
Expand Down
1 change: 1 addition & 0 deletions redcap/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
I don't think this is the ideal workflow, but it's the best I
could come up with for having great, tested, examples
"""

from pathlib import Path

import pytest
Expand Down
3 changes: 1 addition & 2 deletions redcap/methods/arms.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project arms"""

from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, cast

from redcap.methods.base import Base, Json
Expand All @@ -15,7 +16,6 @@ def export_arms(
format_type: Literal["json", "csv", "xml", "df"] = "json",
arms: Optional[List[str]] = None,
):
# pylint: disable=line-too-long
"""
Export the Arms of the Project

Expand All @@ -36,7 +36,6 @@ def export_arms(
>>> proj.export_arms()
[{'arm_num': 1, 'name': 'Arm 1'}]
"""
# pylint:enable=line-too-long
payload = self._initialize_payload(content="arm", format_type=format_type)
if arms:
# Turn list of arms into dict, and append to payload
Expand Down
7 changes: 3 additions & 4 deletions redcap/methods/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""The Base class for all REDCap methods"""

from __future__ import annotations

import json
Expand Down Expand Up @@ -227,12 +228,10 @@ def _filter_metadata(
self,
key: str,
field_name: None = None,
) -> list:
...
) -> list: ...

@overload
def _filter_metadata(self, key: str, field_name: str) -> str:
...
def _filter_metadata(self, key: str, field_name: str) -> str: ...

def _filter_metadata(self, key: str, field_name: Optional[str] = None):
"""Safely filter project metadata based off requested column and field_name"""
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/data_access_groups.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project data access groups"""

from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, cast

from redcap.methods.base import Base, Json
Expand Down
3 changes: 1 addition & 2 deletions redcap/methods/events.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project events"""

from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, cast

from redcap.methods.base import Base, Json
Expand All @@ -15,7 +16,6 @@ def export_events(
format_type: Literal["json", "csv", "xml", "df"] = "json",
arms: Optional[List[str]] = None,
):
# pylint: disable=line-too-long
"""
Export the Events of the Project

Expand All @@ -37,7 +37,6 @@ def export_events(
[{'event_name': 'Event 1', 'arm_num': 1, 'unique_event_name': 'event_1_arm_1',
'custom_event_label': '', 'event_id': ...}, {'event_name': 'Event 2', ...}]
"""
# pylint:enable=line-too-long
payload = self._initialize_payload(content="event", format_type=format_type)
if arms:
# Turn list of arms into dict, and append to payload
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/field_names.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project field names"""

from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union, cast

from redcap.methods.base import Base, Json
Expand Down
4 changes: 0 additions & 4 deletions redcap/methods/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ class Files(Base):

def _check_file_field(self, field: str) -> None:
"""Check that field exists and is a file field"""
# Since we initialize self.field_names as None, pylint worries that this will
# produce an error
# pylint: disable=unsupported-membership-test
is_field = field in self.field_names
# pylint: enable=unsupported-membership-test
is_file = self._filter_metadata(key="field_type", field_name=field) == "file"
if not (is_field and is_file):
msg = f"'{ field }' is not a field or not a 'file' field"
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/instruments.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project instruments"""

from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, cast

from redcap.methods.base import Base, FileMap
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/logging.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project field names"""

from datetime import datetime
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union, cast

Expand Down
1 change: 1 addition & 0 deletions redcap/methods/metadata.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project metadata"""

from typing import (
TYPE_CHECKING,
Any,
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/project_info.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project info"""

from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union, cast

from redcap.methods.base import Base, Json
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/records.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project records"""

from datetime import datetime

from typing import (
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/repeating.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project repeating instruments"""

from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, Literal, cast

from redcap.methods.base import Base
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/reports.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project reports"""

from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union, cast

from redcap.methods.base import Base, Json
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/surveys.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project surveys"""

from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union, cast

from redcap.methods.base import Base, Json
Expand Down
11 changes: 6 additions & 5 deletions redcap/methods/user_roles.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project user roles"""

from typing import (
TYPE_CHECKING,
Any,
Expand Down Expand Up @@ -43,11 +44,11 @@ def export_user_roles(
[{'unique_role_name': ..., 'role_label': 'Test role', 'design': '0', 'alerts': '0',
'user_rights': '0', 'data_access_groups': '0', 'reports': '0', 'stats_and_charts': '0',
'manage_survey_participants': '0', 'calendar': '0', 'data_import_tool': '0',
'data_comparison_tool': '0', 'logging': '0', 'file_repository': '0',
'data_quality_create': '0', 'data_quality_execute': '0', 'api_export': '0',
'api_import': '0', 'mobile_app': '0', 'mobile_app_download_data': '0',
'record_create': '0', 'record_rename': '0', 'record_delete': '0',
'lock_records_customization': '0', 'lock_records': '0', ...,
'data_comparison_tool': '0', 'logging': '0', 'email_logging': '0',
'file_repository': '0', 'data_quality_create': '0', 'data_quality_execute': '0',
'api_export': '0', 'api_import': '0', 'api_modules': '0', 'mobile_app': '0',
'mobile_app_download_data': '0', 'record_create': '0', 'record_rename': '0',
'record_delete': '0', 'lock_records_customization': '0', 'lock_records': '0', ...,
'forms': {'form_1': 2}, 'forms_export': {'form_1': 0}}]
"""
payload = self._initialize_payload(content="userRole", format_type=format_type)
Expand Down
1 change: 1 addition & 0 deletions redcap/methods/users.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""REDCap API methods for Project users"""

from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, cast

from redcap.methods.base import Base, Json
Expand Down
13 changes: 6 additions & 7 deletions redcap/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def __init__(

@staticmethod
def _get_format_key(
payload: Dict[str, Any]
payload: Dict[str, Any],
) -> Optional[Literal["json", "csv", "xml"]]:
"""Determine format of the response

Expand Down Expand Up @@ -101,8 +101,7 @@ def get_content(
format_type: None,
return_empty_json: Literal[True],
return_bytes: Literal[False],
) -> EmptyJson:
...
) -> EmptyJson: ...

@overload
@staticmethod
Expand All @@ -111,8 +110,7 @@ def get_content(
format_type: None,
return_empty_json: Literal[False],
return_bytes: Literal[True],
) -> bytes:
...
) -> bytes: ...

@overload
@staticmethod
Expand All @@ -132,8 +130,7 @@ def get_content(
format_type: Literal["csv", "xml"],
return_empty_json: Literal[False],
return_bytes: Literal[False],
) -> str:
...
) -> str: ...

@staticmethod
def get_content(
Expand Down Expand Up @@ -204,6 +201,8 @@ def execute(
# xml is the default returnFormat for error messages
elif self.fmt == "xml" or self.fmt is None:
bad_request = "<error>" in str(content).lower()
else:
raise ValueError(f"Unsupported format { self.fmt }")

if bad_request:
raise RedcapError(content)
Expand Down
1 change: 1 addition & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Test fixtures for integration tests only"""

# pylint: disable=redefined-outer-name
from datetime import datetime
import os
Expand Down
1 change: 1 addition & 0 deletions tests/integration/test_long_project.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Test suite for longitudinal REDCap Project against real REDCap server"""

# pylint: disable=missing-function-docstring
import os

Expand Down
1 change: 1 addition & 0 deletions tests/integration/test_simple_project.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Test suite for simple REDCap Project against real REDCap server"""

# pylint: disable=missing-function-docstring
import os
from io import StringIO
Expand Down
1 change: 1 addition & 0 deletions tests/unit/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Test fixtures for unit tests only"""

from typing import Dict, Generator

import pytest
Expand Down