Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generating appointments along the side of Encounters #1362

Draft
wants to merge 28 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
945d300
added .idea to gitignore
tomkaith13 Aug 24, 2023
76e05f3
added a dummy appt resource
tomkaith13 Aug 24, 2023
46f5bc0
added changes to report Appointments based on Encounters
tomkaith13 Aug 24, 2023
4b27b09
fixed some appointment related execute bundle errors
tomkaith13 Aug 25, 2023
7207bc2
add patient in appt
tomkaith13 Aug 25, 2023
3114575
added identifier to the appt
tomkaith13 Aug 30, 2023
3d75c6e
added custom extension stringValue
tomkaith13 Aug 30, 2023
ee9bee3
removed unused variable
tomkaith13 Sep 6, 2023
e94889a
add valid attribs for Consent
tomkaith13 Sep 6, 2023
4dc6d46
Merge pull request #1 from tomkaith13/consent
tomkaith13 Sep 6, 2023
9dc7bcb
add consent generation for every encounter
tomkaith13 Sep 8, 2023
33dfe3f
Changed the code to a sha256 hex string for better searchability
tomkaith13 Sep 11, 2023
39f1045
cleaned up a few imports
tomkaith13 Sep 11, 2023
6713fe5
Merge pull request #2 from tomkaith13/one-time-consents
tomkaith13 Sep 12, 2023
02e1952
add a custom extn to inject custom ids
tomkaith13 Sep 12, 2023
01acac6
add multiple extension to consent
tomkaith13 Sep 13, 2023
0f2a6e9
Merge pull request #3 from tomkaith13/consent-with-custom-extn
tomkaith13 Sep 13, 2023
f97815c
added changes to create careplan tree based topology
tomkaith13 Sep 20, 2023
93cce14
Merge pull request #4 from tomkaith13/add-children-careplans
tomkaith13 Sep 20, 2023
d378810
adding Task generation which is tied to a CarePlan
tomkaith13 Sep 25, 2023
878ba9a
Merge pull request #5 from tomkaith13/add-task-as-revlink-to-careplan
tomkaith13 Sep 25, 2023
8cb93fa
Added changes to reverse link ServiceRequest to the root-care-plan
tomkaith13 Sep 26, 2023
86e8a94
Merge pull request #6 from tomkaith13/add-service-request-revlink-to-…
tomkaith13 Sep 26, 2023
5ec445d
add questionnaire-response and link them to consents
tomkaith13 Sep 26, 2023
6e3e4c4
add comments and remv extra consent ref link
tomkaith13 Sep 26, 2023
f67209f
Merge pull request #7 from tomkaith13/add-questionnaire-response-to-e…
tomkaith13 Sep 26, 2023
fac1816
adding start and end times to appt resource
tomkaith13 Oct 13, 2023
8149864
Merge pull request #8 from tomkaith13/add-appt-start-and-end
tomkaith13 Oct 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ bin/**

/.gradle/
/bin/

/.idea/
/failed_exports/
/output/

/out/

**/*.log
Expand Down
227 changes: 179 additions & 48 deletions src/main/java/org/mitre/synthea/export/FhirR4.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import java.awt.geom.Point2D;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
Expand All @@ -24,13 +26,10 @@
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.Address;
import org.hl7.fhir.r4.model.AllergyIntolerance;
import org.hl7.fhir.r4.model.*;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the pull request!

We have not started a formal review yet, but I noticed the change to star-imports.

Please do not use star-imports -- they violate our style guidelines, and should appear as WARN messages when you run the unit tests (which incidentally are failing).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @jawalonoski
Thanks for looking into it.

I just created the PR for others to share the code.
I wasnt intending to merge this 😅
Thats the reason I left it in draft.

I will try cleaning this up if you feel its worth pursuing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this whole PR is a mess. 🤦
I ended up merging some Consent related changes in there as well which was me experimenting Consent generation per Patient which snuck into this PR.

I can close it out if it adds too much noise for you.

