def delete(self, request, questionnaire_id, format=None): """ Deletes A FHIR Questionnaire for the passed ID """ # Use the FHIR client lib to validate our resource. questionnaire_request = BundleEntryRequest( { "url": f"Questionnaire/{questionnaire_id}", "method": "DELETE", } ) questionnaire_entry = BundleEntry() questionnaire_entry.request = questionnaire_request # Validate it. bundle = Bundle() bundle.entry = [questionnaire_entry] bundle.type = "transaction" try: # Create the organization response = requests.post(PPM.fhir_url(), json=bundle.as_json()) logger.debug("Response: {}".format(response.status_code)) response.raise_for_status() return response.json() except Exception as e: logger.exception( "API/Questionnaire: Delete Questionnaire error: {}".format(e), exc_info=True, extra={ "request": request, }, )
def _create_bundle_response(entries): bundle = Bundle() bundle.type = 'searchset' bundle.entry = [] bundle.total = len(entries) for item in entries: entry = BundleEntry() entry.fullUrl = '{}{}'.format(request.base_url, item.id.value) entry.resource = item bundle.entry.append(entry) return bundle
def questionnaire_transaction(cls, questionnaire, questionnaire_id=None): """ Accepts a Questionnaire object and builds the transaction to be used to perform the needed operation in FHIR. Operations can be POST or PUT, depending on if an ID is passed. If the object does not need to be created or updated, the operation will return as a success with an empty response object. :param questionnaire: The Questionnaire object to be persisted :type questionnaire: dict :param questionnaire_id: The ID to use for new Questionnaire, defaults to None :type questionnaire_id: str, optional :return: The response if the resource was created, None if no operation needed :rtype: dict """ # Check for a version matching the created one version = questionnaire["version"] query = {"identifier": f"{FHIR.qualtrics_survey_version_identifier_system}|{version}"} if questionnaire_id: query["_id"] = questionnaire_id questionnaires = FHIR._query_resources("Questionnaire", query) if questionnaires: # No need to recreate it logger.debug(f"PPM/Qualtrics: Questionnaire already exists for survey version {version}") return None # Use the FHIR client lib to validate our resource. questionnaire = Questionnaire(questionnaire) questionnaire_request = BundleEntryRequest( { "url": f"Questionnaire/{questionnaire_id}" if questionnaire_id else "Questionnaire", "method": "PUT" if questionnaire_id else "POST", } ) questionnaire_entry = BundleEntry() questionnaire_entry.resource = questionnaire questionnaire_entry.request = questionnaire_request # Validate it. bundle = Bundle() bundle.entry = [questionnaire_entry] bundle.type = "transaction" # Create the organization response = requests.post(PPM.fhir_url(), json=bundle.as_json()) logger.debug("PPM/Qualtrics: FHIR Response: {}".format(response.status_code)) response.raise_for_status() return response.json()
def _query_resources(queries=[]): # Build the transaction transaction = { 'resourceType': 'Bundle', 'type': 'transaction', 'entry': [] } # Get the questionnaire for query in queries: transaction['entry'].append( {'request': { 'url': query, 'method': 'GET' }}) # Query for a response # Execute the search response = requests.post(settings.FHIR_URL, headers={'content-type': 'application/json'}, json=transaction) response.raise_for_status() # Build the objects bundle = Bundle(response.json()) return bundle
def import_collection(self, filepath, options): """Iterate over a collection bundle and dispatch to the importers for the various resource types""" verbosity = options['verbosity'] strict = options['strict'] logger.info(f'Importing {filepath}') with open(filepath, 'r') as handle: fhirjs = json.load(handle) bundle = Bundle(fhirjs, strict=strict) for entry in fhirjs[ "entry"]: # TODO: Is there something more elegant than mucking through a JSON structure? res = entry[ "resource"] # TODO: Is there something more elegant than mucking through a JSON structure? res_type = res["resourceType"] # OC: Creating spans from the tracer of the execution context will divide the overall execution into small, measurable chunks tracer = execution_context.get_opencensus_tracer() with tracer.span(name=f'Import {res_type}'): logger.debug(f'Resource: {res}') if res_type == "Patient": self.import_patient(res, options) if res_type == "Condition": self.import_condition(res, options) if res_type == "Observation": self.import_observation(res, options)
def parse_response(self, response: Dict) -> None: """ Parse the response from the FHIR server to which we have sent our task. The response looks something like this: .. code-block:: json { "resourceType": "Bundle", "id": "cae48957-e7e6-4649-97f8-0a882076ad0a", "type": "transaction-response", "link": [ { "relation": "self", "url": "http://localhost:8080/fhir" } ], "entry": [ { "response": { "status": "200 OK", "location": "Patient/1/_history/1", "etag": "1" } }, { "response": { "status": "200 OK", "location": "Questionnaire/26/_history/1", "etag": "1" } }, { "response": { "status": "201 Created", "location": "QuestionnaireResponse/42/_history/1", "etag": "1", "lastModified": "2021-05-24T09:30:11.098+00:00" } } ] } The server's reply contains a Bundle (https://www.hl7.org/fhir/bundle.html), which is a container for resources. Here, the bundle contains entry objects (https://www.hl7.org/fhir/bundle-definitions.html#Bundle.entry). """ bundle = Bundle(jsondict=response) if bundle.entry is not None: self._save_exported_entries(bundle)
def patch(request, questionnaire_id, format=None): """ Uses FHIR+json patch to update a resource """ # Base 64 encode the patch parameters = Parameters(request.data) questionnaire_request = BundleEntryRequest( { "url": f"Questionnaire/{questionnaire_id}", "method": "PATCH", } ) questionnaire_entry = BundleEntry() questionnaire_entry.resource = parameters questionnaire_entry.request = questionnaire_request # Validate it. bundle = Bundle() bundle.entry = [questionnaire_entry] bundle.type = "transaction" try: # Create the organization response = requests.post(PPM.fhir_url(), json=bundle.as_json()) logger.debug("Response: {}".format(response.status_code)) response.raise_for_status() return response.json() except Exception as e: logger.exception( "API/Questionnaire: Delete Questionnaire error: {}".format(e), exc_info=True, extra={ "request": request, }, )
def get_patients(): query = FHIRSearch(resource_type=Patient) patient_bundle = query.perform(smart.server) patients = extract_resources(patient_bundle.entry) next_page_url = get_next_url(patient_bundle.link) while next_page_url is not None: response = smart.server.request_json(next_page_url) next_page_bund = Bundle(response) next_page_resources = extract_resources(next_page_bund.entry) patients.extend(next_page_resources) next_page_url = get_next_url(next_page_bund.link) return patients
def _bundle(resources): # Build the bundle bundle = Bundle() bundle.type = 'transaction' bundle.entry = [] for resource in resources: # Build the entry request bundle_entry_request = BundleEntryRequest() bundle_entry_request.method = 'POST' bundle_entry_request.url = resource.resource_type # Add it to the entry bundle_entry = BundleEntry() bundle_entry.resource = resource bundle_entry.fullUrl = resource.id bundle_entry.request = bundle_entry_request # Add it bundle.entry.append(bundle_entry) return bundle
def get_patient(patient_email): # Build the transaction transaction = { 'resourceType': 'Bundle', 'type': 'transaction', 'entry': [] } # Add the request for the patient transaction['entry'].append({ 'request': { 'url': 'Patient?identifier=http://schema.org/email|{}'.format( quote(patient_email)), 'method': 'GET' } }) # Query for a response # Execute the search response = requests.post(settings.FHIR_URL, headers={'content-type': 'application/json'}, json=transaction) response.raise_for_status() # Build the objects bundle = Bundle(response.json()) # Check for the Patient if not bundle.entry[0].resource.entry or bundle.entry[ 0].resource.entry[0].resource.resource_type != 'Patient': logger.error("Patient could not be fetched", exc_info=True, extra={ 'patient': FHIR._obfuscate_email(patient_email), }) raise FHIR.PatientDoesNotExist() # Instantiate it patient = bundle.entry[0].resource.entry[0].resource return patient
def create_bundle(query, paginate=True): # Initialize searchset bundle b = Bundle() b.type = 'searchset' # Handle _summary arg # TODO: Refactor into separate function or decorator summary = request.args.get('_summary') if summary: if summary == 'count': b.total = len(query.all()) return b # TODO: Handle summary = True and summary = Text and summary = Data # Apply pagination if desired and set links if paginate: p, per_page = paginate_query(query=query) b = set_bundle_page_links(bundle=b, pagination=p, per_page=per_page) records = p.items b.total = p.total # Otherwise, execute query as-is else: records = query.all() b.total = len(records) # Loop through results to generate bundle entries for r in records: try: # Try creating a search entry for the bundle e = create_bundle_search_entry(obj=r) # If entry can be made (e.g. if object has working fhir attribute) append to bundle try: b.entry.append(e) except AttributeError: b.entry = [e] # Silently ignore items returned in the query that can't be turned into bundle entries except TypeError: # TODO: Improve error handling or feedback on this function for items that fail to be created pass # TODO: Add OperationOutcome to bundle attribute return b
def make_bundle(value: str, sp_unique: bool = False) -> Bundle: system = "https://some_system" jd = { "type": "transaction", "entry": [ BundleEntry( jsondict={ "request": BundleEntryRequest( jsondict={ "method": "POST", "url": "Questionnaire", "ifNoneExist": f"identifier={system}|{value}", }).as_json(), "resource": Questionnaire( jsondict={ "name": "some_questionnaire_name", "status": "active", "identifier": [ Identifier(jsondict={ "system": system, "value": value }).as_json() ], }).as_json(), }).as_json() ], } # Note: the .as_json() conversions are necessary. if sp_unique: raise NotImplementedError( "sp_unique method not implemented; see " "https://github.com/hapifhir/hapi-fhir/issues/3141") return Bundle(jsondict=jd)
def get_resources(questionnaire_id, patient_email, dry=False): # Build the transaction transaction = { 'resourceType': 'Bundle', 'type': 'transaction', 'entry': [] } # Get the questionnaire transaction['entry'].append({ 'request': { 'url': 'Questionnaire?_id={}'.format(questionnaire_id), 'method': 'GET' } }) # Add the request for the patient transaction['entry'].append({ 'request': { 'url': 'Patient?identifier=http://schema.org/email|{}'.format( quote(patient_email)), 'method': 'GET' } }) # Query for a response # Execute the search response = requests.post(settings.FHIR_URL, headers={'content-type': 'application/json'}, json=transaction) response.raise_for_status() # Build the objects bundle = Bundle(response.json()) # Check for the questionnaire if not bundle.entry[0].resource.entry or bundle.entry[ 0].resource.entry[0].resource.resource_type != 'Questionnaire': logger.error("Questionnaire could not be fetched", exc_info=True, extra={ 'questionnaires': questionnaire_id, }) raise FHIR.QuestionnaireDoesNotExist() # Instantiate it questionnaire = bundle.entry[0].resource.entry[0].resource # Check for the patient if not dry: if not bundle.entry[1].resource.entry or bundle.entry[ 1].resource.entry[0].resource.resource_type != 'Patient': logger.error("Patient could not be fetched", exc_info=True, extra={ 'patient': FHIR._obfuscate_email(patient_email), 'questionnaires': questionnaire_id, 'bundle': bundle.as_json(), }) raise FHIR.PatientDoesNotExist() # Get it patient = bundle.entry[1].resource.entry[0].resource else: # In dry mode, use a fake patient patient = FHIR.get_demo_patient(patient_email) return questionnaire, patient