Cumulative publications graph

This commit is contained in:
Graham Triggs 2017-04-07 15:25:48 -06:00
parent 5449579543
commit 9e2f8439ef
5 changed files with 275 additions and 16 deletions

View file

@ -0,0 +1,144 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.personpubcount;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
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.visutils.QueryRunner;
import edu.cornell.mannlib.vitro.webapp.visualization.visutils.VisualizationRequestHandler;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.jena.query.Dataset;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class CumulativeCountRequestHandler implements VisualizationRequestHandler {
@Override
public AuthorizationRequest getRequiredPrivileges() {
return null;
}
@Override
public ResponseValues generateStandardVisualization(VitroRequest vitroRequest, Log log, Dataset dataSource) throws MalformedQueryParametersException {
return null;
}
@Override
public ResponseValues generateVisualizationForShortURLRequests(Map<String, String> parameters, VitroRequest vitroRequest, Log log, Dataset dataSource) throws MalformedQueryParametersException {
return null;
}
@Override
public Object generateAjaxVisualization(VitroRequest vitroRequest, Log log, Dataset dataSource) throws MalformedQueryParametersException {
String personURI = vitroRequest.getParameter("uri");
QueryRunner<Set<Activity>> queryManager = new PersonPublicationCountQueryRunner(
personURI,
vitroRequest.getRDFService(),
log);
Set<Activity> authorDocuments = queryManager.getQueryResult();
Map<Integer, Map<String, Integer>> yearToTypeCount = new TreeMap<Integer, Map<String, Integer>>();
for (Activity currentActivity : authorDocuments) {
String activityYearStr = currentActivity.getParsedActivityYear();
Integer activityYear;
try {
activityYear = Integer.parseInt(activityYearStr, 10);
} catch (NumberFormatException nfe) {
activityYear = 0;
}
Map<String, Integer> typeCounts;
if (yearToTypeCount.containsKey(activityYear)) {
typeCounts = yearToTypeCount.get(activityYear);
} else {
typeCounts = new TreeMap<String, Integer>();
yearToTypeCount.put(activityYear, typeCounts);
}
String activityType = currentActivity.getActivityType();
if (StringUtils.isEmpty(activityType)) {
activityType = "http://purl.org/ontology/bibo/Document";
}
if (typeCounts.containsKey(activityType)) {
typeCounts.put(activityType, typeCounts.get(activityType) + 1);
} else {
typeCounts.put(activityType, 1);
}
}
StringBuilder csv = new StringBuilder();
csv.append("Year,Previous,Other,Books,Articles\n");
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
List<Integer> years = new ArrayList<Integer>(yearToTypeCount.keySet());
for (int year = currentYear - 9; year < currentYear + 1; year++) {
if (!years.contains(year)) {
years.add(year);
}
}
Collections.sort(years);
int publicationCount = 0;
for (Integer year : years) {
if (year < currentYear - 9) {
if (yearToTypeCount.containsKey(year)) {
Map<String, Integer> typeCounts = yearToTypeCount.get(year);
for (Map.Entry<String, Integer> entry : typeCounts.entrySet()) {
publicationCount += entry.getValue();
}
}
} else {
int articleCount = 0;
int bookCount = 0;
int otherCount = 0;
if (yearToTypeCount.containsKey(year)) {
Map<String, Integer> typeCounts = yearToTypeCount.get(year);
for (Map.Entry<String, Integer> entry : typeCounts.entrySet()) {
if ("http://purl.org/ontology/bibo/AcademicArticle".equalsIgnoreCase(entry.getKey()) ||
"http://purl.org/ontology/bibo/Article".equalsIgnoreCase(entry.getKey()) ) {
articleCount += entry.getValue();
} else if ("http://purl.org/ontology/bibo/Book".equalsIgnoreCase(entry.getKey()) ||
"http://purl.org/ontology/bibo/BookSection".equalsIgnoreCase(entry.getKey()) ||
"http://purl.org/ontology/bibo/Chapter".equalsIgnoreCase(entry.getKey()) ||
"http://purl.org/ontology/bibo/EditedBook".equalsIgnoreCase(entry.getKey()) ) {
bookCount += entry.getValue();
} else {
otherCount += entry.getValue();
}
}
}
csv.append(year).append(",")
.append(publicationCount).append(",")
.append(otherCount).append(",")
.append(bookCount).append(",")
.append(articleCount).append("\n");
publicationCount += articleCount + bookCount + otherCount;
}
}
return csv.toString();
}
@Override
public Map<String, String> generateDataVisualization(VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException {
return null;
}
}

View file

@ -75,6 +75,7 @@ public class PersonPublicationCountQueryRunner implements QueryRunner<Set<Activi
+ "{\n" + "{\n"
+ " <" + queryURI + "> rdfs:label ?authorName .\n" + " <" + queryURI + "> rdfs:label ?authorName .\n"
+ " <" + queryURI + "> core:authorOf ?document .\n" + " <" + queryURI + "> core:authorOf ?document .\n"
+ " ?document vitro:mostSpecificType ?publicationType .\n"
+ " ?document core:publicationDate ?publicationDate .\n" + " ?document core:publicationDate ?publicationDate .\n"
+ "}\n" + "}\n"
+ "WHERE" + "WHERE"
@ -86,7 +87,8 @@ public class PersonPublicationCountQueryRunner implements QueryRunner<Set<Activi
+ " ?authorshipNode rdf:type core:Authorship ; \n" + " ?authorshipNode rdf:type core:Authorship ; \n"
+ " core:relates ?document . \n" + " core:relates ?document . \n"
+ " ?document rdf:type bibo:Document ; \n" + " ?document rdf:type bibo:Document ; \n"
+ " rdfs:label ?documentLabel .\n" + " rdfs:label ?documentLabel ;\n"
+ " vitro:mostSpecificType ?publicationType .\n"
+ " } UNION {\n" + " } UNION {\n"
+ " <" + queryURI + "> rdf:type foaf:Person ;\n" + " <" + queryURI + "> rdf:type foaf:Person ;\n"
+ " core:relatedBy ?authorshipNode . \n" + " core:relatedBy ?authorshipNode . \n"
@ -106,9 +108,10 @@ public class PersonPublicationCountQueryRunner implements QueryRunner<Set<Activi
private String getSparqlQuery(String queryURI) { private String getSparqlQuery(String queryURI) {
String sparqlQuery = QueryConstants.getSparqlPrefixQuery() String sparqlQuery = QueryConstants.getSparqlPrefixQuery()
+ "SELECT ?document ?publicationDate\n" + "SELECT ?document ?publicationType ?publicationDate\n"
+ "WHERE { \n" + "WHERE { \n"
+ " <" + queryURI + "> core:authorOf ?document . \n" + " <" + queryURI + "> core:authorOf ?document . \n"
+ " OPTIONAL { ?document vitro:mostSpecificType ?publicationType . } .\n"
+ " OPTIONAL { ?document core:publicationDate ?publicationDate . } .\n" + " OPTIONAL { ?document core:publicationDate ?publicationDate . } .\n"
+ "}\n"; + "}\n";
@ -172,6 +175,11 @@ public class PersonPublicationCountQueryRunner implements QueryRunner<Set<Activi
biboDocument.setActivityDate(publicationDateNode.asLiteral().getString()); biboDocument.setActivityDate(publicationDateNode.asLiteral().getString());
} }
RDFNode publicationType = qs.get("publicationType");
if (publicationType != null) {
biboDocument.setActivityType(publicationType.asResource().getURI());
}
authorDocuments.add(biboDocument); authorDocuments.add(biboDocument);
} }

