Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support userActivationSettings parameter in RPC method activate_survey #1026

Merged
merged 2 commits into from
Nov 15, 2023
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 .changes/unreleased/Added-20231028-222932.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
kind: Added
body: Support `DestSurveyID` parameter to RPC method `copy_survey`
body: Support `DestSurveyID` parameter in RPC method `copy_survey`
time: 2023-10-28T22:29:32.491728-06:00
custom:
Issue: "1016"
5 changes: 5 additions & 0 deletions .changes/unreleased/Added-20231109-165350.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: Added
body: Support `userActivationSettings` parameter in RPC method `activate_survey`
time: 2023-11-09T16:53:50.378464-06:00
custom:
Issue: "1026"
5 changes: 5 additions & 0 deletions .changes/unreleased/Fixed-20231109-165446.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: Fixed
body: Update `types.YesNo` to include inherited (`I`) values
time: 2023-11-09T16:54:46.179939-06:00
custom:
Issue: "1026"
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
select = DAR
select = DOC
docstring_style=google
max-line-length = 88
strictness = short
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ repos:
hooks:
- id: flake8
additional_dependencies:
- darglint==1.8.1
- pydoclint==0.3.8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.6.1
rev: v1.7.0
hooks:
- id: mypy
additional_dependencies:
Expand All @@ -66,7 +66,7 @@ repos:
- id: validate_manifest

- repo: https://github.com/hadialqattan/pycln
rev: v2.3.0
rev: v2.4.0
hooks:
- id: pycln
args: [--all]
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ Python.
<!-- start integration status -->
| | **PostgreSQL** | **MySQL** |
| - |:--: | :-: |
| 6.3.4 | ✅ | ✅ |
| 6.3.3 | ✅ | ✅ |
| 6.3.1 | ✅ | ✅ |
| 6.3.0 | ✅ | ✅ |
| 6.2.11 | ✅ | ✅ |
| 5.6.44 | ✅ | ✅ |
| 5.6.43 | ✅ | ✅ |
| 5.6.42 | ✅ | ✅ |
| 5.6.41 | ✅ | ✅ |
| 5.6.40 | ✅ | ✅ |
<!-- end integration status -->

