import org.apache.commons.lang.StringUtils import org.hibernate.Query import org.hibernate.SessionFactory import org.openmrs.Obs import org.openmrs.Patient import org.openmrs.module.bahmniemrapi.encountertransaction.contract.BahmniObservation import org.openmrs.util.OpenmrsUtil; import org.openmrs.api.context.Context import org.openmrs.module.bahmniemrapi.obscalculator.ObsValueCalculator; import org.openmrs.module.bahmniemrapi.encountertransaction.contract.BahmniEncounterTransaction import org.openmrs.module.emrapi.encounter.domain.EncounterTransaction; import org.joda.time.LocalDate; import org.joda.time.Months; public class BahmniObsValueCalculator implements ObsValueCalculator { static Double BMI_VERY_SEVERELY_UNDERWEIGHT = 16.0; static Double BMI_SEVERELY_UNDERWEIGHT = 17.0; static Double BMI_UNDERWEIGHT = 18.5; static Double BMI_NORMAL = 25.0; static Double BMI_OVERWEIGHT = 30.0; static Double BMI_OBESE = 35.0; static Double BMI_SEVERELY_OBESE = 40.0; static Map obsParentMap = new HashMap(); public static enum BmiStatus { VERY_SEVERELY_UNDERWEIGHT("Very Severely Underweight"), SEVERELY_UNDERWEIGHT("Severely Underweight"), UNDERWEIGHT("Underweight"), NORMAL("Normal"), OVERWEIGHT("Overweight"), OBESE("Obese"), SEVERELY_OBESE("Severely Obese"), VERY_SEVERELY_OBESE("Very Severely Obese"); private String status; BmiStatus(String status) { this.status = status } @Override public String toString() { return status; } } public void run(BahmniEncounterTransaction bahmniEncounterTransaction) { setBMI(bahmniEncounterTransaction); } static def setBMI(BahmniEncounterTransaction bahmniEncounterTransaction) { Collection observations = bahmniEncounterTransaction.getObservations() def nowAsOfEncounter = bahmniEncounterTransaction.getEncounterDateTime() != null ? bahmniEncounterTransaction.getEncounterDateTime() : new Date(); BahmniObservation heightObservation = find("Height", observations, null) BahmniObservation weightObservation = find("Weight", observations, null) BahmniObservation bmiObservation = find("BMI", observations, null) BahmniObservation bmiStatusObservation = find("BMI STATUS", observations, null) BahmniObservation parent = null; if(heightObservation) { parent = obsParentMap.get(heightObservation) } else if(weightObservation) { parent = obsParentMap.get(weightObservation) } System.out.println("Groovy Scripts"); Patient patient = Context.getPatientService().getPatientByUuid(bahmniEncounterTransaction.getPatientUuid()); def patientAgeInMonthsAsOfEncounter = Months.monthsBetween(new LocalDate(patient.getBirthdate()), new LocalDate(nowAsOfEncounter)).getMonths(); if (hasValue(heightObservation) || hasValue(weightObservation)) { if ((heightObservation && heightObservation.voided) && (weightObservation && weightObservation.voided)) { voidBmiObs(bmiObservation, bmiStatusObservation) return } def previousHeightValue = fetchLatestValue("Height", bahmniEncounterTransaction.getPatientUuid(), heightObservation, nowAsOfEncounter) def previousWeightValue = fetchLatestValue("Weight", bahmniEncounterTransaction.getPatientUuid(), weightObservation, nowAsOfEncounter) Double height = hasValue(heightObservation) && !heightObservation.voided ? heightObservation.getValue() as Double : previousHeightValue Double weight = hasValue(weightObservation) && !weightObservation.voided ? weightObservation.getValue() as Double : previousWeightValue Date obsDatetime = getDate(weightObservation) != null ? getDate(weightObservation) : getDate(heightObservation) if (height == null || weight == null) { voidBmiObs(bmiObservation, bmiStatusObservation) return } def bmi = bmi(height, weight) bmiObservation = bmiObservation ?: createObs("BMI", parent, bahmniEncounterTransaction, obsDatetime) as BahmniObservation; bmiObservation.setValue(bmi); bmiObservation.setComment([height: height, weight: weight, bmi: bmi].toString()) def bmiStatus = bmiStatus(bmi, patientAgeInMonthsAsOfEncounter, patient.getGender()); bmiStatusObservation = bmiStatusObservation ?: createObs("BMI STATUS", parent, bahmniEncounterTransaction, obsDatetime) as BahmniObservation; bmiStatusObservation.setValue(bmiStatus); bmiStatusObservation.setComment([height: height, weight: weight, bmi: bmi, bmiStatus: bmiStatus].toString()) } } private static Date getDate(BahmniObservation observation) { return hasValue(observation) && !observation.voided ? observation.getObservationDateTime() : null; } private static boolean hasValue(BahmniObservation observation) { return observation != null && observation.getValue() != null && !StringUtils.isEmpty(observation.getValue().toString()); } private static void voidBmiObs(BahmniObservation bmiObservation, BahmniObservation bmiStatusObservation) { if (hasValue(bmiObservation)) { bmiObservation.voided = true } if (hasValue(bmiStatusObservation)) { bmiStatusObservation.voided = true } } static BahmniObservation createObs(String conceptName, BahmniObservation parent, BahmniEncounterTransaction encounterTransaction, Date obsDatetime) { def concept = Context.getConceptService().getConceptByName(conceptName) BahmniObservation newObservation = new BahmniObservation() newObservation.setConcept(new EncounterTransaction.Concept(concept.getUuid(), conceptName)) newObservation.setObservationDateTime(obsDatetime); parent == null ? encounterTransaction.addObservation(newObservation) : parent.addGroupMember(newObservation) return newObservation } static def bmi(Double height, Double weight) { Double heightInMeters = height * 0.0254; Double value = weight / (heightInMeters * heightInMeters); return new BigDecimal(value).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); }; static def bmiStatus(Double bmi, Integer ageInMonth, String gender) { BMIChart bmiChart = readCSV(OpenmrsUtil.getApplicationDataDirectory() + "obscalculator/BMI_chart.csv"); def bmiChartLine = bmiChart.get(gender, ageInMonth); if(bmiChartLine != null ) { return bmiChartLine.getStatus(bmi); } if (bmi < BMI_VERY_SEVERELY_UNDERWEIGHT) { return BmiStatus.VERY_SEVERELY_UNDERWEIGHT; } if (bmi < BMI_SEVERELY_UNDERWEIGHT) { return BmiStatus.SEVERELY_UNDERWEIGHT; } if (bmi < BMI_UNDERWEIGHT) { return BmiStatus.UNDERWEIGHT; } if (bmi < BMI_NORMAL) { return BmiStatus.NORMAL; } if (bmi < BMI_OVERWEIGHT) { return BmiStatus.OVERWEIGHT; } if (bmi < BMI_OBESE) { return BmiStatus.OBESE; } if (bmi < BMI_SEVERELY_OBESE) { return BmiStatus.SEVERELY_OBESE; } if (bmi >= BMI_SEVERELY_OBESE) { return BmiStatus.VERY_SEVERELY_OBESE; } return null }; static Double fetchLatestValue(String conceptName, String patientUuid, BahmniObservation excludeObs, Date tillDate) { SessionFactory sessionFactory = Context.getRegisteredComponents(SessionFactory.class).get(0) def excludedObsIsSaved = excludeObs != null && excludeObs.uuid != null String excludeObsClause = excludedObsIsSaved ? " and obs.uuid != :excludeObsUuid" : "" Query queryToGetObservations = sessionFactory.getCurrentSession() .createQuery("select obs " + " from Obs as obs, ConceptName as cn " + " where obs.person.uuid = :patientUuid " + " and cn.concept = obs.concept.conceptId " + " and cn.name = :conceptName " + " and obs.voided = false" + " and obs.obsDatetime <= :till" + excludeObsClause + " order by obs.obsDatetime desc"); queryToGetObservations.setString("patientUuid", patientUuid); queryToGetObservations.setParameterList("conceptName", conceptName); queryToGetObservations.setDate("till", tillDate); if (excludedObsIsSaved) { queryToGetObservations.setString("excludeObsUuid", excludeObs.uuid) } queryToGetObservations.setMaxResults(1); List observations = queryToGetObservations.list(); if (observations.size() > 0) { return observations.get(0).getValueNumeric(); } return null } static BahmniObservation find(String conceptName, Collection observations, BahmniObservation parent) { for (BahmniObservation observation : observations) { if (conceptName.equalsIgnoreCase(observation.getConcept().getName())) { obsParentMap.put(observation, parent); return observation; } BahmniObservation matchingObservation = find(conceptName, observation.getGroupMembers(), observation) if (matchingObservation) return matchingObservation; } return null } static BMIChart readCSV(String fileName) { def chart = new BMIChart(); try { new File(fileName).withReader { reader -> def header = reader.readLine(); reader.splitEachLine(",") { tokens -> chart.add(new BMIChartLine(tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5])); } } } catch (FileNotFoundException e) { } return chart; } static class BMIChartLine { public String gender; public Integer ageInMonth; public Double third; public Double fifteenth; public Double eightyFifth; public Double ninetySeventh; BMIChartLine(String gender, String ageInMonth, String third, String fifteenth, String eightyFifth, String ninetySeventh) { this.gender = gender this.ageInMonth = ageInMonth.toInteger(); this.third = third.toDouble(); this.fifteenth = fifteenth.toDouble(); this.eightyFifth = eightyFifth.toDouble(); this.ninetySeventh = ninetySeventh.toDouble(); } public String getStatus(Double bmi) { if(bmi < third) { return BmiStatus.SEVERELY_UNDERWEIGHT } else if(bmi < fifteenth) { return BmiStatus.UNDERWEIGHT } else if(bmi < eightyFifth) { return BmiStatus.NORMAL } else if(bmi < ninetySeventh) { return BmiStatus.OVERWEIGHT } else { return BmiStatus.OBESE } } } static class BMIChart { List lines; Map map = new HashMap(); public add(BMIChartLine line) { def key = new BMIChartLineKey(line.gender, line.ageInMonth); map.put(key, line); } public BMIChartLine get(String gender, Integer ageInMonth) { def key = new BMIChartLineKey(gender, ageInMonth); return map.get(key); } } static class BMIChartLineKey { public String gender; public Integer ageInMonth; BMIChartLineKey(String gender, Integer ageInMonth) { this.gender = gender this.ageInMonth = ageInMonth } boolean equals(o) { if (this.is(o)) return true if (getClass() != o.class) return false BMIChartLineKey bmiKey = (BMIChartLineKey) o if (ageInMonth != bmiKey.ageInMonth) return false if (gender != bmiKey.gender) return false return true } int hashCode() { int result result = (gender != null ? gender.hashCode() : 0) result = 31 * result + (ageInMonth != null ? ageInMonth.hashCode() : 0) return result } } }