diff --git a/productMods/css/visualization/personlevel/page.css b/productMods/css/visualization/personlevel/page.css index 5dac214d..ef72968f 100644 --- a/productMods/css/visualization/personlevel/page.css +++ b/productMods/css/visualization/personlevel/page.css @@ -258,4 +258,4 @@ table.sparkline_wrapper_table td, th { } #table_heading { width: 80%; -} \ No newline at end of file +} diff --git a/productMods/css/visualization/visualization.css b/productMods/css/visualization/visualization.css index 9fe9a3bb..14d71aae 100644 --- a/productMods/css/visualization/visualization.css +++ b/productMods/css/visualization/visualization.css @@ -23,9 +23,6 @@ span.incomplete-data-holder, margin-top: 1.6em; font-size: 1em; } -#incomplete-data-small { - margin-top: 1.6em; -} .collaboratorship-icon { float: left; padding-right: 8px; @@ -90,6 +87,21 @@ span.incomplete-data-holder, .infoIcon { padding-right:30px; } +.cache-info-small { + padding: 5px; + font-size: .8em; + color: #444; + border: dotted 1px #eee; + background-color: #ffb; + float: right; +} +.cache-info-vis { + padding: 5px; + color: #444; + border: dotted 1px #eee; + background-color: #ffb; + float: right; +} /* ---------------------------*/ /* Visualization Tools -------*/ diff --git a/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonOnGrantsStandalone.ftl b/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonOnGrantsStandalone.ftl index 4a50cc05..2ef0a962 100644 --- a/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonOnGrantsStandalone.ftl +++ b/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonOnGrantsStandalone.ftl @@ -46,6 +46,9 @@ $(document).ready(function () { <#assign currentParameterObject = grantParameter>
+<#if (builtFromCacheTime??) > +
${i18n().using_cache_time} ${builtFromCacheTime?time} (${builtFromCacheTime?date?string("MMM dd yyyy")})
+ <#include "entityComparisonBody.ftl"> diff --git a/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonOnPublicationsStandalone.ftl b/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonOnPublicationsStandalone.ftl index da47d15b..eebd3b0d 100644 --- a/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonOnPublicationsStandalone.ftl +++ b/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonOnPublicationsStandalone.ftl @@ -46,6 +46,9 @@ $(document).ready(function () { <#assign currentParameterObject = publicationParameter>
+<#if (builtFromCacheTime??) > +
${i18n().using_cache_time} ${builtFromCacheTime?time} (${builtFromCacheTime?date?string("MMM dd yyyy")})
+ <#include "entityComparisonBody.ftl"> diff --git a/productMods/templates/freemarker/visualization/mapOfScience/mapOfScienceStandalone.ftl b/productMods/templates/freemarker/visualization/mapOfScience/mapOfScienceStandalone.ftl index 95bfdef9..d84170a0 100644 --- a/productMods/templates/freemarker/visualization/mapOfScience/mapOfScienceStandalone.ftl +++ b/productMods/templates/freemarker/visualization/mapOfScience/mapOfScienceStandalone.ftl @@ -7,6 +7,9 @@ corresponding changes in the included Templates. --> <#include "mapOfScienceSetup.ftl">
+ <#if (builtFromCacheTime??) > +
${i18n().using_cache_time} ${builtFromCacheTime?time} (${builtFromCacheTime?date?string("MMM dd yyyy")})
+ <#--
@@ -83,4 +86,6 @@ corresponding changes in the included Templates. -->
-${headScripts.add('')} \ No newline at end of file +${headScripts.add('')} + +${stylesheets.add('')} diff --git a/productMods/templates/freemarker/visualization/personlevel/coAuthorPersonLevel.ftl b/productMods/templates/freemarker/visualization/personlevel/coAuthorPersonLevel.ftl index fd13aa86..5101fcfe 100644 --- a/productMods/templates/freemarker/visualization/personlevel/coAuthorPersonLevel.ftl +++ b/productMods/templates/freemarker/visualization/personlevel/coAuthorPersonLevel.ftl @@ -150,7 +150,10 @@ $(document).ready(function(){
- + + <#if (builtFromCacheTime??) > +
${i18n().using_cache_time} ${builtFromCacheTime?time} (${builtFromCacheTime?date?string("MMM dd yyyy")})
+
<#if (numOfAuthors?? && numOfAuthors > 0) > diff --git a/productMods/templates/freemarker/visualization/personlevel/coAuthorPersonLevelD3.ftl b/productMods/templates/freemarker/visualization/personlevel/coAuthorPersonLevelD3.ftl index 705b3442..1caa441d 100644 --- a/productMods/templates/freemarker/visualization/personlevel/coAuthorPersonLevelD3.ftl +++ b/productMods/templates/freemarker/visualization/personlevel/coAuthorPersonLevelD3.ftl @@ -266,8 +266,11 @@ $(document).ready(function(){ + <#if (builtFromCacheTime??) > +
${i18n().using_cache_time} ${builtFromCacheTime?time} (${builtFromCacheTime?date?string("MMM dd yyyy")})
+
- + <#if (numOfAuthors?? && numOfAuthors > 0) > <#else> diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/coauthorship/CoAuthorshipQueryRunner.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/coauthorship/CoAuthorshipQueryRunner.java index 04f907db..3bc10c65 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/coauthorship/CoAuthorshipQueryRunner.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/coauthorship/CoAuthorshipQueryRunner.java @@ -2,7 +2,6 @@ package edu.cornell.mannlib.vitro.webapp.visualization.coauthorship; -import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -15,14 +14,12 @@ import java.util.concurrent.ConcurrentHashMap; import com.hp.hpl.jena.query.QueryExecution; import com.hp.hpl.jena.query.QueryExecutionFactory; -import com.hp.hpl.jena.query.ResultSet; -import com.hp.hpl.jena.query.ResultSetFactory; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; -import net.sf.jga.algorithms.Unique; +import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.jena.iri.IRI; @@ -34,7 +31,6 @@ import com.hp.hpl.jena.query.Syntax; import com.hp.hpl.jena.rdf.model.RDFNode; import edu.cornell.mannlib.vitro.webapp.visualization.collaborationutils.CoAuthorshipData; -import edu.cornell.mannlib.vitro.webapp.visualization.collaborationutils.CollaborationData; import edu.cornell.mannlib.vitro.webapp.visualization.collaborationutils.CollaboratorComparator; import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants; import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryFieldLabels; @@ -58,6 +54,8 @@ public class CoAuthorshipQueryRunner implements QueryRunner { protected static final Syntax SYNTAX = Syntax.syntaxARQ; + private static boolean preferCaches = false; + private String egoURI; private RDFService rdfService; @@ -90,34 +88,46 @@ public class CoAuthorshipQueryRunner implements QueryRunner { @Override protected void processQuerySolution(QuerySolution qs) { + RDFNode egoAuthorURLNode = qs.get(QueryFieldLabels.AUTHOR_URL); + RDFNode authorLabelNode = qs.get(QueryFieldLabels.AUTHOR_LABEL); + RDFNode documentNode = qs.get(QueryFieldLabels.DOCUMENT_URL); + RDFNode coAuthorURLNode = qs.get(QueryFieldLabels.CO_AUTHOR_URL); + RDFNode coAuthorLabelNode = qs.get(QueryFieldLabels.CO_AUTHOR_LABEL); + RDFNode publicationDateNode = qs.get(QueryFieldLabels.DOCUMENT_PUBLICATION_DATE); + + String authorURI = egoAuthorURLNode == null ? null : egoAuthorURLNode.asLiteral().getString(); + String authorName = authorLabelNode == null ? null : authorLabelNode.asLiteral().getString(); + String documentURI = documentNode == null ? null : documentNode.asLiteral().getString(); + String documentDate = publicationDateNode == null ? null : publicationDateNode.asLiteral().getString(); + String coAuthorURI = coAuthorURLNode == null ? null : coAuthorURLNode.asLiteral().getString(); + String coAuthorName = coAuthorLabelNode == null ? null : coAuthorLabelNode.asLiteral().getString(); + + processEntry(authorURI, authorName, documentURI, documentDate, coAuthorURI, coAuthorName); + } + + public void processEntry(String authorURI, String authorName, String documentURI, String documentDate, String coAuthorURI, String coAuthorName) { /* * We only want to create only ONE ego node. * */ - RDFNode egoAuthorURLNode = qs.get(QueryFieldLabels.AUTHOR_URL); - if (nodeURLToVO.containsKey(egoAuthorURLNode.toString())) { - - egoNode = nodeURLToVO.get(egoAuthorURLNode.toString()); - + if (nodeURLToVO.containsKey(authorURI)) { + egoNode = nodeURLToVO.get(authorURI); } else { - - egoNode = new Collaborator(egoAuthorURLNode.toString(), nodeIDGenerator); + egoNode = new Collaborator(authorURI, nodeIDGenerator); nodes.add(egoNode); - nodeURLToVO.put(egoAuthorURLNode.toString(), egoNode); + nodeURLToVO.put(authorURI, egoNode); - RDFNode authorLabelNode = qs.get(QueryFieldLabels.AUTHOR_LABEL); - if (authorLabelNode != null) { - egoNode.setCollaboratorName(authorLabelNode.toString()); + if (authorName != null) { + egoNode.setCollaboratorName(authorName); } } - RDFNode documentNode = qs.get(QueryFieldLabels.DOCUMENT_URL); Activity biboDocument; - if (biboDocumentURLToVO.containsKey(documentNode.toString())) { - biboDocument = biboDocumentURLToVO.get(documentNode.toString()); + if (biboDocumentURLToVO.containsKey(documentURI)) { + biboDocument = biboDocumentURLToVO.get(documentURI); } else { - biboDocument = createDocumentVO(qs, documentNode.toString()); - biboDocumentURLToVO.put(documentNode.toString(), biboDocument); + biboDocument = createDocumentVO(documentURI, documentDate); + biboDocumentURLToVO.put(documentURI, biboDocument); } egoNode.addActivity(biboDocument); @@ -127,27 +137,21 @@ public class CoAuthorshipQueryRunner implements QueryRunner { * we do not want a co-author node or Collaboration if the publication has only one * author and that happens to be the ego. * */ - if (qs.get(QueryFieldLabels.AUTHOR_URL).toString().equalsIgnoreCase( - qs.get(QueryFieldLabels.CO_AUTHOR_URL).toString())) { + if (authorURI.equalsIgnoreCase(coAuthorURI)) { return; } Collaborator coAuthorNode; - RDFNode coAuthorURLNode = qs.get(QueryFieldLabels.CO_AUTHOR_URL); - if (nodeURLToVO.containsKey(coAuthorURLNode.toString())) { - - coAuthorNode = nodeURLToVO.get(coAuthorURLNode.toString()); - + if (nodeURLToVO.containsKey(coAuthorURI)) { + coAuthorNode = nodeURLToVO.get(coAuthorURI); } else { - - coAuthorNode = new Collaborator(coAuthorURLNode.toString(), nodeIDGenerator); + coAuthorNode = new Collaborator(coAuthorURI, nodeIDGenerator); nodes.add(coAuthorNode); - nodeURLToVO.put(coAuthorURLNode.toString(), coAuthorNode); + nodeURLToVO.put(coAuthorURI, coAuthorNode); - RDFNode coAuthorLabelNode = qs.get(QueryFieldLabels.CO_AUTHOR_LABEL); - if (coAuthorLabelNode != null) { - coAuthorNode.setCollaboratorName(coAuthorLabelNode.toString()); + if (coAuthorName != null) { + coAuthorNode.setCollaboratorName(coAuthorName); } } @@ -225,13 +229,12 @@ public class CoAuthorshipQueryRunner implements QueryRunner { return new CoAuthorshipData(egoNode, nodes, edges, biboDocumentURLToVO); } - private Activity createDocumentVO(QuerySolution solution, String documentURL) { + private Activity createDocumentVO(String documentURL, String documentDate) { Activity biboDocument = new Activity(documentURL); - RDFNode publicationDateNode = solution.get(QueryFieldLabels.DOCUMENT_PUBLICATION_DATE); - if (publicationDateNode != null) { - biboDocument.setActivityDate(publicationDateNode.toString()); + if (documentDate != null) { + biboDocument.setActivityDate(documentDate); } return biboDocument; @@ -413,17 +416,17 @@ public class CoAuthorshipQueryRunner implements QueryRunner { + "WHERE\n" + "{\n" + " {\n" - + " <" + queryURI + "> rdf:type foaf:Person ;" - + " rdfs:label ?authorLabel ;" - + " core:relatedBy ?authorshipNode . \n" - + " ?authorshipNode rdf:type core:Authorship ;" - + " core:relates ?document . \n" - + " ?document rdf:type ; \n" - + " core:relatedBy ?coAuthorshipNode . \n" - + " ?coAuthorshipNode rdf:type core:Authorship ; \n" - + " core:relates ?coAuthorPerson . \n" - + " ?coAuthorPerson rdf:type foaf:Person ; \n" - + " rdfs:label ?coAuthorPersonLabel . \n" + + " <" + queryURI + "> rdf:type foaf:Person ;" + + " rdfs:label ?authorLabel ;" + + " core:relatedBy ?authorshipNode . \n" + + " ?authorshipNode rdf:type core:Authorship ;" + + " core:relates ?document . \n" + + " ?document rdf:type ; \n" + + " core:relatedBy ?coAuthorshipNode . \n" + + " ?coAuthorshipNode rdf:type core:Authorship ; \n" + + " core:relates ?coAuthorPerson . \n" + + " ?coAuthorPerson rdf:type foaf:Person ; \n" + + " rdfs:label ?coAuthorPersonLabel . \n" + " }\n" + " UNION\n" + " {\n" @@ -487,20 +490,60 @@ public class CoAuthorshipQueryRunner implements QueryRunner { throw new MalformedQueryParametersException("URI parameter is either null or empty."); } - try { - Model constructedModel = ModelFactory.createDefaultModel(); - rdfService.sparqlConstructQuery(generateEgoCoAuthorshipSparqlConstruct(this.egoURI), constructedModel); - QueryResultConsumer consumer = new QueryResultConsumer(); - QueryExecution qe = QueryExecutionFactory.create(generateEgoCoAuthorshipSparqlQuery(this.egoURI), constructedModel); - try { - consumer.processResultSet(qe.execSelect()); - } finally { - qe.close(); - } - data = consumer.getCollaborationData(); - } catch (RDFServiceException e) { - log.error("Unable to execute query", e); - throw new RuntimeException(e); + Date cacheTime = null; + QueryResultConsumer consumer = new QueryResultConsumer(); + + // If we've had long running queries (preferCaches), and the caches are available, use the cache + if (preferCaches && VisualizationCaches.personToPublication.isCached()) { + cacheTime = VisualizationCaches.personToPublication.cachedWhen(); + + Map personLabelsMap = VisualizationCaches.personLabels.get(rdfService); + Map> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService).personToPublication; + Map> publicationToPersonMap = VisualizationCaches.personToPublication.get(rdfService).publicationToPerson; + Map publicationToYearMap = VisualizationCaches.publicationToYear.get(rdfService); + + String authorURI = this.egoURI; + String authorName = personLabelsMap.get(authorURI); + for (String documentURI : personToPublicationMap.get(authorURI)) { + String documentDate = publicationToYearMap.get(documentURI); + for (String coAuthorURI : publicationToPersonMap.get(documentURI)) { + String coAuthorName = personLabelsMap.get(coAuthorURI); + consumer.processEntry(authorURI, authorName, documentURI, documentDate, coAuthorURI, coAuthorName); + } + } + consumer.endProcessing(); + } else { + // Not use the caches, so query the triple store - recording the time it took + long queryStart = System.currentTimeMillis(); + try { + Model constructedModel = ModelFactory.createDefaultModel(); + rdfService.sparqlConstructQuery(generateEgoCoAuthorshipSparqlConstruct(this.egoURI), constructedModel); + QueryExecution qe = QueryExecutionFactory.create(generateEgoCoAuthorshipSparqlQuery(this.egoURI), constructedModel); + try { + consumer.processResultSet(qe.execSelect()); + } finally { + qe.close(); + } + } catch (RDFServiceException e) { + log.error("Unable to execute query", e); + throw new RuntimeException(e); + } finally { + // If the query took more than 5 seconds, start using the caches + if (System.currentTimeMillis() - queryStart > 5000) { + // If the caches haven't already been built, request a rebuild + if (!preferCaches && !VisualizationCaches.personToPublication.isCached()) { + VisualizationCaches.rebuildAll(); + } + + // Attempt to use caches next time + preferCaches = true; + } + } + } + + data = consumer.getCollaborationData(); + if (cacheTime != null) { + data.setBuiltFromCacheTime(cacheTime); } CollaborationDataCacheEntry newEntry = new CollaborationDataCacheEntry(); diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/collaborationutils/CollaborationData.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/collaborationutils/CollaborationData.java index a28f8073..b5fdec18 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/collaborationutils/CollaborationData.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/collaborationutils/CollaborationData.java @@ -2,6 +2,7 @@ package edu.cornell.mannlib.vitro.webapp.visualization.collaborationutils; +import java.util.Date; import java.util.Map; import java.util.Set; @@ -15,6 +16,8 @@ public abstract class CollaborationData { private Collaborator egoCollaborator; private Set> NODE_SCHEMA; private Set> EDGE_SCHEMA; + + private Date builtFromCacheTime = null; public CollaborationData(Collaborator egoCollaborator, Set collaborators, @@ -23,7 +26,9 @@ public abstract class CollaborationData { this.collaborators = collaborators; this.collaborations = collaborations; } - + + public Date getBuiltFromCacheTime() { return builtFromCacheTime; } + public Set getCollaborators() { return collaborators; } @@ -60,6 +65,8 @@ public abstract class CollaborationData { return EDGE_SCHEMA; } + public void setBuiltFromCacheTime(Date time) { this.builtFromCacheTime = time; } + abstract Set> initializeEdgeSchema(); abstract Set> initializeNodeSchema(); 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 125fcbaa..80d7a029 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/mapofscience/MapOfScienceVisualizationRequestHandler.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/mapofscience/MapOfScienceVisualizationRequestHandler.java @@ -90,7 +90,7 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq private Set getPublicationsForPerson(RDFService rdfService, String personUri) { if (preferCachesForPersonMap() && VisualizationCaches.personToPublication.isCached()) { - Map> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService); + Map> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService).personToPublication; return personToPublicationMap.get(personUri); } else { final Set queryResults = new HashSet(); @@ -253,7 +253,7 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq Map> subOrgMap = VisualizationCaches.organizationSubOrgs.get(rdfService); Map> organisationToPeopleMap = VisualizationCaches.organisationToPeopleMap.get(rdfService); - Map> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService); + Map> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService).personToPublication; Map publicationToJournalMap = VisualizationCaches.publicationToJournal.get(rdfService); Set orgPublications = new HashSet(); @@ -397,22 +397,22 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq .getIndividualDao() .getIndividualByURI(entityURI); - if (individual != null && individual.isVClass("http://xmlns.com/foaf/0.1/Person")) { - - return getSubjectPersonEntityAndGenerateDataResponse( - vitroRequest, - entityURI, - individual != null ? individual.getDataValue("http://www.w3.org/2000/01/rdf-schema#label") : "", - currentDataVisMode); - - } else { - - return getSubjectEntityAndGenerateDataResponse( - vitroRequest, - entityURI, - individual != null ? individual.getDataValue("http://www.w3.org/2000/01/rdf-schema#label") : "", - currentDataVisMode); - + try { + if (individual != null && individual.isVClass("http://xmlns.com/foaf/0.1/Person")) { + return getSubjectPersonEntityAndGenerateDataResponse( + vitroRequest, + entityURI, + individual != null ? individual.getDataValue("http://www.w3.org/2000/01/rdf-schema#label") : "", + currentDataVisMode); + } else { + return getSubjectEntityAndGenerateDataResponse( + vitroRequest, + entityURI, + individual != null ? individual.getDataValue("http://www.w3.org/2000/01/rdf-schema#label") : "", + currentDataVisMode); + } + } finally { + VisualizationCaches.buildMissing(); } } @@ -437,11 +437,17 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq if (UtilityFunctions.isEntityAPerson(vreq, entityURI)) { body.put("entityType", "PERSON"); - + if (preferCachesForPersonMap() && VisualizationCaches.personToPublication.isCached()) { + body.put("builtFromCacheTime", VisualizationCaches.personToPublication.cachedWhen()); + } } else { body.put("entityType", "ORGANIZATION"); + if (VisualizationCaches.personToPublication.isCached()) { + body.put("builtFromCacheTime", VisualizationCaches.personToPublication.cachedWhen()); + } } - + + body.put("vivoDefaultNamespace", vreq.getWebappDaoFactory().getDefaultNamespace()); return new TemplateResponseValues(standaloneTemplate, body); diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/personlevel/PersonLevelRequestHandler.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/personlevel/PersonLevelRequestHandler.java index e2b68935..925ff5e6 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/personlevel/PersonLevelRequestHandler.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/personlevel/PersonLevelRequestHandler.java @@ -254,6 +254,10 @@ public class PersonLevelRequestHandler implements VisualizationRequestHandler { body.put("egoPubSparklineVO", egoPubSparklineVO); body.put("uniqueCoauthorsSparklineVO", uniqueCoauthorsSparklineVO); + if (coAuthorshipVO.getBuiltFromCacheTime() != null) { + body.put("builtFromCacheTime", coAuthorshipVO.getBuiltFromCacheTime()); + } + body.put("title", title + "Person Level Visualization"); return new TemplateResponseValues(standaloneTemplate, body); 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 a23af95c..3c470578 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalGrantVisualizationRequestHandler.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalGrantVisualizationRequestHandler.java @@ -102,14 +102,17 @@ public class TemporalGrantVisualizationRequestHandler implements } } - - return getSubjectEntityAndGenerateDataResponse( - vitroRequest, - log, - dataset, - entityURI, - currentDataMode); - + + try { + return getSubjectEntityAndGenerateDataResponse( + vitroRequest, + log, + dataset, + entityURI, + currentDataMode); + } finally { + VisualizationCaches.buildMissing(); + } } private Map prepareDataErrorResponse() { @@ -279,17 +282,21 @@ public class TemporalGrantVisualizationRequestHandler implements String entityURI) { String standaloneTemplate = "entityComparisonOnGrantsStandalone.ftl"; - + String organizationLabel = OrganizationUtilityFunctions.getEntityLabelFromDAO(vreq, entityURI); - + Map body = new HashMap(); body.put("title", organizationLabel + " - Temporal Graph Visualization"); body.put("organizationURI", entityURI); body.put("organizationLocalName", UtilityFunctions.getIndividualLocalName(entityURI, vreq)); body.put("vivoDefaultNamespace", vreq.getWebappDaoFactory().getDefaultNamespace()); body.put("organizationLabel", organizationLabel); - + + if (VisualizationCaches.personToGrant.isCached()) { + body.put("builtFromCacheTime", VisualizationCaches.personToGrant.cachedWhen()); + } + return new TemplateResponseValues(standaloneTemplate, body); } 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 d82b5648..7ce283b5 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalPublicationVisualizationRequestHandler.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/temporalgraph/TemporalPublicationVisualizationRequestHandler.java @@ -92,7 +92,7 @@ public class TemporalPublicationVisualizationRequestHandler implements Map orgMostSpecificLabelMap = VisualizationCaches.organizationToMostSpecificLabel.get(rdfService); Map personMostSpecificLabelMap = VisualizationCaches.personToMostSpecificLabel.get(rdfService); Map> organisationToPeopleMap = VisualizationCaches.organisationToPeopleMap.get(rdfService); - Map> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService); + Map> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService).personToPublication; Map publicationToYearMap = VisualizationCaches.publicationToYear.get(rdfService); Set orgPublications = new HashSet(); @@ -249,14 +249,17 @@ public class TemporalPublicationVisualizationRequestHandler implements } } - - return getSubjectEntityAndGenerateDataResponse( - vitroRequest, - log, - dataset, - entityURI, - currentDataMode); - + + try { + return getSubjectEntityAndGenerateDataResponse( + vitroRequest, + log, + dataset, + entityURI, + currentDataMode); + } finally { + VisualizationCaches.buildMissing(); + } } @@ -268,14 +271,18 @@ public class TemporalPublicationVisualizationRequestHandler implements private TemplateResponseValues prepareStandaloneMarkupResponse(VitroRequest vreq, String entityURI) { String standaloneTemplate = "entityComparisonOnPublicationsStandalone.ftl"; String organizationLabel = OrganizationUtilityFunctions.getEntityLabelFromDAO(vreq, entityURI); - + Map body = new HashMap(); body.put("title", organizationLabel + " - Temporal Graph Visualization"); body.put("organizationURI", entityURI); body.put("organizationLocalName", UtilityFunctions.getIndividualLocalName(entityURI, vreq)); body.put("vivoDefaultNamespace", vreq.getWebappDaoFactory().getDefaultNamespace()); body.put("organizationLabel", organizationLabel); - + + if (VisualizationCaches.personToPublication.isCached()) { + body.put("builtFromCacheTime", VisualizationCaches.personToPublication.cachedWhen()); + } + return new TemplateResponseValues(standaloneTemplate, body); } diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CachingRDFServiceExecutor.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CachingRDFServiceExecutor.java index baae5176..a028b711 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CachingRDFServiceExecutor.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/CachingRDFServiceExecutor.java @@ -6,6 +6,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -35,6 +36,7 @@ public class CachingRDFServiceExecutor { * Background task tracker */ private FutureTask backgroundTask = null; + private Thread backgroundCompletion = null; private long backgroundTaskStartTime = -1; /** @@ -50,6 +52,8 @@ public class CachingRDFServiceExecutor { return cachedResults != null; } + public Date cachedWhen() { return new Date(lastCacheTime); } + /** * Return the cached results if present, or start the task. * Will wait for completion if the cache is not already populated, otherwise the refresh will happen in the background. @@ -100,19 +104,32 @@ public class CachingRDFServiceExecutor { * * @param rdfService an RDF service to use, if the background RDF service is not set */ - public synchronized void build(RDFService rdfService) { + public void build(RDFService rdfService) { // First, check if there are results from the previous background task, and update the cache + if (backgroundTask != null && backgroundTask.isDone()) { + buildComplete(); + } else if (backgroundTask == null) { + buildStart(rdfService); + } + } + + private synchronized void buildComplete() { if (backgroundTask != null && backgroundTask.isDone()) { completeBackgroundTask(); } + } - // If we have a background RDF service, we can launch the task in the background and leave it - if (backgroundRDFService != null) { - startBackgroundTask(backgroundRDFService); - } else if (rdfService != null) { - // No background service, so use the paassed RDF service, and wait for completion - startBackgroundTask(backgroundRDFService); - completeBackgroundTask(); + private synchronized void buildStart(RDFService rdfService) { + if (backgroundTask == null) { + // If we have a background RDF service, we can launch the task in the background and leave it + if (backgroundRDFService != null) { + startBackgroundTask(backgroundRDFService); + completeBackgroundTaskAsync(); + } else if (rdfService != null) { + // No background service, so use the paassed RDF service, and wait for completion + startBackgroundTask(backgroundRDFService); + completeBackgroundTask(); + } } } @@ -186,6 +203,26 @@ public class CachingRDFServiceExecutor { } } + /** + * Complete the background task + */ + private synchronized void completeBackgroundTaskAsync() { + if (backgroundCompletion == null) { + backgroundCompletion = new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(500); + completeBackgroundTask(-1); + } catch (InterruptedException e) { + } + } + }); + backgroundCompletion.setDaemon(true); + backgroundCompletion.start(); + } + } + /** * Complete the background task */ @@ -215,6 +252,7 @@ public class CachingRDFServiceExecutor { // Clear the background task information backgroundTask = null; backgroundTaskStartTime = -1; + backgroundCompletion = null; } } catch (InterruptedException e) { // Task was interrupted, so abort it @@ -307,7 +345,6 @@ public class CachingRDFServiceExecutor { affinity.complete(); } } - } /** diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/PersonPublicationMaps.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/PersonPublicationMaps.java new file mode 100644 index 00000000..5143da50 --- /dev/null +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/PersonPublicationMaps.java @@ -0,0 +1,33 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ +package edu.cornell.mannlib.vitro.webapp.visualization.utilities; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class PersonPublicationMaps { + public Map> personToPublication = new HashMap>(); + public Map> publicationToPerson = new HashMap>(); + + public void put(String person, String document) { + Set documentSet = personToPublication.get(person); + if (documentSet == null) { + documentSet = new HashSet(); + documentSet.add(document); + personToPublication.put(person, documentSet); + } else { + documentSet.add(document); + } + + Set personSet = publicationToPerson.get(document); + if (personSet == null) { + personSet = new HashSet(); + personSet.add(person); + publicationToPerson.put(document, personSet); + } else { + personSet.add(person); + } + } +} + diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/VisualizationCaches.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/VisualizationCaches.java index 3b9f39b8..f99ee9a0 100644 --- a/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/VisualizationCaches.java +++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/utilities/VisualizationCaches.java @@ -45,6 +45,32 @@ final public class VisualizationCaches { grantToYear.build(rdfService); } + public static void buildMissing() { + if (!organizationLabels.isCached()) { organizationLabels.build(null); } + if (!organizationSubOrgs.isCached()) { organizationSubOrgs.build(null); } + if (!organizationToMostSpecificLabel.isCached()) { organizationToMostSpecificLabel.build(null); } + if (!organisationToPeopleMap.isCached()) { organisationToPeopleMap.build(null); } + if (!personLabels.isCached()) { personLabels.build(null); } + if (!personToMostSpecificLabel.isCached()) { personToMostSpecificLabel.build(null); } + if (!personToPublication.isCached()) { personToPublication.build(null); } + if (!publicationToJournal.isCached()) { publicationToJournal.build(null); } + if (!publicationToYear.isCached()) { publicationToYear.build(null); } + if (!personToGrant.isCached()) { personToGrant.build(null); } + if (!grantToYear.isCached()) { grantToYear.build(null); } + } + + /** + * Rebuild the specifield caches + * @param executors + */ + public static void rebuild(CachingRDFServiceExecutor... executors) { + if (executors != null) { + for (CachingRDFServiceExecutor e : executors) { + e.build(null); + } + } + } + /** * Cache of organization labels (uri -> label) */ @@ -263,11 +289,11 @@ final public class VisualizationCaches { /** * Person to publication Map (person uri -> list of publication uri) */ - public static final CachingRDFServiceExecutor>> personToPublication = - new CachingRDFServiceExecutor>>( - new CachingRDFServiceExecutor.RDFServiceCallable>>(visualizationAffinity) { + public static final CachingRDFServiceExecutor personToPublication = + new CachingRDFServiceExecutor( + new CachingRDFServiceExecutor.RDFServiceCallable(visualizationAffinity) { @Override - protected Map> callWithService(RDFService rdfService) throws Exception { + protected PersonPublicationMaps callWithService(RDFService rdfService) throws Exception { String query = QueryConstants.getSparqlPrefixQuery() + "SELECT ?person ?document\n" + "WHERE\n" + @@ -276,10 +302,10 @@ final public class VisualizationCaches { " ?person core:relatedBy ?authorship .\n" + " ?authorship a core:Authorship .\n" + " ?authorship core:relates ?document .\n" + - " ?document a bibo:Document .\n" + + " ?document a .\n" + "}\n"; - final Map> map = new HashMap>(); + final PersonPublicationMaps map = new PersonPublicationMaps(); rdfService.sparqlSelectQuery(query, new ResultSetConsumer() { @Override @@ -289,15 +315,9 @@ final public class VisualizationCaches { if (person != null && document != null) { String personURI = person.getURI(); + String documentURI = document.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()); - } + map.put(personURI, documentURI); } } }); @@ -319,7 +339,7 @@ final public class VisualizationCaches { "SELECT ?document ?journalLabel\n" + "WHERE\n" + "{\n" + - " ?document a bibo:Document .\n" + + " ?document a .\n" + " ?document core:hasPublicationVenue ?journal . \n" + " ?journal rdfs:label ?journalLabel . \n" + "}\n"; @@ -353,7 +373,7 @@ final public class VisualizationCaches { "SELECT ?document ?publicationDate\n" + "WHERE\n" + "{\n" + - " ?document a bibo:Document .\n" + + " ?document a .\n" + " ?document core:dateTimeValue ?dateTimeValue . \n" + " ?dateTimeValue core:dateTime ?publicationDate . \n" + "}\n"; diff --git a/themes/wilma/i18n/all.properties b/themes/wilma/i18n/all.properties index 0ee83c08..4c592aee 100644 --- a/themes/wilma/i18n/all.properties +++ b/themes/wilma/i18n/all.properties @@ -885,3 +885,5 @@ researcher_role = Researcher Role search_service_btn = Search Service through_today = Publications through today's date doi_link=Digital Object Identifier (DOI) + +using_cache_time=Using information cached at