def test_scrub(self): samp1 = 'd8592c74-85f0-2135-e040-8a80115d6401' # 000001766 with Transaction() as t: acct1, src1 = self._get_source_from_sample(t, samp1) sr = SampleRepo(t) # get original source associations src1_sample = sr.get_samples_by_source(acct1, src1)[0] res = sr.scrub(acct1, src1, src1_sample.id) self.assertTrue(res) obs_sample = sr.get_samples_by_source(acct1, src1)[0] self.assertNotEqual(src1_sample.notes, obs_sample.notes)
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_sample_associations(account_id, source_id, token_info): _validate_account_access(token_info, account_id) with Transaction() as t: sample_repo = SampleRepo(t) samples = sample_repo.get_samples_by_source(account_id, source_id) api_samples = [x.to_api() for x in samples] return jsonify(api_samples), 200
def test_update_sample_assocation_with_migration(self): samp1 = 'd8592c74-85f0-2135-e040-8a80115d6401' # 000001766 samp2 = 'ceaa6fd6-0861-4335-aa35-da1857bd5294' # 000067789 with Transaction() as t: acct1, src1 = self._get_source_from_sample(t, samp1) acct2, src2 = self._get_source_from_sample(t, samp2) sr = SampleRepo(t) # get original source associations src1_samples = sr.get_samples_by_source(acct1, src1) src2_samples = sr.get_samples_by_source(acct2, src2) # verify samples are part of the original source self.assertTrue(self._sample_in_source(src1_samples, samp1)) self.assertTrue(self._sample_in_source(src2_samples, samp2)) # swap associations sr._update_sample_association(samp1, src2, True) sr._update_sample_association(samp2, src1, True) # get new samples by source src1_samples = sr.get_samples_by_source(acct1, src1) src2_samples = sr.get_samples_by_source(acct2, src2) # verify samples are part of the new source self.assertTrue(self._sample_in_source(src1_samples, samp2)) self.assertTrue(self._sample_in_source(src2_samples, samp1)) # verify samples are not part of the original source self.assertFalse(self._sample_in_source(src1_samples, samp1)) self.assertFalse(self._sample_in_source(src2_samples, samp2)) # fix associations sr._update_sample_association(samp1, src1, True) sr._update_sample_association(samp2, src2, True) # ...and verify assocations are fixed src1_samples = sr.get_samples_by_source(acct1, src1) src2_samples = sr.get_samples_by_source(acct2, src2) self.assertTrue(self._sample_in_source(src1_samples, samp1)) self.assertTrue(self._sample_in_source(src2_samples, samp2))
def delete_account(account_id, token_info): validate_admin_access(token_info) with Transaction() as t: acct_repo = AccountRepo(t) src_repo = SourceRepo(t) samp_repo = SampleRepo(t) sar_repo = SurveyAnswersRepo(t) acct = acct_repo.get_account(account_id) if acct is None: return jsonify(message="Account not found", code=404), 404 else: # the account is already scrubbed so let's stop early if acct.account_type == 'deleted': return None, 204 sample_count = 0 sources = src_repo.get_sources_in_account(account_id) for source in sources: samples = samp_repo.get_samples_by_source(account_id, source.id) has_samples = len(samples) > 0 sample_count += len(samples) for sample in samples: # we scrub rather than disassociate in the event that the # sample is in our freezers but not with an up-to-date scan samp_repo.scrub(account_id, source.id, sample.id) surveys = sar_repo.list_answered_surveys(account_id, source.id) if has_samples: # if we have samples, we need to scrub survey / source # free text for survey_id in surveys: sar_repo.scrub(account_id, source.id, survey_id) src_repo.scrub(account_id, source.id) else: # if we do not have associated samples, then the source # is safe to delete for survey_id in surveys: sar_repo.delete_answered_survey(account_id, survey_id) src_repo.delete_source(account_id, source.id) # an account is safe to delete if there are no associated samples if sample_count > 0: acct_repo.scrub(account_id) else: acct_repo.delete_account(account_id) t.commit() return None, 204
def delete_dummy_accts(): all_sample_ids = [] acct_ids = [ACCT_ID_1, ACCT_ID_2] with Transaction() as t: acct_repo = AccountRepo(t) source_repo = SourceRepo(t) survey_answers_repo = SurveyAnswersRepo(t) sample_repo = SampleRepo(t) for curr_acct_id in acct_ids: sources = source_repo.get_sources_in_account(curr_acct_id) for curr_source in sources: source_samples = sample_repo.get_samples_by_source( curr_acct_id, curr_source.id) sample_ids = [x.id for x in source_samples] all_sample_ids.extend(sample_ids) # Dissociate all samples linked to this source from all # answered surveys linked to this source, then delete all # answered surveys delete_dummy_answered_surveys_from_source_with_t( t, curr_acct_id, curr_source.id, sample_ids, survey_answers_repo) # Now dissociate all the samples from this source for curr_sample_id in sample_ids: sample_repo.dissociate_sample(curr_acct_id, curr_source.id, curr_sample_id) # Finally, delete the source source_repo.delete_source(curr_acct_id, curr_source.id) # Delete the account acct_repo.delete_account(curr_acct_id) # Belt and suspenders: these test emails are used by some tests outside # of this module as well, so can't be sure they are paired with the # above dummy account ids acct_repo.delete_account_by_email(TEST_EMAIL) acct_repo.delete_account_by_email(TEST_EMAIL_2) # Delete the kit and all samples that were attached to any sources # NB: This won't clean up any samples that were created but NOT # attached to any sources ... if len(all_sample_ids) == 0: all_sample_ids = None _remove_mock_kit(t, mock_sample_ids=all_sample_ids) t.commit()
def test_migrate_sample(self): samp1 = 'd8592c74-85f0-2135-e040-8a80115d6401' # 000001766 samp2 = 'ceaa6fd6-0861-4335-aa35-da1857bd5294' # 000067789 with Transaction() as t: acct1, src1 = self._get_source_from_sample(t, samp1) acct2, src2 = self._get_source_from_sample(t, samp2) sr = SampleRepo(t) sr.migrate_sample(samp1, src1, src2, True) # get new samples by source src1_samples = sr.get_samples_by_source(acct1, src1) src2_samples = sr.get_samples_by_source(acct2, src2) # verify samples are part of the new source self.assertFalse(self._sample_in_source(src1_samples, samp1)) self.assertTrue(self._sample_in_source(src2_samples, samp1))
def query_email_stats(body, token_info): validate_admin_access(token_info) email_list = body.get("emails", []) project = body.get("project") results = [] with Transaction() as t: account_repo = AccountRepo(t) kit_repo = KitRepo(t) source_repo = SourceRepo(t) sample_repo = SampleRepo(t) for email in email_list: result = {'email': email, 'project': project} results.append(result) # can use internal lookup by email, because we have admin access account = account_repo._find_account_by_email(email) # noqa if account is None: result['summary'] = "No Account" continue else: result['account_id'] = account.id result['creation_time'] = account.creation_time result['kit_name'] = account.created_with_kit_id if account.created_with_kit_id is not None: unused = kit_repo.get_kit_unused_samples( account.created_with_kit_id ) if unused is None: result['unclaimed-samples-in-kit'] = 0 else: result['unclaimed-samples-in-kit'] = len(unused.samples) sample_statuses = defaultdict(int) sources = source_repo.get_sources_in_account(account.id) samples_in_project = 0 for source in sources: samples = sample_repo.get_samples_by_source(account.id, source.id) for sample in samples: if project is not None and \ project != "" and \ project not in sample.sample_projects: continue samples_in_project += 1 sample_status = sample_repo.get_sample_status( sample.barcode, sample._latest_scan_timestamp # noqa ) if sample_status is None: sample_status = "never-scanned" sample_statuses[sample_status] += 1 result.update(sample_statuses) if result.get('unclaimed-samples-in-kit', 0) > 0: result['summary'] = 'Possible Unreturned Samples' elif samples_in_project == 0: result['summary'] = "No Samples In Specified Project" elif result.get('sample-is-valid') == samples_in_project: result['summary'] = 'All Samples Valid' else: result['summary'] = 'May Require User Interaction' return jsonify(results), 200