Commit 651fcc56 authored by Diegodlh's avatar Diegodlh
Browse files

Start addressing #T306132 by moving citation code out the citoid module

parent e88eae0b
import {
MediaWikiBaseFieldCitation,
SimpleCitoidCitation,
} from "./citationTypes";
import { BASE_CREATOR_TYPES } from "./creatorTypes";
import { SIMPLE_CITOID_FIELDS } from "./keyTypes";
export function simplifyCitation(
mwbCitation: MediaWikiBaseFieldCitation
): SimpleCitoidCitation {
const simpleCitation: SimpleCitoidCitation = {
itemType: mwbCitation.itemType,
title: mwbCitation.title,
url: mwbCitation.url,
tags: mwbCitation.tags?.map((tag) => tag.tag),
};
// split mediawiki creator arrays into creatorFirst and creatorLast arrays
for (const baseCreatorType of BASE_CREATOR_TYPES) {
const creators = mwbCitation[baseCreatorType];
if (creators) {
simpleCitation[`${baseCreatorType}First`] = creators.map(
(creator) => creator[0]
);
simpleCitation[`${baseCreatorType}Last`] = creators.map(
(creator) => creator[1]
);
}
}
for (const field of SIMPLE_CITOID_FIELDS) {
if (
field in mwbCitation &&
!(field in simpleCitation) &&
field !== "itemType"
) {
const value = mwbCitation[field as keyof MediaWikiBaseFieldCitation] as
| string
| Array<string>;
simpleCitation[field] = value;
}
}
return simpleCitation;
}
import {
RequiredFields,
SpecialFields,
BaseFields,
NonBaseFields,
BaseCreatorFields,
NonBaseCreatorFields,
MediaWikiFields,
ZoteroFields,
SimpleCitoidFields,
} from "./fieldTypes";
import { REQUIRED_FIELDS } from "./keyTypes";
// Citoid citation types
export type MediaWikiCitation = RequiredFields &
Partial<
SpecialFields &
BaseFields &
NonBaseFields &
BaseCreatorFields &
NonBaseCreatorFields &
MediaWikiFields
>;
export type MediaWikiBaseFieldCitation = RequiredFields &
Partial<SpecialFields & BaseFields & BaseCreatorFields & MediaWikiFields>;
export type ZoteroCitation = RequiredFields &
Partial<SpecialFields & BaseFields & NonBaseFields & ZoteroFields>;
export type CitoidCitation =
| MediaWikiCitation
| MediaWikiBaseFieldCitation
| ZoteroCitation;
export function isCitoidCitation(
citation: unknown
): citation is CitoidCitation {
return REQUIRED_FIELDS.every(
(field) => (citation as CitoidCitation)[field] !== undefined
);
}
// Special citation types
export type WebToCitCitation = Omit<MediaWikiBaseFieldCitation, "source"> & {
source: Array<"Web2Cit" | "Zotero">;
};
export type SimpleCitoidCitation = SimpleCitoidFields;
// Creator types
////////////////
// Base creator field key type
export const BASE_CREATOR_TYPES = [
"attorneyAgent",
"author",
"bookAuthor",
"castMember",
"commenter",
"composer",
"contributor",
"cosponsor",
"counsel",
"editor",
"guest",
"interviewer",
"producer",
"recipient",
"reviewedAuthor",
"scriptwriter",
"seriesEditor",
"translator",
"wordsBy",
] as const;
export type BaseCreatorType = typeof BASE_CREATOR_TYPES[number];
// Non-base creator field key type
const NON_BASE_CREATOR_TYPES = [
"artist",
"cartographer",
"director",
"interviewee",
"inventor",
"performer",
"podcaster",
"presenter",
"programmer",
"sponsor",
] as const;
export type NonBaseCreatorType = typeof NON_BASE_CREATOR_TYPES[number];
import { assert, Equals, Implements } from "./utils";
import {
ItemType,
Tag,
MetadataSource,
MediaWikiCreator,
ZoteroCreator,
} from "./valueTypes";
import {
RequiredField,
SpecialField,
BaseField,
NonBaseField,
BaseCreatorField,
NonBaseCreatorField,
SplitBaseCreatorField,
MediaWikiField,
SimpleCitoidField,
} from "./keyTypes";
// Field types
//////////////
// Required fields
export interface RequiredFields {
// required - https://www.mediawiki.org/wiki/Citoid/API#Field_names
itemType: ItemType;
title: string;
url: string;
}
// confirm that RequiredFields has all and only the keys in REQUIRED_FIELDS
assert<Equals<RequiredField, keyof RequiredFields>>(true);
// Special fields
export interface SpecialFields {
tags: Array<Tag>;
key: string;
version: 0;
}
assert<Equals<SpecialField, keyof SpecialFields>>(true);
// Base fields
export type BaseFields = Record<BaseField, string>;
// Non-base fields
export type NonBaseFields = Record<NonBaseField, string>;
// Base creator fields
export type BaseCreatorFields = Record<
BaseCreatorField,
Array<MediaWikiCreator>
>;
// Non-base creator fields
export type NonBaseCreatorFields = Record<
NonBaseCreatorField,
Array<MediaWikiCreator>
>;
// Mediawiki fields
export type MediaWikiFields = Implements<
Record<MediaWikiField, string | Array<string>>,
{
isbn: Array<string>; // array in mediawiki format
issn: Array<string>; // array in mediawiki format
PMCID: string;
PMID: string;
oclc: string;
source: Array<MetadataSource>;
}
>;
// Zotero fields
export interface ZoteroFields {
creators: Array<ZoteroCreator>;
ISBN: string;
ISSN: string;
}
// Simple-citoid fields
export type SimpleCitoidFields = Record<RequiredField, string | string[]> & {
itemType: ItemType;
} & Partial<
Record<
| Exclude<SpecialField, "key" | "version">
| BaseField
| SplitBaseCreatorField
| Exclude<MediaWikiField, "source">,
string | string[]
>
>;
assert<Equals<SimpleCitoidField, keyof SimpleCitoidFields>>(true);
import {
BaseCreatorType,
NonBaseCreatorType,
BASE_CREATOR_TYPES,
} from "./creatorTypes";
// Required field key type
export const REQUIRED_FIELDS = ["itemType", "title", "url"] as const;
export type RequiredField = typeof REQUIRED_FIELDS[number];
// Special field key type
const SPECIAL_FIELDS = ["tags", "key", "version"] as const;
export type SpecialField = typeof SPECIAL_FIELDS[number];
// Base field key type
const BASE_FIELDS = [
"abstractNote",
"accessDate",
"applicationNumber",
"archive",
"archiveLocation",
"artworkSize",
"assignee",
"callNumber",
"code",
"codeNumber",
"committee",
"conferenceName",
"country",
"court",
"date",
"DOI",
"edition",
"extra",
"filingDate",
"history",
"issue",
"issuingAuthority",
"journalAbbreviation",
"language",
"legalStatus",
"legislativeBody",
"libraryCatalog",
"medium",
"meetingName",
"number",
"numberOfVolumes",
"numPages",
"pages",
"place",
"priorityNumbers",
"programmingLanguage",
"publicationTitle",
"publisher",
"references",
"reporter",
"rights",
"runningTime",
"scale",
"section",
"series",
"seriesNumber",
"seriesText",
"seriesTitle",
"session",
"shortTitle",
"system",
"type",
"versionNumber",
"volume",
] as const;
export type BaseField = typeof BASE_FIELDS[number];
// Non-base field key type
const NON_BASE_FIELDS = [
// NO field is mapped to this field in ANY item type
"artworkMedium",
"audioFileType",
"audioRecordingFormat",
"billNumber",
"blogTitle",
"bookTitle",
"caseName",
"codePages",
"codeVolume",
"company",
"dateDecided",
"dateEnacted",
"dictionaryTitle",
"distributor",
"docketNumber",
"documentNumber",
"encyclopediaTitle",
"episodeNumber",
"firstPage",
"forumTitle",
"genre",
"institution",
"interviewMedium",
"issueDate",
"label",
"letterType",
"manuscriptType",
"mapType",
"nameOfAct",
"network",
"patentNumber",
"postType",
"presentationType",
"proceedingsTitle",
"programTitle",
"publicLawNumber",
"reporterVolume",
"reportNumber",
"reportType",
"studio",
"subject",
"thesisType",
"university",
"videoRecordingFormat",
"websiteTitle",
"websiteType",
] as const;
export type NonBaseField = typeof NON_BASE_FIELDS[number];
// Base creator field key type
export type BaseCreatorField = BaseCreatorType;
// Non-base creator field key type
export type NonBaseCreatorField = NonBaseCreatorType;
// Mediawiki field key type
const MEDIA_WIKI_ID_FIELDS = ["isbn", "issn", "PMCID", "PMID", "oclc"] as const;
const MEDIA_WIKI_FIELDS = [
// https://www.mediawiki.org/wiki/Citoid/API#Field_names
...MEDIA_WIKI_ID_FIELDS,
"source",
] as const;
export type MediaWikiField = typeof MEDIA_WIKI_FIELDS[number];
// Simple-citoid field key type
const SIMPLE_CITOID_NON_CREATOR_FIELDS = [
...REQUIRED_FIELDS,
"tags",
...BASE_FIELDS,
...MEDIA_WIKI_ID_FIELDS,
] as const;
type SimpleCitoidNonCreatorField =
typeof SIMPLE_CITOID_NON_CREATOR_FIELDS[number];
const SPLIT_BASE_CREATOR_FIELDS: SplitBaseCreatorField[] = [
...BASE_CREATOR_TYPES.map(
(creator) => `${creator}First` as SplitBaseCreatorField
),
...BASE_CREATOR_TYPES.map(
(creator) => `${creator}Last` as SplitBaseCreatorField
),
];
export type SplitBaseCreatorField =
| `${BaseCreatorType}First`
| `${BaseCreatorType}Last`;
export const SIMPLE_CITOID_FIELDS = [
...SIMPLE_CITOID_NON_CREATOR_FIELDS,
...SPLIT_BASE_CREATOR_FIELDS,
];
export type SimpleCitoidField =
| SimpleCitoidNonCreatorField
| SplitBaseCreatorField;
export function isSimpleCitoidField(
field: unknown
): field is SimpleCitoidField {
if (
SIMPLE_CITOID_NON_CREATOR_FIELDS.includes(
field as SimpleCitoidNonCreatorField
)
) {
return true;
} else if (
SPLIT_BASE_CREATOR_FIELDS.includes(field as SplitBaseCreatorField)
) {
return true;
} else {
return false;
}
}
// add non-base to base map
// from https://stackoverflow.com/questions/55046211/typescript-check-if-type-a-type-b-type-c
// and https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
T
>() => T extends Y ? 1 : 2
? true
: false;
export function assert<T extends boolean>(expect: T) {
return expect;
}
// see https://github.com/Microsoft/TypeScript/issues/24274
export type Implements<T, R extends T> = R;
import { BaseCreatorType, NonBaseCreatorType } from "./creatorTypes";
// Value types
//////////////
export { ItemType } from "../translationField";
// Tag value type
export interface Tag {
tag: string;
type: number;
}
// Source value type
export type MetadataSource =
| "CrossRef"
| "WorldCat"
| "Zotero" // URL queries from Web2Cit would always return this
| "citoid" // not sure when 'citoid' would be a source
| "PubMed";
// ISBN queries may include library catalog as source
// MediaWiki creator value type
export type MediaWikiCreator = [FirstName: string, LastName: string];
// Zotero creator value type
type OneFieldZoteroCreator = {
firstName: string;
lastName: string;
creatorType: BaseCreatorType | NonBaseCreatorType;
};
type TwoFieldZoteroCreator = {
name: string;
creatorType: BaseCreatorType | NonBaseCreatorType;
};
export type ZoteroCreator = OneFieldZoteroCreator | TwoFieldZoteroCreator;
import { CitoidCitation, fetchSimpleCitation } from "./citoid";
import { fetchSimpleCitation } from "./citoid";
import * as nodeFetch from "node-fetch";
import { pages } from "./webpage/samplePages";
......
import fetch, { Headers } from "node-fetch";
import { HTTPResponseError } from "./errors";
import { ItemType } from "./translationField";
import { CITOID_API_ENDPOINT as API_ENDPOINT } from "./config";
import {
CitoidCitation,
MediaWikiBaseFieldCitation,
SimpleCitoidCitation,
isCitoidCitation,
} from "./citation/citationTypes";
import { simplifyCitation } from "./citation/citation";
// type CitoidRequestFormat = "mediawiki" | "mediawiki-basefields" | "zotero";
// | 'bibtex'
......@@ -67,12 +73,6 @@ function translateUrl(
});
}
function isCitoidCitation(citation: unknown): citation is CitoidCitation {
return REQUIRED_FIELDS.every(
(field) => (citation as CitoidCitation)[field] !== undefined
);
}
export function fetchSimpleCitation(
url: string,
language?: string
......@@ -90,344 +90,3 @@ export function fetchSimpleCitation(
});
});
}
export const REQUIRED_FIELDS = ["itemType", "title", "url"] as const;
type RequiredField = typeof REQUIRED_FIELDS[number];
interface RequiredFields {
// required - https://www.mediawiki.org/wiki/Citoid/API#Field_names
itemType: ItemType;
title: string;
url: string;
}
// confirm that RequiredFields has all and only the keys in REQUIRED_FIELDS
// from https://stackoverflow.com/questions/55046211/typescript-check-if-type-a-type-b-type-c
// and https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
T
>() => T extends Y ? 1 : 2
? true
: false;
function assert<T extends boolean>(expect: T) {
return expect;
}
assert<Equals<RequiredField, keyof RequiredFields>>(true);
const SPECIAL_FIELDS = ["tags", "key", "version"] as const;
type SpecialField = typeof SPECIAL_FIELDS[number];
interface SpecialFields {
tags: Array<Tag>;
key: string;
version: 0;
}
assert<Equals<SpecialField, keyof SpecialFields>>(true);
export const BASE_FIELDS = [
"abstractNote",
"accessDate",
"applicationNumber",
"archive",
"archiveLocation",
"artworkSize",
"assignee",
"callNumber",
"code",
"codeNumber",
"committee",
"conferenceName",
"country",
"court",
"date",
"DOI",
"edition",
"extra",
"filingDate",
"history",
"issue",
"issuingAuthority",
"journalAbbreviation",
"language",
"legalStatus",
"legislativeBody",
"libraryCatalog",
"medium",
"meetingName",
"number",
"numberOfVolumes",
"numPages",
"pages",
"place",
"priorityNumbers",
"programmingLanguage",
"publicationTitle",
"publisher",
"references",
"reporter",
"rights",
"runningTime",
"scale",
"section",
"series",
"seriesNumber",
"seriesText",
"seriesTitle",
"session",
"shortTitle",
"system",
"type",
"versionNumber",
"volume",
] as const;
type BaseField = typeof BASE_FIELDS[number];
type BaseFields = Record<BaseField, string>;
export const NON_BASE_FIELDS = [
// NO field is mapped to this field in ANY item type
"artworkMedium",
"audioFileType",
"audioRecordingFormat",
"billNumber",
"blogTitle",
"bookTitle",
"caseName",
"codePages",
"codeVolume",
"company",
"dateDecided",
"dateEnacted",