Skip to content

Commit

Permalink
Introduce some new abstractions to reduce duplicate code
Browse files Browse the repository at this point in the history
  • Loading branch information
GenieTim committed Sep 29, 2021
1 parent dde752b commit 7a060a6
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 99 deletions.
128 changes: 93 additions & 35 deletions src/main/java/edu/harvard/mcz/imagecapture/data/NahimaManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,48 @@ public JSONObject resolveStringSearchToOne(String search, String objectType) thr
return resolveStringSearchToOne(search, objectType, false);
}

/**
* Search an object in Nahima by a string. Create it if it is not found.
*
* @param name the string to search by
* @param objectType the type of object to search
* @param mask the mask of the field (the mapping), required for creation
* @param inner the object with the properties intended for the newly created object
* @return the matching or new object
*/
public JSONObject resolveOrCreate(String name, String objectType, String mask, JSONObject inner) throws IOException, InterruptedException {
JSONObject results = this.resolveStringSearchToOne(name, objectType);
// TODO: select correct
if (results == null) {
// create
JSONObject toCreate = wrapForCreation(inner, objectType, mask);
this.createObjectInNahima(toCreate, objectType);
// TODO: it would be simpler to store the created in cache. But well... current format does not allow
return this.resolveStringSearchToOne(name, objectType, true);
}
return results;
}

/**
* Search an object in Nahima which has a "name" and a "bemerkung" field.
* Create if not existing with Bemerkung = "Created by DataShot {version}"
*
* @param name the expected value of the "name" field
* @param objectType the "_objecttype"
* @param mask the mask of the field (the mapping)
* @return the object in Nahima
*/
public JSONObject resolveNameBemerkungObject(String name, String objectType, String mask) throws IOException, InterruptedException {
return this.resolveOrCreate(name, objectType, mask, new JSONObject(new HashMap<>() {{
put("name", name);
put("bemerkung", "Created by " + ImageCaptureApp.APP_NAME + " " + ImageCaptureApp.getAppVersion());
}}));
}

public JSONObject resolveNameBemerkungObject(String name, String objectType) throws IOException, InterruptedException {
return resolveNameBemerkungObject(name, objectType, objectType + "_all_fields");
}

/**
* Find a person in Nahima
*
Expand Down Expand Up @@ -325,26 +367,17 @@ public JSONObject resolveUnitFor(String unit, String unitSubject) throws IOExcep
* @return the nahima returned object if only one
*/
public JSONObject resolveOtherNrType(String nrType) throws IOException, InterruptedException {
JSONObject results = this.resolveStringSearchToOne(nrType, "id_typen");
// TODO: create / select correct
if (results == null && nrType != null) {
// TODO: create
JSONObject toCreate = new JSONObject(new HashMap<>() {{
put("_idx_in_objects", 0);
put("_mask", "id_typen_all_fields");
put("_objecttype", "id_typen");
put("id_typen", new JSONObject(new HashMap<>() {{
put("_id", JSONObject.NULL);
put("_version", 1);
put("name", nrType);
put("bemerkung", "Created by " + ImageCaptureApp.APP_NAME + " " + ImageCaptureApp.getAppVersion());
}}));
}});
this.createObjectInNahima(toCreate, "id_typen");
// TODO: it would be simpler to store the created in cache. But well... current format does not allow
return this.resolveStringSearchToOne(nrType, "id_typen", true);
}
return results;
return this.resolveNameBemerkungObject(nrType, "id_typen", "id_typen_all_fields");
}

/**
* Find the collection method in Nahima
*
* @param method the collection method to search for
* @return the nahima returned object if only one
*/
public JSONObject resolveCollectionMethod(String method) throws IOException, InterruptedException {
return resolveNameBemerkungObject(method, "sammlungsmethoden", "sammlungsmethoden__all_fields");
}

