From cc92670054078377c66ee685ea22f5f830e31826 Mon Sep 17 00:00:00 2001 From: Paul Wildenhain Date: Tue, 15 Apr 2025 14:46:09 -0400 Subject: [PATCH 1/8] :white_check_mark: Add tests for export survey link --- redcap/methods/surveys.py | 33 ++++++++++---------------- tests/integration/test_long_project.py | 8 +++++++ tests/unit/callback_utils.py | 17 +++++++++++++ tests/unit/test_long_project.py | 5 ++++ 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/redcap/methods/surveys.py b/redcap/methods/surveys.py index fc2b785..239a6fb 100644 --- a/redcap/methods/surveys.py +++ b/redcap/methods/surveys.py @@ -17,7 +17,7 @@ def export_survey_link( record: str, event: Optional[str] = None, repeat_instance: int = 1, - ): + ) -> str: """ Export one survey link @@ -32,36 +32,27 @@ def export_survey_link( event: Unique event name, only used in longitudinal projects repeat_instance: - only for projects with repeating instruments/events) - The repeat instance number of the repeating event (if longitudinal) - or the repeating instrument (if classic or longitudinal). + only for projects with repeating instruments/events) + The repeat instance number of the repeating event (if longitudinal) + or the repeating instrument (if classic or longitudinal). Default value is '1'. Returns: - Str: - URL of survey link requested + URL of survey link requested Examples: - >>> proj.export_survey_link(instrument="form_1", record="5", event="event_1_arm_1") - https://redcap.mytld.com/surveys/?s=6B2zFAEWPVSrXnnx - """ - payload = self._initialize_payload( - content="surveyLink" - ) + >>> proj.export_survey_link(instrument="form_1", record="1", event="event_1_arm_1") + 'https://redcapdemo.vumc.org/surveys/?s=...' + """ + payload = self._initialize_payload(content="surveyLink") payload["instrument"] = instrument payload["record"] = record - if event: - payload["event"] = event payload["repeat_instance"] = repeat_instance - return_type = "str" - response = cast(Union[Json, str], self._call_api(payload, return_type)) + if event: + payload["event"] = event - return self._return_data( - response=response, - content="surveyLink", - format_type=return_type, - ) + return cast(str, self._call_api(payload, return_type="str")) def export_survey_participant_list( self, diff --git a/tests/integration/test_long_project.py b/tests/integration/test_long_project.py index a506674..e77ef1e 100644 --- a/tests/integration/test_long_project.py +++ b/tests/integration/test_long_project.py @@ -303,3 +303,11 @@ def test_fem_import(long_project): response = long_project.import_instrument_event_mappings(current_fem) assert response == 44 + + +@pytest.mark.integration +def test_export_survey_link(long_project): + link = long_project.export_survey_link( + instrument="contact_info", event="enrollment_arm_1", record="1" + ) + assert link.startswith("https://redcapdemo.vumc.org/surveys/?s=") diff --git a/tests/unit/callback_utils.py b/tests/unit/callback_utils.py index 8ab167e..2180ac1 100644 --- a/tests/unit/callback_utils.py +++ b/tests/unit/callback_utils.py @@ -781,6 +781,22 @@ def handle_long_project_survey_participants_request(**kwargs) -> Any: return (201, headers, json.dumps(resp)) +def handle_long_project_survey_link_request(**kwargs) -> Any: + """Get the survey link for a record and instrument and event""" + data = kwargs["data"] + headers = kwargs["headers"] + resp = None + + if ( + "test" in data.get("instrument") + and "raw" in data.get("event") + and "1" in data.get("record") + ): + resp = "https://redcapdemo.vumc.org/surveys/?s=DMgheEPAgLETxJkf" + + return (201, headers, resp) + + def get_simple_project_request_handler(request_type: str) -> Callable: """Given a request type, extract the handler function""" handlers_dict = { @@ -823,6 +839,7 @@ def get_long_project_request_handler(request_type: str) -> Callable: "pdf": handle_long_project_pdf_request, "record": handle_long_project_records_request, "report": handle_long_project_reports_request, + "surveyLink": handle_long_project_survey_link_request, "version": handle_long_project_version_request, } diff --git a/tests/unit/test_long_project.py b/tests/unit/test_long_project.py index 9c4a147..f349a39 100644 --- a/tests/unit/test_long_project.py +++ b/tests/unit/test_long_project.py @@ -71,6 +71,11 @@ def test_export_survey_participants_list(long_project): assert is_json(res) +def test_export_survey_link(long_project): + res = long_project.export_survey_link(instrument="test", record="1", event="raw") + assert res.startswith("https://redcapdemo.vumc.org/surveys/?s=") + + def test_metadata_import_handles_api_error(long_project): metadata = long_project.export_metadata() From 2eb5a2a496ac3f24130e66d3cff7497f56cf2b90 Mon Sep 17 00:00:00 2001 From: Paul Wildenhain Date: Wed, 30 Apr 2025 15:18:28 -0400 Subject: [PATCH 2/8] :sparkles: Add export survey access code --- redcap/methods/surveys.py | 70 +++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/redcap/methods/surveys.py b/redcap/methods/surveys.py index 239a6fb..26a9f2a 100644 --- a/redcap/methods/surveys.py +++ b/redcap/methods/surveys.py @@ -13,8 +13,8 @@ class Surveys(Base): def export_survey_link( self, - instrument: str, record: str, + instrument: str, event: Optional[str] = None, repeat_instance: int = 1, ) -> str: @@ -25,10 +25,10 @@ def export_survey_link( The passed instrument must be set up as a survey instrument. Args: - instrument: - Name of instrument as seen in the Data Dictionary (metadata). record: Name of the record + instrument: + Name of instrument as seen in the Data Dictionary (metadata). event: Unique event name, only used in longitudinal projects repeat_instance: @@ -41,12 +41,68 @@ def export_survey_link( URL of survey link requested Examples: - >>> proj.export_survey_link(instrument="form_1", record="1", event="event_1_arm_1") + >>> proj.export_survey_link(record="1", instrument="form_1", event="event_1_arm_1") 'https://redcapdemo.vumc.org/surveys/?s=...' """ - payload = self._initialize_payload(content="surveyLink") + payload = self._initialize_payload( + content="surveyLink", + # Hard-coded due to the nature of the response + return_format_type="csv", + ) + + payload["record"] = record payload["instrument"] = instrument + payload["repeat_instance"] = repeat_instance + + if event: + payload["event"] = event + + return cast(str, self._call_api(payload, return_type="str")) + + def export_survey_access_code( + self, + record: str, + instrument: str, + event: Optional[str] = None, + repeat_instance: int = 1, + ) -> str: + # pylint: disable=line-too-long + """ + Export a Survey Access Code for a Participant + + Note: + The passed instrument must be set up as a survey instrument. + + Args: + record: + Name of the record + instrument: + Name of instrument as seen in the Data Dictionary (metadata). + event: + Unique event name, only used in longitudinal projects + repeat_instance: + only for projects with repeating instruments/events) + The repeat instance number of the repeating event (if longitudinal) + or the repeating instrument (if classic or longitudinal). + Default value is '1'. + + Returns: + A survey access code for a specified record and data collection + instrument + + Examples: + >>> proj.export_survey_access_code(record="1", instrument="form_1", event="event_1_arm_1") + '...' + """ + # pylint: enable=line-too-long + payload = self._initialize_payload( + content="surveyAccessCode", + # Hard-coded due to the nature of the response + return_format_type="csv", + ) + payload["record"] = record + payload["instrument"] = instrument payload["repeat_instance"] = repeat_instance if event: @@ -60,6 +116,7 @@ def export_survey_participant_list( format_type: Literal["json", "csv", "xml", "df"] = "json", event: Optional[str] = None, df_kwargs: Optional[Dict[str, Any]] = None, + #return_format_type: Literal["json", "csv", "xml"] = "json", ): """ Export the Survey Participant List @@ -94,7 +151,8 @@ def export_survey_participant_list( 'survey_access_code': ...}] """ payload = self._initialize_payload( - content="participantList", format_type=format_type + content="participantList", + format_type=format_type, ) payload["instrument"] = instrument if event: From 6a2a53ec4512c00b6b4e986cb3460d51b4d758ac Mon Sep 17 00:00:00 2001 From: Paul Wildenhain Date: Wed, 30 Apr 2025 15:54:29 -0400 Subject: [PATCH 3/8] :white_check_mark: Add tests for export_survey_access_code --- redcap/methods/surveys.py | 1 - tests/integration/test_long_project.py | 8 ++++++++ tests/unit/callback_utils.py | 17 +++++++++++++++++ tests/unit/test_long_project.py | 7 +++++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/redcap/methods/surveys.py b/redcap/methods/surveys.py index 26a9f2a..361366c 100644 --- a/redcap/methods/surveys.py +++ b/redcap/methods/surveys.py @@ -116,7 +116,6 @@ def export_survey_participant_list( format_type: Literal["json", "csv", "xml", "df"] = "json", event: Optional[str] = None, df_kwargs: Optional[Dict[str, Any]] = None, - #return_format_type: Literal["json", "csv", "xml"] = "json", ): """ Export the Survey Participant List diff --git a/tests/integration/test_long_project.py b/tests/integration/test_long_project.py index e77ef1e..54b5934 100644 --- a/tests/integration/test_long_project.py +++ b/tests/integration/test_long_project.py @@ -311,3 +311,11 @@ def test_export_survey_link(long_project): instrument="contact_info", event="enrollment_arm_1", record="1" ) assert link.startswith("https://redcapdemo.vumc.org/surveys/?s=") + + +@pytest.mark.integration +def test_export_survey_access_code(long_project): + code = long_project.export_survey_access_code( + record="1", instrument="contact_info", event="enrollment_arm_1" + ) + assert len(code) == 9 diff --git a/tests/unit/callback_utils.py b/tests/unit/callback_utils.py index 2180ac1..ec8632a 100644 --- a/tests/unit/callback_utils.py +++ b/tests/unit/callback_utils.py @@ -797,6 +797,22 @@ def handle_long_project_survey_link_request(**kwargs) -> Any: return (201, headers, resp) +def handle_long_project_survey_access_code_request(**kwargs) -> Any: + """Get the survey access code for a record and instrument and event""" + data = kwargs["data"] + headers = kwargs["headers"] + resp = None + + if ( + "test" in data.get("instrument") + and "raw" in data.get("event") + and "1" in data.get("record") + ): + resp = "AAAAAAAAA" + + return (201, headers, resp) + + def get_simple_project_request_handler(request_type: str) -> Callable: """Given a request type, extract the handler function""" handlers_dict = { @@ -840,6 +856,7 @@ def get_long_project_request_handler(request_type: str) -> Callable: "record": handle_long_project_records_request, "report": handle_long_project_reports_request, "surveyLink": handle_long_project_survey_link_request, + "surveyAccessCode": handle_long_project_survey_access_code_request, "version": handle_long_project_version_request, } diff --git a/tests/unit/test_long_project.py b/tests/unit/test_long_project.py index f349a39..8e5da97 100644 --- a/tests/unit/test_long_project.py +++ b/tests/unit/test_long_project.py @@ -76,6 +76,13 @@ def test_export_survey_link(long_project): assert res.startswith("https://redcapdemo.vumc.org/surveys/?s=") +def test_export_survey_access_code(long_project): + res = long_project.export_survey_access_code( + instrument="test", record="1", event="raw" + ) + assert len(res) == 9 + + def test_metadata_import_handles_api_error(long_project): metadata = long_project.export_metadata() From c253427558da0421fe91ec74dcb0bce16994f59a Mon Sep 17 00:00:00 2001 From: Paul Wildenhain Date: Thu, 1 May 2025 16:47:28 -0400 Subject: [PATCH 4/8] :heavy_check_mark: Enable survey queue in test projects --- tests/data/doctest_project.xml | 56 ++++++++++++++++++++------------ tests/data/test_long_project.xml | 51 +++++++++++++++++++---------- 2 files changed, 70 insertions(+), 37 deletions(-) diff --git a/tests/data/doctest_project.xml b/tests/data/doctest_project.xml index 2de7f3e..f045d5e 100644 --- a/tests/data/doctest_project.xml +++ b/tests/data/doctest_project.xml @@ -1,27 +1,43 @@ - - + + - PyCap doctest_project - This file contains the metadata, events, and data for REDCap project "PyCap doctest_project". - PyCap doctest_project + doctest project + This file contains the metadata, events, and data for REDCap project "doctest project". + doctest project 1 + 1 + 1 0 1 + 0 + 1 + 0 + 0 + 1 + + 2 + 0 0 0 - 0 - 0 - 0 - 0 - 1 - 0 + 0 + + + + + + + + 1 + + + 0 @@ -36,24 +52,24 @@ +<p>Have a nice day!</p>" stop_action_acknowledgement="" stop_action_delete_response="0" question_by_section="0" display_page_number="0" question_auto_numbering="1" survey_enabled="1" save_and_return="0" save_and_return_code_bypass="0" logo="" hide_title="0" view_results="0" min_responses_view_results="10" check_diversity_view_results="0" end_survey_redirect_url="" survey_expiration="" promis_skip_question="0" survey_auth_enabled_single="0" edit_completed_response="0" hide_back_button="0" show_required_field_text="1" confirmation_email_subject="" confirmation_email_content="" confirmation_email_from="" confirmation_email_from_display="" confirmation_email_attach_pdf="0" confirmation_email_attachment="" text_to_speech="0" text_to_speech_language="en" end_survey_redirect_next_survey="0" end_survey_redirect_next_survey_logic="" theme="" text_size="1" font_family="16" theme_text_buttons="" theme_bg_page="" theme_text_title="" theme_bg_title="" theme_text_sectionheader="" theme_bg_sectionheader="" theme_text_question="" theme_bg_question="" enhanced_choices="0" repeat_survey_enabled="0" repeat_survey_btn_text="" repeat_survey_btn_location="HIDDEN" response_limit="" response_limit_include_partials="1" response_limit_custom_text="<p>Thank you for your interest; however, the survey is closed because the maximum number of responses has been reached.</p>" survey_time_limit_days="" survey_time_limit_hours="" survey_time_limit_minutes="" email_participant_field="" end_of_survey_pdf_download="0" pdf_save_to_field="" pdf_save_to_event_id="" pdf_save_translated="0" pdf_auto_archive="0" pdf_econsent_version="" pdf_econsent_type="" pdf_econsent_firstname_field="" pdf_econsent_firstname_event_id="" pdf_econsent_lastname_field="" pdf_econsent_lastname_event_id="" pdf_econsent_dob_field="" pdf_econsent_dob_event_id="" pdf_econsent_allow_edit="1" pdf_econsent_signature_field1="" pdf_econsent_signature_field2="" pdf_econsent_signature_field3="" pdf_econsent_signature_field4="" pdf_econsent_signature_field5="" survey_width_percent="" survey_show_font_resize="1" survey_btn_text_prev_page="" survey_btn_text_next_page="" survey_btn_text_submit="" survey_btn_hide_submit="0" survey_btn_hide_submit_logic=""/> + + + - + - + - - - - + - + @@ -132,7 +148,7 @@ - + diff --git a/tests/data/test_long_project.xml b/tests/data/test_long_project.xml index 3f1b778..51934bc 100644 --- a/tests/data/test_long_project.xml +++ b/tests/data/test_long_project.xml @@ -1,27 +1,43 @@ - - + + - PyCap test_long_project: 01-19 18:35:09 - This file contains the metadata, events, and data for REDCap project "PyCap test_long_project: 01-19 18:35:09". - PyCap test_long_project: 01-19 18:35:09 + Test Long Project + This file contains the metadata, events, and data for REDCap project "Test Long Project". + Test Long Project 0 + 1 + 1 0 1 + 0 + 1 + 0 + 0 + 1 + + 2 + 0 0 0 - 0 - 0 - 0 - 0 - 1 - 0 + 0 + + + + + + + + 1 + + + 0 @@ -35,13 +51,14 @@ +<p>Have a nice day!</p>" stop_action_acknowledgement="" stop_action_delete_response="0" question_by_section="0" display_page_number="0" question_auto_numbering="1" survey_enabled="1" save_and_return="0" save_and_return_code_bypass="0" logo="" hide_title="0" view_results="0" min_responses_view_results="10" check_diversity_view_results="0" end_survey_redirect_url="" survey_expiration="" promis_skip_question="0" survey_auth_enabled_single="0" edit_completed_response="0" hide_back_button="0" show_required_field_text="1" confirmation_email_subject="" confirmation_email_content="" confirmation_email_from="" confirmation_email_from_display="" confirmation_email_attach_pdf="0" confirmation_email_attachment="" text_to_speech="0" text_to_speech_language="en" end_survey_redirect_next_survey="0" end_survey_redirect_next_survey_logic="" theme="" text_size="1" font_family="16" theme_text_buttons="" theme_bg_page="" theme_text_title="" theme_bg_title="" theme_text_sectionheader="" theme_bg_sectionheader="" theme_text_question="" theme_bg_question="" enhanced_choices="0" repeat_survey_enabled="0" repeat_survey_btn_text="" repeat_survey_btn_location="HIDDEN" response_limit="" response_limit_include_partials="1" response_limit_custom_text="<p>Thank you for your interest; however, the survey is closed because the maximum number of responses has been reached.</p>" survey_time_limit_days="" survey_time_limit_hours="" survey_time_limit_minutes="" email_participant_field="" end_of_survey_pdf_download="0" pdf_save_to_field="" pdf_save_to_event_id="" pdf_save_translated="0" pdf_auto_archive="0" pdf_econsent_version="" pdf_econsent_type="" pdf_econsent_firstname_field="" pdf_econsent_firstname_event_id="" pdf_econsent_lastname_field="" pdf_econsent_lastname_event_id="" pdf_econsent_dob_field="" pdf_econsent_dob_event_id="" pdf_econsent_allow_edit="1" pdf_econsent_signature_field1="" pdf_econsent_signature_field2="" pdf_econsent_signature_field3="" pdf_econsent_signature_field4="" pdf_econsent_signature_field5="" survey_width_percent="" survey_show_font_resize="1" survey_btn_text_prev_page="" survey_btn_text_next_page="" survey_btn_text_submit="" survey_btn_hide_submit="0" survey_btn_hide_submit_logic=""/> - - - + + + + - + @@ -1163,7 +1180,7 @@ - + From eafa92d791d9860d40ee9f12d9d4a7de7bf2c417 Mon Sep 17 00:00:00 2001 From: Paul Wildenhain Date: Thu, 1 May 2025 16:47:46 -0400 Subject: [PATCH 5/8] :sparkles: Add survey queue link method --- redcap/methods/surveys.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/redcap/methods/surveys.py b/redcap/methods/surveys.py index 361366c..c1f03e0 100644 --- a/redcap/methods/surveys.py +++ b/redcap/methods/surveys.py @@ -59,6 +59,38 @@ def export_survey_link( return cast(str, self._call_api(payload, return_type="str")) + def export_survey_queue_link( + self, + record: str, + ) -> str: + """ + Export one survey queue link + + Note: + The passed instrument must be set up as a survey instrument. The + survey queue must be enabled for the project. + + Args: + record: + Name of the record + + Returns: + URL of survey queue link requested + + Examples: + >>> proj.export_survey_queue_link(record="1") + 'https://redcapdemo.vumc.org/surveys/?sq=...' + """ + payload = self._initialize_payload( + content="surveyQueueLink", + # Hard-coded due to the nature of the response + return_format_type="csv", + ) + + payload["record"] = record + + return cast(str, self._call_api(payload, return_type="str")) + def export_survey_access_code( self, record: str, From c81a521feda34ddacb9388d97142b36da28ce412 Mon Sep 17 00:00:00 2001 From: Paul Wildenhain Date: Fri, 2 May 2025 12:57:35 -0400 Subject: [PATCH 6/8] :white_check_mark: Add survey queue link tests --- tests/integration/test_long_project.py | 38 +++++++++++++++----------- tests/unit/callback_utils.py | 13 +++++++++ tests/unit/test_long_project.py | 5 ++++ 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/tests/integration/test_long_project.py b/tests/integration/test_long_project.py index 54b5934..cf345bf 100644 --- a/tests/integration/test_long_project.py +++ b/tests/integration/test_long_project.py @@ -17,6 +17,28 @@ def test_is_longitudinal(long_project): assert long_project.is_longitudinal +@pytest.mark.integration +def test_export_survey_link(long_project): + link = long_project.export_survey_link( + instrument="contact_info", event="enrollment_arm_1", record="1" + ) + assert link.startswith("https://redcapdemo.vumc.org/surveys/?s=") + + +@pytest.mark.integration +def test_export_survey_queue_link(long_project): + link = long_project.export_survey_queue_link(record="1") + assert link.startswith("https://redcapdemo.vumc.org/surveys/?sq=") + + +@pytest.mark.integration +def test_export_survey_access_code(long_project): + code = long_project.export_survey_access_code( + record="1", instrument="contact_info", event="enrollment_arm_1" + ) + assert len(code) == 9 + + @pytest.mark.integration def test_survey_participant_export(long_project): data = long_project.export_survey_participant_list( @@ -303,19 +325,3 @@ def test_fem_import(long_project): response = long_project.import_instrument_event_mappings(current_fem) assert response == 44 - - -@pytest.mark.integration -def test_export_survey_link(long_project): - link = long_project.export_survey_link( - instrument="contact_info", event="enrollment_arm_1", record="1" - ) - assert link.startswith("https://redcapdemo.vumc.org/surveys/?s=") - - -@pytest.mark.integration -def test_export_survey_access_code(long_project): - code = long_project.export_survey_access_code( - record="1", instrument="contact_info", event="enrollment_arm_1" - ) - assert len(code) == 9 diff --git a/tests/unit/callback_utils.py b/tests/unit/callback_utils.py index ec8632a..35e8d17 100644 --- a/tests/unit/callback_utils.py +++ b/tests/unit/callback_utils.py @@ -797,6 +797,18 @@ def handle_long_project_survey_link_request(**kwargs) -> Any: return (201, headers, resp) +def handle_long_project_survey_queue_link_request(**kwargs) -> Any: + """Get the survey queue link for a record""" + data = kwargs["data"] + headers = kwargs["headers"] + resp = None + + if "1" in data.get("record"): + resp = "https://redcapdemo.vumc.org/surveys/?sq=DMgheEPAgLETxJkf" + + return (201, headers, resp) + + def handle_long_project_survey_access_code_request(**kwargs) -> Any: """Get the survey access code for a record and instrument and event""" data = kwargs["data"] @@ -856,6 +868,7 @@ def get_long_project_request_handler(request_type: str) -> Callable: "record": handle_long_project_records_request, "report": handle_long_project_reports_request, "surveyLink": handle_long_project_survey_link_request, + "surveyQueueLink": handle_long_project_survey_queue_link_request, "surveyAccessCode": handle_long_project_survey_access_code_request, "version": handle_long_project_version_request, } diff --git a/tests/unit/test_long_project.py b/tests/unit/test_long_project.py index 8e5da97..1a57072 100644 --- a/tests/unit/test_long_project.py +++ b/tests/unit/test_long_project.py @@ -76,6 +76,11 @@ def test_export_survey_link(long_project): assert res.startswith("https://redcapdemo.vumc.org/surveys/?s=") +def test_export_survey_queue_link(long_project): + res = long_project.export_survey_queue_link(record="1") + assert res.startswith("https://redcapdemo.vumc.org/surveys/?sq=") + + def test_export_survey_access_code(long_project): res = long_project.export_survey_access_code( instrument="test", record="1", event="raw" From 600a247d8df3ed01f610ec4a04147012acb0fa19 Mon Sep 17 00:00:00 2001 From: Paul Wildenhain Date: Fri, 2 May 2025 13:17:26 -0400 Subject: [PATCH 7/8] :sparkles: Add survey return code --- redcap/methods/surveys.py | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/redcap/methods/surveys.py b/redcap/methods/surveys.py index c1f03e0..0597e07 100644 --- a/redcap/methods/surveys.py +++ b/redcap/methods/surveys.py @@ -142,6 +142,57 @@ def export_survey_access_code( return cast(str, self._call_api(payload, return_type="str")) + def export_survey_return_code( + self, + record: str, + instrument: str, + event: Optional[str] = None, + repeat_instance: int = 1, + ) -> str: + # pylint: disable=line-too-long + """ + Export a Survey Return Code for a Participant + + Note: + The passed instrument must be set up as a survey instrument, which has return codes enabled. + + Args: + record: + Name of the record + instrument: + Name of instrument as seen in the Data Dictionary (metadata). + event: + Unique event name, only used in longitudinal projects + repeat_instance: + only for projects with repeating instruments/events) + The repeat instance number of the repeating event (if longitudinal) + or the repeating instrument (if classic or longitudinal). + Default value is '1'. + + Returns: + A survey return code for a specified record and data collection + instrument + + Examples: + >>> proj.export_survey_return_code(record="1", instrument="form_1", event="event_1_arm_1") + '...' + """ + # pylint: enable=line-too-long + payload = self._initialize_payload( + content="surveyReturnCode", + # Hard-coded due to the nature of the response + return_format_type="csv", + ) + + payload["record"] = record + payload["instrument"] = instrument + payload["repeat_instance"] = repeat_instance + + if event: + payload["event"] = event + + return cast(str, self._call_api(payload, return_type="str")) + def export_survey_participant_list( self, instrument: str, From 51a03e2c998bce13a2d9725723e91b9b409f4f38 Mon Sep 17 00:00:00 2001 From: Paul Wildenhain Date: Fri, 2 May 2025 13:17:44 -0400 Subject: [PATCH 8/8] :white_check_mark: Add tests for survey return code --- tests/data/doctest_project.xml | 8 ++++---- tests/data/test_long_project.xml | 16 ++++++++-------- tests/integration/test_long_project.py | 8 ++++++++ tests/unit/callback_utils.py | 17 +++++++++++++++++ tests/unit/test_long_project.py | 7 +++++++ 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/tests/data/doctest_project.xml b/tests/data/doctest_project.xml index f045d5e..495236a 100644 --- a/tests/data/doctest_project.xml +++ b/tests/data/doctest_project.xml @@ -1,5 +1,5 @@ - + doctest project @@ -52,7 +52,7 @@ +<p>Have a nice day!</p>" stop_action_acknowledgement="" stop_action_delete_response="0" question_by_section="0" display_page_number="0" question_auto_numbering="1" survey_enabled="1" save_and_return="1" save_and_return_code_bypass="0" logo="" hide_title="0" view_results="0" min_responses_view_results="10" check_diversity_view_results="0" end_survey_redirect_url="" survey_expiration="" promis_skip_question="0" survey_auth_enabled_single="0" edit_completed_response="0" hide_back_button="0" show_required_field_text="1" confirmation_email_subject="" confirmation_email_content="" confirmation_email_from="" confirmation_email_from_display="" confirmation_email_attach_pdf="0" confirmation_email_attachment="" text_to_speech="0" text_to_speech_language="en" end_survey_redirect_next_survey="0" end_survey_redirect_next_survey_logic="" theme="" text_size="1" font_family="16" theme_text_buttons="" theme_bg_page="" theme_text_title="" theme_bg_title="" theme_text_sectionheader="" theme_bg_sectionheader="" theme_text_question="" theme_bg_question="" enhanced_choices="0" repeat_survey_enabled="0" repeat_survey_btn_text="" repeat_survey_btn_location="HIDDEN" response_limit="" response_limit_include_partials="1" response_limit_custom_text="<p>Thank you for your interest; however, the survey is closed because the maximum number of responses has been reached.</p>" survey_time_limit_days="" survey_time_limit_hours="" survey_time_limit_minutes="" email_participant_field="" end_of_survey_pdf_download="0" pdf_save_to_field="" pdf_save_to_event_id="" pdf_save_translated="0" pdf_auto_archive="0" pdf_econsent_version="" pdf_econsent_type="" pdf_econsent_firstname_field="" pdf_econsent_firstname_event_id="" pdf_econsent_lastname_field="" pdf_econsent_lastname_event_id="" pdf_econsent_dob_field="" pdf_econsent_dob_event_id="" pdf_econsent_allow_edit="1" pdf_econsent_signature_field1="" pdf_econsent_signature_field2="" pdf_econsent_signature_field3="" pdf_econsent_signature_field4="" pdf_econsent_signature_field5="" survey_width_percent="" survey_show_font_resize="1" survey_btn_text_prev_page="" survey_btn_text_next_page="" survey_btn_text_submit="" survey_btn_hide_submit="0" survey_btn_hide_submit_logic=""/> @@ -64,7 +64,7 @@ - + @@ -148,7 +148,7 @@ - + diff --git a/tests/data/test_long_project.xml b/tests/data/test_long_project.xml index 51934bc..45fb5d7 100644 --- a/tests/data/test_long_project.xml +++ b/tests/data/test_long_project.xml @@ -1,10 +1,10 @@ - - + + - Test Long Project - This file contains the metadata, events, and data for REDCap project "Test Long Project". - Test Long Project + New Test Long Project + This file contains the metadata, events, and data for REDCap project "New Test Long Project". + New Test Long Project 0 @@ -51,14 +51,14 @@ +<p>Have a nice day!</p>" stop_action_acknowledgement="" stop_action_delete_response="0" question_by_section="0" display_page_number="0" question_auto_numbering="1" survey_enabled="1" save_and_return="1" save_and_return_code_bypass="0" logo="" hide_title="0" view_results="0" min_responses_view_results="10" check_diversity_view_results="0" end_survey_redirect_url="" survey_expiration="" promis_skip_question="0" survey_auth_enabled_single="0" edit_completed_response="0" hide_back_button="0" show_required_field_text="1" confirmation_email_subject="" confirmation_email_content="" confirmation_email_from="" confirmation_email_from_display="" confirmation_email_attach_pdf="0" confirmation_email_attachment="" text_to_speech="0" text_to_speech_language="en" end_survey_redirect_next_survey="0" end_survey_redirect_next_survey_logic="" theme="" text_size="1" font_family="16" theme_text_buttons="" theme_bg_page="" theme_text_title="" theme_bg_title="" theme_text_sectionheader="" theme_bg_sectionheader="" theme_text_question="" theme_bg_question="" enhanced_choices="0" repeat_survey_enabled="0" repeat_survey_btn_text="" repeat_survey_btn_location="HIDDEN" response_limit="" response_limit_include_partials="1" response_limit_custom_text="<p>Thank you for your interest; however, the survey is closed because the maximum number of responses has been reached.</p>" survey_time_limit_days="" survey_time_limit_hours="" survey_time_limit_minutes="" email_participant_field="" end_of_survey_pdf_download="0" pdf_save_to_field="" pdf_save_to_event_id="" pdf_save_translated="0" pdf_auto_archive="0" pdf_econsent_version="" pdf_econsent_type="" pdf_econsent_firstname_field="" pdf_econsent_firstname_event_id="" pdf_econsent_lastname_field="" pdf_econsent_lastname_event_id="" pdf_econsent_dob_field="" pdf_econsent_dob_event_id="" pdf_econsent_allow_edit="1" pdf_econsent_signature_field1="" pdf_econsent_signature_field2="" pdf_econsent_signature_field3="" pdf_econsent_signature_field4="" pdf_econsent_signature_field5="" survey_width_percent="" survey_show_font_resize="1" survey_btn_text_prev_page="" survey_btn_text_next_page="" survey_btn_text_submit="" survey_btn_hide_submit="0" survey_btn_hide_submit_logic=""/> - + @@ -1180,7 +1180,7 @@ - + diff --git a/tests/integration/test_long_project.py b/tests/integration/test_long_project.py index cf345bf..cbff629 100644 --- a/tests/integration/test_long_project.py +++ b/tests/integration/test_long_project.py @@ -39,6 +39,14 @@ def test_export_survey_access_code(long_project): assert len(code) == 9 +@pytest.mark.integration +def test_export_survey_return_code(long_project): + code = long_project.export_survey_return_code( + record="1", instrument="contact_info", event="enrollment_arm_1" + ) + assert len(code) == 8 + + @pytest.mark.integration def test_survey_participant_export(long_project): data = long_project.export_survey_participant_list( diff --git a/tests/unit/callback_utils.py b/tests/unit/callback_utils.py index 35e8d17..3d4249d 100644 --- a/tests/unit/callback_utils.py +++ b/tests/unit/callback_utils.py @@ -825,6 +825,22 @@ def handle_long_project_survey_access_code_request(**kwargs) -> Any: return (201, headers, resp) +def handle_long_project_survey_return_code_request(**kwargs) -> Any: + """Get the survey return code for a record and instrument and event""" + data = kwargs["data"] + headers = kwargs["headers"] + resp = None + + if ( + "test" in data.get("instrument") + and "raw" in data.get("event") + and "1" in data.get("record") + ): + resp = "AAAAAAAA" + + return (201, headers, resp) + + def get_simple_project_request_handler(request_type: str) -> Callable: """Given a request type, extract the handler function""" handlers_dict = { @@ -870,6 +886,7 @@ def get_long_project_request_handler(request_type: str) -> Callable: "surveyLink": handle_long_project_survey_link_request, "surveyQueueLink": handle_long_project_survey_queue_link_request, "surveyAccessCode": handle_long_project_survey_access_code_request, + "surveyReturnCode": handle_long_project_survey_return_code_request, "version": handle_long_project_version_request, } diff --git a/tests/unit/test_long_project.py b/tests/unit/test_long_project.py index 1a57072..656deeb 100644 --- a/tests/unit/test_long_project.py +++ b/tests/unit/test_long_project.py @@ -88,6 +88,13 @@ def test_export_survey_access_code(long_project): assert len(res) == 9 +def test_export_survey_return_code(long_project): + res = long_project.export_survey_return_code( + instrument="test", record="1", event="raw" + ) + assert len(res) == 8 + + def test_metadata_import_handles_api_error(long_project): metadata = long_project.export_metadata()