/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.ohd.pophealth.json;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.ohd.pophealth.json.measuremodel.CodedValue;
import org.ohd.pophealth.json.measuremodel.Measure;
import org.ohd.pophealth.json.measuremodel.QualityMeasure;
import org.ohd.pophealth.ccr.importer.NonSupportedCategoryException;
/**
* This class handles the reading of quality measures in JSON
* @author ohdohd
*/
public class MeasureReader {
private final static Logger LOG = Logger.getLogger(MeasureReader.class.getName());
static ObjectMapper om = new ObjectMapper();
/**
* Create a QualityMeasure object from a JSON representation of a quality measure
* @param json The JSON String as defined by popHealth
* @return
* @throws Exception If there is any processing problems an Exception is thrown
*/
public static QualityMeasure extractQualityMeasure(String json) throws Exception {
QualityMeasure qm = new QualityMeasure();
ArrayList<Measure> m = new ArrayList<Measure>();
JsonNode root = om.readValue(json, JsonNode.class);
qm.setId(root.path("id").getTextValue());
qm.setName(root.path("name").getTextValue());
qm.setDescription(root.path("description").getTextValue());
qm.setCategory(root.path("category").getTextValue());
qm.setSteward(root.path("steward").getTextValue());
JsonNode mNode = root.path("measure");
// TODO convert to use getElements to improve performance
Iterator<String> mDefs = mNode.getFieldNames();
while (mDefs.hasNext()) {
String subm = mDefs.next();
LOG.log(Level.FINE, "Extracting submeasure: {0}", subm);
m.add(extractMeasure(subm, mNode));
}
qm.setMeasures(m);
return qm;
}
/*
* Extracts the "measure" child object from the JSON
*/
private static Measure extractMeasure(String measureName, JsonNode mNode) throws Exception {
Measure m = new Measure();
m.setName(measureName);
JsonNode subM = mNode.path(measureName);
m.setDescription(subM.path("description").getTextValue());
// Set the type of measure
String itemsType = (subM.path("type").getTextValue());
m.setItemType(extractItems(itemsType, subM.path("items")));
m.setCategory(extractCategories(subM));
// Set Codes
Iterator<JsonNode> codes = subM.path("codes").getElements();
while (codes.hasNext()) {
m.addCode(extractCode(codes.next()));
}
return m;
}
/*
* Creates a CodedValue object from a codeset in the quality measure JSON
*/
protected static CodedValue extractCode(JsonNode c) throws Exception {
CodedValue cv = new CodedValue();
cv.setCodingSystem(c.path("set").getTextValue());
cv.setVersion(c.path("version").getTextValue());
JsonNode values = c.path("values");
if (values.isArray()) {
for (int i = 0; i < values.size(); i++) {
cv.addValue(values.get(i).getTextValue());
}
} else {
throw new Exception("invalid code node");
}
return cv;
}
/*
* Determines the standard category for the "measure" child
*/
protected static Measure.CAT extractCategories(JsonNode subMeasure) throws Exception {
JsonNode cats = subMeasure.path("standard_category");
// If there is no standard_category node retun unknown category
if (cats.isMissingNode()) {
LOG.warning("Missing a Standard Category");
return Measure.CAT.Unknown;
}
return Measure.getCAT(cats.getTextValue());
}
/*
* Determines the return type ("item") required for this "measure" child
*/
protected static Measure.TYPE extractItems(String itemsType, JsonNode items) throws NonSupportedCategoryException {
//throw new UnsupportedOperationException("Not yet implemented");
// Assumes only one item per mesure/items
// Determine which class of item it is
if (itemsType.equals("array")) {
if (items.path("type").getTextValue().equals("number") && items.path("format").getTextValue().equals("utc-sec")) {
// is of type DateListItem
return Measure.TYPE.DateItem;
} else if (items.path("type").getTextValue().equals("object")) {
JsonNode prop = items.path("properties");
if (prop.isMissingNode()) {
throw new NonSupportedCategoryException("Unknown item array type");
} else {
if (!prop.path("value").isMissingNode() && !prop.path("date").isMissingNode()) {
// is of type ValueDateItem
return Measure.TYPE.ValueDateItem;
} else if (!prop.path("start").isMissingNode() && !prop.path("end").isMissingNode()) {
// is of type DateRangeItem
return Measure.TYPE.DateRangeItem;
}
}
}
} else if (itemsType.equals("boolean")) {
return Measure.TYPE.BooleanItem;
}
// Must not of found a supported Item type so throw error
throw new NonSupportedCategoryException("Unknown item type [" + itemsType + "]");
}
}