Mvolz has submitted this change and it was merged.

Change subject: Adds GET endpoint /api, takes URL & DOI
......................................................................


Adds GET endpoint /api, takes URL & DOI

* New /api endpoint
        * Takes GET requests
        * Cache enabled (client, not server-side)
        * Takes both URL and DOI
* Moved files to /lib/
        * unshorten.js now follows all redirects
        * distingush.js determines whether search
          term is DOI, or URL as default
        * Most request logic moved to request.js
* All responses now have request errors
        * Previously if a request error, body
          was sent with URL as title
        * Now body is still sent, but with 520
          error.
* 'mediawiki' format in /lib/zotero.js changed
        * ISBN now list of isbns
        * ISSN now list of issns
* README.md created

Change-Id: Ia4b58d4b9eda3871c239c50bbacdce814bb3c134
---
A README.md
A lib/distinguish.js
A lib/requests.js
R lib/scrape.js
A lib/unshorten.js
A lib/zotero.js
M localsettings.js.sample
M package.json
M server.js
A test_files/4_input.json
D unshorten.js
D zotero.js
12 files changed, 575 insertions(+), 313 deletions(-)

Approvals:
  Mvolz: Verified; Looks good to me, approved



diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ab5ff25
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+#Citoid
+
+Citoid is a node.js service which currently locates citation data given a URL.
+
+[Citoid Documentation on mediawiki.org](https://www.mediawiki.org/wiki/Citoid)
diff --git a/lib/distinguish.js b/lib/distinguish.js
new file mode 100644
index 0000000..052860c
--- /dev/null
+++ b/lib/distinguish.js
@@ -0,0 +1,53 @@
+#!/usr/bin/env node
+/**
+ * https://www.mediawiki.org/wiki/citoid
+ *
+ * Distinguishes between different search contents, such as
+ * URL or DOI, and sends to appropriate request handler.
+ */
+
+var request = require('request');
+var urlParse = require('url');
+var requestFromURL = require('./requests.js').requestFromURL;
+var requestFromDOI = require('./requests.js').requestFromDOI;
+
+/**
+ * Determine type of string (doi, url) and callback on correct gandler
+ * @param  {[type]}   searchString what the end user searched for
+ * @param  {Function} callback     callback(extractedValue, correctFunction)
+ */
+var distinguish = function (searchString, callback){
+       var search, match, reDOI, parsedURL;
+
+       searchString.trim();
+
+       search = searchString;
+
+       reDOI = new RegExp('\\b10[.][0-9]{4,}[//].*\\b');
+
+       match = search.match(reDOI);
+
+       if (match){
+               callback(match[0], requestFromDOI);
+       } else {
+               parsedURL = urlParse.parse(search);
+               if (!parsedURL.protocol){
+                       search = 'http://'+ search;
+                       callback(search, requestFromURL);
+               } else {
+                       callback(search, requestFromURL); //assume url if not 
doi
+               }
+       }
+};
+
+module.exports = {
+       distinguish: distinguish
+};
+
+/*Test methods in main */
+if (require.main === module) {
+
+       distinguish("example.com", function (extracted, selectedFcn){
+               console.log(extracted);
+       });
+}
\ No newline at end of file
diff --git a/lib/requests.js b/lib/requests.js
new file mode 100644
index 0000000..dfde7d2
--- /dev/null
+++ b/lib/requests.js
@@ -0,0 +1,118 @@
+#!/usr/bin/env node
+/**
+ * https://www.mediawiki.org/wiki/citoid
+ *
+ * Request functions for difference search types,
+ * such as URL or DOI
+ */
+
+var bunyan = require('bunyan');
+var log = bunyan.createLogger({name: "citoid"});
+var unshorten = require('./unshorten.js');
+var scrape = require('./scrape.js').scrape;
+var zoteroRequest = require('./zotero.js').zoteroRequest;
+var util = require('util');
+
+/**
+ * runner function to use for a url
+ * @param  {[type]}   requestedURL want metadata from this url
+ * @param  {[type]}   opts         zoteroRequest options object
+ * @param  {Function} callback     callback (error, statusCode, body)
+ */
+
+var requestFromURL = function (requestedURL, opts, callback){
+       opts.zoteroURL = util.format(opts.zoteroURL, 'web'); //use web endpoint
+       zoteroRequest(requestedURL, opts, function(error, response, body){
+               log.info("Zotero request made for: " + requestedURL);
+               if (error) {
+                       log.error(error);
+                       log.info("Falling back on native scraper.");
+                       scrape(requestedURL, function(error, body){
+                               callback (error, 200, body);
+                       });
+               } else if ( response ) {
+                       //501 indicates no translator available
+                       //this is common- can indicate shortened url
+                       //or a website not specified in the translators
+                       if (response.statusCode == 501){
+                               log.info("Status Code from Zotero: " + 
response.statusCode);
+                               log.info("Looking for redirects...");
+                               //try again following all redirects
+                               //we don't do this initially because many sites
+                               //will redirect this fcn to a log-in screen
+                               unshorten(requestedURL, function(detected, 
expandedURL) {
+                                       if (detected) {
+                                               log.info("Redirect detected to 
"+ expandedURL);
+                                               zoteroRequest(expandedURL, 
opts, function(error, response, body){
+                                                       if (response && !error 
&& response.statusCode == 200){
+                                                               
log.info("Successfully retrieved and translated body from Zotero");
+                                                               callback(null, 
200, body);
+                                                       }
+                                                       else {
+                                                               log.info("No 
Zotero response available; falling back on native scraper.");
+                                                               
scrape(requestedURL, function(error, body){
+                                                                       
callback(error, 200, body);
+                                                               });
+                                                       }
+                                               });
+                                       }
+                                       else {
+                                               log.info("No redirect detected; 
falling back on native scraper.");
+                                               scrape(requestedURL, 
function(error, body){
+                                                       callback(error, 200, 
body);
+                                               });
+                                       }
+                               });
+                       }
+
+                       else if (response.statusCode == 200){
+                               log.info("Successfully retrieved and translated 
body from Zotero");
+                               callback (null, 200, body);
+                       }
+                       else { //other response codes such as 500, 300
+                               log.info("Falling back on native scraper.");
+                               scrape(requestedURL, function(error, body){
+                                       callback (error, 200, body);
+                               });
+                       }
+               } else {
+                       log.info("Falling back on native scraper.");
+                       scrape(requestedURL, function(error, body){
+                               callback (error, 200, body);
+                       });
+               }
+       });
+};
+
+/**
+ * runner function to use for a DOI
+ * @param  {[type]}   requestedDOI want metadata from this DOI
+ * @param  {[type]}   opts         zoteroRequest options object
+ * @param  {Function} callback     callback (error, statusCode, body)
+ */
+
+var requestFromDOI = function (requestedDOI, opts, callback){
+       var doiLink = 'http://dx.doi.org/'+requestedDOI;
+       //TODO: optimise this (can skip some steps in requestFromURL)
+       requestFromURL(doiLink, opts, function(error, responseCode, body){
+               callback(error, responseCode, body);
+       });
+};
+
+/*Test methods in main */
+if (require.main === module) {
+       var opts = {
+               zoteroURL:"http://localhost:1969/%s";,
+               sessionID: "123abc",
+               format: "zotero"
+       };
+
+       requestFromURL("http://example.com";, opts, function (error, statusCode, 
body){
+               console.log(body);
+       });
+}
+
+module.exports = {
+       requestFromURL: requestFromURL,
+       requestFromDOI: requestFromDOI
+};
\ No newline at end of file
diff --git a/scrape.js b/lib/scrape.js
similarity index 68%
rename from scrape.js
rename to lib/scrape.js
index a2731e7..8482571 100644
--- a/scrape.js
+++ b/lib/scrape.js
@@ -12,7 +12,7 @@
  * Currently scrapes title only
  * callback runs on list of json objs (var body)
  * @param  {String}   url      url to scrape
- * @param  {Function} callback callback function
+ * @param  {Function} callback callback(error, body)
  */
 
 var scrape = function(url, callback){
@@ -37,16 +37,16 @@
 
        request(
                {
-                       url: url, 
+                       url: url,
                        headers: {'user-agent': 'Mozilla/5.0'},
-                       followAllRedirects: true 
+                       followAllRedirects: true
                }, function(error, response, html){
 
-                       var json = {itemType: 'webpage', url: url, title: url};
+                       var citation = {itemType: 'webpage', url: url, title: 
url};
 
                        if (error || !response) {
                                console.log(error);
-                               callback([json]);
+                               callback(error, [citation]);
                                return;
                        }
 
@@ -55,28 +55,29 @@
                        }
                        catch (e){
                                console.log('Could not load document: ' + e);
-                               callback([json]);
+                               callback(error, [citation]);
                        }
 
-                       json.title = getTitle();
+                       citation.title = getTitle();
 
                        // Access date on format YYYY-MM-DD
-                       json.accessDate = (new 
Date()).toISOString().substring(0, 10);
+                       citation.accessDate = (new 
Date()).toISOString().substring(0, 10);
 
                        var parsedUrl = response.request.uri ? 
response.request.uri : urlParse.parse(url);
 
-                       if (json.title && parsedUrl && parsedUrl.hostname) {
-                               json.publicationTitle = parsedUrl.hostname;
+                       if (citation.title && parsedUrl && parsedUrl.hostname) {
+                               citation.publicationTitle = parsedUrl.hostname;
                        }
 
-                       callback([json]);
+                       callback(error, [citation]);
        });
 };
 
 if (require.main === module) {
-       var sampleUrl = 'http://example.com';
+       var sampleUrl = 'http://example,.com';
        console.log('scrape fcn running on sample url:'+sampleUrl);
-       scrape(sampleUrl, function(body){
+       scrape(sampleUrl, function(error, body){
+               console.log(error);
                console.log(body);
        });
 }
diff --git a/lib/unshorten.js b/lib/unshorten.js
new file mode 100644
index 0000000..f71d25b
--- /dev/null
+++ b/lib/unshorten.js
@@ -0,0 +1,22 @@
+(function() {
+
+       var request = require('request');
+       var urlParse = require('url');
+
+       /**
+        * [unshorten description]
+        * @param  {String}   url      url trying to be unshortened
+        * @param  {Function} callback callback(detectedRedirect, url)
+        */
+       var unshorten = function (url, callback){
+               request(url, function (error, response){
+                       if (response && !error && (url !== 
response.request.href)){
+                               callback(true, response.request.href);
+                       }
+                       else {
+                               callback(false, url);
+                       }
+               });
+       };
+       module.exports = unshorten;
+}());
diff --git a/lib/zotero.js b/lib/zotero.js
new file mode 100644
index 0000000..17db3d2
--- /dev/null
+++ b/lib/zotero.js
@@ -0,0 +1,239 @@
+#!/usr/bin/env node
+/**
+ * https://www.mediawiki.org/wiki/citoid
+ *
+ * Supplies methods to send requests to a Zotero server
+ */
+
+var request = require('request');
+
+/**
+ * Requests to Zotero server
+ * @param  {String}   requestedURL url being requested
+ * @param  {Object}   opts         options for request
+ * @param  {Function} callback     callback(error, response, body)
+ */
+var zoteroRequest  = function(requestedURL, opts, callback){
+       var options = {
+               url: opts.zoteroURL,
+               method: 'POST',
+               json: {
+                       "url": requestedURL,
+                       "sessionid": opts.sessionID
+               }
+       };
+
+       request(options, function (error, response, body) {
+               if (!error && response.statusCode == 200) {
+                       callback(error, response, modifyBody(requestedURL, 
opts.format, body));
+               }
+               else {
+                       callback(error, response, body);
+               }
+
+       });
+};
+
+/**
+ * Modify body of a zotero or other response body
+ * @param  {String} url    original uri requested
+ * @param  {String} format 'mediawiki', 'mwDeprecated', 'zotero', etc.
+ * @param  {Object} body   JSON request body
+ * @return {Object}        JSON request body
+ */
+var modifyBody = function(url, format, body){
+       var formatFcns = {
+               'mwDeprecated':convertToMWDeprecated,
+               'mediawiki':convertToMediawiki,
+               'zotero':convertToZotero
+               },
+               convert = formatFcns[format];
+
+       //if format is not available, use zotero as default- may want to switch 
to returning error instead
+       if (convert){
+               return convert(url, body);
+       }
+       else {
+               return convertToZotero(url, body);
+       }
+};
+
+/*Specific conversion methods*/
+var convertToZotero = function(url, body){
+       citation = body[0][0];
+
+       fixAccessDate(citation);
+       fixURL(url, citation);
+
+       return [[citation]];
+};
+
+var convertToMediawiki = function(url, body){
+
+       citation = body[0][0];
+
+       replaceCreators(citation);
+       addPMID(citation);
+       fixURL(url, citation);
+       fixAccessDate(citation);
+       fixISBN(citation);
+       fixISSN(citation);
+
+       return [citation];
+};
+
+var convertToMWDeprecated = function(url, body){
+       var zotCreators, issn,
+               creatorTypeCount = {},
+               citation = body[0][0];
+
+       //flattens creator field
+       if (citation.creators) {
+               zotCreators = citation.creators;
+
+               for (var z in zotCreators){
+                       creatorFieldName = zotCreators[z].creatorType;
+                       if (creatorTypeCount[creatorFieldName]){
+                               creatorTypeCount[creatorFieldName] += 1;
+                       }
+                       else {
+                               creatorTypeCount[creatorFieldName] = 1;
+                       }
+                       //Appends number to name, i.e. author -> author1
+                       creatorFieldName += 
(parseInt(creatorTypeCount[creatorFieldName]));
+
+                       citation[creatorFieldName + "-first"] = 
zotCreators[z].firstName;
+                       citation[creatorFieldName + "-last"] = 
zotCreators[z].lastName;
+               }
+               delete citation.creators; //remove creators field
+       }
+
+       fixURL(url, citation);
+       addPMID(citation);
+
+       //In some cases where two ISSNs, return first found
+       //If no match, leave field as is for user to correct
+       if (citation.ISSN){
+               issn = citation.ISSN;
+               reISSN = new RegExp('\\d{4}\\-\\d{3}[\\dX]');
+               match = issn.match(reISSN);
+               if (match) {
+                       citation.ISSN = match[0];
+               }
+       }
+
+       return [citation];
+};
+
+/*Methods for particular fields-
+* Mediawiki format if not otherwise specified
+*/
+
+var replaceCreators = function(citation){
+       if (citation.creators) {
+               zotCreators = citation.creators;
+
+               creatorMap = {};
+
+               for (var z in zotCreators){
+                       var creatorArray,
+                               creatorFieldName = zotCreators[z].creatorType;
+
+                       if (!citation[creatorFieldName]){
+                               creatorArray = [];
+                               citation[creatorFieldName]= creatorArray;
+                       }
+
+                       
citation[creatorFieldName].push([zotCreators[z].firstName, 
zotCreators[z].lastName]);
+               }
+
+               delete citation.creators; //remove creators field
+       }
+};
+
+var addPMID = function(citation){
+       //get pmid out of extra fields
+       if (citation.extra){
+               var extraFields = citation.extra.split('\n');
+               for (var f in extraFields){
+                       //could add them all, but let's not do this in case of 
conflicting fields
+                       var keyValue = extraFields[f].split(': ');
+                       if (keyValue[0] === 'PMID'){
+                               citation[keyValue[0]] = keyValue[1].trim();
+                       }
+               }
+       }
+       //TODO: if no pmid available from zotero output, get one from doi using 
api: http://www.ncbi.nlm.nih.gov/pmc/tools/id-converter-api/
+};
+
+var fixURL = function(url, citation){
+       if (!citation.url){
+               citation.url = url;
+       }
+};
+
+var fixAccessDate = function(citation){
+       if (!citation.accessDate || (citation.accessDate == 
"CURRENT_TIMESTAMP")){
+               citation.accessDate = (new Date()).toISOString().substring(0, 
10);
+       }
+};
+
+var fixISSN = function(citation){
+       var match, i,
+               issn = citation.ISSN;
+
+       reISSN = new RegExp('\\d{4}\\-\\d{3}[\\dX]', 'g');
+
+       if (issn){
+               issn.trim();
+               match = issn.match(reISSN);
+               if (match) {
+                       citation.ISSN = [];
+                       for (i in match){
+                               citation.ISSN.push(match[i]);
+                       }
+               } else{
+                       citation.ISSN = [issn]; //wraps issn field in array in 
case of false negatives
+               }
+       }
+};
+
+var fixISBN = function(citation){
+       var match, i,
+               isbn = citation.ISBN;
+
+       reISBN = new RegExp('((978[\\--– ])?[0-9][0-9\\--– ]{10}[\\--– 
][0-9xX])|((978)?[0-9]{9}[0-9Xx])', 'g');
+
+       if (isbn) {
+               isbn.trim();
+               match = isbn.match(reISBN);
+               if (match) {
+                       citation.ISBN = [];
+                       for (i in match){
+                               citation.ISBN.push(match[i]);
+                       }
+               } else {
+                       citation.ISBN = [isbn]; //wraps isbn field in array in 
case of false negatives
+               }
+       }
+};
+
+/*Test response alterations without having to use server*/
+var testJSON = function(){
+       var sampleJSON = require("../test_files/3_input.json");
+       console.log("before:");
+       console.log(JSON.stringify(sampleJSON));
+       console.log("after:");
+       
console.log(JSON.stringify(modifyBody("http://example.com","mediawiki",sampleJSON)));
+};
+
+/*Test methods in main */
+if (require.main === module) {
+       testJSON();
+}
+
+/*Exports*/
+module.exports = {
+       zoteroRequest: zoteroRequest
+};
+
diff --git a/localsettings.js.sample b/localsettings.js.sample
index 0a3d93b..bb376fa 100644
--- a/localsettings.js.sample
+++ b/localsettings.js.sample
@@ -8,9 +8,6 @@
 
 var CitoidConfig = {
 
-       //Set your WorldCat API key here
-       wskey : false,
-
        // Enable debug mode (prints extra debugging messages)
        debug : true,
 
diff --git a/package.json b/package.json
index 659a421..bc76360 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
                "async" : "latest",
                "bluebird" : "latest",
                "body-parser" : "latest",
+               "bunyan" : "latest",
                "cheerio" : "latest",
                "express" : "latest",
                "path": "latest",
diff --git a/server.js b/server.js
index 881ffff..aba4de1 100644
--- a/server.js
+++ b/server.js
@@ -2,14 +2,14 @@
 /**
  * https://www.mediawiki.org/wiki/citoid
  */
+
 /*external modules*/
-var express = require('express');
-var request = require('request');
 var bodyParser = require('body-parser');
+var bunyan = require('bunyan');
+var express = require('express');
+var path = require('path');
 var urlParse = require('url');
 var util = require('util');
-var path = require('path');
-var unshorten = require('./unshorten.js');
 var opts = require('yargs')
        .usage('Usage: $0 [-c configfile|--config=configfile]')
        .default({
@@ -18,9 +18,8 @@
        .alias( 'c', 'config' );
 var argv = opts.argv;
 
-/*internal modules*/   
-var zoteroRequest = require('./zotero.js').zoteroRequest;
-var scrape = require('./scrape.js').scrape;
+var distinguish = require('./lib/distinguish.js').distinguish;
+var requestFromURL = require('./lib/requests.js').requestFromURL;
 
 /* import local settings*/
 var settingsFile = path.resolve(process.cwd(), argv.c);
@@ -29,18 +28,15 @@
 var citoidInterface = CitoidConfig.citoidInterface;
 var zoteroPort = CitoidConfig.zoteroPort;
 var zoteroInterface = CitoidConfig.zoteroInterface;
-var wskey = CitoidConfig.wskey;
 var debug = CitoidConfig.debug;
 var allowCORS = CitoidConfig.allowCORS;
 
 //url base which allows further formatting by adding a single endpoint, i.e. 
'web'
-var zoteroURL = util.format('http://%s:%s/%s', zoteroInterface, 
zoteroPort.toString()); 
-
-/*testing variables*/
-var testSessionID = "abc123";
+var zoteroURL = util.format('http://%s:%s/%s', zoteroInterface, 
zoteroPort.toString());
 
 //Init citoid webserver
 var citoid = express();
+var log = bunyan.createLogger({name: "citoid"});
 
 //SECURITY WARNING: ALLOWS ALL REQUEST ORIGINS
 //change allowCORS in localsettings.js
@@ -52,8 +48,11 @@
 
 citoid.use(bodyParser.json());
 citoid.use(bodyParser.urlencoded({extended: false}));
+//citoid.use(bunyan());
+citoid.use(express.static('api')); //cache api pages
 
 /*Landing page*/
+/*jshint multistr: true */
 citoid.get('/', function(req, res){
        res.setHeader("Content-Type", "text/html");
        res.send('<!DOCTYPE html>\
@@ -77,89 +76,90 @@
 /*Endpoint for retrieving citations in JSON format from a URL*/
 citoid.post('/url', function(req, res){
 
-       var format = req.body.format,
+       var opts, parsedURL,
+               format = req.body.format,
                requestedURL = req.body.url,
-               zoteroURLWeb = util.format(zoteroURL, 'web');
+               sessionID = "123abc";
+
+       log.info(req);
 
        //temp backwards compatibility
        if (!format){
-               format = 'mwDeprecated';
+               format = 'mwDeprecated';
        }
 
        res.type('application/json');
 
-       //TODO: fix for async error catching
-       try {
-               var parsedURL = urlParse.parse(requestedURL);
-               //defaults to http if no protocol specified.
-               if (!parsedURL.protocol){
-                       requestedURL = 'http://'+ urlParse.format(parsedURL);
-               }
-               else {requestedURL = urlParse.format(parsedURL);}
+       parsedURL = urlParse.parse(requestedURL);
+       //defaults to http if no protocol specified.
+       if (!parsedURL.protocol){
+               requestedURL = 'http://'+ urlParse.format(parsedURL);
        }
-       catch (e){
-               console.log(e); 
-       }
+       else {requestedURL = urlParse.format(parsedURL);}
 
-       //make things work with new format for now..
-       //format = 'mediawiki';
+       opts = {
+               zoteroURL:zoteroURL,
+               sessionID: sessionID,
+               format: format
+       };
 
-       //Request from Zotero and set response
-       zoteroRequest(zoteroURLWeb, requestedURL, testSessionID, format, 
function(error, response, body){
-               console.log("Request made for: " + requestedURL);
-               if (response) {
-                       if (!error) {
-                               //501 indicates no translator availabe
-                               //this is common- can indicate shortened url
-                               //or a website not specified in the translators
-                               if (~[500, 501].indexOf(response.statusCode)){
-                                       //try again with unshortened url
-                                       //we don't do this initially because 
many sites
-                                       //will redirect this fcn to a log-in 
screen
-                                       unshorten(requestedURL, 
function(expandedURL) {
-                                               zoteroRequest(zoteroURLWeb, 
expandedURL, testSessionID, format,
-                                                       function(error, 
response, body){
-                                                       if (response){
-                                                               //if still no 
translator, or translation fails,
-                                                               //send to naive 
scraper
-                                                               if (~[500, 
501].indexOf(response.statusCode)){
-                                                                       
scrape(requestedURL, function(body){
-                                                                               
res.statusCode = 200;
-                                                                               
res.json(body);
-                                                                       });
-                                                               }
-                                                               else {
-                                                                       
res.statusCode = 200;
-                                                                       
res.json(body);
-                                                               }
-                                                       }                       
                                
-                                               });
-                                       });
-                               }
-                               else {
-                                       res.statusCode = response.statusCode;
-                                       res.json(body);
-                               }
-                       }
-                       else {
-                               res.statusCode = 500;
-                               res.json("Internal server error");
-                               console.log(error);
-                       }
+       requestFromURL(requestedURL, opts, function(error, responseCode, body){
+               if (!error){
+                       res.statusCode = responseCode;
+                       res.send(body);
                }
                else {
-                       //no response
-                       var message = "Server at "+zoteroURL+" does not appear 
to be running.";
-                       res.statusCode = 500;
-                       res.json("Internal server error");
-                       console.log(message);
+                       res.statusCode = 520;
+                       res.send(body);
                }
        });
 });
 
+/**Endpoint for retrieving citations based on search term (URL,DOI)*/
+citoid.get('/api', function(req, res){
+       var opts, dSearch,
+               format = req.query.format,
+               search = req.query.search;
+
+       log.info(req);
+
+       if (!search){
+               res.statusCode = 400;
+               res.setHeader("Content-Type", "text/plain");
+               res.send("No 'search' value specified\n");
+       } else if(!format){
+               res.statusCode = 400;
+               res.setHeader("Content-Type", "text/plain");
+               res.send("No 'format' value specified\nOptions are 
'mediawiki','zotero'");
+       } else {
+
+               dSearch = decodeURIComponent(search); //decode urlencoded 
search string
+
+               distinguish(dSearch, function(extractedID, runnerFunction){
+                       opts = {
+                               zoteroURL:zoteroURL,
+                               sessionID:"123abc",
+                               format:format,
+                               identifier:extractedID
+                       };
+
+                       runnerFunction(extractedID, opts, function(error, 
responseCode, body){
+                               if (!error){
+                                       res.statusCode = responseCode;
+                                       res.send(body);
+                               }
+                               else {
+                                       res.statusCode = 520; //Server at 
requested location not available
+                                       res.send(body);
+                               }
+                       });
+               });
+       }
+});
+
 citoid.listen(citoidPort);
 
-console.log('Server running on http://localhost:'+citoidPort);
+log.info('Server started on http://localhost:'+citoidPort);
 
 /*Exports*/
 exports = module.exports = citoid;
diff --git a/test_files/4_input.json b/test_files/4_input.json
new file mode 100644
index 0000000..dd4146a
--- /dev/null
+++ b/test_files/4_input.json
@@ -0,0 +1,47 @@
+[
+    [
+        {
+            "itemKey": "RNW2N68C",
+            "itemVersion": 0,
+            "itemType": "journalArticle",
+            "creators": [
+                {
+                    "firstName": "Ribhi",
+                    "lastName": "Hazin",
+                    "creatorType": "author"
+                },
+                {
+                    "firstName": "Tarek I. Abu-Rajab",
+                    "lastName": "Tamimi",
+                    "creatorType": "author"
+                },
+                {
+                    "firstName": "Jamil Y.",
+                    "lastName": "Abuzetun",
+                    "creatorType": "author"
+                },
+                {
+                    "firstName": "Nizar N.",
+                    "lastName": "Zein",
+                    "creatorType": "author"
+                }
+            ],
+            "tags": [],
+            "title": "Recognizing and treating cutaneous signs of liver 
disease",
+            "date": "10/01/2009",
+            "DOI": "10.3949/ccjm.76A.08113",
+            "language": "en",
+            "publicationTitle": "Cleveland Clinic Journal of Medicine",
+            "journalAbbreviation": "Cleveland Clinic Journal of Medicine",
+            "volume": "76",
+            "issue": "10",
+            "url": "http://www.ccjm.org/content/76/10/599";,
+            "pages": "599-606",
+            "ISSN": "0891-1150, 1939-2869",
+            "extra": "PMID: 19797460",
+            "libraryCatalog": "www.ccjm.org",
+            "accessDate": "CURRENT_TIMESTAMP",
+            "abstractNote": "Cutaneous changes may be the first clue that a 
patient has liver disease. Recognizing these signs is crucial to diagnosing 
liver conditions early. Here we describe the spectrum of skin manifestations 
that may be found in various liver diseases.\nKey points\nPruritus due to liver 
disease is particularly resistant to therapy. Cholestyramine (Questran) 4 
g/day, gradually increased to 24 g/day, is one option. If the pruritus does not 
respond or the patient cannot tolerate cholestyramine, rifampin (Rifadin) can 
be tried.\nSpider angiomas, Bier spots, and “paper-money” skin are all 
superficial vascular problems that may be related to liver disease.\nCutaneous 
lesions often accompany alcoholic cirrhosis and have been detected in up to 43% 
of people with chronic alcoholism. The combination of spider angiomas, palmar 
erythema, and Dupuytren contracture is common in alcoholic cirrhosis.\nAlthough 
porphyria cutanea tarda is associated with liver disease in general, recent 
studies show that patients with hepatitis C are at particularly high risk."
+        }
+    ]
+]
\ No newline at end of file
diff --git a/unshorten.js b/unshorten.js
deleted file mode 100644
index 017793e..0000000
--- a/unshorten.js
+++ /dev/null
@@ -1,16 +0,0 @@
-(function() {
-
-       var request = require('request');
-
-       var unshorten = function (url, callback){
-               request(url, function (error, response, body) {
-                       if (!error && ~[301, 302].indexOf(response.statusCode)) 
{
-                               callback(response.headers.location);
-                       }
-                       else {
-                               callback(url);
-                       }
-               });
-       };
-       module.exports = unshorten;
-}());
diff --git a/zotero.js b/zotero.js
deleted file mode 100644
index 5b917ba..0000000
--- a/zotero.js
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/usr/bin/env node
-/**
- * https://www.mediawiki.org/wiki/citoid
-* 
-* Supplies methods to send requests to a Zotero server
- */
-
-var request = require('request');
-
-var zoteroRequest  = function(zoteroURL, requestedURL, sessionID, format, 
callback){
-       var options = {
-               url: zoteroURL,
-               method: 'POST',
-               json: {
-                       "url": requestedURL,
-                       "sessionid": sessionID
-               }
-       };
-
-       request(options, function (error, response, body) {
-               if (!error && response.statusCode == 200) {
-                       //modify body only if response is okay
-                       callback(error, response, modifyBody(requestedURL, 
format, body));
-               }
-               else {
-                       callback(error, response, body);
-               }
-               
-       });
-};
-
-/*Converts Zotero body into appropriate format*/
-var modifyBody = function(url, format, body){
-       console.log(format);
-       var formatFcns = {
-               'mwDeprecated':convertToMWDeprecated,
-               'mediawiki':convertToMediawiki,
-               'zotero':convertToZotero
-               },
-               convert = formatFcns[format];
-               
-       //if format is not available, use zotero as default- may want to switch 
to returning error instead
-       if (convert){
-               return convert(url, body);
-       }
-       else {
-               return convertToZotero(url, body);
-       }
-};
-
-/*Specific conversion methods*/
-var convertToZotero = function(url, body){
-       return body;
-};
-
-var convertToMediawiki = function(url, body){
-       var citation, zotCreators, creatorMap,
-               creatorTypeCount = {};
-
-       //hack in most cases body will be an array, but in some will be an 
array of arrays
-       if (!(body[0] instanceof Array)){
-               citation = body[0];
-       }
-       else if (!(body[0][0] instanceof Array)){
-               citation = body[0][0];
-       }
-       else {
-               return body;
-       }
-
-       if (citation.creators) {
-               zotCreators = citation.creators;
-
-               creatorMap = {};
-       
-               for (var z in zotCreators){
-                       var creatorArray,
-                               creatorFieldName = zotCreators[z].creatorType;
-
-                       if (!citation[creatorFieldName]){
-                               creatorArray = [];
-                               citation[creatorFieldName]= creatorArray;
-                       }
-
-                       
citation[creatorFieldName].push([zotCreators[z].firstName, 
zotCreators[z].lastName]);
-               }
-
-               delete citation.creators; //remove creators field
-       }
-
-       //some zotero requests come back without the url field filled in
-       if (!citation.url){
-               citation.url = url;
-       }
-
-       //get pmid out of extra fields
-       if (citation.extra){
-               var extraFields = citation.extra.split('\n');
-               for (var f in extraFields){
-                       //could add them all, but let's not do this in case of 
conflicting fields
-                       var keyValue = extraFields[f].split(': ');
-                       if (keyValue[0] === 'PMID'){
-                               citation[keyValue[0]] = keyValue[1].trim();
-                       }
-               }
-       }
-
-       return [citation];
-
-};
-
-var convertToMWDeprecated = function(url, body){
-       var citation, zotCreators,
-               creatorTypeCount = {};
-
-       //hack- in most cases body will be an array, but in some will be an 
array of arrays
-       if (!(body[0] instanceof Array)){
-               citation = body[0];
-       }
-       else if (!(body[0][0] instanceof Array)){
-               citation = body[0][0];
-       }
-       else {
-               return body;
-       }
-
-       if (citation.creators) {
-               zotCreators = citation.creators;
-
-               for (var z in zotCreators){
-                       creatorFieldName = zotCreators[z].creatorType;
-                       if (creatorTypeCount[creatorFieldName]){
-                               creatorTypeCount[creatorFieldName] += 1;
-                       }
-                       else {
-                               creatorTypeCount[creatorFieldName] = 1;
-                       }
-                       //Appends number to name, i.e. author -> author1
-                       creatorFieldName += 
(parseInt(creatorTypeCount[creatorFieldName])); 
-
-                       citation[creatorFieldName + "-first"] = 
zotCreators[z].firstName;
-                       citation[creatorFieldName + "-last"] = 
zotCreators[z].lastName;
-               }
-               delete citation.creators; //remove creators field
-       }
-       //some zotero requests come back without the url field filled in
-       if (!citation.url){
-               citation.url = url;
-       }
-       //get pmid out of extra fields
-       if (citation.extra){
-               var extraFields = citation.extra.split('\n');
-               for (var f in extraFields){
-                       //could add them all, but let's not do this in case of 
conflicting fields
-                       var keyValue = extraFields[f].split(': ');
-                       if (keyValue[0] === 'PMID'){
-                               citation[keyValue[0]] = keyValue[1].trim();
-                       }
-               }
-       }
-       return [citation];
-};
-
-/*Test server fcns*/
-var testServer = function(){
-       var zoteroURL = 'http://localhost:1969/web', //edit this to your own 
endpoint
-       testURL = 
"http://www.tandfonline.com/doi/abs/10.1080/15424060903167229";, //URL that 
works with Zotero
-               //testURL = 
"http://books.google.co.uk/books?hl=en&lr=&id=7lueAgAAQBAJ&oi=fnd&pg=PR5&dq=mediawiki&ots=-Z0o2LCgao&sig=IGHnyWEiNiNvPyXeyCuOcdvi15s#v=onepage&q=mediawiki&f=false";,
 //url that doesn't work with zotero
-               testSessionID = 'abc123';
-               //format = 'mwDeprecated';
-               format = 'mediawiki';
-
-       zoteroRequest(zoteroURL, testURL, testSessionID, format, 
function(error, response, body){
-               if (response) {
-                       if (!error && response.statusCode == 200) {
-                               console.log(body);
-                       }
-                       else if(response.statusCode == 501){
-                               console.log(body);
-                       }
-               }
-               else {console.log("Server at "+zoteroURL+" does not appear to 
be running.");}
-       });
-};
-
-/*Test response alterations without having to use server*/
-var testJSON = function(){
-       var sampleJSON = require("./sampleZoteroResponseBody.json");
-       console.log("before:");
-       console.log(JSON.stringify(sampleJSON));
-       console.log("after:");
-       console.log(JSON.stringify(modifyBody(sampleJSON)));
-};
-
-/*Test methods in main */
-if (require.main === module) {
-       //testJSON();
-       testServer();
-}
-
-/*Exports*/
-module.exports = {
-       zoteroRequest: zoteroRequest
-};
-

-- 
To view, visit https://gerrit.wikimedia.org/r/169690
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ia4b58d4b9eda3871c239c50bbacdce814bb3c134
Gerrit-PatchSet: 6
Gerrit-Project: mediawiki/services/citoid
Gerrit-Branch: master
Gerrit-Owner: Mvolz <[email protected]>
Gerrit-Reviewer: Mvolz <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to