From e9d72001c65c6bef37602ad1a0e254025f2f8bbb Mon Sep 17 00:00:00 2001 From: nathan-moore-97 Date: Tue, 5 Dec 2023 19:04:55 -0500 Subject: [PATCH 1/3] Adding a new ENUM to help with the iBeta questions and making the result json much more explicit --- qualtrix/client.py | 123 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 16 deletions(-) diff --git a/qualtrix/client.py b/qualtrix/client.py index 2d5be13..97d38c8 100644 --- a/qualtrix/client.py +++ b/qualtrix/client.py @@ -1,3 +1,5 @@ +from enum import Enum + import logging import requests import time @@ -13,6 +15,51 @@ auth_header = {"X-API-TOKEN": settings.API_TOKEN} +class IBetaSurveyQuestion(Enum): + TESTER_ID = 1 + TEST_TYPE = 2 + DOCUMENT_MODIFICATION = 4 + IMAGE_MODIFICATION = 5 + SELFIE_TEST_TYPE = 6 + DEVICE_TYPE = 7 + DEVICE_MODEL_APPLE = 8 + DEVICE_MODEL_SAMSUNG = 9 + DEVICE_MODEL_GOOGLE = 10 + FAKE_ID_TYPE = 12 + SPOOF_ARTIFACT_TYPE = 13 + DOCUMENT_TYPE = 15 + SUBJECT_ALTERATIONS = 17 + MASK_TYPE = 18 + + def QID_text_list(self, qx_data: dict) -> str or None: + + choice_raw = qx_data.get(f"QID{self.value}", None) + if choice_raw is None: + return choice_raw + + # Build list of text responses, assuming the worst case that every choice has a + # user-entered plaintext response + responses = [] + + for val in choice_raw: + resp = qx_data.get(f"QID{self.value}_{val}_TEXT", None) + if resp is not None: + responses.append(resp) + return responses + + def QID_text(self, qx_data: dict) -> str or None: + choice_raw = qx_data.get(f"QID{self.value}", None) + # Get plaintext responses + response = qx_data.get(f"QID{self.value}_{choice_raw}_TEXT", None) + return response + + def QID_label(self, qx_labels: dict) -> str or None: + return qx_labels.get(f"QID{self.value}", None) + + def __eq__(self, other): + return self.value == other.value + + def get_response(survey_id: str, response_id: str): for i in range(settings.RETRY_ATTEMPTS): r = requests.get( @@ -141,25 +188,69 @@ def get_answer_from_result(result): """ labels = result["labels"] values = result["values"] - # Data sometimes has labels missing, so return null if val isnt found + # Data sometimes has labels missing, so return null if val isnt found if values.get("survey_type", None) == "quality_test": return { - "tester_id": labels.get("QID1", None), - "test_type": labels.get("QID2", None), - "document_modification": labels.get("QID4", None), - "image_modification": labels.get("QID5", None), - "selfie_test_type": labels.get("QID6", None), - "device_type": labels.get("QID7", None), - "device_model": labels.get("QID8", "") - + labels.get("QID9", "") - + labels.get("QID10", "") - + values.get("QID11_TEXT", ""), - "fake_id_type": labels.get("QID12", None), - "spoof_artifact_type": labels.get("QID13", None), - "document_type": labels.get("QID15", None), - "subject_alterations": labels.get("QID17", None), - "mask_type": labels.get("QID18", "") + values.get("QID18_4_TEXT", ""), + "tester_id": IBetaSurveyQuestion.TESTER_ID.QID_label(labels), + "test_type": IBetaSurveyQuestion.TEST_TYPE.QID_label(labels), + "document_modification": { + "modifications": IBetaSurveyQuestion.DOCUMENT_MODIFICATION.QID_label( + labels + ), + "descriptions": IBetaSurveyQuestion.DOCUMENT_MODIFICATION.QID_text_list( + values + ), + }, + "image_modification": IBetaSurveyQuestion.IMAGE_MODIFICATION.QID_label( + labels + ), + "selfie_test_type": IBetaSurveyQuestion.SELFIE_TEST_TYPE.QID_label(labels), + "device": { + "device_type": IBetaSurveyQuestion.DEVICE_TYPE.QID_label(labels), + "device_type_details": IBetaSurveyQuestion.DEVICE_TYPE.QID_text(values), + "samsung_device_group": { + "device_model": IBetaSurveyQuestion.DEVICE_MODEL_SAMSUNG.QID_label( + labels + ), + "device_details": IBetaSurveyQuestion.DEVICE_MODEL_SAMSUNG.QID_text( + values + ), + }, + "apple_device_group": { + "device_model": IBetaSurveyQuestion.DEVICE_MODEL_APPLE.QID_label( + labels + ), + "device_details": IBetaSurveyQuestion.DEVICE_MODEL_APPLE.QID_text( + values + ), + }, + "google_device_group": { + "device_model": IBetaSurveyQuestion.DEVICE_MODEL_GOOGLE.QID_label( + labels + ), + "device_details": IBetaSurveyQuestion.DEVICE_MODEL_GOOGLE.QID_text( + values + ), + }, + }, + "fake_id_type": IBetaSurveyQuestion.FAKE_ID_TYPE.QID_label(labels), + "spoof_artifact_type": IBetaSurveyQuestion.SPOOF_ARTIFACT_TYPE.QID_text( + values + ), + "document_type": IBetaSurveyQuestion.DOCUMENT_TYPE.QID_label(labels), + "subject_alteration": { + "alterations": IBetaSurveyQuestion.SUBJECT_ALTERATIONS.QID_label( + labels + ), + "descriptions": IBetaSurveyQuestion.SUBJECT_ALTERATIONS.QID_text_list( + values + ), + }, + "mask": { + "type": IBetaSurveyQuestion.MASK_TYPE.QID_label(labels), + "description": IBetaSurveyQuestion.MASK_TYPE.QID_text(values), + }, } else: return { From ff06d6925bd5ef9f7548f72d7834d67be2656091 Mon Sep 17 00:00:00 2001 From: nathan-moore-97 Date: Wed, 6 Dec 2023 13:31:52 -0500 Subject: [PATCH 2/3] Coalescing device values into a single object --- qualtrix/client.py | 65 +++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/qualtrix/client.py b/qualtrix/client.py index 97d38c8..b30f95b 100644 --- a/qualtrix/client.py +++ b/qualtrix/client.py @@ -57,7 +57,7 @@ def QID_label(self, qx_labels: dict) -> str or None: return qx_labels.get(f"QID{self.value}", None) def __eq__(self, other): - return self.value == other.value + return self.value == other def get_response(survey_id: str, response_id: str): @@ -191,6 +191,40 @@ def get_answer_from_result(result): # Data sometimes has labels missing, so return null if val isnt found if values.get("survey_type", None) == "quality_test": + + # Device type -> values returns the integer choice of the user. Casting that to the Enum + # will convert to one of the specific device types (Apple, Google, Samsung) + device_type_choice = IBetaSurveyQuestion.DEVICE_TYPE.QID_label(values) + device_response = { + "device_group": IBetaSurveyQuestion.DEVICE_TYPE.QID_label(labels), + "device_model": None, + # If the user has "Other" device group, (not Apple, Google, or Samsung) the + # self identification field will be here + "device_details": IBetaSurveyQuestion.DEVICE_TYPE.QID_text(values), + } + + if device_type_choice == 1: # Iphone or Ipad + device_response[ + "device_model" + ] = IBetaSurveyQuestion.DEVICE_MODEL_APPLE.QID_label(labels) + device_response[ + "device_details" + ] = IBetaSurveyQuestion.DEVICE_MODEL_APPLE.QID_text(values) + elif device_type_choice == 2: # Samsung Galaxy Phone or Tablet + device_response[ + "device_model" + ] = IBetaSurveyQuestion.DEVICE_MODEL_SAMSUNG.QID_label(labels) + device_response[ + "device_details" + ] = IBetaSurveyQuestion.DEVICE_MODEL_SAMSUNG.QID_text(values) + elif device_type_choice == 3: # Google Phone or Tablet + device_response[ + "device_model" + ] = IBetaSurveyQuestion.DEVICE_MODEL_GOOGLE.QID_label(labels) + device_response[ + "device_details" + ] = IBetaSurveyQuestion.DEVICE_MODEL_GOOGLE.QID_text(values) + return { "tester_id": IBetaSurveyQuestion.TESTER_ID.QID_label(labels), "test_type": IBetaSurveyQuestion.TEST_TYPE.QID_label(labels), @@ -206,34 +240,7 @@ def get_answer_from_result(result): labels ), "selfie_test_type": IBetaSurveyQuestion.SELFIE_TEST_TYPE.QID_label(labels), - "device": { - "device_type": IBetaSurveyQuestion.DEVICE_TYPE.QID_label(labels), - "device_type_details": IBetaSurveyQuestion.DEVICE_TYPE.QID_text(values), - "samsung_device_group": { - "device_model": IBetaSurveyQuestion.DEVICE_MODEL_SAMSUNG.QID_label( - labels - ), - "device_details": IBetaSurveyQuestion.DEVICE_MODEL_SAMSUNG.QID_text( - values - ), - }, - "apple_device_group": { - "device_model": IBetaSurveyQuestion.DEVICE_MODEL_APPLE.QID_label( - labels - ), - "device_details": IBetaSurveyQuestion.DEVICE_MODEL_APPLE.QID_text( - values - ), - }, - "google_device_group": { - "device_model": IBetaSurveyQuestion.DEVICE_MODEL_GOOGLE.QID_label( - labels - ), - "device_details": IBetaSurveyQuestion.DEVICE_MODEL_GOOGLE.QID_text( - values - ), - }, - }, + "device": device_response, "fake_id_type": IBetaSurveyQuestion.FAKE_ID_TYPE.QID_label(labels), "spoof_artifact_type": IBetaSurveyQuestion.SPOOF_ARTIFACT_TYPE.QID_text( values From 02ef4433fa1cb17d82459ed0622c66344a6ecea8 Mon Sep 17 00:00:00 2001 From: nathan-moore-97 Date: Wed, 6 Dec 2023 13:36:04 -0500 Subject: [PATCH 3/3] lint --- qualtrix/client.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qualtrix/client.py b/qualtrix/client.py index b30f95b..f32efff 100644 --- a/qualtrix/client.py +++ b/qualtrix/client.py @@ -32,7 +32,6 @@ class IBetaSurveyQuestion(Enum): MASK_TYPE = 18 def QID_text_list(self, qx_data: dict) -> str or None: - choice_raw = qx_data.get(f"QID{self.value}", None) if choice_raw is None: return choice_raw @@ -191,7 +190,6 @@ def get_answer_from_result(result): # Data sometimes has labels missing, so return null if val isnt found if values.get("survey_type", None) == "quality_test": - # Device type -> values returns the integer choice of the user. Casting that to the Enum # will convert to one of the specific device types (Apple, Google, Samsung) device_type_choice = IBetaSurveyQuestion.DEVICE_TYPE.QID_label(values)