import org.hl7.fhir.r4.model.AllergyIntolerance.AllergyIntoleranceCategory;
import org.hl7.fhir.r4.model.AllergyIntolerance.AllergyIntoleranceCriticality;
import org.hl7.fhir.r4.model.AllergyIntolerance.AllergyIntoleranceType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.BundleEntryRequestComponent;
import org.hl7.fhir.r4.model.Bundle.BundleType;
Expand All @@ -40,7 +39,6 @@
import org.hl7.fhir.r4.model.CarePlan.CarePlanActivityStatus;
import org.hl7.fhir.r4.model.CarePlan.CarePlanIntent;
import org.hl7.fhir.r4.model.CarePlan.CarePlanStatus;
import org.hl7.fhir.r4.model.CareTeam;
import org.hl7.fhir.r4.model.CareTeam.CareTeamParticipantComponent;
import org.hl7.fhir.r4.model.CareTeam.CareTeamStatus;
import org.hl7.fhir.r4.model.Claim.ClaimStatus;
Expand All @@ -49,88 +47,45 @@
import org.hl7.fhir.r4.model.Claim.ItemComponent;
import org.hl7.fhir.r4.model.Claim.ProcedureComponent;
import org.hl7.fhir.r4.model.Claim.SupportingInformationComponent;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Condition;
import org.hl7.fhir.r4.model.ContactPoint;
import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.r4.model.ContactPoint.ContactPointUse;
import org.hl7.fhir.r4.model.Coverage;
import org.hl7.fhir.r4.model.Coverage.CoverageStatus;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.DecimalType;
import org.hl7.fhir.r4.model.Device;
import org.hl7.fhir.r4.model.Device.DeviceNameType;
import org.hl7.fhir.r4.model.Device.FHIRDeviceStatus;
import org.hl7.fhir.r4.model.DiagnosticReport;
import org.hl7.fhir.r4.model.DiagnosticReport.DiagnosticReportStatus;
import org.hl7.fhir.r4.model.DocumentReference;
import org.hl7.fhir.r4.model.DocumentReference.DocumentReferenceContextComponent;
import org.hl7.fhir.r4.model.Dosage;
import org.hl7.fhir.r4.model.Dosage.DosageDoseAndRateComponent;
import org.hl7.fhir.r4.model.Encounter.EncounterHospitalizationComponent;
import org.hl7.fhir.r4.model.Encounter.EncounterStatus;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.r4.model.Enumerations.DocumentReferenceStatus;
import org.hl7.fhir.r4.model.ExplanationOfBenefit;
import org.hl7.fhir.r4.model.ExplanationOfBenefit.RemittanceOutcome;
import org.hl7.fhir.r4.model.ExplanationOfBenefit.TotalComponent;
import org.hl7.fhir.r4.model.ExplanationOfBenefit.Use;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Goal;
import org.hl7.fhir.r4.model.Goal.GoalLifecycleStatus;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Identifier.IdentifierUse;
import org.hl7.fhir.r4.model.ImagingStudy.ImagingStudySeriesComponent;
import org.hl7.fhir.r4.model.ImagingStudy.ImagingStudySeriesInstanceComponent;
import org.hl7.fhir.r4.model.ImagingStudy.ImagingStudyStatus;
import org.hl7.fhir.r4.model.Immunization.ImmunizationStatus;
import org.hl7.fhir.r4.model.Immunization;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.Location.LocationPositionComponent;
import org.hl7.fhir.r4.model.Location.LocationStatus;
import org.hl7.fhir.r4.model.Media;
import org.hl7.fhir.r4.model.Media.MediaStatus;
import org.hl7.fhir.r4.model.Medication.MedicationStatus;
import org.hl7.fhir.r4.model.MedicationAdministration;
import org.hl7.fhir.r4.model.MedicationAdministration.MedicationAdministrationDosageComponent;
import org.hl7.fhir.r4.model.MedicationRequest;
import org.hl7.fhir.r4.model.MedicationRequest.MedicationRequestIntent;
import org.hl7.fhir.r4.model.MedicationRequest.MedicationRequestStatus;
import org.hl7.fhir.r4.model.Meta;
import org.hl7.fhir.r4.model.Money;
import org.hl7.fhir.r4.model.Narrative;
import org.hl7.fhir.r4.model.Narrative.NarrativeStatus;
import org.hl7.fhir.r4.model.Observation.ObservationComponentComponent;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Patient.ContactComponent;
import org.hl7.fhir.r4.model.Patient.PatientCommunicationComponent;
import org.hl7.fhir.r4.model.Period;
import org.hl7.fhir.r4.model.PositiveIntType;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.PractitionerRole;
import org.hl7.fhir.r4.model.Procedure.ProcedureStatus;
import org.hl7.fhir.r4.model.Property;
import org.hl7.fhir.r4.model.Provenance;
import org.hl7.fhir.r4.model.Provenance.ProvenanceAgentComponent;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.ServiceRequest;
import org.hl7.fhir.r4.model.SimpleQuantity;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.SupplyDelivery;
import org.hl7.fhir.r4.model.SupplyDelivery.SupplyDeliveryStatus;
import org.hl7.fhir.r4.model.SupplyDelivery.SupplyDeliverySuppliedItemComponent;
import org.hl7.fhir.r4.model.Timing;
import org.hl7.fhir.r4.model.Timing.TimingRepeatComponent;
import org.hl7.fhir.r4.model.Timing.UnitsOfTime;
import org.hl7.fhir.r4.model.Type;
import org.hl7.fhir.r4.model.codesystems.DoseRateType;