## Installation
Expand Down
15 changes: 7 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@ target-version = "py38"
[tool.ruff.lint]
explicit-preview-rules = false
ignore = [
"ANN101", # missing-type-self
"DJ", # flake8-django
"FIX002", # line-contains-todo
"ANN101", # missing-type-self
"DJ", # flake8-django
"FIX002", # line-contains-todo
"COM812", # missing-trailing-comma
"ISC001", # single-line-implicit-string-concatenation
"D107", # undocumented-public-init
]
preview = true
select = [
Expand Down
19 changes: 16 additions & 3 deletions src/citric/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ def __init__(
requests_session: requests.Session | None = None,
auth_plugin: str = "Authdb",
) -> None:
"""Create a LimeSurvey Python API client."""
self.__session = self.session_class(
url,
username,
Expand Down Expand Up @@ -169,20 +168,34 @@ def get_fieldmap(self, survey_id: int) -> dict[str, t.Any]:
"""
return self.session.get_fieldmap(survey_id)

def activate_survey(self, survey_id: int) -> types.OperationStatus:
def activate_survey(
self,
survey_id: int,
*,
user_activation_settings: types.SurveyUserActivationSettings | None = None,
) -> types.OperationStatus:
"""Activate a survey.

Calls :rpc_method:`activate_survey`.

Args:
survey_id: ID of survey to be activated.
user_activation_settings: Optional user activation settings.

Returns:
Status and plugin feedback.

.. versionadded:: 0.0.1
"""
return self.session.activate_survey(survey_id)
activation_settings = (
{
key: "Y" if value else "N"
for key, value in user_activation_settings.items()
}
if user_activation_settings
else None
)
return self.session.activate_survey(survey_id, activation_settings)

def activate_tokens(
self,
Expand Down
13 changes: 5 additions & 8 deletions src/citric/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ class ResponseMismatchError(Exception):


class LimeSurveyError(Exception):
"""Basic exception raised by LimeSurvey."""
"""Basic exception raised by LimeSurvey.

def __init__(self, message: str) -> None:
"""Create a generic error for the LimeSurvey RPC API.
Args:
message: Exception message. By default none, and a generic message is used.
"""

Args:
message: Exception message. By default none, and a generic message is used.
"""
def __init__(self, message: str) -> None:
super().__init__(message)


Expand All @@ -31,15 +30,13 @@ class RPCInterfaceNotEnabledError(LimeSurveyError):
"""RPC interface not enabled on LimeSurvey."""

def __init__(self) -> None:
"""Create a new exception."""
super().__init__("RPC interface not enabled")


class InvalidJSONResponseError(LimeSurveyError):
"""RPC interface maybe not enabled on LimeSurvey."""

def __init__(self) -> None:
"""Create a new exception."""
msg = (
"Received a non-JSON response, verify that the JSON RPC interface is "
"enabled in global settings"
Expand Down
8 changes: 6 additions & 2 deletions src/citric/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@


class Method(t.Generic[T]):
"""RPC method."""
"""RPC method.

Args:
caller: RPC caller function.
name: RPC method name.
"""

def __init__(self, caller: t.Callable[[str], T], name: str) -> None:
"""Instantiate an RPC method."""
self.__caller = caller
self.__name = name

Expand Down
1 change: 0 additions & 1 deletion src/citric/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ def __init__(
requests_session: requests.Session | None = None,
json_encoder: type[json.JSONEncoder] | None = None,
) -> None:
"""Create a LimeSurvey RPC session."""
self.url = url
self._session = requests_session or requests.session()
self._session.headers["User-Agent"] = self.USER_AGENT
Expand Down
23 changes: 22 additions & 1 deletion src/citric/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
"RPCResponse",
"SetQuotaPropertiesResult",
"SurveyProperties",
"SurveyUserActivationSettings",
"CPDBParticipantImportResult",
]

Result: TypeAlias = t.Any
YesNo: TypeAlias = t.Literal["Y", "N"]
YesNo: TypeAlias = t.Literal["Y", "N", "I"]


class FileUploadResult(t.TypedDict):
Expand Down Expand Up @@ -495,3 +496,23 @@ class CPDBParticipantImportResult(t.TypedDict):

ImportCount: int
UpdateCount: int


class SurveyUserActivationSettings(t.TypedDict, total=False):
"""User settings for survey activation.

Keys:
anonymized: Whether the survey is anonymized.
datestamp: Whether the survey records dates.
ipaddr: Whether the survey records IP addresses.
ipanonymize: Whether the survey anonymizes IP addresses.
refurl: Whether the survey records referrer URLs.
savetimings: Whether the survey saves response timings.
"""

anonymized: bool
datestamp: bool
ipaddr: bool
ipanonymize: bool
refurl: bool
savetimings: bool
10 changes: 6 additions & 4 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ class MockSession(Session):
"restrictToLanguages": "en fr es",
}

def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
"""Create a mock session."""

def rpc(self, method: str, *params: t.Any) -> dict[str, t.Any]:
"""Process a mock RPC call."""
return {"method": method, "params": [*params]}
Expand Down Expand Up @@ -197,7 +194,12 @@ def client() -> t.Generator[Client, None, None]:

def test_activate_survey(client: MockClient):
"""Test activate_survey client method."""
assert_client_session_call(client, "activate_survey", 1)
assert_client_session_call(
client,
"activate_survey",
1,
user_activation_settings=None,
)


def test_activate_tokens(client: MockClient):
Expand Down
47 changes: 46 additions & 1 deletion tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def test_copy_survey_destination_id(
pytest.mark.xfail(
server_version < semver.VersionInfo.parse("6.4.0-dev"),
reason=(
"The destination_survey_id parameter is only supported in LimeSurvey "
"The destination_survey_id parameter is not supported in LimeSurvey "
f"{server_version} < 6.4.0"
),
),
Expand Down Expand Up @@ -348,6 +348,51 @@ def test_activate_survey(client: citric.Client, survey_id: int):
assert properties_after["active"] == "Y"


@pytest.mark.integration_test
def test_activate_survey_with_settings(
request: pytest.FixtureRequest,
client: citric.Client,
server_version: semver.VersionInfo,
survey_id: int,
):
"""Test whether the survey gets activated with the requested settings."""
min_version = (5, 6, 45) if server_version < (6, 0) else (6, 3, 5)
request.applymarker(
pytest.mark.xfail(
server_version < min_version or server_version.prerelease is not None,
reason=(
"The user_activation_settings parameter is not supported in LimeSurvey "
f"{server_version} < {'.'.join(str(v) for v in min_version)}"
),
),
)

properties_before = client.get_survey_properties(
survey_id,
["active", "anonymized", "ipaddr"],
)
assert properties_before["active"] == "N"
assert properties_before["anonymized"] == "N"
assert properties_before["ipaddr"] == "I"

result = client.activate_survey(
survey_id,
user_activation_settings={
"anonymized": True,
"ipaddr": False,
},
)
assert result["status"] == "OK"

properties_after = client.get_survey_properties(
survey_id,
["active", "anonymized", "ipaddr"],
)
assert properties_after["active"] == "Y"
assert properties_after["anonymized"] == "Y"
assert properties_after["ipaddr"] == "N"


@pytest.mark.integration_test
def test_activate_tokens(client: citric.Client, survey_id: int):
"""Test whether the participants table gets activated."""
Expand Down
Loading