def test_create_campaign_invalid(self): # test to verify unique constraint on title with Transaction() as t: duplicate_campaign = { "title": "Test Campaign", "associated_projects": ["1", ] } campaign_repo = CampaignRepo(t) with self.assertRaises(UniqueViolation): campaign_repo.create_campaign(**duplicate_campaign) # test to verify requirement of associated_projects with Transaction() as t: campaign_no_projects = { "title": "Unique Campaign Name" } campaign_repo = CampaignRepo(t) with self.assertRaises(KeyError): campaign_repo.create_campaign(**campaign_no_projects) # test to verify that associated_projects must be valid project_ids with Transaction() as t: campaign_bad_projects = { "title": "Unique Campaign Name", "associated_projects": ["-1", ] } campaign_repo = CampaignRepo(t) with self.assertRaises(ForeignKeyViolation): campaign_repo.create_campaign(**campaign_bad_projects)
def test_update_project(self): with Transaction() as t: admin_repo = AdminRepo(t) proj_id = admin_repo.create_project( Project(project_name=DUMMY_PROJ_NAME, is_microsetta=False, bank_samples=False)) t.commit() # create post input json input_json = json.dumps(self.FULL_PROJ_INFO) # execute project put (update) response = self.client.put(f"/api/admin/projects/{proj_id}", content_type='application/json', data=input_json, headers=MOCK_HEADERS) # check response code self.assertEqual(204, response.status_code) with Transaction() as t: with t.dict_cursor() as cur: cur.execute( "select * FROM project " "WHERE " "project_id = %s", (proj_id, )) row = cur.fetchone() stored_result = dict(row) expected_result = self.FULL_PROJ_INFO.copy() expected_result["project"] = expected_result.pop("project_name") expected_result["project_id"] = proj_id self.assertEqual(expected_result, stored_result)
def put_interested_user_address_update(body): interested_user_id = body['interested_user_id'] email = body['email'] valid_data = _validate_iuid_and_email_syntax(interested_user_id, email) if not valid_data: return jsonify( code=404, message="Invalid user." ), 404 else: with Transaction() as t: i_u_repo = InterestedUserRepo(t) interested_user = \ i_u_repo.get_interested_user_by_id(interested_user_id) valid_user = _validate_user_match(interested_user, email) if not valid_user: return jsonify( code=404, message="Invalid user." ), 404 else: # reset address_checked flag so the verify_address() function # can run and update with Melissa-generated results interested_user.address_checked = False interested_user.address_1 = body['address_1'] interested_user.address_2 = body['address_2'] interested_user.city = body['city'] interested_user.state = body['state'] interested_user.postal_code = body['postal'] update_success = \ i_u_repo.update_interested_user(interested_user) if update_success is False: return jsonify( code=400, message="Failed to update address." ), 400 t.commit() # open new transaction so we don't lose user data if there's a problem # with address validation with Transaction() as t: interested_user_repo = InterestedUserRepo(t) try: interested_user_repo.verify_address(interested_user_id) except RepoException: # we really shouldn't reach this point, but just in case return jsonify( code=400, message="Failed to verify address." ), 400 t.commit() return jsonify(user_id=interested_user_id), 200
def test_code_nondeterminism(self): email1 = "*****@*****.**" with Transaction() as t: activations = ActivationRepo(t) code1 = activations.get_activation_code(email1) t.rollback() with Transaction() as t: activations = ActivationRepo(t) code2 = activations.get_activation_code(email1) t.rollback() self.assertNotEqual(code1, code2)
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 test_retrieve_diagnostics_by_barcode_nonexistent(self): with Transaction() as t: admin_repo = AdminRepo(t) # Uhh, should this return a 404 not found or just an empty # diagnostic object...? diag = admin_repo.retrieve_diagnostics_by_barcode('NotABarcode :D') self.assertIsNone(diag)
def top_food_report(account_id, source_id, survey_id, token_info): _validate_account_access(token_info, account_id) with Transaction() as t: vioscreen_repo = VioscreenRepo(t) # Vioscreen username is our survey_id status = vioscreen_repo.get_vioscreen_status(account_id, source_id, survey_id) if status != 3: # Oops, we don't have results available for this one raise NotFound("No such survey recorded") vio = VioscreenAdminAPI() sessions = vio.sessions(survey_id) # Looks like vioscreen supports multiple sessions per user, do we care? session_id = sessions[0]['sessionId'] report = vio.top_food_report(session_id) response = make_response(report) response.headers.set("Content-Type", "application/pdf") # TODO: Do we want it to download a file or be embedded in the html? # response.headers.set('Content-Disposition', # 'attachment', # filename='top-food-report.pdf') return response
def _submit_vioscreen_status(account_id, source_id, info_str): # get information out of encrypted vioscreen url info = vioscreen.decode_key(info_str).decode("utf-8") vio_info = {} for keyval in info.split("&"): key, val = keyval.split("=") vio_info[key] = val with Transaction() as t: vio_repo = VioscreenRepo(t) # Add the status to the survey vio_repo.upsert_vioscreen_status(account_id, source_id, vio_info["username"], int(vio_info["status"])) t.commit() response = flask.Response() response.status_code = 201 # TODO FIXME HACK: This location can't actually return any info about ffq! # But I need SOME response that contains the survey_id or client can't # associate the survey with a sample. response.headers['Location'] = '/api/accounts/%s' \ '/sources/%s' \ '/surveys/%s' % \ (account_id, source_id, vio_info["username"]) return response
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_add_transaction_duplicate(self): with Transaction() as t: r = UserTransaction(t) r.add_transaction(TRANSACTION_ONE_ITEM) with self.assertRaises(UniqueViolation): r.add_transaction(TRANSACTION_ONE_ITEM)
def project_statistics_detailed(token_info, project_id): validate_admin_access(token_info) with Transaction() as t: admin_repo = AdminRepo(t) summary = admin_repo.get_project_detailed_statistics(project_id) return jsonify(summary), 200
def test_get_survey(self): with Transaction() as t: admin_repo = AdminRepo(t) BARCODE = '000004216' with self.assertRaises(NotFound): admin_repo.get_survey_metadata("NOTABARCODE") meta = admin_repo.get_survey_metadata(BARCODE, survey_template_id=1) self.assertEqual(meta['sample_barcode'], BARCODE) self.assertIn('host_subject_id', meta) # And there should be one survey answered self.assertEqual(len(meta['survey_answers']), 1) all_meta = admin_repo.get_survey_metadata(BARCODE) self.assertEqual(all_meta['sample_barcode'], BARCODE) self.assertEqual(all_meta['host_subject_id'], all_meta['host_subject_id']) # And there should be more than one survey answered self.assertGreater(len(all_meta['survey_answers']), 1) # And the meta survey should exist somewhere in all_meta found = False for survey in all_meta['survey_answers']: if "1" in survey["response"] and \ survey["response"]["1"][0] == 'DIET_TYPE': found = True self.assertDictEqual(meta['survey_answers'][0], survey) self.assertTrue(found)
def test_email_stats(self): with Transaction() as t: accts = AccountRepo(t) acct1 = accts.get_account("65dcd6c8-69fa-4de8-a33a-3de4957a0c79") acct2 = accts.get_account("556f5dc4-8cf2-49ae-876c-32fbdfb005dd") # execute articles get for project in [None, "American Gut Project", "NotAProj"]: response = self.client.post( "/api/admin/account_email_summary", headers=MOCK_HEADERS, content_type='application/json', data=json.dumps({ "emails": [acct1.email, acct2.email], "project": project }) ) self.assertEqual(200, response.status_code) result = json.loads(response.data) self.assertEqual(result[0]["account_id"], "65dcd6c8-69fa-4de8-a33a-3de4957a0c79") self.assertEqual(result[1]["account_id"], "556f5dc4-8cf2-49ae-876c-32fbdfb005dd") if project is None or project == "American Gut Project": self.assertEqual(result[0]["sample-is-valid"], 1) else: self.assertEqual(result[0].get("sample-is-valid", 0), 0)
def test_get_mpeds_does_not_exist(self): with Transaction() as t: s = VioscreenSessionRepo(t) s.upsert_session(VIOSCREEN_SESSION) r = VioscreenMPedsRepo(t) obs = r.get_mpeds('does not exist') self.assertEqual(obs, None)
def test_create_project(self): with Transaction() as t: admin_repo = AdminRepo(t) with t.cursor() as cur: cur.execute("SELECT project " "FROM barcodes.project " "WHERE project = 'doesnotexist'") self.assertEqual(len(cur.fetchall()), 0) admin_repo.create_project('doesnotexist', True, False) cur.execute("SELECT project, is_microsetta, bank_samples, " "plating_start_date " "FROM barcodes.project " "WHERE project = 'doesnotexist'") obs = cur.fetchall() self.assertEqual(obs, [ ('doesnotexist', True, False, None), ]) plating_start_date = date(2020, 7, 31) admin_repo.create_project('doesnotexist2', False, True, plating_start_date) cur.execute("SELECT project, is_microsetta, bank_samples, " "plating_start_date " "FROM barcodes.project " "WHERE project = 'doesnotexist2'") obs = cur.fetchall() self.assertEqual(obs, [ ('doesnotexist2', False, True, plating_start_date), ])
def test_create_project_success_full(self): with Transaction() as t: admin_repo = AdminRepo(t) # Note: using dict_cursor here so results are DictRow # objects that can easily be converted to a dictionary with t.dict_cursor() as cur: cur.execute("SELECT project " "FROM barcodes.project " "WHERE project = 'full_test_proj'") self.assertEqual(len(cur.fetchall()), 0) full_project_dict = self._FULL_PROJECT_DICT.copy() full_project_dict[p.PROJ_NAME_KEY] = 'full_test_proj' input = p.Project.from_dict(full_project_dict) output_id = admin_repo.create_project(input) cur.execute("SELECT * " "FROM barcodes.project " "WHERE project = 'full_test_proj'") row = cur.fetchone() obs_dict = dict(row) full_project_dict["project_id"] = output_id full_project_dict[p.DB_PROJ_NAME_KEY] = \ full_project_dict.pop(p.PROJ_NAME_KEY) self.assertEqual(obs_dict, full_project_dict)
def test_get_fundrazr_transactions(self): # integration test, verify we can pull from fundrazr and insert # real obtained data # we should have zero transactions as test database doesn't have any # resident now = datetime.datetime.now() with Transaction() as t: ut = UserTransaction(t) obs = ut.get_transactions(before=now) self.assertEqual(obs, []) get_fundrazr_transactions(test_transaction=t) obs = ut.get_transactions(before=now) # staging has like 7 transactions, but let's not assume that'll be # true in perpetuity self.assertTrue(len(obs) > 0) # rerun to make sure we dont get more transactions, and that we # properly handle the lack of new transactions seen = len(obs) get_fundrazr_transactions(test_transaction=t) obs = ut.get_transactions(before=now) self.assertEqual(len(obs), seen)
def test_most_recent_transaction(self): with Transaction() as t: r = UserTransaction(t) self._load_some_transactions(r) obs = r.most_recent_transaction() exp = self.obj1 self._payment_equal([obs, ], [exp, ])
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_get_food_consumption_does_not_exist(self): with Transaction() as t: s = VioscreenSessionRepo(t) s.upsert_session(VIOSCREEN_SESSION) r = VioscreenFoodConsumptionRepo(t) obs = r.get_food_consumption('does not exist') self.assertEqual(obs, None)
def test_scan_barcode_success(self): with Transaction() as t: # TODO FIXME HACK: Need to build mock barcodes rather than using # these fixed ones TEST_BARCODE = '000000001' TEST_STATUS = "sample-has-inconsistencies" TEST_NOTES = "THIS IS A UNIT TEST" admin_repo = AdminRepo(t) # check that before doing a scan, no scans are recorded for this diag = admin_repo.retrieve_diagnostics_by_barcode(TEST_BARCODE) self.assertEqual(len(diag['scans_info']), 0) # do a scan admin_repo.scan_barcode(TEST_BARCODE, { "sample_status": TEST_STATUS, "technician_notes": TEST_NOTES }) # show that now a scan is recorded for this barcode diag = admin_repo.retrieve_diagnostics_by_barcode(TEST_BARCODE) self.assertEqual(len(diag['scans_info']), 1) first_scan = diag['scans_info'][0] self.assertEqual(first_scan['technician_notes'], TEST_NOTES) self.assertEqual(first_scan['sample_status'], TEST_STATUS)
def test_geocode_accounts_valid(self, test_verify_address): test_verify_address.return_value = { "address_1": DUMMY_ACCT_INFO_1['address']['street'], "address_2": "", "city": DUMMY_ACCT_INFO_1['address']['city'], "state": DUMMY_ACCT_INFO_1['address']['state'], "postal": DUMMY_ACCT_INFO_1['address']['post_code'], "country": DUMMY_ACCT_INFO_1['address']['country_code'], "latitude": RESULT_LAT, "longitude": RESULT_LONG, "valid": True } with Transaction() as t: ar = AccountRepo(t) acct_1 = Account.from_dict(DUMMY_ACCT_INFO_1, ACCT_MOCK_ISS_1, ACCT_MOCK_SUB_1) ar.create_account(acct_1) ar.geocode_accounts() with t.dict_cursor() as cur: cur.execute("SELECT latitude, longitude, address_verified, " "cannot_geocode FROM ag.account " "WHERE id = %s", (ACCT_ID_1,)) r = cur.fetchone() self.assertAlmostEqual(r['latitude'], RESULT_LAT, 9) self.assertAlmostEqual(r['longitude'], RESULT_LONG, 9) self.assertTrue(r['address_verified']) self.assertFalse(r['cannot_geocode'])
def setup_test_data(): teardown_test_data() with Transaction() as t: acct_repo = AccountRepo(t) acc = Account(ACCT_ID_1, "*****@*****.**", "admin", ACCT_MOCK_ISS, ACCT_MOCK_SUB, "Dan", "H", Address( "456 Dan Lane", "Danville", "CA", 12345, "US" ), "fakekit", "en_US") acct_repo.create_account(acc) with t.cursor() as cur: cur.execute("UPDATE barcodes.project" " SET is_active = FALSE" " WHERE project_id = 2") t.commit()
def sample_pulldown_multiple_survey(token_info, sample_barcode): validate_admin_access(token_info) with Transaction() as t: admin_repo = AdminRepo(t) sample_pulldown = admin_repo.get_survey_metadata(sample_barcode) return jsonify(sample_pulldown), 200
def create_dummy_answered_survey(dummy_acct_id, dummy_source_id, survey_template_id=PRIMARY_SURVEY_TEMPLATE_ID, survey_answers_id=DUMMY_ANSWERED_SURVEY_ID, survey_model=None, dummy_sample_id=None): if survey_model is None: survey_model = DUMMY_SURVEY_ANSWERS_MODEL with Transaction() as t: survey_answers_repo = SurveyAnswersRepo(t) survey_answers_id = survey_answers_repo.submit_answered_survey( dummy_acct_id, dummy_source_id, localization.EN_US, survey_template_id, survey_model, survey_answers_id ) if dummy_sample_id is not None: survey_answers_repo.associate_answered_survey_with_sample( dummy_acct_id, dummy_source_id, dummy_sample_id, survey_answers_id) t.commit() return survey_answers_id
def validate_admin_access(token_info): with Transaction() as t: account_repo = AccountRepo(t) account = account_repo.find_linked_account(token_info['iss'], token_info['sub']) if account is None or account.account_type != 'admin': raise Unauthorized()
def create_project(body, token_info): validate_admin_access(token_info) project_name = body['project_name'] is_microsetta = body['is_microsetta'] bank_samples = body['bank_samples'] plating_start_date = body.get('plating_start_date') if plating_start_date is not None: try: plating_start_date = datetime.datetime.strptime( plating_start_date, "%Y-%m-%d") except ValueError: raise BadRequest( "plating start date '{0}' is not a valid date in YYYY-MM-DD " "format".format(plating_start_date)) if len(project_name) == 0: return jsonify(code=400, message="No project name provided"), 400 if not bank_samples and plating_start_date is not None: raise RepoException("Plating start date cannot be set for" " unbanked projects") with Transaction() as t: admin_repo = AdminRepo(t) admin_repo.create_project(project_name, is_microsetta, bank_samples, plating_start_date) t.commit() return {}, 201
def teardown_test_data(): with Transaction() as t: acct_repo = AccountRepo(t) admin_repo = AdminRepo(t) acct_repo.delete_account(ACCT_ID_1) admin_repo.delete_project_by_name(DUMMY_PROJ_NAME) t.commit()
def _validate_account_access(token_info, account_id): with Transaction() as t: account_repo = AccountRepo(t) token_associated_account = account_repo.find_linked_account( token_info['iss'], token_info['sub']) account = account_repo.get_account(account_id) if account is None: raise NotFound(ACCT_NOT_FOUND_MSG) else: # Whether or not the token_info is associated with an admin acct token_authenticates_admin = \ token_associated_account is not None and \ token_associated_account.account_type == 'admin' # Enum of how closely token info matches requested account_id auth_match = account.account_matches_auth( token_info[JWT_EMAIL_CLAIM_KEY], token_info[JWT_ISS_CLAIM_KEY], token_info[JWT_SUB_CLAIM_KEY]) # If token doesn't match requested account id, and doesn't grant # admin access to the system, deny. if auth_match == AuthorizationMatch.NO_MATCH and \ not token_authenticates_admin: raise Unauthorized() return account
def submit_answered_survey(account_id, source_id, language_tag, body, token_info): _validate_account_access(token_info, account_id) if body['survey_template_id'] == SurveyTemplateRepo.VIOSCREEN_ID: return _submit_vioscreen_status(account_id, source_id, body["survey_text"]["key"]) # TODO: Is this supposed to return new survey id? # TODO: Rename survey_text to survey_model/model to match Vue's naming? with Transaction() as t: survey_answers_repo = SurveyAnswersRepo(t) survey_answers_id = survey_answers_repo.submit_answered_survey( account_id, source_id, language_tag, body["survey_template_id"], body["survey_text"]) t.commit() response = flask.Response() response.status_code = 201 response.headers['Location'] = '/api/accounts/%s' \ '/sources/%s' \ '/surveys/%s' % \ (account_id, source_id, survey_answers_id) return response