Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connects to #1466: Allow query via DOIs when adding a new Article #1947

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
101 changes: 68 additions & 33 deletions src/clincoded/static/components/add_external_resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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});
Expand All @@ -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});
}
Expand Down
5 changes: 3 additions & 2 deletions src/clincoded/static/components/curation_central.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ var PmidSelectionList = createReactClass({
<div className="pmid-selection-wrapper">
<div className="pmid-selection-add">
<AddResourceId resourceType="pubmed" wrapperClass="inline-button-wrapper-fullwidth" buttonWrapperClass="inline-button-wrapper-fullwidth" buttonClass="btn btn-primary pmid-selection-add-btn"
protocol={this.props.protocol} parentObj={this.props.currGdm} buttonText="Add New PMID" modalButtonText="Add Article" updateParentForm={this.props.updateGdmArticles} buttonOnly={true} />
protocol={this.props.protocol} parentObj={this.props.currGdm} buttonText="Add New Article" modalButtonText="Add Article" updateParentForm={this.props.updateGdmArticles} buttonOnly={true} />
</div>
{annotations ?
<div className="pmid-selection-list" id="user-pmid-list">
Expand All @@ -283,14 +283,15 @@ var PmidSelectionList = createReactClass({
<PmidSummary article={annotation.article} />
</div>
<div className="pmid-selection-list-pmid"><a href={external_url_map['PubMed'] + annotation.article.pmid} target="_blank">PMID: {annotation.article.pmid}</a></div>
{ annotation.article.doi ? <div className="pmid-selection-list-pmid"><a href={external_url_map['PubMed'] + annotation.article.pmid} target="_blank">DOI: {annotation.article.doi}</a></div> : null}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for annotation.article to be undefined? Accessing annotation.article.doi or annotation.article.pmid could throw an error if annotation.article is undefined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Howard! Apologies for it taking a while to come back to this. I have not been able to recreate the issue with an undefined article. Wanted to ask which steps you took when testing to recreate it and figure it out! Thanks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't run into it. I was just wondering if it could ever be the case. I was thinking it could be safer to add a check to make sure the properties are defined, but since we both haven't come across it, then maybe it should be fine.

</div>
);
})}
</div>
: null}
{annotations.length === 0 ?
<div className="pmid-selection-help">
<i>Add papers to this Gene-Disease Record using the <strong>Add New PMID(s)</strong> button; click on any added paper to view its abstract and begin curating evidence from that paper.</i>
<i>Add papers to this Gene-Disease Record using the <strong>Add New Article(s)</strong> button; click on any added paper to view its abstract and begin curating evidence from that paper.</i>
</div>
:null }
</div>
Expand Down
1 change: 1 addition & 0 deletions src/clincoded/static/components/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/',
Expand Down
15 changes: 14 additions & 1 deletion src/clincoded/static/libs/parse-pubmed.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
6 changes: 2 additions & 4 deletions src/clincoded/static/scss/clincoded/modules/_curator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
margin-top: 10px;
}

.curr-pmid-overview {
}

.pmid-doi-btns {
margin: 10px 0;

Expand Down Expand Up @@ -255,6 +252,7 @@
border: 1px solid darken(#dddddd, 10%);
cursor: pointer;
background-color: #ffffff;
overflow: hidden;
@include border-radius(3px);

div {
Expand All @@ -276,7 +274,7 @@
}

.pmid-selection-list-pmid {
font-weight: bold;
font-weight: bold;
}

.pmid-selection-help {
Expand Down
10 changes: 5 additions & 5 deletions src/clincoded/tests/features/curation_central.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down