/**
Expand All @@ -367,6 +400,20 @@ public JSONObject resolveUnitForErrorRadius(String unit) throws IOException, Int
return resolveUnitFor(unit, "einheitenfuerdenfehlerradius");
}

/**
* Find a family in Nahima
*
* @param family
* @return
* @throws IOException
* @throws InterruptedException
*/
public JSONObject resolveFamily(String family) throws IOException, InterruptedException {
return resolveOrCreate(family, "familien", "familien_all_fields", new JSONObject(new HashMap<>() {{
put("familielat", family);
}}));
}

/**
* Find the location "Datum" in Nahima
*
Expand All @@ -386,21 +433,6 @@ public JSONObject resolveDatumFormat(String format) throws IOException, Interrup
}
}

/**
* Find the collection method in Nahima
*
* @param method the collection method to search for
* @return the nahima returned object if only one
*/
public JSONObject resolveCollectionMethod(String method) throws IOException, InterruptedException {
JSONObject results = this.resolveStringSearchToOne(method, "sammlungsmethoden");
// TODO: create / select correct
if (results == null) {
// TODO: create
}
return results;
}

/**
* This function translates a full-hydrated EasyDB object into an object that can be used to reference the
* fully-hydrated object when creating another entity
Expand Down Expand Up @@ -430,4 +462,30 @@ public JSONObject reduceAssociateForAssociation(JSONObject associate) {
reduced.put((String) associate.get("_objecttype"), child);
return reduced;
}

/**
* Add pool, id and version so the object can be created appropriately in Nahima
*
* @param inner the object to create
* @param objectType the "_objecttype" to create the object in
* @param mask the mask specifying the fields to use
* @return the wrapped object, ready to be created
*/
public JSONObject wrapForCreation(JSONObject inner, String objectType, String mask) {
inner.put("_pool", defaultPool);
inner.put("_id", JSONObject.NULL);
inner.put("_version", 1);

JSONObject result = new JSONObject(new HashMap<>() {{
put("_mask", mask);
put("_objecttype", objectType);
put("_idx_in_objects", 0);
}});
result.put(objectType, inner);
return result;
}

