def _remote_survey_url_vioscreen(transaction, account_id, source_id, language_tag, survey_redirect_url, vioscreen_ext_sample_id): # assumes an instance of Transaction is already available acct_repo = AccountRepo(transaction) survey_template_repo = SurveyTemplateRepo(transaction) if vioscreen_ext_sample_id: # User is about to start a vioscreen survey for this sample # record this in the database. db_vioscreen_id = survey_template_repo.create_vioscreen_id( account_id, source_id, vioscreen_ext_sample_id) else: raise ValueError("Vioscreen Template requires " "vioscreen_ext_sample_id parameter.") (birth_year, gender, height, weight) = \ survey_template_repo.fetch_user_basic_physiology( account_id, source_id) account = acct_repo.get_account(account_id) country_code = account.address.country_code url = vioscreen.gen_survey_url(db_vioscreen_id, language_tag, survey_redirect_url, birth_year=birth_year, gender=gender, height=height, weight=weight, country_code=country_code) return url
def _remote_survey_url_polyphenol_ffq(transaction, account_id, source_id, language_tag): st_repo = SurveyTemplateRepo(transaction) # right now, ID won't exist # future plans to allow surveys to behave more flexibly will use this # functionality to allow participants to re-join in-progress surveys polyphenol_ffq_id, study = \ st_repo.get_polyphenol_ffq_id_if_exists(account_id, source_id) if polyphenol_ffq_id is None: # The Polyphenol FFQ belongs to Danone and they're interested in # tracking results that come from their sponsored studies # separately from other samples. We pass 'THDMI' as the study for # THDMI samples and 'Microsetta' for all other samples. Therefore, # we need to determine if the source has any THDMI-associated samples. # Without investing significant developer effort to build a category # system around projects, a basic text search is the best compromise. study = 'Microsetta' 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('THDMI'): study = 'THDMI' break polyphenol_ffq_id = st_repo.create_polyphenol_ffq_entry( account_id, source_id, language_tag, study) return polyphenol_ffq.gen_ffq_url(polyphenol_ffq_id, study, language_tag)
def read_survey_templates(account_id, source_id, language_tag, token_info): _validate_account_access(token_info, account_id) # TODO: I don't think surveys have names... only survey groups have names. # So what can I pass down to the user that will make any sense here? # Note to future maintainers, # 2/21/20: currently the only way to figure this out # is to look through the "surveys" and "survey_group" tables, try: # select survey_id, american from surveys left join survey_group on # 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 [1, 3, 4, 5, 6, SurveyTemplateRepo.VIOSCREEN_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 test_get_polyphenol_ffq_id_if_exists_false(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) obs = \ template_repo.get_polyphenol_ffq_id_if_exists(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) self.assertEqual(obs, (None, None))
def test_create_vioscreen_id_valid(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) obs = template_repo.create_vioscreen_id(TEST2_ACCOUNT_ID, TEST2_SOURCE_ID, TEST2_SAMPLE_ID) self.assertTrue(obs is not None) t.rollback()
def test_create_myfoodrepo_id(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) obs = template_repo.create_myfoodrepo_entry( TEST2_ACCOUNT_ID, TEST2_SOURCE_ID) self.assertTrue(obs) t.rollback()
def test_set_myfoodrepo_no_slot(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) with self.assertRaises(KeyError): template_repo.set_myfoodrepo_id(TEST2_ACCOUNT_ID, TEST2_SOURCE_ID, "asubject") t.rollback()
def read_myfoodrepo_available_slots(): with Transaction() as t: st_repo = SurveyTemplateRepo(t) available = st_repo.myfoodrepo_slots_available() total = st_repo.myfoodrepo_slots_total() resp = jsonify(code=200, number_of_available_slots=available, total_number_of_slots=total) return resp, 200
def test_create_polyphenol_ffq_entry_valid(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) obs = template_repo.create_polyphenol_ffq_entry( TEST1_ACCOUNT_ID, TEST1_SOURCE_ID, 'en_US', 'THDMI') try: uuid.UUID(obs) valid_uuid_returned = True except ValueError: valid_uuid_returned = False self.assertTrue(valid_uuid_returned)
def read_survey_template(account_id, source_id, survey_template_id, language_tag, token_info, survey_redirect_url=None): _validate_account_access(token_info, account_id) # TODO: can we get rid of source_id? I don't have anything useful to do # with it... I guess I could check if the source is a dog before giving # out a pet information survey? with Transaction() as t: survey_template_repo = SurveyTemplateRepo(t) info = survey_template_repo.get_survey_template_link_info( survey_template_id) # For external surveys, we generate links pointing out if survey_template_id == SurveyTemplateRepo.VIOSCREEN_ID: url = vioscreen.gen_survey_url( language_tag, survey_redirect_url ) # TODO FIXME HACK: This field's contents are not specified! info.survey_template_text = { "url": url } return jsonify(info), 200 # For local surveys, we generate the json representing the survey survey_template = survey_template_repo.get_survey_template( survey_template_id, language_tag) info.survey_template_text = vue_adapter.to_vue_schema(survey_template) # TODO FIXME HACK: We need a better way to enforce validation on fields # that need it, can this be stored adjacent to the survey questions? client_side_validation = { "108": { # Height "inputType": "number", "validator": "number", "min": 0, "max": None }, "113": { # Weight "inputType": "number", "validator": "number", "min": 0, "max": None } } for group in info.survey_template_text.groups: for field in group.fields: if field.id in client_side_validation: field.set(**client_side_validation[field.id]) return jsonify(info), 200
def test_get_polyphenol_ffq_id_if_exists_true(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) test_pffq_id = \ template_repo.create_polyphenol_ffq_entry(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID, 'en_US', 'THDMI') obs = \ template_repo.get_polyphenol_ffq_id_if_exists(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) self.assertEqual((test_pffq_id, 'THDMI'), obs)
def _get_session_by_account_details(account_id, source_id, sample_id): with Transaction() as t: surv_temp = SurveyTemplateRepo(t) vio_sess = VioscreenSessionRepo(t) vio_username = surv_temp.get_vioscreen_id_if_exists( account_id, source_id, sample_id) if vio_username is None: return True, (jsonify(code=404, message="Username not found"), 404) vioscreen_session = vio_sess.get_sessions_by_username(vio_username) if vioscreen_session is None: return True, (jsonify(code=404, message="Session not found"), 404) return False, vioscreen_session
def test_create_vioscreen_id_idempotent(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) obs1 = template_repo.create_vioscreen_id(TEST2_ACCOUNT_ID, TEST2_SOURCE_ID, TEST2_SAMPLE_ID) obs2 = template_repo.create_vioscreen_id(TEST2_ACCOUNT_ID, TEST2_SOURCE_ID, TEST2_SAMPLE_ID) self.assertEqual(obs1, obs2) obs = template_repo.create_vioscreen_id(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID, TEST1_SAMPLE_ID) self.assertEqual(obs, TEST1_VIO_ID) t.rollback()
def read_answered_surveys(account_id, source_id, language_tag, token_info): _validate_account_access(token_info, account_id) with Transaction() as t: survey_answers_repo = SurveyAnswersRepo(t) survey_template_repo = SurveyTemplateRepo(t) answered_surveys = survey_answers_repo.list_answered_surveys( account_id, source_id) api_objs = [] for ans in answered_surveys: template_id = survey_answers_repo.find_survey_template_id(ans) if template_id is None: continue o = survey_template_repo.get_survey_template_link_info(template_id) api_objs.append(o.to_api(ans)) return jsonify(api_objs), 200
def test_create_myfoodrepo_id_no_slots(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) # insert 1 less than the available slots slots = SERVER_CONFIG['myfoodrepo_slots'] cur = t.cursor() cur.execute( """INSERT INTO ag.myfoodrepo_registry (account_id, source_id) SELECT account_id, id as source_id FROM ag.source WHERE id NOT IN %s LIMIT %s""", ((TEST1_SOURCE_ID, TEST2_SOURCE_ID), slots - 1)) # our next insertion should work obs = template_repo.create_myfoodrepo_entry( TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) self.assertTrue(obs) # we should now be at the maximum number of slots, so our final # insertion should fail obs = template_repo.create_myfoodrepo_entry( TEST2_ACCOUNT_ID, TEST2_SOURCE_ID) self.assertFalse(obs) # update some of our creation timestamps cur.execute( """UPDATE ag.myfoodrepo_registry SET creation_timestamp=NOW() - INTERVAL '30 days' WHERE source_id IN ( SELECT source_id FROM ag.myfoodrepo_registry WHERE source_id != %s LIMIT 5 )""", (TEST2_SOURCE_ID, )) # we now have slots, so we should be successful creating an entry obs = template_repo.create_myfoodrepo_entry( TEST2_ACCOUNT_ID, TEST2_SOURCE_ID) self.assertTrue(obs)
def read_answered_survey_associations(account_id, source_id, sample_id, token_info): _validate_account_access(token_info, account_id) with Transaction() as t: answers_repo = SurveyAnswersRepo(t) template_repo = SurveyTemplateRepo(t) answered_surveys = answers_repo.list_answered_surveys_by_sample( account_id, source_id, sample_id) resp_obj = [] for answered_survey in answered_surveys: template_id = answers_repo.find_survey_template_id(answered_survey) if template_id is None: continue info = template_repo.get_survey_template_link_info(template_id) resp_obj.append(info.to_api(answered_survey)) t.commit() return jsonify(resp_obj), 200
def read_answered_survey(account_id, source_id, survey_id, language_tag, token_info): _validate_account_access(token_info, account_id) with Transaction() as t: survey_answers_repo = SurveyAnswersRepo(t) survey_answers = survey_answers_repo.get_answered_survey( account_id, source_id, survey_id, language_tag) if not survey_answers: return jsonify(code=404, message="No survey answers found"), 404 template_id = survey_answers_repo.find_survey_template_id(survey_id) if template_id is None: return jsonify(code=422, message="No answers in survey"), 422 template_repo = SurveyTemplateRepo(t) link_info = template_repo.get_survey_template_link_info(template_id) link_info.survey_id = survey_id link_info.survey_text = survey_answers return jsonify(link_info), 200
def test_create_myfoodrepo_id_bad_source_or_account(self): # transaction needs to be created each time as the FK violation # disrupts the active transaction with Transaction() as t: template_repo = SurveyTemplateRepo(t) with self.assertRaises(ForeignKeyViolation): template_repo.create_myfoodrepo_entry(str(uuid.uuid4()), TEST2_SOURCE_ID) with Transaction() as t: template_repo = SurveyTemplateRepo(t) with self.assertRaises(ForeignKeyViolation): template_repo.create_myfoodrepo_entry(TEST2_ACCOUNT_ID, str(uuid.uuid4()))
def _remote_survey_url_myfoodrepo(transaction, account_id, source_id, language_tag): # assumes an instance of Transaction is already available st_repo = SurveyTemplateRepo(transaction) # do we already have an id? mfr_id, created = st_repo.get_myfoodrepo_id_if_exists( account_id, source_id) if mfr_id is None: # we need an ID so let's try and get one if created is None: # we need a slot and an id slot = st_repo.create_myfoodrepo_entry(account_id, source_id) if not slot: # we could not obtain a slot raise NotFound("Sorry, but the annotators are all allocated") mfr_id = myfoodrepo.create_subj() else: # we have a slot but no id mfr_id = myfoodrepo.create_subj() st_repo.set_myfoodrepo_id(account_id, source_id, mfr_id) else: # we already have an ID then just return the URL pass return myfoodrepo.gen_survey_url(mfr_id)
def test_set_myfoodrepo_cannot_assign_new_id(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) obs = template_repo.create_myfoodrepo_entry( TEST2_ACCOUNT_ID, TEST2_SOURCE_ID) self.assertTrue(obs) template_repo.set_myfoodrepo_id(TEST2_ACCOUNT_ID, TEST2_SOURCE_ID, "asubject") with self.assertRaises(KeyError): template_repo.set_myfoodrepo_id(TEST2_ACCOUNT_ID, TEST2_SOURCE_ID, "adifferentsubject") t.rollback()
def test_get_myfoodrepo_id_if_exists(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) obs = template_repo.get_myfoodrepo_id_if_exists( TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) self.assertEqual(obs, (None, None)) obs = template_repo.create_myfoodrepo_entry( TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) self.assertTrue(obs) template_repo.set_myfoodrepo_id(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID, "asubject") obs = template_repo.get_myfoodrepo_id_if_exists( TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) self.assertEqual(obs[0], "asubject") self.assertTrue(obs[1] is not None)
def test_create_polyphenol_ffq_entry_invalid(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) with self.assertRaises(InvalidTextRepresentation): template_repo.create_polyphenol_ffq_entry( '', TEST1_SOURCE_ID, '', '')
def read_survey_template(account_id, source_id, survey_template_id, language_tag, token_info, survey_redirect_url=None, vioscreen_ext_sample_id=None): _validate_account_access(token_info, account_id) with Transaction() as t: survey_template_repo = SurveyTemplateRepo(t) info = survey_template_repo.get_survey_template_link_info( survey_template_id) remote_surveys = set(survey_template_repo.remote_surveys()) # For external surveys, we generate links pointing out if survey_template_id in remote_surveys: if survey_template_id == SurveyTemplateRepo.VIOSCREEN_ID: url = _remote_survey_url_vioscreen(t, account_id, source_id, language_tag, survey_redirect_url, vioscreen_ext_sample_id) elif survey_template_id == SurveyTemplateRepo.MYFOODREPO_ID: url = _remote_survey_url_myfoodrepo(t, account_id, source_id, language_tag) elif survey_template_id == SurveyTemplateRepo.POLYPHENOL_FFQ_ID: url = _remote_survey_url_polyphenol_ffq( t, account_id, source_id, language_tag) else: raise ValueError(f"Cannot generate URL for survey " f"{survey_template_id}") # TODO FIXME HACK: This field's contents are not specified! info.survey_template_text = {"url": url} t.commit() return jsonify(info), 200 # For local surveys, we generate the json representing the survey survey_template = survey_template_repo.get_survey_template( survey_template_id, language_tag) info.survey_template_text = vue_adapter.to_vue_schema(survey_template) # TODO FIXME HACK: We need a better way to enforce validation on fields # that need it, can this be stored adjacent to the survey questions? client_side_validation = { "108": { # Height "inputType": "number", "validator": "integer", "min": 0, "max": None }, "113": { # Weight "inputType": "number", "validator": "integer", "min": 0, "max": None } } for group in info.survey_template_text.groups: for field in group.fields: if field.id in client_side_validation: field.set(**client_side_validation[field.id]) return jsonify(info), 200
def test_fetch_user_basic_physiology(self): with Transaction() as t: tr = SurveyTemplateRepo(t) # year and gender already set for this survey # weight and height are scrambled in the test # database as they're remarked as free text with t.cursor() as cur: cur.execute("""UPDATE ag.survey_answers_other SET response='["254"]' WHERE survey_id=%s AND survey_question_id=%s""", (TEST1_SURVEY_ID, 108)) # height_cm cur.execute("""UPDATE ag.survey_answers_other SET response='["100"]' WHERE survey_id=%s AND survey_question_id=%s""", (TEST1_SURVEY_ID, 113)) # weight_kg tr = SurveyTemplateRepo(t) obs = tr.fetch_user_basic_physiology(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) exp = (1973, 'Male', 100, 220.462) self.assertEqual(obs, exp) cur.execute("""UPDATE ag.survey_answers_other SET response='["100"]' WHERE survey_id=%s AND survey_question_id=%s""", (TEST1_SURVEY_ID, 108)) # height_cm cur.execute("""UPDATE ag.survey_answers SET response='inches' WHERE survey_id=%s AND survey_question_id=%s""", (TEST1_SURVEY_ID, 109)) # height_units cur.execute("""UPDATE ag.survey_answers SET response='pounds' WHERE survey_id=%s AND survey_question_id=%s""", (TEST1_SURVEY_ID, 114)) # weight_units obs = tr.fetch_user_basic_physiology(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) exp = (1973, 'Male', 100, 100) self.assertEqual(obs, exp) # equiv of Unspecified for height cur.execute("""UPDATE ag.survey_answers_other SET response='[""]' WHERE survey_id=%s AND survey_question_id=%s""", (TEST1_SURVEY_ID, 108)) # height_cm obs = tr.fetch_user_basic_physiology(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) exp = (1973, 'Male', None, 100) self.assertEqual(obs, exp) # equiv of Unspecified for weight cur.execute("""UPDATE ag.survey_answers_other SET response='[""]' WHERE survey_id=%s AND survey_question_id=%s""", (TEST1_SURVEY_ID, 113)) # weight_kg obs = tr.fetch_user_basic_physiology(TEST1_ACCOUNT_ID, TEST1_SOURCE_ID) exp = (1973, 'Male', None, None) self.assertEqual(obs, exp)
def test_get_vioscreen_id_if_exists_invalid(self): with Transaction() as t: template_repo = SurveyTemplateRepo(t) obs = template_repo.get_vioscreen_id_if_exists( TEST2_ACCOUNT_ID, TEST2_SOURCE_ID, TEST2_SAMPLE_ID) self.assertEqual(obs, None)
def read_survey_template(account_id, source_id, survey_template_id, language_tag, token_info, survey_redirect_url=None, vioscreen_ext_sample_id=None): _validate_account_access(token_info, account_id) # TODO: can we get rid of source_id? I don't have anything useful to do # with it... I guess I could check if the source is a dog before giving # out a pet information survey? with Transaction() as t: survey_template_repo = SurveyTemplateRepo(t) info = survey_template_repo.get_survey_template_link_info( survey_template_id) # For external surveys, we generate links pointing out if survey_template_id == SurveyTemplateRepo.VIOSCREEN_ID: if vioscreen_ext_sample_id: # User is about to start a vioscreen survey for this sample # record this in the database. db_vioscreen_id = survey_template_repo.create_vioscreen_id( account_id, source_id, vioscreen_ext_sample_id) else: raise ValueError("Vioscreen Template requires " "vioscreen_ext_sample_id parameter.") (birth_year, gender ) = survey_template_repo.fetch_user_birth_year_gender(account_id) url = vioscreen.gen_survey_url(db_vioscreen_id, language_tag, survey_redirect_url, birth_year=birth_year, gender=gender) # TODO FIXME HACK: This field's contents are not specified! info.survey_template_text = {"url": url} t.commit() return jsonify(info), 200 # For local surveys, we generate the json representing the survey survey_template = survey_template_repo.get_survey_template( survey_template_id, language_tag) info.survey_template_text = vue_adapter.to_vue_schema(survey_template) # TODO FIXME HACK: We need a better way to enforce validation on fields # that need it, can this be stored adjacent to the survey questions? client_side_validation = { "108": { # Height "inputType": "number", "validator": "number", "min": 0, "max": None }, "113": { # Weight "inputType": "number", "validator": "number", "min": 0, "max": None } } for group in info.survey_template_text.groups: for field in group.fields: if field.id in client_side_validation: field.set(**client_side_validation[field.id]) return jsonify(info), 200
def submit_answered_survey(self, ag_login_id, source_id, language_tag, survey_template_id, survey_model, survey_answers_id=None): # note that "ag_login_id" is the same as account_id # This is actually pretty complicated in the current schema: # We need to filter the model down to questions that are in the # template # We need to ensure that the account has access to write the given # participant # We need to generate a survey_answer id # We need to log that the user submitted this survey # We need to write each answer to one or more rows # TODO: We need to ensure that the account has access to write the # given participant!?! if survey_answers_id is None: survey_answers_id = str(uuid.uuid4()) survey_template_repo = SurveyTemplateRepo(self._transaction) survey_template = survey_template_repo.get_survey_template( survey_template_id, language_tag) with self._transaction.cursor() as cur: # Log that the user submitted this survey cur.execute( "INSERT INTO ag_login_surveys " "(ag_login_id, survey_id, source_id) " "VALUES(%s, %s, %s)", (ag_login_id, survey_answers_id, source_id)) # Write each answer for survey_template_group in survey_template.groups: for survey_question in survey_template_group.questions: survey_question_id = survey_question.id q_type = survey_question.response_type # TODO FIXME HACK: Modify DB to make this unnecessary! # We MUST record at least ONE answer for each survey # (even if the user answered nothing) # or we can't properly track the survey template id later. # Therefore, if the user answered NOTHING, store an empty # string for the first string or text question in the # survey, just so something is recorded. if len(survey_model) == 0 and \ (q_type == "STRING" or q_type == "TEXT"): survey_model[str(survey_question_id)] = "" if str(survey_question_id) not in survey_model: # TODO: Is this supposed to leave the question blank # or write Unspecified? continue answer = survey_model[str(survey_question_id)] if q_type == "SINGLE": # Normalize localized answer normalized_answer = self._unlocalize( answer, language_tag) try: cur.execute( "INSERT INTO survey_answers " "(survey_id, " "survey_question_id, " "response) " "VALUES(%s, %s, %s)", (survey_answers_id, survey_question_id, normalized_answer)) except psycopg2.errors.ForeignKeyViolation: raise BadRequest("Invalid survey response: %s" % answer) if q_type == "MULTIPLE": for ans in answer: normalized_answer = self._unlocalize( ans, language_tag) try: cur.execute( "INSERT INTO survey_answers " "(survey_id, " "survey_question_id, " "response) " "VALUES(%s, %s, %s)", (survey_answers_id, survey_question_id, normalized_answer)) except psycopg2.errors.ForeignKeyViolation: raise BadRequest( "Invalid survey response: %s" % ans) if q_type == "STRING" or q_type == "TEXT": # Note: Can't convert language on free text... cur.execute( "INSERT INTO survey_answers_other " "(survey_id, " "survey_question_id, " "response) " "VALUES(%s, %s, %s)", (survey_answers_id, survey_question_id, answer)) if len(survey_model) == 0: # we should not have gotten to the end without recording at least # ONE answer (even an empty one) ... but it could happen if this # survey template includes NO text or string questions AND the # user doesn't answer any of the questions it does include. Not # worth making the code robust to this case, as this whole "include # one empty answer" is a temporary hack, but at least ensure we # know this problem occurred if it ever does raise RepoException("No answers provided for survey template %s " "and not able to add an empty string default" % survey_template_id) return survey_answers_id
def per_sample(project, barcodes, strip_sampleid): summaries = [] with Transaction() as t: admin_repo = AdminRepo(t) sample_repo = SampleRepo(t) template_repo = SurveyTemplateRepo(t) vs_repo = VioscreenSessionRepo(t) if project is not None: project_barcodes = admin_repo.get_project_barcodes(project) else: project = 'Unspecified' if barcodes is None: barcodes = project_barcodes for barcode in barcodes: diag = admin_repo.retrieve_diagnostics_by_barcode(barcode) if diag is None: raise NotFound(f"Barcode not found: {barcode}") sample = diag['sample'] account = diag['account'] source = diag['source'] account_email = None if account is None else account.email source_email = None source_type = None if source is None else source.source_type vio_id = None if source is not None and source_type == Source.SOURCE_TYPE_HUMAN: source_email = source.source_data.email vio_id = template_repo.get_vioscreen_id_if_exists(account.id, source.id, sample.id) # at least one sample has been observed that "is_microsetta", # described in the barcodes.project_barcode table, but which is # unexpectedly not present in ag.ag_kit_barcodes if sample is None: sample_status = None sample_site = None ffq_complete = None ffq_taken = None else: sample_status = sample_repo.get_sample_status( sample.barcode, sample._latest_scan_timestamp ) sample_site = sample.site ffq_complete, ffq_taken, _ = vs_repo.get_ffq_status_by_sample( sample.id ) summary = { "sampleid": None if strip_sampleid else barcode, "project": project, "source-type": source_type, "site-sampled": sample_site, "source-email": source_email, "account-email": account_email, "vioscreen_username": vio_id, "ffq-taken": ffq_taken, "ffq-complete": ffq_complete, "sample-status": sample_status, "sample-received": sample_status is not None } for status in ["sample-is-valid", "no-associated-source", "no-registered-account", "no-collection-info", "sample-has-inconsistencies", "received-unknown-validity"]: summary[status] = sample_status == status summaries.append(summary) return summaries