diff --git a/opensrp-gizi/src/main/java/util/VaksinatorFormEntityConverter.java b/opensrp-gizi/src/main/java/util/VaksinatorFormEntityConverter.java new file mode 100644 index 0000000..421e480 --- /dev/null +++ b/opensrp-gizi/src/main/java/util/VaksinatorFormEntityConverter.java @@ -0,0 +1,560 @@ +package util; + +/** + * Created by Dani on 10/11/2017. + */ + +import android.content.Context; +import android.util.Log; + +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.joda.time.DateTime; +import org.json.JSONArray; +import org.json.JSONObject; +import org.smartregister.clientandeventmodel.Address; +import org.smartregister.clientandeventmodel.Client; +import org.smartregister.clientandeventmodel.DateUtil; +import org.smartregister.clientandeventmodel.Event; +import org.smartregister.clientandeventmodel.FormAttributeParser; +import org.smartregister.clientandeventmodel.FormEntityConstants; +import org.smartregister.clientandeventmodel.FormEntityConstants.Encounter; +import org.smartregister.clientandeventmodel.FormEntityConstants.FormEntity; +import org.smartregister.clientandeventmodel.FormEntityConstants.Person; +import org.smartregister.clientandeventmodel.FormFieldMap; +import org.smartregister.clientandeventmodel.FormSubmission; +import org.smartregister.clientandeventmodel.FormSubmissionMap; +import org.smartregister.clientandeventmodel.Obs; +import org.smartregister.clientandeventmodel.SubformMap; +import org.smartregister.util.AssetHandler; +import org.smartregister.util.FormUtils; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPathExpressionException; + +public class VaksinatorFormEntityConverter { + private static final String TAG = "FormEntityConverter"; + private FormAttributeParser formAttributeParser; + private Context mContext; + + public VaksinatorFormEntityConverter(FormAttributeParser formAttributeParser, Context _context) { + this.formAttributeParser = formAttributeParser; + mContext = _context; + } + + /** + * Whether form submission is an openmrs form. The xlsform made openmrs form by mapping to an + * encounter_type in settings in xlsform. + * + * @param fs + * @return + */ + public boolean isOpenmrsForm(FormSubmissionMap fs) { + String eventType = fs.formAttributes().get("encounter_type"); + return !StringUtils.isEmpty(eventType); + } + + /** + * Extract Event from given form submission + * + * @param fs + * @return + * @throws ParseException + */ + public Event getEventFromFormSubmission(FormSubmissionMap fs) throws ParseException { + return createEvent(fs.entityId(), fs.formAttributes().get("encounter_type"), fs.fields(), + fs); + } + + private Event createEvent(String entityId, String eventType, List fields, + FormSubmissionMap fs) throws ParseException { + String encounterDateField = getFieldName(Encounter.encounter_date, fs); + String encounterLocation = getFieldName(Encounter.location_id, fs); + + //TODO + String encounterStart = getFieldName(Encounter.encounter_start, fs); + String encounterEnd = getFieldName(Encounter.encounter_end, fs); + + Date encounterDate = new DateTime(FormEntityConstants.FORM_DATE.format(new Date())) + .toDate(); + if (fs.getFieldValue(encounterDateField) != null) { + encounterDate = new DateTime( + FormEntityConstants.FORM_DATE.parse(fs.getFieldValue(encounterDateField))) + .toDate(); + } + + Event e = (Event) new Event().withBaseEntityId(entityId)//should be different for main + // and subform + .withEventDate(encounterDate).withEventType(eventType) + .withLocationId(fs.getFieldValue(encounterLocation)).withProviderId(fs.providerId()) + .withEntityType(fs.bindType()).withFormSubmissionId(fs.instanceId()) + .withDateCreated(new Date()); + + for (FormFieldMap fl : fields) { + Map fat = fl.fieldAttributes(); + if (!fl.values().isEmpty() && !StringUtils.isEmpty(fl.values().get(0)) && fat + .containsKey("openmrs_entity") && fat.get("openmrs_entity") + .equalsIgnoreCase("concept")) { + List vall = new ArrayList<>(); + List humanReadableValues = new ArrayList<>(); + for (String vl : fl.values()) { + String val = fl.valueCodes(vl) == null ? null + : fl.valueCodes(vl).get("openmrs_code"); + // String hval=fl.getValues()==null?null:fl.getValues(); + val = StringUtils.isEmpty(val) ? vl : val; + vall.add(val); + + if (fl.valueCodes(vl) != null && fl.valueCodes(vl).get("openmrs_code") + != null) {// this value is in concept id form + String hval = fl.getValues() == null ? null : fl.getValues().get(0); + humanReadableValues.add(hval); + } + } + e.addObs(new Obs("concept", fl.type(), fat.get("openmrs_entity_id"), + fat.get("openmrs_entity_parent"), vall, humanReadableValues, null, + fl.name())); + } + } + return e; + } + + public Event getEventFromFormSubmission(FormSubmission fs) throws IllegalStateException { + try { + return getEventFromFormSubmission(formAttributeParser.createFormSubmissionMap(fs)); + } catch (JsonIOException | JsonSyntaxException | XPathExpressionException + | ParseException | ParserConfigurationException | SAXException | IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Extract Event for given subform with given data mapped to specified Encounter Type. + * + * @param fs + * @param + * @param eventType + * @param subformInstance + * @return + * @throws ParseException + */ + private Event getEventForSubform(FormSubmissionMap fs, String eventType, SubformMap + subformInstance) throws ParseException { + return createEvent(subformInstance.entityId(), + subformInstance.formAttributes().get("openmrs_entity_id"), subformInstance.fields(), + fs); + } + + /** + * Get field name for specified openmrs entity in given form submission + * + * @param en + * @param fs + * @return + */ + String getFieldName(FormEntity en, FormSubmissionMap fs) { + return getFieldName(en, fs.fields()); + } + + /** + * Get field name for specified openmrs entity in given form submission for given subform + * + * @param en + * @param + * @param + * @return + */ + String getFieldName(FormEntity en, SubformMap subf) { + return getFieldName(en, subf.fields()); + } + + String getFieldName(FormEntity en, List fields) { + for (FormFieldMap f : fields) { + if (f.fieldAttributes().containsKey("openmrs_entity") && f.fieldAttributes() + .get("openmrs_entity").equalsIgnoreCase(en.entity()) && f.fieldAttributes() + .get("openmrs_entity_id").equalsIgnoreCase(en.entityId())) { + return f.name(); + } + } + return null; + } + + /** + * Get field name for specified openmrs attribute mappings in given form submission + * + * @param entity + * @param entityId + * @param entityParentId + * @param fs + * @return + */ + String getFieldName(String entity, String entityId, String entityParentId, FormSubmissionMap + fs) { + return getFieldName(entity, entityId, entityParentId, fs.fields()); + } + + String getFieldName(String entity, String entityId, String entityParentId, SubformMap subf) { + return getFieldName(entity, entityId, entityParentId, subf.fields()); + } + + String getFieldName(String entity, String entityId, String entityParentId, List + fields) { + for (FormFieldMap f : fields) { + if (f.fieldAttributes().containsKey("openmrs_entity") && f.fieldAttributes() + .get("openmrs_entity").equalsIgnoreCase(entity) && f.fieldAttributes() + .get("openmrs_entity_id").equalsIgnoreCase(entityId) && f.fieldAttributes() + .get("openmrs_entity_parent").equalsIgnoreCase(entityParentId)) { + return f.name(); + } + } + return null; + } + + Map extractAddresses(FormSubmissionMap fs) throws ParseException { + Map paddr = new HashMap<>(); + for (FormFieldMap fl : fs.fields()) { + fillAddressFields(fl, paddr); + } + return paddr; + } + + Map extractAddressesForSubform(SubformMap subf) throws ParseException { + Map paddr = new HashMap<>(); + for (FormFieldMap fl : subf.fields()) { + fillAddressFields(fl, paddr); + } + return paddr; + } + + void fillAddressFields(FormFieldMap fl, Map addresses) throws ParseException { + Map att = fl.fieldAttributes(); + if (att.containsKey("openmrs_entity") && att.get("openmrs_entity") + .equalsIgnoreCase("person_address")) { + String addressType = att.get("openmrs_entity_parent"); + String addressField = att.get("openmrs_entity_id"); + Address ad = addresses.get(addressType); + if (ad == null) { + ad = new Address(addressType, null, null, null, null, null, null, null, null); + } + + if (addressField.equalsIgnoreCase("startDate") || addressField + .equalsIgnoreCase("start_date")) { + ad.setStartDate(DateUtil.parseDate(fl.value())); + } else if (addressField.equalsIgnoreCase("endDate") || addressField + .equalsIgnoreCase("end_date")) { + ad.setEndDate(DateUtil.parseDate(fl.value())); + } else if (addressField.equalsIgnoreCase("latitude")) { + ad.setLatitude(fl.value()); + } else if (addressField.equalsIgnoreCase("longitute")) { + ad.setLongitude(fl.value()); + } else if (addressField.equalsIgnoreCase("geopoint")) { + // example geopoint 34.044494 -84.695704 4 76 = lat lon alt prec + String geopoint = fl.value(); + if (!StringUtils.isEmpty(geopoint)) { + String[] g = geopoint.split(" "); + ad.setLatitude(g[0]); + ad.setLongitude(g[1]); + ad.setGeopoint(geopoint); + } + } else if (addressField.equalsIgnoreCase("postal_code") || addressField + .equalsIgnoreCase("postalCode")) { + ad.setPostalCode(fl.value()); + } else if (addressField.equalsIgnoreCase("sub_town") || addressField + .equalsIgnoreCase("subTown")) { + ad.setSubTown(fl.value()); + } else if (addressField.equalsIgnoreCase("town")) { + ad.setTown(fl.value()); + } else if (addressField.equalsIgnoreCase("sub_district") || addressField + .equalsIgnoreCase("subDistrict")) { + ad.setSubDistrict(fl.value()); + } else if (addressField.equalsIgnoreCase("district") || addressField + .equalsIgnoreCase("county") || addressField.equalsIgnoreCase("county_district") + || addressField.equalsIgnoreCase("countyDistrict")) { + ad.setCountyDistrict(fl.value()); + } else if (addressField.equalsIgnoreCase("city") || addressField + .equalsIgnoreCase("village") || addressField.equalsIgnoreCase("cityVillage") + || addressField.equalsIgnoreCase("city_village")) { + ad.setCityVillage(fl.value()); + } else if (addressField.equalsIgnoreCase("state") || addressField + .equalsIgnoreCase("state_province") || addressField + .equalsIgnoreCase("stateProvince")) { + ad.setStateProvince(fl.value()); + } else if (addressField.equalsIgnoreCase("country")) { + ad.setCountry(fl.value()); + } else { + ad.addAddressField(addressField, fl.value()); + } + addresses.put(addressType, ad); + } + } + + Map extractIdentifiers(FormSubmissionMap fs) { + Map pids = new HashMap<>(); + fillIdentifiers(pids, fs.fields()); + return pids; + } + + Map extractIdentifiers(SubformMap subf) { + Map pids = new HashMap<>(); + fillIdentifiers(pids, subf.fields()); + return pids; + } + + void fillIdentifiers(Map pids, List fields) { + for (FormFieldMap fl : fields) { + if (fl.values().size() < 2 && !StringUtils.isEmpty(fl.value())) { + Map att = fl.fieldAttributes(); + + if (att.containsKey("openmrs_entity") && att.get("openmrs_entity") + .equalsIgnoreCase("person_identifier")) { + pids.put(att.get("openmrs_entity_id"), fl.value()); + } + } + } + } + + Map extractAttributes(FormSubmissionMap fs) { + Map pattributes = new HashMap<>(); + fillAttributes(pattributes, fs.fields()); + return pattributes; + } + + Map extractAttributes(SubformMap subf) { + Map pattributes = new HashMap<>(); + fillAttributes(pattributes, subf.fields()); + return pattributes; + } + + Map fillAttributes(Map pattributes, List fields) { + for (FormFieldMap fl : fields) { + if (fl.values().size() < 2 && !StringUtils.isEmpty(fl.value())) { + Map att = fl.fieldAttributes(); + if (att.containsKey("openmrs_entity") && att.get("openmrs_entity") + .equalsIgnoreCase("person_attribute")) { + pattributes.put(att.get("openmrs_entity_id"), fl.value()); + } + } + } + return pattributes; + } + + /** + * Extract Client from given form submission + * + * @param + * @return + * @throws ParseException + */ + public Client getClientFromFormSubmission(FormSubmission fsubmission) throws + IllegalStateException { + FormSubmissionMap fs; + try { + fs = formAttributeParser.createFormSubmissionMap(fsubmission); + return createBaseClient(fs); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + public Client getClientFromFormSubmission(FormSubmissionMap fsubmission) throws Exception { + return createBaseClient(fsubmission); + + } + + public Client createBaseClient(FormSubmissionMap fs) throws ParseException { + Log.d(TAG, "createBaseClient: "+fs.formAttributes()); + + + String firstName = fs.getFieldValue(getFieldName(Person.first_name, fs)); + String middleName = fs.getFieldValue(getFieldName(Person.middle_name, fs)); + String lastName = fs.getFieldValue(getFieldName(Person.last_name, fs)); + String bd = fs.getFieldValue(getFieldName(Person.birthdate, fs)); + DateTime birthdate = bd == null ? null : new DateTime(bd).withTimeAtStartOfDay(); + String dd = fs.getFieldValue(getFieldName(Person.deathdate, fs)); + DateTime deathdate = dd == null ? null : new DateTime(dd).withTimeAtStartOfDay(); + String aproxbd = fs.getFieldValue(getFieldName(Person.birthdate_estimated, fs)); + Boolean birthdateApprox = false; + if (!StringUtils.isEmpty(aproxbd) && NumberUtils.isNumber(aproxbd)) { + int bde = 0; + try { + bde = Integer.parseInt(aproxbd); + } catch (Exception e) { + Log.e(TAG, e.toString(), e); + } + birthdateApprox = bde > 0; + } + String aproxdd = fs.getFieldValue(getFieldName(Person.deathdate_estimated, fs)); + Boolean deathdateApprox = false; + if (!StringUtils.isEmpty(aproxdd) && NumberUtils.isNumber(aproxdd)) { + int dde = 0; + try { + dde = Integer.parseInt(aproxdd); + } catch (Exception e) { + Log.e(TAG, e.toString(), e); + } + deathdateApprox = dde > 0; + } + String gender = fs.getFieldValue(getFieldName(Person.gender, fs)); + + List
addresses = new ArrayList<>(extractAddresses(fs).values()); + + Client c = (Client) new Client(fs.entityId()).withFirstName(firstName) + .withMiddleName(middleName).withLastName(lastName) + .withBirthdate((birthdate != null ? birthdate.toDate() : null), birthdateApprox) + .withDeathdate(deathdate != null ? deathdate.toDate() : null, deathdateApprox) + .withGender(gender).withDateCreated(new Date()); + + c.withAddresses(addresses).withAttributes(extractAttributes(fs)) + .withIdentifiers(extractIdentifiers(fs)); + for (FormFieldMap ffm:fs.fields()){ + if (ffm.name().equals("relationalid")){ + c.addRelationship(ffm.fieldAttributes().get("openmrs_entity_id"), ffm.value()); + } + } + return c; + } + + public Client createSubformClient(SubformMap subf) throws ParseException { + String firstName = subf.getFieldValue(getFieldName(Person.first_name, subf)); + String gender = subf.getFieldValue(getFieldName(Person.gender, subf)); + String bb = subf.getFieldValue(getFieldName(Person.birthdate, subf)); + + Map idents = extractIdentifiers(subf); + //these bunch of lines are making it impossible to create a child model since a child + // doesnt have a firstname but only gender +// if (StringUtils.isEmpty(firstName) +// && StringUtils.isEmpty(bb) +// && idents.size() < 1 && StringUtils.isEmpty(gender)) {//we need to ignore uuid +// of entity +// // if empty repeat group leave this entry and move to next +// return null; +// } + + String middleName = subf.getFieldValue(getFieldName(Person.middle_name, subf)); + String lastName = subf.getFieldValue(getFieldName(Person.last_name, subf)); + DateTime birthdate = new DateTime(bb).withTimeAtStartOfDay(); + String dd = subf.getFieldValue(getFieldName(Person.deathdate, subf)); + DateTime deathdate = dd == null ? null : new DateTime(dd).withTimeAtStartOfDay(); + String aproxbd = subf.getFieldValue(getFieldName(Person.birthdate_estimated, subf)); + Boolean birthdateApprox = false; + if (!StringUtils.isEmpty(aproxbd) && NumberUtils.isNumber(aproxbd)) { + int bde = 0; + try { + bde = Integer.parseInt(aproxbd); + } catch (Exception e) { + Log.e(TAG, e.toString(), e); + } + birthdateApprox = bde > 0; + } + String aproxdd = subf.getFieldValue(getFieldName(Person.deathdate_estimated, subf)); + Boolean deathdateApprox = false; + if (!StringUtils.isEmpty(aproxdd) && NumberUtils.isNumber(aproxdd)) { + int dde = 0; + try { + dde = Integer.parseInt(aproxdd); + } catch (Exception e) { + Log.e(TAG, e.toString(), e); + } + deathdateApprox = dde > 0; + } + + List
addresses = new ArrayList<>(extractAddressesForSubform(subf).values()); + + Client c = (Client) new Client(subf.getFieldValue("id")).withFirstName(firstName) + .withMiddleName(middleName).withLastName(lastName) + .withBirthdate(new DateTime(birthdate).toDate(), birthdateApprox) + .withDeathdate(new DateTime(deathdate).toDate(), deathdateApprox).withGender(gender) + .withDateCreated(new Date()); + + c.withAddresses(addresses).withAttributes(extractAttributes(subf)).withIdentifiers(idents); + + addRelationship(subf, c); + + return c; + } + + private void addRelationship(SubformMap subformMap, Client client) { + try { + String relationships = AssetHandler + .readFileFromAssetsFolder(FormUtils.ecClientRelationships, mContext); + JSONArray jsonArray = null; + + jsonArray = new JSONArray(relationships); + + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject rObject = jsonArray.getJSONObject(i); + if (rObject.has("field")) { + //is this a new child registration, add person relationships -mother + if (subformMap.getField(rObject.getString("field")) != null) { + + client.addRelationship(rObject.getString("client_relationship"), + subformMap.getField(rObject.getString("field")).value()); + + } + } + } + } catch (Exception e) { + Log.e(TAG, e.toString(), e); + } + } + + /** + * Extract Client and Event from given form submission for entities dependent on main + * beneficiary (excluding main beneficiary). + * The dependent entities are specified via subforms (repeat groups) in xls forms. + * + * @param + * @return The clients and events Map with id of dependent entity as key. Each entry in Map + * contains an + * internal map that holds Client and Event info as "client" and "event" respectively for that + * dependent entity (whose id is the key of main Map). + * Ex: + * {222222-55d555-ffffff-232323-ffffff: {client: ClientObjForGivenID, event: + * EventObjForGivenIDAndForm}}, + * {339393-545445-ffdddd-333333-ffffff: {client: ClientObjForGivenID, event: + * EventObjForGivenIDAndForm}}, + * {278383-765766-dddddd-767666-ffffff: {client: ClientObjForGivenID, event: + * EventObjForGivenIDAndForm}} + * @throws ParseException + */ + public Map> getDependentClientsFromFormSubmission( + FormSubmission fsubmission) throws IllegalStateException { + FormSubmissionMap fs; + try { + fs = formAttributeParser.createFormSubmissionMap(fsubmission); + Map> map = new HashMap<>(); + for (SubformMap sbf : fs.subforms()) { + Map att = sbf.formAttributes(); + if (att.containsKey("openmrs_entity") && att.get("openmrs_entity") + .equalsIgnoreCase("person")) { + Map cne = new HashMap<>(); + + Client subformClient = createSubformClient(sbf); + + if (subformClient != null) { + cne.put("client", subformClient); + cne.put("event", getEventForSubform(fs, att.get("openmrs_entity_id"), sbf)); + + map.put(sbf.entityId(), cne); + } + } + } + return map; + } catch (JsonIOException | JsonSyntaxException | XPathExpressionException | + ParserConfigurationException | SAXException | IOException | ParseException e) { + Log.e(TAG, e.toString(), e); + throw new IllegalStateException(e); + } + } +} \ No newline at end of file diff --git a/opensrp-gizi/src/main/java/util/VaksinatorFormUtils.java b/opensrp-gizi/src/main/java/util/VaksinatorFormUtils.java new file mode 100644 index 0000000..4ba147e --- /dev/null +++ b/opensrp-gizi/src/main/java/util/VaksinatorFormUtils.java @@ -0,0 +1,1111 @@ +package util; + +import android.content.Context; +import android.content.Intent; +import android.util.Xml; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.XML; +import org.smartregister.CoreLibrary; +import org.smartregister.clientandeventmodel.Client; +import org.smartregister.clientandeventmodel.Event; +import org.smartregister.clientandeventmodel.FormAttributeParser; +import org.smartregister.clientandeventmodel.FormData; +import org.smartregister.clientandeventmodel.FormField; +import org.smartregister.clientandeventmodel.FormInstance; +import org.smartregister.clientandeventmodel.SubFormData; +import org.smartregister.domain.SyncStatus; +import org.smartregister.domain.form.FormSubmission; +import org.smartregister.domain.form.SubForm; +import org.smartregister.service.intentservices.ReplicationIntentService; +import org.smartregister.sync.CloudantDataHandler; +import org.smartregister.util.AssetHandler; +import org.smartregister.util.Log; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.text.Format; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +/** + * Created by Dani on 08/11/2017. + */ +public class VaksinatorFormUtils { + + public static final String TAG = "VaksinatorFormUtils"; + public static final String ecClientRelationships = "ec_client_relationships.json"; + private static final String shouldLoadValueKey = "shouldLoadValue"; + private static final String relationalIdKey = "relational_id"; + private static final String databaseIdKey = "_id"; + private static final String injectedBaseEntityIdKey = "injectedBaseEntityId"; + private static VaksinatorFormUtils instance; + private Context mContext; + private org.smartregister.Context theAppContext; + private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + private Format formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private VaksinatorFormEntityConverter formEntityConverter; + private CloudantDataHandler mCloudantDataHandler; + + public VaksinatorFormUtils(Context context) throws Exception { + mContext = context; + theAppContext = CoreLibrary.getInstance().context(); + FormAttributeParser formAttributeParser = new FormAttributeParser(context); + formEntityConverter = new VaksinatorFormEntityConverter(formAttributeParser, mContext); + // Protect creation of static variable. + mCloudantDataHandler = CloudantDataHandler.getInstance(context.getApplicationContext()); + } + + public static VaksinatorFormUtils getInstance(Context ctx) throws Exception { + if (instance == null) { + instance = new VaksinatorFormUtils(ctx); + } + + return instance; + } + + /* Checks if the provided node has Child elements + * @param element + * @return + */ + public static boolean hasChildElements(Node element) { + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + if (children.item(i).getNodeType() == Node.ELEMENT_NODE) { + return true; + } + } + + return false; + } + + private static JSONObject retrieveRelationshipJsonForLink(String link, JSONArray array) + throws Exception { + for (int i = 0; i < array.length(); i++) { + JSONObject object = array.getJSONObject(i); + if (relationShipExist(link, object)) { + System.out.println("Relationship found ##"); + + return object; + } + } + + return null; + } + + private static boolean relationShipExist(String link, JSONObject json) { + try { + String[] path = link.split("\\."); + String parentTable = path[0]; + String childTable = path[1]; + + String jsonParentTableString = json.getString("parent"); + String jsonChildTableString = json.getString("child"); + + boolean parentToChildExist = + jsonParentTableString.equals(parentTable) && jsonChildTableString + .equals(childTable); + boolean childToParentExist = + jsonParentTableString.equals(childTable) && jsonChildTableString + .equals(parentTable); + + if (parentToChildExist || childToParentExist) { + return true; + } + + } catch (Exception e) { + android.util.Log.e(TAG, e.toString(), e); + } + + return false; + } + + public static int getIndexForFormName(String formName, String[] formNames) { + for (int i = 0; i < formNames.length; i++) { + if (formName.equalsIgnoreCase(formNames[i])) { + return i; + } + } + + return -1; + } + + public FormSubmission generateFormSubmisionFromXMLString(String entity_id, String formData, + String formName, JSONObject + overrides) throws Exception { + JSONObject formSubmission = XML.toJSONObject(formData); + + //FileUtilities fu = new FileUtilities(); + //fu.write("xmlform.txt", formData); + //fu.write("xmlformsubmission.txt", formSubmission.toString()); + System.out.println(formSubmission); + + // use the form_definition.json to iterate through fields + String formDefinitionJson = readFileFromAssetsFolder( + "www/form/" + formName + "/form_definition.json"); + JSONObject formDefinition = new JSONObject(formDefinitionJson); + + String rootNodeKey = formSubmission.keys().next(); + + // retrieve the id, if it fails use the provided value by the param + entity_id = formSubmission.getJSONObject(rootNodeKey).has(databaseIdKey) ? formSubmission + .getJSONObject(rootNodeKey).getString(databaseIdKey) : generateRandomUUIDString(); + + //String bindPath = formDefinition.getJSONObject("form").getString("bind_type"); + JSONObject fieldsDefinition = formDefinition.getJSONObject("form"); + JSONArray populatedFieldsArray = getPopulatedFieldsForArray(fieldsDefinition, entity_id, + formSubmission, overrides); + + // replace all the fields in the form + formDefinition.getJSONObject("form").put("fields", populatedFieldsArray); + + //get the subforms + if (formDefinition.getJSONObject("form").has("sub_forms")) { + JSONObject subFormDefinition = formDefinition.getJSONObject("form"). + getJSONArray("sub_forms").getJSONObject(0); + // get the bind path for the sub-form, helps us to locate the node that holds the data + // in the corresponding data json + String bindPath = subFormDefinition.getString("default_bind_path"); + + // get the actual sub-form data + JSONArray subFormDataArray = new JSONArray(); + Object subFormDataObject = getObjectAtPath(bindPath.split("/"), formSubmission); + + if (subFormDataObject instanceof JSONObject) { + JSONObject subFormData = (JSONObject) subFormDataObject; + subFormDataArray.put(0, subFormData); + } else if (subFormDataObject instanceof JSONArray) { + subFormDataArray = (JSONArray) subFormDataObject; + } + + JSONArray subForms = getSubForms(subFormDataArray, entity_id, subFormDefinition, + overrides); + + // replace the subforms field with real data + formDefinition.getJSONObject("form").put("sub_forms", subForms); + //throw new Exception(); + } + + String instanceId = generateRandomUUIDString(); + String entityId = retrieveIdForSubmission(formDefinition); + String formDefinitionVersionString = formDefinition + .getString("form_data_definition_version"); + + String clientVersion = String.valueOf(new Date().getTime()); + String instance = formDefinition.toString(); + FormSubmission fs = new FormSubmission(instanceId, entityId, formName, instance, + clientVersion, SyncStatus.PENDING, formDefinitionVersionString); + + generateClientAndEventModelsForFormSubmission(fs, formName); + + return fs; + } + + private void generateClientAndEventModelsForFormSubmission(FormSubmission formSubmission, + String formName) { + org.smartregister.clientandeventmodel.FormSubmission v2FormSubmission; + + String anmId = CoreLibrary.getInstance().context().anmService().fetchDetails().name(); + String instanceId = formSubmission.instanceId(); + String entityId = formSubmission.entityId(); + Long clientVersion = new Date().getTime(); + String formDataDefinitionVersion = formSubmission.formDataDefinitionVersion(); + + String bind_type = formSubmission.getFormInstance().getForm().getBind_type(); + String default_bind_path = formSubmission.getFormInstance().getForm() + .getDefault_bind_path(); + + List fields = convertFormFields(formSubmission.getFormInstance().getForm(). + getFields()); + + List sub_forms = getSubFormList(formSubmission); + FormData formData = new FormData(bind_type, default_bind_path, fields, sub_forms); + + FormInstance formInstance = new FormInstance(formData); + formInstance.setForm_data_definition_version(formDataDefinitionVersion); + + v2FormSubmission = new org.smartregister.clientandeventmodel.FormSubmission(anmId, + instanceId, formName, entityId, clientVersion, formDataDefinitionVersion, + formInstance, clientVersion); + + // retrieve client and events + Client c = formEntityConverter.getClientFromFormSubmission(v2FormSubmission); + printClient(c); + Event e = formEntityConverter.getEventFromFormSubmission(v2FormSubmission); + printEvent(e); + org.smartregister.cloudant.models.Event event = new org.smartregister.cloudant.models.Event( + e); + createNewEventDocument(event); + if (c != null) { + org.smartregister.cloudant.models.Client client = new org.smartregister.cloudant + .models.Client( + c); + createNewClientDocument(client); + } + + Map> dep = formEntityConverter. + getDependentClientsFromFormSubmission(v2FormSubmission); + for (Map cm : dep.values()) { + Client cin = (Client) cm.get("client"); + Event evin = (Event) cm.get("event"); + event = new org.smartregister.cloudant.models.Event(evin); + createNewEventDocument(event); + + if (cin != null) { + org.smartregister.cloudant.models.Client client = new org.smartregister.cloudant + .models.Client( + cin); + createNewClientDocument(client); + printClient(cin); + } + printEvent(evin); + + } + + startReplicationIntentService(); + } + + private void printClient(Client client) { + Log.logDebug("============== CLIENT ================"); + Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").create(); + String clientJson = gson.toJson(client); + Log.logDebug(clientJson); + Log.logDebug("===================================="); + + } + + private void printEvent(Event event) { + Log.logDebug("============== EVENT ================"); + Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").create(); + String eventJson = gson.toJson(event); + Log.logDebug(eventJson); + Log.logDebug("===================================="); + } + + /** + * Start ReplicationIntentService which handles cloudant sync processes + */ + private void startReplicationIntentService() { + + Intent serviceIntent = new Intent(mContext, ReplicationIntentService.class); + mContext.startService(serviceIntent); + } + + private List getSubFormList(FormSubmission formSubmission) { + List sub_forms = new ArrayList(); + List subForms = formSubmission.getFormInstance().getForm().getSub_forms(); + if (subForms != null) { + for (SubForm sf : subForms) { + SubFormData sd = new SubFormData(); + sd.setDefault_bind_path(sf.getDefaultBindPath()); + sd.setBind_type(sf.getBindType()); + + List subFormFields = convertFormFields(sf.getFields()); + sd.setFields(subFormFields); + + sd.setInstances(sf.getInstances()); + sd.setName(sf.getName()); + sub_forms.add(sd); + } + } + + return sub_forms; + } + + private List convertFormFields(List + formFields) { + List fields = new ArrayList(); + for (org.smartregister.domain.form.FormField ff : formFields) { + FormField f = new FormField(ff.getName(), ff.getValue(), ff.getSource()); + fields.add(f); + } + + return fields; + } + + private JSONArray getSubForms(JSONArray subFormDataArray, String entity_id, JSONObject + subFormDefinition, JSONObject overrides) throws Exception { + JSONArray subForms = new JSONArray(); + + JSONArray subFormFields = getFieldsArrayForSubFormDefinition(subFormDefinition); + JSONArray subFormInstances = new JSONArray(); + + // the id of each subform is contained in the attribute of the enclosing element + for (int i = 0; i < subFormDataArray.length(); i++) { + JSONObject subFormData = subFormDataArray.getJSONObject(i); + String relationalId = + subFormData.has(relationalIdKey) ? subFormData.getString(relationalIdKey) + : entity_id; + String id = subFormData.has(databaseIdKey) ? subFormData.getString(databaseIdKey) + : generateRandomUUIDString(); + JSONObject subFormInstance = getFieldValuesForSubFormDefinition(subFormDefinition, + relationalId, id, subFormData, overrides); + subFormInstances.put(i, subFormInstance); + } + + subFormDefinition.put("instances", subFormInstances); + subFormDefinition.put("fields", subFormFields); + subForms.put(0, subFormDefinition); + + return subForms; + } + + public String generateXMLInputForFormWithEntityId(String entityId, String formName, String + overrides) { + try { + // get the field overrides map + JSONObject fieldOverrides = new JSONObject(); + if (overrides != null) { + fieldOverrides = new JSONObject(overrides); + String overridesStr = fieldOverrides.getString("fieldOverrides"); + fieldOverrides = new JSONObject(overridesStr); + } + + // use the form_definition.json to get the form mappings + String formDefinitionJson = readFileFromAssetsFolder( + "www/form/" + formName + "/form_definition.json"); + JSONObject formDefinition = new JSONObject(formDefinitionJson); + String ec_bind_path = formDefinition.getJSONObject("form").getString("ec_bind_type"); + + String sql = + "select * from " + ec_bind_path + " where base_entity_id='" + entityId + "'"; + Map dbEntity = theAppContext.formDataRepository(). + getMapFromSQLQuery(sql); + Map detailsMap = theAppContext.detailsRepository(). + getAllDetailsForClient(entityId); + detailsMap.putAll(dbEntity); + + JSONObject entityJson = new JSONObject(); + if (detailsMap != null && !detailsMap.isEmpty()) { + entityJson = new JSONObject(detailsMap); + } + + //read the xml form model, the expected form model that is passed to the form mirrors it + String formModelString = readFileFromAssetsFolder( + "www/form/" + formName + "/model" + ".xml").replaceAll("\n", " ") + .replaceAll("\r", " "); + InputStream is = new ByteArrayInputStream(formModelString.getBytes()); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setValidating(false); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(is); + + XmlSerializer serializer = Xml.newSerializer(); + StringWriter writer = new StringWriter(); + serializer.setOutput(writer); + serializer.startDocument("UTF-8", true); + + //skip processing + NodeList els = ((Element) document.getElementsByTagName("model").item(0)). + getElementsByTagName("instance"); + Element el = (Element) els.item(0); + NodeList entries = el.getChildNodes(); + int num = entries.getLength(); + for (int i = 0; i < num; i++) { + Node n = entries.item(i); + if (n instanceof Element) { + Element node = (Element) n; + writeXML(node, serializer, fieldOverrides, formDefinition, entityJson, null); + } + } + + serializer.endDocument(); + + String xml = writer.toString(); + // Add model and instance tags + xml = xml.substring(56); + System.out.println(xml); + android.util.Log.d(TAG, "generateXMLInputForFormWithEntityId: "+xml); + + return xml; + + } catch (Exception e) { + android.util.Log.e(TAG, e.toString(), e); + } + return ""; + } + + private void writeXML(Element node, XmlSerializer serializer, JSONObject fieldOverrides, + JSONObject formDefinition, JSONObject entityJson, String parentId) { + try { + String nodeName = node.getNodeName(); + String entityId = + entityJson.has("id") ? entityJson.getString("id") : generateRandomUUIDString(); + String relationalId = + entityJson.has(relationalIdKey) ? entityJson.getString(relationalIdKey) + : parentId; + + serializer.startTag("", nodeName); + + // write the xml attributes + writeXMLAttributes(node, serializer, entityId, relationalId); + + String nodeValue = retrieveValueForNodeName(nodeName, entityJson, formDefinition); + //overwrite the node value with contents from overrides map + if (fieldOverrides.has(nodeName)) { + nodeValue = fieldOverrides.getString(nodeName); + } + //write the node value + if (nodeValue != null) { + serializer.text(nodeValue); + } + + List subFormNames = getSubFormNames(formDefinition); + + // get all child nodes + NodeList entries = node.getChildNodes(); + int num = entries.getLength(); + for (int i = 0; i < num; i++) { + if (entries.item(i) instanceof Element) { + Element child = (Element) entries.item(i); + String fieldName = child.getNodeName(); + + // its a subform element process it + if (!subFormNames.isEmpty() && subFormNames.contains(fieldName)) { + // its a subform element process it + // get the subform definition + JSONArray subForms = formDefinition.getJSONObject("form"). + getJSONArray("sub_forms"); + JSONObject subFormDefinition = retriveSubformDefinitionForBindPath(subForms, + fieldName); + if (subFormDefinition != null) { + + String childTableName = subFormDefinition.getString("ec_bind_type"); + String sql = "select * from '" + childTableName + "' where " + + "relational_id = '" + entityId + "'"; + String childRecordsString = theAppContext.formDataRepository(). + queryList(sql); + JSONArray childRecords = new JSONArray(childRecordsString); + + JSONArray fieldsArray = subFormDefinition.getJSONArray("fields"); + // check whether we are supposed to load the id of the child record + JSONObject idFieldDefn = getJsonFieldFromArray("id", fieldsArray); + + // definition for id + boolean shouldLoadId = + idFieldDefn.has(shouldLoadValueKey) && idFieldDefn + .getBoolean(shouldLoadValueKey); + + if (shouldLoadId && childRecords.length() > 0) { + for (int k = 0; k < childRecords.length(); k++) { + JSONObject childEntityJson = childRecords.getJSONObject(k); + JSONObject obj = getCombinedJsonObjectForObject( + childEntityJson); + writeXML(child, serializer, fieldOverrides, subFormDefinition, + childEntityJson, entityId); + } + + } + } + } else { + // it's not a sub-form element write its value + serializer.startTag("", fieldName); + // write the xml attributes + // a value node doesn't have id or relationalId fields + writeXMLAttributes(child, serializer, null, null); + // write the node value + String value = retrieveValueForNodeName(fieldName, entityJson, + formDefinition); + // write the node value + if (value != null) { + serializer.text(value); + } + + // overwrite the node value with contents from overrides map + if (fieldOverrides.has(fieldName)) { + serializer.text(fieldOverrides.getString(fieldName)); + } + + serializer.endTag("", fieldName); + } + } + } + + serializer.endTag("", node.getNodeName()); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Retrieve additional details for this record from the details Table. + * + * @param entityJson + * @return + */ + private JSONObject getCombinedJsonObjectForObject(JSONObject entityJson) { + try { + String baseEntityId = entityJson.getString("base_entity_id"); + Map map = theAppContext.detailsRepository(). + getAllDetailsForClient(baseEntityId); + + for (String key : map.keySet()) { + if (!entityJson.has(key)) { + entityJson.put(key, map.get(key)); + } + } + } catch (Exception e) { + android.util.Log.e(TAG, e.toString(), e); + } + return entityJson; + } + + /** + * Iterate through the provided array and retrieve a json object whose name attribute matches + * the name supplied + * + * @param fieldName + * @param array + * @return + */ + private JSONObject getJsonFieldFromArray(String fieldName, JSONArray array) { + try { + if (array != null) { + for (int i = 0; i < array.length(); i++) { + JSONObject field = array.getJSONObject(i); + String name = field.has("name") ? field.getString("name") : null; + if (name.equals(fieldName)) { + return field; + } + } + } + } catch (Exception e) { + android.util.Log.e(TAG, e.toString(), e); + } + return null; + } + + /** + * retrieves node value for cases which the nodename don't match the name of the xml element + * + * @param nodeName + * @param entityJson + * @param formDefinition + * @return + */ + private String retrieveValueForNodeName(String nodeName, JSONObject entityJson, JSONObject + formDefinition) { + try { + if (entityJson != null && entityJson.length() > 0) { + JSONObject fieldsObject = + formDefinition.has("form") ? formDefinition.getJSONObject("form") + : formDefinition.has("sub_forms") ? formDefinition + .getJSONObject("sub_forms") : formDefinition; + if (fieldsObject.has("fields")) { + JSONArray fields = fieldsObject.getJSONArray("fields"); + for (int i = 0; i < fields.length(); i++) { + JSONObject field = fields.getJSONObject(i); + String bindPath = field.has("bind") ? field.getString("bind") : null; + String name = field.has("name") ? field.getString("name") : null; + + boolean matchingNodeFound = + bindPath != null && name != null && bindPath.endsWith(nodeName) + || name != null && name.equals(nodeName); + + if (matchingNodeFound) { + if (field.has("shouldLoadValue") && field + .getBoolean("shouldLoadValue")) { + String keyName = entityJson.has(nodeName) ? nodeName : name; + if (entityJson.has(keyName)) { + return entityJson.getString(keyName); + } else { + return ""; + } + } else { + // the shouldLoadValue flag isn't set + return ""; + } + } + } + } + } + } catch (Exception e) { + android.util.Log.e(TAG, e.toString(), e); + } + + return ""; + } + + /** + * Currently not used but, the method should retrieve the path of a given node, + * useful when confirming if the current node has been properly mapped to its bind_path + **/ + private String getXPath(Node node) { + Node parent = node.getParentNode(); + if (parent == null) { + return "/" + node.getNodeName(); + } + + return getXPath(parent) + "/"; + } + + private List getSubFormNames(JSONObject formDefinition) throws Exception { + List subFormNames = new ArrayList(); + if (formDefinition.has("form") && formDefinition.getJSONObject("form").has("sub_forms")) { + JSONArray subForms = formDefinition.getJSONObject("form").getJSONArray("sub_forms"); + for (int i = 0; i < subForms.length(); i++) { + JSONObject subForm = subForms.getJSONObject(i); + String subFormNameStr = subForm.getString("default_bind_path"); + String[] path = subFormNameStr.split("/"); + String subFormName = path[path.length - 1]; // the last token + subFormNames.add(subFormName); + } + } + + return subFormNames; + } + + private JSONObject retriveSubformDefinitionForBindPath(JSONArray subForms, String fieldName) + throws Exception { + for (int i = 0; i < subForms.length(); i++) { + JSONObject subForm = subForms.getJSONObject(i); + String subFormNameStr = subForm.getString("default_bind_path"); + String[] path = subFormNameStr.split("/"); + String subFormName = path[path.length - 1]; // the last token + + if (fieldName.equalsIgnoreCase(subFormName)) { + return subForm; + } + } + + return null; + } + + private void writeXMLAttributes(Element node, XmlSerializer serializer, String id, String + relationalId) { + try { + // get a map containing the attributes of this node + NamedNodeMap attributes = node.getAttributes(); + + // get the number of nodes in this map + int numAttrs = attributes.getLength(); + + if (id != null) { + serializer.attribute("", databaseIdKey, id); + } + + if (relationalId != null) { + serializer.attribute("", relationalIdKey, relationalId); + } + + for (int i = 0; i < numAttrs; i++) { + Attr attr = (Attr) attributes.item(i); + String attrName = attr.getNodeName(); + String attrValue = attr.getNodeValue(); + serializer.attribute("", attrName, attrValue); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private String generateRandomUUIDString() { + return UUID.randomUUID().toString(); + } + + private String retrieveIdForSubmission(JSONObject jsonObject) throws Exception { + JSONArray fields = jsonObject.getJSONObject("form").getJSONArray("fields"); + + for (int i = 0; i < fields.length(); i++) { + JSONObject field = fields.getJSONObject(i); + + if (field.has("name") && field.getString("name").equalsIgnoreCase("id")) { + return field.getString("value"); + } + } + return null; + } + + public Object getObjectAtPath(String[] path, JSONObject jsonObject) throws Exception { + JSONObject object = jsonObject; + int i = 0; + while (i < path.length - 1) { + if (object.has(path[i])) { + Object o = object.get(path[i]); + if (o instanceof JSONObject) { + object = object.getJSONObject(path[i]); + } else if (o instanceof JSONArray) { + object = object.getJSONArray(path[i]).getJSONObject(0); + } + } + i++; + } + return object.has(path[i]) ? object.get(path[i]) : null; + } + + public JSONArray getPopulatedFieldsForArray(JSONObject fieldsDefinition, String entityId, + JSONObject jsonObject, JSONObject overrides) + throws Exception { + String bindPath = fieldsDefinition.getString("bind_type"); + String sql = "select * from " + bindPath + " where id='" + entityId + "'"; + String dbEntity = theAppContext.formDataRepository().queryUniqueResult(sql); + + JSONObject entityJson = new JSONObject(); + + if (dbEntity != null && !dbEntity.isEmpty()) { + entityJson = new JSONObject(dbEntity); + } + + JSONArray fieldsArray = fieldsDefinition.getJSONArray("fields"); + + for (int i = 0; i < fieldsArray.length(); i++) { + JSONObject item = fieldsArray.getJSONObject(i); + + if (!item.has("name")) { + continue; // skip elements without name + } + + String itemName = item.getString("name"); + boolean shouldLoadValue = + item.has("shouldLoadValue") && item.getBoolean("shouldLoadValue"); + + if (item.has("bind")) { + String pathSting = item.getString("bind"); + pathSting = pathSting.startsWith("/") ? pathSting.substring(1) : pathSting; + String[] path = pathSting.split("/"); + String value = getValueForPath(path, jsonObject); + item.put("value", value); + } + + if (shouldLoadValue && overrides.has(item.getString("name"))) { + // if the value is not set use the value in the overrides filed + if (!item.has("value")) { + item.put("value", overrides.getString(item.getString("name"))); + } + } + + // map the id field for child elements + if (isForeignIdPath(item)) { + String value = null; + if (entityJson.length() > 0 && shouldLoadValue) { + //retrieve the child attributes + value = retrieveValueForLinkedRecord(item.getString("source"), entityJson); + } + + // generate uuid if its still not available + if (item.getString("source").endsWith(".id") && value == null) { + value = generateRandomUUIDString(); + } + + if (value != null && !item.has("value")) { + item.put("value", value); + } + } + + // add source property if not available + if (!item.has("source")) { + item.put("source", bindPath + "." + item.getString("name")); + } + + if (itemName.equalsIgnoreCase("id") && !isForeignIdPath(item)) { + assert entityId != null; + item.put("value", entityId); + } + + if (itemName.equalsIgnoreCase(injectedBaseEntityIdKey)) { + assert entityId != null; + item.put("value", entityId); + } + + if (itemName.equalsIgnoreCase("start") || itemName.equalsIgnoreCase("end")) { + try { + boolean isEndTime = itemName.equalsIgnoreCase("end"); + String val = + item.has("value") ? item.getString("value") : sdf.format(new Date()); + + if (isEndTime) { + val = formatter.format(new Date()); + } else { + Date d = sdf.parse(val); + //parse the date to match OpenMRS format + val = formatter.format(d); + } + + item.put("value", val); + } catch (Exception e) { + android.util.Log.e(TAG, e.toString(), e); + } + } + } + return fieldsArray; + } + + private boolean isForeignIdPath(JSONObject item) throws Exception { + // e.g ibu.anak.id + return item.has("source") && item.getString("source").split("\\.").length > 2; + } + + public String retrieveValueForLinkedRecord(String link, JSONObject entityJson) { + try { + String entityRelationships = readFileFromAssetsFolder( + "www/form/entity_relationship" + ".json"); + JSONArray json = new JSONArray(entityRelationships); + Log.logInfo(json.toString()); + + JSONObject rJson; + + if ((rJson = retrieveRelationshipJsonForLink(link, json)) != null) { + String[] path = link.split("\\."); + String parentTable = path[0]; + String childTable = path[1]; + + String joinValueKey = + parentTable.equals(rJson.getString("parent")) ? rJson.getString("from") + : rJson.getString("to"); + joinValueKey = joinValueKey.contains(".") ? joinValueKey + .substring(joinValueKey.lastIndexOf(".") + 1) : joinValueKey; + + String val = entityJson.getString(joinValueKey); + + String joinField = + parentTable.equals(rJson.getString("parent")) ? rJson.getString("to") + : rJson.getString("from"); + String sql = + "select * from " + childTable + " where " + joinField + "='" + val + "'"; + Log.logInfo(sql); + String dbEntity = theAppContext.formDataRepository().queryUniqueResult(sql); + JSONObject linkedEntityJson = new JSONObject(); + + if (dbEntity != null && !dbEntity.isEmpty()) { + linkedEntityJson = new JSONObject(dbEntity); + } + + // finally retrieve the value from the child entity, need to improve or remove + // entirely these hacks + String sourceKey = link.substring(link.lastIndexOf(".") + 1); + + if (linkedEntityJson.has(sourceKey)) { + return linkedEntityJson.getString(sourceKey); + } + } + + } catch (Exception e) { + android.util.Log.e(TAG, e.toString(), e); + } + return null; + } + + public JSONArray getFieldsArrayForSubFormDefinition(JSONObject fieldsDefinition) throws + Exception { + JSONArray fieldsArray = fieldsDefinition.getJSONArray("fields"); + String bindPath = fieldsDefinition.getString("bind_type"); + + JSONArray subFormFieldsArray = new JSONArray(); + + for (int i = 0; i < fieldsArray.length(); i++) { + JSONObject field = new JSONObject(); + JSONObject item = fieldsArray.getJSONObject(i); + + if (!item.has("name")) { + continue; // skip elements without name + } + + field.put("name", item.getString("name")); + + if (!item.has("source")) { + field.put("source", bindPath + "." + item.getString("name")); + } else { + field.put("source", bindPath + "." + item.getString("source")); + } + + subFormFieldsArray.put(i, field); + } + + return subFormFieldsArray; + } + + public JSONObject getFieldValuesForSubFormDefinition(JSONObject fieldsDefinition, String + relationalId, String entityId, JSONObject jsonObject, JSONObject overrides) throws + Exception { + + JSONArray fieldsArray = fieldsDefinition.getJSONArray("fields"); + + JSONObject fieldsValues = new JSONObject(); + + for (int i = 0; i < fieldsArray.length(); i++) { + JSONObject item = fieldsArray.getJSONObject(i); + + if (!item.has("name")) { + continue; // skip elements without name + } + + if (item.has("bind")) { + String pathSting = item.getString("bind"); + pathSting = pathSting.startsWith("/") ? pathSting.substring(1) : pathSting; + String[] path = pathSting.split("/"); + + //check if we need to override this val + if (overrides.has(item.getString("name"))) { + fieldsValues.put(item.getString("name"), + overrides.getString(item.getString("name"))); + } else { + String value = getValueForPath(path, jsonObject); + fieldsValues.put(item.getString("name"), value); + } + } + + //TODO: generate the id for the record + if (item.has("name") && item.getString("name").equalsIgnoreCase("id")) { + String id = entityId != null ? entityId : generateRandomUUIDString(); + fieldsValues.put(item.getString("name"), id); + } + + //TODO: generate the relational for the record + if (item.has("name") && item.getString("name").equalsIgnoreCase(relationalIdKey)) { + fieldsValues.put(item.getString("name"), relationalId); + } + + //TODO: generate the injectedBaseEntityIdKey for the record + if (item.has("name") && item.getString("name") + .equalsIgnoreCase(injectedBaseEntityIdKey)) { + fieldsValues.put(item.getString("name"), relationalId); + } + + populateRelationField(item, fieldsValues, relationalId); + } + return fieldsValues; + } + + private String getECClientRelationships() { + return AssetHandler.readFileFromAssetsFolder(ecClientRelationships, mContext); + } + + /** + * see if the current field is a client_relationship field, if so set it's value to the + * relationId since that's the parent base_entity_id + * + * @param fieldItem + * @param fieldsValues + * @param relationalId + */ + private void populateRelationField(JSONObject fieldItem, JSONObject fieldsValues, String + relationalId) { + try { + if (fieldItem.has("name")) { + String fieldName = fieldItem.getString("name"); + String relationships = getECClientRelationships(); + JSONArray jsonArray = new JSONArray(relationships); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject rObject = jsonArray.getJSONObject(i); + if (rObject.has("field") && rObject.getString("field") + .equalsIgnoreCase(fieldName)) { + fieldsValues.put(fieldName, relationalId); + } + } + } + + } catch (JSONException e) { + android.util.Log.e(TAG, e.toString(), e); + } + } + + public String getValueForPath(String[] path, JSONObject jsonObject) throws Exception { + JSONObject object = jsonObject; + String value = null; + int i = 0; + + while (i < path.length - 1) { + if (object.has(path[i])) { + Object o = object.get(path[i]); + if (o instanceof JSONObject) { + object = object.getJSONObject(path[i]); + } else if (o instanceof JSONArray) { + object = object.getJSONArray(path[i]).getJSONObject(0); + } + } + + i++; + } + Object valueObject = object.has(path[i]) ? object.get(path[i]) : null; + + if (valueObject == null) { + return value; + } + if (valueObject instanceof JSONObject && ((JSONObject) valueObject).has("content")) { + value = ((JSONObject) object.get(path[i])).getString("content"); + } else if (valueObject instanceof JSONArray) { + value = ((JSONArray) valueObject).get(0).toString(); + } else if (!(valueObject instanceof JSONObject)) { + value = valueObject.toString(); + } + + return value; + } + + private String readFileFromAssetsFolder(String fileName) { + String fileContents = null; + try { + InputStream is = mContext.getAssets().open(fileName); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + fileContents = new String(buffer, "UTF-8"); + } catch (IOException ex) { + android.util.Log.e(TAG, ex.toString(), ex); + + return null; + } + + //Log.d("File", fileContents); + + return fileContents; + } + + public JSONObject getFormJson(String formIdentity) { + if (mContext != null) { + try { + InputStream inputStream = mContext.getApplicationContext().getAssets() + .open("json" + ".form/" + formIdentity + ".json"); + BufferedReader reader = new BufferedReader( + new InputStreamReader(inputStream, "UTF-8")); + String jsonString; + StringBuilder stringBuilder = new StringBuilder(); + + while ((jsonString = reader.readLine()) != null) { + stringBuilder.append(jsonString); + } + inputStream.close(); + + return new JSONObject(stringBuilder.toString()); + } catch (IOException | JSONException e) { + e.printStackTrace(); + } + } + + return null; + } + + private void createNewEventDocument(org.smartregister.cloudant.models.Event event) { + mCloudantDataHandler.createEventDocument(event); + } + + private void createNewClientDocument(org.smartregister.cloudant.models.Client client) { + mCloudantDataHandler.createClientDocument(client); + } +} \ No newline at end of file