Skip to content

Commit

Permalink
🎓 JATS author/affiliation updates (#654)
Browse files Browse the repository at this point in the history
* 🎓 Separate authors and other contributors into different contrib-groups in JATS
* 💵 Institution info is explicitly listed under funding in JATS
* 🎓 Add DOI as affiliation identifier in frontmatter and JATS
* ✍️ Add affiliation DOI to docs
* 🔧 Improve open-funder-registry doi check in JATS
* 🔧 Consume doi-utils 2.0.1 with isOpenFunderRegistry
  • Loading branch information
fwkoch authored Oct 11, 2023
1 parent acffc6f commit 651dd77
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 88 deletions.
5 changes: 5 additions & 0 deletions .changeset/hungry-deers-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-to-jats': patch
---

Institution info is explicitly listed under funding in jats
6 changes: 6 additions & 0 deletions .changeset/perfect-camels-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'myst-frontmatter': patch
'myst-to-jats': patch
---

Add doi as affiliation identifier in frontmatter and jats
5 changes: 5 additions & 0 deletions .changeset/twelve-rockets-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-to-jats': patch
---

Separate authors and other contributors into different contrib-groups
5 changes: 3 additions & 2 deletions docs/frontmatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,8 @@ affiliations:
however, at least one of these is required.
* - `department`
- a string - the affiliation department (e.g. Chemistry 🧪)
* - `ror`, `isni`, `ringgold`
- Identifiers for the affiliation (ROR, ISNI, and Ringgold).
* - `doi`, `ror`, `isni`, `ringgold`
- Identifiers for the affiliation (DOI, ROR, ISNI, and Ringgold).

We suggest using https://ror.org if possible to search for your institution.

Expand All @@ -401,6 +401,7 @@ affiliations:
ringgold: 1846
isni: 0000 0004 1936 7558
ror: 05qwgg493
doi: 10.13039/100018578
```
* - `email`
- a string - email of the affiliation, required if `corresponding` is `true`
Expand Down
7 changes: 4 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/jtex/docs/document.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ name (`string`)
institution, department, address, city, state, postal_code, country (`string`)
: Individual parts of an affiliation's address information.

isni, ringgold, ror (`string`)
doi, isni, ringgold, ror (`string`)
: Institution unique identifiers.

url, email, phone, fax (`string`)
Expand All @@ -143,7 +143,7 @@ letter (`string`)
: The letter of the affiliation, starting at "A". If there are more letters than 26, they will be repeated ("AA").

value (`Affiliation`)
: This contains most of the same affiliation fields (name, institution, department, address, city, state, postal_code, country, isni, ringgold, ror, url, email, phone, fax).
: This contains most of the same affiliation fields (name, institution, department, address, city, state, postal_code, country, doi, isni, ringgold, ror, url, email, phone, fax).
: This field is deprecated and will be removed when all templates are updated. For new templates, please use the values directly on the affiliation (e.g. `affiliation.name`, not `affiliation.value.name`)

The authors and affiliations are often the most complex part of the template, the following statements can give you some ideas of how to use the above properties.
Expand Down
1 change: 1 addition & 0 deletions packages/myst-frontmatter/src/frontmatter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface Affiliation {
isni?: string;
ringgold?: number;
ror?: string;
doi?: string;
url?: string;
email?: string;
phone?: string;
Expand Down
26 changes: 17 additions & 9 deletions packages/myst-frontmatter/src/frontmatter/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const AFFILIATION_KEYS = [
'isni',
'ringgold',
'ror',
'doi',
'url',
'email',
'phone',
Expand Down Expand Up @@ -288,6 +289,18 @@ function pseudoUniqueId(kind: string, index: number, file?: string) {
return `${kind}${suffix}-generated-uid-${index}`;
}

function validateDoi(value: any, opts: ValidationOptions) {
const doiString = validateString(value, opts);
if (doiString !== undefined) {
if (doi.validate(doiString, { strict: true })) {
return doiString;
} else {
validationError('must be valid DOI', opts);
}
}
return undefined;
}

/**
* Update stash of authors/affiliations based on input value
*
Expand Down Expand Up @@ -428,6 +441,9 @@ export function validateAffiliation(input: any, opts: ValidationOptions) {
...incrementOptions('ringgold', opts),
});
}
if (defined(value.doi)) {
output.doi = validateDoi(value.doi, incrementOptions('doi', opts));
}
if (defined(value.collaboration)) {
output.collaboration = validateBoolean(
value.collaboration,
Expand Down Expand Up @@ -1161,15 +1177,7 @@ export function validateSharedProjectFrontmatterKeys(
output.name = validateString(value.name, incrementOptions('name', opts));
}
if (defined(value.doi)) {
const doiOpts = incrementOptions('doi', opts);
const doiString = validateString(value.doi, doiOpts);
if (doiString !== undefined) {
if (doi.validate(doiString, { strict: true })) {
output.doi = doiString;
} else {
validationError('must be valid DOI', doiOpts);
}
}
output.doi = validateDoi(value.doi, incrementOptions('doi', opts));
}
if (defined(value.arxiv)) {
output.arxiv = validateUrl(value.arxiv, {
Expand Down
22 changes: 22 additions & 0 deletions packages/myst-frontmatter/tests/affiliations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,25 @@ cases:
affiliations:
- id: univa
name: University A
- title: Affiliation with doi passes
raw:
affiliations:
- id: univa
name: University A
doi: 10.13039/100000879
normalized:
affiliations:
- id: univa
name: University A
doi: 10.13039/100000879
- title: Affiliation with invalid doi fails
raw:
affiliations:
- id: univa
name: University A
doi: invalid doi
normalized:
affiliations:
- id: univa
name: University A
errors: 1
1 change: 1 addition & 0 deletions packages/myst-to-jats/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"dependencies": {
"citation-js-utils": "^1.0.2",
"credit-roles": "^2.1.0",
"doi-utils": "^2.0.1",
"jats-tags": "^1.0.1",
"katex": "^0.15.2",
"myst-common": "^1.1.7",
Expand Down
158 changes: 90 additions & 68 deletions packages/myst-to-jats/src/frontmatter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Contributor, ProjectFrontmatter } from 'myst-frontmatter';
import type { Contributor, ProjectFrontmatter, Affiliation } from 'myst-frontmatter';
import * as credit from 'credit-roles';
import { doi } from 'doi-utils';
import type { Element, IJatsSerializer } from './types.js';

export function getJournalIds(): Element[] {
Expand Down Expand Up @@ -213,16 +214,86 @@ export function getArticleAuthors(frontmatter: ProjectFrontmatter): Element[] {
}
return { type: 'element', name: 'contrib', attributes, elements };
};
const contribs = [
...(frontmatter.authors ?? []).map((author): Element => {
return generateContrib(author, 'author');
}),
...(frontmatter.contributors ?? []).map((contributor): Element => {
return generateContrib(contributor);
}),
];
const authorContribs = (frontmatter.authors ?? []).map((author): Element => {
return generateContrib(author, 'author');
});
const otherContribs = (frontmatter.contributors ?? []).map((contributor): Element => {
return generateContrib(contributor);
});
const contribGroups: Element[] = [];
if (authorContribs.length) {
contribGroups.push({ type: 'element', name: 'contrib-group', elements: authorContribs });
}
if (otherContribs.length) {
contribGroups.push({ type: 'element', name: 'contrib-group', elements: otherContribs });
}
return contribGroups;
}

return contribs?.length ? [{ type: 'element', name: 'contrib-group', elements: contribs }] : [];
function instWrapElementsFromAffiliation(affiliation: Affiliation): Element[] {
const elements: Element[] = [];
const instWrapElements: Element[] = [];
if (affiliation.name) {
instWrapElements.push({
type: 'element',
name: 'institution',
elements: [{ type: 'text', text: affiliation.name }],
});
}
if (affiliation.isni) {
instWrapElements.push({
type: 'element',
name: 'institution-id',
attributes: { 'institution-id-type': 'isni' },
elements: [{ type: 'text', text: affiliation.isni }],
});
}
if (affiliation.ringgold) {
instWrapElements.push({
type: 'element',
name: 'institution-id',
attributes: { 'institution-id-type': 'ringgold' },
elements: [{ type: 'text', text: `${affiliation.ringgold}` }],
});
}
if (affiliation.ror) {
instWrapElements.push({
type: 'element',
name: 'institution-id',
attributes: { 'institution-id-type': 'ror' },
elements: [{ type: 'text', text: affiliation.ror }],
});
}
if (affiliation.doi) {
const doiAttrs: Record<string, string> = { 'institution-id-type': 'doi' };
if (doi.isOpenFunderRegistry(affiliation.doi)) {
doiAttrs.vocab = 'open-funder-registry';
}
instWrapElements.push({
type: 'element',
name: 'institution-id',
attributes: doiAttrs,
elements: [{ type: 'text', text: affiliation.doi }],
});
}
if (instWrapElements.length) {
elements.push({ type: 'element', name: 'institution-wrap', elements: instWrapElements });
}
if (affiliation.department) {
elements.push({
type: 'element',
name: 'institution-wrap',
elements: [
{
type: 'element',
name: 'institution',
attributes: { 'content-type': 'dept' },
elements: [{ type: 'text', text: affiliation.department }],
},
],
});
}
return elements;
}

export function getArticleAffiliations(frontmatter: ProjectFrontmatter): Element[] {
Expand All @@ -232,55 +303,7 @@ export function getArticleAffiliations(frontmatter: ProjectFrontmatter): Element
if (affiliation.id) {
attributes.id = affiliation.id;
}
const instWrapElements: Element[] = [];
if (affiliation.name) {
instWrapElements.push({
type: 'element',
name: 'institution',
elements: [{ type: 'text', text: affiliation.name }],
});
}
if (affiliation.isni) {
instWrapElements.push({
type: 'element',
name: 'institution-id',
attributes: { 'institution-id-type': 'isni' },
elements: [{ type: 'text', text: affiliation.isni }],
});
}
if (affiliation.ringgold) {
instWrapElements.push({
type: 'element',
name: 'institution-id',
attributes: { 'institution-id-type': 'ringgold' },
elements: [{ type: 'text', text: `${affiliation.ringgold}` }],
});
}
if (affiliation.ror) {
instWrapElements.push({
type: 'element',
name: 'institution-id',
attributes: { 'institution-id-type': 'ror' },
elements: [{ type: 'text', text: affiliation.ror }],
});
}
if (instWrapElements.length) {
elements.push({ type: 'element', name: 'institution-wrap', elements: instWrapElements });
}
if (affiliation.department) {
elements.push({
type: 'element',
name: 'institution-wrap',
elements: [
{
type: 'element',
name: 'institution',
attributes: { 'content-type': 'dept' },
elements: [{ type: 'text', text: affiliation.department }],
},
],
});
}
elements.push(...instWrapElementsFromAffiliation(affiliation));
if (affiliation.address) {
elements.push({
type: 'element',
Expand Down Expand Up @@ -400,19 +423,18 @@ export function getFundingGroup(frontmatter: ProjectFrontmatter): Element[] {
elements.push(
...fund.awards.map((award): Element => {
const awardElements: Element[] = [];
if (award.sources?.length) {
const resolvedSources = award.sources
?.map((source) => {
return frontmatter.affiliations?.find((aff) => aff.id === source);
})
.filter((source): source is Affiliation => !!source);
if (resolvedSources?.length) {
awardElements.push(
...award.sources.map((source): Element => {
...resolvedSources.map((source): Element => {
return {
type: 'element',
name: 'funding-source',
elements: [
{
type: 'element',
name: 'xref',
attributes: { 'ref-type': 'aff', rid: source },
},
],
elements: instWrapElementsFromAffiliation(source),
};
}),
);
Expand Down
Loading

0 comments on commit 651dd77

Please sign in to comment.