Skip to content

Commit

Permalink
Addeed Practitioner & Medication Request Resources to Patient export.…
Browse files Browse the repository at this point in the history
… Created MockEncounterRepository.java for testing purposes.
  • Loading branch information
JLpro-cd committed Mar 2, 2025
1 parent 775a240 commit 4e8ce1b
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 6 deletions.
126 changes: 122 additions & 4 deletions app/femr/business/services/system/FhirExportService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
import ca.uhn.fhir.util.BundleBuilder;
import com.google.inject.Inject;
import femr.business.services.core.IFhirExportService;
import femr.data.daos.core.IEncounterRepository;
import femr.data.daos.core.IPatientRepository;
import femr.data.models.core.IPatient;
import femr.data.daos.core.IPrescriptionRepository;
import femr.data.daos.core.IUserRepository;
import femr.data.daos.system.UserRepository;
import femr.data.models.core.*;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.r5.model.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.*;


/**
* Converts between fEMR data models and FHIR models.
Expand All @@ -21,10 +25,14 @@ public class FhirExportService implements IFhirExportService {
private static final FhirContext fhirContext = FhirContext.forR5();

IPatientRepository patientRepository;
IEncounterRepository encounterRepository;
IPrescriptionRepository prescriptionRepository;

@Inject
public FhirExportService(IPatientRepository patientRepository) {
public FhirExportService(IPatientRepository patientRepository, IEncounterRepository encounterRepository, IPrescriptionRepository prescriptionRepository) {
this.patientRepository = patientRepository;
this.encounterRepository = encounterRepository;
this.prescriptionRepository = prescriptionRepository;
}

private BundleBuilder buildPatientBundle(int patientId) {
Expand Down Expand Up @@ -85,13 +93,93 @@ private void addPatientData(BundleBuilder bundleBuilder, int patientId) {
fhirPatient.setAddress(Collections.singletonList(address));
}

// Gather all encounters
List<? extends IPatientEncounter> encounters = encounterRepository.retrievePatientEncountersByPatientIdAsc(patientId);

// To ensure we don't add duplicate practitioners
Set<Integer> addedUserIds = new HashSet<>();

// Similarly, but for medication IDs
Set<Integer> addedMedIds = new HashSet<>();

for (IPatientEncounter encounter : encounters) {
List<? extends IPatientPrescription> prescriptions = prescriptionRepository.retrieveUnreplacedPrescriptionsByEncounterId(encounter.getId());

for (IPatientPrescription prescription : prescriptions) {
addMedicationRequestForPrescription(bundleBuilder, prescription, patientId, addedMedIds);
}

IUser nurse = encounter.getNurse();
IUser physician = encounter.getDoctor();
IUser pharmacist = encounter.getPharmacist();

// If nurse is present and not yet added, add them
if (nurse != null && !addedUserIds.contains(nurse.getId())) {
addPractitionerData(bundleBuilder, nurse, "Nurse");
addedUserIds.add(nurse.getId());
}

// If physician is present and not yet added, add them
if (physician != null && !addedUserIds.contains(physician.getId())) {
addPractitionerData(bundleBuilder, physician, "Physician");
addedUserIds.add(physician.getId());
}

// If pharmacist is present and not yet added, add them
if (pharmacist != null && !addedUserIds.contains(pharmacist.getId())) {
addPractitionerData(bundleBuilder, pharmacist, "Pharmacist");
addedUserIds.add(pharmacist.getId());
}
}

// city is omitted as it is where the patient is treated and not
// a property of the patient itself. So it will go with the encounter model.

// TODO: add photo

}

private void addMedicationRequestForPrescription(BundleBuilder bundleBuilder, IPatientPrescription prescription, int patientId, Set<Integer> addedMedIds) {
IMedication domainMedication = prescription.getMedication();

if (domainMedication == null) {
// If there's no medication info, we can't build the FHIR resources
return;
}

// Only add Medication resource if we haven't already.
int medId = domainMedication.getId();
if (!addedMedIds.contains(medId)) {
// Create a Medication resource
Medication fhirMedication = new Medication();
// Ex. "Medication-123"
fhirMedication.setId("Medication-" + medId);
fhirMedication.setCode(new CodeableConcept().setText(domainMedication.getName()));

// Add to bundle
IBase medEntry = bundleBuilder.addEntry();
bundleBuilder.addToEntry(medEntry, "resource", fhirMedication);

// Mark this medId as added
addedMedIds.add(medId);
}

// Now we create a MedicationRequest referencing that Medication
MedicationRequest fhirMedRequest = new MedicationRequest();
// Ex. "MedicationRequest-13"
fhirMedRequest.setId("MedicationRequest-" + prescription.getId());
// Assign references
CodeableReference medCodeableRef = new CodeableReference();
medCodeableRef.setReference(new Reference("Medication-" + medId));
fhirMedRequest.setMedication(medCodeableRef);

// Linking to patient
fhirMedRequest.setSubject(new Reference("Patient/" + patientId));

IBase requestEntry = bundleBuilder.addEntry();
bundleBuilder.addToEntry(requestEntry, "resource", fhirMedRequest);
}

/**
* @param patientSex either male or female, or some other string, or null.
* Hopefully that doesn't happen.
Expand All @@ -111,6 +199,36 @@ private static Enumerations.AdministrativeGender getAdministrativeGender(String
}
}

/**
* Helper method for creating Practitioner resources out of IUsers & adding them to a Bundle.
* @param prefix A string which describes the Practitioner's specific role. Physician, Nurse, Pharmacist...
*/
private void addPractitionerData(BundleBuilder bundleBuilder, IUser user, String prefix) {
// Creating Practitioner resource and assigning it a unique ID:
Practitioner fhirPractitioner = new Practitioner();
// Ex. User 42 (Nurse)
fhirPractitioner.setId("User " + user.getId() + " (" + prefix + ")");

// Populating name
HumanName name = new HumanName();
name.setFamily(user.getLastName());
name.addGiven(user.getFirstName());
name.addPrefix(prefix);
fhirPractitioner.setName(Collections.singletonList(name));

// If the practitioner has an email, add it as a ContactPoint.
if (user.getEmail() != null) {
ContactPoint emailContact = new ContactPoint();
emailContact.setSystem(ContactPoint.ContactPointSystem.EMAIL);
emailContact.setValue(user.getEmail());
fhirPractitioner.setTelecom(Collections.singletonList(emailContact));
}

// Add to bundle
IBase entry = bundleBuilder.addEntry();
bundleBuilder.addToEntry(entry, "resource", fhirPractitioner);
}