View file

@ -14,6 +14,7 @@ import edu.cornell.mannlib.vitro.webapp.visualization.visutils.UtilityFunctions;
public class Activity extends Individual { public class Activity extends Individual {
private String activityDate; private String activityDate;
private String activityType;
public Activity(String activityURI) { public Activity(String activityURI) {
super(activityURI); super(activityURI);
@ -31,6 +32,10 @@ public class Activity extends Individual {
this.setIndividualLabel(activityLabel); this.setIndividualLabel(activityLabel);
} }
public String getActivityType() { return this.activityType; }
public void setActivityType(String activityType) { this.activityType = activityType; }
/** /**
* This method will be called to get the final/inferred year for the publication. * This method will be called to get the final/inferred year for the publication.
* The 2 choices, in order, are, * The 2 choices, in order, are,

View file

@ -12,6 +12,9 @@
<bean id="capability_map" <bean id="capability_map"
class="edu.cornell.mannlib.vitro.webapp.visualization.capabilitymap.CapabilityMapRequestHandler" /> class="edu.cornell.mannlib.vitro.webapp.visualization.capabilitymap.CapabilityMapRequestHandler" />
<bean id="cumulative_pub_count"
class="edu.cornell.mannlib.vitro.webapp.visualization.personpubcount.CumulativeCountRequestHandler" />
<bean id="person_pub_count" <bean id="person_pub_count"
class="edu.cornell.mannlib.vitro.webapp.visualization.personpubcount.PersonPublicationCountRequestHandler" /> class="edu.cornell.mannlib.vitro.webapp.visualization.personpubcount.PersonPublicationCountRequestHandler" />
@ -53,6 +56,10 @@
<ref bean="capability_map"></ref> <ref bean="capability_map"></ref>
</entry> </entry>
<entry key="cumulative_pub_count">
<ref bean="cumulative_pub_count"></ref>
</entry>
<entry key="person_pub_count"> <entry key="person_pub_count">
<ref bean="person_pub_count"></ref> <ref bean="person_pub_count"></ref>
</entry> </entry>

View file

@ -18,16 +18,120 @@
<#assign standardVisualizationURLRoot ="/visualization"> <#assign standardVisualizationURLRoot ="/visualization">
<#if isAuthor> <#if isAuthor>
${scripts.add('<script type="text/javascript" src="${urls.base}/js/d3.min.js"></script>')}
<#assign coAuthorIcon = "${urls.images}/visualization/coauthorship/co_author_icon.png"> <#assign coAuthorIcon = "${urls.images}/visualization/coauthorship/co_author_icon.png">
<#assign mapOfScienceIcon = "${urls.images}/visualization/mapofscience/scimap_icon.png"> <#assign mapOfScienceIcon = "${urls.images}/visualization/mapofscience/scimap_icon.png">
<#assign coAuthorVisUrl = individual.coAuthorVisUrl()> <#assign coAuthorVisUrl = individual.coAuthorVisUrl()>
<#assign mapOfScienceVisUrl = individual.mapOfScienceUrl()> <#assign mapOfScienceVisUrl = individual.mapOfScienceUrl()>
<#assign googleJSAPI = "https://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%22imagesparkline%22%5D%7D%5D%7D"> <span id="publicationsHeading">${i18n().publications_in_vivo}</span>
<span id="sparklineHeading">${i18n().publications_in_vivo}</span> <svg width="360" height="200" id="publicationsChart" onload="renderPublicationsChart()">
</svg>
<div id="vis_container_coauthor">&nbsp;</div> <script>
var dataUrl = '${urls.base}/visualizationAjax?vis=cumulative_pub_count&uri=${individual.uri?url}';
function renderPublicationsChart() {
var svg = d3.select("#publicationsChart"),
margin = {top: 30, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.05)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#777777", "#1f77b4", "#aec7e8", "#ff7f0e"]);
d3.csv(dataUrl, function (d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function (error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
x.domain(data.map(function (d) {
return d.Year;
}));
y.domain([0, d3.max(data, function (d) {
return d.total;
})]).nice();
z.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function (d) {
return z(d.key);
})
.selectAll("rect")
.data(function (d) {
return d;
})
.enter().append("rect")
.attr("x", function (d) {
return x(d.data.Year);
})
.attr("y", function (d) {
return y(d[1]);
})
.attr("height", function (d) {
return y(d[0]) - y(d[1]);
})
.attr("width", x.bandwidth());
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000");
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice(1,4).reverse())
.enter().append("g")
.attr("transform", function (d, i) {
return "translate(-" + (200 - i * 80) +",-25)";
});
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function (d) {
return d;
});
});
}
</script>
<div id="coauthorship_link_container" class="collaboratorship-link-container"> <div id="coauthorship_link_container" class="collaboratorship-link-container">
<a href="${coAuthorVisUrl}" title="${i18n().co_author_network}" class="btn btn-info" role="button"> <a href="${coAuthorVisUrl}" title="${i18n().co_author_network}" class="btn btn-info" role="button">
@ -42,15 +146,6 @@
${i18n().map_of_science_capitalized} ${i18n().map_of_science_capitalized}
</a> </a>
</div> </div>
${scripts.add('<script type="text/javascript" src="${googleJSAPI}"></script>',
'<script type="text/javascript" src="${urls.base}/js/visualization/visualization-helper-functions.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/visualization/sparkline.js"></script>')}
<script type="text/javascript">
var visualizationUrl = '${urls.base}/visualizationAjax?uri=${individual.uri?url}&template=${visRequestingTemplate!}';
var infoIconSrc = '${urls.images}/iconInfo.png';
</script>
</#if> </#if>
<#if isInvestigator> <#if isInvestigator>