Alberta CII (Community Information Integration)¶
This document defines the FHIR mapping and implementation details for the Alberta CII (Community Information Integration) project.
Overview¶
Alberta Netcare Electronic Health Record collects key patient health information from facilities across the province and makes it available at the point of care. It currently includes lab test results, radiology, dispensed drugs, and many types of acute care and specialist reports. Until now, primary care data has only been shared among the same instance of an Electronic Record System (EMR). This project aims to incorporate primary care data to Alberta Netcare.
The data to be captured will be based on the Pan-Canadian Primary Health Care Electronic Medical Record Content Standard.
Polaris CII will be using an XML message wrapper as a container to transport a set of encounters and/or PDF files to the CI2N integration hub web service. Encounters or Consult Note PDF files will be sent in a submission envelope.
Project Details¶
| Field | Value |
|---|---|
| Ticket | INIT-156 |
| Product Manager | Anton Shaffer (Oscar EMR), Angela Lee (Polaris) |
| Project Manager | Dan Black |
| Designer | Renato Pagnani |
| Technical Lead | Brad Swanson |
| FHIR Experts | Raman Dhanoa & Shawn Vincent |
| Operations | Polaris Platform Team |
Definitions¶
- CII - Community Information Integration
- CI2N - Community Information to Netcare
- Netcare - Alberta's Provincial EHR
- CED - Community Encounter Digest is the extract that is sent when a patient completes an encounter with a family physician (aka EMR_EXTRACT)
- PFDs - Pulmonary Function Diagnostic tests
- PDF Extract - PDF_EXTRACTs can be operative reports, consult letters, or pulmonary function tests. Providers have to be validated and enabled for each type of PDF extract.
Use Cases¶
As a provider, I need to: - Participate in Alberta's CII program to share information about my patient encounters and/or generated consult reports and letters - Respect my patient's right to privacy and be able to mark their records as not to be uploaded to Netcare - Have updates to patient records uploaded automatically to CI2N - Mark specific reports/letters/PFDs to be submitted to Netcare
Business Requirements¶
Confidentiality¶
- Masking Patient Records: At the very least, EMRs must be able to flag patients that should not have their data sent to Netcare
- CII has the capability to provide additional granularity with respect to only masking the patient's address, masking particular encounters, and/or masking particular observations
Extract Types¶
- Automatically Sent: CED (Community Encounter Digest)
- Sent When Explicitly Marked: Operative reports, Consult letters, PFDs
Data Modelling & Submission Criteria¶
- Standardized Data Model: All data must be modelled uniformly to be able to determine what should be included in exports
- Workflow Considerations: Identify encounter status (F = Final, U = Update, D = Delete) for different workflows and observation types
- Encounter Completion: Define rules for when an encounter is considered complete and ready for submission
- Non-Encounter Submissions: Some PDF extracts may need to be created outside of standard encounter workflows
- PDF Extract Type and Sub-type: PDF extracts need to have an associated type and sub-type
- Types: Consult Report, Operative Summary, PFD
- Consult Report sub-type is "Consult Report"
- Operative Summary sub-type comes from a predefined list of sub-types
- PFD sub-type comes from a predefined list of sub-types
- PDF Extract Deletion Handling: If a PDF extract was previously sent but is now marked as "not to be sent," a deletion (D) should be submitted
- Submission Frequency: Submissions occur nightly and only send data changed since the last submission
- Initial CII Activation: Enabling CII should set the last submission date (or equivalent) to avoid sending historical data unnecessarily
- Historical Data Limitations: Only changes within the past year should be submitted; older details should be excluded even if the encounter falls within the past year
- Error Handling & Retries: Validation errors should not trigger retries, but failures due to CI2N endpoint unavailability should be retried
- Netcare PDF Requirements: PDFs sent to Netcare must have very specific information in the header and footer of each page
- Header: 'Name: {Upper(last)}, {first} PHN/ULI: {phn} DOB: {dob} Gender: {gender}'
- Footer: 'Page {currentPage} of {totalPages}'
- PDF Extract Size: The Base64 encoded size of the PDF data cannot exceed 8MB
- PDF Deletions & Patients opting out of CII: If a PDF is being removed from Netcare, a boilerplate PDF is actually sent in its place, called a "Cancelled Specialty Consult Report"
- Oversized PDFs: PDFs > 8MBs Base64 encoded will not be sent. We should track the resources that were not able to be sent
- Overall Submission File Size: Any one submission file cannot exceed 20MB
Tenant & Provider Settings¶
- Tenant Settings:
- Each tenant must have a recorded Submitting Site ID and Submitting Site Name
- Each tenant must configure which extract types are enabled
- Provider Settings:
- Each provider must have individual toggles for enabling/disabling each extract type
- Each provider must have an associated facility, particularly for multi-site environments
- Record the "service delivery location ID" (an AH facility ID) and the "service delivery location name"
- Each provider must have an associated PRACID (this is the Alberta Health billing ULI)
- Each provider must have a list of expertise
FHIR Mappings¶
The Polaris FHIR mappings have been defined by the FHIR Team and are available in the CII FHIR Mapping Details section of this document.
User Interface Components¶
There are 3 main web components to be added to the EMR's UI:
1. CII Management¶
Configuration of CII requires that specific locations and providers have the ability to send different extracts to CII. As well, there are details about locations and providers that must be provided for the submissions to be valid.
2. Send PDFs to Netcare¶
Sending PDF documents to Netcare requires user classification of the document so that it is categorized properly in Netcare.
This web component operates in 2 modes: existing, and new.
Existing documents¶
The EMR will pass in the system and value of the document as attributes on the web component. The web component will save the user's choices as they are made.
New documents¶
The system and value of the document identifier is unknown when creating a new document. In this scenario, the expected workflow is: 1. The user starts to create the new document, and the Configure document to send to Netcare web component is available for the user to choose options from the dropdown 2. The user saves the document 3. The EMR saves the document in its system of record 4. In the background the document will sync to MedPlum 5. The EMR will post a save event to the web component with the system and value of the document 6. The web component will save the user's choices
3. Patient Confidentiality¶
CII requires, at a minimum, the ability to mask a patient's data from being sent to CII. Patients that are marked as opted out of CII should not have the Configure document to send to Netcare UI displayed for documents on their chart.
Testing and Conformance¶
EMRs using the Polaris CII implementation must complete conformance with Alberta Health. Here's a high level overview of Alberta Health's integration process:
Access to a Sandbox environment can be granted so that developers and QA can verify their implementations before final conformance.
Once Sandbox testing is complete, the e2e conformance testing can begin. The conformance test sessions are conducted live via screen sharing. They are arranged by the Alberta Health QA with the vendor, Alberta Health business team, AHS QA, Orion Health, and CII/CPAR Operations. During these sessions, the vendor submits transactions for each service based on a script provided by the AH QA team. There is at least one pre-conformance session (additional sessions are arranged if many anomalies are discovered) and a final conformance test session. All sessions are recorded, and screenshots are taken throughout. A final report is created for production approval by Alberta Health, containing a detailed description of the testing and the results.
Architecture¶
Data Flow Sequence Diagram¶
sequenceDiagram
participant EMR as EMR Applications
participant FHIR as MedPlum FHIR Repository
participant CII as CII Web Components
participant BG as CII Background Submission Process
participant AH as Alberta Health
EMR->>FHIR: Replicate encounter/file data
CII->>FHIR: Query settings
CII->>FHIR: Update settings
BG->>FHIR: Extract data for submission
BG->>BG: Generate XML file
BG->>AH: Submit XML to Alberta Health
BG->>FHIR: Store XML file and submission information
Security & Privacy¶
Web components and Bots will be using the ID framework already established for Polaris.
As stated in the requirements, patients may wish to opt out of their data being sent to Netcare. The UI allows for this scenario and there should be accompanying unit tests for the submission code.
Integration¶
Alberta Health CPAR/CII Sandbox access will help us with our development and acceptance testing.
User Permissions¶
EMR applications will manage access to the CII feature. MedPlum does have the ability to set access policies for users, and it is expected that the EMR will replicate users into MedPlum with appropriate access permissions for CII.
Feature Flag¶
EMRs will need to be able to toggle access to this feature at the tenant level.
App State maintained by CII App (not sent from EMR)¶
For each organization - CII enabled - submitting site identifier - submitting site name
For each tenant location - CED enabled - Consult Reports enabled - Operative Summary enabled - Pulmonary Function Reports enabled - Facility ID
For each provider - CED enabled - Consult Reports enabled - Operative Summary enabled - Pulmonary Function Reports enabled - Prac ID - Specialty
XXX per PDF document
So the CII fields S1, S2, S3, C1, C3, C5, B4, B5, B6, B7, B8 are not expected to be provided by the EMR data feed.
General Polaris Mapping Guidance¶
Submission¶
Changed/Deleted since last submission¶
- how to do this?
- app state / storing previous submissions / MedPlum timestamps&audit?
- Aaron?
- last submission date: AppState?
Collecting data for submission¶
- preemptively by MedPlum?
- consider changed/deleted
- what happens if a submission doesn't go, and we need to do another?
- could changes (including add/modify/delete --just be captured in MedPlum by day, and then fetch the relevant days?
Masking¶
- only Patient-level masking will be supported by CII.
- it is NOT expected that the source EMRs will provide any patient-level masking information.
- masking information will be stored in a PolarisCoreAppState instance associated with the patient.
- NEXT-566
Example:
{ "code": { "coding": [ { "system": "https://fhir.apps.health/cii/CodeSystem/app-state", "code": "opt-out", "display": "CII Patient Masked" } ] }, "subject": { "reference": { "system": "https://fhir.apps.health/NamingSystem/Juno-32-patient-identifier", "value": "1234567890" }, "type": "Patient", "display": "Example Patient" }, "extension": [ { "url": "https://fhir.apps.health/StructureDefinition/app-state-boolean-value", "valueBoolean": true } ] }
Organization (CII Tenant/Submitting Site)¶
Modelling is identical to CPAR. See CPAR Polaris Mapping for details.
Location (FHIR Location, CII Service Delivery Location, Juno Site)¶
- C1_Service_Delivery_Location_Identifier
-
not stored in Juno and will come fromt he CII config UI.
-
C2_Service_Delivery_Location_Name From PolarisCoreLocation.name
-
C3_Service_Delivery_Location_Type_Of_Service
-
supposed to differentiate between in-person/phone/virtual appointments. Not required though, and Healthquest doesn’t send it.
-
C4_Service_Delivery_Postal_Code From PolarisCoreLocation.address.postalCode
-
C5_Service_Delivery_Location_Identifier_Type
- hard coded
Encounter¶
Primary care EMRs do not generally model the concept of an Encounter. There are Appointments and Encounter Notes, but no explicit Encounter.
The standard way of dealing with this is to heuristically assign Encounters based on "everything that happened in a specific day".
Rather than forcing each EMR to implement this logic, we are proposing to implement it in MedPlum as a postprocessing step.
-
D01_Encounter_Identifier Derived from PolarisCoreEncounter.identifier
-
D1_Encounter_Request_Date Derived from PolarisCoreAppointment.requestedPeriod.start
-
D2_Client_Encounter_Reason Derived from PolarisCoreEncounter.reasonCode.text
-
D3_Encounter_Date Derived from PolarisCoreEncounter.period.start
-
D4_Encounter_Mode Derived from PolarisCoreEncounter.class.code
-
D5_Encounter_Payment_Source
-
Not required. Not supported for Juno. Juno does not have native billing.
-
D7_Encounter_Billing_Code
- Not required. Not supported for Juno. Juno does not have native billing.
- when and if we need to model this in the future, we will likely model a Claim resource, like this example:
Possible Future Claim Modelling
{
"resourceType": "Claim",
"identifier": { ...claim identifier... }
"status": "active",
"type": {
"coding": {...some code for 'provincial billing'...}
},
"use": "claim",
"patient": { "identifier": { ... patient identifier... } },
"billablePeriod": { "start": "2025-04-01" },
"item": [
{
"sequence": 1,
"productOrService": {
"coding": [
{
"system": "... provincial billing code system...",
"code": "13030",
"display": "Family practice visit"
}
]
}
}
],
"encounter": [{ "reference": "Encounter/456" }]
}
-
D02_Encounter_Masking_Flag Not supported for CII integration (only patient-level masking with App State will be supported)
-
D03_Encounter_Status
This is weird.
This is an aggregate of the document statuses of the documents associated with the encounter. ('Final', 'Updated', 'Deleted'). Calculated based on previous submissions for this encounter. Not drawn from Polaris or the EMRs.
- D04_Encounter_Export_DateTime
- Derived from PolarisCoreEncounter.meta.lastUpdated
- This is maintained by MedPLum and should not be sent by the EMR.
- "Represents the date/time to millis of when the encounter is exported from the EMR System. Date is used to chronologically enforce Hub processing of received encounters."
- we will use the MedPlum timestamp
Patient (aka Client)¶
- A1_Client_Identifier
- A2_Client_Identifier_Type
- A3_Client_Identifier_Assigning_Authority Derived from PolarisCorePatient.identifier (specifically the JurisdictionalID slice)
Polaris patients should in general have 2 IDs:
- The Polaris ID, with system identifier:
https://https://fhir.apps.health/NamingSystem/{product-instance-identifier}-patient-identifier -
Any Canadian jurisdictional health card numbers recorded for the patient, with system identifiers based on province here: https://build.fhir.org/ig/HL7-Canada/ca-baseline/StructureDefinition-profile-patient.html#jurisdictional-health-number-jhn
-
A01_Client_Last_Name
-
A02_Client_Given_Name Derived from PolarisCorePatient.name
-
A4_Client_Date_Of_Birth Derived from PolarisCorePatient.birthDate
-
A5_Client_Administrative_Gender Derived from PolarisCorePatient.gender
-
A9_Client_Status_Code Derived from PolarisCorePatient.active
-
A11_Client_Rostered_Start_Date
- A11_Client_Rostered_Start_Date_Date_Precision
- A12_Client_Rostered_End_Date
- Somehow from CPAR Polaris App? XXX
- Dan: "We don't track this with CPAR. Healthquest sends the end_date of the patient record, if it exists (so the patient was marked as inactive in the EMR)."
XXXX where in FHIR does the "end_date of the patient record live"????
-
A14_Client_Residence_Postal_Code Derived from PolarisCorePatient.address.postalCode
-
A03_Client_Masking_Flag Stored in App State (see above)
-
A04_Client_Address_Masking_Flag
- Hard code "N" (not masked)
- From Dan: "We don't do this level of masking, but the field is required. I think we just pass 'N'."
Provider (Practitioner)¶
PolarisCorePractitioner - B1_Provider_Last_Name - B2_Provider_First_Name - B3_Provider_Middle_Name Derived from PolarisCorePractitioner.name
- B4_Provider_Identifier
- B5_Provider_Identifier_Type
- B6_Provider_Identifier_Assigning_Authority
- B7_Provider_Role
- B8_Provider_Expertise Stored in AppState associated with the PolarisProvider. XXX keys?
PDF Documents¶
Additional requirement: (not used by CII), if the EMR has coded the referenced document with some internal code, it should be placed in the DocumentReference as DocumentReference.identifier, with a system of "https://fhir.apps.health/NamingSystem/
Issue: XXX how do you embed components on entities that are not yet saved / have an ID. Smart on FHIR? Can we wait until we save? Chicken and egg problem. - Dan: "Fairly certain we have a solution. We’ve designed a workflow where the component loads it’s value from app-state (with the subject as the document reference (or whatever)), and will load nothing if no record is found. Actually getting the component to save will be done via event messaging between the EMR and the component. The EMR would:
- execute the create/save on it’s database
- save the document to MedPlun
- dispatch a “save” event for the web component with the system and value of the document resource
- the component creates an app-state record for the document and the user’s selections
Unresolved: XXX Need to define how the content of the PDF is sent.
Unresolved: XXX Need to define how the content is augmented with CII headers and footers
- PDF_Document_Type_Coding
- Document_Type_Description_Text
- Document_Type_Code
- Document_Type_Coding_System
- PDF_Submission_Type
- PDF_Document_Masking_Flag
XXX I assume that all of these fields map from App State.
Coded Clinical Data¶
Observation Health Concern¶
PolarisCoreCondition - a structured Problem List entry.
-
E11_Observation_Health_Concern Derived from Condition.code.text
-
E12_Observation_Health_Concern_Date_Of_Onset
- E12_Observation_Health_Concern_Date_Of_Onset_Date_Precision Derived from Condition.onset
VItals - coding¶
Observations representing vitals can be differentiated from lab results using Observation.category
VItals are coded with LOINC codes (system: http://loinc.org)
| Observation | LOINC | Rationale |
|---|---|---|
| Systolic Blood Pressure | 8480-6 | Interoperability-standard code for Systolic BP in FHIR Vital Signs profiles (adopted verbatim by Canada Baseline) |
| Diastolic Blood Pressure | 8462-4 | Interoperability-standard code for Diastolic BP in FHIR Vital Signs profiles (adopted verbatim by Canada Baseline) |
| Body Height | 8302-2 | Interoperability-standard code for Body height in FHIR Vital Signs profiles (adopted verbatim by Canada Baseline) |
| Body Weight | 29463-7 | Interoperability-standard code for Body weight in FHIR Vital Signs profiles (adopted verbatim by Canada Baseline) |
| Waist Circumference | 8280-0 | Widely adopted in international IGs (e.g. AU Core) for waist circumference measurement; not part of core Canada Baseline profiles |
| Oxygen Saturation | 2708-6 | Interoperability-standard code for Oxygen saturation in FHIR Vital Signs profiles (adopted verbatim by Canada Baseline) |
| Heart Rate (Pulse) | 8867-4 | Interoperability-standard code for Heart rate in FHIR Vital Signs profiles (adopted verbatim by Canada Baseline) |
| Head Circumference | 9843-4 | Interoperability-standard code for Head circumference in FHIR Vital Signs profiles (adopted verbatim by Canada Baseline) |
- 1 "Observation-vitalsigns - FHIR v6.0.0-ballot2"
- 2 "LOINC 8280-0 Waist Circumference at umbilicus by Tape measure"
- 3 "Observation - Waist Circumference - AU Core Implementation Guide v0.4.0 ..."
Blood Pressure (systolic/diastolic)¶
-
E23_Observation_Systolic_Blood_Pressure Derived from Observation.valueQuantity.value and Use LOINC Code for Systolic Blood Pressure: 8480-6
-
E24_Observation_Diastolic_Blood_Pressure Derived from Observation.valueQuantity.value and Use LOINC Code for Diastolic Blood Pressure: 8462-4
Height + units¶
- E28_Observation_Height
- E29_Observation_Height_Unit_Of_Measure Derived from Observation.valueQuantity.value and .valueQuantity.unit Use LOINC Code for Height: 8302-2
Weight + units¶
- E30_Observation_Weight
- E31_Observation_Weight_Unit_Of_Measure Derived from Observation.valueQuantity.value and .valueQuantity.unit Use LOINC Code for Weight: 29463-7 (modified in Polaris v1.1.0 spec from 3140-5)
Waist Circumference + units¶
- E32_Observation_Waist_Circumference
- E33_Observation_Waist_Circumference_Unit_Of_Measure Derived from Observation.valueQuantity.value and .valueQuantity.unit Use LOINC Code for Waist Circumference: 8280-0 (modified in Polaris v1.1.0 spec from 3141-1)
O2Sat + units¶
- E35_Oxygen_Saturation
- E36_Oxygen_Saturation_Unit_Of_Measure Derived from Observation.valueQuantity.value and .valueQuantity.unit Use LOINC Code for O2Sat: 2708-6 (modified in Polaris v1.1.0 spec from 3142-9)
Pulse + units¶
- E37_Pulse
- E38_Pulse_Unit_Of_Measure Derived from Observation.valueQuantity.value and .valueQuantity.unit Use LOINC Code for Pulse: 8867-4 (modified in Polaris v1.1.0 spec from 3143-7)
Head Circumference + units¶
- E39_Head_Circumference
- E40_Head_Circumference_Unit_Of_Measure Derived from Observation.valueQuantity.value and .valueQuantity.unit Use LOINC Code for Head Circumference: 9843-4 (modified in Polaris v1.1.0 spec from 3144-5)
Allergies¶
-
E18_Observation_Allergy_Agent Derived from AllergyIntolerance.code.text
-
Association of Allergy with Encounter Derived from AllergyIntolerance.recordedDate
Intervention¶
-
F1_Intervention_Treatment Derived from Procedure.code.text
-
F2_Intervention_Treatment_Date
- F2_Intervention_Treatment_Date_Date_Precision Derived from Procedure.performedDateTime
Lab Request¶
-
distinguish lab request from ServiceRequest.category
-
G1_Lab_Test_Name_Ordered Derived from ServiceRequest.code.text
-
G2_Lab_Test_Order_Date Derived from ServiceRequest.authoredOn
Lab Report¶
NOTE: this is only for lab results that are entered directly into the EMR (this is extremely rare in practice -- most EMRs have this functionality and very few users use it, as nearly all labs come in through lab interfaces).
-
distinguish lab observation using Observation.category
-
H1_Lab_Test_Performed_Date Derived from Observation.effectiveDateTime
-
H2_Lab_Test_Result_Name Derived from Observation.code.text
-
H3_Lab_Test_Result_Value Derived from Observation.valueQuantity.value
-
H4_Lab_Test_Result_Unit_Of_Measure Derived from Observation.valueQuantity.unit
DI Request¶
-
distinguish DI request from ServiceRequest.category
-
I1_Diagnostic_Imaging_Test_Ordered Derived from ServiceRequest.code.text
-
I2_Diagnostic_Imaging_Test_Ordered_Date Derived from ServiceRequest.authoredOn
DI Result¶
- J1_Di_Test_Performed_Date Derived from Observation.effectiveDateTime
MedicationRequest¶
-
M1_Medication_Prescribed_Name Derived from MedicationRequest.contained[Medication].code.text
-
M2_Medication_Prescription_Date
-
M2a_Medication_Prescription_Date_Date_Precision Derived from MedicationRequest.authoredOn
-
M3_Prescription_Expected_Completion_Date
-
M3a_Prescription_Expected_Completion_Date_Date_Precision Same as M14 (except for titrated meds) Derived from MedicationRequest.dosageInstruction.timing.repeat.boundsPeriod.end
-
M13_Medication_Start_Date
-
M13a_Medication_Start_Date_Date_Precision Derived from MedicationRequest.dosageInstruction.timing.repeat.boundsPeriod.start
-
M14_Medication_End_Date
-
M14a_Medication_End_Date_Date_Precision Derived from MedicationRequest.dosageInstruction.timing.repeat.boundsPeriod.end
-
M5_Medication_Strength
-
M6_Medication_Strength_Unit_Of_Measure Derived from MedicationRequest.contained[Medication].ingredient.strength
-
M7_Medication_Dosage
-
M8_Medication_Dosage_Unit_Of_Measure Derived from MedicationRequest.dosageInstruction
-
M9_Medication_Form Derived from MedicationRequest.contained[Medication].form.text
-
M10_Medication_Frequency Derived from MedicationRequest.dosageInstruction
-
M11_Medication_Route Derived from MedicationRequest.dosageInstruction
-
M12_Medication_Number_Of_Repeat_Refills Derived from MedicationRequest.dosageInstruction
-
M91_Supply_Duration
- M92_Supply_Duration_Unit_Code Derived from MedicationRequest.dosageInstruction.expectedSupplyDuration
Dispensed Medication¶
-
N1_Medication_Dispensed Derived from MedicationDispense.medicationCodeableConcept.text
-
N2_Medication_Dispensed_Date Derived from MedicationDispense.whenHandedOver
Referral¶
-
distinguish referral request from ServiceRequest.category
-
K1_Referral_Service Derived from ServiceRequest.code.text
-
K2_Referral_Requested_Date
-
K2_Referral_Requested_Date_Date_Precision Derived from ServiceRequest.authoredOn
-
L1_Referral_Occurred_Date Derived from ServiceRequest.occurrenceDateTime
Immunization¶
-
O1_Vaccine_Administered_Name Derived from Immunization.vaccineCode.text
-
O2_Vaccine_Administered_Date
-
O2_Vaccine_Administered_Date_Date_Precision Derived from Immunization.occurrenceDateTime
-
O3_Vaccine_Administered_Lot_Number Derived from Immunization.lotNumber