private String toJson(BundleBuilder bundleBuilder) {
// Create a parser
IParser parser = fhirContext.newJsonParser();
Expand Down
13 changes: 11 additions & 2 deletions test/femr/business/services/TestFhirExportService.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package femr.business.services;

import femr.business.services.system.FhirExportService;
import femr.data.daos.core.IEncounterRepository;
import femr.data.daos.core.IPatientRepository;
import femr.data.daos.core.IPrescriptionRepository;
import femr.data.daos.system.EncounterRepository;
import mock.femr.data.daos.MockEncounterRepository;
import mock.femr.data.daos.MockPatientRepository;
import mock.femr.data.daos.MockPrescriptionRepository;
import mock.femr.data.models.MockPatient;
import org.junit.Test;
import org.json.*;
Expand All @@ -16,19 +21,23 @@ public class TestFhirExportService {
@Test
public void smokeTestBlankDocument() {
IPatientRepository patientRepository = new MockPatientRepository();
FhirExportService export = new FhirExportService(patientRepository);
IEncounterRepository encounterRepository = new MockEncounterRepository();
IPrescriptionRepository prescriptionRepository = new MockPrescriptionRepository();
FhirExportService export = new FhirExportService(patientRepository, encounterRepository, prescriptionRepository);

System.out.println(export.exportPatient(1));
}

@Test
public void nonStandardSex() {
MockPatientRepository patientRepository = new MockPatientRepository();
IEncounterRepository encounterRepository = new MockEncounterRepository();
IPrescriptionRepository prescriptionRepository = new MockPrescriptionRepository();
patientRepository.mockPatient = new MockPatient();
patientRepository.mockPatient.setSex("SOMETHING NOT M or F");


FhirExportService export = new FhirExportService(patientRepository);
FhirExportService export = new FhirExportService(patientRepository, encounterRepository, prescriptionRepository);

String jsonString = export.exportPatient(1);

Expand Down
57 changes: 57 additions & 0 deletions test/mock/femr/data/daos/MockEncounterRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package mock.femr.data.daos;

import femr.data.daos.core.IEncounterRepository;
import femr.data.models.core.IPatientEncounter;
import org.joda.time.DateTime;

import java.util.ArrayList;
import java.util.List;

public class MockEncounterRepository implements IEncounterRepository {
public List<IPatientEncounter> mockEncounters = new ArrayList<>();

@Override
public IPatientEncounter createPatientEncounter(int patientID, DateTime date, int userId, Integer patientAgeClassificationId, Integer tripId, String languageCode) {
return null;
}

@Override
public IPatientEncounter deletePatientEncounter(int encounterId, String reason, int userId) {
return null;
}

@Override
public List<? extends IPatientEncounter> retrievePatientEncounters(DateTime from, DateTime to, Integer tripId) {
return null;
}

@Override
public IPatientEncounter retrievePatientEncounterById(int id) {
return null;
}

@Override
public List<? extends IPatientEncounter> retrievePatientEncountersByPatientIdAsc(int patientId) {
return mockEncounters;
}

@Override
public List<? extends IPatientEncounter> retrievePatientEncountersByPatientIdDesc(int patientId) {
return mockEncounters;
}

@Override
public IPatientEncounter savePatientEncounterMedicalCheckin(int encounterId, int userId, DateTime date) {
return null;
}

@Override
public IPatientEncounter savePatientEncounterPharmacyCheckin(int encounterId, int userId, DateTime date) {
return null;
}

@Override
public IPatientEncounter savePatientEncounterDiabetesScreening(int encounterId, int userId, DateTime date, Boolean isScreened) {
return null;
}
}

0 comments on commit 4e8ce1b

Please sign in to comment.