Skip to content

Instantly share code, notes, and snippets.

@rivaldi8
Last active July 31, 2017 07:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save rivaldi8/7a9f2483d5648e570d4c to your computer and use it in GitHub Desktop.
Save rivaldi8/7a9f2483d5648e570d4c to your computer and use it in GitHub Desktop.
/**
* Functions to add citation count or other statistics from several
* services (like Scopus) to the impact section of an item view.
*/
var IMPACT_SERVLET_URL = "/impact/ImpactServlet.py";
function addImpactServiceCitationCount(service) {
var idSelector = "#" + service;
var doi = $(idSelector).data("doi");
$.getJSON(IMPACT_SERVLET_URL + "?service=" + service + "&doi=" + doi)
.done(function(data) {
var citations = data["citationCount"];
var linkBack = data["linkBack"];
if (citations && linkBack) {
$(idSelector + " a").attr("href", linkBack);
$(idSelector + " .citation-count").html(citations);
$(idSelector).removeClass("hidden");
}
})
.fail(function(jqxhr, textStatus, error) {
console.log("Error retrieving " + service + " citation count: " + error);
});
}
function addCitationCounts() {
addImpactServiceCitationCount("wos");
addImpactServiceCitationCount("scopus");
}
$(document).ready(addCitationCounts);
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import requests
import json
import xml.etree.ElementTree as ElementTree
from javax.servlet.http import HttpServlet
from net.sf.ehcache import Cache;
from net.sf.ehcache import CacheManager;
from net.sf.ehcache import Element;
class ImpactServlet(HttpServlet):
# This doesn't work:
# def init(self):
# so we use the other constructor:
def init(self, config):
# Create cache used by CachedImpactService
impactCache = Cache("impactCache",
2000, # max elements
False, # don't store to disk
False, # don't cache forever
3600*24*7, # cache for 1 week (from WoS doc)
0) # don't expire elements not accessed
CacheManager.getInstance().addCache(impactCache)
config.getServletContext().setAttribute("impactCache", impactCache)
# This doesn't work:
# HttpServlet.init(self, config)
# so we have to store the ServletConfig by ourselves:
self._config = config
def doGet(self, request, response):
self.doPost(request, response)
def doPost(self, request, response):
try:
impactService = self._getImpactService(request)
except ValueError as e:
response.sendError(response.SC_NOT_IMPLEMENTED, e.message)
return
response.setContentType("application/json")
response.setCharacterEncoding("UTF-8")
response.getWriter().println(impactService.query())
def _getImpactService(self, request):
"""
Returns the ``ImpactService`` instance corresponding to the one
specified in the request.
Services are specified in the ``service`` parameter. Possible values:
- ``scopus``
- ``wos`` (Web of Science)
:param request: ``HttpRequest`` received by the servlet.
:raises InvalidArgumentException: if the service is not one of the
supported.
"""
serviceToQuery = request.getParameter("service")
if serviceToQuery == "scopus":
impactService = Scopus(request)
elif serviceToQuery == "wos":
impactService = WebOfScience(request)
else:
raise ValueError("Invalid service '{0}'.".format(serviceToQuery))
cache = self._config.getServletContext().getAttribute("impactCache")
return CachedImpactService(impactService, cache, request)
class ImpactService(object):
"""
Interface definition for impact services.
"""
def __init__(self, request):
"""
Creates an ``ImpactService`` that will process the impact request in
``request``.
:param request: ``HttpRequest`` received by the servlet.
"""
raise NotImplementedError()
def query(self):
"""
Returns the impact information returned by the impact service.
"""
serviceResponse = self._getServiceResponse()
return self._buildResponseToClient(serviceResponse)
def _getServiceResponse(self):
"""
Returns a string with the response from the impact service.
"""
raise NotImplementedError()
def _buildResponseToClient(self, serviceResponse):
"""
Extracts the citation count and link back from the impact service
response and returns it as a JSON document.
Example:
{
"citationCount": "12",
"linkBack": "http://gateway.webofknowledge.com/foo"
}
:param serviceResponse: string with the response returned by the impact
service.
:returns: JSON document as a string with impact information.
"""
raise NotImplementedError()
def _getJsonDocument(self, citationCount, linkBack):
"""
Returns a JSON document with the citation count and the link back specified.
"""
jsonObject = {}
jsonObject["citationCount"] = int(citationCount)
jsonObject["linkBack"] = linkBack
return json.dumps(jsonObject)
class CachedImpactService(ImpactService):
"""
Decorator to cache responses from ``ImpactService``.
The cache can be skipped by adding "cache=skip" parameter to the request.
"""
def __init__(self, impactService, cache, request):
"""
Constructs a ``CachedImpactService`` that caches the response from
``impactService`` in ``cache``.
:param impactService: ``ImpactService`` from where the response will be
cached.
:param cache: EhCache ``Cache`` object where the response will be cached.
:param request: ``HttpRequest`` received by the servlet.
"""
self._impactService = impactService
self._cache = cache
self._request = request
def query(self):
"""
Returns impact information returned by the impact service.
"""
cachedImpact = self._cache.get(self._generateCacheKey())
if not cachedImpact or self._shouldSkipCache():
responseData = self._impactService.query()
cachedImpact = Element(self._generateCacheKey(), responseData)
self._cache.put(cachedImpact)
else:
responseData = cachedImpact.getObjectValue()
if self._showCacheStatistics():
responseData = self._getStatistics()
return responseData
def _generateCacheKey(self):
"""
Returns a cache key build from the ``HttpRequest``.
"""
serviceToQuery = self._request.getParameter("service")
doi = self._request.getParameter("doi")
return "{0}:{1}".format(serviceToQuery, doi)
def _shouldSkipCache(self):
"""
Returns true if the user has requested to skip the cache.
:returns: Returns true if and only if the ``HttpRequest`` contains
the parameter "cache=skip".
"""
cacheParam = self._request.getParameter("cache")
return cacheParam == "skip"
def _showCacheStatistics(self):
"""
Returns true if the user has requested to show cache statistics.
:returns: Returns true if and only if the ``HttpRequest`` contains
the parameter "cache=stats".
"""
cacheParam = self._request.getParameter("cache")
return cacheParam == "stats"
def _getStatistics(self):
size = self._cache.getStatistics().getSize()
sizeBytes = self._cache.getStatistics().getLocalHeapSizeInBytes()
hitCount = self._cache.getStatistics().cacheHitCount()
hitRatio = self._cache.getStatistics().cacheHitRatio()
missCount = self._cache.getStatistics().cacheMissCount()
expiredCount = self._cache.getStatistics().cacheExpiredCount()
return """Statistics:
Size: {0} ({1} bytes)
Hit count: {2} ({3} ratio)
Miss count: {4}
Expired count: {5}
""".format(size, sizeBytes, hitCount, hitRatio, missCount, expiredCount)
class Scopus(ImpactService):
QUERY_URL = "http://api.elsevier.com/content/abstract/citation-count?httpAccept=application/json&apiKey={0}&doi={1}"
API_KEY = "YOUR-API-KEY"
def __init__(self, request):
self.doi = request.getParameter("doi")
def _getServiceResponse(self):
return requests.get(self.QUERY_URL.format(self.API_KEY, self.doi))
def _buildResponseToClient(self, serviceResponse):
serviceResponseObject = json.loads(serviceResponse.text)
citationCount = serviceResponseObject["citation-count-response"]\
["document"]["citation-count"]
linkBack = self._getLinkBack(serviceResponseObject)
return self._getJsonDocument(citationCount, linkBack)
def _getLinkBack(self, scopusData):
linkList = scopusData["citation-count-response"]["document"]["link"]
linkBackObject = next(link for link in linkList if link["@rel"] == "scopus-citedby")
return linkBackObject["@href"]
class WebOfScience(ImpactService):
QUERY_URL = "http://gateway.webofknowledge.com/gateway/Gateway.cgi"
QUERY_DOC = """<?xml version="1.0" encoding="UTF-8"?>
<request xmlns="http://www.isinet.com/xrpc42">
<fn name="LinksAMR.retrieve">
<list>
<map>
<val name="username">your-username</val>
<val name="password">your-password</val>
</map>
<map>
<list name="WOS">
<val>timesCited</val>
<val>citingArticlesURL</val>
</list>
</map>
<map>
<map name="cite_1">
<val name="doi">{0}</val>
</map>
</map>
</list>
</fn>
</request>
"""
CITATION_COUNT_XPATH = ".//wos:val[@name='timesCited']"
LINKBACK_XPATH = ".//wos:val[@name='citingArticlesURL']"
def __init__(self, request):
self.doi = request.getParameter("doi")
def _getServiceResponse(self):
return requests.post(self.QUERY_URL, data=self.QUERY_DOC.format(self.doi))
def _buildResponseToClient(self, serviceResponse):
root = ElementTree.fromstring(serviceResponse.text)
namespaces = {"wos": "http://www.isinet.com/xrpc42"}
citationCount = root.findtext(self.CITATION_COUNT_XPATH, namespaces=namespaces)
linkBack = root.findtext(self.LINKBACK_XPATH, namespaces=namespaces)
return self._getJsonDocument(citationCount, linkBack)
<xsl:template name="impact-wos">
<script src="{$theme-path}scripts/impact.js" defer="defer">&#160;</script>
<p id="wos" class="hidden">
<xsl:attribute name="data-doi">
<xsl:value-of select="$identifier_doi" />
</xsl:attribute>
<a href="to be filled by impact.js">
<img src="{concat($theme-path,'/images/wos.png')}"
i18n:attr="alt"
alt="xmlui.mirage2.itemSummaryView.ImpactSection.WosLogoAlt" />&#x2002;
<span class="citation-count"></span>&#x000A0;
<i18n:text>xmlui.mirage2.itemSummaryView.ImpactSection.WosLink</i18n:text>
</a>
</p>
</xsl:template>
<xsl:template name="impact-scopus">
<script src="{$theme-path}scripts/impact.js" defer="defer">&#160;</script>
<p id="scopus" class="hidden">
<xsl:attribute name="data-doi">
<xsl:value-of select="$identifier_doi" />
</xsl:attribute>
<a href="to be filled by impact.js">
<img src="{concat($theme-path,'/images/scopus.png')}"
i18n:attr="alt"
alt="xmlui.mirage2.itemSummaryView.ImpactSection.ScopusLogoAlt" />&#x2002;
<span class="citation-count"></span>&#x000A0;
<i18n:text>xmlui.mirage2.itemSummaryView.ImpactSection.ScopusLink</i18n:text>
</a>
</p>
</xsl:template>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment