diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/mapofscience/MapOfScienceVisualizationRequestHandler.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/mapofscience/MapOfScienceVisualizationRequestHandler.java index 001482d4..0ffd5904 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/mapofscience/MapOfScienceVisualizationRequestHandler.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/mapofscience/MapOfScienceVisualizationRequestHandler.java @@ -9,9 +9,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.FutureTask; import com.hp.hpl.jena.query.QuerySolution; import com.hp.hpl.jena.query.ResultSet; @@ -20,6 +17,9 @@ import com.hp.hpl.jena.rdf.model.Resource; import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.CachingRDFServiceExecutor; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches; import mapping.ScienceMapping; import mapping.ScienceMappingResult; @@ -40,9 +40,7 @@ import edu.cornell.mannlib.vitro.webapp.visualization.constants.MapOfScienceCons import edu.cornell.mannlib.vitro.webapp.visualization.constants.VisConstants; import edu.cornell.mannlib.vitro.webapp.visualization.exceptions.MalformedQueryParametersException; import edu.cornell.mannlib.vitro.webapp.visualization.temporalgraph.OrganizationUtilityFunctions; -import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.Activity; import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.GenericQueryMap; -import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.MapOfScienceActivity; import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.json.MapOfScience; import edu.cornell.mannlib.vitro.webapp.visualization.visutils.UtilityFunctions; import edu.cornell.mannlib.vitro.webapp.visualization.visutils.VisualizationRequestHandler; @@ -93,14 +91,13 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq return prepareStandaloneMarkupResponse(vitroRequest, entityURI); } - private Map getSubjectPersonEntityAndGenerateDataResponse( VitroRequest vitroRequest, String subjectEntityURI, String entityLabel, VisConstants.DataVisMode dataOuputFormat) throws MalformedQueryParametersException { RDFService rdfService = vitroRequest.getRDFService(); - Map> personToPublicationMap = cachedPersonToPublication.get(rdfService); + Map> personToPublicationMap = VisualizationCaches.cachedPersonToPublication.get(rdfService); Map publicationToJournalMap = cachedPublicationToJournal.get(rdfService); if (!personToPublicationMap.containsKey(subjectEntityURI)) { @@ -163,25 +160,13 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq } } - private Set addOrgAndAllSubOrgs(Set allSubOrgs, String org, Map> subOrgMap) { - if (allSubOrgs.add(org)) { - if (subOrgMap.containsKey(org)) { - for (String subOrg : subOrgMap.get(org)) { - addOrgAndAllSubOrgs(allSubOrgs, subOrg, subOrgMap); - } - } - } - - return allSubOrgs; - } - private Map getSubjectEntityAndGenerateDataResponse( VitroRequest vitroRequest, String subjectEntityURI, String entityLabel, VisConstants.DataVisMode dataOuputFormat) throws MalformedQueryParametersException { RDFService rdfService = vitroRequest.getRDFService(); - Map orgLabelMap = cachedOrganizationLabels.get(rdfService); + Map orgLabelMap = VisualizationCaches.cachedOrganizationLabels.get(rdfService); if (orgLabelMap.get(subjectEntityURI) == null) { if (VisConstants.DataVisMode.JSON.equals(dataOuputFormat)) { @@ -191,9 +176,9 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq } } - Map> subOrgMap = cachedOrganizationSubOrgs.get(rdfService); - Map> organisationToPeopleMap = cachedOrganisationToPeopleMap.get(rdfService); - Map> personToPublicationMap = cachedPersonToPublication.get(rdfService); + Map> subOrgMap = VisualizationCaches.cachedOrganizationSubOrgs.get(rdfService); + Map> organisationToPeopleMap = VisualizationCaches.cachedOrganisationToPeopleMap.get(rdfService); + Map> personToPublicationMap = VisualizationCaches.cachedPersonToPublication.get(rdfService); Map publicationToJournalMap = cachedPublicationToJournal.get(rdfService); Set orgPublications = new HashSet(); @@ -201,44 +186,15 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq Map> subOrgPublicationsMap = new HashMap>(); - if (subOrgMap.containsKey(subjectEntityURI)) { - for (String topSubOrg : subOrgMap.get(subjectEntityURI)) { - Set subOrgPublications = new HashSet(); - Set subOrgPublicationsPeople = new HashSet(); - - Set fullSubOrgs = addOrgAndAllSubOrgs(new HashSet(), topSubOrg, subOrgMap); - - for (String subOrg : fullSubOrgs) { - Set peopleInSubOrg = organisationToPeopleMap.get(subOrg); - if (peopleInSubOrg != null) { - for (String person : peopleInSubOrg) { - if (personToPublicationMap.containsKey(person)) { - if (subOrgPublicationsPeople.add(person)) { - subOrgPublications.addAll(personToPublicationMap.get(person)); - - if (orgPublicationsPeople.add(person)) { - orgPublications.addAll(personToPublicationMap.get(person)); - } - } - } - } - } - } - - subOrgPublicationsMap.put(topSubOrg, subOrgPublications); - } - } - - Set people = organisationToPeopleMap.get(subjectEntityURI); - if (people != null) { - for (String person : people) { - if (personToPublicationMap.containsKey(person)) { - if (orgPublicationsPeople.add(person)) { - orgPublications.addAll(personToPublicationMap.get(person)); - } - } - } - } + OrgUtils.getObjectMappingsForOrgAnSubOrgs( + subjectEntityURI, + orgPublications, + orgPublicationsPeople, + subOrgPublicationsMap, + subOrgMap, + organisationToPeopleMap, + personToPublicationMap + ); if (orgPublications.isEmpty()) { if (VisConstants.DataVisMode.JSON.equals(dataOuputFormat)) { @@ -584,191 +540,11 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq return null; } - private static CachingRDFServiceExecutor> cachedOrganizationLabels = - new CachingRDFServiceExecutor<>( - new CachingRDFServiceExecutor.RDFServiceCallable>() { - @Override - Map callWithService(RDFService rdfService) throws Exception { - String query = QueryConstants.getSparqlPrefixQuery() + - "SELECT ?org ?orgLabel\n" + - "WHERE\n" + - "{\n" + - " ?org a foaf:Organization .\n" + - " ?org rdfs:label ?orgLabel .\n" + - "}\n"; - - Map map = new HashMap<>(); - - InputStream is = null; - ResultSet rs = null; - try { - is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); - rs = ResultSetFactory.fromJSON(is); - - while (rs.hasNext()) { - QuerySolution qs = rs.next(); - String org = qs.getResource("org").getURI(); - String orgLabel = qs.getLiteral("orgLabel").getString(); - - map.put(org, orgLabel); - } - } finally { - silentlyClose(is); - } - - return map; - } - } - ); - - private static CachingRDFServiceExecutor>> cachedOrganizationSubOrgs = - new CachingRDFServiceExecutor<>( - new CachingRDFServiceExecutor.RDFServiceCallable>>() { - @Override - Map> callWithService(RDFService rdfService) throws Exception { - String query = QueryConstants.getSparqlPrefixQuery() + - "SELECT ?org ?subOrg\n" + - "WHERE\n" + - "{\n" + - " ?org a foaf:Organization .\n" + - " ?org ?subOrg .\n" + - "}\n"; - - Map> map = new HashMap<>(); - - InputStream is = null; - ResultSet rs = null; - try { - is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); - rs = ResultSetFactory.fromJSON(is); - - while (rs.hasNext()) { - QuerySolution qs = rs.next(); - String org = qs.getResource("org").getURI(); - String subOrg = qs.getResource("subOrg").getURI(); - - Set subOrgs = map.get(org); - if (subOrgs == null) { - subOrgs = new HashSet(); - subOrgs.add(subOrg); - map.put(org, subOrgs); - } else { - subOrgs.add(subOrg); - } - } - } finally { - silentlyClose(is); - } - - return map; - } - } - ); - - private static CachingRDFServiceExecutor>> cachedOrganisationToPeopleMap = - new CachingRDFServiceExecutor>>( - new CachingRDFServiceExecutor.RDFServiceCallable>>() { - @Override - public Map> callWithService(RDFService rdfService) throws Exception { - String query = QueryConstants.getSparqlPrefixQuery() + - "SELECT ?organisation ?person\n" + - "WHERE\n" + - "{\n" + - " ?organisation a foaf:Organization .\n" + - " ?organisation core:relatedBy ?position .\n" + - " ?position core:relates ?person .\n" + - " ?person a foaf:Person .\n" + - "}\n"; - - // TODO Critical section? - - Map> orgToPeopleMap = new HashMap>(); - - InputStream is = null; - ResultSet rs = null; - try { - is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); - rs = ResultSetFactory.fromJSON(is); - - while (rs.hasNext()) { - QuerySolution qs = rs.next(); - String org = qs.getResource("organisation").getURI(); - String person = qs.getResource("person").getURI(); - - Set people = orgToPeopleMap.get(org); - if (people == null) { - people = new HashSet(); - people.add(person); - orgToPeopleMap.put(org, people); - } else { - people.add(person); - } - } - } finally { - silentlyClose(is); - } - - return orgToPeopleMap; - } - } - ); - - private static CachingRDFServiceExecutor>> cachedPersonToPublication = - new CachingRDFServiceExecutor>>( - new CachingRDFServiceExecutor.RDFServiceCallable>>() { - @Override - public Map> callWithService(RDFService rdfService) throws Exception { - String query = QueryConstants.getSparqlPrefixQuery() + - "SELECT ?person ?document\n" + - "WHERE\n" + - "{\n" + - " ?person a foaf:Person .\n" + - " ?person core:relatedBy ?authorship .\n" + - " ?authorship core:relates ?document .\n" + - " ?document a bibo:Document .\n" + - "}\n"; - - Map> map = new HashMap>(); - - InputStream is = null; - ResultSet rs = null; - try { - is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); - rs = ResultSetFactory.fromJSON(is); - - while (rs.hasNext()) { - QuerySolution qs = rs.next(); - - Resource person = qs.getResource("person"); - Resource document = qs.getResource("document"); - - if (person != null && document != null) { - String personURI = person.getURI(); - - Set documents = map.get(personURI); - if (documents == null) { - documents = new HashSet(); - documents.add(document.getURI()); - map.put(personURI, documents); - } else { - documents.add(document.getURI()); - } - } - } - } finally { - silentlyClose(is); - } - - return map; - } - } - ); - private static CachingRDFServiceExecutor> cachedPublicationToJournal = new CachingRDFServiceExecutor<>( new CachingRDFServiceExecutor.RDFServiceCallable>() { @Override - Map callWithService(RDFService rdfService) throws Exception { + protected Map callWithService(RDFService rdfService) throws Exception { String query = QueryConstants.getSparqlPrefixQuery() + "SELECT ?document ?journalLabel\n" + "WHERE\n" + @@ -830,107 +606,6 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq return total; } } - - private static class CachingRDFServiceExecutor { - private T cachedResults; - private long lastCacheTime; - - private RDFServiceCallable resultBuilder; - private FutureTask backgroundTask = null; - - CachingRDFServiceExecutor(RDFServiceCallable resultBuilder) { - this.resultBuilder = resultBuilder; - } - - synchronized T get(RDFService rdfService) { - if (cachedResults != null) { - if (!resultBuilder.invalidateCache(System.currentTimeMillis() - lastCacheTime)) { - return cachedResults; - } - } - - try { - if (backgroundTask == null) { - resultBuilder.setRDFService(rdfService); - backgroundTask = new FutureTask(resultBuilder); - - Thread thread = new Thread(backgroundTask); - thread.setDaemon(true); - thread.start(); - - if (cachedResults == null || resultBuilder.executionTime < 2000) { - completeBackgroundTask(); - } - } else if (backgroundTask.isDone()) { - completeBackgroundTask(); - } - } catch (InterruptedException e) { - abortBackgroundTask(); - } catch (ExecutionException e) { - abortBackgroundTask(); - throw new RuntimeException("Background RDF thread through an exception", e.getCause()); - } - - return cachedResults; - } - - private void abortBackgroundTask() { - if (backgroundTask != null) { - backgroundTask.cancel(true); - backgroundTask = null; - } - } - - private void completeBackgroundTask() throws InterruptedException, ExecutionException { - if (backgroundTask != null) { - cachedResults = backgroundTask.get(); - lastCacheTime = System.currentTimeMillis(); - backgroundTask = null; - } - } - static abstract class RDFServiceCallable implements Callable { - private RDFService rdfService; - private long executionTime = -1; - - final void setRDFService(RDFService rdfService) { - this.rdfService = rdfService; - } - - @Override - final public T call() throws Exception { - long start = System.currentTimeMillis(); - T val = callWithService(rdfService); - executionTime = System.currentTimeMillis() - start; - return val; - } - - abstract T callWithService(RDFService rdfService) throws Exception; - - boolean invalidateCache(long timeCached) { - if (executionTime > -1) { - /* - Determine validity as a function of the time it takes to execute the query. - - Query exec time | Keep cache for - -----------------+----------------- - 10 seconds | 20 minutes - 30 seconds | 1 hour - 1 minute | 2 hours - 5 minutes | 10 hours - - - Multiplier of the last execution time is 120. - - At most, keep a cache for one day (24 * 60 * 60 * 1000 = 86400000) - */ - - return timeCached > Math.min(executionTime * 120, 86400000); - } - return false; - } - } - } - private static void silentlyClose(InputStream is) { try { if (is != null) { diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalGrantVisualizationRequestHandler.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalGrantVisualizationRequestHandler.java index 5a02de66..11835447 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalGrantVisualizationRequestHandler.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalGrantVisualizationRequestHandler.java @@ -3,12 +3,18 @@ package edu.cornell.mannlib.vitro.webapp.visualization.temporalgraph; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.CounterUtils; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches; +import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.Individual; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -43,21 +49,17 @@ public class TemporalGrantVisualizationRequestHandler implements VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException { - String entityURI = vitroRequest - .getParameter(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY); - - return generateStandardVisualizationForGrantTemporalVis(vitroRequest, - log, dataset, entityURI); + String entityURI = vitroRequest.getParameter(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY); + return generateStandardVisualizationForGrantTemporalVis(vitroRequest, log, dataset, entityURI); } private ResponseValues generateStandardVisualizationForGrantTemporalVis( VitroRequest vitroRequest, Log log, Dataset dataset, String entityURI) throws MalformedQueryParametersException { + if (StringUtils.isBlank(entityURI)) { - - entityURI = OrganizationUtilityFunctions - .getStaffProvidedOrComputedHighestLevelOrganization( + entityURI = OrganizationUtilityFunctions.getStaffProvidedOrComputedHighestLevelOrganization( log, dataset, vitroRequest); @@ -73,19 +75,16 @@ public class TemporalGrantVisualizationRequestHandler implements Map parameters, VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException { - return generateStandardVisualizationForGrantTemporalVis( vitroRequest, log, dataset, parameters.get(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY)); } @Override - public Map generateDataVisualization( - VitroRequest vitroRequest, Log log, Dataset dataset) + public Map generateDataVisualization(VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException { - String entityURI = vitroRequest - .getParameter(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY); + String entityURI = vitroRequest.getParameter(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY); VisConstants.DataVisMode currentDataMode = VisConstants.DataVisMode.CSV; @@ -120,77 +119,146 @@ public class TemporalGrantVisualizationRequestHandler implements } private Map prepareDataErrorResponse() { - String outputFileName = "no-organization_grants-per-year.csv"; Map fileData = new HashMap(); - fileData.put(DataVisualizationController.FILE_NAME_KEY, - outputFileName); - fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, - "application/octet-stream"); + fileData.put(DataVisualizationController.FILE_NAME_KEY, outputFileName); + fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, "application/octet-stream"); fileData.put(DataVisualizationController.FILE_CONTENT_KEY, ""); return fileData; } @Override - public Object generateAjaxVisualization(VitroRequest vitroRequest, Log log, - Dataset dataset) throws MalformedQueryParametersException { + public Object generateAjaxVisualization(VitroRequest vitroRequest, Log log, Dataset dataset) + throws MalformedQueryParametersException { - throw new UnsupportedOperationException("Entity Grant Count " - + "does not provide Ajax response."); + throw new UnsupportedOperationException("Entity Grant Count does not provide Ajax response."); } private Map getSubjectEntityAndGenerateDataResponse( VitroRequest vitroRequest, Log log, Dataset dataset, String subjectEntityURI, VisConstants.DataVisMode visMode) throws MalformedQueryParametersException { - - Entity organizationEntity = SelectOnModelUtilities - .getSubjectOrganizationHierarchy(dataset, subjectEntityURI); - if (organizationEntity.getSubEntities() == null) { - + RDFService rdfService = vitroRequest.getRDFService(); + + Map orgLabelMap = VisualizationCaches.cachedOrganizationLabels.get(rdfService); + Map personLabelMap = VisualizationCaches.cachedPersonLabels.get(rdfService); + + if (orgLabelMap.get(subjectEntityURI) == null) { if (VisConstants.DataVisMode.JSON.equals(visMode)) { return prepareStandaloneDataErrorResponse(); } else { return prepareDataErrorResponse(); } - } - - Map grantURIForAssociatedPeopleToVO = new HashMap(); - Map allGrantURIToVO = new HashMap(); - - allGrantURIToVO = SelectOnModelUtilities.getGrantsForAllSubOrganizations(dataset, organizationEntity); - - Entity organizationWithAssociatedPeople = SelectOnModelUtilities - .getSubjectOrganizationAssociatedPeople(dataset, subjectEntityURI); - - if (organizationWithAssociatedPeople.getSubEntities() != null) { - - grantURIForAssociatedPeopleToVO = SelectOnModelUtilities - .getGrantsForAssociatedPeople(dataset, organizationWithAssociatedPeople.getSubEntities()); - - organizationEntity = OrganizationUtilityFunctions.mergeEntityIfShareSameURI( - organizationEntity, - organizationWithAssociatedPeople); } - - if (allGrantURIToVO.isEmpty() && grantURIForAssociatedPeopleToVO.isEmpty()) { - + + Map> subOrgMap = VisualizationCaches.cachedOrganizationSubOrgs.get(rdfService); + Map> organisationToPeopleMap = VisualizationCaches.cachedOrganisationToPeopleMap.get(rdfService); + Map orgMostSpecificLabelMap = VisualizationCaches.cachedOrganizationToMostSpecificLabel.get(rdfService); + Map personMostSpecificLabelMap = VisualizationCaches.cachedPersonToMostSpecificLabel.get(rdfService); + Map> personToGrantMap = VisualizationCaches.cachedPersonToGrant.get(rdfService); + Map grantToYearMap = VisualizationCaches.cachedGrantToYear.get(rdfService); + + Set orgGrants = new HashSet(); + Set orgGrantsPeople = new HashSet(); + + Map> subOrgPublicationsMap = new HashMap>(); + + OrgUtils.getObjectMappingsForOrgAnSubOrgs( + subjectEntityURI, + orgGrants, + orgGrantsPeople, + subOrgPublicationsMap, + subOrgMap, + organisationToPeopleMap, + personToGrantMap + ); + + if (orgGrants.isEmpty()) { if (VisConstants.DataVisMode.JSON.equals(visMode)) { return prepareStandaloneDataErrorResponse(); } else { return prepareDataErrorResponse(); } - - } else { - + } else { + + Map fileData = new HashMap(); if (VisConstants.DataVisMode.JSON.equals(visMode)) { - return prepareStandaloneDataResponse(vitroRequest, organizationEntity); + Gson json = new Gson(); + Set subEntitiesJson = new HashSet(); + + // For each suborganisation + for (String subOrg : subOrgPublicationsMap.keySet()) { + JsonObject entityJson = new JsonObject(orgLabelMap.get(subOrg)); + + List> yearPubCounts = CounterUtils.getObjectCountByYear(subOrgPublicationsMap.get(subOrg), grantToYearMap); + + String type = orgMostSpecificLabelMap.get(subOrg); + + entityJson.setYearToActivityCount(yearPubCounts); + entityJson.setOrganizationTypes(Arrays.asList(type == null ? "Organization" : type)); + entityJson.setEntityURI(subOrg); + entityJson.setVisMode("ORGANIZATION"); + + subEntitiesJson.add(entityJson); + } + + // For each person + for (String person : orgGrantsPeople) { + JsonObject entityJson = new JsonObject(personLabelMap.get(person)); + + List> yearPubCounts = CounterUtils.getObjectCountByYear(personToGrantMap.get(person), grantToYearMap); + + String type = personMostSpecificLabelMap.get(person); + + entityJson.setYearToActivityCount(yearPubCounts); + entityJson.setOrganizationTypes(Arrays.asList(type == null ? "Person" : type)); + entityJson.setEntityURI(person); + entityJson.setVisMode("PERSON"); + + subEntitiesJson.add(entityJson); + } + + SubjectEntityJSON subjectEntityJSON = new SubjectEntityJSON( + orgLabelMap.get(subjectEntityURI), + subjectEntityURI, + OrgUtils.getParentURIsToLabel(subjectEntityURI, subOrgMap, orgLabelMap)); + + subEntitiesJson.add(subjectEntityJSON); + + fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, "application/octet-stream"); + fileData.put(DataVisualizationController.FILE_CONTENT_KEY, json.toJson(subEntitiesJson)); + } else { - return prepareDataResponse(organizationEntity); + String entityLabel = orgLabelMap.get(subjectEntityURI); + if (StringUtils.isBlank(entityLabel)) { + entityLabel = "no-organization"; + } + + StringBuilder csvFileContent = new StringBuilder(); + + csvFileContent.append("Entity Name, Grant Count, Entity Type\n"); + + for (String subOrg : subOrgPublicationsMap.keySet()) { + csvFileContent.append(StringEscapeUtils.escapeCsv(orgLabelMap.get(subOrg))); + csvFileContent.append(", "); + + csvFileContent.append(subOrgPublicationsMap.get(subOrg).size()); + csvFileContent.append(", "); + + csvFileContent.append("Organization"); + csvFileContent.append("\n"); + + } + + String outputFileName = UtilityFunctions.slugify(entityLabel) + "_grants-per-year" + ".csv"; + fileData.put(DataVisualizationController.FILE_NAME_KEY, outputFileName); + fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, "application/octet-stream"); + fileData.put(DataVisualizationController.FILE_CONTENT_KEY, csvFileContent.toString()); } + return fileData; } } @@ -205,56 +273,6 @@ public class TemporalGrantVisualizationRequestHandler implements return fileData; } - - private Map prepareStandaloneDataResponse( - VitroRequest vitroRequest, - Entity entity) { - - Map fileData = new HashMap(); - - fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, - "application/octet-stream"); - fileData.put(DataVisualizationController.FILE_CONTENT_KEY, - writeGrantsOverTimeJSON(vitroRequest, - entity)); - return fileData; - } - - /** - * Provides response when json file containing the grant count over the - * years is requested. - * - * @param entity - * @param subentities - * @param subOrganizationTypesResult - */ - private Map prepareDataResponse(Entity entity) { - - String entityLabel = entity.getEntityLabel(); - - /* - * To make sure that null/empty records for entity names do not cause any mischief. - * */ - if (StringUtils.isBlank(entityLabel)) { - entityLabel = "no-organization"; - } - - String outputFileName = UtilityFunctions.slugify(entityLabel) - + "_grants-per-year" + ".csv"; - - - Map fileData = new HashMap(); - - fileData.put(DataVisualizationController.FILE_NAME_KEY, - outputFileName); - fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, - "application/octet-stream"); - fileData.put(DataVisualizationController.FILE_CONTENT_KEY, - getEntityGrantsPerYearCSVContent(entity)); - return fileData; - } - - private TemplateResponseValues prepareStandaloneMarkupResponse(VitroRequest vreq, String entityURI) { @@ -273,90 +291,6 @@ public class TemporalGrantVisualizationRequestHandler implements return new TemplateResponseValues(standaloneTemplate, body); } - /** - * Function to generate a json file for year <-> grant count mapping. - * @param vreq - * @param subentities - * @param subOrganizationTypesResult - */ - private String writeGrantsOverTimeJSON(VitroRequest vreq, - Entity subjectEntity) { - - Gson json = new Gson(); - Set jsonifiedResponse = new HashSet(); - - for (SubEntity subentity : subjectEntity.getSubEntities()) { - JsonObject entityJson = new JsonObject( - subentity.getIndividualLabel()); - - List> yearGrantCount = new ArrayList>(); - - for (Map.Entry grantEntry : UtilityFunctions - .getYearToActivityCount(subentity.getActivities()) - .entrySet()) { - - List currentGrantYear = new ArrayList(); - if (grantEntry.getKey().equals( - VOConstants.DEFAULT_GRANT_YEAR)) { - currentGrantYear.add(-1); - } else { - currentGrantYear.add(Integer.parseInt(grantEntry.getKey())); - } - - currentGrantYear.add(grantEntry.getValue()); - yearGrantCount.add(currentGrantYear); - } - - entityJson.setYearToActivityCount(yearGrantCount); - - entityJson.setOrganizationTypes(subentity.getEntityTypeLabels()); - - entityJson.setEntityURI(subentity.getIndividualURI()); - - entityJson.setLastCachedAtDateTime(subentity.getLastCachedAtDateTime()); - - if (subentity.getEntityClass().equals(VOConstants.EntityClassType.PERSON)) { - entityJson.setVisMode("PERSON"); - } else if (subentity.getEntityClass().equals(VOConstants.EntityClassType.ORGANIZATION)) { - entityJson.setVisMode("ORGANIZATION"); - } - - jsonifiedResponse.add(entityJson); - } - - - SubjectEntityJSON subjectEntityJSON = new SubjectEntityJSON(subjectEntity.getEntityLabel(), - subjectEntity.getEntityURI(), - subjectEntity.getParents()); - - jsonifiedResponse.add(subjectEntityJSON); - - return json.toJson(jsonifiedResponse); - } - - private String getEntityGrantsPerYearCSVContent(Entity entity) { - - StringBuilder csvFileContent = new StringBuilder(); - - csvFileContent.append("Entity Name, Grant Count, Entity Type\n"); - - for (SubEntity subEntity : entity.getSubEntities()) { - - csvFileContent.append(StringEscapeUtils.escapeCsv(subEntity.getIndividualLabel())); - csvFileContent.append(", "); - csvFileContent.append(subEntity.getActivities().size()); - csvFileContent.append(", "); - - String allTypes = StringUtils.join(subEntity.getEntityTypeLabels(), "; "); - - csvFileContent.append(StringEscapeUtils.escapeCsv(allTypes)); - csvFileContent.append("\n"); - } - - return csvFileContent.toString(); - } - - @Override public AuthorizationRequest getRequiredPrivileges() { return null; diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalPublicationVisualizationRequestHandler.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalPublicationVisualizationRequestHandler.java index cd517476..bb305fe5 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalPublicationVisualizationRequestHandler.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalPublicationVisualizationRequestHandler.java @@ -2,13 +2,17 @@ package edu.cornell.mannlib.vitro.webapp.visualization.temporalgraph; -import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.CounterUtils; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -22,15 +26,10 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.Res import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.visualization.DataVisualizationController; import edu.cornell.mannlib.vitro.webapp.controller.visualization.VisualizationFrameworkConstants; -import edu.cornell.mannlib.vitro.webapp.visualization.constants.VOConstants; import edu.cornell.mannlib.vitro.webapp.visualization.constants.VisConstants; import edu.cornell.mannlib.vitro.webapp.visualization.exceptions.MalformedQueryParametersException; -import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.Activity; -import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.Entity; -import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.SubEntity; import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.json.JsonObject; import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.json.SubjectEntityJSON; -import edu.cornell.mannlib.vitro.webapp.visualization.visutils.SelectOnModelUtilities; import edu.cornell.mannlib.vitro.webapp.visualization.visutils.UtilityFunctions; import edu.cornell.mannlib.vitro.webapp.visualization.visutils.VisualizationRequestHandler; @@ -38,16 +37,11 @@ public class TemporalPublicationVisualizationRequestHandler implements VisualizationRequestHandler { @Override - public ResponseValues generateStandardVisualization( - VitroRequest vitroRequest, Log log, Dataset dataset) + public ResponseValues generateStandardVisualization(VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException { - - String entityURI = vitroRequest - .getParameter(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY); - - return generateStandardVisualizationForPublicationTemporalVis( - vitroRequest, log, dataset, entityURI); + String entityURI = vitroRequest.getParameter(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY); + return generateStandardVisualizationForPublicationTemporalVis(vitroRequest, log, dataset, entityURI); } @Override @@ -55,10 +49,8 @@ public class TemporalPublicationVisualizationRequestHandler implements Map parameters, VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException { - return generateStandardVisualizationForPublicationTemporalVis( vitroRequest, log, dataset, parameters.get(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY)); - } private ResponseValues generateStandardVisualizationForPublicationTemporalVis( @@ -66,13 +58,11 @@ public class TemporalPublicationVisualizationRequestHandler implements String entityURI) throws MalformedQueryParametersException { if (StringUtils.isBlank(entityURI)) { - entityURI = OrganizationUtilityFunctions .getStaffProvidedOrComputedHighestLevelOrganization( log, dataset, vitroRequest); - } @@ -83,121 +73,150 @@ public class TemporalPublicationVisualizationRequestHandler implements VitroRequest vitroRequest, Log log, Dataset dataset, String subjectEntityURI, VisConstants.DataVisMode visMode) throws MalformedQueryParametersException { - - Entity organizationEntity = SelectOnModelUtilities - .getSubjectOrganizationHierarchy(dataset, subjectEntityURI); - - if (organizationEntity.getSubEntities() == null) { - + + RDFService rdfService = vitroRequest.getRDFService(); + + Map orgLabelMap = VisualizationCaches.cachedOrganizationLabels.get(rdfService); + Map personLabelMap = VisualizationCaches.cachedPersonLabels.get(rdfService); + + if (orgLabelMap.get(subjectEntityURI) == null) { if (VisConstants.DataVisMode.JSON.equals(visMode)) { return prepareStandaloneDataErrorResponse(); } else { return prepareDataErrorResponse(); } } - - Map documentURIForAssociatedPeopleTOVO = new HashMap(); - Map allDocumentURIToVOs = new HashMap(); - - allDocumentURIToVOs = SelectOnModelUtilities.getPublicationsForAllSubOrganizations(dataset, organizationEntity); - - Entity organizationWithAssociatedPeople = SelectOnModelUtilities - .getSubjectOrganizationAssociatedPeople(dataset, subjectEntityURI); - - if (organizationWithAssociatedPeople.getSubEntities() != null) { - - documentURIForAssociatedPeopleTOVO = SelectOnModelUtilities - .getPublicationsForAssociatedPeople(dataset, organizationWithAssociatedPeople.getSubEntities()); - - organizationEntity = OrganizationUtilityFunctions.mergeEntityIfShareSameURI( - organizationEntity, - organizationWithAssociatedPeople); - } - - if (allDocumentURIToVOs.isEmpty() && documentURIForAssociatedPeopleTOVO.isEmpty()) { - + + Map> subOrgMap = VisualizationCaches.cachedOrganizationSubOrgs.get(rdfService); + Map orgMostSpecificLabelMap = VisualizationCaches.cachedOrganizationToMostSpecificLabel.get(rdfService); + Map personMostSpecificLabelMap = VisualizationCaches.cachedPersonToMostSpecificLabel.get(rdfService); + Map> organisationToPeopleMap = VisualizationCaches.cachedOrganisationToPeopleMap.get(rdfService); + Map> personToPublicationMap = VisualizationCaches.cachedPersonToPublication.get(rdfService); + Map publicationToYearMap = VisualizationCaches.cachedPublicationToYear.get(rdfService); + + Set orgPublications = new HashSet(); + Set orgPublicationsPeople = new HashSet(); + + Map> subOrgPublicationsMap = new HashMap>(); + + OrgUtils.getObjectMappingsForOrgAnSubOrgs( + subjectEntityURI, + orgPublications, + orgPublicationsPeople, + subOrgPublicationsMap, + subOrgMap, + organisationToPeopleMap, + personToPublicationMap + ); + + if (orgPublications.isEmpty()) { if (VisConstants.DataVisMode.JSON.equals(visMode)) { return prepareStandaloneDataErrorResponse(); } else { return prepareDataErrorResponse(); } - - } else { - + } else { + + Map fileData = new HashMap(); if (VisConstants.DataVisMode.JSON.equals(visMode)) { - return prepareStandaloneDataResponse(vitroRequest, organizationEntity); + Gson json = new Gson(); + Set subEntitiesJson = new HashSet(); + + // For each suborganisation + for (String subOrg : subOrgPublicationsMap.keySet()) { + JsonObject entityJson = new JsonObject(orgLabelMap.get(subOrg)); + + List> yearPubCounts = CounterUtils.getObjectCountByYear(subOrgPublicationsMap.get(subOrg), publicationToYearMap); + + String type = orgMostSpecificLabelMap.get(subOrg); + + entityJson.setYearToActivityCount(yearPubCounts); + entityJson.setOrganizationTypes(Arrays.asList(type == null ? "Organization" : type)); + entityJson.setEntityURI(subOrg); + entityJson.setVisMode("ORGANIZATION"); + + subEntitiesJson.add(entityJson); + } + + // For each person + for (String person : orgPublicationsPeople) { + JsonObject entityJson = new JsonObject(personLabelMap.get(person)); + + List> yearPubCounts = CounterUtils.getObjectCountByYear(personToPublicationMap.get(person), publicationToYearMap); + + String type = personMostSpecificLabelMap.get(person); + + entityJson.setYearToActivityCount(yearPubCounts); + entityJson.setOrganizationTypes(Arrays.asList(type == null ? "Person" : type)); + entityJson.setEntityURI(person); + entityJson.setVisMode("PERSON"); + + subEntitiesJson.add(entityJson); + } + + SubjectEntityJSON subjectEntityJSON = new SubjectEntityJSON( + orgLabelMap.get(subjectEntityURI), + subjectEntityURI, + OrgUtils.getParentURIsToLabel(subjectEntityURI, subOrgMap, orgLabelMap)); + + subEntitiesJson.add(subjectEntityJSON); + + fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, "application/octet-stream"); + fileData.put(DataVisualizationController.FILE_CONTENT_KEY, json.toJson(subEntitiesJson)); + } else { - return prepareDataResponse(organizationEntity); + String entityLabel = orgLabelMap.get(subjectEntityURI); + if (StringUtils.isBlank(entityLabel)) { + entityLabel = "no-organization"; + } + + StringBuilder csvFileContent = new StringBuilder(); + + csvFileContent.append("Entity Name, Publication Count, Entity Type\n"); + + for (String subOrg : subOrgPublicationsMap.keySet()) { + csvFileContent.append(StringEscapeUtils.escapeCsv(orgLabelMap.get(subOrg))); + csvFileContent.append(", "); + + csvFileContent.append(subOrgPublicationsMap.get(subOrg).size()); + csvFileContent.append(", "); + + csvFileContent.append("Organization"); + csvFileContent.append("\n"); + + } + + String outputFileName = UtilityFunctions.slugify(entityLabel) + "_publications-per-year" + ".csv"; + fileData.put(DataVisualizationController.FILE_NAME_KEY, outputFileName); + fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, "application/octet-stream"); + fileData.put(DataVisualizationController.FILE_CONTENT_KEY, csvFileContent.toString()); } + return fileData; } } - - /** - * Provides response when json file containing the publication count over the - * years is requested. - * - * @param entity - * @param subentities - * @param subOrganizationTypesResult - */ - private Map prepareDataResponse(Entity entity) { - String entityLabel = entity.getEntityLabel(); - - /* - * To make sure that null/empty records for entity names do not cause any mischief. - * */ - if (StringUtils.isBlank(entityLabel)) { - entityLabel = "no-organization"; - } - - String outputFileName = UtilityFunctions.slugify(entityLabel) - + "_publications-per-year" + ".csv"; - - - Map fileData = new HashMap(); - - fileData.put(DataVisualizationController.FILE_NAME_KEY, - outputFileName); - fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, - "application/octet-stream"); - fileData.put(DataVisualizationController.FILE_CONTENT_KEY, - getEntityPublicationsPerYearCSVContent(entity)); - return fileData; - } - private Map prepareDataErrorResponse() { - String outputFileName = "no-organization_publications-per-year.csv"; Map fileData = new HashMap(); - - fileData.put(DataVisualizationController.FILE_NAME_KEY, - outputFileName); - fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, - "application/octet-stream"); - fileData.put(DataVisualizationController.FILE_CONTENT_KEY, ""); + fileData.put(DataVisualizationController.FILE_NAME_KEY, outputFileName); + fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, "application/octet-stream"); + fileData.put(DataVisualizationController.FILE_CONTENT_KEY, ""); return fileData; } private Map prepareStandaloneDataErrorResponse() { - Map fileData = new HashMap(); - - fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, - "application/octet-stream"); - fileData.put(DataVisualizationController.FILE_CONTENT_KEY, - "{\"error\" : \"No Publications for this Organization found in VIVO.\"}"); + fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, "application/octet-stream"); + fileData.put(DataVisualizationController.FILE_CONTENT_KEY, "{\"error\" : \"No Publications for this Organization found in VIVO.\"}"); return fileData; } @Override - public Map generateDataVisualization( - VitroRequest vitroRequest, Log log, Dataset dataset) + public Map generateDataVisualization(VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException { - String entityURI = vitroRequest - .getParameter(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY); + String entityURI = vitroRequest.getParameter(VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY); VisConstants.DataVisMode currentDataMode = VisConstants.DataVisMode.CSV; @@ -233,33 +252,13 @@ public class TemporalPublicationVisualizationRequestHandler implements @Override - public Object generateAjaxVisualization(VitroRequest vitroRequest, Log log, - Dataset dataset) throws MalformedQueryParametersException { + public Object generateAjaxVisualization(VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException { throw new UnsupportedOperationException("Entity Pub Count does not provide Ajax Response."); } - private Map prepareStandaloneDataResponse( - VitroRequest vitroRequest, - Entity entity) { - - Map fileData = new HashMap(); - - fileData.put(DataVisualizationController.FILE_CONTENT_TYPE_KEY, - "application/octet-stream"); - fileData.put(DataVisualizationController.FILE_CONTENT_KEY, - writePublicationsOverTimeJSON(vitroRequest, - entity)); - return fileData; - } - - private TemplateResponseValues prepareStandaloneMarkupResponse(VitroRequest vreq, - String entityURI) { - + private TemplateResponseValues prepareStandaloneMarkupResponse(VitroRequest vreq, String entityURI) { String standaloneTemplate = "entityComparisonOnPublicationsStandalone.ftl"; - - String organizationLabel = OrganizationUtilityFunctions - .getEntityLabelFromDAO(vreq, - entityURI); + String organizationLabel = OrganizationUtilityFunctions.getEntityLabelFromDAO(vreq, entityURI); Map body = new HashMap(); body.put("title", organizationLabel + " - Temporal Graph Visualization"); @@ -271,91 +270,9 @@ public class TemporalPublicationVisualizationRequestHandler implements return new TemplateResponseValues(standaloneTemplate, body); } - /** - * Function to generate a json file for year <-> publication count mapping. - * @param vreq - * @param subentities - * @param subOrganizationTypesResult - */ - private String writePublicationsOverTimeJSON(VitroRequest vreq, - Entity subjectEntity) { - - Gson json = new Gson(); - Set subEntitiesJson = new HashSet(); - - for (SubEntity subentity : subjectEntity.getSubEntities()) { - - JsonObject entityJson = new JsonObject( - subentity.getIndividualLabel()); - - List> yearPubCount = new ArrayList>(); - - for (Map.Entry pubEntry : UtilityFunctions - .getYearToActivityCount(subentity.getActivities()) - .entrySet()) { - - List currentPubYear = new ArrayList(); - if (pubEntry.getKey().equals(VOConstants.DEFAULT_PUBLICATION_YEAR)) { - currentPubYear.add(-1); - } else { - currentPubYear.add(Integer.parseInt(pubEntry.getKey())); - } - - currentPubYear.add(pubEntry.getValue()); - yearPubCount.add(currentPubYear); - } - - entityJson.setYearToActivityCount(yearPubCount); - - entityJson.setOrganizationTypes(subentity.getEntityTypeLabels()); - - entityJson.setEntityURI(subentity.getIndividualURI()); - - entityJson.setLastCachedAtDateTime(subentity.getLastCachedAtDateTime()); - - if (subentity.getEntityClass().equals(VOConstants.EntityClassType.PERSON)) { - entityJson.setVisMode("PERSON"); - } else if (subentity.getEntityClass().equals(VOConstants.EntityClassType.ORGANIZATION)) { - entityJson.setVisMode("ORGANIZATION"); - } - - subEntitiesJson.add(entityJson); - } - - SubjectEntityJSON subjectEntityJSON = new SubjectEntityJSON(subjectEntity.getEntityLabel(), - subjectEntity.getEntityURI(), - subjectEntity.getParents()); - - subEntitiesJson.add(subjectEntityJSON); - - return json.toJson(subEntitiesJson); - } - - private String getEntityPublicationsPerYearCSVContent(Entity entity) { - - StringBuilder csvFileContent = new StringBuilder(); - - csvFileContent.append("Entity Name, Publication Count, Entity Type\n"); - - for (SubEntity subEntity : entity.getSubEntities()) { - - csvFileContent.append(StringEscapeUtils.escapeCsv(subEntity.getIndividualLabel())); - csvFileContent.append(", "); - csvFileContent.append(subEntity.getActivities().size()); - csvFileContent.append(", "); - - String allTypes = StringUtils.join(subEntity.getEntityTypeLabels(), "; "); - - csvFileContent.append(StringEscapeUtils.escapeCsv(allTypes)); - csvFileContent.append("\n"); - } - return csvFileContent.toString(); - } - @Override public AuthorizationRequest getRequiredPrivileges() { // TODO Auto-generated method stub return null; } - -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CachingRDFServiceExecutor.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CachingRDFServiceExecutor.java new file mode 100644 index 00000000..27d4a6d8 --- /dev/null +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CachingRDFServiceExecutor.java @@ -0,0 +1,108 @@ +package edu.cornell.mannlib.vitro.webapp.visualization.utilities; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + +public class CachingRDFServiceExecutor { + private T cachedResults; + private long lastCacheTime; + + private RDFServiceCallable resultBuilder; + private FutureTask backgroundTask = null; + + public CachingRDFServiceExecutor(RDFServiceCallable resultBuilder) { + this.resultBuilder = resultBuilder; + } + + public synchronized T get(RDFService rdfService) { + if (cachedResults != null) { + if (!resultBuilder.invalidateCache(System.currentTimeMillis() - lastCacheTime)) { + return cachedResults; + } + } + + try { + if (backgroundTask == null) { + resultBuilder.setRDFService(rdfService); + backgroundTask = new FutureTask(resultBuilder); + + Thread thread = new Thread(backgroundTask); + thread.setDaemon(true); + thread.start(); + + if (cachedResults == null || resultBuilder.executionTime < 2000) { + completeBackgroundTask(); + } + } else if (backgroundTask.isDone()) { + completeBackgroundTask(); + } + } catch (InterruptedException e) { + abortBackgroundTask(); + } catch (ExecutionException e) { + abortBackgroundTask(); + throw new RuntimeException("Background RDF thread through an exception", e.getCause()); + } + + return cachedResults; + } + + private void abortBackgroundTask() { + if (backgroundTask != null) { + backgroundTask.cancel(true); + backgroundTask = null; + } + } + + private void completeBackgroundTask() throws InterruptedException, ExecutionException { + if (backgroundTask != null) { + cachedResults = backgroundTask.get(); + lastCacheTime = System.currentTimeMillis(); + backgroundTask = null; + } + } + + public static abstract class RDFServiceCallable implements Callable { + private RDFService rdfService; + private long executionTime = -1; + + final void setRDFService(RDFService rdfService) { + this.rdfService = rdfService; + } + + @Override + final public T call() throws Exception { + long start = System.currentTimeMillis(); + T val = callWithService(rdfService); + executionTime = System.currentTimeMillis() - start; + return val; + } + + protected abstract T callWithService(RDFService rdfService) throws Exception; + + boolean invalidateCache(long timeCached) { + if (executionTime > -1) { + /* + Determine validity as a function of the time it takes to execute the query. + + Query exec time | Keep cache for + -----------------+----------------- + 10 seconds | 20 minutes + 30 seconds | 1 hour + 1 minute | 2 hours + 5 minutes | 10 hours + + + Multiplier of the last execution time is 120. + + At most, keep a cache for one day (24 * 60 * 60 * 1000 = 86400000) + */ + + return timeCached > Math.min(executionTime * 120, 86400000); + } + return false; + } + } +} diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CounterUtils.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CounterUtils.java new file mode 100644 index 00000000..682f9fff --- /dev/null +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CounterUtils.java @@ -0,0 +1,47 @@ +package edu.cornell.mannlib.vitro.webapp.visualization.utilities; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class CounterUtils { + public static final List> getObjectCountByYear(Set objects, Map objectToYearMap) { + List> yearCounts = new ArrayList<>(); + if (objects != null) { + int[] counts = new int[Calendar.getInstance().get(Calendar.YEAR) + 1000]; + for (String publication : objects) { + int year = 0; + try { + year = Integer.parseInt(objectToYearMap.get(publication), 10); + } catch (Throwable t) { + } + + if (year > counts.length - 1) { + year = 0; + } + + counts[year]++; + } + + for (int i = 1; i < counts.length; i++) { + if (counts[i] > 0) { + List currentYear = new ArrayList(); + currentYear.add(i); + currentYear.add(counts[i]); + yearCounts.add(currentYear); + } + } + + if (counts[0] > 0) { + List currentYear = new ArrayList(); + currentYear.add(-1); + currentYear.add(counts[0]); + yearCounts.add(currentYear); + } + } + + return yearCounts; + } +} diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/OrgUtils.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/OrgUtils.java new file mode 100644 index 00000000..5497e3fe --- /dev/null +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/OrgUtils.java @@ -0,0 +1,86 @@ +package edu.cornell.mannlib.vitro.webapp.visualization.utilities; + +import org.apache.commons.lang.StringUtils; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +public class OrgUtils { + public static Map getParentURIsToLabel(String org, Map> subOrgMap, Map orgLabelMap) { + Map parentURIsToLabel = new TreeMap<>(); + + if (!StringUtils.isEmpty(org)) { + for (Map.Entry> orgMapEntry : subOrgMap.entrySet()) { + Set subOrgs = orgMapEntry.getValue(); + if (subOrgs != null && subOrgs.contains(org) && orgLabelMap.containsKey(orgMapEntry.getKey())) { + parentURIsToLabel.put(orgMapEntry.getKey(), orgLabelMap.get(orgMapEntry.getKey())); + } + } + } + + return parentURIsToLabel; + } + + public static void getObjectMappingsForOrgAnSubOrgs( + String orgUri, + Set orgObjects, + Set orgObjectsIncludesPeople, + Map> subOrgObjectMap, + Map> subOrgMap, + Map> organisationToPeopleMap, + Map> personToObjectMap + ) { + if (subOrgMap.containsKey(orgUri)) { + for (String topSubOrg : subOrgMap.get(orgUri)) { + Set subOrgPublications = new HashSet(); + Set subOrgPublicationsPeople = new HashSet(); + + Set fullSubOrgs = OrgUtils.orgAndAllSubOrgs(new HashSet(), topSubOrg, subOrgMap); + + for (String subOrg : fullSubOrgs) { + Set peopleInSubOrg = organisationToPeopleMap.get(subOrg); + if (peopleInSubOrg != null) { + for (String person : peopleInSubOrg) { + if (personToObjectMap.containsKey(person)) { + if (subOrgPublicationsPeople.add(person)) { + subOrgPublications.addAll(personToObjectMap.get(person)); + + if (orgObjectsIncludesPeople.add(person)) { + orgObjects.addAll(personToObjectMap.get(person)); + } + } + } + } + } + } + + subOrgObjectMap.put(topSubOrg, subOrgPublications); + } + } + + Set people = organisationToPeopleMap.get(orgUri); + if (people != null) { + for (String person : people) { + if (personToObjectMap.containsKey(person)) { + if (orgObjectsIncludesPeople.add(person)) { + orgObjects.addAll(personToObjectMap.get(person)); + } + } + } + } + } + + private static Set orgAndAllSubOrgs(Set allSubOrgs, String org, Map> subOrgMap) { + if (allSubOrgs.add(org)) { + if (subOrgMap.containsKey(org)) { + for (String subOrg : subOrgMap.get(org)) { + orgAndAllSubOrgs(allSubOrgs, subOrg, subOrgMap); + } + } + } + + return allSubOrgs; + } +} diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/VisualizationCaches.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/VisualizationCaches.java new file mode 100644 index 00000000..3b4980c9 --- /dev/null +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/VisualizationCaches.java @@ -0,0 +1,507 @@ +package edu.cornell.mannlib.vitro.webapp.visualization.utilities; + +import com.hp.hpl.jena.query.QuerySolution; +import com.hp.hpl.jena.query.ResultSet; +import com.hp.hpl.jena.query.ResultSetFactory; +import com.hp.hpl.jena.rdf.model.Resource; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants; +import edu.cornell.mannlib.vitro.webapp.visualization.visutils.UtilityFunctions; +import org.joda.time.DateTime; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +final public class VisualizationCaches { + public static final CachingRDFServiceExecutor> cachedOrganizationLabels = + new CachingRDFServiceExecutor<>( + new CachingRDFServiceExecutor.RDFServiceCallable>() { + @Override + protected Map callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?org ?orgLabel\n" + + "WHERE\n" + + "{\n" + + " ?org a foaf:Organization .\n" + + " ?org rdfs:label ?orgLabel .\n" + + "}\n"; + + Map map = new HashMap<>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String org = qs.getResource("org").getURI(); + String orgLabel = qs.getLiteral("orgLabel").getString(); + + map.put(org, orgLabel); + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor>> cachedOrganizationSubOrgs = + new CachingRDFServiceExecutor<>( + new CachingRDFServiceExecutor.RDFServiceCallable>>() { + @Override + protected Map> callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?org ?subOrg\n" + + "WHERE\n" + + "{\n" + + " ?org a foaf:Organization .\n" + + " ?org ?subOrg .\n" + + "}\n"; + + Map> map = new HashMap<>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String org = qs.getResource("org").getURI(); + String subOrg = qs.getResource("subOrg").getURI(); + + Set subOrgs = map.get(org); + if (subOrgs == null) { + subOrgs = new HashSet(); + subOrgs.add(subOrg); + map.put(org, subOrgs); + } else { + subOrgs.add(subOrg); + } + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor> cachedOrganizationToMostSpecificLabel = + new CachingRDFServiceExecutor<>( + new CachingRDFServiceExecutor.RDFServiceCallable>() { + @Override + protected Map callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?org ?typeLabel\n" + + "WHERE\n" + + "{\n" + + " ?org a foaf:Organization .\n" + + " ?org vitro:mostSpecificType ?type .\n" + + " ?type rdfs:label ?typeLabel .\n" + + "}\n"; + + Map map = new HashMap<>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String org = qs.getResource("org").getURI(); + String typeLabel = qs.getLiteral("typeLabel").getString(); + map.put(org, String.valueOf(typeLabel)); + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor>> cachedOrganisationToPeopleMap = + new CachingRDFServiceExecutor>>( + new CachingRDFServiceExecutor.RDFServiceCallable>>() { + @Override + protected Map> callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?organisation ?person\n" + + "WHERE\n" + + "{\n" + + " ?organisation a foaf:Organization .\n" + + " ?organisation core:relatedBy ?position .\n" + + " ?position core:relates ?person .\n" + + " ?person a foaf:Person .\n" + + "}\n"; + + // TODO Critical section? + + Map> orgToPeopleMap = new HashMap>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String org = qs.getResource("organisation").getURI(); + String person = qs.getResource("person").getURI(); + + Set people = orgToPeopleMap.get(org); + if (people == null) { + people = new HashSet(); + people.add(person); + orgToPeopleMap.put(org, people); + } else { + people.add(person); + } + } + } finally { + silentlyClose(is); + } + + return orgToPeopleMap; + } + } + ); + + public static final CachingRDFServiceExecutor> cachedPersonLabels = + new CachingRDFServiceExecutor<>( + new CachingRDFServiceExecutor.RDFServiceCallable>() { + @Override + protected Map callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?person ?personLabel\n" + + "WHERE\n" + + "{\n" + + " ?person a foaf:Person .\n" + + " ?person rdfs:label ?personLabel .\n" + + "}\n"; + + Map map = new HashMap<>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String person = qs.getResource("person").getURI(); + String personLabel = qs.getLiteral("personLabel").getString(); + + map.put(person, personLabel); + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor>> cachedPersonToPublication = + new CachingRDFServiceExecutor>>( + new CachingRDFServiceExecutor.RDFServiceCallable>>() { + @Override + protected Map> callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?person ?document\n" + + "WHERE\n" + + "{\n" + + " ?person a foaf:Person .\n" + + " ?person core:relatedBy ?authorship .\n" + + " ?authorship a core:Authorship .\n" + + " ?authorship core:relates ?document .\n" + + " ?document a bibo:Document .\n" + + "}\n"; + + Map> map = new HashMap>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + + Resource person = qs.getResource("person"); + Resource document = qs.getResource("document"); + + if (person != null && document != null) { + String personURI = person.getURI(); + + Set documents = map.get(personURI); + if (documents == null) { + documents = new HashSet(); + documents.add(document.getURI()); + map.put(personURI, documents); + } else { + documents.add(document.getURI()); + } + } + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor> cachedPublicationToYear = + new CachingRDFServiceExecutor<>( + new CachingRDFServiceExecutor.RDFServiceCallable>() { + @Override + protected Map callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?document ?publicationDate\n" + + "WHERE\n" + + "{\n" + + " ?document a bibo:Document .\n" + + " ?document core:dateTimeValue ?dateTimeValue . \n" + + " ?dateTimeValue core:dateTime ?publicationDate . \n" + + "}\n"; + + Map map = new HashMap<>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String document = qs.getResource("document").getURI(); + String pubDate = qs.getLiteral("publicationDate").getString(); + if (pubDate != null) { + DateTime validParsedDateTimeObject = UtilityFunctions + .getValidParsedDateTimeObject(pubDate); + + if (validParsedDateTimeObject != null) { + map.put(document, String.valueOf(validParsedDateTimeObject.getYear())); + } + } + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor>> cachedPersonToGrant = + new CachingRDFServiceExecutor>>( + new CachingRDFServiceExecutor.RDFServiceCallable>>() { + @Override + protected Map> callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?person ?grant\n" + + "WHERE\n" + + "{\n" + + " ?person a foaf:Person .\n" + + " ?person ?role .\n" + + " { ?role a core:PrincipalInvestigatorRole . } UNION { ?role a core:CoPrincipalInvestigatorRole . } \n" + + " ?role core:relatedBy ?grant .\n" + + " ?grant a core:Grant .\n" + + "}\n"; + + Map> map = new HashMap>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + + Resource person = qs.getResource("person"); + Resource grant = qs.getResource("grant"); + + if (person != null && grant != null) { + String personURI = person.getURI(); + + Set documents = map.get(personURI); + if (documents == null) { + documents = new HashSet(); + documents.add(grant.getURI()); + map.put(personURI, documents); + } else { + documents.add(grant.getURI()); + } + } + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor> cachedGrantToYear = + new CachingRDFServiceExecutor<>( + new CachingRDFServiceExecutor.RDFServiceCallable>() { + @Override + protected Map callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?grant ?startDateTimeValue\n" + + "WHERE\n" + + "{\n" + + " ?grant a core:Grant .\n" + + " ?grant core:dateTimeInterval ?dateTimeIntervalValue . \n" + + " ?dateTimeIntervalValue core:start ?startDate . \n" + + " ?startDate core:dateTime ?startDateTimeValue . \n" + + "}\n"; + + Map map = new HashMap<>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String grant = qs.getResource("grant").getURI(); + String startDate = qs.getLiteral("startDateTimeValue").getString(); + if (startDate != null) { + DateTime validParsedDateTimeObject = UtilityFunctions + .getValidParsedDateTimeObject(startDate); + + if (validParsedDateTimeObject != null) { + map.put(grant, String.valueOf(validParsedDateTimeObject.getYear())); + } + } + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor> cachedGrantToRoleYear = + new CachingRDFServiceExecutor<>( + new CachingRDFServiceExecutor.RDFServiceCallable>() { + @Override + protected Map callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?grant ?startDateTimeValue\n" + + "WHERE\n" + + "{\n" + + " ?grant a core:Grant .\n" + + " ?grant core:relates ?role .\n" + + " ?role core:dateTimeInterval ?dateTimeIntervalValue . \n" + + " ?dateTimeIntervalValue core:start ?startDate . \n" + + " ?startDate core:dateTime ?startDateTimeValue . \n" + + "}\n"; + + Map map = new HashMap<>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String grant = qs.getResource("grant").getURI(); + String startDate = qs.getLiteral("startDateTimeValue").getString(); + if (startDate != null) { + DateTime validParsedDateTimeObject = UtilityFunctions + .getValidParsedDateTimeObject(startDate); + + if (validParsedDateTimeObject != null) { + map.put(grant, String.valueOf(validParsedDateTimeObject.getYear())); + } + } + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + public static final CachingRDFServiceExecutor> cachedPersonToMostSpecificLabel = + new CachingRDFServiceExecutor<>( + new CachingRDFServiceExecutor.RDFServiceCallable>() { + @Override + protected Map callWithService(RDFService rdfService) throws Exception { + String query = QueryConstants.getSparqlPrefixQuery() + + "SELECT ?person ?typeLabel\n" + + "WHERE\n" + + "{\n" + + " ?person a foaf:Person .\n" + + " ?person vitro:mostSpecificType ?type .\n" + + " ?type rdfs:label ?typeLabel .\n" + + "}\n"; + + Map map = new HashMap<>(); + + InputStream is = null; + ResultSet rs = null; + try { + is = rdfService.sparqlSelectQuery(query, RDFService.ResultFormat.JSON); + rs = ResultSetFactory.fromJSON(is); + + while (rs.hasNext()) { + QuerySolution qs = rs.next(); + String person = qs.getResource("person").getURI(); + String typeLabel = qs.getLiteral("typeLabel").getString(); + map.put(person, String.valueOf(typeLabel)); + } + } finally { + silentlyClose(is); + } + + return map; + } + } + ); + + private static void silentlyClose(InputStream is) { + try { + if (is != null) { + is.close(); + } + } catch (Throwable t) { + + } + } +} diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/valueobjects/json/SubjectEntityJSON.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/valueobjects/json/SubjectEntityJSON.java index 2c0e5a08..a0a20782 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/valueobjects/json/SubjectEntityJSON.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/valueobjects/json/SubjectEntityJSON.java @@ -21,6 +21,13 @@ public class SubjectEntityJSON { this.setParentURIToLabel(parentOrganizations); } + public SubjectEntityJSON(String subjectEntityURI, String label, + Map parentURIToLabel) { + this.subjectEntityURI = subjectEntityURI; + this.subjectEntityLabel = label; + this.parentURIToLabel = parentURIToLabel; + } + public String getSubjectEntityURI() { return subjectEntityURI; }