+
+
+
+
Visualization Testbed (Not to be seen by eventual end users)
+
+
+
+
+
Hello World!
+
+
+
+
+
+
+
+
+
vis query for person -> "Crane, Brian"
+
+
+
iioio
\ No newline at end of file
diff --git a/productMods/templates/freemarker/visualization/publicationCount.ftl b/productMods/templates/freemarker/visualization/publicationCount.ftl
new file mode 100644
index 00000000..ab78c65a
--- /dev/null
+++ b/productMods/templates/freemarker/visualization/publicationCount.ftl
@@ -0,0 +1,7 @@
+<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
+
+<#assign googleJSAPI = 'http://www.google.com/jsapi?autoload=%7B%22modules%22%3A%5B%7B%22name%22%3A%22visualization%22%2C%22version%22%3A%221%22%2C%22packages%22%3A%5B%22areachart%22%2C%22imagesparkline%22%5D%7D%5D%7D'>
+
+${headScripts.add(googleJSAPI)}
+
+<#include "/visualization/publicationSparklineContent.ftl">
\ No newline at end of file
diff --git a/productMods/templates/freemarker/visualization/publicationSparklineContent.ftl b/productMods/templates/freemarker/visualization/publicationSparklineContent.ftl
new file mode 100644
index 00000000..1285bfd0
--- /dev/null
+++ b/productMods/templates/freemarker/visualization/publicationSparklineContent.ftl
@@ -0,0 +1,247 @@
+<#assign visContainerID = '${sparklineVO.visContainerDivID}'>
+
+<#if sparklineVO.shortVisMode>
+ <#assign sparklineContainerID = 'pub_count_short_sparkline_vis'>
+<#else>
+ <#assign sparklineContainerID = 'pub_count_full_sparkline_vis'>
+#if>
+
+<#-- This is used to prevent collision between sparkline & visualization conatiner div ids. -->
+<#if visContainerID?upper_case == sparklineContainerID?upper_case>
+ <#assign sparklineContainerID = visContainerID + "_spark">
+#if>
+
+
+
+
+
+
+
+
+
+
+
+<#if sparklineVO.shortVisMode>
+
+
+ View all VIVO publications and corresponding co-author network.
+
+
+
+<#else>
+
+
+
+
+
+ Publications per year (.CSV File)
+
+
+
+
+ Year
+ |
+
+ Publications
+ |
+
+
+
+
+ <#list sparklineVO.yearToActivityCount?keys as year>
+
+
+ ${year}
+ |
+
+ ${sparklineVO.yearToActivityCount[year]}
+ |
+
+ #list>
+
+
+
+ Download data as
.csv file.
+
+
+
+#if>
+
+
\ No newline at end of file
diff --git a/productMods/templates/freemarker/visualization/sparklineAjaxVisContent.ftl b/productMods/templates/freemarker/visualization/sparklineAjaxVisContent.ftl
new file mode 100644
index 00000000..d7bbedb4
--- /dev/null
+++ b/productMods/templates/freemarker/visualization/sparklineAjaxVisContent.ftl
@@ -0,0 +1,5 @@
+<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
+
+<#if shouldVIVOrenderVis>
+ <#include "/visualization/publicationSparklineContent.ftl">
+#if>
diff --git a/productMods/templates/freemarker/visualization/vistest.ftl b/productMods/templates/freemarker/visualization/vistest.ftl
new file mode 100644
index 00000000..e69de29b
diff --git a/productMods/templates/freemarker/visualization/visualizationError.ftl b/productMods/templates/freemarker/visualization/visualizationError.ftl
new file mode 100644
index 00000000..00bd865f
--- /dev/null
+++ b/productMods/templates/freemarker/visualization/visualizationError.ftl
@@ -0,0 +1,8 @@
+<%-- $This file is distributed under the terms of the license in /doc/license.txt$ --%>
+
+
+
+${error}
+
+
+
diff --git a/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/VisualizationFrameworkConstants.java b/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/VisualizationFrameworkConstants.java
index b58af3ea..ef40004a 100644
--- a/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/VisualizationFrameworkConstants.java
+++ b/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/VisualizationFrameworkConstants.java
@@ -11,6 +11,14 @@ public class VisualizationFrameworkConstants {
*/
public static final String RELATIVE_LOCATION_OF_VISUALIZATIONS_BEAN =
"/WEB-INF/visualization/visualizations-beans-injection.xml";
+
+ /*
+ * Freemarker Version
+ * */
+ public static final String RELATIVE_LOCATION_OF_FM_VISUALIZATIONS_BEAN =
+ "/WEB-INF/visualization/visualizations-beans-injection-fm.xml";
+
+ public static final String ERROR_TEMPLATE = "/visualization/visualizationError.ftl";
/*
* Vis URL prefix that is seen by all the users
diff --git a/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/DummyVisClientController.java b/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/DummyVisClientController.java
new file mode 100644
index 00000000..e9c47da1
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/DummyVisClientController.java
@@ -0,0 +1,82 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.controller.visualization.freemarker;
+
+/*
+Copyright (c) 2010, Cornell University
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Cornell University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
+import edu.cornell.mannlib.vitro.webapp.beans.Portal;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
+
+/**
+ * Services a sparql query. This will return a simple error message and a 501 if
+ * there is no jena Model.
+ *
+ * @author bdc34
+ *
+ */
+public class DummyVisClientController extends FreemarkerHttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Log log = LogFactory.getLog(DummyVisClientController.class.getName());
+ private static final String TEMPLATE_DEFAULT = "/visualization/dummyVisClient.ftl";
+
+ @Override
+ protected int requiredLoginLevel() {
+ // User must be logged in to view this page.
+ return LoginStatusBean.EDITOR;
+ }
+
+ @Override
+ protected ResponseValues processRequest(VitroRequest vreq) {
+ Portal portal = vreq.getPortal();
+
+ Map
body = new HashMap();
+ body.put("portalBean", portal);
+ //body.put("scripts", "/templates/visualization/visualization_scripts.jsp");
+
+ return new TemplateResponseValues(TEMPLATE_DEFAULT, body);
+ }
+
+ @Override
+ protected String getTitle(String siteName) {
+ return "Dummy Visualization Controller";
+ }
+
+}
\ No newline at end of file
diff --git a/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/VisualizationController.java b/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/VisualizationController.java
new file mode 100644
index 00000000..88c1bf5e
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/VisualizationController.java
@@ -0,0 +1,215 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.controller.visualization.freemarker;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.hp.hpl.jena.query.DataSource;
+import com.hp.hpl.jena.query.DatasetFactory;
+import com.hp.hpl.jena.query.Syntax;
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelMaker;
+
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
+import edu.cornell.mannlib.vitro.webapp.controller.visualization.VisualizationFrameworkConstants;
+import edu.cornell.mannlib.vitro.webapp.visualization.constants.VisConstants;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.visutils.UtilityFunctions;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.visutils.VisualizationRequestHandler;
+import freemarker.template.Configuration;
+
+/**
+ * Services a visualization request. This will return a simple error message and a 501 if
+ * there is no jena Model.
+ *
+ * @author cdtank
+ */
+@SuppressWarnings("serial")
+public class VisualizationController extends FreemarkerHttpServlet {
+
+ private Map visualizationIDsToClass;
+
+ public static final String URL_ENCODING_SCHEME = "UTF-8";
+
+ private static final Log log = LogFactory.getLog(VisualizationController.class.getName());
+
+ protected static final Syntax SYNTAX = Syntax.syntaxARQ;
+
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ VitroRequest vreq = new VitroRequest(request);
+
+ String renderMode = vreq.getParameter(VisualizationFrameworkConstants
+ .RENDER_MODE_KEY);
+
+ if (StringUtils.equalsIgnoreCase(renderMode, VisualizationFrameworkConstants.DYNAMIC_RENDER_MODE)) {
+
+ System.out.println("inside doing ajax request that is dynamic response");
+
+ Configuration config = getConfig(vreq);
+ TemplateResponseValues trv = (TemplateResponseValues) processRequest(vreq);
+ writeTemplate(trv.getTemplateName(), trv.getMap(), config, request, response);
+
+ } else {
+ System.out.println("inside doing super.doGet that is normal response");
+ super.doGet(request, response);
+ }
+
+
+ }
+
+
+
+ /* This method is overridden to inject vis dependencies i.e. the vis algorithms that are
+ * being implemented into the vis controller. Modified Dependency Injection pattern is
+ * used here. XML file containing the location of all the vis is saved in accessible folder.
+ * @see javax.servlet.GenericServlet#init()
+ */
+ @Override
+ public void init() throws ServletException {
+ super.init();
+ try {
+
+ String resourcePath =
+ getServletContext()
+ .getRealPath(VisualizationFrameworkConstants
+ .RELATIVE_LOCATION_OF_FM_VISUALIZATIONS_BEAN);
+
+ ApplicationContext context = new ClassPathXmlApplicationContext(
+ "file:" + resourcePath);
+
+ BeanFactory factory = context;
+
+ VisualizationInjector visualizationInjector =
+ (VisualizationInjector) factory.getBean("visualizationInjector");
+
+ visualizationIDsToClass = visualizationInjector.getVisualizationIDToClass();
+
+ } catch (Exception e) {
+ log.error(e);
+ }
+ }
+
+
+
+ @Override
+ protected ResponseValues processRequest(VitroRequest vreq) {
+
+ /*
+ * Based on the query parameters passed via URI get the appropriate visualization
+ * request handler.
+ * */
+ VisualizationRequestHandler visRequestHandler =
+ getVisualizationRequestHandler(vreq);
+
+ if (visRequestHandler != null) {
+
+ /*
+ * Pass the query to the selected visualization request handler & render the visualization.
+ * Since the visualization content is directly added to the response object we are side-
+ * effecting this method.
+ * */
+ return renderVisualization(vreq, visRequestHandler);
+
+ } else {
+
+ return UtilityFunctions.handleMalformedParameters("Visualization Query Error",
+ "Inappropriate query parameters were submitted.",
+ vreq);
+
+ }
+
+
+ }
+
+
+ private ResponseValues renderVisualization(VitroRequest vitroRequest,
+ VisualizationRequestHandler visRequestHandler) {
+
+ Model model = vitroRequest.getJenaOntModel(); // getModel()
+ if (model == null) {
+
+ String errorMessage = "This service is not supporeted by the current "
+ + "webapp configuration. A jena model is required in the "
+ + "servlet context.";
+
+ log.error(errorMessage);
+
+ return UtilityFunctions.handleMalformedParameters("Visualization Query Error",
+ errorMessage,
+ vitroRequest);
+
+ }
+
+ DataSource dataSource = setupJENADataSource(model, vitroRequest);
+
+ if (dataSource != null && visRequestHandler != null) {
+
+ return visRequestHandler.generateVisualization(vitroRequest,
+ log,
+ dataSource);
+
+ } else {
+
+ String errorMessage = "Data Model Empty &/or Inappropriate "
+ + "query parameters were submitted. ";
+
+ log.error(errorMessage);
+
+ return UtilityFunctions.handleMalformedParameters("Visualization Query Error",
+ errorMessage,
+ vitroRequest);
+
+
+ }
+ }
+
+ private VisualizationRequestHandler getVisualizationRequestHandler(
+ VitroRequest vitroRequest) {
+
+ String visType = vitroRequest.getParameter(VisualizationFrameworkConstants
+ .VIS_TYPE_KEY);
+ VisualizationRequestHandler visRequestHandler = null;
+
+ System.out.println(visType + " --> " + visualizationIDsToClass);
+
+ try {
+ visRequestHandler = visualizationIDsToClass.get(visType);
+ } catch (NullPointerException nullKeyException) {
+
+ return null;
+ }
+
+ return visRequestHandler;
+ }
+
+ private DataSource setupJENADataSource(Model model, VitroRequest vreq) {
+
+ log.debug("rdfResultFormat was: " + VisConstants.RDF_RESULT_FORMAT_PARAM);
+
+ DataSource dataSource = DatasetFactory.create();
+ ModelMaker maker = (ModelMaker) getServletContext().getAttribute("vitroJenaModelMaker");
+
+ dataSource.setDefaultModel(model);
+
+ return dataSource;
+ }
+
+}
+
diff --git a/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/VisualizationInjector.java b/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/VisualizationInjector.java
new file mode 100644
index 00000000..03a60bf6
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/controller/visualization/freemarker/VisualizationInjector.java
@@ -0,0 +1,22 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+package edu.cornell.mannlib.vitro.webapp.controller.visualization.freemarker;
+
+import java.util.Map;
+
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.visutils.VisualizationRequestHandler;
+
+public class VisualizationInjector {
+ private Map visualizationIDToClass;
+
+ public Map getVisualizationIDToClass() {
+
+ System.out.println("giving the vis id class " + visualizationIDToClass);
+ return visualizationIDToClass;
+ }
+
+ public void setVisualizations(Map visualizationIDToClass) {
+ this.visualizationIDToClass = visualizationIDToClass;
+ System.out.println("setting the vis id class " + visualizationIDToClass);
+ }
+
+}
diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountQueryRunner.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountQueryRunner.java
new file mode 100644
index 00000000..ca25837f
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountQueryRunner.java
@@ -0,0 +1,220 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.visualization.freemarker.personpubcount;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+
+import com.hp.hpl.jena.iri.IRI;
+import com.hp.hpl.jena.iri.IRIFactory;
+import com.hp.hpl.jena.iri.Violation;
+import com.hp.hpl.jena.query.DataSource;
+import com.hp.hpl.jena.query.Query;
+import com.hp.hpl.jena.query.QueryExecution;
+import com.hp.hpl.jena.query.QueryExecutionFactory;
+import com.hp.hpl.jena.query.QueryFactory;
+import com.hp.hpl.jena.query.QuerySolution;
+import com.hp.hpl.jena.query.ResultSet;
+import com.hp.hpl.jena.query.Syntax;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+
+import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants;
+import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryFieldLabels;
+import edu.cornell.mannlib.vitro.webapp.visualization.exceptions.MalformedQueryParametersException;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects.BiboDocument;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects.Individual;
+import edu.cornell.mannlib.vitro.webapp.visualization.visutils.QueryRunner;
+
+
+
+/**
+ * This query runner is used to execute a sparql query that will fetch all the publications
+ * defined by bibo:Document property for a particular individual.
+ *
+ * @author cdtank
+ */
+public class PersonPublicationCountQueryRunner implements QueryRunner> {
+
+ protected static final Syntax SYNTAX = Syntax.syntaxARQ;
+
+ private String personURI;
+ private DataSource dataSource;
+
+ private Individual author;
+
+ public Individual getAuthor() {
+ return author;
+ }
+
+ private Log log;
+
+ private static final String SPARQL_QUERY_COMMON_SELECT_CLAUSE = ""
+ + "SELECT (str(?authorLabel) as ?authorLabelLit) "
+ + " (str(?document) as ?documentLit) "
+ + " (str(?documentMoniker) as ?documentMonikerLit) "
+ + " (str(?documentLabel) as ?documentLabelLit) "
+ + " (str(?documentBlurb) as ?documentBlurbLit) "
+ + " (str(?publicationYear) as ?publicationYearLit) "
+ + " (str(?publicationYearMonth) as ?publicationYearMonthLit) "
+ + " (str(?publicationDate) as ?publicationDateLit) "
+ + " (str(?documentDescription) as ?documentDescriptionLit) ";
+
+ private static final String SPARQL_QUERY_COMMON_WHERE_CLAUSE = ""
+ + "?document rdfs:label ?documentLabel ."
+ + "OPTIONAL { ?document core:year ?publicationYear } ."
+ + "OPTIONAL { ?document core:yearMonth ?publicationYearMonth } ."
+ + "OPTIONAL { ?document core:date ?publicationDate } ."
+ + "OPTIONAL { ?document vitro:moniker ?documentMoniker } ."
+ + "OPTIONAL { ?document vitro:blurb ?documentBlurb } ."
+ + "OPTIONAL { ?document vitro:description ?documentDescription }";
+
+ public PersonPublicationCountQueryRunner(String personURI,
+ DataSource dataSource, Log log) {
+
+ this.personURI = personURI;
+ this.dataSource = dataSource;
+ this.log = log;
+
+ }
+
+ private Set createJavaValueObjects(ResultSet resultSet) {
+ Set authorDocuments = new HashSet();
+
+ while (resultSet.hasNext()) {
+ QuerySolution solution = resultSet.nextSolution();
+
+ BiboDocument biboDocument = new BiboDocument(
+ solution.get(QueryFieldLabels.DOCUMENT_URL)
+ .toString());
+
+ RDFNode documentLabelNode = solution.get(QueryFieldLabels.DOCUMENT_LABEL);
+ if (documentLabelNode != null) {
+ biboDocument.setDocumentLabel(documentLabelNode.toString());
+ }
+
+
+ RDFNode documentBlurbNode = solution.get(QueryFieldLabels.DOCUMENT_BLURB);
+ if (documentBlurbNode != null) {
+ biboDocument.setDocumentBlurb(documentBlurbNode.toString());
+ }
+
+ RDFNode documentmonikerNode = solution.get(QueryFieldLabels.DOCUMENT_MONIKER);
+ if (documentmonikerNode != null) {
+ biboDocument.setDocumentMoniker(documentmonikerNode.toString());
+ }
+
+ RDFNode documentDescriptionNode = solution.get(QueryFieldLabels.DOCUMENT_DESCRIPTION);
+ if (documentDescriptionNode != null) {
+ biboDocument.setDocumentDescription(documentDescriptionNode.toString());
+ }
+
+ RDFNode publicationYearNode = solution.get(QueryFieldLabels.DOCUMENT_PUBLICATION_YEAR);
+ if (publicationYearNode != null) {
+ biboDocument.setPublicationYear(publicationYearNode.toString());
+ }
+
+ RDFNode publicationYearMonthNode = solution.get(
+ QueryFieldLabels
+ .DOCUMENT_PUBLICATION_YEAR_MONTH);
+ if (publicationYearMonthNode != null) {
+ biboDocument.setPublicationYearMonth(publicationYearMonthNode.toString());
+ }
+
+ RDFNode publicationDateNode = solution.get(QueryFieldLabels.DOCUMENT_PUBLICATION_DATE);
+ if (publicationDateNode != null) {
+ biboDocument.setPublicationDate(publicationDateNode.toString());
+ }
+
+ /*
+ * Since we are getting publication count for just one author at a time we need
+ * to create only one "Individual" instance. We test against the null for "author" to
+ * make sure that it has not already been instantiated.
+ * */
+ RDFNode authorURLNode = solution.get(QueryFieldLabels.AUTHOR_URL);
+ if (authorURLNode != null && author == null) {
+ author = new Individual(authorURLNode.toString());
+ RDFNode authorLabelNode = solution.get(QueryFieldLabels.AUTHOR_LABEL);
+ if (authorLabelNode != null) {
+ author.setIndividualLabel(authorLabelNode.toString());
+ }
+ }
+
+ authorDocuments.add(biboDocument);
+ }
+ return authorDocuments;
+ }
+
+ private ResultSet executeQuery(String queryURI,
+ DataSource dataSource) {
+
+ QueryExecution queryExecution = null;
+// try {
+ Query query = QueryFactory.create(getSparqlQuery(queryURI), SYNTAX);
+
+// QuerySolutionMap qs = new QuerySolutionMap();
+// qs.add("authPerson", queryParam); // bind resource to s
+
+ queryExecution = QueryExecutionFactory.create(query, dataSource);
+
+
+// if (query.isSelectType()) {
+ return queryExecution.execSelect();
+// }
+// } finally {
+// if (queryExecution != null) {
+// queryExecution.close();
+// }
+// }
+// return null;
+ }
+
+ private String getSparqlQuery(String queryURI) {
+// Resource uri1 = ResourceFactory.createResource(queryURI);
+
+ String sparqlQuery = QueryConstants.getSparqlPrefixQuery()
+ + SPARQL_QUERY_COMMON_SELECT_CLAUSE
+ + "(str(<" + queryURI + ">) as ?authPersonLit) "
+ + "WHERE { "
+ + "<" + queryURI + "> rdf:type foaf:Person ;"
+ + " rdfs:label ?authorLabel ;"
+ + " core:authorInAuthorship ?authorshipNode . "
+ + " ?authorshipNode rdf:type core:Authorship ;"
+ + " core:linkedInformationResource ?document . "
+ + SPARQL_QUERY_COMMON_WHERE_CLAUSE
+ + "}";
+
+// System.out.println("SPARQL query for person pub count -> \n" + sparqlQuery);
+ return sparqlQuery;
+ }
+
+ public Set getQueryResult()
+ throws MalformedQueryParametersException {
+
+ if (StringUtils.isNotBlank(this.personURI)) {
+
+ /*
+ * To test for the validity of the URI submitted.
+ * */
+ IRIFactory iRIFactory = IRIFactory.jenaImplementation();
+ IRI iri = iRIFactory.create(this.personURI);
+ if (iri.hasViolation(false)) {
+ String errorMsg = ((Violation) iri.violations(false).next()).getShortMessage();
+ log.error("Pub Count vis Query " + errorMsg);
+ throw new MalformedQueryParametersException(
+ "URI provided for an individual is malformed.");
+ }
+
+ } else {
+ throw new MalformedQueryParametersException("URL parameter is either null or empty.");
+ }
+
+ ResultSet resultSet = executeQuery(this.personURI,
+ this.dataSource);
+
+ return createJavaValueObjects(resultSet);
+ }
+
+}
diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountRequestHandler.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountRequestHandler.java
new file mode 100644
index 00000000..cd381d58
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountRequestHandler.java
@@ -0,0 +1,342 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.visualization.freemarker.personpubcount;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+
+import com.hp.hpl.jena.query.DataSource;
+import com.itextpdf.text.Document;
+import com.itextpdf.text.DocumentException;
+import com.itextpdf.text.pdf.PdfWriter;
+
+import edu.cornell.mannlib.vitro.webapp.beans.Portal;
+import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.FileResponseValues;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
+import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
+import edu.cornell.mannlib.vitro.webapp.controller.visualization.VisualizationFrameworkConstants;
+
+import edu.cornell.mannlib.vitro.webapp.visualization.exceptions.MalformedQueryParametersException;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.visutils.UtilityFunctions;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.visutils.VisualizationRequestHandler;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects.BiboDocument;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects.Individual;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects.SparklineData;
+import edu.cornell.mannlib.vitro.webapp.visualization.visutils.PDFDocument;
+import edu.cornell.mannlib.vitro.webapp.visualization.visutils.QueryRunner;
+import edu.cornell.mannlib.vitro.webapp.web.ContentType;
+
+/**
+ *
+ * This request handler is used to serve the content related to an individual's
+ * publications over the years like,
+ * 1. Sprakline representing this
+ * 2. An entire page dedicated to the sparkline vis which will also have links to
+ * download the data using which the sparkline was rendered & its tabular representation etc.
+ * 3. Downloadable CSV file containing number of publications over the years.
+ * 4. Downloadable PDf file containing the publications content, among other things.
+ * Currently this is disabled because the feature is half-baked. We plan to activate this in
+ * the next major release.
+ *
+ * @author cdtank
+ */
+public class PersonPublicationCountRequestHandler implements VisualizationRequestHandler {
+
+ public ResponseValues generateVisualization(VitroRequest vitroRequest,
+ Log log,
+ DataSource dataSource) {
+
+ String peronURI = vitroRequest.getParameter(
+ VisualizationFrameworkConstants
+ .INDIVIDUAL_URI_KEY);
+
+ String renderMode = vitroRequest.getParameter(
+ VisualizationFrameworkConstants
+ .RENDER_MODE_KEY);
+
+ String visMode = vitroRequest.getParameter(
+ VisualizationFrameworkConstants
+ .VIS_MODE_KEY);
+
+ String visContainer = vitroRequest.getParameter(
+ VisualizationFrameworkConstants
+ .VIS_CONTAINER_KEY);
+
+ QueryRunner> queryManager =
+ new PersonPublicationCountQueryRunner(peronURI, dataSource, log);
+
+ try {
+ Set authorDocuments = queryManager.getQueryResult();
+
+ /*
+ * Create a map from the year to number of publications. Use the BiboDocument's
+ * parsedPublicationYear to populate the data.
+ * */
+ Map yearToPublicationCount =
+ UtilityFunctions.getYearToPublicationCount(authorDocuments);
+
+ Individual author = ((PersonPublicationCountQueryRunner) queryManager).getAuthor();
+
+ if (VisualizationFrameworkConstants.DATA_RENDER_MODE
+ .equalsIgnoreCase(renderMode)) {
+
+ return prepareDataResponse(author,
+ authorDocuments,
+ yearToPublicationCount);
+ }
+
+
+ /*
+ * For now we are disabling the capability to render pdf file.
+ * */
+ /*
+ if (VisualizationFrameworkConstants.PDF_RENDER_MODE
+ .equalsIgnoreCase(renderMode)) {
+
+ preparePDFResponse(author,
+ authorDocuments,
+ yearToPublicationCount,
+ response);
+ return;
+ }
+ */
+
+ /*
+ * Computations required to generate HTML for the sparkline & related context.
+ * */
+ PersonPublicationCountVisCodeGenerator visualizationCodeGenerator =
+ new PersonPublicationCountVisCodeGenerator(vitroRequest.getContextPath(),
+ peronURI,
+ visMode,
+ visContainer,
+ authorDocuments,
+ yearToPublicationCount,
+ log);
+
+ SparklineData sparklineData = visualizationCodeGenerator
+ .getValueObjectContainer();
+
+ /*
+ * This is side-effecting because the response of this method is just to redirect to
+ * a page with visualization on it.
+ * */
+ RequestDispatcher requestDispatcher = null;
+
+ if (VisualizationFrameworkConstants.DYNAMIC_RENDER_MODE
+ .equalsIgnoreCase(renderMode)) {
+
+ return prepareDynamicResponse(vitroRequest,
+ sparklineData,
+ yearToPublicationCount);
+
+// requestDispatcher = request.getRequestDispatcher("/templates/page/blankPage.jsp");
+
+ } else {
+ return prepareStandaloneResponse(vitroRequest,
+ sparklineData);
+ }
+
+ } catch (MalformedQueryParametersException e) {
+ return UtilityFunctions.handleMalformedParameters(
+ "Visualization Query Error - Individual Publication Count",
+ e.getMessage(),
+ vitroRequest);
+ }
+ }
+
+ private String getPublicationsOverTimeCSVContent(Map yearToPublicationCount) {
+
+ StringBuilder csvFileContent = new StringBuilder();
+
+ csvFileContent.append("Year, Publications\n");
+
+ for (Entry currentEntry : yearToPublicationCount.entrySet()) {
+ csvFileContent.append(StringEscapeUtils.escapeCsv(currentEntry.getKey()));
+ csvFileContent.append(",");
+ csvFileContent.append(currentEntry.getValue());
+ csvFileContent.append("\n");
+ }
+
+ return csvFileContent.toString();
+ }
+
+ /**
+ * Provides response when csv file containing the publication count over the years
+ * is requested.
+ * @param author
+ * @param authorDocuments
+ * @param yearToPublicationCount
+ * @return
+ */
+ private FileResponseValues prepareDataResponse(
+ Individual author,
+ Set authorDocuments,
+ Map yearToPublicationCount) {
+
+
+ String authorName = null;
+
+ /*
+ * To protect against cases where there are no author documents associated with the
+ * individual.
+ * */
+ if (authorDocuments.size() > 0) {
+ authorName = author.getIndividualLabel();
+ }
+
+ /*
+ * To make sure that null/empty records for author names do not cause any mischief.
+ * */
+ if (StringUtils.isBlank(authorName)) {
+ authorName = "no-author";
+ }
+
+ String outputFileName = UtilityFunctions.slugify(authorName)
+ + "_publications-per-year" + ".csv";
+
+
+ Map fileContents = new HashMap();
+ fileContents.put("fileContent", getPublicationsOverTimeCSVContent(yearToPublicationCount));
+
+ return new FileResponseValues(new ContentType(), outputFileName, fileContents);
+ }
+
+ /**
+ * Provides response when an entire page dedicated to publication sparkline is requested.
+ * @param vreq
+ * @param valueObjectContainer
+ * @return
+ */
+ private TemplateResponseValues prepareStandaloneResponse(VitroRequest vreq,
+ SparklineData valueObjectContainer) {
+
+ Portal portal = vreq.getPortal();
+
+ String standaloneTemplate = "/visualization/publicationCount.ftl";
+
+ Map body = new HashMap();
+ body.put("portalBean", portal);
+ body.put("title", "Individual Publication Count visualization");
+ body.put("sparklineVO", valueObjectContainer);
+
+ /*
+ * DO NOT DO THIS HERE. Set stylesheets/scripts in the *.ftl instead using $(scripts.add)
+ * */
+// body.put("scripts", "/templates/visualization/visualization_scripts.jsp");
+
+ return new TemplateResponseValues(standaloneTemplate, body);
+
+ }
+
+ /**
+ * Provides response when the publication sparkline has to be rendered in already existing
+ * page, e.g. profile page.
+ * @param vreq
+ * @param valueObjectContainer
+ * @param yearToPublicationCount
+ * @return
+ */
+ private TemplateResponseValues prepareDynamicResponse(
+ VitroRequest vreq,
+ SparklineData valueObjectContainer,
+ Map yearToPublicationCount) {
+
+ Portal portal = vreq.getPortal();
+
+
+ String dynamicTemplate = "/visualization/sparklineAjaxVisContent.ftl";
+
+ Map body = new HashMap();
+ body.put("portalBean", portal);
+ body.put("sparklineVO", valueObjectContainer);
+
+ /*
+ * DO NOT DO THIS HERE. Set stylesheets/scripts in the *.ftl instead using $(scripts.add)
+ * */
+// body.put("scripts", "/templates/visualization/visualization_scripts.jsp");
+
+ if (yearToPublicationCount.size() > 0) {
+ body.put("shouldVIVOrenderVis", true);
+ } else {
+ body.put("shouldVIVOrenderVis", false);
+ }
+
+ return new TemplateResponseValues(dynamicTemplate, body);
+
+ }
+
+ private void preparePDFResponse(
+ Individual author,
+ Set authorDocuments,
+ Map yearToPublicationCount,
+ HttpServletResponse response) {
+
+ String authorName = null;
+
+ // To protect against cases where there are no author documents associated with the
+// / individual.
+ if (authorDocuments.size() > 0) {
+ authorName = author.getIndividualLabel();
+ }
+
+ //To make sure that null/empty records for author names do not cause any mischief.
+ if (StringUtils.isBlank(authorName)) {
+ authorName = "no-author";
+ }
+
+ String outputFileName = UtilityFunctions.slugify(authorName) + "_report" + ".pdf";
+
+ response.setContentType("application/pdf");
+ response.setHeader("Content-Disposition", "attachment;filename=" + outputFileName);
+
+ ServletOutputStream responseOutputStream;
+ try {
+ responseOutputStream = response.getOutputStream();
+
+
+ Document document = new Document();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PdfWriter pdfWriter = PdfWriter.getInstance(document, baos);
+ document.open();
+
+ PDFDocument pdfDocument = new PDFDocument(authorName,
+ yearToPublicationCount,
+ document,
+ pdfWriter);
+
+ document.close();
+
+ // setting some response headers & content type
+ response.setHeader("Expires", "0");
+ response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
+ response.setHeader("Pragma", "public");
+ response.setContentLength(baos.size());
+ // write ByteArrayOutputStream to the ServletOutputStream
+ baos.writeTo(responseOutputStream);
+ responseOutputStream.flush();
+ responseOutputStream.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (DocumentException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+
+
+}
diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountVisCodeGenerator.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountVisCodeGenerator.java
new file mode 100644
index 00000000..4abd587d
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/personpubcount/PersonPublicationCountVisCodeGenerator.java
@@ -0,0 +1,676 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.visualization.freemarker.personpubcount;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.apache.commons.logging.Log;
+
+import edu.cornell.mannlib.vitro.webapp.controller.visualization.freemarker.VisualizationController;
+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.freemarker.valueobjects.BiboDocument;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects.SparklineData;
+import edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects.YearPublicationCountDataElement;
+
+
+@SuppressWarnings("serial")
+public class PersonPublicationCountVisCodeGenerator {
+
+ /*
+ * There are 2 modes of sparkline that are available via this visualization.
+ * 1. Short Sparkline - This sparkline will render all the data points (or sparks),
+ * which in this case are the publications over the years, from the last 10 years.
+ *
+ * 2. Full Sparkline - This sparkline will render all the data points (or sparks)
+ * spanning the career of the person & last 10 years at the minimum, in case if
+ * the person started his career in the last 10 yeras.
+ * */
+ private static final Map VIS_DIV_NAMES = new HashMap() { {
+
+ put("SHORT_SPARK", "pub_count_short_sparkline_vis");
+ put("FULL_SPARK", "pub_count_full_sparkline_vis");
+
+ } };
+
+ private static final String VISUALIZATION_STYLE_CLASS = "sparkline_style";
+
+ private static final String DEFAULT_VIS_CONTAINER_DIV_ID = "pub_count_vis_container";
+
+ private Map yearToPublicationCount;
+
+ private Log log;
+
+ private SparklineData sparklineData;
+
+ private String contextPath;
+
+ private String individualURI;
+
+ public PersonPublicationCountVisCodeGenerator(String contextPath,
+ String individualURIParam,
+ String visMode,
+ String visContainer,
+ Set authorDocuments,
+ Map yearToPublicationCount,
+ Log log) {
+
+ this.contextPath = contextPath;
+ this.individualURI = individualURIParam;
+
+ this.yearToPublicationCount = yearToPublicationCount;
+ this.sparklineData = new SparklineData();
+
+ sparklineData.setYearToActivityCount(yearToPublicationCount);
+
+
+ this.log = log;
+
+ generateVisualizationCode(visMode, visContainer, authorDocuments);
+ }
+
+ /**
+ * This method is used to generate the visualization code (HMTL, CSS & JavaScript).
+ * There 2 parts to it - 1. Actual Content Code & 2. Context Code.
+ * 1. Actual Content code in this case is the sparkline image, text related to
+ * data and the wrapping tables. This is generated via call to google vis API through
+ * JavaScript.
+ * 2. Context code is generally optional but contains code pertaining to tabulated
+ * data & links to download files etc.
+ * @param visMode
+ * @param visContainer
+ * @param authorDocuments
+ */
+ private void generateVisualizationCode(String visMode,
+ String visContainer,
+ Set authorDocuments) {
+
+ sparklineData.setSparklineContent(getMainVisualizationCode(authorDocuments,
+ visMode,
+ visContainer));
+
+
+ sparklineData.setSparklineContext(getVisualizationContextCode(visMode));
+
+ }
+
+ private String getMainVisualizationCode(Set authorDocuments,
+ String visMode,
+ String providedVisContainerID) {
+
+ int numOfYearsToBeRendered = 0;
+ int currentYear = Calendar.getInstance().get(Calendar.YEAR);
+ int shortSparkMinYear = currentYear
+ - VisConstants.MINIMUM_YEARS_CONSIDERED_FOR_SPARKLINE
+ + 1;
+
+ /*
+ * This is required because when deciding the range of years over which the vis
+ * was rendered we dont want to be influenced by the "DEFAULT_PUBLICATION_YEAR".
+ * */
+ Set publishedYears = new HashSet(yearToPublicationCount.keySet());
+ publishedYears.remove(VOConstants.DEFAULT_PUBLICATION_YEAR);
+
+ /*
+ * We are setting the default value of minPublishedYear to be 10 years before
+ * the current year (which is suitably represented by the shortSparkMinYear),
+ * this in case we run into invalid set of published years.
+ * */
+ int minPublishedYear = shortSparkMinYear;
+
+ String visContainerID = null;
+
+ StringBuilder visualizationCode = new StringBuilder();
+
+ if (yearToPublicationCount.size() > 0) {
+ try {
+ minPublishedYear = Integer.parseInt(Collections.min(publishedYears));
+ } catch (NoSuchElementException e1) {
+ log.debug("vis: " + e1.getMessage() + " error occurred for "
+ + yearToPublicationCount.toString());
+ } catch (NumberFormatException e2) {
+ log.debug("vis: " + e2.getMessage() + " error occurred for "
+ + yearToPublicationCount.toString());
+ }
+ }
+
+ int minPubYearConsidered = 0;
+
+ /*
+ * There might be a case that the author has made his first publication within the
+ * last 10 years but we want to make sure that the sparkline is representative of
+ * at least the last 10 years, so we will set the minPubYearConsidered to
+ * "currentYear - 10" which is also given by "shortSparkMinYear".
+ * */
+ if (minPublishedYear > shortSparkMinYear) {
+ minPubYearConsidered = shortSparkMinYear;
+ } else {
+ minPubYearConsidered = minPublishedYear;
+ }
+
+ numOfYearsToBeRendered = currentYear - minPubYearConsidered + 1;
+
+ sparklineData.setNumOfYearsToBeRendered(numOfYearsToBeRendered);
+
+ visualizationCode.append("\n");
+
+ visualizationCode.append("\n";
+ }
+
+ private String getVisualizationContextCode(String visMode) {
+
+ String visualizationContextCode = "";
+ if (VisualizationFrameworkConstants.SHORT_SPARKLINE_VIS_MODE.equalsIgnoreCase(visMode)) {
+ visualizationContextCode = generateShortVisContext();
+ } else {
+ visualizationContextCode = generateFullVisContext();
+ }
+
+ log.debug(visualizationContextCode);
+
+ return visualizationContextCode;
+ }
+
+ private String generateFullVisContext() {
+
+ StringBuilder divContextCode = new StringBuilder();
+
+ String csvDownloadURLHref = "";
+
+ if (yearToPublicationCount.size() > 0) {
+
+ try {
+ if (getCSVDownloadURL() != null) {
+
+ csvDownloadURLHref = "Download data as .csv file.
";
+ sparklineData.setDownloadDataLink(getCSVDownloadURL());
+
+ } else {
+ csvDownloadURLHref = "";
+ }
+
+ } catch (UnsupportedEncodingException e) {
+ csvDownloadURLHref = "";
+ }
+ } else {
+ csvDownloadURLHref = "No data available to export.
";
+ }
+
+ String tableCode = generateDataTable();
+
+ divContextCode.append("" + tableCode + csvDownloadURLHref + "
");
+
+ sparklineData.setTable(tableCode);
+
+ return divContextCode.toString();
+ }
+
+ private String getCSVDownloadURL()
+ throws UnsupportedEncodingException {
+
+ if (yearToPublicationCount.size() > 0) {
+
+ String secondaryContextPath = "";
+ if (!contextPath.contains(VisualizationFrameworkConstants.VISUALIZATION_URL_PREFIX)) {
+ secondaryContextPath = VisualizationFrameworkConstants.VISUALIZATION_URL_PREFIX;
+ }
+
+ String downloadURL = contextPath
+ + secondaryContextPath
+ + "?" + VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY
+ + "=" + URLEncoder.encode(individualURI,
+ VisualizationController.URL_ENCODING_SCHEME)
+ .toString()
+ + "&" + VisualizationFrameworkConstants.VIS_TYPE_KEY
+ + "=" + URLEncoder.encode(
+ VisualizationFrameworkConstants
+ .PERSON_PUBLICATION_COUNT_VIS,
+ VisualizationController.URL_ENCODING_SCHEME)
+ .toString()
+ + "&" + VisualizationFrameworkConstants.RENDER_MODE_KEY
+ + "=" + URLEncoder.encode(VisualizationFrameworkConstants
+ .DATA_RENDER_MODE,
+ VisualizationController.URL_ENCODING_SCHEME)
+ .toString();
+ return downloadURL;
+ } else {
+ return null;
+ }
+ }
+
+ private String generateShortVisContext() {
+
+ StringBuilder divContextCode = new StringBuilder();
+
+ try {
+
+ String fullTimelineLink;
+ if (yearToPublicationCount.size() > 0) {
+
+ String secondaryContextPath = "";
+ if (!contextPath.contains(VisualizationFrameworkConstants.VISUALIZATION_URL_PREFIX)) {
+ secondaryContextPath = VisualizationFrameworkConstants.VISUALIZATION_URL_PREFIX;
+ }
+
+ String fullTimelineNetworkURL = contextPath
+ + secondaryContextPath
+ + "?"
+ + VisualizationFrameworkConstants.INDIVIDUAL_URI_KEY
+ + "=" + URLEncoder.encode(individualURI,
+ VisualizationController.URL_ENCODING_SCHEME).toString()
+ + "&"
+ + VisualizationFrameworkConstants.VIS_TYPE_KEY
+ + "=" + URLEncoder.encode("person_level",
+ VisualizationController.URL_ENCODING_SCHEME).toString()
+ + "&"
+ + VisualizationFrameworkConstants.RENDER_MODE_KEY
+ + "=" + URLEncoder.encode(VisualizationFrameworkConstants
+ .STANDALONE_RENDER_MODE,
+ VisualizationController.URL_ENCODING_SCHEME).toString();
+
+ fullTimelineLink = "View all VIVO "
+ + "publications and corresponding co-author network.";
+
+ sparklineData.setFullTimelineNetworkLink(fullTimelineNetworkURL);
+
+ } else {
+ fullTimelineLink = "No data available to render full timeline.
";
+ }
+
+ divContextCode.append("" + fullTimelineLink + "");
+
+ } catch (UnsupportedEncodingException e) {
+ log.error(e);
+ }
+ return divContextCode.toString();
+ }
+
+ private String generateDataTable() {
+
+ String csvDownloadURLHref = "";
+
+ try {
+ if (getCSVDownloadURL() != null) {
+ csvDownloadURLHref = "(.CSV File)";
+ } else {
+ csvDownloadURLHref = "";
+ }
+ } catch (UnsupportedEncodingException e) {
+ csvDownloadURLHref = "";
+ }
+
+ StringBuilder dataTable = new StringBuilder();
+
+ dataTable.append(""
+ + "Publications per year " + csvDownloadURLHref + ""
+ + ""
+ + ""
+ + "Year | "
+ + "Publications | "
+ + "
"
+ + ""
+ + "");
+
+ for (Entry currentEntry : yearToPublicationCount.entrySet()) {
+ dataTable.append(""
+ + "" + currentEntry.getKey() + " | "
+ + "" + currentEntry.getValue() + " | "
+ + "
");
+ }
+
+ dataTable.append("\n
\n");
+
+ return dataTable.toString();
+ }
+
+ public SparklineData getValueObjectContainer() {
+ return sparklineData;
+ }
+}
diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/BiboDocument.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/BiboDocument.java
new file mode 100644
index 00000000..24cf27b8
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/BiboDocument.java
@@ -0,0 +1,183 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import edu.cornell.mannlib.vitro.webapp.visualization.constants.VOConstants;
+
+/**
+ * @author cdtank
+ *
+ */
+public class BiboDocument extends Individual {
+
+ private String documentMoniker;
+ private String documentBlurb;
+ private String documentDescription;
+ private String publicationYear;
+ private String publicationYearMonth;
+ private String publicationDate;
+ private String parsedPublicationYear = VOConstants.DEFAULT_PUBLICATION_YEAR;
+
+ public BiboDocument(String documentURL) {
+ super(documentURL);
+ }
+
+ public String getDocumentURL() {
+ return this.getIndividualURI();
+ }
+
+ public String getDocumentMoniker() {
+ return documentMoniker;
+ }
+
+ public void setDocumentMoniker(String documentMoniker) {
+ this.documentMoniker = documentMoniker;
+ }
+
+ public String getDocumentLabel() {
+ return this.getIndividualLabel();
+ }
+
+ public void setDocumentLabel(String documentLabel) {
+ this.setIndividualLabel(documentLabel);
+ }
+
+ public String getDocumentBlurb() {
+ return documentBlurb;
+ }
+
+ public void setDocumentBlurb(String documentBlurb) {
+ this.documentBlurb = documentBlurb;
+
+// if (documentBlurb != null) {
+// this.setParsedPublicationYear(parsePublicationYear(documentBlurb));
+// }
+ }
+
+ private String parsePublicationYear(String documentBlurb) {
+
+ /*
+ * This pattern will match all group of numbers which have only 4 digits
+ * delimited by the word boundary.
+ * */
+ String pattern = "(?= VOConstants.MINIMUM_PUBLICATION_YEAR) {
+ publishedYear = candidateYearInteger.toString();
+ }
+
+ }
+
+ return publishedYear;
+ }
+
+ public String getDocumentDescription() {
+ return documentDescription;
+ }
+ public void setDocumentDescription(String documentDescription) {
+ this.documentDescription = documentDescription;
+ }
+
+ /**
+ * This method will be called when there is no usable core:year value found
+ * for the bibo:Document. It will first check & parse core:yearMonth failing
+ * which it will try core:date
+ * @return
+ */
+ public String getParsedPublicationYear() {
+
+ /*
+ * We are assuming that core:yearMonth has "YYYY-MM" format. This is based
+ * off of http://www.w3.org/TR/xmlschema-2/#gYearMonth , which is what
+ * core:yearMonth points to internally.
+ * */
+ if (publicationYearMonth != null
+ && publicationYearMonth.length() >= VOConstants.NUM_CHARS_IN_YEAR_FORMAT
+ && isValidPublicationYear(publicationYearMonth.substring(
+ 0,
+ VOConstants.NUM_CHARS_IN_YEAR_FORMAT))) {
+
+ return publicationYearMonth.substring(0, VOConstants.NUM_CHARS_IN_YEAR_FORMAT);
+
+ }
+
+ if (publicationDate != null
+ && publicationDate.length() >= VOConstants.NUM_CHARS_IN_YEAR_FORMAT
+ && isValidPublicationYear(publicationDate
+ .substring(0,
+ VOConstants.NUM_CHARS_IN_YEAR_FORMAT))) {
+
+ return publicationDate.substring(0, VOConstants.NUM_CHARS_IN_YEAR_FORMAT);
+ }
+
+ /*
+ * If all else fails return default unknown year identifier
+ * */
+ return VOConstants.DEFAULT_PUBLICATION_YEAR;
+ }
+
+ /*
+ * This publicationYear value is directly from the data supported by the ontology.
+ * If this is empty only then use the parsedPublicationYear.
+ * */
+ public String getPublicationYear() {
+ if (publicationYear != null && isValidPublicationYear(publicationYear)) {
+ return publicationYear;
+ } else {
+ return null;
+ }
+
+ }
+
+ public void setPublicationYear(String publicationYear) {
+ this.publicationYear = publicationYear;
+ }
+
+ public String getPublicationYearMonth() {
+ return publicationYearMonth;
+ }
+
+ public void setPublicationYearMonth(String publicationYearMonth) {
+ this.publicationYearMonth = publicationYearMonth;
+ }
+
+ public String getPublicationDate() {
+ return publicationDate;
+ }
+
+ public void setPublicationDate(String publicationDate) {
+ this.publicationDate = publicationDate;
+ }
+
+ private boolean isValidPublicationYear(String testPublicationYear) {
+
+ if (testPublicationYear.length() != 0
+ && testPublicationYear.trim().length() == VOConstants.NUM_CHARS_IN_YEAR_FORMAT
+ && testPublicationYear.matches("\\d+")
+ && Integer.parseInt(testPublicationYear) >= VOConstants.MINIMUM_PUBLICATION_YEAR) {
+ return true;
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/Child.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/Child.java
new file mode 100644
index 00000000..3cce4c43
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/Child.java
@@ -0,0 +1,42 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+package edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects;
+
+import java.util.Set;
+import java.util.HashSet;
+/**
+ * @author bkoniden
+ * Deepak Konidena
+ *
+ */
+public class Child extends Individual {
+
+ Set documents = new HashSet();
+
+ public Child(String individualURI) {
+ super(individualURI);
+ }
+
+ public Set getDocuments() {
+ return documents;
+ }
+
+ public Child(String individualURI, String individualLabel) {
+ super(individualURI, individualLabel);
+ }
+
+ @Override
+ public boolean equals(Object other){
+ boolean result = false;
+ if (other instanceof Child){
+ Child person = (Child) other;
+ result = (this.getIndividualLabel().equals(person.getIndividualLabel())
+ && this.getIndividualURI().equals(person.getIndividualURI()));
+ }
+ return result;
+ }
+
+ @Override
+ public int hashCode(){
+ return(41*(getIndividualLabel().hashCode() + 41*(getIndividualURI().hashCode())));
+ }
+}
diff --git a/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/CoAuthorshipData.java b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/CoAuthorshipData.java
new file mode 100644
index 00000000..720856ed
--- /dev/null
+++ b/src/edu/cornell/mannlib/vitro/webapp/visualization/freemarker/valueobjects/CoAuthorshipData.java
@@ -0,0 +1,229 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.webapp.visualization.freemarker.valueobjects;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class CoAuthorshipData {
+
+ private Set nodes;
+ private Set edges;
+ private Node egoNode;
+ private Set