/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.ohd.pophealth.ccr.importer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.astm.ccr.ActorReferenceType;
import org.astm.ccr.ActorType;
import org.astm.ccr.ActorType.Person.Name;
import org.astm.ccr.Agent;
import org.astm.ccr.AlertType;
import org.astm.ccr.CCRCodedDataObjectType;
import org.astm.ccr.CodeType;
import org.astm.ccr.CodedDescriptionType;
import org.astm.ccr.ContinuityOfCareRecord;
import org.astm.ccr.DateTimeType;
import org.astm.ccr.EncounterType;
import org.astm.ccr.GoalType;
import org.astm.ccr.PlanOfCareType;
import org.astm.ccr.PlanType;
import org.astm.ccr.ProblemType;
import org.astm.ccr.ProcedureType;
import org.astm.ccr.ResultType;
import org.astm.ccr.SocialHistoryType;
import org.astm.ccr.StructuredProductType;
import org.astm.ccr.StructuredProductType.Product;
import org.astm.ccr.TestType;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.ohd.pophealth.json.clinicalmodel.Actor;
import org.ohd.pophealth.json.clinicalmodel.Allergy;
import org.ohd.pophealth.json.clinicalmodel.BaseObject;
import org.ohd.pophealth.json.clinicalmodel.Medication;
import org.ohd.pophealth.json.clinicalmodel.Test;
import org.ohd.pophealth.json.measuremodel.CodedValue;
import org.ohd.pophealth.json.clinicalmodel.Condition;
import org.ohd.pophealth.json.clinicalmodel.Encounter;
import org.ohd.pophealth.json.clinicalmodel.Goal;
import org.ohd.pophealth.json.clinicalmodel.Order;
import org.ohd.pophealth.json.clinicalmodel.Patient;
import org.ohd.pophealth.json.clinicalmodel.Procedure;
import org.ohd.pophealth.json.clinicalmodel.Record;
import org.ohd.pophealth.json.clinicalmodel.Result;
/**
* This class handles the extraction of clinical data from the ASTM CCR into a
* set of standard categories for matching against measures
* @author ohdohd
*/
public class RecordCreator {
private final static Logger LOG = Logger.getLogger(RecordCreator.class.getName());
// Current CCR being worked on
private ContinuityOfCareRecord ccr;
// Controlled Vocabulary for working with CCR
private Vocabulary v;
// Required TermSets
private static final String[] requiredTermSets = {"onset", "occurred",
"resolved", "ended", "collected", "ordered", "gender_male", "gender_female"};
private DateTimeFormatter fmt = ISODateTimeFormat.dateTimeParser(); // Formatter to parse ISO8601 Date Strings
/**
* Construct a RecordCreator using a particular Vocabulary. There is a base
* vocabulary that can be used: org.ohd.pophealth.ccr.importer.ccrvocabulary.json
*
* @param vocab CCR Vocabulary to use during CCR data extraction
* @throws InCompleteVocabularyException Thrown if not all of the required
* termsets are in the passed Vocabulary. Can call <code>getRequiredTermSets()</code>
* to get a list of required termsets for this RecordCreator
*/
public RecordCreator(Vocabulary vocab) throws InCompleteVocabularyException {
//Check that required TermSet are in the vocabulary
for (String s : requiredTermSets) {
if (!vocab.isValidTermSet(s)) {
throw new InCompleteVocabularyException("Did not find required termset: " + s);
}
}
this.v = vocab;
}
/**
* Returns a list of names of required term sets
*
* @return Array of termset ids
*/
public static String[] getRequiredTermSets() {
return requiredTermSets;
}
/**
* Creates a <code>Record</code> from the passed CCR
*
* @param ccr The CCR as an object generated by JAXB
* @return the extracted record
*/
public Record createRecord(ContinuityOfCareRecord ccr) {
this.ccr = ccr;
// Create a new Record and set all the attributes
Record r = new Record();
r.setPatient(createPatient());
r.setActors(createActors());
r.setConditions(createConditions());
r.setEncounters(createEncounters());
r.setProcedures(createProcedures());
r.setResults(createResults());
r.setMedications(createMedications());
r.setAllergies(createAllergies());
r.setOrders(createOrders());
if (LOG.isLoggable(Level.FINEST)) {
try {
LOG.finest(r.toJson(true));
} catch (JsonMappingException ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.SEVERE, null, ex);
} catch (JsonGenerationException ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.SEVERE, null, ex);
}
}
return r;
}
/*
* This method walks through the list of CCR Actors and finds the Actor
* with an <ActorObjectID> equal to the passed actorid.
*/
private ActorType getActorById(String actorid) {
for (ActorType a : ccr.getActors().getActor()) {
if (actorid.equals(a.getActorObjectID())) {
return a;
}
}
return null;
}
/*
* Extracts the data for the <code>Patient</code> object for the <code>
* Record</code>
*/
private Patient createPatient() {
ActorType pt = getActorById(ccr.getPatient().get(0).getActorID());
Patient p = new Patient();
// Set Date Of Birth - Assumes DOB required by CCR Validation
if (pt.getPerson() != null && pt.getPerson().getDateOfBirth() != null){
DateTimeType dt = pt.getPerson().getDateOfBirth();
String dob = dt.getExactDateTime();
p.setBirthdate(convertISO8601toSecfromEpoch(dob));
}else{
p.setBirthdate(BaseObject.minDate);
}
// Set Name
if (pt.getPerson() != null && pt.getPerson().getName() != null){
Name n = pt.getPerson().getName();
// Try Current Name first
if (n.getCurrentName() != null){
if (!n.getCurrentName().getFamily().isEmpty()){
// Just grab the first family name
// TODO handle multiple last names
p.setLast(n.getCurrentName().getFamily().get(0));
}
if (!n.getCurrentName().getGiven().isEmpty()){
p.setFirst(n.getCurrentName().getGiven().get(0));
}
// Second try birth name
}else if (n.getBirthName() != null){
if (!n.getBirthName().getFamily().isEmpty()){
// Just grab the first family name
// TODO handle multiple last names
p.setLast(n.getBirthName().getFamily().get(0));
}
if (!n.getBirthName().getGiven().isEmpty()){
p.setFirst(n.getBirthName().getGiven().get(0));
}
}
// TODO Add code to finally check for Additional Names
}
// Set Gender
if (pt.getPerson() != null && pt.getPerson().getGender() != null) {
if (isConceptMatch(v.getTermSet("gender_male"), pt.getPerson().getGender())) {
p.setGender("M");
} else if (isConceptMatch(v.getTermSet("gender_female"), pt.getPerson().getGender())) {
p.setGender("F");
}
}
// TODO Get Race, Ethnicity in the future
return p;
}
/**
* Converts a full or partial ISO8601 date string into milliseconds for epoch
* @param iso a full or partial datetime in ISO8601 format
* @return returns milliseconds from Epoch, if there is a problem returns -999999999999999
*/
public long convertISO8601toMSfromEpoch(String iso) {
if (iso == null || "".equals(iso)) {
return -999999999999999L;
}
DateTime dt = fmt.parseDateTime(iso);
return dt.getMillis();
}
/**
* Converts a full or partial ISO8601 date string into seconds for epoch
* @param iso a full or partial datetime in ISO8601 format
* @return returns seconds from Epoch, if there is a problem returns -999999999999
*/
public long convertISO8601toSecfromEpoch(String iso) {
return convertISO8601toMSfromEpoch(iso) / 1000;
}
/*
* Walks through the <Actor> nodes in /ContinuityOfCareRecord/Actors and
* creates clinical model Actors
*/
private ArrayList<Actor> createActors() {
ArrayList<Actor> al = new ArrayList<Actor>();
// Double check there are Actors in the CCR although it should not be the case there are none
if (ccr.getActors() != null) {
for (ActorType at : ccr.getActors().getActor()) {
Actor a = new Actor(at.getActorObjectID());
al.add(a);
}
}
return al;
}
/*
* Walks through the <Problem> nodes in /ContinuityOfCareRecord/Body/Problems
* and creates clinical model Conditions. Also walks through <SocialHistoryElement>
* nodes in /ContinuityOfCareRecord/Body/SocialHistory and creates clinical model conditions
*/
private ArrayList<Condition> createConditions() {
ArrayList<Condition> cl = new ArrayList<Condition>();
// Walk through CCR Problems
if (ccr.getBody().getProblems() != null) {
for (ProblemType pt : ccr.getBody().getProblems().getProblem()) {
Condition c = new Condition(pt.getCCRDataObjectID());
// Set Description
if (pt.getDescription() != null) {
c.setDescription(convertToCodedValue(pt.getDescription()));
}
// Set status of the condition (i.e. is it active, chronic, resolved, etc.)
if (pt.getStatus() != null) {
c.setStatus(convertToCodedValue(pt.getStatus()));
}
// Set the onset date of the Condition
String dateOnset = null;
try {
dateOnset = findDate(v.getTermSet("onset"), pt.getDateTime());
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, "No onset date found for problem [{0}]", pt.getCCRDataObjectID());
}
if (dateOnset != null) {
c.setOnset(convertISO8601toSecfromEpoch(dateOnset));
}
// Set the resolution date of the Condition
String dateResolve = null;
try {
dateResolve = findDate(v.getTermSet("resolved"), pt.getDateTime());
} catch (NoValidDateFound ex) {
// Not a problem many CCR Problems will not have a resolve date.
Logger.getLogger(RecordCreator.class.getName()).log(Level.FINE, "No resolve date found for problem [{0}]", pt.getCCRDataObjectID());
}
if (dateResolve != null) {
c.setResolution(convertISO8601toSecfromEpoch(dateResolve));
}
//TODO set Type on Condition
cl.add(c);
}
}
// Walk through CCR SocialHistory
if (ccr.getBody().getSocialHistory() != null) {
for (SocialHistoryType sht : ccr.getBody().getSocialHistory().getSocialHistoryElement()) {
Condition c = new Condition(sht.getCCRDataObjectID());
// Set description
if (sht.getDescription() != null) {
c.setDescription(convertToCodedValue(sht.getDescription()));
}
// Set status
if (sht.getStatus() != null) {
c.setStatus(convertToCodedValue(sht.getStatus()));
}
// Set onset date
String dateOnset = null;
try {
dateOnset = findDate(v.getTermSet("onset"), sht.getDateTime());
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getLocalizedMessage());
}
if (dateOnset != null) {
c.setOnset(convertISO8601toSecfromEpoch(dateOnset));
}
// Set resolve date
String dateResolve = null;
try {
dateResolve = findDate(v.getTermSet("resolved"), sht.getDateTime());
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getLocalizedMessage());
}
if (dateResolve != null) {
c.setResolution(convertISO8601toSecfromEpoch(dateResolve));
}
//TODO set Type on Condition
cl.add(c);
}
}
return cl;
}
/*
* Walks through the <Encounter> nodes in /ContinuityOfCareRecord/Body/Encounters
* and creates clinical model Encounters
*/
private ArrayList<Encounter> createEncounters() {
ArrayList<Encounter> el = new ArrayList<Encounter>();
if (ccr.getBody().getEncounters() != null) {
for (EncounterType et : ccr.getBody().getEncounters().getEncounter()) {
Encounter e = createEncounter(et);
if (e != null) {
el.add(e);
}
}
}
return el;
}
private Encounter createEncounter(EncounterType et) {
Encounter e = new Encounter(et.getCCRDataObjectID());
// Set description
if (et.getDescription() != null) {
e.setDescription(convertToCodedValue(et.getDescription()));
}
// Set the list of practitioners
if (et.getPractitioners() != null) {
for (ActorReferenceType art : et.getPractitioners().getPractitioner()) {
e.addProvider(art.getActorID());
}
}
// Set the date the encounter occurred
String dateOccurred = null;
try {
dateOccurred = findDate(v.getTermSet("occurred"), et.getDateTime());
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
if (dateOccurred != null) {
e.setOccurred(convertISO8601toSecfromEpoch(dateOccurred));
}
// Set the date the encounter ended
String dateEnded = null;
try {
dateEnded = findDate(v.getTermSet("ended"), et.getDateTime());
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
if (dateEnded != null) {
e.setEnded(convertISO8601toSecfromEpoch(dateEnded));
}
return e;
}
/*
* Walks through the <Result> nodes in both /ContinuityOfCareRecord/Body/Results
* and /ContinuityOfCareRecord/Body/VitalSigns
*/
private ArrayList<Result> createResults() {
ArrayList<Result> rl = new ArrayList<Result>();
// Create results from the CCR <Results> section
if (ccr.getBody().getResults() != null) {
for (ResultType rtr : ccr.getBody().getResults().getResult()) {
rl.add(createResult(rtr));
}
}
// Create results from the CCR <VitalSigns> section
if (ccr.getBody().getVitalSigns() != null) {
for (ResultType rtv : ccr.getBody().getVitalSigns().getResult()) {
rl.add(createResult(rtv));
}
}
return rl;
}
/*
* Creates a clinical model result from a CCR ResultType
*/
private Result createResult(ResultType rt) {
Result r = new Result(rt.getCCRDataObjectID());
// Set description
if (rt.getDescription() != null) {
r.setDescription(convertToCodedValue(rt.getDescription()));
}
// Set type
if (rt.getType() != null) {
r.setType(convertToCodedValue(rt.getType()));
}
// Set collection date
String rCollectedDate = null;
try {
rCollectedDate = findDate(v.getTermSet("collected"), rt.getDateTime());
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
if (rCollectedDate != null) {
r.setCollectionTime(convertISO8601toSecfromEpoch(rCollectedDate));
}
// Create and set the list of tests for this result
r.setTests(createTests(rt, rCollectedDate));
return r;
}
/*
* Creates a list of clinical model Test objects from the <Test> children of
* the CCR <Result>
*/
private ArrayList<Test> createTests(ResultType rt, String resultCollectTime) {
ArrayList<Test> tl = new ArrayList<Test>();
if (rt != null) {
for (TestType tt : rt.getTest()) {
Test t = new Test(tt.getCCRDataObjectID());
// Try to set the collection date of the test. If the <Test> does
// no have a collection date try to use the collection date of the
// result.
try {
// Find if there is a collection date for the test
String collectedDate = findDate(v.getTermSet("collected"), tt.getDateTime());
if (collectedDate != null) {
// Found a test collection date to use it
t.setCollectionTime(convertISO8601toSecfromEpoch(collectedDate));
} else if (resultCollectTime != null) {
// Did not find a test collection date so use result collection time
t.setCollectionTime(convertISO8601toSecfromEpoch(resultCollectTime));
}
} catch (NoValidDateFound ex) {
// No problem, just use the result collected time
t.setCollectionTime(convertISO8601toSecfromEpoch(resultCollectTime));
}
// Set description
if (tt.getDescription() != null) {
t.setDescription(convertToCodedValue(tt.getDescription()));
}
// Set the test value and units
if (tt.getTestResult().getValue() != null) {
t.setValue(tt.getTestResult().getValue());
if (tt.getTestResult().getUnits() != null) {
if (tt.getTestResult().getUnits().getUnit() != null) {
t.setUnits(tt.getTestResult().getUnits().getUnit());
}
}
}// TODO Else could set value based on <Description> of TestResult
tl.add(t);
}
}
return tl;
}
/*
* Create clinical model Medications from CCR Medications in
* /ContinuityOfCare/Body/Medications and /ContinuityOfCare/Body/Immunizations
*/
private ArrayList<Medication> createMedications() {
ArrayList<Medication> ml = new ArrayList<Medication>();
// Create from CCR Medications
if (ccr.getBody().getMedications() != null) {
for (StructuredProductType med : ccr.getBody().getMedications().getMedication()) {
ml.add(createMedication(med));
}
}
// Create from CCR Immunizations
if (ccr.getBody().getImmunizations() != null) {
for (StructuredProductType imm : ccr.getBody().getImmunizations().getImmunization()) {
ml.add(createMedication(imm));
}
}
return ml;
}
/*
* Creates a clinical model Medication from a CCR Structured Product
*/
private Medication createMedication(StructuredProductType med) {
Medication m = new Medication(med.getCCRDataObjectID());
// Set the description of the Medication by adding the general CCR
// description and the codedvalues of the product name and brand name
if (med.getDescription() != null) {
m.addDescription(convertToCodedValue(med.getDescription()));
}
// A CCR Medication may have multiple products
for (Product pt : med.getProduct()) {
m.addDescription(convertToCodedValue(pt.getProductName()));
if (pt.getBrandName() != null) {
m.addDescription(convertToCodedValue(pt.getBrandName()));
}
}
// Set the type
if (med.getType() != null) {
m.setType(convertToCodedValue(med.getType()));
}
// Set the status
if (med.getStatus() != null) {
m.setStatus(convertToCodedValue(med.getStatus()));
}
// Set the dates associated with the medication
try {
String startDate = findDate(v.getTermSet("onset"), med.getDateTime());
if (startDate != null) {
m.setStarted(convertISO8601toSecfromEpoch(startDate));
}
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
try {
String stopDate = findDate(v.getTermSet("ended"), med.getDateTime());
if (stopDate != null) {
m.setStopped(convertISO8601toSecfromEpoch(stopDate));
}
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
return m;
}
/*
* Create clinical model Allergy objects from CCR Alerts
*/
private ArrayList<Allergy> createAllergies() {
ArrayList<Allergy> al = new ArrayList<Allergy>();
if (ccr.getBody().getAlerts() != null) {
for (AlertType at : ccr.getBody().getAlerts().getAlert()) {
Allergy a = new Allergy(at.getCCRDataObjectID());
// Set type
if (at.getType() != null) {
a.setType(convertToCodedValue(at.getType()));
}
// Set the description using Alert <Description> and Alert <Agent>
if (at.getDescription() != null) {
a.addDescription(convertToCodedValue(at.getDescription()));
}
for (Agent agt : at.getAgent()) {
// Walk through any Products and add to description
if (agt.getProducts() != null) {
for (StructuredProductType pt : agt.getProducts().getProduct()) {
if (pt.getDescription() != null) {
a.addDescription(convertToCodedValue(pt.getDescription()));
}
// A medication Product can be a collection of products
for (Product pdt : pt.getProduct()) {
a.addDescription(convertToCodedValue(pdt.getProductName()));
if (pdt.getBrandName() != null) {
a.addDescription(convertToCodedValue(pdt.getBrandName()));
}
}
}
}
// Walk through the Environmental Agents as well
if (agt.getEnvironmentalAgents() != null) {
for (CCRCodedDataObjectType cdt : agt.getEnvironmentalAgents().getEnvironmentalAgent()) {
if (cdt.getDescription() != null) {
a.addDescription(convertToCodedValue(cdt.getDescription()));
}
}
}
}
try {
// Set Onset Date
String onsetDate = findDate(v.getTermSet("onset"), at.getDateTime());
if (onsetDate != null) {
a.setOnset(convertISO8601toSecfromEpoch(onsetDate));
}
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
al.add(a);
}
}
return al;
}
/*
* Create clinical model procedures from CCR Procedures
*/
private ArrayList<Procedure> createProcedures() {
// At this point a Procedure is just another encounter
ArrayList<Procedure> pL = new ArrayList<Procedure>();
if (ccr.getBody().getProcedures() != null) {
for (ProcedureType pt : ccr.getBody().getProcedures().getProcedure()) {
Procedure p = new Procedure(pt.getCCRDataObjectID());
// Set type
if (pt.getType() != null) {
p.setType(convertToCodedValue(pt.getType()));
}
// Set description
if (pt.getDescription() != null) {
p.setDescription(convertToCodedValue(pt.getDescription()));
}
// Set the date the procedure occurred
try {
String encounterDate = findDate(v.getTermSet("occurred"), pt.getDateTime());
if (encounterDate != null) {
p.setOccurred(convertISO8601toSecfromEpoch(encounterDate));
}
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
// Set the date the procedure ended on
try {
String endDate = findDate(v.getTermSet("ended"), pt.getDateTime());
if (endDate != null) {
p.setEnded(convertISO8601toSecfromEpoch(endDate));
}
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
// Set the list of practitioners involved in the procedure
if (pt.getPractitioners() != null) {
for (ActorReferenceType art : pt.getPractitioners().getPractitioner()) {
p.addProvider(art.getActorID());
}
}
pL.add(p);
}
}
return pL;
}
/*
* Create clinical model Orders from CCR Orders. Also where clinical model
* Goals are created.
*/
private ArrayList<Order> createOrders() {
ArrayList<Order> ol = new ArrayList<Order>();
// There are three places for coded descriptions for orders
// //Plan/Description //Plan/OrderRequest/Description //Plan/OrderRequest/*/Description
// A JSON Order is equal to an OrderRequest, but need to push Plan date and description
// down if not set in the OrderRequest.
if (ccr.getBody().getPlanOfCare() != null) {
for (PlanType pt : ccr.getBody().getPlanOfCare().getPlan()) {
String planOrderDate = null;
ArrayList<CodedValue> planDescription = new ArrayList<CodedValue>();
ArrayList<CodedValue> planType = new ArrayList<CodedValue>();
// Check for an ordered date for the <Plan>
try {
planOrderDate = findDate(v.getTermSet("ordered"), pt.getDateTime());
} catch (NoValidDateFound ex) {
//Logger.getLogger(RecordCreator.class.getName()).log(Level.INFO, "No ordered date found", ex);
}
// Check for a description for the <Plan>
if (pt.getDescription() != null) {
planDescription.addAll(convertToCodedValue(pt.getDescription()));
}
// Set the type
if (pt.getType() != null) {
planType.addAll(convertToCodedValue(pt.getType()));
}
// Now walk through each <OrderRequest> and create a clinical model Order
for (PlanOfCareType pct : pt.getOrderRequest()) {
Order o = new Order(pt.getCCRDataObjectID());
// Check for an ordered date on the OrderRequest and if not found use <Plan> order date
try {
String orderDate = findDate(v.getTermSet("ordered"), pct.getDateTime());
} catch (NoValidDateFound ex) {
if (planOrderDate == null) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
} else {
o.setOrderDate(convertISO8601toSecfromEpoch(planOrderDate));
}
}
// Add the <Plan> type to the Order type
o.addType(planType);
// Add the <Plan> description to the Order description
o.addDescription(planDescription);
// If the OrderRequest has a description add it to the Order as well
if (pct.getDescription() != null) {
o.addDescription(convertToCodedValue(pct.getDescription()));
}
// For each of the types of orderrequests, add to the Order
// Walk through Procedures
// TODO Add Procedure to Clinical Model
// Walk through Products
if (pct.getProducts() != null) {
for (StructuredProductType spt : pct.getProducts().getProduct()) {
o.addOrderRequest(createMedication(spt));
}
}
// Walk through Medications
if (pct.getMedications() != null) {
for (StructuredProductType spt : pct.getMedications().getMedication()) {
o.addOrderRequest(createMedication(spt));
}
}
// Walk through Immunizations
if (pct.getImmunizations() != null) {
for (StructuredProductType spt : pct.getImmunizations().getImmunization()) {
o.addOrderRequest(createMedication(spt));
}
}
// Walk through Services
if (pct.getServices() != null) {
for (EncounterType et : pct.getServices().getService()) {
o.addOrderRequest(createEncounter(et));
}
}
// Walk through Encounters
if (pct.getEncounters() != null) {
for (EncounterType et : pct.getEncounters().getEncounter()) {
o.addOrderRequest(createEncounter(et));
}
}
// TODO Skipped Authorizations
// Create the Goals for the order
if (pct.getGoals() != null) {
for (GoalType gt : pct.getGoals().getGoal()) {
Goal g = new Goal(gt.getCCRDataObjectID());
if (gt.getDescription() != null) {
g.setDescription(convertToCodedValue(gt.getDescription()));
}
if (gt.getType() != null) {
g.setType(convertToCodedValue(gt.getType()));
}
try {
String goalDate = findDate(v.getTermSet("onset"), gt.getDateTime());
if (goalDate != null) {
g.setGoalDate(convertISO8601toSecfromEpoch(goalDate));
}
} catch (NoValidDateFound ex) {
Logger.getLogger(RecordCreator.class.getName()).log(Level.WARNING, ex.getMessage());
}
o.addGoal(g);
}
}
ol.add(o);
}
}
}
return ol;
}
/*
* Converts a CCR coded items into a clinical model coded values
*/
private ArrayList<CodedValue> convertToCodedValue(CodedDescriptionType cdt) {
ArrayList<CodedValue> cvList = new ArrayList<CodedValue>();
if (cdt == null) {
return cvList;
}
// Set the <Text> field as a CodedValue
if (cdt.getText() != null) {
CodedValue cvText = new CodedValue();
cvText.setCodingSystem("TEXT");
cvText.addValue(cdt.getText());
cvList.add(cvText);
}
// TODO combine codes with the same coding system
// most CCRs representing patient data will not have multiple codes for
// the same coding system
// Create CodedValue for each code
for (CodeType ct : cdt.getCode()) {
CodedValue cv = new CodedValue();
cv.setCodingSystem(ct.getCodingSystem());
if (ct.getVersion() != null) {
cv.setVersion(ct.getVersion());
}
cv.addValue(ct.getValue());
cvList.add(cv);
}
return cvList;
}
/**
* Determines if a CCR coded item is equal to any coded value in a list
* @param cvList The list of coded value item to compare to
* @param cdt The CCR coded item to compare against
* @return <code>true</code> if there is a match, otherwise <code>false</code>
*/
public static boolean codeMatch(ArrayList<CodedValue> cvList, CodedDescriptionType cdt) {
// TODO Need to check for coding system.
// Do not now becuase no overlap in SNOMED, ICD9, ICD10
// Will need to leverage coding system names from CCR Validator and map
// to the names used by quality measures
// Step through each coded value in the list
for (CodedValue cv : cvList) {
// For the coded value step through each value in the array
for (String c : cv.getValues()) {
// Compare the value to each code in the CCR coded item
for (CodeType ct : cdt.getCode()) {
if (c.equals(ct.getValue())) {
return true;
}
}
}
}
// No code match found
return false;
}
/*
* Tries to find a particular date based on the supplied termset
*/
private String findDate(TermSet type, List<DateTimeType> dateTime) throws NoValidDateFound {
LOG.log(Level.FINEST, "Looking for date for: {0}", type.getId());
// empty list throw error
if (dateTime == null || dateTime.isEmpty()) {
throw new NoValidDateFound("DateTime List was empty");
// TODO Consider Defaulting to timestamp of the CCR
}
// if there is only one date, return it if we are looking for onset, occurred, or collected
if (dateTime.size() == 1 && (type.getId().equals("onset") || type.getId().equals("occurred") || type.getId().equals("collected"))) {
if (dateTime.get(0).getExactDateTime() != null) {
return dateTime.get(0).getExactDateTime();
}
//throw new NoValidDateFound("No ExactDateTime Found");
// TODO Not sure if should default to CCR timestamp
} else {
for (DateTimeType dt : dateTime) {
// if there is a <Type> for the datetime check to see if it is
// a concept match and if so return the datetime
if (dt.getType() != null) {
if (isConceptMatch(type, dt.getType())) {
if (dt.getExactDateTime() != null) {
return dt.getExactDateTime();
}
//throw new NoValidDateFound("No ExactDateTime Found");
}
}
}
}
// No right type date found
return null;
}
/*
* This is like <code>codeMatch</code> method put checks the type string
* against the term in the CCR coded item. Typically there are not controlled
* vocabularies for type or status for CCR data objects.
*/
private boolean isConceptMatch(TermSet ts, CodedDescriptionType type) {
// Check for matching codes
Iterator<CodedValue> iCode = ts.getCodeIterator();
while (iCode.hasNext()) {
if (compareCodes(iCode.next(), type.getCode())) {
return true;
}
}
// No code match so check for matching terms
Iterator<String> iTerm = ts.getTermIterator();
while (iTerm.hasNext()) {
if (compareTerms(iTerm.next(), type.getText())) {
return true;
}
}
// No code or term match, so no concept match
return false;
}
/*
* Compares the codes in the CodedValue with the list of CCR codes
*/
private boolean compareCodes(CodedValue cv, List<CodeType> code) {
// Step through each code in the CCR item
for (CodeType ct : code) {
// If it is using the same coding system check the codes
if (isSameCodingSystem(cv, ct)) {
// Check against each code value in the CodedValue
for (String vCode : cv.getValues()) {
if (vCode.equals(ct.getValue())) {
return true;
}
}
}
}
// No code match
return false;
}
/*
* Simply compares the two terms
* Separated out as a method so in future could plugin string normalization or
* other more complex comparasions using the UMLS and Semantic Network
*/
private boolean compareTerms(String term, String text) {
// At this point a simple non-case sensitive comparision
boolean b = term.equalsIgnoreCase(text);
LOG.log(Level.FINEST, "term [{0}] text [{1}]={2}", new Object[]{term, text, b});
return b;
}
/*
* In the real world it will require a little bit of coding to handle the
* variations in naming of coding systems and the issues around versioning
* of the coding systems.
*/
private boolean isSameCodingSystem(CodedValue cv, CodeType ct) {
// TODO Not yet implemented (works okay now for ICD, SNOMED, RXNORM)
// Need to check the codingsystem in the CCR code against the
// correct TermSet based on cv.getCodingSystem()
return true;
}
}