From 81bb8a4bbd074d6dc1eb5ae0eb90577971d77931 Mon Sep 17 00:00:00 2001 From: Dominic Date: Sun, 24 Oct 2021 11:56:52 +0200 Subject: [PATCH 01/15] Fetching citations from Crossref using REST API --- src/crossref.js | 125 ++++++++++++++++++++++++++++++++++++--- src/sourceItemWrapper.js | 36 ++++++++++- 2 files changed, 151 insertions(+), 10 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 94d9c3d1..fa3e1e6a 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -1,17 +1,126 @@ import Wikicite from './wikicite'; -/* global Services */ -/* global window */ +/* global Zotero */ export default class Crossref{ - static getCitations() { - Services.prompt.alert( - window, - Wikicite.getString('wikicite.global.unsupported'), - Wikicite.getString('wikicite.crossref.get-citations.unsupported') - ); + static async getCitations(doi) { + let url = "https://api.crossref.org/works/" + doi; + + const getRequestJSON = function(url) { + return new Promise((resolve) => { + Zotero.HTTP.doGet(url, function (response){ + if (response.status == 200){ + resolve(JSON.parse(response.responseText)); + } + else{ + Zotero.debug("Couldn't access URL: " + url); + resolve(null); + } + }); + }); + } + const JSONResponse = await getRequestJSON(url); + if (JSONResponse){ + const references = JSONResponse.message.reference + let referenceItems = []; + if (references && references.length > 0){ + for (let reference of references){ + const referenceItem = await Crossref.parseReferenceItem(reference); + if (referenceItem){ + referenceItems.push(referenceItem); + } + } + } + else{ + // Message: we found the item in CrossRef but there were no references + } + return referenceItems; + } + else{ + return []; + } } + static async parseReferenceItem(crossrefItem){ + let newItem = null; + let identifier; + if (crossrefItem.DOI){ + identifier = {DOI: crossrefItem.DOI}; + } + else if(crossrefItem.isbn){ + identifier = {ISBN: crossrefItem.ISBN}; + } + if (identifier){ + newItem = await this.getItemFromIdentifier(identifier); + } + else{ + newItem = this.getItemFromCrossrefReference(crossrefItem); + } + return newItem; + } + + static async getItemFromIdentifier(identifier){ + await Zotero.Schema.schemaUpdatePromise; + let translation = new Zotero.Translate.Search(); + translation.setIdentifier(identifier); + + let jsonItems; + try { + // set libraryID to false so we don't save this item in the Zotero library + jsonItems = await translation.translate({libraryID: false}); + } catch { + Zotero.debug('No items returned for identifier ' + identifier); + } + + if (jsonItems) { + const jsonItem = jsonItems[0] + const newItem = new Zotero.Item(jsonItem.itemType); + newItem.fromJSON(jsonItem); + return newItem; + } + else{ + return null; + } + } + + static getItemFromCrossrefReference(crossrefItem){ + let jsonItem = {}; + if (crossrefItem['journal-title']){ + jsonItem.itemType = 'journalArticle'; + jsonItem.title = crossrefItem['article-title'] || crossrefItem['volume-title']; + } + else if(crossrefItem['volume-title']){ + jsonItem.itemType = 'book'; + jsonItem.title = crossrefItem['volume-title']; + } + else if(crossrefItem.unstructured){ + // Implement reference text parsing here + Zotero.debug("Couldn't parse Crossref reference - unstructured references are not yet supported. " + JSON.stringify(crossrefItem)); + return null; + } + else{ + Zotero.debug("Couldn't determine type of Crossref reference - doesn't contain `journal-title` or `volume-title` field. " + JSON.stringify(crossrefItem)); + return null; + } + jsonItem.date = crossrefItem.year; + jsonItem.pages = crossrefItem['first-page']; + jsonItem.volume = crossrefItem.volume; + jsonItem.issue = crossrefItem.issue; + jsonItem.creators = [{ + 'creatorType': 'author', + 'name': crossrefItem.author + }]; + // remove undefined properties + for (let key in jsonItem){ + if(jsonItem[key] === undefined){ + delete jsonItem[key]; + } + } + const newItem = new Zotero.Item(jsonItem.itemType); + newItem.fromJSON(jsonItem); + return newItem; + } + static getDOI() { } diff --git a/src/sourceItemWrapper.js b/src/sourceItemWrapper.js index ab80106a..286b752c 100644 --- a/src/sourceItemWrapper.js +++ b/src/sourceItemWrapper.js @@ -495,8 +495,40 @@ class SourceItemWrapper extends ItemWrapper { // if provided, sync to wikidata, export to croci, etc, only for that citation // if not, do it for all - getFromCrossref() { - Crossref.getCitations(); + async getFromCrossref() { + if (this.doi){ + const progress = new Progress( + 'loading', + "Getting item citations from CrossRef" + ); + const newCitedItems = await Crossref.getCitations(this.doi); + if (newCitedItems.length > 0){ + let newCitations = []; + for (let newItem of newCitedItems){ + const citation = new Citation({item: newItem, ocis: []}, this); + newCitations.push(citation) + } + this.addCitations(newCitations); + progress.updateLine( + 'done', + "Got item citations from CrossRef" + ); + } + else{ + progress.updateLine( + 'error', + "Couldn't get any citations from CrossRef" + ); + } + } + else{ + // Actually, should the menu item only be enabled if the item has a DOI? + Services.prompt.alert( + window, + "Can't get CrossRef references without DOI", + "Item has no DOI so can't get reference data from CrossRef." + ); + } // fail if item doesn't have a DOI specified // In general I would say to try and get DOI with another plugin if not available locally // call the crossref api From 6f76d9c969f703b678119c06d2bd92877457b76b Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 4 Nov 2021 00:00:49 +0100 Subject: [PATCH 02/15] Cleaned up code for getting Crossref references --- src/crossref.js | 60 ++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index fa3e1e6a..756a0a16 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -4,54 +4,31 @@ import Wikicite from './wikicite'; export default class Crossref{ static async getCitations(doi) { - let url = "https://api.crossref.org/works/" + doi; + const JSONResponse = await Crossref.getDOI(doi); - const getRequestJSON = function(url) { - return new Promise((resolve) => { - Zotero.HTTP.doGet(url, function (response){ - if (response.status == 200){ - resolve(JSON.parse(response.responseText)); - } - else{ - Zotero.debug("Couldn't access URL: " + url); - resolve(null); - } - }); - }); - } - const JSONResponse = await getRequestJSON(url); if (JSONResponse){ const references = JSONResponse.message.reference - let referenceItems = []; if (references && references.length > 0){ - for (let reference of references){ - const referenceItem = await Crossref.parseReferenceItem(reference); - if (referenceItem){ - referenceItems.push(referenceItem); - } - } + const parsedReferences = await Promise.all(references.map(async (reference) => { + const parsedReference = await Crossref.parseReferenceItem(reference); + return parsedReference; + })); + return parsedReferences.filter(Boolean); } else{ - // Message: we found the item in CrossRef but there were no references + Zotero.debug("Item found in Crossref but doesn't contain any references"); } - return referenceItems; - } - else{ - return []; } + return []; } static async parseReferenceItem(crossrefItem){ let newItem = null; - let identifier; if (crossrefItem.DOI){ - identifier = {DOI: crossrefItem.DOI}; + newItem = await this.getItemFromIdentifier({DOI: crossrefItem.DOI}); } else if(crossrefItem.isbn){ - identifier = {ISBN: crossrefItem.ISBN}; - } - if (identifier){ - newItem = await this.getItemFromIdentifier(identifier); + newItem = await this.getItemFromIdentifier({ISBN: crossrefItem.ISBN}); } else{ newItem = this.getItemFromCrossrefReference(crossrefItem); @@ -73,7 +50,7 @@ export default class Crossref{ } if (jsonItems) { - const jsonItem = jsonItems[0] + const jsonItem = jsonItems[0]; const newItem = new Zotero.Item(jsonItem.itemType); newItem.fromJSON(jsonItem); return newItem; @@ -94,7 +71,7 @@ export default class Crossref{ jsonItem.title = crossrefItem['volume-title']; } else if(crossrefItem.unstructured){ - // Implement reference text parsing here + // todo: Implement reference text parsing here Zotero.debug("Couldn't parse Crossref reference - unstructured references are not yet supported. " + JSON.stringify(crossrefItem)); return null; } @@ -121,7 +98,18 @@ export default class Crossref{ return newItem; } - static getDOI() { + static async getDOI(doi) { + let url = "https://api.crossref.org/works/" + doi; + let JSONResponse; + + try{ + const response = await Zotero.HTTP.request('GET', url); + JSONResponse = JSON.parse(response.responseText); + } + catch { + Zotero.debug("Couldn't access URL: " + url); + } + return JSONResponse; } } From c8af711f82f7104ff596af5d304a1fc39609a8ab Mon Sep 17 00:00:00 2001 From: Dominic Date: Thu, 4 Nov 2021 00:27:22 +0100 Subject: [PATCH 03/15] Added localisable strings --- src/crossref.js | 2 -- src/sourceItemWrapper.js | 26 +++++-------------- .../chrome/locale/en-US/wikicite.properties | 6 ++++- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 756a0a16..9c9f2e3f 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -1,5 +1,3 @@ -import Wikicite from './wikicite'; - /* global Zotero */ export default class Crossref{ diff --git a/src/sourceItemWrapper.js b/src/sourceItemWrapper.js index 286b752c..5dfd4bb2 100644 --- a/src/sourceItemWrapper.js +++ b/src/sourceItemWrapper.js @@ -499,7 +499,7 @@ class SourceItemWrapper extends ItemWrapper { if (this.doi){ const progress = new Progress( 'loading', - "Getting item citations from CrossRef" + Wikicite.getString('wikicite.crossref.get-citations.loading') ); const newCitedItems = await Crossref.getCitations(this.doi); if (newCitedItems.length > 0){ @@ -511,38 +511,24 @@ class SourceItemWrapper extends ItemWrapper { this.addCitations(newCitations); progress.updateLine( 'done', - "Got item citations from CrossRef" + Wikicite.getString('wikicite.crossref.get-citations.done') ); } else{ progress.updateLine( 'error', - "Couldn't get any citations from CrossRef" + Wikicite.getString('wikicite.crossref.get-citations.none') ); } } else{ - // Actually, should the menu item only be enabled if the item has a DOI? + // Should never be called because the menu option is only enabled when the item has a DOI Services.prompt.alert( window, - "Can't get CrossRef references without DOI", - "Item has no DOI so can't get reference data from CrossRef." + Wikicite.getString('wikicite.crossref.get-citations.no-doi-title'), + Wikicite.getString('wikicite.crossref.get-citations.no-doi-message') ); } - // fail if item doesn't have a DOI specified - // In general I would say to try and get DOI with another plugin if not available locally - // call the crossref api - // the zotero-citationcounts already calls crossref for citations. Check it - // call this.add multiple times, or provide an aray - // if citation retrieved has doi, check if citation already exists locally - // if yes, set providers.crossref to true - // decide whether to ignore citations retrieved without doi - // or if I will check if they exist already using other fields (title, etc) - - // offer to automatically get QID from wikidata for target items - // using Wikidata.getQID(items) - - // offer to automatically link to zotero items } getFromOcc() { diff --git a/static/chrome/locale/en-US/wikicite.properties b/static/chrome/locale/en-US/wikicite.properties index adf8544e..d61b6963 100644 --- a/static/chrome/locale/en-US/wikicite.properties +++ b/static/chrome/locale/en-US/wikicite.properties @@ -34,7 +34,11 @@ wikicite.citations-pane.linked.confirm.message = This citation is linked to item wikicite.citations-pane.linked.confirm.title = Linked citation wikicite.citations-pane.linked.confirm.unlink = Unlink wikicite.citations-pane.more = More... -wikicite.crossref.get-citations.unsupported = Getting citations from Crossref not yet supported +wikicite.crossref.get-citations.done = Got item citations from CrossRef +wikicite.crossref.get-citations.loading = Getting item citations from CrossRef +wikicite.crossref.get-citations.none = Couldn\'t get item citations from CrossRef +wikicite.crossref.get-citations.no-doi-message = Item has no DOI so reference data can\'t be retrieved from CrossRef. +wikicite.crossref.get-citations.no-doi-title = Can\'t get CrossRef references for item without DOI wikicite.editor.cancel = Cancel wikicite.editor.save = Save wikicite.editor.title = Citation Editor From dfbd5c985bd785f51a2e64de8f292d3417dcbb4f Mon Sep 17 00:00:00 2001 From: Dominic Date: Sun, 7 Nov 2021 15:12:20 +0100 Subject: [PATCH 04/15] Simplify converting new items to citations --- src/sourceItemWrapper.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/sourceItemWrapper.js b/src/sourceItemWrapper.js index 5dfd4bb2..4636f7b6 100644 --- a/src/sourceItemWrapper.js +++ b/src/sourceItemWrapper.js @@ -503,11 +503,7 @@ class SourceItemWrapper extends ItemWrapper { ); const newCitedItems = await Crossref.getCitations(this.doi); if (newCitedItems.length > 0){ - let newCitations = []; - for (let newItem of newCitedItems){ - const citation = new Citation({item: newItem, ocis: []}, this); - newCitations.push(citation) - } + const newCitations = newCitedItems.map((newItem) => new Citation({item: newItem, ocis: []}, this)); this.addCitations(newCitations); progress.updateLine( 'done', From cd7290eb07033db7527220ebe2fe6a0ab8f6284b Mon Sep 17 00:00:00 2001 From: Dominic Date: Wed, 23 Feb 2022 23:53:32 +0100 Subject: [PATCH 05/15] Move functionality to `Crossref` class --- src/crossref.js | 73 +++++++++++++++++++++++++++++++++++++--- src/sourceItemWrapper.js | 32 ++---------------- src/zoteroOverlay.jsx | 10 ++++-- 3 files changed, 77 insertions(+), 38 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 9c9f2e3f..81bed1b1 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -1,6 +1,69 @@ +import Wikicite, { debug } from './wikicite'; +import Citation from './citation'; +import Progress from './progress'; +/* global Services */ /* global Zotero */ +/* global window */ export default class Crossref{ + + /** + * Sync source item citations with Wikidata. + * @param {SourceItemWrapper[]} sourceItems - One or more source items to sync citations for. + */ + static async addCrossrefCitationsToItems(sourceItems){ + + const sourceItemsWithDOI = sourceItems.filter((sourceItem) => sourceItem.getPID('DOI')); + + if (sourceItemsWithDOI.length == 0){ + Services.prompt.alert( + window, + Wikicite.getString('wikicite.crossref.get-citations.no-doi-title'), + Wikicite.getString('wikicite.crossref.get-citations.no-doi-message') + ); + return; + } + else if(sourceItemsWithDOI.length < sourceItems.length){ + const confirmed = Services.prompt.confirm( + window, + 'Some items do not have DOIs', // Wikicite.getString('wikicite.crossref.get-citations.no-doi-title'), + 'Get CrossRef citations for items with DOIs?'// Wikicite.getString('wikicite.crossref.get-citations.no-doi-message') + ); + if (!confirmed){ + return; + } + } + + const progress = new Progress( + 'loading', + Wikicite.getString('wikicite.crossref.get-citations.loading') + ); + + try{ + // fix: this await is broken by an error + await Promise.allSettled(sourceItemsWithDOI.forEach(async (sourceItem) => { + const newCitedItems = await Crossref.getCitations(sourceItem.doi); + if (newCitedItems.length > 0){ + const newCitations = newCitedItems.map((newItem) => new Citation({item: newItem, ocis: []}, sourceItem)); + sourceItem.addCitations(newCitations); + } + })); + } + catch (error){ + progress.updateLine( + 'error', + Wikicite.getString('wikicite.crossref.get-citations.none') + ); + debug(error); + return; + } + + progress.updateLine( + 'done', + Wikicite.getString('wikicite.crossref.get-citations.done') + ); + } + static async getCitations(doi) { const JSONResponse = await Crossref.getDOI(doi); @@ -14,7 +77,7 @@ export default class Crossref{ return parsedReferences.filter(Boolean); } else{ - Zotero.debug("Item found in Crossref but doesn't contain any references"); + debug("Item found in Crossref but doesn't contain any references"); } } return []; @@ -44,7 +107,7 @@ export default class Crossref{ // set libraryID to false so we don't save this item in the Zotero library jsonItems = await translation.translate({libraryID: false}); } catch { - Zotero.debug('No items returned for identifier ' + identifier); + debug('No items returned for identifier ' + identifier); } if (jsonItems) { @@ -70,11 +133,11 @@ export default class Crossref{ } else if(crossrefItem.unstructured){ // todo: Implement reference text parsing here - Zotero.debug("Couldn't parse Crossref reference - unstructured references are not yet supported. " + JSON.stringify(crossrefItem)); + debug("Couldn't parse Crossref reference - unstructured references are not yet supported. " + JSON.stringify(crossrefItem)); return null; } else{ - Zotero.debug("Couldn't determine type of Crossref reference - doesn't contain `journal-title` or `volume-title` field. " + JSON.stringify(crossrefItem)); + debug("Couldn't determine type of Crossref reference - doesn't contain `journal-title` or `volume-title` field. " + JSON.stringify(crossrefItem)); return null; } jsonItem.date = crossrefItem.year; @@ -105,7 +168,7 @@ export default class Crossref{ JSONResponse = JSON.parse(response.responseText); } catch { - Zotero.debug("Couldn't access URL: " + url); + debug("Couldn't access URL: " + url); } return JSONResponse; diff --git a/src/sourceItemWrapper.js b/src/sourceItemWrapper.js index 4636f7b6..b3c701e3 100644 --- a/src/sourceItemWrapper.js +++ b/src/sourceItemWrapper.js @@ -495,36 +495,8 @@ class SourceItemWrapper extends ItemWrapper { // if provided, sync to wikidata, export to croci, etc, only for that citation // if not, do it for all - async getFromCrossref() { - if (this.doi){ - const progress = new Progress( - 'loading', - Wikicite.getString('wikicite.crossref.get-citations.loading') - ); - const newCitedItems = await Crossref.getCitations(this.doi); - if (newCitedItems.length > 0){ - const newCitations = newCitedItems.map((newItem) => new Citation({item: newItem, ocis: []}, this)); - this.addCitations(newCitations); - progress.updateLine( - 'done', - Wikicite.getString('wikicite.crossref.get-citations.done') - ); - } - else{ - progress.updateLine( - 'error', - Wikicite.getString('wikicite.crossref.get-citations.none') - ); - } - } - else{ - // Should never be called because the menu option is only enabled when the item has a DOI - Services.prompt.alert( - window, - Wikicite.getString('wikicite.crossref.get-citations.no-doi-title'), - Wikicite.getString('wikicite.crossref.get-citations.no-doi-message') - ); - } + getFromCrossref() { + Crossref.addCrossrefCitationsToItems([this]); } getFromOcc() { diff --git a/src/zoteroOverlay.jsx b/src/zoteroOverlay.jsx index 9ca467ee..b2ad145b 100644 --- a/src/zoteroOverlay.jsx +++ b/src/zoteroOverlay.jsx @@ -1,4 +1,5 @@ import Wikicite, { debug } from './wikicite'; +import Citation from './citation'; import Citations from './citations'; import CitationsBoxContainer from './containers/citationsBoxContainer'; import Crossref from './crossref'; @@ -360,12 +361,15 @@ const zoteroOverlay = { } }, - getFromCrossref: function(menuName) { + getFromCrossref: async function(menuName) { // get items selected // filter items with doi // generate batch call to crossref // only add items not available locally yet - Crossref.getCitations(); + const items = await this.getSelectedItems(menuName, true); + if (items.length){ + Crossref.addCrossrefCitationsToItems(items); + } }, getFromOCC: function(menuName) { @@ -861,7 +865,7 @@ const zoteroOverlay = { doc.getElementById('citation-menu-fetch-qid').disabled = false; doc.getElementById('citation-menu-file-export').disabled = false; doc.getElementById('citation-menu-croci-export').disabled = !sourceItem.doi || !targetItem.doi; - doc.getElementById('citation-menu-oci-crossref').disabled = !ociSuppliers.includes('crossref'); + doc.getElementById('citation-menu-oci-crossref').disabled = !sourceItem.doi || !ociSuppliers.includes('crossref'); doc.getElementById('citation-menu-oci-occ').disabled = !ociSuppliers.includes('occ'); doc.getElementById('citation-menu-oci-wikidata').disabled = !ociSuppliers.includes('wikidata'); }, From 6b9fde5ac8d6da27d8f4a2a46dd55ca93cd16528 Mon Sep 17 00:00:00 2001 From: Dom Date: Fri, 28 Oct 2022 01:10:27 +0200 Subject: [PATCH 06/15] Fetching citations from crossref seems to be working --- src/crossref.js | 50 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 81bed1b1..cfb2cf59 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -40,14 +40,36 @@ export default class Crossref{ ); try{ - // fix: this await is broken by an error - await Promise.allSettled(sourceItemsWithDOI.forEach(async (sourceItem) => { - const newCitedItems = await Crossref.getCitations(sourceItem.doi); - if (newCitedItems.length > 0){ - const newCitations = newCitedItems.map((newItem) => new Citation({item: newItem, ocis: []}, sourceItem)); - sourceItem.addCitations(newCitations); - } + const sourceItemCitations = await Promise.all(sourceItemsWithDOI.map(async (sourceItem) => { + return await Crossref.getCitations(sourceItem.doi); })); + + const numberOfCitations = sourceItemCitations.map((citations) => citations.length); + const itemsToBeUpdated = numberOfCitations.filter((number) => number > 0).length; + const citationsToBeAdded = numberOfCitations.reduce((sum, value) => sum + value, 0); + + const confirmed = Services.prompt.confirm( + window, + 'Add citations from Crossref', + `${itemsToBeUpdated} item(s) will be updated\n${citationsToBeAdded} citation(s) will be added.` + ) + + if (confirmed){ + await Zotero.DB.executeTransaction(async function() { + for (let i = 0; i < sourceItemsWithDOI.length; i++){ + const sourceItem = sourceItemsWithDOI[i]; + const newCitedItems = sourceItemCitations[i]; + if (newCitedItems.length > 0){ + const newCitations = newCitedItems.map((newItem) => new Citation({item: newItem, ocis: []}, sourceItem)); + sourceItem.addCitations(newCitations); + } + } + }) + progress.updateLine( + 'done', + Wikicite.getString('wikicite.crossref.get-citations.done') + ); + } } catch (error){ progress.updateLine( @@ -55,13 +77,10 @@ export default class Crossref{ Wikicite.getString('wikicite.crossref.get-citations.none') ); debug(error); - return; } - - progress.updateLine( - 'done', - Wikicite.getString('wikicite.crossref.get-citations.done') - ); + finally { + progress.close(); + } } static async getCitations(doi) { @@ -112,6 +131,11 @@ export default class Crossref{ if (jsonItems) { const jsonItem = jsonItems[0]; + // delete irrelevant fields to avoid warnings in Item#fromJSON + delete jsonItem['notes']; + delete jsonItem['seeAlso']; + delete jsonItem['attachments']; + const newItem = new Zotero.Item(jsonItem.itemType); newItem.fromJSON(jsonItem); return newItem; From b7970738201d3765d5e71fc005a31c85b3bc3b48 Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 31 Oct 2022 00:28:05 +0100 Subject: [PATCH 07/15] Update workflow for getting Crossref references 1. Get list of references from Crossref for items - show how many to the user 2. Parse the references source item by source item, indicating progress because it can take a long time 3. Add references for all source items --- package.json | 2 +- src/crossref.js | 188 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 127 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index d25bad90..151c4156 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-cita", - "version": "0.4.0", + "version": "0.5.1-beta", "description": "Cita: a Wikidata addon for Zotero", "scripts": { "postinstall": "patch-package", diff --git a/src/crossref.js b/src/crossref.js index cfb2cf59..c650ddfd 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -8,13 +8,13 @@ import Progress from './progress'; export default class Crossref{ /** - * Sync source item citations with Wikidata. - * @param {SourceItemWrapper[]} sourceItems - One or more source items to sync citations for. + * Get source item citations from CrossRef. + * @param {SourceItemWrapper[]} sourceItems - One or more source items to get citations for. */ static async addCrossrefCitationsToItems(sourceItems){ + // Make sure that at least some of the source items have DOIs const sourceItemsWithDOI = sourceItems.filter((sourceItem) => sourceItem.getPID('DOI')); - if (sourceItemsWithDOI.length == 0){ Services.prompt.alert( window, @@ -23,58 +23,83 @@ export default class Crossref{ ); return; } - else if(sourceItemsWithDOI.length < sourceItems.length){ - const confirmed = Services.prompt.confirm( - window, - 'Some items do not have DOIs', // Wikicite.getString('wikicite.crossref.get-citations.no-doi-title'), - 'Get CrossRef citations for items with DOIs?'// Wikicite.getString('wikicite.crossref.get-citations.no-doi-message') - ); - if (!confirmed){ - return; - } - } + // Get reference information for items from CrossRef const progress = new Progress( 'loading', Wikicite.getString('wikicite.crossref.get-citations.loading') ); + let sourceItemReferences; try{ - const sourceItemCitations = await Promise.all(sourceItemsWithDOI.map(async (sourceItem) => { - return await Crossref.getCitations(sourceItem.doi); - })); + sourceItemReferences = await Promise.all( + sourceItemsWithDOI.map(async (sourceItem) => await Crossref.getReferences(sourceItem.doi)) + ); + } + catch (error){ + progress.updateLine( + 'error', + 'Failed to get references for items from Crossref.' + ); + debug(error); + return; + } + + // Confirm with the user to add these citations + const numberOfCitations = sourceItemReferences.map((references) => references.length); + const itemsToBeUpdated = numberOfCitations.filter((number) => number > 0).length; + const citationsToBeAdded = numberOfCitations.reduce((sum, value) => sum + value, 0); + const confirmed = Services.prompt.confirm( + window, + 'Add citations from Crossref', + `Found references for ${itemsToBeUpdated} out of ${sourceItems.length} item(s).\n${citationsToBeAdded} citation(s) will be parsed.` + ) + if (!confirmed){ + return; + } + + // Parse this reference information, then add to sourceItems + progress.updateLine( + 'loading', + 'Parsing CrossRef references' + ); - const numberOfCitations = sourceItemCitations.map((citations) => citations.length); - const itemsToBeUpdated = numberOfCitations.filter((number) => number > 0).length; - const citationsToBeAdded = numberOfCitations.reduce((sum, value) => sum + value, 0); + try { + let parsedItemReferences = []; + let parsedItems = 0; + for (let sourceItemReferenceList of sourceItemReferences) { + if (!sourceItemReferenceList.length) { + parsedItemReferences.push([]); + continue; + } - const confirmed = Services.prompt.confirm( - window, - 'Add citations from Crossref', - `${itemsToBeUpdated} item(s) will be updated\n${citationsToBeAdded} citation(s) will be added.` - ) - - if (confirmed){ - await Zotero.DB.executeTransaction(async function() { - for (let i = 0; i < sourceItemsWithDOI.length; i++){ - const sourceItem = sourceItemsWithDOI[i]; - const newCitedItems = sourceItemCitations[i]; - if (newCitedItems.length > 0){ - const newCitations = newCitedItems.map((newItem) => new Citation({item: newItem, ocis: []}, sourceItem)); - sourceItem.addCitations(newCitations); - } - } - }) + parsedItemReferences.push(await Crossref.parseReferences(sourceItemReferenceList)); progress.updateLine( - 'done', - Wikicite.getString('wikicite.crossref.get-citations.done') + 'loading', + `Parsed references for ${++parsedItems} out of ${itemsToBeUpdated} item(s).` ); } + + // Add these citations to the items + await Zotero.DB.executeTransaction(async function() { + for (let i = 0; i < sourceItemsWithDOI.length; i++){ + const sourceItem = sourceItemsWithDOI[i]; + const newCitedItems = parsedItemReferences[i]; + if (newCitedItems.length > 0){ + const newCitations = newCitedItems.map((newItem) => new Citation({item: newItem, ocis: []}, sourceItem)); + sourceItem.addCitations(newCitations); + } + } + }) + progress.updateLine( + 'done', + Wikicite.getString('wikicite.crossref.get-citations.done') + ); } catch (error){ progress.updateLine( 'error', - Wikicite.getString('wikicite.crossref.get-citations.none') + 'Failed to parse Crossref references' ); debug(error); } @@ -83,25 +108,43 @@ export default class Crossref{ } } - static async getCitations(doi) { - const JSONResponse = await Crossref.getDOI(doi); - - if (JSONResponse){ - const references = JSONResponse.message.reference - if (references && references.length > 0){ - const parsedReferences = await Promise.all(references.map(async (reference) => { - const parsedReference = await Crossref.parseReferenceItem(reference); - return parsedReference; - })); - return parsedReferences.filter(Boolean); - } - else{ - debug("Item found in Crossref but doesn't contain any references"); - } + /** + * Get a list of references from Crossref for an item with a certain DOI. + * Returned in JSON Crossref format. + * @param {string} doi - DOI for the item for which to get references. + * @returns {Promise} list of references, or [] if none. + */ + static async getReferences(doi) { + const JSONResponse = await Crossref.getCrossrefDOI(doi); + if (!JSONResponse || !JSONResponse.message.reference) { + return []; + } + + return JSONResponse.message.reference; + } + + /** + * Parse a list of references in JSON Crossref format. + * @param {string[]} crossrefReferences - Array of Crossref references to parse to Zotero items. + * @returns {Promise} Zotero items parsed from references (where parsing is possible). + */ + static async parseReferences(crossrefReferences){ + if (!crossrefReferences.length){ + debug("Item found in Crossref but doesn't contain any references"); + return []; } - return []; - } + const parsedReferences = await Promise.all( + crossrefReferences.map(async (reference) => await Crossref.parseReferenceItem(reference)) + ); + return parsedReferences.filter(Boolean); + } + + /** + * Parse a single item in JSON Crossref format to a Zotero Item. + * @param {string} crossrefItem - An reference item in JSON Crossref format. + * @returns {Promise} Zotero item parsed from the Crossref reference, or null if parsing failed. + */ static async parseReferenceItem(crossrefItem){ let newItem = null; if (crossrefItem.DOI){ @@ -111,11 +154,16 @@ export default class Crossref{ newItem = await this.getItemFromIdentifier({ISBN: crossrefItem.ISBN}); } else{ - newItem = this.getItemFromCrossrefReference(crossrefItem); + newItem = this.parseItemFromCrossrefReference(crossrefItem); } return newItem; } + /** + * Get a Zotero Item from a valid Zotero identifier - includes DOI, ISBN, PMID, ArXiv ID, and more. + * @param {{string: string}} identifier - A reference item in JSON Crossref format. + * @returns {Promise} Zotero item parsed from the identifier, or null if parsing failed. + */ static async getItemFromIdentifier(identifier){ await Zotero.Schema.schemaUpdatePromise; let translation = new Zotero.Translate.Search(); @@ -145,7 +193,12 @@ export default class Crossref{ } } - static getItemFromCrossrefReference(crossrefItem){ + /** + * Get a Zotero Item from a Crossref reference item that doesn't include an identifier. + * @param {string} crossrefItem - A reference item in JSON Crossref format. + * @returns {Promise} Zotero item parsed from the identifier, or null if parsing failed. + */ + static parseItemFromCrossrefReference(crossrefItem){ let jsonItem = {}; if (crossrefItem['journal-title']){ jsonItem.itemType = 'journalArticle'; @@ -183,12 +236,23 @@ export default class Crossref{ return newItem; } - static async getDOI(doi) { - let url = "https://api.crossref.org/works/" + doi; + /** + * Get the information about an item from Crossref via DOI lookup. + * @param {string} doi - DOI for the item of interest. + * @returns {Promise} JSON Crossref item. Format described at https://api.crossref.org/swagger-ui/index.html#/Works/get_works__doi_ + */ + static async getCrossrefDOI(doi) { + let url = `https://api.crossref.org/works/${Zotero.Utilities.cleanDOI(doi)}`; let JSONResponse; try{ - const response = await Zotero.HTTP.request('GET', url); + const response = await Zotero.HTTP.request('GET', + url, + { + headers: { + 'User-Agent': `${Wikicite.getUserAgent()} mailto:cita@duck.com` + } + }); JSONResponse = JSON.parse(response.responseText); } catch { @@ -196,5 +260,5 @@ export default class Crossref{ } return JSONResponse; - } + } } From 4d6f0feae7804289544bdeb731cef9697c4dbec3 Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 31 Oct 2022 00:35:20 +0100 Subject: [PATCH 08/15] Handle the case where none of the items with DOIs have references on Crossref --- src/crossref.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/crossref.js b/src/crossref.js index c650ddfd..24071652 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -49,12 +49,20 @@ export default class Crossref{ const numberOfCitations = sourceItemReferences.map((references) => references.length); const itemsToBeUpdated = numberOfCitations.filter((number) => number > 0).length; const citationsToBeAdded = numberOfCitations.reduce((sum, value) => sum + value, 0); + if (numberOfCitations == 0){ + progress.updateLine( + 'error', + 'Found no Crossref references for items.' + ); + return; + } const confirmed = Services.prompt.confirm( window, 'Add citations from Crossref', `Found references for ${itemsToBeUpdated} out of ${sourceItems.length} item(s).\n${citationsToBeAdded} citation(s) will be parsed.` ) if (!confirmed){ + progress.close(); return; } From ed795bd762cc66dabd4099f67a0ab67793220984 Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 31 Oct 2022 23:58:04 +0100 Subject: [PATCH 09/15] Localise Crossref messages --- src/crossref.js | 14 +++++++------- static/chrome/locale/en-US/wikicite.properties | 16 +++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 24071652..e9f03a9d 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -39,7 +39,7 @@ export default class Crossref{ catch (error){ progress.updateLine( 'error', - 'Failed to get references for items from Crossref.' + Wikicite.getString('wikicite.crossref.get-citations.error-getting-references') ); debug(error); return; @@ -52,14 +52,14 @@ export default class Crossref{ if (numberOfCitations == 0){ progress.updateLine( 'error', - 'Found no Crossref references for items.' + Wikicite.getString('wikicite.crossref.get-citations.no-references') ); return; } const confirmed = Services.prompt.confirm( window, - 'Add citations from Crossref', - `Found references for ${itemsToBeUpdated} out of ${sourceItems.length} item(s).\n${citationsToBeAdded} citation(s) will be parsed.` + Wikicite.getString('wikicite.crossref.get-citations.confirm-title'), + Wikicite.formatString('wikicite.crossref.get-citations.confirm-message', [itemsToBeUpdated, sourceItems.length, citationsToBeAdded]) ) if (!confirmed){ progress.close(); @@ -69,7 +69,7 @@ export default class Crossref{ // Parse this reference information, then add to sourceItems progress.updateLine( 'loading', - 'Parsing CrossRef references' + Wikicite.getString('wikicite.crossref.get-citations.parsing') ); try { @@ -84,7 +84,7 @@ export default class Crossref{ parsedItemReferences.push(await Crossref.parseReferences(sourceItemReferenceList)); progress.updateLine( 'loading', - `Parsed references for ${++parsedItems} out of ${itemsToBeUpdated} item(s).` + Wikicite.formatString('wikicite.crossref.get-citations.parsing-progress', [++parsedItems, itemsToBeUpdated]) ); } @@ -107,7 +107,7 @@ export default class Crossref{ catch (error){ progress.updateLine( 'error', - 'Failed to parse Crossref references' + Wikicite.getString('wikicite.crossref.get-citations.error-parsing-references') ); debug(error); } diff --git a/static/chrome/locale/en-US/wikicite.properties b/static/chrome/locale/en-US/wikicite.properties index d61b6963..fe11daaf 100644 --- a/static/chrome/locale/en-US/wikicite.properties +++ b/static/chrome/locale/en-US/wikicite.properties @@ -34,11 +34,17 @@ wikicite.citations-pane.linked.confirm.message = This citation is linked to item wikicite.citations-pane.linked.confirm.title = Linked citation wikicite.citations-pane.linked.confirm.unlink = Unlink wikicite.citations-pane.more = More... -wikicite.crossref.get-citations.done = Got item citations from CrossRef -wikicite.crossref.get-citations.loading = Getting item citations from CrossRef -wikicite.crossref.get-citations.none = Couldn\'t get item citations from CrossRef -wikicite.crossref.get-citations.no-doi-message = Item has no DOI so reference data can\'t be retrieved from CrossRef. -wikicite.crossref.get-citations.no-doi-title = Can\'t get CrossRef references for item without DOI +wikicite.crossref.get-citations.confirm-message = Found references for %s out of %s item(s).\n%s citation(s) will be parsed. +wikicite.crossref.get-citations.confirm-title = Add citations from Crossref? +wikicite.crossref.get-citations.done = Got item citations from Crossref +wikicite.crossref.get-citations.error-getting-references = Failed to get citations for item(s) from Crossref +wikicite.crossref.get-citations.error-parsing-references = Failed to parse citations from Crossref +wikicite.crossref.get-citations.loading = Getting item citations from Crossref +wikicite.crossref.get-citations.no-references = Didn't find any Crossref citations for item(s) +wikicite.crossref.get-citations.no-doi-message = No items with a DOI provided - reference data can't be retrieved from Crossref. +wikicite.crossref.get-citations.no-doi-title = Can't get Crossref references for items without DOIs +wikicite.crossref.get-citations.parsing = Parsing item citations from Crossref +wikicite.crossref.get-citations.parsing-progress = Parsed citations for %s out of %s items wikicite.editor.cancel = Cancel wikicite.editor.save = Save wikicite.editor.title = Citation Editor From 64bb696b9ebfd6baf53c04baab7defccbec675c7 Mon Sep 17 00:00:00 2001 From: Dom Date: Tue, 1 Nov 2022 00:01:26 +0100 Subject: [PATCH 10/15] Fix bug when no references were found --- src/crossref.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crossref.js b/src/crossref.js index e9f03a9d..52275950 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -49,7 +49,7 @@ export default class Crossref{ const numberOfCitations = sourceItemReferences.map((references) => references.length); const itemsToBeUpdated = numberOfCitations.filter((number) => number > 0).length; const citationsToBeAdded = numberOfCitations.reduce((sum, value) => sum + value, 0); - if (numberOfCitations == 0){ + if (citationsToBeAdded == 0){ progress.updateLine( 'error', Wikicite.getString('wikicite.crossref.get-citations.no-references') From c341e09823f417983585db96dc762182cdc15ff1 Mon Sep 17 00:00:00 2001 From: Dom Date: Thu, 3 Nov 2022 00:27:01 +0100 Subject: [PATCH 11/15] Updates to code structure from feedback --- src/crossref.js | 97 ++++++++++++++++++------------------------------- 1 file changed, 35 insertions(+), 62 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 52275950..213b3a87 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -123,12 +123,22 @@ export default class Crossref{ * @returns {Promise} list of references, or [] if none. */ static async getReferences(doi) { - const JSONResponse = await Crossref.getCrossrefDOI(doi); - if (!JSONResponse || !JSONResponse.message.reference) { - return []; - } + const url = `https://api.crossref.org/works/${Zotero.Utilities.cleanDOI( + doi + )}`; + const options = { + headers: { + "User-Agent": `${Wikicite.getUserAgent()} mailto:cita@duck.com`, + }, + responseType: "json", + }; + + const response = await Zotero.HTTP.request("GET", url, options).catch(() => + debug("Couldn't access URL: " + url) + ); + if (!response) return []; - return JSONResponse.message.reference; + return response.response.message.reference || []; } /** @@ -136,35 +146,26 @@ export default class Crossref{ * @param {string[]} crossrefReferences - Array of Crossref references to parse to Zotero items. * @returns {Promise} Zotero items parsed from references (where parsing is possible). */ - static async parseReferences(crossrefReferences){ - if (!crossrefReferences.length){ + static async parseReferences(crossrefReferences) { + if (!crossrefReferences.length) { debug("Item found in Crossref but doesn't contain any references"); return []; } - const parsedReferences = await Promise.all( - crossrefReferences.map(async (reference) => await Crossref.parseReferenceItem(reference)) - ); - return parsedReferences.filter(Boolean); - } + const parsedReferences = await Zotero.Promise.allSettled( + crossrefReferences.map(async (crossrefItem) => { + if (crossrefItem.DOI) + return this.getItemFromIdentifier({ DOI: crossrefItem.DOI }); - /** - * Parse a single item in JSON Crossref format to a Zotero Item. - * @param {string} crossrefItem - An reference item in JSON Crossref format. - * @returns {Promise} Zotero item parsed from the Crossref reference, or null if parsing failed. - */ - static async parseReferenceItem(crossrefItem){ - let newItem = null; - if (crossrefItem.DOI){ - newItem = await this.getItemFromIdentifier({DOI: crossrefItem.DOI}); - } - else if(crossrefItem.isbn){ - newItem = await this.getItemFromIdentifier({ISBN: crossrefItem.ISBN}); - } - else{ - newItem = this.parseItemFromCrossrefReference(crossrefItem); - } - return newItem; + if (crossrefItem.isbn) + return this.getItemFromIdentifier({ ISBN: crossrefItem.ISBN }); + + return this.parseItemFromCrossrefReference(crossrefItem); + }) + ); + return parsedReferences + .map((reference) => reference.value || null) + .filter(Boolean); } /** @@ -204,7 +205,7 @@ export default class Crossref{ /** * Get a Zotero Item from a Crossref reference item that doesn't include an identifier. * @param {string} crossrefItem - A reference item in JSON Crossref format. - * @returns {Promise} Zotero item parsed from the identifier, or null if parsing failed. + * @returns {Promise} Zotero item parsed from the identifier, or null if parsing failed. */ static parseItemFromCrossrefReference(crossrefItem){ let jsonItem = {}; @@ -218,12 +219,10 @@ export default class Crossref{ } else if(crossrefItem.unstructured){ // todo: Implement reference text parsing here - debug("Couldn't parse Crossref reference - unstructured references are not yet supported. " + JSON.stringify(crossrefItem)); - return null; + return Promise.reject("Couldn't parse Crossref reference - unstructured references are not yet supported. " + JSON.stringify(crossrefItem)); } else{ - debug("Couldn't determine type of Crossref reference - doesn't contain `journal-title` or `volume-title` field. " + JSON.stringify(crossrefItem)); - return null; + return Promise.reject("Couldn't determine type of Crossref reference - doesn't contain `journal-title` or `volume-title` field. " + JSON.stringify(crossrefItem)); } jsonItem.date = crossrefItem.year; jsonItem.pages = crossrefItem['first-page']; @@ -241,32 +240,6 @@ export default class Crossref{ } const newItem = new Zotero.Item(jsonItem.itemType); newItem.fromJSON(jsonItem); - return newItem; - } - - /** - * Get the information about an item from Crossref via DOI lookup. - * @param {string} doi - DOI for the item of interest. - * @returns {Promise} JSON Crossref item. Format described at https://api.crossref.org/swagger-ui/index.html#/Works/get_works__doi_ - */ - static async getCrossrefDOI(doi) { - let url = `https://api.crossref.org/works/${Zotero.Utilities.cleanDOI(doi)}`; - let JSONResponse; - - try{ - const response = await Zotero.HTTP.request('GET', - url, - { - headers: { - 'User-Agent': `${Wikicite.getUserAgent()} mailto:cita@duck.com` - } - }); - JSONResponse = JSON.parse(response.responseText); - } - catch { - debug("Couldn't access URL: " + url); - } - - return JSONResponse; + return Promise.resolve(newItem); } -} +} \ No newline at end of file From 8ba4abcdbb48beaf11b6ec41fcbf192a87182640 Mon Sep 17 00:00:00 2001 From: Dom Date: Thu, 3 Nov 2022 22:39:18 +0100 Subject: [PATCH 12/15] Change for loops to map or forEach --- src/crossref.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 213b3a87..3bfe957f 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -73,32 +73,29 @@ export default class Crossref{ ); try { - let parsedItemReferences = []; let parsedItems = 0; - for (let sourceItemReferenceList of sourceItemReferences) { - if (!sourceItemReferenceList.length) { - parsedItemReferences.push([]); - continue; - } - - parsedItemReferences.push(await Crossref.parseReferences(sourceItemReferenceList)); + const parsedItemReferences = await Promise.all(sourceItemReferences.map(async (sourceItemReferenceList) => { + if (!sourceItemReferenceList.length) + return []; + + const parsedReferences = await Crossref.parseReferences(sourceItemReferenceList); progress.updateLine( 'loading', Wikicite.formatString('wikicite.crossref.get-citations.parsing-progress', [++parsedItems, itemsToBeUpdated]) ); - } + return parsedReferences; + })); // Add these citations to the items await Zotero.DB.executeTransaction(async function() { - for (let i = 0; i < sourceItemsWithDOI.length; i++){ - const sourceItem = sourceItemsWithDOI[i]; - const newCitedItems = parsedItemReferences[i]; + sourceItemsWithDOI.forEach((sourceItem, index) => { + const newCitedItems = parsedItemReferences[index]; if (newCitedItems.length > 0){ const newCitations = newCitedItems.map((newItem) => new Citation({item: newItem, ocis: []}, sourceItem)); sourceItem.addCitations(newCitations); } - } - }) + }); + }); progress.updateLine( 'done', Wikicite.getString('wikicite.crossref.get-citations.done') From c4969d657c56565672d80f59443b406ba1a381e1 Mon Sep 17 00:00:00 2001 From: Dom Date: Fri, 6 Jan 2023 21:30:51 +0100 Subject: [PATCH 13/15] Remove version change --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 151c4156..d25bad90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-cita", - "version": "0.5.1-beta", + "version": "0.4.0", "description": "Cita: a Wikidata addon for Zotero", "scripts": { "postinstall": "patch-package", From 731b03c5b17f6b644e8eba4c409e74d1cb53db33 Mon Sep 17 00:00:00 2001 From: Dom Date: Fri, 6 Jan 2023 23:52:13 +0100 Subject: [PATCH 14/15] Add comment about 429 responses --- src/crossref.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 3bfe957f..9c429aeb 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -130,8 +130,8 @@ export default class Crossref{ responseType: "json", }; - const response = await Zotero.HTTP.request("GET", url, options).catch(() => - debug("Couldn't access URL: " + url) + const response = await Zotero.HTTP.request("GET", url, options).catch((e) => + debug(`Couldn't access URL: ${url}. Got status ${e.xmlhttp.status}.`) ); if (!response) return []; @@ -180,7 +180,12 @@ export default class Crossref{ // set libraryID to false so we don't save this item in the Zotero library jsonItems = await translation.translate({libraryID: false}); } catch { - debug('No items returned for identifier ' + identifier); + debug(`No items returned for identifier ${identifier}`); + // We could get a 429 error inside the translation if we make too many + // requests to Crossref too quickly. Would need to be fixed in Zotero... + // I don't think we can identify this here unfortunately... + // (See https://forums.zotero.org/discussion/84985/new-odd-result-when-importing-pmids-with-magic-wand) + } } if (jsonItems) { From 5cb768304678e0a5cf88bd75ef90d08f59ebd91e Mon Sep 17 00:00:00 2001 From: Dom Date: Sat, 7 Jan 2023 00:06:08 +0100 Subject: [PATCH 15/15] Catch 429 errors when getting item reference lists --- src/crossref.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/crossref.js b/src/crossref.js index 9c429aeb..34d643c1 100644 --- a/src/crossref.js +++ b/src/crossref.js @@ -130,9 +130,12 @@ export default class Crossref{ responseType: "json", }; - const response = await Zotero.HTTP.request("GET", url, options).catch((e) => - debug(`Couldn't access URL: ${url}. Got status ${e.xmlhttp.status}.`) - ); + const response = await Zotero.HTTP.request("GET", url, options).catch((e) => { + debug(`Couldn't access URL: ${url}. Got status ${e.status}.`); + if (e.status == 429) { + throw new Error("Received a 429 rate limit response from Crossref (https://github.com/CrossRef/rest-api-doc#rate-limits). Try getting references for fewer items at a time.") + } + }); if (!response) return []; return response.response.message.reference || [];