/*******************************************************************************
* Copyright 2014 Miami-Dade County
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.sharegov.cirm.utils;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
import mjson.Json;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.sharegov.cirm.OWL;
import org.sharegov.cirm.rest.UserService;
/**
* Utility methods for accessing activities in Json format.
*
* Some Methods access UserService (LDAP!) and or the Cirm OWL environment.
*
* @author Thomas Hilpold
*/
public class SRJsonActivityUtil
{
/**
* Returns a list of Activities.
*
* @param sr
* @return a list of activity json
*/
public static List<Json> getActivities(Json sr)
{
List<Json> result = null;
if(sr.has("hasServiceActivity"))
{
result = (sr.at("hasServiceActivity").isArray()) ? sr.at("hasServiceActivity").asJsonList() : Collections.singletonList(sr.at("hasServiceActivity"));
}
return result;
}
/**
* Returns the most recent activity (highest datetime).
* Most recent is defined as (Max(created datetime, updated datetime)).
*
* @param sr an SR Json (no prefixes)
* @return the max activity by date, null if the sr has no activities.
* @throws NullPointerException if one activity of given legacycode is found that has neither created nor updated date.
*/
public static Json getMostRecentActivity(Json sr)
{
Json result = null;
if(sr.has("hasServiceActivity"))
{
List<Json> activities = (sr.at("hasServiceActivity").isArray()) ? sr.at("hasServiceActivity").asJsonList() : Collections.singletonList(sr.at("hasServiceActivity"));
result = getMostRecentActivity(activities);
}
return result;
}
/**
* Returns the most recent activity among those matching legacyCode (highest datetime).
* Most recent is defined as (Max(created datetime, updated datetime)).
* If more than one activities have equal Max(created, updated), the one with the highest
* index in the Json activity list is returned.
*
* @param sr an SR Json (no prefixes)
* @param legacyCode
* @return the max activity by date, null if the sr has no activities.
* @throws NullPointerException if one activity of given legacycode is found that has neither created nor updated date.
*/
public static Json getMostRecentActivityByLegacyCode(Json sr, String legacyCode)
{
Json result = null;
if(legacyCode != null && sr.has("hasServiceActivity"))
{
List<Json> activities = (sr.at("hasServiceActivity").isArray()) ? sr.at("hasServiceActivity").asJsonList() : Collections.singletonList(sr.at("hasServiceActivity"));
//Look into dates of act with matching legacyCode
List<Json> activitiesOneLegacyCode = new LinkedList<Json>();
for (Json activity : activities)
{
String activityIRI;
if (activity.at("hasActivity").isString())
activityIRI = activity.at("hasActivity").asString();
else
activityIRI = activity.at("hasActivity").at("iri").asString();
OWLLiteral currentLegacyCode = OWL.dataProperty(OWL.individual(IRI.create(activityIRI)), "legacy:hasLegacyCode");
if(currentLegacyCode != null && currentLegacyCode.getLiteral().equals(legacyCode))
activitiesOneLegacyCode.add(activity);
}
result = getMostRecentActivity(activitiesOneLegacyCode);
}
return result;
}
/**
* Returns the most recent activity (highest dateTime).
* Most recent is defined as (Max(created datetime, updated datetime)).
* If more than one activities have equal Max(created, updated), the one with the highest
* index in the Json activity list is returned.
* @param activities a list of activity Json (no prefixes)
* @return an activity Json
* @throws NullPointerException if activities is null
*/
public static Json getMostRecentActivity(List<Json> activities)
{
if(activities.isEmpty())
return null;
else
return sortActivitiesByDateTime(activities).get(activities.size() - 1);
}
/**
* Sorts activities by Max(created datetime, updated datetime) ascending
*
* @param activities a list of activity Jsons (no prefixes)
* @return a new List containing all activities by Max(created, updated) in ascending order.
* @throws NullPointerException if an activity's max date could not be determined
*/
public static List<Json> sortActivitiesByDateTime(List<Json> activities)
{
if (activities.size() <= 1) return activities;
List<Json> result;
//1. determine max dateTime of hasDateCreated and hasUpdatedDate for each
TreeMap<Date, List<Json>> dateToActMap = new TreeMap<Date, List<Json>>();
for (Json act : activities)
{
Date d = getActivityMaxDateTime(act);
if (!dateToActMap.containsKey(d))
{
dateToActMap.put(d, new LinkedList<Json>(Collections.singletonList(act))); //will throw NPE if date null
}
else
{
dateToActMap.get(d).add(act);
}
}
result = new ArrayList<Json>(activities.size());
for (List<Json> l : dateToActMap.values())
{
result.addAll(l);
}
return result;
}
/**
* Determines Max(hasDateCreated, hasUpdatedDate) for an activity.
*
* @param activity a Json representation of an activity (no prefixes)
* @return the most recent activity's max(updated, created) date or null, if no date could be parsed.
* @throws NullPointerException is activity is null
* @throws IllegalStateException if activity has neither parseable created nor updated date.
*/
public static Date getActivityMaxDateTime(Json activity)
{
Date created = null;
Date updated = null;
Date max = null;
String createdStr = activity.at("hasDateCreated", "").asString();
String updatedStr = activity.at("hasUpdatedDate", "").asString();
try
{
if (!createdStr.isEmpty())
{
created = GenUtils.parseDate(createdStr);
}
} catch (Exception e)
{
System.err.println("could not parse date from hasDateCreated " + createdStr + " in activity: " + activity );
}
try
{
if (!updatedStr.isEmpty())
{
updated = GenUtils.parseDate(updatedStr);
}
} catch (Exception e)
{
System.err.println("could not parse date from hasUpdatedDate " + createdStr + " in activity: " + activity );
}
if (created == null && updated == null)
{
throw new IllegalStateException("Encountered activity with neither a parsable created nor updated date: " + activity.toString());
}
if (created == null) max = updated;
if (updated == null) max = created;
if (max == null)
{
max = created.after(updated)? created : updated;
}
return max;
}
/**
* Returns the type label of the activity's type.
* If no label property is contained in the activity Json due to earlier
* serialization, the label is retrieved from the ontology.
* @param activity a Json representation of an activity (no prefixes)
* @return
*/
public static String getActivityTypeLabel(Json activity)
{
String result;
if (activity.at("hasActivity", Json.object()).isObject())
{
Json label = activity.at("hasActivity", Json.object()).at("label", "");
result = (label.isString())? label.asString() : null;
}
else
{ //Serialized earlier, holds iri
String activityIri = activity.at("hasActivity").asString();
result = OWL.getEntityLabel(OWL.individual(activityIri));
}
return result;
}
/**
* Returns the label of the outcome.
*
* @param activity a Json representation of an activity (no prefixes)
* @return the label or an empty string, if no hasOutcome or hasOutcome has no label.
*/
public static String getHasOutcomeLabel(Json activity)
{
String result;
//TODO need to find first serialized outcome?
//still true if two or more activities of same type are in SR and
//IRIs were not fully resolved before calling this method.
Json outcome = activity.at("hasOutcome", Json.object());
if (outcome.isObject()) {
result = outcome.at("label", "").asString();
} else if (outcome.isString()) {
String outcomeStr = outcome.asString();
if (outcomeStr.contains("#")) {
result = outcomeStr.split("#")[1];
} else {
result = outcomeStr;
}
} else {
throw new RuntimeException("Misconfiguration of outcome detected in Activity: " + activity);
}
return result;
}
/**
* Returns the details of an activity.
* @param activity a Json representation of an activity (no prefixes)
* @return
*/
public static String getHasDetails(Json activity)
{
return activity.at("hasDetails", "").asString();
}
/**
* Returns the due date of the activity as formatted string.
*
* @param activity a Json representation of an activity (no prefixes)
* @return null, if the activity has no due date String.
*/
public static String getHasDueDate(Json activity, String dateFormatPattern)
{
Json dueDateJ = activity.at("hasDueDate", Json.nil());
if (dueDateJ.isNull()) return null;
Date dueDate = null;
try {
dueDate = GenUtils.parseDate(dueDateJ.asString());
} catch(Exception e) { System.err.println("ActivityResolver failed to parse date: " + dueDateJ.toString());
}
return dueDate != null? new SimpleDateFormat(dateFormatPattern).format(dueDate) : null;
}
/**
* Returns the created date of the activity.
* @param activity a Json representation of an activity (no prefixes)
* @return a DATE_PATTERN formatted date or null
*/
public static String getHasDateCreated(Json activity, String dateFormatPattern)
{
Json hasDateCreatedJ = activity.at("hasDateCreated", Json.nil());
if (hasDateCreatedJ.isNull()) return null;
Date hasDateCreated = null;
try {
hasDateCreated = GenUtils.parseDate(hasDateCreatedJ.asString());
} catch(Exception e) { System.err.println("ActivityResolver:getActivityDateTime failed to parse date: " + hasDateCreatedJ.toString());
}
return hasDateCreated != null? new SimpleDateFormat(dateFormatPattern).format(hasDateCreated) : null;
}
/**
* Returns the employee name of the creator of the activity.
* UserService is used.
*
* @param activity a Json representation of an activity (no prefixes)
* @return the employee name or null, if isCreatedBy is missing or isCreatedBy is not a string.
*/
public static String getIsCreatedByName(Json activity)
{
Json createdByENumJ = activity.at("isCreatedBy", Json.nil());
if (createdByENumJ.isNull()) return null;
if (createdByENumJ.isString())
return new UserService().getFullName(createdByENumJ.asString());
else
return null;
}
/**
* Calculates a due date 90 days after hasDateCreated.
*
* @param activity a Json representation of an activity (no prefixes)
* @return
*/
public static String getDueDate90Days(Json activity, String datePattern)
{
Json dateCreatedJ = activity.at("hasDateCreated", Json.nil());
if (dateCreatedJ.isNull()) return null;
Date dueDate = null;
try {
Date dateCreated = GenUtils.parseDate(dateCreatedJ.asString());
//add 90 days / See orig SQL
dueDate = new Date(dateCreated.getTime() + 90L * 24 * 60 * 60 * 1000);
} catch(Exception e) { System.err.println("ActivityResolver:getCustomDUEDATE_SWR failed to parse date: " + dateCreatedJ);
}
return dueDate != null? new SimpleDateFormat(datePattern).format(dueDate) : null;
}
/**
* Returns the name of the assigned staff of the activity by resolving an eKey or cKey.
* If the assigned string is not an eKey or cKey, the string itself is returned (e.g. an email address).
* For e or c keys, UserService is used.
* @param activity a Json representation of an activity (no prefixes)
* @return
*/
public static String getAssignedStaffName(Json activity)
{
Json createdByENumJ = activity.at("isAssignedTo", Json.object());
if (createdByENumJ.isString())
{
if (isUserEorCKey(createdByENumJ.asString()))
return new UserService().getFullName(createdByENumJ.asString());
else // Email address?
return createdByENumJ.asString();
}
else
return null;
}
/**
* Determines if the string is a c or e-key.
*
* @param s any string or null
* @return false, if s null, s empty, or s not an e or c key.
*/
public static boolean isUserEorCKey(String s)
{
return (s != null && !s.isEmpty() &&
(s.toUpperCase().startsWith("E") || s.toUpperCase().startsWith("C"))
&& Character.isDigit(s.charAt(s.length()-1)));
}
}