package edu.gatech.i3l.omop.mapping; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.WebApplicationContext; import edu.gatech.i3l.fhir.dstu2.entities.Concept; import edu.gatech.i3l.fhir.dstu2.entities.Location; import edu.gatech.i3l.fhir.dstu2.entities.PersonComplement; import edu.gatech.i3l.fhir.jpa.dao.BaseFhirDao; import edu.gatech.i3l.fhir.jpa.entity.BaseResourceEntity; /** * This class serves as cache for Concept values in Omop schema based database. * It is suggested to statr a new thread and pass this singleton to run. * * @author Ismael Sarmento */ public class OmopConceptMapping implements Runnable { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OmopConceptMapping.class); private static OmopConceptMapping omopConceptMapping = new OmopConceptMapping(); private EntityManager entityManager; /* * Concepts' Classes */ public static final String UNDEFINED = "Undefined"; public static final String GENDER = "Gender"; // public static final String MARITAL_STATUS = "Marital Status"; public static final String DRUG_EXPOSURE_TYPE = "Drug Type"; public static final String CLINICAL_FINDING = "Clinical finding"; public static final String QUALIFIER_VALUE = "Qualifier Value"; public static final String PROCEDURE = "Procedure"; public static final String OBSERVABLE_ENTITY = "Observable entity"; public static final String CONDITION_TYPE = "Condition Type"; public static final String LOINC_CODE = "LOINC Code"; public static final String SPECIALTY = "Specialty"; public static final String UCUM_CODE = "UCUM"; public static final String UCUM_CODE_STANDARD = "UCUM Standard"; public static final String UCUM_CODE_CUSTOM = "UCUM Custom"; public static final String VISIT = "Visit"; public static final String PLACE_OF_SERVICE = "Place of Service"; /* * Vocabularies */ // private static final String OMOP_4_5 = "OMOP Vocabulary v4.5 20-Oct-2014"; // private static final String GENDER_VOCABULARY = "HL7 Administrative Sex"; private static final String SNOMED_CT = "SNOMED"; private static final String LOINC = "LOINC"; private static final String OMOP_CONDITION_TYPE = "OMOP Condition Occurrence Type"; private static final String UCUM = "UCUM"; // private static final String CMS_PLACE_OF_SERVICE = "CMS Place of Service"; /** * A mapping for some of the existing concepts in the database. The key for the outter mapping is the Concept Class. * The inner map has the value(name) of the Concept as key and the respective id in the database as values in the map. */ protected Map<String, Map<String, Long>> concepts = new HashMap<String, Map<String, Long>>(); private OmopConceptMapping(){} public void loadConcepts(){ CriteriaBuilder builder = entityManager.getCriteriaBuilder(); concepts.put(GENDER, findConceptMap(builder, GENDER, null)); concepts.put(DRUG_EXPOSURE_TYPE, findConceptMap(builder, DRUG_EXPOSURE_TYPE, null)); concepts.put(CLINICAL_FINDING, findConceptMap(builder, CLINICAL_FINDING, SNOMED_CT)); //TODO test size of the object/map concepts.put(CONDITION_TYPE, findConceptMap(builder, CONDITION_TYPE, OMOP_CONDITION_TYPE)); concepts.put(QUALIFIER_VALUE, findConceptMap(builder, QUALIFIER_VALUE, null)); concepts.put(LOINC_CODE, findConceptMap(builder, LOINC_CODE, LOINC)); concepts.put(UCUM_CODE, findConceptMap(builder, UCUM_CODE, UCUM)); concepts.put(UCUM_CODE_STANDARD, findConceptMap(builder, UCUM_CODE, UCUM)); concepts.put(UCUM_CODE_CUSTOM, findConceptMap(builder, UCUM_CODE, UCUM)); concepts.put(VISIT, findConceptMap(builder, VISIT, null)); } public static OmopConceptMapping getInstance(){ return omopConceptMapping; } /** * Searches on the database for the concepts, using, as filters, the concept class and the respective vocabulary name. * @param builder * @param conceptClass * @param vocabularyName * @return A map containing the names(values) of the concepts and their respective id's in the database. */ private Map<String, Long> findConceptMap(CriteriaBuilder builder, String conceptClass, String vocabularyId){ CriteriaQuery<Object[]> criteria = builder.createQuery(Object[].class); Root<Concept> from = criteria.from(Concept.class); Path<Long> idPath = from.get("id"); Path<String> codePath = from.get("conceptCode"); criteria.multiselect(codePath, idPath); //TODO unit test, order matters here Predicate p1 = builder.like(from.get("conceptClassId").as(String.class), conceptClass); if(vocabularyId != null){ Predicate p2 = builder.like(from.get("vocabulary").get("id").as(String.class), vocabularyId); criteria.where(builder.and(p1, p2)); } else{ criteria.where(builder.and(p1)); } TypedQuery<Object[]> query = entityManager.createQuery(criteria); Map<String, Long> retVal = new HashMap<String, Long>(); List<Object[]> resultList = query.getResultList(); for (Object[] result : resultList) { retVal.put(((String)result[0]).toLowerCase(), (Long)result[1]); } return retVal; } @Override public void run() { String baseFhirDao = "myBaseDao"; WebApplicationContext myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext(); entityManager = myAppCtx.getBean(baseFhirDao, BaseFhirDao.class).getEntityManager(); loadConcepts(); } public Long get(String conceptValue, String... conceptClass) { Long retVal = null; for (int i = 0; i < conceptClass.length; i++) { Map<String, Long> concepts = this.concepts.get(conceptClass[i]); if(concepts != null){ retVal = concepts.get(conceptValue.toLowerCase()); if(retVal != null){ return retVal; } } } ourLog.warn("A respective value for {} '{}' could not be found in the database.", conceptClass, conceptValue); return retVal; } public Long get(String conceptCode){ TypedQuery<Long> query = entityManager.createNamedQuery("findConceptByCode", Long.class).setParameter("code", conceptCode); List<Long> results = query.getResultList(); if (results.size() > 0) return results.get(0); else return null; // return (Long) query.getSingleResult(); } public Map<String, Long> getConceptsForClass(String conceptClass) { return concepts.get(conceptClass); } public String getDomain(String conceptCode) { TypedQuery<String> query = entityManager.createNamedQuery("findDomainByCode", String.class).setParameter("code", conceptCode); List<String> results = query.getResultList(); if (results.size() > 0) return results.get(0); else return null; } public String getVocabularyReference(String vocabularyID) { TypedQuery<String> query = entityManager.createNamedQuery("findReferenceById", String.class).setParameter("value", vocabularyID); List<String> results = query.getResultList(); if (results.size() > 0) return results.get(0); else return null; // return (String) query.getSingleResult(); } public Long getPersonByNameAndLocation(PersonComplement person, Location location) { if (person == null) return null; // String queryString = "SELECT p.person_id FROM PERSON p join f_person f on p.person_id = f.person_id join location l on p.location_id = l.location_id where"; String queryString = "SELECT p FROM PersonComplement p WHERE"; String family_name = person.getFamilyName(); String given1_name = person.getGivenName1(); String given2_name = person.getGivenName2(); // String line1 = null, line2 = null, city = null, state = null, zip = null; // if (address != null) { // List<StringDt> lines = address.getLine(); // if (lines.size() > 0) line1 = lines.get(0).toString(); // if (lines.size() > 1) line2 = lines.get(1).toString(); // city = address.getCity(); // state = address.getState(); // zip = address.getPostalCode(); // } // Construct where clause here. String where_clause = ""; if (family_name != null) { where_clause = "familyName like :fname"; } if (given1_name != null) { if (where_clause == "") where_clause = "givenName1 like :gname1"; else where_clause += " AND givenName1 like :gname1"; } if (given2_name != null) { if (where_clause == "") where_clause = "givenName2 like :gname2"; else where_clause += " AND givenName2 like :gname2"; } if (location != null) { if (where_clause == "") where_clause = "location = :location"; else where_clause += " AND location = :location"; } // if (line1 != null) { // if (where_clause == "") where_clause = "l.address1 like :line1"; // else where_clause += " AND l.address1 like :line1"; // } // if (line2 != null) { // if (where_clause == "") where_clause = "l.address2 like :line2"; // else where_clause += " AND l.address2 like :line2"; // } // if (city != null) { // if (where_clause == "") where_clause = "l.city like :city"; // else where_clause += " AND l.city like :city"; // } // if (state != null) { // if (where_clause == "") where_clause = "l.state like :state"; // else where_clause += " AND l.state like :state"; // } // if (zip != null) { // if (where_clause == "") where_clause = "l.zipCode like :zip"; // else where_clause += " AND l.zipCode like :zip"; // } queryString += " "+where_clause; System.out.println("Query for Person"+queryString); TypedQuery<? extends BaseResourceEntity> query = entityManager.createQuery(queryString, PersonComplement.class); if (family_name != null) query = query.setParameter("fname", family_name); if (given1_name != null) query = query.setParameter("gname1", given1_name); if (given2_name != null) query = query.setParameter("gname2", given2_name); if (location != null) query = query.setParameter("location", location); // if (line1 != null) query.setParameter("line1", line1); // if (line2 != null) query.setParameter("line2", line2); // if (city != null) query.setParameter("city", city); // if (state != null) query.setParameter("state", state); // if (zip != null) query.setParameter("zip", zip); System.out.println("family:"+family_name+" gname1:"+given1_name+" gname2"+given2_name); List<? extends BaseResourceEntity> results = query.getResultList(); if (results.size() > 0) { PersonComplement person_c = (PersonComplement) results.get(0); return person_c.getId(); } else return null; // = f.family_name like 'Test1' and f.given1_name like 'Byron' and l.address_1 like '886 Fox Valley Dr.' and city like 'Stone Mountain' and state like 'GA' and zip like '30088'; } public Object loadEntityById(Class<? extends BaseResourceEntity> class1, Long primaryKey){ return entityManager.find(class1, primaryKey); } // OK... we need to sort things out. I am piggybacking on this. I think we will need to refactor // the name this class to cover more generic needs of db query. public Object loadEntityBySource(Class<? extends BaseResourceEntity> class_, String tableName, String columnName, String value) { String query = "SELECT t"+" FROM "+tableName+" t WHERE "+columnName+" like :value"; // System.out.println("hello:"+query); List<? extends BaseResourceEntity> results = entityManager.createQuery(query, class_) .setParameter("value", value).getResultList(); if (results.size() > 0) return results.get(0); else return null; } public Object loadEntityByLocation(Class<? extends BaseResourceEntity> class_, String line1, String line2, String city, String state, String zipCode) { String query; List<? extends BaseResourceEntity> results; if (line2 != null) { query = "SELECT t FROM Location t WHERE address1 LIKE :line1 AND address2 LIKE :line2 AND city LIKE :city AND state LIKE :state AND zipCode LIKE :zip"; results = entityManager.createQuery(query, class_) .setParameter("line1", line1) .setParameter("line2", line2) .setParameter("city", city) .setParameter("state", state) .setParameter("zip", zipCode) .getResultList(); } else { query = "SELECT t FROM Location t WHERE address1 LIKE :line1 AND city LIKE :city AND state LIKE :state AND zipCode LIKE :zip"; results = entityManager.createQuery(query, class_) .setParameter("line1", line1) .setParameter("city", city) .setParameter("state", state) .setParameter("zip", zipCode) .getResultList(); } System.out.println("loadEntityByLocaiton:"+query+" result size:"+results.size()); if (results.size() > 0) return results.get(0); else return null; } // // // CriteriaBuilder builder = entityManager.getCriteriaBuilder(); // CriteriaQuery<Object[]> criteria = builder.createQuery(Object[].class); // Root<? extends BaseResourceEntity> from = criteria.from(class_); // Path<Long> idPath = from.get("id"); // Path<String> codePath = from.get("conceptCode"); // criteria.multiselect(codePath, idPath); //TODO unit test, order matters here // Predicate p1 = builder.like(from.get("conceptClassId").as(String.class), conceptClass); // if(vocabularyId != null){ // Predicate p2 = builder.like(from.get("vocabulary").get("id").as(String.class), vocabularyId); // criteria.where(builder.and(p1, p2)); // } else{ // criteria.where(builder.and(p1)); // } }