From 30fdd4a96c7c7726327288737471b90a18966439 Mon Sep 17 00:00:00 2001 From: ayobi Date: Wed, 18 Sep 2024 23:08:14 -0300 Subject: [PATCH 1/8] init commit --- microsetta_private_api/api/_survey.py | 39 +++++- microsetta_private_api/api/tests/test_api.py | 2 + .../api/tests/test_integration.py | 38 ++++++ microsetta_private_api/db/patches/0143.sql | 14 ++ .../repo/survey_template_repo.py | 122 +++++++++++++++++- microsetta_private_api/server_config.json | 1 + .../util/skin_scoring_app_ffq.py | 12 ++ 7 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 microsetta_private_api/db/patches/0143.sql create mode 100644 microsetta_private_api/util/skin_scoring_app_ffq.py diff --git a/microsetta_private_api/api/_survey.py b/microsetta_private_api/api/_survey.py index a138df1de..7c8c010ef 100644 --- a/microsetta_private_api/api/_survey.py +++ b/microsetta_private_api/api/_survey.py @@ -14,7 +14,7 @@ from microsetta_private_api.repo.transaction import Transaction from microsetta_private_api.repo.vioscreen_repo import VioscreenRepo from microsetta_private_api.util import vioscreen, myfoodrepo, vue_adapter, \ - polyphenol_ffq + polyphenol_ffq, skin_scoring_app_ffq from microsetta_private_api.util.vioscreen import VioscreenAdminAPI from microsetta_private_api.config_manager import SERVER_CONFIG @@ -43,6 +43,7 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): SurveyTemplateRepo.VIOSCREEN_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, SurveyTemplateRepo.SPAIN_FFQ_ID, + SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID, SurveyTemplateRepo.BASIC_INFO_ID, SurveyTemplateRepo.AT_HOME_ID, SurveyTemplateRepo.LIFESTYLE_ID, @@ -181,6 +182,37 @@ def _remote_survey_url_spain_ffq(transaction, account_id, source_id): return SERVER_CONFIG['spain_ffq_url'] +def _remote_survey_url_skin_scoring_app_ffq(transaction, + account_id, + source_id, + language_tag): + st_repo = SurveyTemplateRepo(transaction) + + skin_scoring_app_ffq_id, study = \ + st_repo.get_skin_scoring_app_ffq_id_if_exists(account_id, + source_id) + + if skin_scoring_app_ffq_id is None: + study = 'SBI' + sample_repo = SampleRepo(transaction) + samples = sample_repo.get_samples_by_source(account_id, source_id) + for s in samples: + for s_p in s.sample_projects: + if s_p.startswith('SBI'): + study = 'SBI' + break + + skin_scoring_app_ffq_id = \ + st_repo.create_skin_scoring_app_ffq_entry(account_id, + source_id, + language_tag, + study) + + return skin_scoring_app_ffq.gen_ffq_url(skin_scoring_app_ffq_id, + study, + language_tag) + + def read_survey_template(account_id, source_id, survey_template_id, language_tag, token_info, survey_redirect_url=None, vioscreen_ext_sample_id=None, @@ -220,6 +252,11 @@ def read_survey_template(account_id, source_id, survey_template_id, url = _remote_survey_url_spain_ffq(t, account_id, source_id) + elif survey_template_id == \ + SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID: + url = _remote_survey_url_skin_scoring_app_ffq(t, + account_id, + source_id) else: raise ValueError(f"Cannot generate URL for survey " f"{survey_template_id}") diff --git a/microsetta_private_api/api/tests/test_api.py b/microsetta_private_api/api/tests/test_api.py index d65b8bb5c..49d008353 100644 --- a/microsetta_private_api/api/tests/test_api.py +++ b/microsetta_private_api/api/tests/test_api.py @@ -384,6 +384,8 @@ def delete_dummy_accts(): template_repo.delete_polyphenol_ffq(curr_acct_id, curr_source.id) template_repo.delete_spain_ffq(curr_acct_id, curr_source.id) + template_repo.delete_skin_scoring_app_ffq(curr_acct_id, + curr_source.id) # Dissociate all samples linked to this source from all # answered surveys linked to this source, then delete all diff --git a/microsetta_private_api/api/tests/test_integration.py b/microsetta_private_api/api/tests/test_integration.py index 4b6451d94..bde22516a 100644 --- a/microsetta_private_api/api/tests/test_integration.py +++ b/microsetta_private_api/api/tests/test_integration.py @@ -410,6 +410,7 @@ def test_surveys(self): [SurveyTemplateRepo.VIOSCREEN_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, SurveyTemplateRepo.SPAIN_FFQ_ID, + SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID, SurveyTemplateRepo.BASIC_INFO_ID, SurveyTemplateRepo.AT_HOME_ID, SurveyTemplateRepo.LIFESTYLE_ID, @@ -625,6 +626,42 @@ def test_bobo_takes_polyphenol_ffq(self): ) check_response(resp, 404) + @skipIf(SERVER_CONFIG['skin_scoring_app_ffq_url'] in + ('', 'ssafq_placeholder'), + "Skin Scoring App FFQ secrets not provided") + def test_bobo_takes_skin_scoring_app_ffq(self): + bobo = self._bobo_to_claim_a_sample() + + # take Skin Scoring App FFQ + resp = self.client.get( + '/api/accounts/%s/sources/%s/survey_templates/10005' + '?language_tag=en_US' % + (ACCT_ID, bobo['source_id']), + headers=MOCK_HEADERS + ) + check_response(resp) + data = json.loads(resp.data) + exp_start = SERVER_CONFIG['skin_scoring_app_ffq_url'] + url = data['survey_template_text']['url'] + self.assertTrue(url.startswith(exp_start)) + + # verify we err if we attempt to answer the survey. an "answer" here is + # undefined + resp = self.client.post( + '/api/accounts/%s/sources/%s/surveys' + '?language_tag=en_US' % + (ACCT_ID, bobo['source_id']), + content_type='application/json', + data=json.dumps( + { + "survey_template_id": + SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID, + "survey_text": {'key': 'stuff'} + }), + headers=MOCK_HEADERS + ) + check_response(resp, 404) + @skipIf(SERVER_CONFIG['spain_ffq_url'] in ('', 'sffq_placeholder'), "Spain FFQ secrets not provided") def test_bobo_takes_spain_ffq(self): @@ -693,6 +730,7 @@ def test_bobo_takes_all_local_surveys(self): SurveyTemplateRepo.MYFOODREPO_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, SurveyTemplateRepo.SPAIN_FFQ_ID, + SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID, SurveyTemplateRepo.BASIC_INFO_ID, SurveyTemplateRepo.AT_HOME_ID, SurveyTemplateRepo.LIFESTYLE_ID, diff --git a/microsetta_private_api/db/patches/0143.sql b/microsetta_private_api/db/patches/0143.sql new file mode 100644 index 000000000..d57405df0 --- /dev/null +++ b/microsetta_private_api/db/patches/0143.sql @@ -0,0 +1,14 @@ +CREATE TABLE ag.skin_scoring_app_ffq_registry ( + skin_scoring_app_ffq_id VARCHAR PRIMARY KEY, + account_id UUID NOT NULL, + source_id UUID, + language_tag VARCHAR, + study VARCHAR, + deleted BOOLEAN NOT NULL DEFAULT false, + creation_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + CONSTRAINT fk_skin_scoring_app_ffq_registry_account FOREIGN KEY (account_id) REFERENCES ag.account(id), + CONSTRAINT fk_skin_scoring_app_ffq_registry_source FOREIGN KEY (source_id) REFERENCES ag.source(id) +); + +CREATE INDEX skin_scoring_app_ffq_registry_source ON ag.skin_scoring_app_ffq_registry (account_id, source_id); \ No newline at end of file diff --git a/microsetta_private_api/repo/survey_template_repo.py b/microsetta_private_api/repo/survey_template_repo.py index fc1f2a511..e444299ba 100644 --- a/microsetta_private_api/repo/survey_template_repo.py +++ b/microsetta_private_api/repo/survey_template_repo.py @@ -1,3 +1,5 @@ +import random +import string from werkzeug.exceptions import NotFound from microsetta_private_api.config_manager import SERVER_CONFIG @@ -23,6 +25,7 @@ class SurveyTemplateRepo(BaseRepo): MYFOODREPO_ID = 10002 POLYPHENOL_FFQ_ID = 10003 SPAIN_FFQ_ID = 10004 + SKIN_SCORING_APP_FFQ_ID = 10005 BASIC_INFO_ID = 10 AT_HOME_ID = 11 LIFESTYLE_ID = 12 @@ -105,6 +108,12 @@ class SurveyTemplateRepo(BaseRepo): "1.0", "remote" ), + SKIN_SCORING_APP_FFQ_ID: SurveyTemplateLinkInfo( + SKIN_SCORING_APP_FFQ_ID, + "Skin Scoring App FFQ", + "1.0", + "remote" + ), BASIC_INFO_ID: SurveyTemplateLinkInfo( BASIC_INFO_ID, "Basic Information", @@ -755,6 +764,115 @@ def delete_spain_ffq(self, account_id, source_id): AND source_id=%s""", (account_id, source_id)) + def create_skin_scoring_app_ffq_entry(self, + account_id, + source_id, + language_tag, + study): + """Return a newly created Skin Scoring App FFQ ID + + Parameters + ---------- + account_id : str, UUID + The account UUID + source_id : str, UUID + The source UUID + language_tag: str + The user's language tag + study: str + The study variable we'll pass to L'Oréal's FFQ + + Returns + ------- + str + The newly created Skin Scoring App FFQ ID + """ + characters = string.ascii_lowercase + string.digits + skin_scoring_app_ffq_id = ''.join(random.choices(characters, k=8)) + + with self._transaction.cursor() as cur: + cur.execute("""INSERT INTO ag.skin_scoring_app_ffq_registry + (skin_scoring_app_ffq_id, account_id, + source_id, language_tag, study) + VALUES (%s, %s, %s, %s, %s)""", + (skin_scoring_app_ffq_id, account_id, + source_id, language_tag, study)) + + # Put a survey into ag_login_surveys + cur.execute("INSERT INTO ag_login_surveys(" + "ag_login_id, " + "survey_id, " + "vioscreen_status, " + "source_id, " + "survey_template_id) " + "VALUES(%s, %s, %s, %s, %s)", + (account_id, skin_scoring_app_ffq_id, None, source_id, + SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID)) + + return skin_scoring_app_ffq_id + + def get_skin_scoring_app_ffq_id_if_exists(self, + account_id, + source_id): + """Return a Skin Scoring App FFQ ID if one exists + + Parameters + ---------- + account_id : str, UUID + The account UUID + source_id : str, UUID + The source UUID + + Returns + ------- + (UUID, str) or (None, None) + The associated Skin Scoring App FFQ ID and study + It's impossible to find one without the other + """ + with self._transaction.cursor() as cur: + cur.execute("""SELECT skin_scoring_app_ffq_id, study + FROM ag.skin_scoring_app_ffq_registry + WHERE account_id=%s AND source_id=%s""", + (account_id, source_id)) + res = cur.fetchone() + + if res is None: + return (None, None) + else: + return res + + def delete_skin_scoring_app_ffq(self, account_id, source_id): + """Intended for admin use, remove Skin Scoring App FFQ entries + + This method is idempotent. + + This method deletes ALL Skin Scoring App FFQ surveys associated with an + account and source + + This is a hard delete, we REMOVE rows rather than setting a flag + + Parameters + ---------- + account_id : str, UUID + The account UUID + source_id : str, UUID + The source UUID + """ + with self._transaction.cursor() as cur: + existing, _ = \ + self.get_skin_scoring_app_ffq_id_if_exists(account_id, + source_id) + if existing is not None: + cur.execute("""DELETE FROM ag.ag_login_surveys + WHERE ag_login_id=%s + AND source_id=%s + AND survey_id=%s""", + (account_id, source_id, existing)) + cur.execute("""DELETE FROM ag.skin_scoring_app_ffq_registry + WHERE account_id=%s + AND source_id=%s""", + (account_id, source_id)) + def get_vioscreen_sample_to_user(self): """Obtain a mapping of sample barcode to vioscreen user""" with self._transaction.cursor() as cur: @@ -1127,6 +1245,7 @@ def has_external_surveys(self, account_id, source_id): getters = (self.get_myfoodrepo_id_if_exists, self.get_polyphenol_ffq_id_if_exists, self.get_spain_ffq_id_if_exists, + self.get_skin_scoring_app_ffq_id_if_exists, self.get_vioscreen_all_ids_if_exists) for get in getters: @@ -1306,7 +1425,8 @@ def _generate_empty_survey(self, survey_template_id, return_tuple=False): if survey_template_id in [self.VIOSCREEN_ID, self.MYFOODREPO_ID, self.POLYPHENOL_FFQ_ID, - self.SPAIN_FFQ_ID]: + self.SPAIN_FFQ_ID, + self.SKIN_SCORING_APP_FFQ_ID]: raise ValueError("survey_template_id must be for a local " "survey") else: diff --git a/microsetta_private_api/server_config.json b/microsetta_private_api/server_config.json index d49f8a7bd..49d854c6d 100644 --- a/microsetta_private_api/server_config.json +++ b/microsetta_private_api/server_config.json @@ -42,6 +42,7 @@ "fundrazr_organization": "fundrazr_org_placeholder", "polyphenol_ffq_url": "pffq_placeholder", "spain_ffq_url": "sffq_placeholder", + "skin_scoring_app_ffq_url": "ssafq_placeholder", "fulfillment_account_id": "000fc4cd-8fa4-db8b-e050-8a800c5d81b7", "google_geocoding_url": "https://maps.googleapis.com/maps/api/geocode/json", "google_geocoding_key": "geocoding_key_placeholder", diff --git a/microsetta_private_api/util/skin_scoring_app_ffq.py b/microsetta_private_api/util/skin_scoring_app_ffq.py new file mode 100644 index 000000000..675ee5e40 --- /dev/null +++ b/microsetta_private_api/util/skin_scoring_app_ffq.py @@ -0,0 +1,12 @@ +from microsetta_private_api.config_manager import SERVER_CONFIG + + +def gen_ffq_url(skin_scoring_app_ffq_id, study, language_tag): + language_tag = language_tag.lower() + + url = SERVER_CONFIG['skin_scoring_app_ffq_url'] + url += f"?yid={skin_scoring_app_ffq_id}" + url += f"&country={language_tag}" + url += f"&study={study}" + + return url From ff86b4a45291567b70ecf499c7e1b659f6558312 Mon Sep 17 00:00:00 2001 From: ayobi Date: Thu, 19 Sep 2024 23:01:42 -0300 Subject: [PATCH 2/8] removing ffq, study, adding unique code safety --- microsetta_private_api/api/_survey.py | 48 ++++---- microsetta_private_api/api/tests/test_api.py | 4 +- .../api/tests/test_integration.py | 18 +-- microsetta_private_api/db/patches/0143.sql | 11 +- .../repo/survey_template_repo.py | 111 ++++++++++-------- microsetta_private_api/server_config.json | 2 +- .../util/skin_scoring_app.py | 11 ++ .../util/skin_scoring_app_ffq.py | 12 -- 8 files changed, 112 insertions(+), 105 deletions(-) create mode 100644 microsetta_private_api/util/skin_scoring_app.py delete mode 100644 microsetta_private_api/util/skin_scoring_app_ffq.py diff --git a/microsetta_private_api/api/_survey.py b/microsetta_private_api/api/_survey.py index 7c8c010ef..8884c58f5 100644 --- a/microsetta_private_api/api/_survey.py +++ b/microsetta_private_api/api/_survey.py @@ -14,7 +14,7 @@ from microsetta_private_api.repo.transaction import Transaction from microsetta_private_api.repo.vioscreen_repo import VioscreenRepo from microsetta_private_api.util import vioscreen, myfoodrepo, vue_adapter, \ - polyphenol_ffq, skin_scoring_app_ffq + polyphenol_ffq, skin_scoring_app from microsetta_private_api.util.vioscreen import VioscreenAdminAPI from microsetta_private_api.config_manager import SERVER_CONFIG @@ -43,7 +43,7 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): SurveyTemplateRepo.VIOSCREEN_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, SurveyTemplateRepo.SPAIN_FFQ_ID, - SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID, + SurveyTemplateRepo.SKIN_SCORING_APP_ID, SurveyTemplateRepo.BASIC_INFO_ID, SurveyTemplateRepo.AT_HOME_ID, SurveyTemplateRepo.LIFESTYLE_ID, @@ -182,35 +182,30 @@ def _remote_survey_url_spain_ffq(transaction, account_id, source_id): return SERVER_CONFIG['spain_ffq_url'] -def _remote_survey_url_skin_scoring_app_ffq(transaction, - account_id, - source_id, - language_tag): +def _remote_survey_url_skin_scoring_app(transaction, + account_id, + source_id, + language_tag): st_repo = SurveyTemplateRepo(transaction) - skin_scoring_app_ffq_id, study = \ - st_repo.get_skin_scoring_app_ffq_id_if_exists(account_id, - source_id) - - if skin_scoring_app_ffq_id is None: - study = 'SBI' + skin_scoring_app_id = \ + st_repo.get_skin_scoring_app_id_if_exists(account_id, + source_id) + + if skin_scoring_app_id is None: sample_repo = SampleRepo(transaction) samples = sample_repo.get_samples_by_source(account_id, source_id) for s in samples: for s_p in s.sample_projects: if s_p.startswith('SBI'): - study = 'SBI' break - skin_scoring_app_ffq_id = \ - st_repo.create_skin_scoring_app_ffq_entry(account_id, - source_id, - language_tag, - study) - - return skin_scoring_app_ffq.gen_ffq_url(skin_scoring_app_ffq_id, - study, - language_tag) + skin_scoring_app_id = \ + st_repo.create_skin_scoring_app_entry(account_id, + source_id, + language_tag) + return skin_scoring_app.gen_url(skin_scoring_app_id, + language_tag) def read_survey_template(account_id, source_id, survey_template_id, @@ -253,10 +248,11 @@ def read_survey_template(account_id, source_id, survey_template_id, account_id, source_id) elif survey_template_id == \ - SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID: - url = _remote_survey_url_skin_scoring_app_ffq(t, - account_id, - source_id) + SurveyTemplateRepo.SKIN_SCORING_APP_ID: + url = _remote_survey_url_skin_scoring_app(t, + account_id, + source_id, + language_tag) else: raise ValueError(f"Cannot generate URL for survey " f"{survey_template_id}") diff --git a/microsetta_private_api/api/tests/test_api.py b/microsetta_private_api/api/tests/test_api.py index 49d008353..e8641034c 100644 --- a/microsetta_private_api/api/tests/test_api.py +++ b/microsetta_private_api/api/tests/test_api.py @@ -384,8 +384,8 @@ def delete_dummy_accts(): template_repo.delete_polyphenol_ffq(curr_acct_id, curr_source.id) template_repo.delete_spain_ffq(curr_acct_id, curr_source.id) - template_repo.delete_skin_scoring_app_ffq(curr_acct_id, - curr_source.id) + template_repo.delete_skin_scoring_app(curr_acct_id, + curr_source.id) # Dissociate all samples linked to this source from all # answered surveys linked to this source, then delete all diff --git a/microsetta_private_api/api/tests/test_integration.py b/microsetta_private_api/api/tests/test_integration.py index bde22516a..2960df748 100644 --- a/microsetta_private_api/api/tests/test_integration.py +++ b/microsetta_private_api/api/tests/test_integration.py @@ -410,7 +410,7 @@ def test_surveys(self): [SurveyTemplateRepo.VIOSCREEN_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, SurveyTemplateRepo.SPAIN_FFQ_ID, - SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID, + SurveyTemplateRepo.SKIN_SCORING_APP_ID, SurveyTemplateRepo.BASIC_INFO_ID, SurveyTemplateRepo.AT_HOME_ID, SurveyTemplateRepo.LIFESTYLE_ID, @@ -626,13 +626,13 @@ def test_bobo_takes_polyphenol_ffq(self): ) check_response(resp, 404) - @skipIf(SERVER_CONFIG['skin_scoring_app_ffq_url'] in - ('', 'ssafq_placeholder'), - "Skin Scoring App FFQ secrets not provided") - def test_bobo_takes_skin_scoring_app_ffq(self): + @skipIf(SERVER_CONFIG['skin_scoring_app_url'] in + ('', 'ssa_placeholder'), + "Skin Scoring App secrets not provided") + def test_bobo_takes_skin_scoring_app(self): bobo = self._bobo_to_claim_a_sample() - # take Skin Scoring App FFQ + # take Skin Scoring App resp = self.client.get( '/api/accounts/%s/sources/%s/survey_templates/10005' '?language_tag=en_US' % @@ -641,7 +641,7 @@ def test_bobo_takes_skin_scoring_app_ffq(self): ) check_response(resp) data = json.loads(resp.data) - exp_start = SERVER_CONFIG['skin_scoring_app_ffq_url'] + exp_start = SERVER_CONFIG['skin_scoring_app_url'] url = data['survey_template_text']['url'] self.assertTrue(url.startswith(exp_start)) @@ -655,7 +655,7 @@ def test_bobo_takes_skin_scoring_app_ffq(self): data=json.dumps( { "survey_template_id": - SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID, + SurveyTemplateRepo.SKIN_SCORING_APP_ID, "survey_text": {'key': 'stuff'} }), headers=MOCK_HEADERS @@ -730,7 +730,7 @@ def test_bobo_takes_all_local_surveys(self): SurveyTemplateRepo.MYFOODREPO_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, SurveyTemplateRepo.SPAIN_FFQ_ID, - SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID, + SurveyTemplateRepo.SKIN_SCORING_APP_ID, SurveyTemplateRepo.BASIC_INFO_ID, SurveyTemplateRepo.AT_HOME_ID, SurveyTemplateRepo.LIFESTYLE_ID, diff --git a/microsetta_private_api/db/patches/0143.sql b/microsetta_private_api/db/patches/0143.sql index d57405df0..87859411b 100644 --- a/microsetta_private_api/db/patches/0143.sql +++ b/microsetta_private_api/db/patches/0143.sql @@ -1,14 +1,13 @@ -CREATE TABLE ag.skin_scoring_app_ffq_registry ( - skin_scoring_app_ffq_id VARCHAR PRIMARY KEY, +CREATE TABLE ag.skin_scoring_app_registry ( + skin_scoring_app_id VARCHAR PRIMARY KEY, account_id UUID NOT NULL, source_id UUID, language_tag VARCHAR, - study VARCHAR, deleted BOOLEAN NOT NULL DEFAULT false, creation_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(), - CONSTRAINT fk_skin_scoring_app_ffq_registry_account FOREIGN KEY (account_id) REFERENCES ag.account(id), - CONSTRAINT fk_skin_scoring_app_ffq_registry_source FOREIGN KEY (source_id) REFERENCES ag.source(id) + CONSTRAINT fk_skin_scoring_app_registry_account FOREIGN KEY (account_id) REFERENCES ag.account(id), + CONSTRAINT fk_skin_scoring_app_registry_source FOREIGN KEY (source_id) REFERENCES ag.source(id) ); -CREATE INDEX skin_scoring_app_ffq_registry_source ON ag.skin_scoring_app_ffq_registry (account_id, source_id); \ No newline at end of file +CREATE INDEX skin_scoring_app_registry_source ON ag.skin_scoring_app_registry (account_id, source_id); \ No newline at end of file diff --git a/microsetta_private_api/repo/survey_template_repo.py b/microsetta_private_api/repo/survey_template_repo.py index e444299ba..c2086d48a 100644 --- a/microsetta_private_api/repo/survey_template_repo.py +++ b/microsetta_private_api/repo/survey_template_repo.py @@ -1,5 +1,6 @@ import random import string +import psycopg2 from werkzeug.exceptions import NotFound from microsetta_private_api.config_manager import SERVER_CONFIG @@ -25,7 +26,7 @@ class SurveyTemplateRepo(BaseRepo): MYFOODREPO_ID = 10002 POLYPHENOL_FFQ_ID = 10003 SPAIN_FFQ_ID = 10004 - SKIN_SCORING_APP_FFQ_ID = 10005 + SKIN_SCORING_APP_ID = 10005 BASIC_INFO_ID = 10 AT_HOME_ID = 11 LIFESTYLE_ID = 12 @@ -39,6 +40,7 @@ class SurveyTemplateRepo(BaseRepo): SURFERS_ID = 20 COVID19_ID = 21 OTHER_ID = 22 + SBI_PROJECT_ID = 23 SURVEY_INFO = { # For now, let's keep legacy survey info as well. @@ -108,9 +110,9 @@ class SurveyTemplateRepo(BaseRepo): "1.0", "remote" ), - SKIN_SCORING_APP_FFQ_ID: SurveyTemplateLinkInfo( - SKIN_SCORING_APP_FFQ_ID, - "Skin Scoring App FFQ", + SKIN_SCORING_APP_ID: SurveyTemplateLinkInfo( + SKIN_SCORING_APP_ID, + "Skin Scoring App", "1.0", "remote" ), @@ -764,12 +766,11 @@ def delete_spain_ffq(self, account_id, source_id): AND source_id=%s""", (account_id, source_id)) - def create_skin_scoring_app_ffq_entry(self, - account_id, - source_id, - language_tag, - study): - """Return a newly created Skin Scoring App FFQ ID + def create_skin_scoring_app_entry(self, + account_id, + source_id, + language_tag,): + """Return a newly created Skin Scoring App ID Parameters ---------- @@ -779,42 +780,54 @@ def create_skin_scoring_app_ffq_entry(self, The source UUID language_tag: str The user's language tag - study: str - The study variable we'll pass to L'Oréal's FFQ Returns ------- str - The newly created Skin Scoring App FFQ ID + The newly created Skin Scoring App ID """ characters = string.ascii_lowercase + string.digits - skin_scoring_app_ffq_id = ''.join(random.choices(characters, k=8)) - with self._transaction.cursor() as cur: - cur.execute("""INSERT INTO ag.skin_scoring_app_ffq_registry - (skin_scoring_app_ffq_id, account_id, - source_id, language_tag, study) - VALUES (%s, %s, %s, %s, %s)""", - (skin_scoring_app_ffq_id, account_id, - source_id, language_tag, study)) + for attempt in range(5): + skin_scoring_app_id = ''.join(random.choices(characters, k=8)) + + try: + with self._transaction.cursor() as cur: + cur.execute("""INSERT INTO ag.skin_scoring_app_registry + (skin_scoring_app_id, account_id, + source_id, language_tag) + VALUES (%s, %s, %s, %s)""", + (skin_scoring_app_id, account_id, + source_id, language_tag)) + + # Put a survey into ag_login_surveys + cur.execute("INSERT INTO ag_login_surveys(" + "ag_login_id, " + "survey_id, " + "vioscreen_status, " + "source_id, " + "survey_template_id) " + "VALUES(%s, %s, %s, %s, %s)", + (account_id, skin_scoring_app_id, None, source_id, + SurveyTemplateRepo.SKIN_SCORING_APP_ID)) + + return skin_scoring_app_id + except psycopg2.IntegrityError as e: + self._transaction.rollback() + + if e.pgcode == '23505': + print(f"Duplicate ID '{skin_scoring_app_id}' " + f"detected. Retrying... (Attempt {attempt + 1}/{5})") + else: + raise - # Put a survey into ag_login_surveys - cur.execute("INSERT INTO ag_login_surveys(" - "ag_login_id, " - "survey_id, " - "vioscreen_status, " - "source_id, " - "survey_template_id) " - "VALUES(%s, %s, %s, %s, %s)", - (account_id, skin_scoring_app_ffq_id, None, source_id, - SurveyTemplateRepo.SKIN_SCORING_APP_FFQ_ID)) + raise Exception(f"Unable to generate a unique Skin Scoring App ID after 5 attempts") - return skin_scoring_app_ffq_id - def get_skin_scoring_app_ffq_id_if_exists(self, - account_id, - source_id): - """Return a Skin Scoring App FFQ ID if one exists + def get_skin_scoring_app_id_if_exists(self, + account_id, + source_id): + """Return a Skin Scoring App ID if one exists Parameters ---------- @@ -825,28 +838,28 @@ def get_skin_scoring_app_ffq_id_if_exists(self, Returns ------- - (UUID, str) or (None, None) - The associated Skin Scoring App FFQ ID and study + (str) or (None) + The associated Skin Scoring App ID It's impossible to find one without the other """ with self._transaction.cursor() as cur: - cur.execute("""SELECT skin_scoring_app_ffq_id, study - FROM ag.skin_scoring_app_ffq_registry + cur.execute("""SELECT skin_scoring_app_id + FROM ag.skin_scoring_app_registry WHERE account_id=%s AND source_id=%s""", (account_id, source_id)) res = cur.fetchone() if res is None: - return (None, None) + return None else: return res - def delete_skin_scoring_app_ffq(self, account_id, source_id): - """Intended for admin use, remove Skin Scoring App FFQ entries + def delete_skin_scoring_app(self, account_id, source_id): + """Intended for admin use, remove Skin Scoring App entries This method is idempotent. - This method deletes ALL Skin Scoring App FFQ surveys associated with an + This method deletes ALL Skin Scoring App surveys associated with an account and source This is a hard delete, we REMOVE rows rather than setting a flag @@ -860,15 +873,15 @@ def delete_skin_scoring_app_ffq(self, account_id, source_id): """ with self._transaction.cursor() as cur: existing, _ = \ - self.get_skin_scoring_app_ffq_id_if_exists(account_id, - source_id) + self.get_skin_scoring_app_id_if_exists(account_id, + source_id) if existing is not None: cur.execute("""DELETE FROM ag.ag_login_surveys WHERE ag_login_id=%s AND source_id=%s AND survey_id=%s""", (account_id, source_id, existing)) - cur.execute("""DELETE FROM ag.skin_scoring_app_ffq_registry + cur.execute("""DELETE FROM ag.skin_scoring_app_registry WHERE account_id=%s AND source_id=%s""", (account_id, source_id)) @@ -1245,7 +1258,7 @@ def has_external_surveys(self, account_id, source_id): getters = (self.get_myfoodrepo_id_if_exists, self.get_polyphenol_ffq_id_if_exists, self.get_spain_ffq_id_if_exists, - self.get_skin_scoring_app_ffq_id_if_exists, + self.get_skin_scoring_app_id_if_exists, self.get_vioscreen_all_ids_if_exists) for get in getters: @@ -1426,7 +1439,7 @@ def _generate_empty_survey(self, survey_template_id, return_tuple=False): self.MYFOODREPO_ID, self.POLYPHENOL_FFQ_ID, self.SPAIN_FFQ_ID, - self.SKIN_SCORING_APP_FFQ_ID]: + self.SKIN_SCORING_APP_ID]: raise ValueError("survey_template_id must be for a local " "survey") else: diff --git a/microsetta_private_api/server_config.json b/microsetta_private_api/server_config.json index 49d854c6d..2f7cc0420 100644 --- a/microsetta_private_api/server_config.json +++ b/microsetta_private_api/server_config.json @@ -42,7 +42,7 @@ "fundrazr_organization": "fundrazr_org_placeholder", "polyphenol_ffq_url": "pffq_placeholder", "spain_ffq_url": "sffq_placeholder", - "skin_scoring_app_ffq_url": "ssafq_placeholder", + "skin_scoring_app_url": "ssa_placeholder", "fulfillment_account_id": "000fc4cd-8fa4-db8b-e050-8a800c5d81b7", "google_geocoding_url": "https://maps.googleapis.com/maps/api/geocode/json", "google_geocoding_key": "geocoding_key_placeholder", diff --git a/microsetta_private_api/util/skin_scoring_app.py b/microsetta_private_api/util/skin_scoring_app.py new file mode 100644 index 000000000..77eeb0841 --- /dev/null +++ b/microsetta_private_api/util/skin_scoring_app.py @@ -0,0 +1,11 @@ +from microsetta_private_api.config_manager import SERVER_CONFIG + + +def gen_url(skin_scoring_app_id, language_tag): + language_tag = language_tag.lower() + + url = SERVER_CONFIG['skin_scoring_app_url'] + url += f"?yid={skin_scoring_app_id}" + url += f"&country={language_tag}" + + return url diff --git a/microsetta_private_api/util/skin_scoring_app_ffq.py b/microsetta_private_api/util/skin_scoring_app_ffq.py deleted file mode 100644 index 675ee5e40..000000000 --- a/microsetta_private_api/util/skin_scoring_app_ffq.py +++ /dev/null @@ -1,12 +0,0 @@ -from microsetta_private_api.config_manager import SERVER_CONFIG - - -def gen_ffq_url(skin_scoring_app_ffq_id, study, language_tag): - language_tag = language_tag.lower() - - url = SERVER_CONFIG['skin_scoring_app_ffq_url'] - url += f"?yid={skin_scoring_app_ffq_id}" - url += f"&country={language_tag}" - url += f"&study={study}" - - return url From c54d86a74756308adb7845accd92eef13b375fed Mon Sep 17 00:00:00 2001 From: ayobi Date: Thu, 19 Sep 2024 23:04:10 -0300 Subject: [PATCH 3/8] lint --- microsetta_private_api/api/_survey.py | 2 +- microsetta_private_api/repo/survey_template_repo.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/microsetta_private_api/api/_survey.py b/microsetta_private_api/api/_survey.py index 8884c58f5..6bb60877a 100644 --- a/microsetta_private_api/api/_survey.py +++ b/microsetta_private_api/api/_survey.py @@ -191,7 +191,7 @@ def _remote_survey_url_skin_scoring_app(transaction, skin_scoring_app_id = \ st_repo.get_skin_scoring_app_id_if_exists(account_id, source_id) - + if skin_scoring_app_id is None: sample_repo = SampleRepo(transaction) samples = sample_repo.get_samples_by_source(account_id, source_id) diff --git a/microsetta_private_api/repo/survey_template_repo.py b/microsetta_private_api/repo/survey_template_repo.py index c2086d48a..2da10350c 100644 --- a/microsetta_private_api/repo/survey_template_repo.py +++ b/microsetta_private_api/repo/survey_template_repo.py @@ -808,7 +808,8 @@ def create_skin_scoring_app_entry(self, "source_id, " "survey_template_id) " "VALUES(%s, %s, %s, %s, %s)", - (account_id, skin_scoring_app_id, None, source_id, + (account_id, skin_scoring_app_id, None, + source_id, SurveyTemplateRepo.SKIN_SCORING_APP_ID)) return skin_scoring_app_id @@ -821,8 +822,8 @@ def create_skin_scoring_app_entry(self, else: raise - raise Exception(f"Unable to generate a unique Skin Scoring App ID after 5 attempts") - + raise Exception("Unable to generate a unique " + "Skin Scoring App ID after 5 attempts") def get_skin_scoring_app_id_if_exists(self, account_id, From 2791b5487b181ffc047b9cb487e43100af47f806 Mon Sep 17 00:00:00 2001 From: ayobi Date: Fri, 20 Sep 2024 21:08:34 -0300 Subject: [PATCH 4/8] filter read survey template based on skin project --- microsetta_private_api/api/_survey.py | 98 ++++++++++++------- .../api/tests/test_integration.py | 2 - .../repo/survey_template_repo.py | 3 +- 3 files changed, 65 insertions(+), 38 deletions(-) diff --git a/microsetta_private_api/api/_survey.py b/microsetta_private_api/api/_survey.py index 6bb60877a..5225d723e 100644 --- a/microsetta_private_api/api/_survey.py +++ b/microsetta_private_api/api/_survey.py @@ -32,34 +32,71 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): # survey_group = group_order; with Transaction() as t: - source_repo = SourceRepo(t) - source = source_repo.get_source(account_id, source_id) - if source is None: - return jsonify(code=404, message="No source found"), 404 - template_repo = SurveyTemplateRepo(t) - if source.source_type == Source.SOURCE_TYPE_HUMAN: - return jsonify([template_repo.get_survey_template_link_info(x) - for x in [ - SurveyTemplateRepo.VIOSCREEN_ID, - SurveyTemplateRepo.POLYPHENOL_FFQ_ID, - SurveyTemplateRepo.SPAIN_FFQ_ID, - SurveyTemplateRepo.SKIN_SCORING_APP_ID, - SurveyTemplateRepo.BASIC_INFO_ID, - SurveyTemplateRepo.AT_HOME_ID, - SurveyTemplateRepo.LIFESTYLE_ID, - SurveyTemplateRepo.GUT_ID, - SurveyTemplateRepo.GENERAL_HEALTH_ID, - SurveyTemplateRepo.HEALTH_DIAG_ID, - SurveyTemplateRepo.ALLERGIES_ID, - SurveyTemplateRepo.DIET_ID, - SurveyTemplateRepo.DETAILED_DIET_ID, - SurveyTemplateRepo.OTHER_ID - ]]), 200 - elif source.source_type == Source.SOURCE_TYPE_ANIMAL: - return jsonify([template_repo.get_survey_template_link_info(x) - for x in [2]]), 200 + # Checking samples to see any have permission to + # see any skin related surveys + sample_repo = SampleRepo(t) + samples = sample_repo.get_samples_by_source(account_id, source_id) + for s in samples: + for skin_samples in s.sample_projects: + if skin_samples.startswith('Skin'): + break + if samples: + source_repo = SourceRepo(t) + source = source_repo.get_source(account_id, source_id) + if source is None: + return jsonify(code=404, message="No source found"), 404 + template_repo = SurveyTemplateRepo(t) + if source.source_type == Source.SOURCE_TYPE_HUMAN: + return jsonify([template_repo.get_survey_template_link_info(x) + for x in [ + SurveyTemplateRepo.VIOSCREEN_ID, + SurveyTemplateRepo.POLYPHENOL_FFQ_ID, + SurveyTemplateRepo.SPAIN_FFQ_ID, + SurveyTemplateRepo.SKIN_SCORING_APP_ID, + SurveyTemplateRepo.BASIC_INFO_ID, + SurveyTemplateRepo.AT_HOME_ID, + SurveyTemplateRepo.LIFESTYLE_ID, + SurveyTemplateRepo.GUT_ID, + SurveyTemplateRepo.GENERAL_HEALTH_ID, + SurveyTemplateRepo.HEALTH_DIAG_ID, + SurveyTemplateRepo.ALLERGIES_ID, + SurveyTemplateRepo.DIET_ID, + SurveyTemplateRepo.DETAILED_DIET_ID, + SurveyTemplateRepo.OTHER_ID + ]]), 200 + elif source.source_type == Source.SOURCE_TYPE_ANIMAL: + return jsonify([template_repo.get_survey_template_link_info(x) + for x in [2]]), 200 + else: + return jsonify([]), 200 else: - return jsonify([]), 200 + source_repo = SourceRepo(t) + source = source_repo.get_source(account_id, source_id) + if source is None: + return jsonify(code=404, message="No source found"), 404 + template_repo = SurveyTemplateRepo(t) + if source.source_type == Source.SOURCE_TYPE_HUMAN: + return jsonify([template_repo.get_survey_template_link_info(x) + for x in [ + SurveyTemplateRepo.VIOSCREEN_ID, + SurveyTemplateRepo.POLYPHENOL_FFQ_ID, + SurveyTemplateRepo.SPAIN_FFQ_ID, + SurveyTemplateRepo.BASIC_INFO_ID, + SurveyTemplateRepo.AT_HOME_ID, + SurveyTemplateRepo.LIFESTYLE_ID, + SurveyTemplateRepo.GUT_ID, + SurveyTemplateRepo.GENERAL_HEALTH_ID, + SurveyTemplateRepo.HEALTH_DIAG_ID, + SurveyTemplateRepo.ALLERGIES_ID, + SurveyTemplateRepo.DIET_ID, + SurveyTemplateRepo.DETAILED_DIET_ID, + SurveyTemplateRepo.OTHER_ID + ]]), 200 + elif source.source_type == Source.SOURCE_TYPE_ANIMAL: + return jsonify([template_repo.get_survey_template_link_info(x) + for x in [2]]), 200 + else: + return jsonify([]), 200 def _remote_survey_url_vioscreen(transaction, account_id, source_id, @@ -193,13 +230,6 @@ def _remote_survey_url_skin_scoring_app(transaction, source_id) if skin_scoring_app_id is None: - sample_repo = SampleRepo(transaction) - samples = sample_repo.get_samples_by_source(account_id, source_id) - for s in samples: - for s_p in s.sample_projects: - if s_p.startswith('SBI'): - break - skin_scoring_app_id = \ st_repo.create_skin_scoring_app_entry(account_id, source_id, diff --git a/microsetta_private_api/api/tests/test_integration.py b/microsetta_private_api/api/tests/test_integration.py index 2960df748..4ebd2fd5d 100644 --- a/microsetta_private_api/api/tests/test_integration.py +++ b/microsetta_private_api/api/tests/test_integration.py @@ -410,7 +410,6 @@ def test_surveys(self): [SurveyTemplateRepo.VIOSCREEN_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, SurveyTemplateRepo.SPAIN_FFQ_ID, - SurveyTemplateRepo.SKIN_SCORING_APP_ID, SurveyTemplateRepo.BASIC_INFO_ID, SurveyTemplateRepo.AT_HOME_ID, SurveyTemplateRepo.LIFESTYLE_ID, @@ -730,7 +729,6 @@ def test_bobo_takes_all_local_surveys(self): SurveyTemplateRepo.MYFOODREPO_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, SurveyTemplateRepo.SPAIN_FFQ_ID, - SurveyTemplateRepo.SKIN_SCORING_APP_ID, SurveyTemplateRepo.BASIC_INFO_ID, SurveyTemplateRepo.AT_HOME_ID, SurveyTemplateRepo.LIFESTYLE_ID, diff --git a/microsetta_private_api/repo/survey_template_repo.py b/microsetta_private_api/repo/survey_template_repo.py index 2da10350c..fb7537810 100644 --- a/microsetta_private_api/repo/survey_template_repo.py +++ b/microsetta_private_api/repo/survey_template_repo.py @@ -40,7 +40,6 @@ class SurveyTemplateRepo(BaseRepo): SURFERS_ID = 20 COVID19_ID = 21 OTHER_ID = 22 - SBI_PROJECT_ID = 23 SURVEY_INFO = { # For now, let's keep legacy survey info as well. @@ -873,7 +872,7 @@ def delete_skin_scoring_app(self, account_id, source_id): The source UUID """ with self._transaction.cursor() as cur: - existing, _ = \ + existing = \ self.get_skin_scoring_app_id_if_exists(account_id, source_id) if existing is not None: From a40b3760a834f882fa887aa96602f19685ea67a6 Mon Sep 17 00:00:00 2001 From: ayobi Date: Sun, 22 Sep 2024 16:55:04 -0300 Subject: [PATCH 5/8] reduce repeated code --- microsetta_private_api/api/_survey.py | 98 +++++++++++---------------- 1 file changed, 38 insertions(+), 60 deletions(-) diff --git a/microsetta_private_api/api/_survey.py b/microsetta_private_api/api/_survey.py index 5225d723e..7d01decbb 100644 --- a/microsetta_private_api/api/_survey.py +++ b/microsetta_private_api/api/_survey.py @@ -36,67 +36,45 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): # see any skin related surveys sample_repo = SampleRepo(t) samples = sample_repo.get_samples_by_source(account_id, source_id) - for s in samples: - for skin_samples in s.sample_projects: - if skin_samples.startswith('Skin'): - break - if samples: - source_repo = SourceRepo(t) - source = source_repo.get_source(account_id, source_id) - if source is None: - return jsonify(code=404, message="No source found"), 404 - template_repo = SurveyTemplateRepo(t) - if source.source_type == Source.SOURCE_TYPE_HUMAN: - return jsonify([template_repo.get_survey_template_link_info(x) - for x in [ - SurveyTemplateRepo.VIOSCREEN_ID, - SurveyTemplateRepo.POLYPHENOL_FFQ_ID, - SurveyTemplateRepo.SPAIN_FFQ_ID, - SurveyTemplateRepo.SKIN_SCORING_APP_ID, - SurveyTemplateRepo.BASIC_INFO_ID, - SurveyTemplateRepo.AT_HOME_ID, - SurveyTemplateRepo.LIFESTYLE_ID, - SurveyTemplateRepo.GUT_ID, - SurveyTemplateRepo.GENERAL_HEALTH_ID, - SurveyTemplateRepo.HEALTH_DIAG_ID, - SurveyTemplateRepo.ALLERGIES_ID, - SurveyTemplateRepo.DIET_ID, - SurveyTemplateRepo.DETAILED_DIET_ID, - SurveyTemplateRepo.OTHER_ID - ]]), 200 - elif source.source_type == Source.SOURCE_TYPE_ANIMAL: - return jsonify([template_repo.get_survey_template_link_info(x) - for x in [2]]), 200 - else: - return jsonify([]), 200 + + has_skin_sample = any(skin_samples.startswith('Skin') + for s in samples + for skin_samples in s.sample_projects) + + source_repo = SourceRepo(t) + source = source_repo.get_source(account_id, source_id) + + if source is None: + return jsonify(code=404, message="No source found"), 404 + + template_repo = SurveyTemplateRepo(t) + + if source.source_type == Source.SOURCE_TYPE_HUMAN: + template_ids = [ + SurveyTemplateRepo.VIOSCREEN_ID, + SurveyTemplateRepo.POLYPHENOL_FFQ_ID, + SurveyTemplateRepo.SPAIN_FFQ_ID, + SurveyTemplateRepo.BASIC_INFO_ID, + SurveyTemplateRepo.AT_HOME_ID, + SurveyTemplateRepo.LIFESTYLE_ID, + SurveyTemplateRepo.GUT_ID, + SurveyTemplateRepo.GENERAL_HEALTH_ID, + SurveyTemplateRepo.HEALTH_DIAG_ID, + SurveyTemplateRepo.ALLERGIES_ID, + SurveyTemplateRepo.DIET_ID, + SurveyTemplateRepo.DETAILED_DIET_ID, + SurveyTemplateRepo.OTHER_ID + ] + if has_skin_sample: + template_ids.append(SurveyTemplateRepo.SKIN_SCORING_APP_ID) + + elif source.source_type == Source.SOURCE_TYPE_ANIMAL: + template_ids = [2] else: - source_repo = SourceRepo(t) - source = source_repo.get_source(account_id, source_id) - if source is None: - return jsonify(code=404, message="No source found"), 404 - template_repo = SurveyTemplateRepo(t) - if source.source_type == Source.SOURCE_TYPE_HUMAN: - return jsonify([template_repo.get_survey_template_link_info(x) - for x in [ - SurveyTemplateRepo.VIOSCREEN_ID, - SurveyTemplateRepo.POLYPHENOL_FFQ_ID, - SurveyTemplateRepo.SPAIN_FFQ_ID, - SurveyTemplateRepo.BASIC_INFO_ID, - SurveyTemplateRepo.AT_HOME_ID, - SurveyTemplateRepo.LIFESTYLE_ID, - SurveyTemplateRepo.GUT_ID, - SurveyTemplateRepo.GENERAL_HEALTH_ID, - SurveyTemplateRepo.HEALTH_DIAG_ID, - SurveyTemplateRepo.ALLERGIES_ID, - SurveyTemplateRepo.DIET_ID, - SurveyTemplateRepo.DETAILED_DIET_ID, - SurveyTemplateRepo.OTHER_ID - ]]), 200 - elif source.source_type == Source.SOURCE_TYPE_ANIMAL: - return jsonify([template_repo.get_survey_template_link_info(x) - for x in [2]]), 200 - else: - return jsonify([]), 200 + template_ids = [] + + return jsonify([template_repo.get_survey_template_link_info(x) + for x in template_ids]), 200 def _remote_survey_url_vioscreen(transaction, account_id, source_id, From fea91e03e4d9979fdebf04a5e6432293bee1ed65 Mon Sep 17 00:00:00 2001 From: ayobi Date: Mon, 23 Sep 2024 12:06:06 -0300 Subject: [PATCH 6/8] handle none --- microsetta_private_api/api/_survey.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/microsetta_private_api/api/_survey.py b/microsetta_private_api/api/_survey.py index 7d01decbb..60bd53511 100644 --- a/microsetta_private_api/api/_survey.py +++ b/microsetta_private_api/api/_survey.py @@ -36,10 +36,13 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): # see any skin related surveys sample_repo = SampleRepo(t) samples = sample_repo.get_samples_by_source(account_id, source_id) - - has_skin_sample = any(skin_samples.startswith('Skin') - for s in samples - for skin_samples in s.sample_projects) + if samples is not None: + has_skin_sample = any(skin_samples is not None and + skin_samples.startswith('Skin') + for s in samples + for skin_samples in s.sample_projects) + else: + has_skin_sample = False source_repo = SourceRepo(t) source = source_repo.get_source(account_id, source_id) @@ -72,6 +75,7 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): template_ids = [2] else: template_ids = [] + print("temp ids", template_ids) return jsonify([template_repo.get_survey_template_link_info(x) for x in template_ids]), 200 From 19473d293d82cfc8f3e16375187632dc87e3896a Mon Sep 17 00:00:00 2001 From: ayobi Date: Mon, 23 Sep 2024 14:12:44 -0300 Subject: [PATCH 7/8] use sbi project id instead of text search/some refactor --- microsetta_private_api/api/_survey.py | 21 +++++++------------ microsetta_private_api/repo/sample_repo.py | 14 +++++++++++++ .../repo/survey_template_repo.py | 1 + 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/microsetta_private_api/api/_survey.py b/microsetta_private_api/api/_survey.py index 60bd53511..b3562da7c 100644 --- a/microsetta_private_api/api/_survey.py +++ b/microsetta_private_api/api/_survey.py @@ -32,18 +32,6 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): # survey_group = group_order; with Transaction() as t: - # Checking samples to see any have permission to - # see any skin related surveys - sample_repo = SampleRepo(t) - samples = sample_repo.get_samples_by_source(account_id, source_id) - if samples is not None: - has_skin_sample = any(skin_samples is not None and - skin_samples.startswith('Skin') - for s in samples - for skin_samples in s.sample_projects) - else: - has_skin_sample = False - source_repo = SourceRepo(t) source = source_repo.get_source(account_id, source_id) @@ -53,6 +41,14 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): template_repo = SurveyTemplateRepo(t) if source.source_type == Source.SOURCE_TYPE_HUMAN: + # Checking samples to see if any have + # permission to see Skin Scoring App survey + sample_repo = SampleRepo(t) + samples = sample_repo.get_samples_by_source(account_id, source_id) + has_skin_sample = any( + s.project_id == SurveyTemplateRepo.SBI_PROJECT_ID + for s in samples) if samples else False + template_ids = [ SurveyTemplateRepo.VIOSCREEN_ID, SurveyTemplateRepo.POLYPHENOL_FFQ_ID, @@ -75,7 +71,6 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): template_ids = [2] else: template_ids = [] - print("temp ids", template_ids) return jsonify([template_repo.get_survey_template_link_info(x) for x in template_ids]), 200 diff --git a/microsetta_private_api/repo/sample_repo.py b/microsetta_private_api/repo/sample_repo.py index 64c361b87..694cf5aa0 100644 --- a/microsetta_private_api/repo/sample_repo.py +++ b/microsetta_private_api/repo/sample_repo.py @@ -258,6 +258,9 @@ def get_samples_by_source(self, account_id, source_id, sample.kit_id = self._get_supplied_kit_id_by_sample( sample.barcode ) + sample.project_id = self._get_project_id_by_sample( + sample.barcode + ) samples.append(sample) return samples @@ -404,6 +407,17 @@ def _get_supplied_kit_id_by_sample(self, sample_barcode): row = cur.fetchone() return row[0] + def _get_project_id_by_sample(self, sample_barcode): + with self._transaction.cursor() as cur: + cur.execute( + "SELECT project_id " + "FROM barcodes.project_barcode " + "WHERE barcode = %s", + (sample_barcode, ) + ) + row = cur.fetchone() + return row[0] + def scrub(self, account_id, source_id, sample_id): """Wipe out free text information for a sample diff --git a/microsetta_private_api/repo/survey_template_repo.py b/microsetta_private_api/repo/survey_template_repo.py index fb7537810..85325b80b 100644 --- a/microsetta_private_api/repo/survey_template_repo.py +++ b/microsetta_private_api/repo/survey_template_repo.py @@ -40,6 +40,7 @@ class SurveyTemplateRepo(BaseRepo): SURFERS_ID = 20 COVID19_ID = 21 OTHER_ID = 22 + SBI_PROJECT_ID = 58 SURVEY_INFO = { # For now, let's keep legacy survey info as well. From 7698a490f8fea51ce6367da21703232aa4ae74f0 Mon Sep 17 00:00:00 2001 From: ayobi Date: Tue, 24 Sep 2024 17:20:26 -0300 Subject: [PATCH 8/8] changes based on feedback/added tests --- microsetta_private_api/api/_survey.py | 10 ++-- microsetta_private_api/api/tests/test_api.py | 2 - microsetta_private_api/repo/sample_repo.py | 10 ++-- .../repo/survey_template_repo.py | 49 ++----------------- .../repo/tests/test_sample.py | 24 +++++++++ .../repo/tests/test_survey_template_repo.py | 35 +++++++++++++ 6 files changed, 76 insertions(+), 54 deletions(-) diff --git a/microsetta_private_api/api/_survey.py b/microsetta_private_api/api/_survey.py index b3562da7c..4c3c57fec 100644 --- a/microsetta_private_api/api/_survey.py +++ b/microsetta_private_api/api/_survey.py @@ -45,9 +45,13 @@ def read_survey_templates(account_id, source_id, language_tag, token_info): # permission to see Skin Scoring App survey sample_repo = SampleRepo(t) samples = sample_repo.get_samples_by_source(account_id, source_id) - has_skin_sample = any( - s.project_id == SurveyTemplateRepo.SBI_PROJECT_ID - for s in samples) if samples else False + if samples: + has_skin_sample = any( + SurveyTemplateRepo.SBI_PROJECT_ID + in s.project_id for s in samples + ) + else: + has_skin_sample = False template_ids = [ SurveyTemplateRepo.VIOSCREEN_ID, diff --git a/microsetta_private_api/api/tests/test_api.py b/microsetta_private_api/api/tests/test_api.py index e8641034c..d65b8bb5c 100644 --- a/microsetta_private_api/api/tests/test_api.py +++ b/microsetta_private_api/api/tests/test_api.py @@ -384,8 +384,6 @@ def delete_dummy_accts(): template_repo.delete_polyphenol_ffq(curr_acct_id, curr_source.id) template_repo.delete_spain_ffq(curr_acct_id, curr_source.id) - template_repo.delete_skin_scoring_app(curr_acct_id, - curr_source.id) # Dissociate all samples linked to this source from all # answered surveys linked to this source, then delete all diff --git a/microsetta_private_api/repo/sample_repo.py b/microsetta_private_api/repo/sample_repo.py index 694cf5aa0..a08276f71 100644 --- a/microsetta_private_api/repo/sample_repo.py +++ b/microsetta_private_api/repo/sample_repo.py @@ -258,7 +258,7 @@ def get_samples_by_source(self, account_id, source_id, sample.kit_id = self._get_supplied_kit_id_by_sample( sample.barcode ) - sample.project_id = self._get_project_id_by_sample( + sample.project_id = self._get_project_ids_by_sample( sample.barcode ) samples.append(sample) @@ -407,7 +407,7 @@ def _get_supplied_kit_id_by_sample(self, sample_barcode): row = cur.fetchone() return row[0] - def _get_project_id_by_sample(self, sample_barcode): + def _get_project_ids_by_sample(self, sample_barcode): with self._transaction.cursor() as cur: cur.execute( "SELECT project_id " @@ -415,8 +415,10 @@ def _get_project_id_by_sample(self, sample_barcode): "WHERE barcode = %s", (sample_barcode, ) ) - row = cur.fetchone() - return row[0] + rows = cur.fetchall() + + project_ids = [row[0] for row in rows] + return project_ids def scrub(self, account_id, source_id, sample_id): """Wipe out free text information for a sample diff --git a/microsetta_private_api/repo/survey_template_repo.py b/microsetta_private_api/repo/survey_template_repo.py index 85325b80b..a8d906739 100644 --- a/microsetta_private_api/repo/survey_template_repo.py +++ b/microsetta_private_api/repo/survey_template_repo.py @@ -788,7 +788,7 @@ def create_skin_scoring_app_entry(self, """ characters = string.ascii_lowercase + string.digits - for attempt in range(5): + while True: skin_scoring_app_id = ''.join(random.choices(characters, k=8)) try: @@ -810,21 +810,12 @@ def create_skin_scoring_app_entry(self, "VALUES(%s, %s, %s, %s, %s)", (account_id, skin_scoring_app_id, None, source_id, - SurveyTemplateRepo.SKIN_SCORING_APP_ID)) + SurveyTemplateRepo.SKIN_SCORING_APP_ID)) return skin_scoring_app_id - except psycopg2.IntegrityError as e: + except psycopg2.IntegrityError: self._transaction.rollback() - if e.pgcode == '23505': - print(f"Duplicate ID '{skin_scoring_app_id}' " - f"detected. Retrying... (Attempt {attempt + 1}/{5})") - else: - raise - - raise Exception("Unable to generate a unique " - "Skin Scoring App ID after 5 attempts") - def get_skin_scoring_app_id_if_exists(self, account_id, source_id): @@ -853,39 +844,7 @@ def get_skin_scoring_app_id_if_exists(self, if res is None: return None else: - return res - - def delete_skin_scoring_app(self, account_id, source_id): - """Intended for admin use, remove Skin Scoring App entries - - This method is idempotent. - - This method deletes ALL Skin Scoring App surveys associated with an - account and source - - This is a hard delete, we REMOVE rows rather than setting a flag - - Parameters - ---------- - account_id : str, UUID - The account UUID - source_id : str, UUID - The source UUID - """ - with self._transaction.cursor() as cur: - existing = \ - self.get_skin_scoring_app_id_if_exists(account_id, - source_id) - if existing is not None: - cur.execute("""DELETE FROM ag.ag_login_surveys - WHERE ag_login_id=%s - AND source_id=%s - AND survey_id=%s""", - (account_id, source_id, existing)) - cur.execute("""DELETE FROM ag.skin_scoring_app_registry - WHERE account_id=%s - AND source_id=%s""", - (account_id, source_id)) + return res[0] def get_vioscreen_sample_to_user(self): """Obtain a mapping of sample barcode to vioscreen user""" diff --git a/microsetta_private_api/repo/tests/test_sample.py b/microsetta_private_api/repo/tests/test_sample.py index e917ed439..5793033d1 100644 --- a/microsetta_private_api/repo/tests/test_sample.py +++ b/microsetta_private_api/repo/tests/test_sample.py @@ -168,6 +168,30 @@ def test_get_supplied_kit_id_by_sample(self): ) self.assertEqual(kit_id, supplied_kit_id) + def test_get_project_ids_by_sample(self): + with Transaction() as t: + # First we'll create a kit so that we have a kit_id/barcode combo + # to test with + admin_repo = AdminRepo(t) + res = admin_repo.create_kits( + 1, + 1, + "UNITTEST", + [1, 2] + ) + + # Extract the info from results. We know we created 1 kit with 1 + # sample so we don't need to iterate + kit_info = res['created'][0] + sample_barcode = kit_info['sample_barcodes'][0] + + # Verify that the function returns the correct project_id + sample_repo = SampleRepo(t) + project_ids = sample_repo._get_project_ids_by_sample( + sample_barcode + ) + self.assertEqual([1, 2], project_ids) + if __name__ == '__main__': unittest.main() diff --git a/microsetta_private_api/repo/tests/test_survey_template_repo.py b/microsetta_private_api/repo/tests/test_survey_template_repo.py index 1ed86cfc1..94bb6bd7d 100644 --- a/microsetta_private_api/repo/tests/test_survey_template_repo.py +++ b/microsetta_private_api/repo/tests/test_survey_template_repo.py @@ -395,6 +395,41 @@ def test_get_spain_ffq_id_if_exists_false(self): TEST1_SOURCE_ID) self.assertEqual(obs, None) + def test_create_skin_scoring_app_entry_valid(self): + with Transaction() as t: + template_repo = SurveyTemplateRepo(t) + obs = template_repo.create_skin_scoring_app_entry(TEST1_ACCOUNT_ID, + TEST1_SOURCE_ID, + 'en_US') + self.assertEqual(len(obs), 8) + + def test_create_skin_scoring_app_entry_invalid(self): + with Transaction() as t: + template_repo = SurveyTemplateRepo(t) + with self.assertRaises(InvalidTextRepresentation): + template_repo.create_skin_scoring_app_entry('', + TEST1_SOURCE_ID, + 'en_US') + + def test_get_skin_scoring_app_id_if_exists_true(self): + with Transaction() as t: + template_repo = SurveyTemplateRepo(t) + test_ssa_id = template_repo.create_skin_scoring_app_entry( + TEST1_ACCOUNT_ID, TEST1_SOURCE_ID, 'en_US' + ) + obs = template_repo.get_skin_scoring_app_id_if_exists( + TEST1_ACCOUNT_ID, TEST1_SOURCE_ID + ) + self.assertEqual(test_ssa_id, obs) + + def test_get_skin_scoring_app_id_if_exists_false(self): + with Transaction() as t: + template_repo = SurveyTemplateRepo(t) + obs = template_repo.get_skin_scoring_app_id_if_exists( + TEST1_ACCOUNT_ID, TEST1_SOURCE_ID + ) + self.assertEqual(obs, None) + def test_create_vioscreen_id_valid(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t)