diff --git a/src/clincoded/static/components/add_external_resource.js b/src/clincoded/static/components/add_external_resource.js index d2f733d86..72e64fb11 100644 --- a/src/clincoded/static/components/add_external_resource.js +++ b/src/clincoded/static/components/add_external_resource.js @@ -370,19 +370,19 @@ function pubmedTxt(field, extra) { } switch(field) { case 'modalTitle': - txt = 'Add new PubMed Article'; + txt = 'Add new Article'; break; case 'inputLabel': - txt = 'Enter a PMID'; + txt = 'Enter a PMID or DOI'; break; case 'editLabel': - txt = 'Edit PMID'; + txt = 'Edit PMID/DOI'; break; case 'inputButton': - txt = 'Retrieve PubMed Article'; + txt = 'Retrieve Article'; break; case 'resourceResponse': - txt = "Select \"" + extra + "\" (below) if the following citation is correct; otherwise, edit the PMID (above) to retrieve a different article."; + txt = "Select \"" + extra + "\" (below) if the following citation is correct; otherwise, edit the PMID/DOI (above) to retrieve a different article."; break; } return txt; @@ -421,16 +421,25 @@ function pubmedValidateForm() { this.setFormErrors('resourceId', 'Please re-enter PMID without any leading 0\'s'); this.setState({submitBusy: false}); } - // valid if the input only has numbers - if (valid && !formInput.match(/^[0-9]*$/)) { + // Check if form input is a DOI/PMID, then validate + if (valid && formInput.match(/^10.\d{4,9}\/[-._;()\/:A-Z0-9]+$/gi)) { + var doiPresent = true; + } else if (valid && formInput.match(/^[0-9]*$/)) { + var pmidPresent = true; + } else if (valid && !doiPresent && !formInput.match(/^[0-9]*$/)) { valid = false; this.setFormErrors('resourceId', 'PMID should contain only numbers'); this.setState({submitBusy: false}); - } + } else if (valid && !pmidPresent && !formInput.match(/^10.\d{4,9}\/[-._;()\/:A-Z0-9]+$/gi)) { + valid = false; + this.setFormErrors('resourceId', 'Invalid DOI'); + this.setState({submitBusy: false}); + } + // valid if parent object is GDM and input isn't already associated with it if (valid && this.props.parentObj && this.props.parentObj['@type'] && this.props.parentObj['@type'][0] == 'gdm') { for (var i = 0; i < this.props.parentObj.annotations.length; i++) { - if (this.props.parentObj.annotations[i].article.pmid == formInput) { + if (this.props.parentObj.annotations[i].article.pmid == formInput || this.props.parentObj.annotations[i].article.doi == formInput) { valid = false; this.setFormErrors('resourceId', 'This article has already been associated with this Gene-Disease Record'); this.setState({submitBusy: false}); @@ -439,50 +448,76 @@ function pubmedValidateForm() { } } // valid if parent object is evidence list (VCI) and input isn't already associated with it - final behavior TBD - /* if (valid && this.props.parentObj && this.props.parentObj['@type'] && this.props.parentObj['@type'][0] == 'evidenceList') { for (var j = 0; j < this.props.parentObj.evidenceList.length; j++) { - if (this.props.parentObj.evidenceList[j].articles[0].pmid == formInput) { + if (this.props.parentObj.evidenceList[j].articles[0].pmid == formInput || this.props.parentObj.evidenceList[j].articles[0].doi == formInput) { valid = false; this.setFormErrors('resourceId', 'This article has already been associated with this evidence group'); this.setState({submitBusy: false}); break; } } - }*/ + } return valid; } +function getPubmedResponse(id) { + this.getRestData('/articles/' + id) + .then(article => { + // article already exists in db + this.setState({queryResourceBusy: false, tempResource: article, resourceFetched: true}); + }, () => { + var url = external_url_map['PubMedSearch']; + // PubMed article not in our DB; go out to PubMed itself to retrieve it as XML + this.getRestDataXml(external_url_map['PubMedSearch'] + id).then(xml => { + var data = parsePubmed(xml); + if (data.pmid) { + // found the result we want + this.setState({queryResourceBusy: false, tempResource: data, resourceFetched: true}); + } else { + // no result from ClinVar + this.setFormErrors('resourceId', 'PMID not found'); + this.setState({queryResourceBusy: false, resourceFetched: false}); + } + }); + }).catch(e => { + // error handling for PubMed query + this.setFormErrors('resourceId', 'Error querying PubMed. Please check your input and try again.'); + this.setState({queryResourceBusy: false, resourceFetched: false}); + }); +} function pubmedQueryResource() { // for pinging and parsing data from PubMed this.saveFormValue('resourceId', this.state.inputValue); + // Give 'getPubmedResponse' correct scope for 'this' + const receivePubmed = getPubmedResponse.bind(this); if (pubmedValidateForm.call(this)) { var url = external_url_map['PubMedSearch']; var data; // Remove possible prefix like "PMID:" before sending queries var id = this.state.inputValue.replace(/^PMID\s*:\s*(\S*)$/i, '$1'); - this.getRestData('/articles/' + id).then(article => { - // article already exists in db - this.setState({queryResourceBusy: false, tempResource: article, resourceFetched: true}); - }, () => { - var url = external_url_map['PubMedSearch']; - // PubMed article not in our DB; go out to PubMed itself to retrieve it as XML - this.getRestDataXml(external_url_map['PubMedSearch'] + id).then(xml => { - var data = parsePubmed(xml); - if (data.pmid) { - // found the result we want - this.setState({queryResourceBusy: false, tempResource: data, resourceFetched: true}); - } else { - // no result from ClinVar - this.setFormErrors('resourceId', 'PMID not found'); - this.setState({queryResourceBusy: false, resourceFetched: false}); - } + // Check whether input is a valid DOI + const doiRegex = /^10.\d{4,9}\/[-._;()\/:A-Z0-9]+$/gi; + const validDoi = id.match(doiRegex); + // Handle search via DOI + if (validDoi) { + const doiUrl = external_url_map['DoiToPubMed'] + validDoi[0]; + const doiData = this.getRestDataXml(doiUrl).then(xml => { + let data = parsePubmed(xml); + return data; + }) + Promise.resolve(doiData).then(res => { + id = res.pmid; + return id; + }) + // Take the resolved PMID and then query PubMed + .then(id => { + receivePubmed(id); }); - }).catch(e => { - // error handling for PubMed query - this.setFormErrors('resourceId', 'Error querying PubMed. Please check your input and try again.'); - this.setState({queryResourceBusy: false, resourceFetched: false}); - }); + // If not a DOI, query PubMed using PMID + } else { + receivePubmed(id); + } } else { this.setState({queryResourceBusy: false}); } diff --git a/src/clincoded/static/components/curation_central.js b/src/clincoded/static/components/curation_central.js index 4f7afa09e..1cbae558d 100644 --- a/src/clincoded/static/components/curation_central.js +++ b/src/clincoded/static/components/curation_central.js @@ -269,7 +269,7 @@ var PmidSelectionList = createReactClass({
+ protocol={this.props.protocol} parentObj={this.props.currGdm} buttonText="Add New Article" modalButtonText="Add Article" updateParentForm={this.props.updateGdmArticles} buttonOnly={true} />
{annotations ?
@@ -283,6 +283,7 @@ var PmidSelectionList = createReactClass({
PMID: {annotation.article.pmid}
+ { annotation.article.doi ?
DOI: {annotation.article.doi}
: null}
); })} @@ -290,7 +291,7 @@ var PmidSelectionList = createReactClass({ : null} {annotations.length === 0 ?
- Add papers to this Gene-Disease Record using the Add New PMID(s) button; click on any added paper to view its abstract and begin curating evidence from that paper. + Add papers to this Gene-Disease Record using the Add New Article(s) button; click on any added paper to view its abstract and begin curating evidence from that paper.
:null } diff --git a/src/clincoded/static/components/globals.js b/src/clincoded/static/components/globals.js index 54a2c3510..85903ce0c 100644 --- a/src/clincoded/static/components/globals.js +++ b/src/clincoded/static/components/globals.js @@ -164,6 +164,7 @@ module.exports.dbxref_prefix_map = { module.exports.external_url_map = { 'PubMedSearch': 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?api_key=986bd80f43a3ba6ec1bc7a50e7bda60c1b09&db=PubMed&retmode=xml&id=', + 'DoiToPubMed': 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=', 'PubMed': 'https://www.ncbi.nlm.nih.gov/pubmed/', 'OrphaNet': 'http://www.orpha.net/consor/cgi-bin/OC_Exp.php?lng=EN&Expert=', 'OrphanetHome': 'http://www.orpha.net/', diff --git a/src/clincoded/static/libs/parse-pubmed.js b/src/clincoded/static/libs/parse-pubmed.js index a9643b22e..e8169df72 100644 --- a/src/clincoded/static/libs/parse-pubmed.js +++ b/src/clincoded/static/libs/parse-pubmed.js @@ -13,15 +13,28 @@ function parsePubmed(xml){ var article = {}; var doc = new DOMParser().parseFromString(xml, 'text/xml'); + // Get PMID from DOI XML + var $DOIResult = doc.getElementsByTagName('eSearchResult')[0]; + if($DOIResult) { + var $PMID = $DOIResult.getElementsByTagName('Id')[0]; + if($PMID) { + article.pmid = $PMID.textContent; + } + } + var $PubmedArticle = doc.getElementsByTagName('PubmedArticle')[0]; if($PubmedArticle){ var publicationData = ''; - // Get the PMID + // Get the PMID and DOI var $PMID = $PubmedArticle.getElementsByTagName('PMID')[0]; + var $DOI = $PubmedArticle.querySelector('[IdType="doi"]') if($PMID) { article.pmid = $PMID.textContent; } + if($DOI) { + article.doi = $DOI.textContent; + } // Get the journal name var $Journal = $PubmedArticle.getElementsByTagName('Journal')[0]; diff --git a/src/clincoded/static/scss/clincoded/modules/_curator.scss b/src/clincoded/static/scss/clincoded/modules/_curator.scss index cb22eaf9c..27e7dfa98 100644 --- a/src/clincoded/static/scss/clincoded/modules/_curator.scss +++ b/src/clincoded/static/scss/clincoded/modules/_curator.scss @@ -2,9 +2,6 @@ margin-top: 10px; } -.curr-pmid-overview { -} - .pmid-doi-btns { margin: 10px 0; @@ -255,6 +252,7 @@ border: 1px solid darken(#dddddd, 10%); cursor: pointer; background-color: #ffffff; + overflow: hidden; @include border-radius(3px); div { @@ -276,7 +274,7 @@ } .pmid-selection-list-pmid { - font-weight: bold; + font-weight: bold; } .pmid-selection-help { diff --git a/src/clincoded/tests/features/curation_central.feature b/src/clincoded/tests/features/curation_central.feature index c032806ac..a8679ec6b 100644 --- a/src/clincoded/tests/features/curation_central.feature +++ b/src/clincoded/tests/features/curation_central.feature @@ -37,19 +37,19 @@ Feature: Curation Central Then I should see "Any user may explore the demo version of the ClinGen interfaces" - Scenario: Test PMID modal in Curation-central + Scenario: Test Article modal in Curation-central When I visit "/logout" Then I should see "Demo Login" When I press "Demo Login" And I wait for 10 seconds Then I should see "Logout ClinGen Test Curator" When I go to "/curation-central/?gdm=9ffd7c1e-16d9-11e5-b283-60f81dc5b05a/" - Then I should see "Add New PMID" within 5 seconds - When I press "Add New PMID" + Then I should see "Add New Article" within 5 seconds + When I press "Add New Article" Then I should see an element with the css selector ".modal-dialog" within 5 seconds - Then I should see "Enter a PMID" + Then I should see "Enter a PMID or DOI" When I fill in the css element field "input.form-control" with "123456" - When I press the button "Retrieve PubMed Article" + When I press the button "Retrieve Article" Then I should see "Grados OB. " within 10 seconds When I press the button "Add Article" Then I should not see an element with the css selector ".modal-open"