15.2.103. camcops_server.cc_modules.cc_fhir

camcops_server/cc_modules/cc_fhir.py


Copyright (C) 2012, University of Cambridge, Department of Psychiatry. Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

This file is part of CamCOPS.

CamCOPS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

CamCOPS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.


Implements communication with a FHIR server.

Fast Healthcare Interoperability Resources

https://www.hl7.org/fhir/

Our implementation exports:

  • patients as FHIR Patient resources;

  • task concepts as FHIR Questionnaire resources;

  • task instances as FHIR QuestionnaireResponse resources.

Currently PHQ9 and APEQPT (anonymous) are supported. Each task and patient (if appropriate is sent to the FHIR server in a single “transaction” Bundle). The resources are given a unique identifier based on the URL of the CamCOPS server.

We use the Python client https://github.com/smart-on-fhir/client-py/. This only supports one version of the FHIR specification (currently 4.0.1).

Testing: HAPI FHIR server locally

To test with a HAPI FHIR server locally, which was installed from instructions at https://github.com/hapifhir/hapi-fhir-jpaserver-starter (Docker). Most simply:

docker run -p 8080:8080 hapiproject/hapi:latest

with the following entry in the CamCOPS export recipient configuration:

FHIR_API_URL = http://localhost:8080/fhir

To inspect it while it’s running (apart from via its log):

Testing: Other

There are also public sandboxes at:

Intermittent problem with If-None-Exist

This problem occurs intermittently:

class camcops_server.cc_modules.cc_fhir.FHIRAnswerType(value)[source]

An enum for value type keys of QuestionnaireResponseItemAnswer.

class camcops_server.cc_modules.cc_fhir.FHIRAnsweredQuestion(qname: str, qtext: str, qtype: camcops_server.cc_modules.cc_fhir.FHIRQuestionType, answer_type: camcops_server.cc_modules.cc_fhir.FHIRAnswerType, answer: Any, answer_options: Optional[Dict[Any, str]] = None)[source]

Represents a question in a questionnaire-based task. That includes both the abstract aspects:

  • What kind of question is it (e.g. multiple-choice, real-value answer, text)? That can go into some detail, e.g. possible responses for a multiple-choice question. (Thus, the FHIR Questionnaire.)

and the concrete aspects:

  • what is the response/answer for a specific task instance? (Thus, the FHIR QuestionnaireResponse.)

Used for autodiscovery.

__init__(qname: str, qtext: str, qtype: camcops_server.cc_modules.cc_fhir.FHIRQuestionType, answer_type: camcops_server.cc_modules.cc_fhir.FHIRAnswerType, answer: Any, answer_options: Optional[Dict[Any, str]] = None) None[source]
Parameters
  • qname – Name (task attribute name) of the question, e.g. “q1”.

  • qtext – Question text (e.g. “How was your day?”).

  • qtype – Question type, e.g. multiple-choice.

  • answer_type – Answer type, e.g. integer.

  • answer – Actual answer.

  • answer_options – For multiple-choice questions (MCQs), a dictionary mapping answer codes to human-legible display text.

property is_mcq: bool

Is this a multiple-choice question?

questionnaire_item() Dict[source]

Returns a JSON/dict representation of a FHIR QuestionnaireItem.

questionnaire_response_item() Dict[source]

Returns a JSON/dict representation of a FHIR QuestionnaireResponseItem.

class camcops_server.cc_modules.cc_fhir.FHIRQuestionType(value)[source]

An enum for value type keys of QuestionnaireResponseItemAnswer.

class camcops_server.cc_modules.cc_fhir.FhirTaskExporter(request: CamcopsRequest, exported_task_fhir: ExportedTaskFhir)[source]

Class that knows how to export a single task to FHIR.

__init__(request: CamcopsRequest, exported_task_fhir: ExportedTaskFhir) None[source]
export_task() None[source]

Export a single task to the server, with associated patient information if the task has an associated patient.

parse_response(response: Dict) None[source]

Parse the response from the FHIR server to which we have sent our task. The response looks something like this:

{
  "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).

camcops_server.cc_modules.cc_fhir.fhir_observation_component_from_snomed(req: CamcopsRequest, expr: camcops_server.cc_modules.cc_snomed.SnomedExpression) Dict[source]

Returns a FHIR ObservationComponent (as a dict in JSON format) for a SNOMED CT expression.

camcops_server.cc_modules.cc_fhir.fhir_pk_identifier(req: CamcopsRequest, tablename: str, pk: int, value_within_task: str) fhirclient.models.identifier.Identifier[source]

Creates a “fallback” identifier – this is poor, but allows unique identification of anything (such as a patient with no proper ID numbers) based on its CamCOPS table name and server PK.

camcops_server.cc_modules.cc_fhir.fhir_reference_from_identifier(identifier: fhirclient.models.identifier.Identifier) str[source]

Returns a reference to a specific FHIR identifier.

camcops_server.cc_modules.cc_fhir.fhir_system_value(system: str, value: str) str[source]

How FHIR expresses system/value pairs.

camcops_server.cc_modules.cc_fhir.fhir_sysval_from_id(identifier: fhirclient.models.identifier.Identifier) str[source]

How FHIR expresses system/value pairs.

camcops_server.cc_modules.cc_fhir.make_fhir_bundle_entry(resource_type_url: str, identifier: fhirclient.models.identifier.Identifier, resource: Dict, identifier_is_list: bool = True) Dict[source]

Builds a FHIR BundleEntry, as a JSON dict.

This also takes care of the identifier, by ensuring (a) that the resource is labelled with the identifier, and (b) that the BundleEntryRequest has an ifNoneExist condition referring to that identifier.