import org.hl7.fhir.utilities.xhtml.NodeType;
Expand Down Expand Up @@ -160,6 +115,7 @@
import org.mitre.synthea.world.concepts.HealthRecord.Procedure;
import org.mitre.synthea.world.concepts.HealthRecord.Report;
import org.mitre.synthea.world.geography.Location;
import org.opencds.cqf.cql.engine.runtime.DateTime;

public class FhirR4 {
// HAPI FHIR warns that the context creation is expensive, and should be performed
Expand Down Expand Up @@ -403,6 +359,11 @@ public static Bundle convertToFHIR(Person person, long stopTime) {

BundleEntryComponent personEntry = basicInfo(person, bundle, stopTime);

if (shouldExport(Consent.class)) {
consentByPerson(person, bundle, personEntry,"");
consentByPerson(person, bundle, personEntry,"gina");
}

for (Encounter encounter : person.record.encounters) {
BundleEntryComponent encounterEntry = encounter(person, personEntry, bundle, encounter);

Expand Down Expand Up @@ -505,6 +466,11 @@ public static Bundle convertToFHIR(Person person, long stopTime) {
encounterClaim, encounter, encounter.claim);
}
}

// add appointment to every encounter as well
if (shouldExport(org.hl7.fhir.r4.model.Appointment.class)) {
encounterAppointment(person, personEntry, bundle, encounter, encounterEntry);
}
}