public JSONObject wrapForCreation(JSONObject inner, String objectType) {
return wrapForCreation(inner, objectType, objectType + "_all_fields");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class Specimen2JSONSerializer implements ToJSONSerializerInterface {
Expand Down Expand Up @@ -67,7 +68,7 @@ public JSONObject serialize2JSON(Object target) {
}
otherIds.put(dataShotId);
result.put("_nested:entomologie__andereids", otherIds);
//
// set determinations
JSONArray reverseNestedDeterminations = new JSONArray();
for (Determination det : toSerialize.getDeterminations()) {
Map<String, Object> reverseNestedCollectionMap = new HashMap<>() {{
Expand All @@ -89,37 +90,27 @@ public JSONObject serialize2JSON(Object target) {

JSONObject reverseNestedDetermination = new JSONObject(reverseNestedCollectionMap);

// resolutions
tryNonInteractiveResolve(reverseNestedDetermination, "familie", () -> nahimaManager.resolveFamily(toSerialize.getFamily()));

reverseNestedDetermination.put("_nested:bestimmung__kommentarezurbestimmung", new JSONArray(new String[]{toSerialize.getIdentificationRemarks()}));
reverseNestedDetermination.put("bestimmungsdatumtrans", det.getDateIdentified());
// try to parse and set the date correctly
try {
reverseNestedDetermination.put("bestimmungsdatum", this.dateToNahima(det.getDateIdentified()));
} catch (ParseException e) {
log.error("Failed to parse bestimmungsdatum", e);
}
tryNonInteractiveResolve(reverseNestedDetermination, "bestimmungsdatum", () -> this.dateToNahima(det.getDateIdentified()));

JSONArray identifierPersons = new JSONArray();
try {
JSONObject identifierPerson = nahimaManager.resolvePerson(det.getIdentifiedBy());
if (identifierPerson != null) {
identifierPersons.put(nahimaManager.reduceAssociateForAssociation(identifierPerson));
}
} catch (IOException | InterruptedException e) {
log.error("Failed to resolve person", e);
}
tryNonInteractiveResolve(identifierPersons, () -> nahimaManager.reduceAssociateForAssociation(nahimaManager.resolvePerson(det.getIdentifiedBy())), "person");
reverseNestedDetermination.put("_nested:bestimmung__bestimmer_person", identifierPersons);

try {
JSONObject resolvedTypeStatus = nahimaManager.resolveTypeStatus(det.getTypeStatus());
reverseNestedDetermination.put("typusstatus", resolvedTypeStatus);
} catch (IOException | InterruptedException e) {
log.error("Failed to resolve type status", e);
}
tryNonInteractiveResolve(reverseNestedDetermination, "typusstatus", () -> nahimaManager.resolveTypeStatus(det.getTypeStatus()));
// TODO: other fields

// finally,
reverseNestedDeterminations.put(reverseNestedDetermination);
}
// TODO: add specimen's own det


result.put("_reverse_nested:bestimmung:entomologie", reverseNestedDeterminations);

JSONArray reverseNestedCollections = new JSONArray();
Expand Down Expand Up @@ -150,52 +141,32 @@ public JSONObject serialize2JSON(Object target) {
}};
JSONObject reverseNestedCollection = new JSONObject(reverseNestedCollectionMap);

try {
reverseNestedCollection.put("sammelort", nahimaManager.reduceAssociateForAssociation(nahimaManager.resolveLocation(String.join(" ", toSerialize.getCountry(), toSerialize.getPrimaryDivison(), toSerialize.getSpecificLocality()))));
} catch (IOException | InterruptedException e) {
log.error("Failed to resolve location", e);
}

try {
reverseNestedCollection.put("einheitderhoehe", nahimaManager.reduceAssociateForAssociation(nahimaManager.resolveUnitForHeight(toSerialize.getElev_units())));
} catch (IOException | InterruptedException e) {
log.error("Failed to resolve unit", e);
}
tryNonInteractiveResolve(reverseNestedCollection, "sammelort", () -> nahimaManager.reduceAssociateForAssociation(
nahimaManager.resolveLocation(String.join(" ", toSerialize.getCountry(), toSerialize.getPrimaryDivison(), toSerialize.getSpecificLocality()))));
tryNonInteractiveResolve(reverseNestedCollection, "einheitderhoehe", () -> nahimaManager.reduceAssociateForAssociation(
nahimaManager.resolveUnitForHeight(toSerialize.getElev_units())));

if (georef != null) {
try {
reverseNestedCollection.put("einheitdesfehlerradius", nahimaManager.reduceAssociateForAssociation(nahimaManager.resolveUnitForErrorRadius(georef.getMaxErrorUnits())));
} catch (IOException | InterruptedException e) {
log.error("Failed to resolve unit", e);
}
tryNonInteractiveResolve(reverseNestedCollection, "einheitdesfehlerradius", () -> nahimaManager.reduceAssociateForAssociation(
nahimaManager.resolveUnitForErrorRadius(georef.getMaxErrorUnits()))
);
tryNonInteractiveResolve(reverseNestedCollection, "datumsformatgeodaeischeskooordinatensystem", () -> nahimaManager.reduceAssociateForAssociation(nahimaManager.resolveDatumFormat(georef.getDatum())));

try {
reverseNestedCollection.put("datumsformatgeodaeischeskooordinatensystem", nahimaManager.reduceAssociateForAssociation(nahimaManager.resolveDatumFormat(georef.getDatum())));
} catch (IOException | InterruptedException e) {
log.error("Failed to resolve date format", e);
}
}

try {
reverseNestedCollection.put("_nested:aufsammlung__sammelmethoden", new JSONArray(new JSONObject[]{
new JSONObject(new HashMap<>() {{
put("sammlungsmethode", nahimaManager.resolveCollectionMethod(toSerialize.getCollectingMethod()));
}})
}));
} catch (IOException | InterruptedException e) {
log.error("Failed to resolve collection method", e);
}
tryNonInteractiveResolve(reverseNestedCollection, "_nested:aufsammlung__sammelmethoden", () -> new JSONArray(new JSONObject[]{
new JSONObject(new HashMap<>() {{
put("sammlungsmethode", nahimaManager.resolveCollectionMethod(toSerialize.getCollectingMethod()));
}})
}));

JSONArray collectors = new JSONArray();
for (Collector collector : toSerialize.getCollectors()) {
try {
JSONObject person = nahimaManager.reduceAssociateForAssociation(nahimaManager.resolvePerson(collector.getCollectorName()));
collectors.put(new JSONObject(new HashMap<>() {{
put("sammler", person);
}}));
} catch (IOException | InterruptedException e) {
log.error("Failed to resolve date format", e);
}
tryNonInteractiveResolve(collectors, () -> new JSONObject(new HashMap<>() {{
put("sammler", nahimaManager.reduceAssociateForAssociation(
nahimaManager.resolvePerson(collector.getCollectorName()))
);
}}), "sammler");
}
reverseNestedCollection.put("_nested:aufsammlung__sammler", collectors);

Expand All @@ -212,12 +183,10 @@ public JSONObject serialize2JSON(Object target) {
log.error("Failed to parse datum " + toSerialize.getDateEmerged() + " " + toSerialize.getDateNos(), e);
}
reverseNestedCollection.put("indikator_fuer_kultur_zucht", toSerialize.getDateEmergedIndicator());
reverseNestedCollection.put("kultur_zucht", !Objects.equals(toSerialize.getDateEmerged(), "") && toSerialize.getDateEmerged() != null);
reverseNestedCollection.put("sammeldatumtrans", toSerialize.getDateCollected());
try {
reverseNestedCollection.put("sammeldatum", dateToNahima(toSerialize.getDateCollected()));
} catch (ParseException e) {
log.error("Failed to parse datum " + toSerialize.getDateCollected(), e);
}
tryNonInteractiveResolve(reverseNestedCollection, "sammeldatum", () -> dateToNahima(toSerialize.getDateCollected()));

// TODO: other fields

reverseNestedCollections.put(reverseNestedCollection);
Expand Down Expand Up @@ -265,14 +234,65 @@ protected JSONObject dateToNahima(Date date) {
return returnValue;
}

/**
* Wrap a value in a JSON object indicating the language (en-US) of the value
*
* @param value the JSON Object
*/
protected JSONObject wrapInLan(Object value) {
return wrapInLan(value, "en-US");
}

/**
* Wrap a value in a JSON object indicating the language of the value
*
* @param value the JSON Object
*/
protected JSONObject wrapInLan(Object value, String language) {
assert language != null;
JSONObject returnValue = new JSONObject();
returnValue.put("en-US", value);
returnValue.put(language, value);
return returnValue;
}

protected void tryNonInteractiveResolve(JSONObject target, String key, ResolverMethodInterface resolver) {
// try to parse and set correctly
try {
Object returnValue = resolver.doResolve();
if (returnValue != null) {
target.put(key, returnValue);
}
} catch (Exception e) {
log.error("Failed to resolve " + key, e);
}
}

protected void tryNonInteractiveResolve(JSONArray target, ResolverMethodInterface resolver, String debugHint) {
// try to parse and set correctly
try {
Object returnValue = resolver.doResolve();
if (returnValue != null) {
target.put(returnValue);
}
} catch (Exception e) {
log.error("Failed to resolve " + debugHint, e);
}
}

@Override
public boolean supportsSerializationOf(Object target) {
return target instanceof Specimen;
}

protected interface ResolverMethodInterface {
Object doResolve() throws Exception;
}

protected interface JSONObjectResolverMethodInterface extends ResolverMethodInterface {
JSONObject doResolve() throws Exception;
}

protected interface JSONArrayResolverMethodInterface extends ResolverMethodInterface {
JSONArray doResolve() throws Exception;
}
}

0 comments on commit 7a060a6

Please sign in to comment.