Commit 94fb639a authored by Alf Eaton's avatar Alf Eaton
Browse files

Initial commit

parents
{
"presets": ["env", "stage-2"],
"plugins": ["transform-async-generator-functions"]
}
node_modules/
dist/
Given some information about a paper (e.g. DOI or title) and an author position (integer), this web service tries to find that paper in PubMed or PMC, picks out the author from the author list, then returns email addresses and/or affiliations (which may contain email addresses).
[example](http://localhost:3000/author?term=%2210.1186/s12864-017-3982-1%22[DOI]&position=2)
[
{
"title": "The genomic architecture of mastitis resistance in dairy sheep",
"authors": [
{
"name": {
"first": "G.",
"last": "Banos"
},
"email": "georgios.banos@sruc.ac.uk"
},
{
"name": {
"first": "G.",
"last": "Bramis"
},
"email": "georgios.bramis@ceva.com"
},
{
"name": {
"first": "S. J.",
"last": "Bush"
},
"email": "stephen.bush@roslin.ed.ac.uk"
},
{
"name": {
"first": "E. L.",
"last": "Clark"
},
"email": "emily.clark@roslin.ed.ac.uk"
},
{
"name": {
"first": "M. E.B.",
"last": "McCulloch"
},
"email": "mary.mcculloch@ed.ac.uk"
},
{
"name": {
"first": "J.",
"last": "Smith"
},
"email": "jacqueline.smith@roslin.ed.ac.uk"
},
{
"name": {
"first": "G.",
"last": "Schulze"
},
"email": "gunnar.schulze@uib.no"
},
{
"name": {
"first": "G.",
"last": "Arsenos"
},
"email": "arsenosg@vet.auth.gr"
},
{
"name": {
"first": "D. A.",
"last": "Hume"
},
"email": "david.hume@roslin.ed.ac.uk"
},
{
"name": {
"first": "A.",
"last": "Psifidi"
},
"email": "androniki.psifidi@roslin.ed.ac.uk"
}
]
}
]
This source diff could not be displayed because it is too large. You can view the blob instead.
[
{
"title": "The genomic architecture of mastitis resistance in dairy sheep.",
"authors": [
{
"name": {
"first": "",
"initials": "G",
"last": "Banos"
},
"affiliations": [
"The Roslin Institute and Royal (Dick) School of Veterinary Studies, University of Edinburgh, Easter Bush, Midlothian, EH25 9RG, UK.",
"Scotland's Rural College, Edinburgh, Easter Bush, Midlothian, EH25 9RG, UK.",
"School of Veterinary Medicine, Aristotle University of Thessaloniki, 54124, Thessaloniki, Greece."
]
},
{
"name": {
"first": "",
"initials": "G",
"last": "Bramis"
},
"affiliations": [
"School of Veterinary Medicine, Aristotle University of Thessaloniki, 54124, Thessaloniki, Greece."
]
},
{
"name": {
"first": "",
"initials": "SJ",
"last": "Bush"
},
"affiliations": [
"The Roslin Institute and Royal (Dick) School of Veterinary Studies, University of Edinburgh, Easter Bush, Midlothian, EH25 9RG, UK."
]
},
{
"name": {
"first": "",
"initials": "EL",
"last": "Clark"
},
"affiliations": [
"The Roslin Institute and Royal (Dick) School of Veterinary Studies, University of Edinburgh, Easter Bush, Midlothian, EH25 9RG, UK."
]
},
{
"name": {
"first": "",
"initials": "MEB",
"last": "McCulloch"
},
"affiliations": [
"The Roslin Institute and Royal (Dick) School of Veterinary Studies, University of Edinburgh, Easter Bush, Midlothian, EH25 9RG, UK."
]
},
{
"name": {
"first": "",
"initials": "J",
"last": "Smith"
},
"affiliations": [
"The Roslin Institute and Royal (Dick) School of Veterinary Studies, University of Edinburgh, Easter Bush, Midlothian, EH25 9RG, UK."
]
},
{
"name": {
"first": "",
"initials": "G",
"last": "Schulze"
},
"affiliations": [
"School of Informatics, University of Bergen, 5008, Bergen, Norway."
]
},
{
"name": {
"first": "",
"initials": "G",
"last": "Arsenos"
},
"affiliations": [
"School of Veterinary Medicine, Aristotle University of Thessaloniki, 54124, Thessaloniki, Greece."
]
},
{
"name": {
"first": "",
"initials": "DA",
"last": "Hume"
},
"affiliations": [
"The Roslin Institute and Royal (Dick) School of Veterinary Studies, University of Edinburgh, Easter Bush, Midlothian, EH25 9RG, UK."
]
},
{
"name": {
"first": "",
"initials": "A",
"last": "Psifidi"
},
"affiliations": [
"The Roslin Institute and Royal (Dick) School of Veterinary Studies, University of Edinburgh, Easter Bush, Midlothian, EH25 9RG, UK. androniki.psifidi@roslin.ed.ac.uk.",
"Royal Veterinary College, University of London, AL9 7TA, Hatfield, UK. androniki.psifidi@roslin.ed.ac.uk."
]
}
]
}
]
This diff is collapsed.
require('babel-polyfill')
const fs = require('fs-extra')
const libxmljs = require('libxmljs')
const { parse } = require('../src/pmc')
test('parses response', () => {
const xml = fs.readFileSync(__dirname + '/data/efetch-result-pmc.xml')
const doc = libxmljs.parseXml(xml)
const result = parse(doc)
// fs.writeJsonSync(__dirname + '/data/efetch-result-pmc.json', result, { spaces: 2 })
const expected = fs.readJsonSync(__dirname + '/data/efetch-result-pmc.json')
expect(result).toEqual(expected)
})
require('babel-polyfill')
const fs = require('fs-extra')
const libxmljs = require('libxmljs')
const { parse } = require('../src/pubmed')
test('parses response', () => {
const xml = fs.readFileSync(__dirname + '/data/efetch-result-pubmed.xml')
const doc = libxmljs.parseXml(xml)
const result = parse(doc)
// fs.writeJsonSync(__dirname + '/data/efetch-result-pubmed.json', result, { spaces: 2 })
const expected = fs.readJsonSync(__dirname + '/data/efetch-result-pubmed.json')
expect(result).toEqual(expected)
})
{
"name": "author-email",
"version": "1.0.0",
"main": "dist",
"author": "Alf Eaton <eaton.alf@gmail.com>",
"license": "MIT",
"dependencies": {
"axios": "^0.17.1",
"cors": "^2.8.4",
"express": "^4.16.2",
"fs-extra": "^5.0.0",
"libxmljs": "^0.18.7"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-plugin-transform-async-generator-functions": "^6.24.1",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-2": "^6.24.1",
"jest": "^22.1.0",
"nodemon": "^1.14.11",
"rimraf": "^2.6.2"
},
"scripts": {
"dev": "nodemon --exec babel-node -- src",
"test": "jest --watch",
"prebuild": "rimraf dist",
"build": "babel src --out-dir dist",
"start": "node dist"
}
}
const axios = require('axios')
const libxmljs = require('libxmljs')
// axios.interceptors.request.use(config => {
// console.log(config)
// return config
// }, error => {
// console.log(error)
// return Promise.reject(error)
// })
//
// axios.interceptors.response.use(config => {
// console.log(config)
// return config
// }, error => {
// console.log(error)
// return Promise.reject(error)
// })
const request = (util, params) => {
return axios(`https://eutils.ncbi.nlm.nih.gov/entrez/eutils/${util}.fcgi`, { params })
.then(response => response.data)
}
const esearch = db => options => request('esearch', {
db,
usehistory: 'y',
retmax: 0,
retmode: 'json',
...options,
})
const efetch = db => options => ({ esearchresult }) => request('efetch', {
db,
webenv: esearchresult.webenv,
query_key: esearchresult.querykey,
retmode: 'xml',
...options,
}).then(libxmljs.parseXml)
module.exports = { esearch, efetch }
const express = require('express')
const cors = require('cors')
const eutils = require('./pmc')
const app = express()
app.set('json spaces', 2)
app.use(cors())
app.use('/author', (req, res) => {
const { term, position } = req.query
if (position === undefined || term === undefined) {
return res.json({
error: 'term and position are required'
})
}
const authorByPosition = results => {
if (!results.length) throw new Error('No matches')
const { title, authors } = results[0]
if (!authors[position]) throw new Error('Position not found')
const { name, email } = authors[position]
return { title, name, email }
}
eutils.search({ term })
.then(eutils.fetch({ retmax: 1 }))
.then(eutils.parse)
.then(authorByPosition)
.then(output => {
res.json(output)
})
.catch(e => {
res.json({
error: e.message
})
})
})
const server = app.listen(3000, () => {
console.log('Listening on port ' + server.address().port)
})
import { efetch, esearch } from './eutils'
module.exports = {
search: esearch('pmc'),
fetch: efetch('pmc'),
parse: doc => {
const parseAuthor = author => ({
name: {
first: author.get('string(name/given-names)'),
last: author.get('string(name/surname)'),
},
email: author.find('string(address/email)'), // TODO: can this be multiple?
})
const parseArticle = article => ({
title: article.get('string(front/article-meta/title-group/article-title[1])'),
authors: article.find('front/article-meta/contrib-group/contrib').map(parseAuthor)
})
return doc
.find('/pmc-articleset/article')
.map(parseArticle)
}
}
import { esearch, efetch } from './eutils'
module.exports = {
search: esearch('pubmed'),
fetch: efetch('pubmed'),
parse: doc => {
const parseAffiliation = affiliation => affiliation.text()
const parseAuthor = author => ({
name: {
first: author.get('string(FirstName)'),
initials: author.get('string(Initials)'),
last: author.get('string(LastName)'),
},
affiliations: author.find('AffiliationInfo/Affiliation').map(parseAffiliation)
})
const parseArticle = article => ({
title: article.get('string(ArticleTitle)'),
authors: article.find('AuthorList/Author').map(parseAuthor)
})
return doc
.find('/PubmedArticleSet/PubmedArticle/MedlineCitation/Article')
.map(parseArticle)
}
}
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment