def main(client): request = make_request_body(client.args.file) resource = SimpleFhirR4Reader(request) if not client.args.endpoint: path = 'SupplyRequest' else: path = client.args.endpoint if path == 'SupplyRequest': order_id = resource.identifier.get(system='http://joinallofus.org/fhir/orderId').value else: order_id = resource.basedOn[0].identifier.value #get(system='http://joinallofus.org/fhir/orderId').value if not client.args.verb: verb = 'PUT' else: verb = client.args.verb if verb == 'PUT': path = path + '/{}'.format(order_id) response = client.request_json(path, verb, request, check_status=False) pprint.pprint(response)
def test_list_exceptions(self): fhir = SimpleFhirR4Reader([0, 1]) with self.assertRaises(AttributeError): fhir.foo with self.assertRaises(IndexError): fhir[2] with self.assertRaises(IndexError): fhir['b']
def test_looks_up_top_level_references(self): fhir = SimpleFhirR4Reader({ 'contained': [{ 'id': 'some-foo', 'value': 123 }], 'foo': { 'reference': '#some-foo' } }) self.assertEqual(fhir.foo.value, 123)
def _filter_order_fields(self, resource, pid): fhir_resource = SimpleFhirR4Reader(resource) summary = ParticipantSummaryDao().get(pid) if not summary: raise BadRequest('No summary for particpant id: {}'.format(pid)) code_dict = summary.asdict() format_json_code(code_dict, self.code_dao, 'genderIdentityId') format_json_code(code_dict, self.code_dao, 'stateId') # MayoLink api has strong opinions on what should be sent and the order of elements. Dont touch. order = { 'order': { 'collected': fhir_resource.authoredOn, 'account': '', 'number': fhir_resource.extension.get( url=VIBRENT_BARCODE_URL).valueString, 'patient': { 'medical_record_number': str(summary.biobankId), 'first_name': '*', 'last_name': str(summary.biobankId), 'middle_name': '', 'birth_date': '3/3/1933', 'gender': code_dict['genderIdentity'], 'address1': summary.streetAddress, 'address2': summary.streetAddress2, 'city': summary.city, 'state': code_dict['state'], 'postal_code': str(summary.zipCode), 'phone': str(summary.phoneNumber), 'account_number': None, 'race': summary.race, 'ethnic_group': None }, 'physician': { 'name': None, 'phone': None, 'npi': None }, 'report_notes': fhir_resource.extension.get(url=VIBRENT_ORDER_URL).valueString, 'tests': { 'test': { 'code': '1SAL2', 'name': 'PMI Saliva, FDA Kit', 'comments': None } }, 'comments': 'Salivary Kit Order, direct from participant' } } return order
def test_list_lookup(self): fhir = SimpleFhirR4Reader([ { 'a': 'foo', 'b': 0 }, { 'a': 'bar', 'b': 1 }, ]) self.assertEqual(fhir.get(dict(a='bar'), 'b'), 1)
def test_dict_exceptions(self): fhir = SimpleFhirR4Reader({'a': 'foo'}) with self.assertRaises(AttributeError): fhir.b with self.assertRaises(KeyError): fhir['b'] with self.assertRaises(KeyError): fhir[0] with self.assertRaises(KeyError): fhir[-1] with self.assertRaises(TypeError): fhir[1:]
def _post_supply_delivery(self, resource): try: fhir = SimpleFhirR4Reader(resource) patient = fhir.patient pid = patient.identifier p_id = from_client_participant_id(pid.value) bo_id = fhir.basedOn[0].identifier.value _id = self.dao.get_id( ObjDict({ 'participantId': p_id, 'order_id': int(bo_id) })) tracking_status = fhir.extension.get( url=DV_FHIR_URL + 'tracking-status').valueString.lower() except AttributeError as e: raise BadRequest(e.message) except Exception as e: raise BadRequest(e.message) if not _id: raise Conflict( 'Existing SupplyRequest for order required for SupplyDelivery') dvo = self.dao.get(_id) if not dvo: raise Conflict( 'Existing SupplyRequest for order required for SupplyDelivery') merged_resource = None # Note: POST tracking status should be either 'enroute/in_transit'. PUT should only be 'delivered'. if tracking_status in [ 'in_transit', 'enroute', 'delivered' ] and self._to_mayo(fhir) and not dvo.biobankOrderId: # Send to mayolink and create internal biobank order response = self.dao.send_order(resource, p_id) merged_resource = merge_dicts(response, resource) merged_resource['id'] = _id logging.info( 'Sending salivary order to biobank for participant: %s', p_id) self.dao.insert_biobank_order(p_id, merged_resource) response = super(DvOrderApi, self).put(bo_id, participant_id=p_id, skip_etag=True, resource=merged_resource) response[2]['Location'] = '/rdr/v1/SupplyDelivery/{}'.format(bo_id) response[2]['auth_user'] = resource['auth_user'] if response[1] == 200: created_response = list(response) created_response[1] = 201 return tuple(created_response) return response
def _post_supply_request(self, resource): fhir_resource = SimpleFhirR4Reader(resource) patient = fhir_resource.contained.get(resourceType='Patient') pid = patient.identifier.get(system=VIBRENT_FHIR_URL + 'participantId').value p_id = from_client_participant_id(pid) response = super(DvOrderApi, self).post(participant_id=p_id) order_id = fhir_resource.identifier.get(system=VIBRENT_FHIR_URL + 'orderId').value response[2]['Location'] = '/rdr/v1/SupplyRequest/{}'.format(order_id) if response[1] == 200: created_response = list(response) created_response[1] = 201 return tuple(created_response) return response
def _put_supply_request(self, resource, bo_id): # handle invalid FHIR documents try: fhir_resource = SimpleFhirR4Reader(resource) barcode_url = None if fhir_resource.extension.get( url=VIBRENT_FULFILLMENT_URL).valueString == 'shipped': barcode_url = fhir_resource.extension.get( url=VIBRENT_BARCODE_URL).url pid = fhir_resource.contained.get( resourceType='Patient').identifier.get( system=VIBRENT_FHIR_URL + 'participantId') p_id = from_client_participant_id(pid.value) except AttributeError as e: raise BadRequest(e.message) except Exception as e: raise BadRequest(e.message) merged_resource = None if not p_id: raise BadRequest( 'Request must include participant id and must be of type int') if str(barcode_url) == VIBRENT_BARCODE_URL: _id = self.dao.get_id( ObjDict({ 'participantId': p_id, 'order_id': int(bo_id) })) ex_obj = self.dao.get(_id) if not ex_obj.barcode: # Send to mayolink and create internal biobank order response = self.dao.send_order(resource, p_id) merged_resource = merge_dicts(response, resource) merged_resource['id'] = _id self.dao.insert_biobank_order(p_id, merged_resource) if merged_resource: response = super(DvOrderApi, self).put(bo_id, participant_id=p_id, skip_etag=True, resource=merged_resource) else: response = super(DvOrderApi, self).put(bo_id, participant_id=p_id, skip_etag=True) return response
def _put_supply_request(self, resource, bo_id): # handle invalid FHIR documents try: fhir_resource = SimpleFhirR4Reader(resource) pid = fhir_resource.contained.get( resourceType='Patient').identifier.get(system=DV_FHIR_URL + 'participantId') p_id = from_client_participant_id(pid.value) except AttributeError as e: raise BadRequest(e.message) except Exception as e: raise BadRequest(e.message) if not p_id: raise BadRequest('Request must include participant id') response = super(DvOrderApi, self).put(bo_id, participant_id=p_id, skip_etag=True) return response
def _post_supply_delivery(self, resource): fhir_resource = SimpleFhirR4Reader(resource) patient = fhir_resource.patient pid = patient.identifier p_id = from_client_participant_id(pid.value) bo_id = fhir_resource.basedOn[0].identifier.value pk = {'participantId': p_id, 'order_id': bo_id} obj = ObjDict(pk) if not self.dao.get_id(obj): raise Conflict( 'Existing SupplyRequest for order required for SupplyDelivery') response = super(DvOrderApi, self).put(bo_id, participant_id=p_id, skip_etag=True) response[2]['Location'] = '/rdr/v1/SupplyDelivery/{}'.format(bo_id) if response[1] == 200: created_response = list(response) created_response[1] = 201 return tuple(created_response) return response
def _put_supply_delivery(self, resource, bo_id): # handle invalid FHIR documents try: fhir = SimpleFhirR4Reader(resource) participant_id = fhir.patient.identifier.value p_id = from_client_participant_id(participant_id) update_time = dateutil.parser.parse(fhir.occurrenceDateTime) carrier_name = fhir.extension.get(url=VIBRENT_FHIR_URL + 'carrier').valueString eta = dateutil.parser.parse( fhir.extension.get(url=VIBRENT_FHIR_URL + "expected-delivery-date").valueDateTime) tracking_status = fhir.extension.get(url=VIBRENT_FHIR_URL + 'tracking-status').valueString except AttributeError as e: raise BadRequest(e.message) except Exception as e: raise BadRequest(e.message) tracking_status_enum = getattr(OrderShipmentTrackingStatus, tracking_status.upper(), OrderShipmentTrackingStatus.UNSET) biobank_dv_order_id = self.dao.get_id( ObjDict({ 'participantId': p_id, 'order_id': int(bo_id) })) order = self.dao.get(biobank_dv_order_id) order.shipmentLastUpdate = update_time.date() order.shipmentCarrier = carrier_name order.shipmentEstArrival = eta.date() order.shipmentStatus = tracking_status_enum response = super(DvOrderApi, self).put(bo_id, participant_id=p_id, skip_etag=True) return response
def test_dict_filter_function_key(self): fhir = SimpleFhirR4Reader({'a': 0, 'b': 1, 'c': 2, 'd': 3}) odd_values_only_filter = lambda x: x[1] % 2 self.assertEqual(list(fhir.get(odd_values_only_filter)), [('b', 1), ('d', 3)])
def build_expected_resource_type_data(self, resource_type): """Helper function to build the data we are expecting from the test-data file.""" fhir_resource = SimpleFhirR4Reader(resource_type) test_fields = {} fhir_address = {} # fields to test with the same structure in both payloads fhir_device = fhir_resource.contained.get(resourceType="Device") test_fields.update({ 'itemName': fhir_device.deviceName.get(type="manufacturer-name").name, 'orderType': fhir_resource.extension.get(url=DV_ORDER_URL).valueString }) # add the fields to test for each resource type (SupplyRequest, SupplyDelivery) if resource_type == self.post_request: test_fields.update({ 'order_id': int( fhir_resource.identifier.get(system=DV_FHIR_URL + "orderId").value), 'supplier': fhir_resource.contained.get(resourceType="Organization").id, 'supplierStatus': fhir_resource.extension.get( url=DV_FULFILLMENT_URL).valueString, 'itemQuantity': fhir_resource.quantity.value, 'itemSKUCode': fhir_device.identifier.get(system=DV_FHIR_URL + "SKU").value, }) # Address Handling fhir_address = fhir_resource.contained.get( resourceType="Patient").address[0] if resource_type == self.post_delivery: test_fields.update({ 'order_id': int(fhir_resource.basedOn[0].identifier.value), 'shipmentEstArrival': parse_date( fhir_resource.extension.get( url=DV_FHIR_URL + "expected-delivery-date").valueDateTime), 'shipmentCarrier': fhir_resource.extension.get(url=DV_FHIR_URL + "carrier").valueString, 'trackingId': fhir_resource.identifier.get(system=DV_FHIR_URL + "trackingId").value, 'shipmentLastUpdate': parse_date(fhir_resource.occurrenceDateTime), }) # Address Handling fhir_address = fhir_resource.contained.get( resourceType="Location").get("address") address_fields = { "streetAddress1": fhir_address.line[0], "streetAddress2": '', "city": fhir_address.city, "stateId": get_code_id(fhir_address, self.code_dao, "state", "State_"), "zipCode": fhir_address.postalCode, } # street address 2 if len(list(fhir_address.line)) > 1: address_fields['streetAddress2'] = fhir_address.line[1] test_fields.update(address_fields) Supply = namedtuple('Supply', test_fields.keys()) expected_data = Supply(**test_fields) return expected_data
def test_enumerate_tracking_status(self): fhir_resource = SimpleFhirR4Reader(self.post_delivery) status = self.dao._enumerate_order_tracking_status( fhir_resource.extension.get(url=DV_FHIR_URL + 'tracking-status').valueString) self.assertEquals(status, OrderShipmentTrackingStatus.IN_TRANSIT)
def test_enumerate_shipping_status(self): fhir_resource = SimpleFhirR4Reader(self.post_request) status = self.dao._enumerate_order_shipping_status( fhir_resource.status) self.assertEquals(status, OrderShipmentStatus.SHIPPED)
def _filter_order_fields(self, resource, pid): fhir_resource = SimpleFhirR4Reader(resource) summary = ParticipantSummaryDao().get(pid) if not summary: raise BadRequest('No summary for participant id: {}'.format(pid)) code_dict = summary.asdict() format_json_code(code_dict, self.code_dao, 'genderIdentityId') format_json_code(code_dict, self.code_dao, 'stateId') if 'genderIdentity' in code_dict and code_dict['genderIdentity']: if code_dict['genderIdentity'] == 'GenderIdentity_Woman': gender_val = 'F' elif code_dict['genderIdentity'] == 'GenderIdentity_Man': gender_val = 'M' else: gender_val = 'U' else: gender_val = 'U' order_id = int(fhir_resource.basedOn[0].identifier.value) with self.session() as session: result = session.query(BiobankDVOrder.barcode).filter( BiobankDVOrder.order_id == order_id).first() barcode = None if not result else result if isinstance( result, str) else result.barcode # MayoLink api has strong opinions on what should be sent and the order of elements. Dont touch. order = { 'order': { 'collected': fhir_resource.occurrenceDateTime, 'account': '', 'number': barcode, 'patient': { 'medical_record_number': str(to_client_biobank_id(summary.biobankId)), 'first_name': '*', 'last_name': str(to_client_biobank_id(summary.biobankId)), 'middle_name': '', 'birth_date': '3/3/1933', 'gender': gender_val, 'address1': summary.streetAddress, 'address2': summary.streetAddress2, 'city': summary.city, 'state': code_dict['state'], 'postal_code': str(summary.zipCode), 'phone': str(summary.phoneNumber), 'account_number': None, 'race': summary.race, 'ethnic_group': None }, 'physician': { 'name': 'None', # must be a string value, not None. 'phone': None, 'npi': None }, 'report_notes': fhir_resource.extension.get(url=DV_ORDER_URL).valueString, 'tests': { 'test': { 'code': '1SAL2', 'name': 'PMI Saliva, FDA Kit', 'comments': None } }, 'comments': 'Salivary Kit Order, direct from participant' } } return order
def from_client_json(self, resource_json, id_=None, expected_version=None, participant_id=None, client_id=None): #pylint: disable=unused-argument """Initial loading of the DV order table does not include all attributes.""" fhir_resource = SimpleFhirR4Reader(resource_json) order = BiobankDVOrder(participantId=participant_id) order.participantId = participant_id if resource_json['resourceType'].lower() == 'supplydelivery': order.order_id = int(fhir_resource.basedOn[0].identifier.value) existing_obj = self.get(self.get_id(order)) if not existing_obj: raise NotFound('existing order record not found') # handling of biobankStatus from Mayolink API try: existing_obj.biobankStatus = resource_json['biobankStatus'] except KeyError: # resource will only have biobankStatus on a PUT pass existing_obj.shipmentStatus = self._enumerate_order_tracking_status( fhir_resource.extension.get(url=DV_FHIR_URL + 'tracking-status').valueString) existing_obj.shipmentCarrier = fhir_resource.extension.get( url=DV_FHIR_URL + 'carrier').valueString # shipmentEstArrival # The fhir_resource.get() method # will raise an exception on "expected-delivery-date" # if the resource doesn't have that path delivery_date_url = [ extension.url for extension in fhir_resource["extension"] if extension.url == DV_FHIR_URL + "expected-delivery-date" ] if delivery_date_url: existing_obj.shipmentEstArrival = parse_date( fhir_resource.extension.get( url=DV_FHIR_URL + "expected-delivery-date").valueDateTime) existing_obj.trackingId = fhir_resource.identifier.get( system=DV_FHIR_URL + 'trackingId').value # USPS status existing_obj.orderStatus = self._enumerate_order_shipping_status( fhir_resource.status) # USPS status time existing_obj.shipmentLastUpdate = parse_date( fhir_resource.occurrenceDateTime) order_address = fhir_resource.contained.get( resourceType='Location').get('address') address_use = fhir_resource.contained.get( resourceType='Location').get('address').get('use') order_address.stateId = get_code_id(order_address, self.code_dao, 'state', 'State_') existing_obj.address = { 'city': existing_obj.city, 'state': existing_obj.stateId, 'postalCode': existing_obj.zipCode, 'line': [existing_obj.streetAddress1] } if existing_obj.streetAddress2 is not None and existing_obj.streetAddress2 != '': existing_obj.address['line'].append( existing_obj.streetAddress2) if address_use.lower() == 'home': existing_obj.city = order_address.city existing_obj.stateId = order_address.stateId existing_obj.streetAddress1 = order_address.line[0] existing_obj.zipCode = order_address.postalCode if len(order_address._obj['line'][0]) > 1: try: existing_obj.streetAddress2 = order_address._obj[ 'line'][1] except IndexError: pass elif address_use.lower() == 'work': existing_obj.biobankCity = order_address.city existing_obj.biobankStateId = order_address.stateId existing_obj.biobankStreetAddress1 = order_address.line[0] existing_obj.biobankZipCode = order_address.postalCode if hasattr(fhir_resource, 'biobankTrackingId'): existing_obj.biobankTrackingId = fhir_resource.biobankTrackingId existing_obj.biobankReceived = parse_date( fhir_resource.received) return existing_obj if resource_json['resourceType'].lower() == 'supplyrequest': order.order_id = int( fhir_resource.identifier.get(system=DV_FHIR_URL + 'orderId').value) if id_ and int(id_) != order.order_id: raise Conflict( 'url order id param does not match document order id') if hasattr(fhir_resource, 'authoredOn'): order.order_date = parse_date(fhir_resource.authoredOn) order.supplier = fhir_resource.contained.get( resourceType='Organization').id order.created = clock.CLOCK.now() order.supplierStatus = fhir_resource.extension.get( url=DV_FULFILLMENT_URL).valueString fhir_device = fhir_resource.contained.get(resourceType='Device') order.itemName = fhir_device.deviceName.get( type='manufacturer-name').name order.itemSKUCode = fhir_device.identifier.get(system=DV_FHIR_URL + 'SKU').value order.itemQuantity = fhir_resource.quantity.value fhir_patient = fhir_resource.contained.get(resourceType='Patient') fhir_address = fhir_patient.address[0] order.streetAddress1 = fhir_address.line[0] order.streetAddress2 = '\n'.join(fhir_address.line[1:]) order.city = fhir_address.city order.stateId = get_code_id(fhir_address, self.code_dao, 'state', 'State_') order.zipCode = fhir_address.postalCode order.orderType = fhir_resource.extension.get( url=DV_ORDER_URL).valueString if id_ is None: order.version = 1 else: # A put request may add new attributes existing_obj = self.get(self.get_id(order)) if not existing_obj: raise NotFound('existing order record not found') order.id = existing_obj.id order.version = expected_version order.biobankStatus = fhir_resource.biobankStatus if hasattr( fhir_resource, 'biobankStatus') else None try: order.barcode = fhir_resource.extension.get( url=DV_BARCODE_URL).valueString except ValueError: order.barcode = None return order
def test_list_item_at_index(self): fhir = SimpleFhirR4Reader({'a': [2, 4, 6, 8]}) self.assertEqual(fhir.get('a', 1), 4)
def test_list_key_lookup(self): fhir = SimpleFhirR4Reader(['foo', 'bar', 'baz']) self.assertEqual(fhir[1], 'bar') self.assertEqual(fhir[-1], 'baz')
def test_list_filter_function_key(self): fhir = SimpleFhirR4Reader(list(range(10))) odds_only_filter = lambda x: x % 2 self.assertEqual(list(fhir.get(odds_only_filter)), [1, 3, 5, 7, 9])
def test_attribute_lookup(self): fhir = SimpleFhirR4Reader({'foo': {'bar': {'baz': 123}}}) self.assertEqual(fhir.foo.bar.baz, 123)
def test_nested_dict_lookup(self): fhir = SimpleFhirR4Reader({'a': {'b': 'bar'}}) self.assertEqual(fhir.get('a', 'b'), 'bar')
def test_dict_lookup(self): fhir = SimpleFhirR4Reader({'a': 'foo'}) self.assertEqual(fhir.get('a'), 'foo')
def setUp(self): self.fhir = SimpleFhirR4Reader(EXAMPLE_SUPPLY_REQUEST)
def test_dict_key_lookup(self): fhir = SimpleFhirR4Reader({'a': 'foo'}) self.assertEqual(fhir['a'], 'foo')
def _put_supply_delivery(self, resource, bo_id): # handle invalid FHIR documents try: fhir = SimpleFhirR4Reader(resource) participant_id = fhir.patient.identifier.value p_id = from_client_participant_id(participant_id) update_time = dateutil.parser.parse(fhir.occurrenceDateTime) carrier_name = fhir.extension.get(url=DV_FHIR_URL + 'carrier').valueString eta = None if hasattr(fhir['extension'], DV_FHIR_URL + 'expected-delivery-date'): eta = dateutil.parser.parse( fhir.extension.get(url=DV_FHIR_URL + "expected-delivery-date").valueDateTime) tracking_status = fhir.extension.get(url=DV_FHIR_URL + 'tracking-status').valueString if tracking_status: tracking_status = tracking_status.lower() except AttributeError as e: raise BadRequest(e.message) except Exception as e: raise BadRequest(e.message) _id = self.dao.get_id( ObjDict({ 'participantId': p_id, 'order_id': int(bo_id) })) if not _id: raise Conflict( 'Existing SupplyRequest for order required for SupplyDelivery') dvo = self.dao.get(_id) if not dvo: raise Conflict( 'Existing SupplyRequest for order required for SupplyDelivery') tracking_status_enum = \ getattr(OrderShipmentTrackingStatus, tracking_status.upper(), OrderShipmentTrackingStatus.UNSET) dvo.shipmentLastUpdate = update_time.date() dvo.shipmentCarrier = carrier_name if eta: dvo.shipmentEstArrival = eta.date() dvo.shipmentStatus = tracking_status_enum if not p_id: raise BadRequest('Request must include participant id') merged_resource = None # Note: PUT tracking status should only be 'delivered'. POST should be either 'enroute/in_transit'. if tracking_status in [ 'in_transit', 'enroute', 'delivered' ] and self._to_mayo(fhir) and not dvo.biobankOrderId: # Send to mayolink and create internal biobank order response = self.dao.send_order(resource, p_id) merged_resource = merge_dicts(response, resource) merged_resource['id'] = _id logging.info( 'Sending salivary order to biobank for participant: %s', p_id) self.dao.insert_biobank_order(p_id, merged_resource) response = super(DvOrderApi, self).put(bo_id, participant_id=p_id, skip_etag=True, resource=merged_resource) return response
def test_list_key_range(self): fhir = SimpleFhirR4Reader(['foo', 'bar', 'baz']) self.assertEqual(fhir[1:], ['bar', 'baz']) self.assertEqual(fhir[:-1], ['foo', 'bar'])