From 7e17159774dd712d6c1c608e4f906bf9b65b7cfb Mon Sep 17 00:00:00 2001 From: Dominic D Date: Tue, 20 Aug 2024 18:50:56 +0000 Subject: [PATCH] Fix local citations network (#284) * Fix local citations network for typescript * Enable LCN Needed to change to arrow functions to get a correctly bound `this` * copy LCN files on build * Also copy LCN lib folder * Clone submodules when building on github actions * Fix typo in LCN URL * Fix localisation for % completion in local citation network --- .github/workflows/release.yml | 2 + src/cita/localCitationNetwork.ts | 60 ++++++++++++------- src/cita/zoteroOverlay.tsx | 50 ++++++++-------- .../chrome/locale/en-US/wikicite.properties | 4 +- tsconfig.json | 2 +- zotero-plugin.config.ts | 19 +++++- 6 files changed, 85 insertions(+), 52 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2fbf7544..96bc118c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v2 + with: + submodules: recursive - name: install node uses: actions/setup-node@v1 diff --git a/src/cita/localCitationNetwork.ts b/src/cita/localCitationNetwork.ts index 45af3bbe..cafa4c92 100644 --- a/src/cita/localCitationNetwork.ts +++ b/src/cita/localCitationNetwork.ts @@ -4,19 +4,34 @@ import Progress from "./progress"; import SourceItemWrapper from "./sourceItemWrapper"; import Wikicite from "./wikicite"; import * as prefs from "../cita/preferences"; - -declare const Zotero: any; -declare const ZoteroPane: any; +import { config } from "../../package.json"; export default class LCN { - items: any[]; - itemMap: Map; + items: Zotero.Item[]; + itemMap: Map< + string, + { + id: string | undefined; + doi: string | undefined; + title: string; + authors: { + LN: string; + FN: string; + }[]; + year: string; + journal: string; + references: (string | undefined)[]; + abstract: string; + url: string | undefined; + } + >; inputKeys: string[]; libraryID: number; progress: Progress; - constructor(items) { - if (!items.length) return; + constructor(items: Zotero.Item[]) { + if (!items.length) + throw `Can't create a local citation network without any items`; this.items = items; this.itemMap = new Map(); // itemKey/tmpItemKey -> ItemWrapper @@ -89,7 +104,7 @@ export default class LCN { const linkedToItem = Zotero.Items.getByLibraryAndKey( this.libraryID, citation.target.key, - ); + ) as Zotero.Item; // Non-linked citation target items are not part of the // LCN "input items" set. // Citations of these non-linked citation target items @@ -114,10 +129,10 @@ export default class LCN { // collect item's unique identifiers (including name) and clean // them, to make sure the same item always gets the same tmp key const cleanDOI = Zotero.Utilities.cleanDOI( - citation.target.doi, + citation.target.doi!, ); const cleanISBN = Zotero.Utilities.cleanISBN( - citation.target.isbn, + citation.target.isbn!, ); const qid = citation.target.qid; const uids = { @@ -139,13 +154,13 @@ export default class LCN { // retrieve tmp keys already given to this item, // i.e., the target item of another source item's citation // had one or more of the same uids or title - const tmpKeys = new Set(); + const tmpKeys: Set = new Set(); for (const [key, value] of Object.entries(uids)) { const tmpKey = tmpKeyMap.get(`${key}:${value}`); if (tmpKey) tmpKeys.add(tmpKey); } - let tmpKey; + let tmpKey: string; if (tmpKeys.size === 0) { // if no matching temp keys found, create a new one do { @@ -182,14 +197,14 @@ export default class LCN { ); } } - this.itemMap.set(wrappedItem.key, parseWrappedItem(wrappedItem)); + this.itemMap.set(wrappedItem.key!, parseWrappedItem(wrappedItem)); } this.progress.updateLine("done"); } - openItem(key) { + openItem(key: string) { ZoteroPane.selectItem( - Zotero.Items.getIDFromLibraryAndKey(this.libraryID, key), + Zotero.Items.getIDFromLibraryAndKey(this.libraryID, key) as number, ); window.focus(); } @@ -212,7 +227,7 @@ export default class LCN { await Zotero.Promise.delay(100); window.openDialog( - "chrome://cita/content/Local-Citation-Network/index.html?API=Cita&listOfKeys=" + + `chrome://${config.addonRef}/content/Local-Citation-Network/index.html?API=Cita&listOfKeys=` + this.inputKeys.join(","), "", windowFeatures.join(","), @@ -232,7 +247,7 @@ export default class LCN { /** * Get localized string for LCN window */ -function getString(name, params) { +function getString(name: string, params: unknown) { name = "wikicite.lcn.window." + name; let string; if (params) { @@ -246,11 +261,11 @@ function getString(name, params) { /** * Get ItemWrapper and return Data Item as understood by Local Citations Network */ -function parseWrappedItem(wrappedItem) { +function parseWrappedItem(wrappedItem: ItemWrapper | SourceItemWrapper) { const authors = wrappedItem.item .getCreators() .map((creator) => ({ LN: creator.lastName, FN: creator.firstName })); - if (!authors.length) authors.push({ LN: undefined }); + if (!authors.length) authors.push({ LN: "", FN: "" }); return { id: wrappedItem.key, doi: wrappedItem.doi, @@ -258,9 +273,10 @@ function parseWrappedItem(wrappedItem) { authors: authors, year: wrappedItem.item.getField("year"), journal: wrappedItem.item.getField("publicationTitle"), - references: wrappedItem.citations - ? wrappedItem.citations.map((citation) => citation.target.key) - : [], + references: + "citations" in wrappedItem + ? wrappedItem.citations.map((citation) => citation.target.key) + : [], abstract: wrappedItem.item.getField("abstractNote"), url: wrappedItem.url, }; diff --git a/src/cita/zoteroOverlay.tsx b/src/cita/zoteroOverlay.tsx index eb9e2220..106a46de 100644 --- a/src/cita/zoteroOverlay.tsx +++ b/src/cita/zoteroOverlay.tsx @@ -3,7 +3,7 @@ import Citations from "./citations"; import CitationsBoxContainer from "../containers/citationsBoxContainer"; import Crossref from "./crossref"; import Extraction from "./extract"; -// import LCN from './localCitationNetwork'; +import LCN from "./localCitationNetwork"; import OCI from "../oci"; import OpenCitations from "./opencitations"; import * as React from "react"; @@ -397,7 +397,15 @@ class ZoteroOverlay { * @param {Boolean} [wrap=true] Whether to return wrapped items or not * @return {Array} Array of selected regular items */ - async getSelectedItems(menuName: MenuSelectionType, wrap = true) { + async getSelectedItems( + menuName: MenuSelectionType, + wrap: false, + ): Promise; + async getSelectedItems( + menuName: MenuSelectionType, + wrap: true, + ): Promise; + async getSelectedItems(menuName: MenuSelectionType, wrap: boolean = true) { // Fixme: Consider using the Citations class methods instead let items; switch (menuName) { @@ -435,10 +443,7 @@ class ZoteroOverlay { } async fetchQIDs(menuName: MenuSelectionType) { - const items = (await this.getSelectedItems( - menuName, - true, - )) as SourceItemWrapper[]; + const items = await this.getSelectedItems(menuName, true); const qidMap = await Wikidata.reconcile(items); if (qidMap) { for (const item of items) { @@ -449,10 +454,7 @@ class ZoteroOverlay { } async syncWithWikidata(menuName: MenuSelectionType) { - const items = (await this.getSelectedItems( - menuName, - true, - )) as SourceItemWrapper[]; + const items = await this.getSelectedItems(menuName, true); if (items.length) { Citations.syncItemCitationsWithWikidata(items); } @@ -496,14 +498,12 @@ class ZoteroOverlay { } async localCitationNetwork(menuName: MenuSelectionType) { - // fix: enable LCN - alert("Local Citation Network isn't supported yet"); - // const items = await this.getSelectedItems(menuName, false); - // if (items.length) { - // const lcn = new LCN(items); - // await lcn.init(); - // lcn.show(); - // } + const items = await this.getSelectedItems(menuName, false); + if (items.length) { + const lcn = new LCN(items); + await lcn.init(); + lcn.show(); + } } /******************************************/ @@ -1174,13 +1174,13 @@ class ZoteroOverlay { MenuFunction, (menuName: MenuSelectionType) => void > = new Map([ - ["fetchQIDs", this.fetchQIDs], - ["syncWithWikidata", this.syncWithWikidata], - ["getFromCrossref", this.getFromCrossref], - ["getFromOCC", this.getFromOCC], - ["getFromAttachments", this.getFromAttachments], - ["addAsCitations", this.addAsCitations], - ["localCitationNetwork", this.localCitationNetwork], + ["fetchQIDs", () => this.fetchQIDs(menuName)], + ["syncWithWikidata", () => this.syncWithWikidata(menuName)], + ["getFromCrossref", () => this.getFromCrossref(menuName)], + ["getFromOCC", () => this.getFromOCC(menuName)], + ["getFromAttachments", () => this.getFromAttachments(menuName)], + ["addAsCitations", () => this.addAsCitations(menuName)], + ["localCitationNetwork", () => this.localCitationNetwork(menuName)], ]); for (const [functionName, func] of menuFunctions) { if ( diff --git a/static/chrome/locale/en-US/wikicite.properties b/static/chrome/locale/en-US/wikicite.properties index 51164c76..8f8f5a4f 100644 --- a/static/chrome/locale/en-US/wikicite.properties +++ b/static/chrome/locale/en-US/wikicite.properties @@ -83,8 +83,8 @@ wikicite.lcn.window.filter.select.journal = Journal wikicite.lcn.window.footer = Adapted from %1$s's %2$s wikicite.lcn.window.input-articles.header = Input articles (%s) wikicite.lcn.window.input-articles.info.source = Based on local citation data -wikicite.lcn.window.input-articles.info.completeness = estimated completeness %s%% -wikicite.lcn.window.input-articles.info.completeness.tooltip = %1$s of %2$s input articles have reference-lists themselves in Cita (%3$s%%). +wikicite.lcn.window.input-articles.info.completeness = estimated completeness %s% +wikicite.lcn.window.input-articles.info.completeness.tooltip = %1$s of %2$s input articles have reference-lists themselves in Cita (%3$s%). wikicite.lcn.window.input-articles.info.open-external = Open with %s wikicite.lcn.window.input-articles.info.open-external.tooltip = Open new graph for %s articles with DOI using citation data retrieved through external APIs wikicite.lcn.window.incoming-suggestions.header = Incoming suggestions (%1$s %2$s) diff --git a/tsconfig.json b/tsconfig.json index 0df0552b..7be19a88 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,5 +11,5 @@ "sourceMap": true }, "include": ["src/**/*", "typings", "node_modules/zotero-types"], - "exclude": ["build", "static", "src/cita/localCitationNetwork.ts"] + "exclude": ["build", "static"] } diff --git a/zotero-plugin.config.ts b/zotero-plugin.config.ts index 776f25d1..a7b8077e 100644 --- a/zotero-plugin.config.ts +++ b/zotero-plugin.config.ts @@ -1,7 +1,6 @@ import { defineConfig } from "zotero-plugin-scaffold"; import pkg from "./package.json"; -import { copyFileSync, readdirSync, renameSync } from "fs"; -import path from "path"; +import { copyFileSync, readdirSync, renameSync, mkdirSync, cpSync } from "fs"; import fse from "fs-extra"; import { replaceInFileSync } from "zotero-plugin-scaffold/tools"; @@ -96,6 +95,22 @@ export default defineConfig({ from: /wikicite_prefs_citation-storage-(note|extra)=/g, to: "$&\n .label=", }); + + // Copy local citations network + mkdirSync("build/addon/chrome/content/Local-Citation-Network/"); + copyFileSync( + "Local-Citation-Network/index.js", + "build/addon/chrome/content/Local-Citation-Network/index.js", + ); + copyFileSync( + "Local-Citation-Network/index.html", + "build/addon/chrome/content/Local-Citation-Network/index.html", + ); + cpSync( + "Local-Citation-Network/lib", + "build/addon/chrome/content/Local-Citation-Network/lib", + { recursive: true }, + ); }, }, // If you want to checkout update.json into the repository, uncomment the following lines: