def _create_daklapack_order(order_dict): order_dict[ORDER_ID_KEY] = str(uuid.uuid4()) with Transaction() as t: try: daklapack_order = DaklapackOrder.from_api(**order_dict) except ValueError as e: raise RepoException(e) # The Daklapack API wants a *list* of orders, and we are submitting one # at a time, so we have a list of one item :) post_response = post_daklapack_orders( [daklapack_order.order_structure]) if post_response.status_code >= 400: # for now, very basic error handling--just pass on dak api error response_msg = {"order_address": daklapack_order.order_structure[ADDR_DICT_KEY], "order_success": False, "daklapack_api_error_msg": post_response.get_data(as_text=True), "daklapack_api_error_code": post_response.status_code} return response_msg # write order to db admin_repo = AdminRepo(t) order_id = admin_repo.create_daklapack_order(daklapack_order) t.commit() status_msg = {"order_address": daklapack_order.order_structure[ADDR_DICT_KEY], "order_success": True, "order_id": order_id} return status_msg
def get_daklapack_articles(token_info): validate_admin_access(token_info) with Transaction() as t: admin_repo = AdminRepo(t) dak_article_dicts = admin_repo.get_daklapack_articles() return jsonify(dak_article_dicts), 200
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 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 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 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_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 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 per_sample_summary(email, project, strip_sampleid): with Transaction() as t: admin = AdminRepo(t) project_name = admin.get_project_name(project) summaries = per_sample(project, barcodes=None, strip_sampleid=strip_sampleid) df = pd.DataFrame(summaries) _, path = tempfile.mkstemp() df.to_csv(path) date = datetime.datetime.now().strftime("%d%b%Y") filename = f'project-{project_name}-summary-{date}.csv' # NOTE: we are not using .delay so this action remains # within the current celery task template_args = {'date': date, 'project': project_name} send_basic_email(email, f"[TMI-summary] Project {project}", 'email/sample_summary', list(template_args), template_args, "EMAIL", "EMAIL_PER_PROJECT_SUMMARY", attachment_filepath=path, attachment_filename=filename) os.remove(path)
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 test_get_daklapack_articles(self): with Transaction() as t: admin_repo = AdminRepo(t) articles = admin_repo.get_daklapack_articles() self.assertEqual(24, len(articles)) first_article = articles[0] first_article.pop("dak_article_id") self.assertEqual(FIRST_DAKLAPACK_ARTICLE, first_article)
def get_projects(token_info, include_stats, is_active=None): validate_admin_access(token_info) with Transaction() as t: admin_repo = AdminRepo(t) projects_list = admin_repo.get_projects(include_stats, is_active) result = [x.to_api() for x in projects_list] return jsonify(result), 200
def update_project(project_id, body): project = Project.from_dict(body) with Transaction() as t: admin_repo = AdminRepo(t) admin_repo.update_project(project_id, project) t.commit() return '', 204
def test_search_kit_id(self): with Transaction() as t: admin_repo = AdminRepo(t) diag = admin_repo.retrieve_diagnostics_by_kit_id('test') self.assertIsNotNone(diag) self.assertIsNotNone(diag['kit']) diag = admin_repo.retrieve_diagnostics_by_kit_id('NotAKitId!!!!') self.assertIsNone(diag)
def search_email(token_info, email): validate_admin_access(token_info) with Transaction() as t: admin_repo = AdminRepo(t) diag = admin_repo.retrieve_diagnostics_by_email(email) if diag is None: return jsonify(code=404, message="Email not found"), 404 return jsonify(diag), 200
def test_scan_barcode_error_nonexistent(self): with Transaction() as t: admin_repo = AdminRepo(t) with self.assertRaises(NotFound): admin_repo.scan_barcode("THIZIZNOTAREALBARCODEISWARE", { "sample_status": "Abc", "technician_notes": "123" }) self.fail("Shouldn't get here")
def search_kit_id(token_info, kit_id): validate_admin_access(token_info) with Transaction() as t: admin_repo = AdminRepo(t) diag = admin_repo.retrieve_diagnostics_by_kit_id(kit_id) if diag is None: return jsonify(code=404, message="Kit ID not found"), 404 return jsonify(diag), 200
def test_retrieve_diagnostics_by_barcode_nonexistent(self): with Transaction() as t: # TODO FIXME HACK: Need to build mock barcodes rather than using # these fixed ones 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 search_barcode(token_info, sample_barcode): validate_admin_access(token_info) with Transaction() as t: admin_repo = AdminRepo(t) diag = admin_repo.retrieve_diagnostics_by_barcode(sample_barcode) if diag is None: return jsonify(code=404, message="Barcode not found"), 404 return jsonify(diag), 200
def test_detailed_project_statistics(self): with Transaction() as t: admin_repo = AdminRepo(t) agp_summary = admin_repo.get_project_detailed_statistics(1) self.assertIn('project_id', agp_summary) self.assertIn('project_name', agp_summary) self.assertIn('number_of_samples', agp_summary) self.assertIn('number_of_samples_scanned_in', agp_summary) self.assertIn('sample_status_counts', agp_summary)
def test_retrieve_diagnostics_by_barcode_not_agp(self): with Transaction() as t: admin_repo = AdminRepo(t) diag = admin_repo.retrieve_diagnostics_by_barcode('000044481') self.assertIsNotNone(diag['barcode_info']) self.assertIsNone(diag['account']) self.assertIsNone(diag['source']) self.assertIsNone(diag['sample']) self.assertGreater(len(diag['scans_info']), 0) self.assertGreater(len(diag['projects_info']), 0)
def test_summary_statistics(self): with Transaction() as t: admin_repo = AdminRepo(t) summary = admin_repo.get_project_summary_statistics() self.assertGreater(len(summary), 1) for stats in summary: self.assertIn('project_id', stats) self.assertIn('project_name', stats) self.assertIn('number_of_samples', stats)
def test_search_email(self): with Transaction() as t: admin_repo = AdminRepo(t) diag = admin_repo.retrieve_diagnostics_by_email( 'yqrc&[email protected]') self.assertIsNotNone(diag) self.assertEqual(len(diag['accounts']), 1) diag = admin_repo.retrieve_diagnostics_by_email('.com') self.assertIsNotNone(diag) self.assertGreater(len(diag['accounts']), 1)
def test_retrieve_diagnostics_by_barcode_wo_extra_info(self): with Transaction() as t: # TODO FIXME HACK: Need to build mock barcodes rather than using # these fixed ones admin_repo = AdminRepo(t) diag = admin_repo.retrieve_diagnostics_by_barcode('000033903') self.assertIsNotNone(diag['barcode_info']) self.assertIsNone(diag['account']) self.assertIsNone(diag['source']) self.assertIsNone(diag['sample']) self.assertGreater(len(diag['projects_info']), 0) self.assertEqual(len(diag['scans_info']), 0)
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) with t.cursor() as cur: cur.execute("UPDATE barcodes.project" " SET is_active = TRUE" " WHERE project_id = 2") t.commit()
def barcode_query(body, token_info): # Validating admin access is absolutely critical here # Failing to do so enables sql query access # to non admin users validate_admin_access(token_info) with Transaction() as t: repo = AdminRepo(t) cond, cond_params = build_condition(body) barcodes = repo.search_barcode(cond, cond_params) t.rollback() # Queries don't need to commit changes. return jsonify(barcodes), 200
def scan_barcode(token_info, sample_barcode, body): validate_admin_access(token_info) with Transaction() as t: admin_repo = AdminRepo(t) scan_id = admin_repo.scan_barcode(sample_barcode, body) t.commit() response = jsonify({"scan_id": scan_id}) response.status_code = 201 response.headers['Location'] = '/api/admin/search/samples/%s' % \ sample_barcode return response
def test_retrieve_diagnostics_by_barcode_w_scans(self): def make_tz_datetime(y, m, d): return datetime.datetime(y, m, d, 0, 0, tzinfo=psycopg2.tz.FixedOffsetTimezone( offset=-420, name=None)) # Non-AGP barcode so no acct, source, or sample; # also no preexisting scans in test db test_barcode = '000004531' first_scan_id = 'f7fd3022-3a9c-4f79-b92c-5cebd83cba38' second_scan_id = '76aec821-aa28-4dea-a796-2cfd1276f78c' first_scan = { "barcode_scan_id": first_scan_id, "barcode": test_barcode, "scan_timestamp": make_tz_datetime(2017, 7, 16), "sample_status": 'no-registered-account', "technician_notes": "huh?" } second_scan = { "barcode_scan_id": second_scan_id, "barcode": test_barcode, "scan_timestamp": make_tz_datetime(2020, 12, 4), "sample_status": 'sample-is-valid', "technician_notes": None } try: add_dummy_scan(first_scan) add_dummy_scan(second_scan) with Transaction() as t: admin_repo = AdminRepo(t) diag = admin_repo.retrieve_diagnostics_by_barcode(test_barcode) self.assertIsNotNone(diag['barcode_info']) self.assertIsNone(diag['account']) self.assertIsNone(diag['source']) self.assertIsNone(diag['sample']) self.assertGreater(len(diag['projects_info']), 0) self.assertEqual(len(diag['scans_info']), 2) # order matters in the returned vals, so test that self.assertEqual(diag['scans_info'][0], first_scan) self.assertEqual(diag['scans_info'][1], second_scan) self.assertEqual(diag['latest_scan'], second_scan) finally: delete_test_scan(first_scan_id) delete_test_scan(second_scan_id)
def create_project(body, token_info): validate_admin_access(token_info) project_name = body['project_name'] is_microsetta = body['is_microsetta'] if len(project_name) == 0: return jsonify(code=400, message="No project name provided"), 400 with Transaction() as t: admin_repo = AdminRepo(t) admin_repo.create_project(project_name, is_microsetta) t.commit() return {}, 201
def test_process_order_articles_unknown_status(self): with Transaction() as t: dummy_orders = self.make_dummy_dak_orders(t) an_order_id = dummy_orders[0][0] admin_repo = AdminRepo(t) # NB: these have to be patched *where they will be looked up*, not # where they are originally defined; see # https://docs.python.org/3/library/unittest.mock.html#where-to-patch with patch("microsetta_private_api.admin.daklapack_communication." "get_daklapack_order_details") as mock_dak_order_info: # NB: this is returning the same json as for the "sent" # case; this json says each article was sent (which would # not be the case in the "unknown" case) but that field isn't # used for the "unknown" case so I am not bothering to make a # whole new input to change that field for verisimilitude mock_dak_order_info.side_effect = [ make_test_response(200, self.ARTICLES_INFO) ] with self.assertRaisesRegex( ValueError, "Order 7ed917ef-0c4d-431a-9aa0-0a1f4f41f44b " "has an unexpected status: InProduction"): process_order_articles(admin_repo, an_order_id, "InProduction", "2021-02-26T08:30:18.805519Z")