Comparing version 0.2.1 to 0.3.0
@@ -11,10 +11,14 @@ | ||
const url = replServer.context.url = function(params) { | ||
params.test = true; | ||
var urlGateway = gateway(params); | ||
const url = replServer.context.url = function(args) { | ||
var urlGateway = gateway(args); | ||
return urlGateway.generateUrl(); | ||
} | ||
replServer.context.open = function(params) { | ||
open(url(params)); | ||
replServer.context.open = function(args) { | ||
open(url(args)); | ||
return; | ||
} | ||
replServer.context.noop = function() { | ||
return ''; | ||
} |
12
index.js
'use strict'; | ||
module.exports = require('./src/controllers'); | ||
module.exports = { | ||
pubmed: require('./src/pubmed'), | ||
createSearch: function() { | ||
console.error('createSearch has been deprecated. Please use the pure function `ncbi.pubmed.search` instead.'); | ||
}, | ||
createCitation: function() { | ||
console.error('createCitation has been deprecated. Please use pure functions instead (see documentation)'); | ||
} | ||
} |
{ | ||
"name": "node-ncbi", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "Access and parse the NCBI eUtils API in Node or the Browser", | ||
@@ -27,6 +27,7 @@ "keywords": [ | ||
"popsicle": "^0.5.12", | ||
"underscore": "^1.8.3", | ||
"react-addons-update": "^15.0.2", | ||
"xml2js": "^0.4.9" | ||
}, | ||
"devDependencies": { | ||
"babel-preset-es2015": "^6.6.0", | ||
"gulp": "^3.9.1", | ||
@@ -33,0 +34,0 @@ "gulp-eslint": "^2.0.0", |
@@ -7,2 +7,5 @@ # node-ncbi | ||
**Note:** Browser use isn't ideal right now due to the very large XML parser. I'll come with a dedicated script | ||
that relies on the browser's native DOM parsing to get around this. | ||
## Getting started | ||
@@ -16,7 +19,7 @@ | ||
### PubMed Searches | ||
### Performing a search | ||
```js | ||
let pubmedSearch = ncbi.createSearch('actin'); | ||
pubmedSearch.search().then((results) => { | ||
const pubmed = ncbi.pubmed; | ||
pubmed.search('actin').then((results) => { | ||
console.log(results); | ||
@@ -26,29 +29,41 @@ }); | ||
Will log an array of objects. The objects represent PubMed "summaries" containing title, authors, journal and citation information, etc.. | ||
Will log | ||
``` | ||
{ | ||
count: (Number), | ||
papers: (Array) | ||
} | ||
``` | ||
where `count` is the total number of papers, independent of pagination. The "papers" represent PubMed "summaries" containing title, authors, journal and citation information, etc.. | ||
By default, 10 results will be retrieved at a time. To get the next set of results: | ||
```js | ||
pubmedSearch.getPage(1).then( ... ); | ||
```javascript | ||
pubmed.search('actin', 1).then((results) => { | ||
console.log(results); | ||
}); | ||
``` | ||
**Note**: An earlier version used `pubmedSearch.nextPage` without an argument. I decided that storing this one tiny piece of state in the controller was stupid. | ||
To change the number of results retrieved at a time: | ||
```js | ||
let pubmedSearch = ncbi.createSearch('actin', { | ||
resultsPerPage: 100 | ||
```javascript | ||
pubmed.search('actin', 0, 20).then((results) => { | ||
console.log(results); | ||
}); | ||
``` | ||
###Getting the details of a paper | ||
###Looking up a specific paper | ||
```js | ||
var paper = ncbi.createCitation(20517925); | ||
```javascript | ||
pubmed.summary(20517925).then((paper) => { | ||
console.log(paper); | ||
}); | ||
``` | ||
where the only argument is a PMID (PubMed ID #). | ||
The following methods are available: | ||
In addition, following methods are available: | ||
- `abstract()` - get the abstract | ||
@@ -68,37 +83,19 @@ - `summary()` - get the "summary" - an object of fields containing title, authors, citation info, etc. | ||
### Overview | ||
### REPL | ||
The module consists of three main parts: a Gateway class that controls access to the API, a set of Document parsers for finding the required information in the returned documents, and Controllers for tying the two together. | ||
To help with creating Gateways are seeing the data structures returned by the API, node-ncbi provides a custom REPL. Start it with `npm start`. You can then run `url({object})` or `open({object})` where {object} is an object literal that looks like the following: | ||
Since many of the exposed methods require accessing the API multiple times (ie - perform a search and get ID numbers, then find the individual documents by sending those ID numbers) controllers configure as many gateways and parsers as needed to accomplish a particular task. | ||
Gateways are instantiated with an object literal as follows: | ||
```js | ||
let gateway = Gateway({ | ||
documentType: 'esearch' | 'esummary' | 'elink' | 'efetch' (default: 'esearch'), | ||
responseType: 'json' | 'xml' (default: 'json'), | ||
params: {} (set of parameters for the API) | ||
test: false | ||
}); | ||
```javascript | ||
utility: 'esearch', | ||
params: { | ||
db: 'pubmed', | ||
term: query, | ||
retstart: start, | ||
retmax: resultsPerPage | ||
} | ||
``` | ||
See the [API documentaion](http://www.ncbi.nlm.nih.gov/books/NBK25500/) for more information on document types and available parameters. | ||
`url` will log the URL needed to access eUtils while `open` will open that URL in a browser. This can help with debugging and to look at the actual data which is useful to create new queries. [See the full documentation of eUtils](http://www.ncbi.nlm.nih.gov/books/NBK25500/) for more information on creating | ||
queries. | ||
A set of PubMed IDs can be added like so: | ||
```js | ||
gateway.addIds([1111111, 2222222]); | ||
``` | ||
The most important method is `gateway.send()` which returns a Promise resolving to the appropriate parser for the returned document type. The parser methods are pretty self-explanatory and are named for the type of information that they will return. | ||
### REPL | ||
To help with creating Gateways are seeing the data structures returned by the API, node-ncbi provides a custom REPL. Start it with `npm start`. You can then run `url({object})` or `open({object})` where {object} is an object literal for creating gateways as described above. `url` will log the URL needed to access eUtils while `open` will open that URL in a browser. This can help to see the actual data which is useful to create new parsers. | ||
### Unit tests | ||
The Gateway and the Parsers are tested independetly in `test/test.js`. Run with `gulp test`. | ||
### ESLint | ||
@@ -105,0 +102,0 @@ |
@@ -1,7 +0,5 @@ | ||
'strict mode'; | ||
const _ = require('underscore'); | ||
const update = require('react-addons-update'); | ||
const popsicle = require('popsicle'); | ||
const createParser = require('../documents'); | ||
const parse = require('./parse'); | ||
@@ -22,28 +20,6 @@ /** | ||
Gateway.getBase = function() { | ||
return this.base = 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/' + this.settings.documentType + '.fcgi?'; | ||
return this.base = 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/' + this.settings.utility + '.fcgi?'; | ||
} | ||
/** | ||
* Set parameters after object instatiation. | ||
* Note: this method is also called by the constructor to add the responseType | ||
* as the retmode property. | ||
* @arg: params | Object | new URL parameters indexed by name | ||
* @return the URL parameters after modification | ||
*/ | ||
Gateway.addParams = function(params) { | ||
_.extend(this.settings.params, params); | ||
return this.settings.params; | ||
} | ||
/** | ||
* Add article IDs for performing an efetch or esummary type of request. | ||
* @arg: ids | Array | array of ID numbers (eg pubmed ids) | ||
* @return: the full Object of URL parameters | ||
*/ | ||
Gateway.addIds = function(ids) { | ||
var idString = _.isArray(ids) ? ids.join() : ids; | ||
return this.addParams({id: idString}); | ||
} | ||
/** | ||
* Create the URL to access the API. | ||
@@ -56,4 +32,9 @@ * @return String | A URL representing the call that will be made, based on this.settings | ||
for (var key in this.settings.params) { | ||
url = url + key + '=' + this.settings.params[key]; | ||
url = url + '&'; | ||
try { | ||
url += key + '=' + this.settings.params[key].toString(); | ||
url += '&'; | ||
} catch(e) { | ||
//skip if this parameter cannot be converted to a string | ||
continue; | ||
} | ||
} | ||
@@ -72,7 +53,2 @@ //remove final & | ||
var url = this.generateUrl(); | ||
if (this.test) { | ||
return new Promise(function(resolve) { | ||
resolve('Test call to NCBI eUtils: ' + url); | ||
}); | ||
} | ||
return popsicle({ | ||
@@ -84,4 +60,5 @@ method: 'GET', | ||
/** | ||
* Send off the request and create a parser. | ||
* Send off the request and parse the returned data. | ||
* @return Promise | Call .then(function(document)) to access the methods in the | ||
@@ -94,12 +71,8 @@ * parser object (count, ids, summaries, abstract). | ||
*/ | ||
Gateway.resolve = function(methodName) { | ||
return this.send().then(document => { | ||
var parser = createParser(document.body, this.settings.documentType); | ||
if (methodName) { | ||
return parser[methodName](); | ||
} else { | ||
return parser; | ||
} | ||
Gateway.resolve = function(query) { | ||
return this.send().then(res => { | ||
const dataObj = parse(res.body); | ||
return query(dataObj); | ||
}); | ||
} | ||
}; | ||
@@ -117,11 +90,14 @@ /** | ||
module.exports = function(args) { | ||
var gateway = Object.create(Gateway); | ||
gateway.settings = _.extend({ | ||
documentType: 'esearch', | ||
responseType: 'json', | ||
params: {}, | ||
test: false | ||
}, args); | ||
gateway.addParams({retmode: gateway.settings.responseType}); | ||
const defaults = { | ||
utility: 'esearch', | ||
params: { | ||
retmode: 'json', | ||
db: 'pubmed' | ||
} | ||
}; | ||
const settings = update(defaults, {$merge: args, params: {$merge: args.params}}); | ||
const gateway = Object.assign(Object.create(Gateway), { | ||
settings: settings | ||
}); | ||
return gateway; | ||
} |
@@ -15,5 +15,6 @@ 'use strict'; | ||
*/ | ||
pubmedSearch: function(query, start, end) { | ||
pubmedSearch: function(query, page, resultsPerPage) { | ||
const start = page * resultsPerPage; | ||
return createGateway({ | ||
documentType: 'esearch', | ||
utility: 'esearch', | ||
params: { | ||
@@ -23,3 +24,3 @@ db: 'pubmed', | ||
retstart: start, | ||
retmax: end - start | ||
retmax: resultsPerPage | ||
} | ||
@@ -33,10 +34,9 @@ }); | ||
pubmedSummary: function(ids) { | ||
const gateway = createGateway({ | ||
documentType: 'esummary', | ||
return createGateway({ | ||
utility: 'esummary', | ||
params: { | ||
db: 'pubmed' | ||
db: 'pubmed', | ||
id: ids | ||
} | ||
}); | ||
gateway.addIds(ids); | ||
return gateway; | ||
}, | ||
@@ -49,11 +49,10 @@ | ||
pubmedRecord: function(ids) { | ||
const gateway = createGateway({ | ||
documentType: 'efetch', | ||
responseType: 'xml', | ||
return createGateway({ | ||
utility: 'efetch', | ||
params: { | ||
db: 'pubmed' | ||
db: 'pubmed', | ||
retmode: 'xml', | ||
id: ids | ||
} | ||
}); | ||
gateway.addIds(ids); | ||
return gateway; | ||
}, | ||
@@ -68,14 +67,13 @@ | ||
pubmedLinks: function(id) { | ||
const gateway = createGateway({ | ||
documentType: 'elink', | ||
return createGateway({ | ||
utility: 'elink', | ||
params: { | ||
db: 'pubmed', | ||
dbfrom: 'pubmed', | ||
cmd: 'neighbor' | ||
cmd: 'neighbor', | ||
id: id | ||
} | ||
}); | ||
gateway.addIds(id); | ||
return gateway; | ||
} | ||
} |
@@ -20,3 +20,3 @@ /* eslint-env mocha, node */ | ||
it('should build a valid search url from parameters', function() { | ||
assert.equal(search.generateUrl(), 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=ydenberg%20ca&retstart=0&retmax=10&retmode=json'); | ||
assert.equal(search.generateUrl(), 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?retmode=json&db=pubmed&term=ydenberg%20ca&retstart=0&retmax=10'); | ||
}); | ||
@@ -32,3 +32,3 @@ }); | ||
it('should build a valid link url from parameters', function() { | ||
assert.equal(links.generateUrl(), 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/elink.fcgi?db=pubmed&dbfrom=pubmed&cmd=neighbor&retmode=json&id=22588722'); | ||
assert.equal(links.generateUrl(), 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/elink.fcgi?retmode=json&db=pubmed&dbfrom=pubmed&cmd=neighbor&id=22588722'); | ||
}); | ||
@@ -39,9 +39,13 @@ }); | ||
const parse = require('../src/gateways/parse'); | ||
function getDoc(filename, callback) { | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
fs.readFile(path.join(__dirname, 'docs', filename), 'UTF-8', callback); | ||
fs.readFile(path.join(__dirname, 'docs', filename), 'UTF-8', (err, data) => { | ||
callback(err, parse(data)); | ||
}); | ||
} | ||
var createParser = require('../src/documents'); | ||
var q = require('../src/queries'); | ||
describe('parser', function() { | ||
@@ -52,4 +56,3 @@ | ||
getDoc('search.json', function(err, contents) { | ||
var parser = createParser(contents, 'esearch'); | ||
assert.equal(parser.count(), 9); | ||
assert.equal(q.count(contents), 9); | ||
done(); | ||
@@ -63,4 +66,3 @@ }); | ||
getDoc('search.json', function(err, contents) { | ||
var parser = createParser(contents, 'esearch'); | ||
assert.equal(parser.ids().length, 9); | ||
assert.equal(q.ids(contents).length, 9); | ||
done(); | ||
@@ -74,5 +76,3 @@ }) | ||
getDoc('summary.json', function(err, contents) { | ||
var parser = createParser(contents, 'esummary'); | ||
var summaries = parser.summaries(); | ||
assert.equal(summaries.length, 9); | ||
assert.equal(q.summaries(contents).length, 9); | ||
done(); | ||
@@ -86,4 +86,3 @@ }); | ||
getDoc('fetch.xml', function(err, contents) { | ||
var parser = createParser(contents, 'efetch'); | ||
assert.ok( parser.abstracts(true) ); | ||
assert.equal(typeof q.abstract(contents), 'string'); | ||
done(); | ||
@@ -94,7 +93,6 @@ }); | ||
describe('citedBy', function() { | ||
describe('findLinks', function() { | ||
it('should find all of the papers that have cited this one', function(done) { | ||
getDoc('elink.json', function(err, contents) { | ||
var parser = createParser(contents, 'elink'); | ||
assert.equal(parser.citedBy().length, 2); | ||
assert.equal(q.findLinks('pubmed_pubmed_citedin', contents).length, 2); | ||
done(); | ||
@@ -105,19 +103,23 @@ }); | ||
describe('cites', function() { | ||
it('should find all the papers this paper cites', function(done) { | ||
getDoc('elink.json', function(err, contents) { | ||
var parser = createParser(contents, 'elink'); | ||
assert.ok(parser.cites()); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
function areSummaries(summaries) { | ||
return (typeof summaries[0].articleids === 'object'); | ||
} | ||
var pubmed = require('../src/pubmed'); | ||
describe('Pubmed module', function() { | ||
it('should perform a search', function(done) { | ||
pubmed.search('ydenberg ca').then(results => { | ||
assert(areSummaries(results.papers)); | ||
done(); | ||
}) | ||
}); | ||
describe('similar', function() { | ||
it('should find all the papers PubMed flags as similar to this one', function(done) { | ||
getDoc('elink.json', function(err, contents) { | ||
var parser = createParser(contents, 'elink'); | ||
assert.ok(parser.similar()); | ||
done(); | ||
}); | ||
it('should return papers that cite this one', function(done) { | ||
pubmed.citedBy(19188495).then(results => { | ||
assert(areSummaries(results)); | ||
done(); | ||
}); | ||
@@ -124,0 +126,0 @@ }); |
76398
7
17
1981
123
+ Addedreact-addons-update@^15.0.2
+ Addedobject-assign@4.1.1(transitive)
+ Addedreact-addons-update@15.6.3(transitive)
- Removedunderscore@^1.8.3
- Removedunderscore@1.13.7(transitive)