if (USE_US_CORE_IG && shouldExport(Provenance.class)) {
Expand All @@ -514,6 +480,171 @@ public static Bundle convertToFHIR(Person person, long stopTime) {
return bundle;
}

private static BundleEntryComponent consentByPerson(Person person, Bundle bundle, BundleEntryComponent personEntry, String type) {
org.hl7.fhir.r4.model.Consent consent = new Consent();

consent.setPatient(new Reference()
.setReference(personEntry.getFullUrl())
.setDisplay(person.attributes
.get(Person.FIRST_NAME).toString()
+
person.attributes.get(Person.LAST_NAME).toString()));
List<CodeableConcept> codingList = new ArrayList<>();
// set category
if (type.equals(new String(""))) {
CodeableConcept termsAndConditionsCC = new CodeableConcept();
termsAndConditionsCC.addCoding().setCode("terms_and_conditions")
.setDisplay("Terms and Conditions")
.setSystem("terms and conditions");
codingList.add(termsAndConditionsCC);
} else {
CodeableConcept ginaCC = new CodeableConcept();
ginaCC.addCoding().setCode("gina")
.setDisplay("GINA")
.setSystem("gina");
codingList.add(ginaCC);
}


consent.setCategory(codingList);

// status set
consent.setStatus(Consent.ConsentState.ACTIVE);

// date time
consent.setDateTime(Date.from(Instant.now()));

consent.setId(String.valueOf(UUID.randomUUID()));
if (type.equals(new String(""))) {
consent.setProvision(new Consent.provisionComponent().setType(Consent.ConsentProvisionType.PERMIT));
} else {
LocalDateTime now = LocalDateTime.now();
LocalDateTime end = now.plusDays(365);



consent.setProvision(new Consent.provisionComponent().setType(Consent.ConsentProvisionType.PERMIT)
.setPeriod(
new Period()
.setStart(java.sql.Timestamp.valueOf(now))
.setEnd(java.sql.Timestamp.valueOf(end))
)
);
}

// adding scope
CodeableConcept scopeCC = new CodeableConcept();
scopeCC.addCoding().setCode("active");
consent.setScope(scopeCC);

// policy rule Codeable Concept addition
CodeableConcept policyRuleCC = new CodeableConcept();
policyRuleCC.addCoding().setCode("policy rule").setDisplay("Policy Rule");
consent.setPolicyRule(policyRuleCC);



return newEntry(bundle, consent, consent.getId());
}


private static BundleEntryComponent encounterAppointment(Person person, BundleEntryComponent personEntry,
Bundle bundle, Encounter encounter,
BundleEntryComponent encounterEntry) {

org.hl7.fhir.r4.model.Appointment apptResource = new org.hl7.fhir.r4.model.Appointment();
apptResource.setId(String.valueOf(UUID.randomUUID()));

// convert participant
org.hl7.fhir.r4.model.Encounter encounterResource =
(org.hl7.fhir.r4.model.Encounter) encounterEntry.getResource();

//status
apptResource.setStatus(Appointment.AppointmentStatus.PROPOSED);

// identifier
List<Identifier> identifierList = new ArrayList<>();
identifierList.add(new Identifier()
.setSystem("http://fhir.league.com/r4/LeagueUserProfiles/NamingSystem/league-user-id")
.setValue((String) person.attributes.get(Person.ID)));

apptResource.setIdentifier(identifierList);

// appt type

CodeableConcept aTypeCC = new CodeableConcept();
aTypeCC.setText("Appt Type");
Coding apptTypeCoding = aTypeCC.addCoding().setCode("apptType")
.setDisplay("Appt Type display").setSystem("appt Type System");

apptResource.setAppointmentType(aTypeCC);

// serviceCategory
List<org.hl7.fhir.r4.model.CodeableConcept> categories = new ArrayList<>();
CodeableConcept cc = new CodeableConcept();
cc.setText("category 1");
Coding serviceCatCoding = cc.addCoding();
serviceCatCoding.setDisplay("cat 1 display");
serviceCatCoding.setSystem("cat 1 system");
serviceCatCoding.setCode("cat1");

categories.add(cc);
apptResource.setServiceCategory(categories);

// serviceType
List<org.hl7.fhir.r4.model.CodeableConcept> types = new ArrayList<>();
CodeableConcept stcc = new CodeableConcept();
stcc.setText("type 1");
Coding serviceTypesCoding = stcc.addCoding();
serviceTypesCoding.setDisplay("type 1 display");
serviceTypesCoding.setSystem("type 1 system");
serviceTypesCoding.setCode("type1");

types.add(stcc);
apptResource.setServiceType(types);

//adding a custom extension
List<Extension> extnList = new ArrayList<>();
Coding extnCoding = new Coding().setCode("extn code 1").setDisplay("extn display code");
extnList.add(new Extension()
.setUrl("http://fhir.league.com/r4/LeagueCareServiceBookings/StructureDefinition/custom-extension")
.setValue(new StringType("custom-exn-val")));
apptResource.setExtension(extnList);


// add all participants
List<Appointment.AppointmentParticipantComponent> aPList = new ArrayList<>();
for (org.hl7.fhir.r4.model.Encounter.EncounterParticipantComponent eParticipant : encounterResource.getParticipant()) {
Appointment.AppointmentParticipantComponent aProvider = new Appointment.AppointmentParticipantComponent();
// Add provider
aProvider.setPeriod(eParticipant.getPeriod());
aProvider.setActor(eParticipant.getIndividual());
aProvider.setStatus(Appointment.ParticipationStatus.ACCEPTED);

aPList.add(aProvider);


// Add location
Appointment.AppointmentParticipantComponent aLoc = new Appointment.AppointmentParticipantComponent();
aLoc.setActor(encounterResource.getLocation().get(0).getLocation());
aLoc.setStatus(Appointment.ParticipationStatus.ACCEPTED);
aPList.add(aLoc);

//Add patient
Appointment.AppointmentParticipantComponent aPatient = new Appointment.AppointmentParticipantComponent();
aPatient.setActor(encounterResource.getSubject());
aPatient.setStatus(Appointment.ParticipationStatus.ACCEPTED);
aPList.add(aPatient);

}

apptResource.setParticipant(aPList);


return newEntry(bundle, apptResource, apptResource.getId());

}

/**
* Convert the given Person into a JSON String, containing a FHIR Bundle of the Person and the
* associated entries from their health record.
Expand Down
Loading