/*
* Copyright (c) 2014. The Trustees of Indiana University.
*
* This version of the code is licensed under the MPL 2.0 Open Source license with additional
* healthcare disclaimer. If the user is an entity intending to commercialize any application
* that uses this code in a for-profit venture, please contact the copyright holder.
*/
package com.muzima.service;
import android.util.Log;
import com.muzima.api.model.Concept;
import com.muzima.api.model.Encounter;
import com.muzima.api.model.Observation;
import com.muzima.api.model.Patient;
import com.muzima.controller.ConceptController;
import com.muzima.controller.EncounterController;
import com.muzima.controller.ObservationController;
import com.muzima.controller.PatientController;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import static android.util.Xml.newPullParser;
import static com.muzima.utils.DateUtils.parse;
public class FormParser {
private final PatientController patientController;
private final ConceptController conceptController;
private final EncounterController encounterController;
private final ObservationController observationController;
private final ObservationParserUtility observationParserUtility;
private XmlPullParser parser;
private Patient patient;
private Encounter encounter;
private List<Observation> observations;
public FormParser(PatientController patientController, ConceptController conceptController, EncounterController encounterController, ObservationController observationController) {
this(newPullParser(), patientController, conceptController, encounterController, observationController);
}
public FormParser(XmlPullParser parser, PatientController patientController,
ConceptController conceptController, EncounterController encounterController, ObservationController observationController) {
this.encounterController = encounterController;
this.observationController = observationController;
try {
if (parser != null) {
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
}
} catch (XmlPullParserException e) {
throw new ParseFormException(e);
}
this.parser = parser;
this.patientController = patientController;
this.conceptController = conceptController;
this.observationParserUtility = new ObservationParserUtility(conceptController);
}
public List<Observation> parseAndSaveObservations(String xml, String formDataUuid)
throws XmlPullParserException, IOException, ParseException, PatientController.PatientLoadException,
ConceptController.ConceptFetchException, ConceptController.ConceptSaveException,
ConceptController.ConceptParseException, ObservationController.ParseObservationException {
parser.setInput(new ByteArrayInputStream(xml.getBytes()), null);
parser.nextTag();
while (!isEndOf("form")) {
if (isStartOf("patient")) {
patient = getPatient(parser);
}
if (isStartOf("encounter")) {
encounter = createEncounter(parser,formDataUuid);
}
if (isStartOf("obs")) {
observations = createObservations(parser);
}
parser.next();
}
associatePatientsWithEncountersAndObservations();
return observations;
}
private void associatePatientsWithEncountersAndObservations() {
encounter.setPatient(patient);
for (Observation observation : observations) {
observation.setPerson(patient);
observation.setEncounter(encounter);
observation.setObservationDatetime(encounter.getEncounterDatetime());
observation.setUuid(observationParserUtility.getObservationUuid());
}
try {
encounterController.saveEncounter(encounter);
conceptController.saveConcepts(observationParserUtility.getNewConceptList());
observationController.saveObservations(observations);
} catch (EncounterController.SaveEncounterException e) {
Log.e(this.getClass().getSimpleName(), "Saving encounter throwing exception!", e);
} catch (ObservationController.SaveObservationException e) {
Log.e(this.getClass().getSimpleName(), "Saving encounter throwing exception!", e);
} catch (ConceptController.ConceptSaveException e) {
Log.e(this.getClass().getSimpleName(), "Saving encounter throwing exception!", e);
}
}
private Patient getPatient(XmlPullParser parser) throws XmlPullParserException, IOException, PatientController.PatientLoadException {
while (!isEndOf("patient")) {
if (isStartOf("patient.uuid")) {
return patientController.getPatientByUuid(parser.nextText());
}
parser.next();
}
return null;
}
private Encounter createEncounter(XmlPullParser parser, String formDataUuid) throws XmlPullParserException, IOException, ParseException {
Encounter encounter = null;
while (!isEndOf("encounter")) {
if (isStartOf("encounter.encounter_datetime")) {
encounter = observationParserUtility.getEncounterEntity(parse(parser.nextText()), patient,formDataUuid);
}
parser.next();
}
return encounter;
}
private boolean isEndOf(String tagName) throws XmlPullParserException {
return parser.getEventType() == XmlPullParser.END_TAG && tagName.equals(parser.getName());
}
private boolean isStartOf(String tagName) throws XmlPullParserException {
return parser.getEventType() == XmlPullParser.START_TAG && tagName.equals(parser.getName());
}
private List<Observation> createObservations(XmlPullParser parser) throws XmlPullParserException,
IOException, ConceptController.ConceptFetchException, ParseException, ConceptController.ConceptSaveException,
ConceptController.ConceptParseException, ObservationController.ParseObservationException{
List<Observation> observationList = new ArrayList<Observation>();
Stack<String> conceptNames = new Stack<String>();
while (!isEndOf("obs")) {
if (parser.getEventType() == XmlPullParser.START_TAG) {
String conceptName = parser.getAttributeValue("", "concept");
if (conceptName != null) {
conceptNames.push(conceptName);
}
String multipleSelect = parser.getAttributeValue("", "multipleSelect");
boolean isMultipleSelect = false;
if (multipleSelect != null) {
isMultipleSelect = multipleSelect.equalsIgnoreCase("true");
if (isMultipleSelect) {
String questionConceptName = parser.getName();
while (!isEndOf(questionConceptName)) {
parser.next();
if (parser.getEventType() == XmlPullParser.START_TAG) {
String codedObservationName = parser.getAttributeValue("", "concept");
if (codedObservationName != null) {
observationList.add(getObservation(conceptNames, codedObservationName));
}
}
}
}
} else if (isStartOf("value")) {
observationList.add(getObservation(conceptNames, parser.nextText()));
}
}
if (parser.getEventType() == XmlPullParser.END_TAG && !conceptNames.empty()) {
if (!(parser.getName().equals("value") || parser.getName().equals("date") || parser.getName().equals("time"))) {
conceptNames.pop();
}
}
parser.next();
}
observationList.removeAll(Collections.singleton(null));
return observationList;
}
private Observation getObservation(Stack<String> conceptNames, String codedObservationName)
throws ConceptController.ConceptFetchException, ConceptController.ConceptParseException,
ObservationController.ParseObservationException {
Concept conceptEntity = observationParserUtility.getConceptEntity(conceptNames.peek());
return observationParserUtility.getObservationEntity(conceptEntity, codedObservationName);
}
public class ParseFormException extends RuntimeException {
public ParseFormException(Exception e) {
super(e);
}
}
}