diff --git a/.changeset/eighty-nails-press.md b/.changeset/eighty-nails-press.md new file mode 100644 index 0000000..b05620f --- /dev/null +++ b/.changeset/eighty-nails-press.md @@ -0,0 +1,5 @@ +--- +"crossref-utils": patch +--- + +Support abstract part diff --git a/src/cli/deposit.ts b/src/cli/deposit.ts index b9d04ad..c130759 100644 --- a/src/cli/deposit.ts +++ b/src/cli/deposit.ts @@ -13,6 +13,7 @@ import { loadProject, parseMyst, processProject, + resolveFrontmatterParts, selectors, } from 'myst-cli'; import type { ISession } from 'myst-cli'; @@ -85,7 +86,9 @@ export async function depositArticleFromSource(session: ISession, depositSource: } else { fileContents.forEach(({ mdast }) => { if (abstractPart) return; - abstractPart = extractPart(mdast, 'abstract'); + abstractPart = extractPart(mdast, 'abstract', { + frontmatterParts: resolveFrontmatterParts(session, projectFrontmatter), + }); }); } fileContents.forEach(({ references }) => { @@ -108,7 +111,9 @@ export async function depositArticleFromSource(session: ISession, depositSource: ? projectFrontmatter?.subtitle ?? undefined : frontmatter?.subtitle; frontmatter = { ...fileContent.frontmatter, title, subtitle }; - abstractPart = extractPart(fileContent.mdast, 'abstract'); + abstractPart = extractPart(fileContent.mdast, 'abstract', { + frontmatterParts: resolveFrontmatterParts(session, frontmatter), + }); fileContent.references.cite?.order.forEach((key) => { const value = fileContent.references.cite?.data[key].doi; if (value) dois[key] = value; @@ -117,6 +122,7 @@ export async function depositArticleFromSource(session: ISession, depositSource: } let abstract: Element | undefined; + const description = (frontmatter?.description || projectFrontmatter?.description)?.trim(); if (abstractPart) { transformXrefToLink(abstractPart); transformCiteToText(abstractPart); @@ -128,6 +134,20 @@ export async function depositArticleFromSource(session: ISession, depositSource: { name: 'jats:abstract' }, jats.map((e) => element2JatsUnist(e)), ) as Element; + } else if (description) { + // Use the project description as the fallback for the abstract + abstractPart = { + type: 'root', + children: [{ type: 'paragraph', children: [{ type: 'text', value: description }] }], + }; + transformNewlineToSpace(abstractPart); + const serializer = new JatsSerializer(new VFile(), abstractPart as any); + const jats = serializer.render(true).elements(); + abstract = u( + 'element', + { name: 'jats:abstract' }, + jats.map((e) => element2JatsUnist(e)), + ) as Element; } return { frontmatter: frontmatter ?? {}, dois, abstract, configFile }; } @@ -612,7 +632,7 @@ export async function deposit(session: ISession, opts: DepositOptions) { throw new Error('preprint deposit may only use a single article'); } const { frontmatter, dois, abstract } = depositArticles[0]; - body = preprintFromMyst(frontmatter, dois, abstract); + body = preprintFromMyst(session, frontmatter, dois, abstract); } const batch = new DoiBatch( { id: opts.id ?? uuid(), depositor: { name, email }, registrant }, diff --git a/src/funding.ts b/src/funding.ts index 358b536..61cdb21 100644 --- a/src/funding.ts +++ b/src/funding.ts @@ -1,6 +1,7 @@ import type { Award, ProjectFrontmatter } from 'myst-frontmatter'; import type { Fundref } from './types.js'; import type { ISession } from 'myst-cli-utils'; +import { e, t } from './utils.js'; export function fundrefFromMyst( session: ISession, @@ -35,3 +36,29 @@ export function fundrefFromMyst( return { sources, awardNumbers }; }); } + +export function createFundingXml(funding?: Fundref[]) { + if (!funding || funding.length === 0) return null; + return e( + 'fr:program', + { name: 'fundref' }, + funding.map((fundingItem) => { + if (!fundingItem.sources.length) { + throw new Error('Fundref entry must have at least one source'); + } + return e('fr:assertion', { name: 'fundgroup' }, [ + ...fundingItem.sources.map((source) => { + return e('fr:assertion', { name: 'funder_name' }, [ + t(source.name), + ...source.identifiers.map((id) => { + return e('fr:assertion', { name: 'funder_identifier' }, id); + }), + ]); + }), + ...fundingItem.awardNumbers.map((awardNumber) => { + return e('fr:assertion', { name: 'award_number' }, awardNumber); + }), + ]); + }), + ); +} diff --git a/src/journal.ts b/src/journal.ts index 16ba8ac..079f5b5 100644 --- a/src/journal.ts +++ b/src/journal.ts @@ -1,11 +1,11 @@ import type { Element } from 'xast'; -import { e, t } from './utils.js'; +import { e } from './utils.js'; import type { JournalArticle, JournalIssue, JournalMetadata } from './types.js'; import { publicationDateXml } from './dates.js'; import type { ProjectFrontmatter } from 'myst-frontmatter'; import { contributorsXmlFromMystAuthors } from './contributors.js'; import { normalize } from 'doi-utils'; -import { fundrefFromMyst } from './funding.js'; +import { createFundingXml, fundrefFromMyst } from './funding.js'; import type { ISession } from 'myst-cli-utils'; /** @@ -172,32 +172,9 @@ export function journalArticleXml({ children.push(e('pages', pageChildren)); } // publisher_item - if (funding?.length) { - children.push( - e( - 'fr:program', - { name: 'fundref' }, - funding.map((fundingItem) => { - if (!fundingItem.sources.length) { - throw new Error('Fundref entry must have at least one source'); - } - return e('fr:assertion', { name: 'fundgroup' }, [ - ...fundingItem.sources.map((source) => { - return e('fr:assertion', { name: 'funder_name' }, [ - t(source.name), - ...source.identifiers.map((id) => { - return e('fr:assertion', { name: 'funder_identifier' }, id); - }), - ]); - }), - ...fundingItem.awardNumbers.map((awardNumber) => { - return e('fr:assertion', { name: 'award_number' }, awardNumber); - }), - ]); - }), - ), - ); - } + const fundingXml = createFundingXml(funding); + if (fundingXml) children.push(fundingXml); + if (license) { children.push( e('ai:program', { name: 'AccessIndicators' }, [ diff --git a/src/preprint.ts b/src/preprint.ts index b0f7bd5..25d33f4 100644 --- a/src/preprint.ts +++ b/src/preprint.ts @@ -5,6 +5,8 @@ import type { ProjectFrontmatter } from 'myst-frontmatter'; import { normalize } from 'doi-utils'; import { contributorsXmlFromMystAuthors } from './contributors.js'; import { dateXml } from './dates.js'; +import { createFundingXml, fundrefFromMyst } from './funding.js'; +import type { ISession } from 'myst-cli'; /** * Create posted content xml @@ -19,6 +21,7 @@ export function preprintXml({ title, subtitle, abstract, + funding, doi_data, citations, license, @@ -35,6 +38,10 @@ export function preprintXml({ children.push(e('titles', titles)); children.push(posted_date); if (abstract) children.push(abstract); + + const fundingXml = createFundingXml(funding); + if (fundingXml) children.push(fundingXml); + if (license) { children.push( e('ai:program', { name: 'AccessIndicators' }, [ @@ -85,6 +92,7 @@ export function preprintXml({ } export function preprintFromMyst( + session: ISession, myst: ProjectFrontmatter, citations?: Record, abstract?: Element, @@ -98,6 +106,7 @@ export function preprintFromMyst( date: typeof date === 'string' ? new Date(date) : undefined, license: license?.content?.url, abstract, + funding: fundrefFromMyst(session, myst), }; if (license && license.content?.CC) { // Only put in CC licenses at this time