1+ import requests
2+ from qualtrics_settings import BASE_URL , HEADERS , SURVEYIDS , QualtricsAPIError
3+
4+ def get_mailing_list_id_of_distribution (survey_id : str , distribution_id : str ) -> str :
5+ """Get the mailing list ID connected to a distribution
6+
7+ Args:
8+ survey_id (str): The ID of the survey to add someone to.
9+ distribution_id (str): The ID of the distribution to get the mailing list ID for.
10+
11+ Raises:
12+ QualtricsAPIError: If there is an error while fetching the mailing list ID or format is unexpected.
13+
14+ Returns:
15+ str: The mailing list ID connected to the distribution.
16+ """
17+ print ("Finding mailing list ID of distribution... " )
18+ endpoint = f"{ BASE_URL } /distributions/{ distribution_id } "
19+ parameters = {
20+ "surveyId" : survey_id
21+ }
22+ try :
23+ response = requests .get (endpoint , headers = HEADERS , params = parameters )
24+ response .raise_for_status ()
25+ mailing_list_id = response .json ()["result" ]["recipients" ]["mailingListId" ]
26+
27+ return mailing_list_id
28+ except requests .exceptions .RequestException as exc :
29+ raise QualtricsAPIError (
30+ f"Error while fetching mailing list ID for distribution { distribution_id } ."
31+ ) from exc
32+ except (KeyError , TypeError , ValueError ) as exc :
33+ raise QualtricsAPIError (
34+ f"Unexpected response format when fetching mailing list ID for distribution { distribution_id } ."
35+ ) from exc
36+
37+ def get_directory_id () -> str :
38+ """Get the directory ID for a Qualtrics, assuming there is just one.
39+
40+ Raises:
41+ QualtricsAPIError: If there is an error while fetching the directory ID or format is unexpected.
42+
43+ Returns:
44+ str: The directory ID for the Qualtrics account.
45+ """
46+ print ("Getting directory ID... " )
47+ endpoint = f"{ BASE_URL } /directories"
48+ parameters = {
49+ "includeCount" : False
50+ }
51+ try :
52+ response = requests .get (endpoint , headers = HEADERS , params = parameters )
53+ response .raise_for_status ()
54+ data = response .json ()
55+ directory_id = data ["result" ]["elements" ][0 ]["directoryId" ]
56+
57+ return directory_id
58+ except requests .exceptions .RequestException as exc :
59+ raise QualtricsAPIError (
60+ f"Error while getting the directory ID."
61+ ) from exc
62+ except (KeyError , TypeError , ValueError ) as exc :
63+ raise QualtricsAPIError (
64+ f"Unexpected response format when fetching directory ID."
65+ ) from exc
66+
67+ def check_contact_in_mailing_list (participant_study_id : str , mailing_list_id : str , directory_id : str ) -> bool :
68+ """Find who is already in that mailing list, check if the participant's ID is already there.
69+
70+ Args:
71+ participant_study_id (str): ID of the participant to check if they are in the mailing list.
72+ mailing_list_id (str): The ID of the mailing list to check.
73+ directory_id (str): The ID of the directory containing the mailing list.
74+
75+ Returns:
76+ bool: True if the participant's ID is already in the mailing list, False otherwise.
77+ """
78+ print ("Checking for contact in mailing list... " )
79+ endpoint = f"{ BASE_URL } /directories/{ directory_id } /mailinglists/{ mailing_list_id } /contacts"
80+ try :
81+ response = requests .get (endpoint , headers = HEADERS )
82+ response .raise_for_status ()
83+ contacts = response .json ()["result" ]["elements" ]
84+ except requests .exceptions .RequestException as exc :
85+ raise QualtricsAPIError (
86+ f"Error while checking for contact in mailing list."
87+ ) from exc
88+ except (KeyError , TypeError , ValueError ) as exc :
89+ raise QualtricsAPIError (
90+ f"Unexpected response format when checking for contact in mailing list."
91+ ) from exc
92+
93+ contact_external_data_references = [contact ["extRef" ] for contact in contacts if "extRef" in contact ]
94+
95+
96+ if participant_study_id not in contact_external_data_references :
97+ return False
98+ return True
99+
100+ def add_contact_to_mailing_list (participant_study_id : str , mailing_list_id : str , directory_id : str , embedded_data_field : str ) -> None :
101+ """Add contact to a mailing list
102+
103+ Args:
104+ participant_study_id (str): ID of the participant whose progress you want to add.
105+ mailing_list_id (str): Mailing list to add to.
106+ directory_id (str): Directory containing the mailing list.
107+ embedded_data_field (str): The field name in Qualtrics where the participant_study_id is stored.
108+
109+ Raises:
110+ QualtricsAPIError: If there is an error while adding the contact or format is unexpected.
111+ """
112+ print ("Adding contact to mailing list... " )
113+ endpoint = f"{ BASE_URL } /directories/{ directory_id } /mailinglists/{ mailing_list_id } /contacts"
114+ contact_information_payload = {
115+ "extRef" : participant_study_id ,
116+ "embeddedData" : {embedded_data_field : participant_study_id }
117+ }
118+ try :
119+ response = requests .post (endpoint , headers = HEADERS , json = contact_information_payload )
120+ response .raise_for_status ()
121+
122+ except requests .exceptions .RequestException as exc :
123+ raise QualtricsAPIError (
124+ f"Error while adding contact to mailing list."
125+ ) from exc
126+ except (KeyError , TypeError , ValueError ) as exc :
127+ raise QualtricsAPIError (
128+ f"Unexpected response format when adding contact to mailing list."
129+ ) from exc
130+
131+ def get_personal_link (survey_id : str , distribution_id : str , participant_study_id : str ) -> str :
132+ """Get the person link of an individual for a specified survey.
133+
134+ Args:
135+ survey_id (str): The ID of the survey to get the personal link for.
136+ distribution_id (str): The ID of the distribution of personal links.
137+ participant_study_id (str): The ID of the participant whose.
138+
139+ Returns:
140+ str: Personal link for the participant, looks like
141+ https://survey.uu.nl/jfe/form/SV_efCMOg6wHU0T8ii?Q_CHLqe2Pdrma&_g_=g
142+ """
143+ print ("Fetching personal link... " )
144+ endpoint = f"{ BASE_URL } /distributions/{ distribution_id } /links"
145+ parameters = {
146+ "surveyId" : str (survey_id )
147+ }
148+ try :
149+ response = requests .get (endpoint , headers = HEADERS , params = parameters )
150+ response .raise_for_status ()
151+ data = response .json ()
152+ personal_link = [item ["link" ] for item in data ["result" ]["elements" ] if ("externalDataReference" in item ) and (item ["externalDataReference" ] == participant_study_id )][0 ]
153+
154+ return personal_link
155+ except requests .exceptions .RequestException as exc :
156+ raise QualtricsAPIError (
157+ f"Error while fetching personal link of individual { participant_study_id } ."
158+ ) from exc
159+ except (KeyError , TypeError , ValueError ) as exc :
160+ raise QualtricsAPIError (
161+ f"Unexpected response format when fetching personal link of individual { participant_study_id } ."
162+ ) from exc
163+
164+ def check_inputs_validity (participant_study_id : str , embedded_data_field : str , survey_id : str , distribution_id : str ) -> bool :
165+ print ("Checking input validity... " )
166+ if not isinstance (participant_study_id , str ) or participant_study_id .strip () == "" :
167+ raise ValueError ("Invalid participant_study_id. It should be a non-empty string." )
168+
169+ if not isinstance (embedded_data_field , str ) or embedded_data_field .strip () == "" :
170+ raise ValueError ("Invalid embedded_data_field. It should be a non-empty string." )
171+
172+ if not isinstance (survey_id , str ) or not survey_id .startswith ("SV_" ):
173+ raise ValueError ("Invalid survey_id. It should be a string starting with 'SV_'." )
174+
175+ if not isinstance (distribution_id , str ) or distribution_id .strip () == "" :
176+ raise ValueError ("Invalid distribution_id. It should be a non-empty string." )
177+
178+ return True
179+
180+
181+ def add_individual_to_survey (participant_study_id , embedded_data_field , survey_id , distribution_id ):
182+ check_inputs_validity (participant_study_id , embedded_data_field , survey_id , distribution_id )
183+
184+ mailing_list_id = get_mailing_list_id_of_distribution (survey_id , distribution_id )
185+ directory_id = get_directory_id ()
186+ contact_exists = check_contact_in_mailing_list (participant_study_id , mailing_list_id , directory_id )
187+
188+ if not contact_exists :
189+ add_contact_to_mailing_list (participant_study_id , mailing_list_id , directory_id )
190+ else :
191+ print (f"Participant study ID { participant_study_id } already exists in this mailing list" )
192+
193+ personal_link = get_personal_link (survey_id , distribution_id , participant_study_id )
194+ return personal_link
195+
196+ if __name__ == "__main__" :
197+ participant_study_id = "11111TEST11111"
198+ survey_id = SURVEYIDS .my_test_survey2_id
199+ distribution_id = "EMD_SZFeoK7LAJBHU4d"
200+ embedded_data_field = "study_id_child"
201+
202+ link = add_individual_to_survey (participant_study_id , embedded_data_field , survey_id , distribution_id )
203+ print (link )
0 commit comments