Skip to content

Commit

Permalink
Enable annotating author roles with CRediT
Browse files Browse the repository at this point in the history
  • Loading branch information
cthoyt committed Oct 23, 2024
1 parent de44627 commit 6058039
Show file tree
Hide file tree
Showing 14 changed files with 749 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## UNRELEASED

- Support for annotating author roles with the Contribution Role Taxonomy (CRediT) (https://github.com/openjournals/inara/pull/87)
- Fix bug in the height of ROR logos (https://github.com/openjournals/inara/pull/90)
- Fix bug in application of `prepare-affiliations.lua` filter (Charles Tapley Hoyt)
- Fix a bug in the injection of `SOURCE_DATE_EPOCH` in tests (https://github.com/openjournals/inara/pull/86)
Expand Down
2 changes: 2 additions & 0 deletions data/defaults/pdf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ filters:
path: self-citation.lua
- type: lua
path: fix-bibentry-spacing.lua
- type: lua
path: prepare-credit.lua
variables:
# styling options
colorlinks: true
Expand Down
3 changes: 3 additions & 0 deletions data/defaults/preprint.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
to: latex
output-file: paper.preprint.tex
template: preprint.latex
filters:
- type: lua
path: prepare-credit.lua
variables:
# styling options
colorlinks: true
Expand Down
2 changes: 2 additions & 0 deletions data/defaults/tex.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ filters:
path: self-citation.lua
- type: lua
path: fix-bibentry-spacing.lua
- type: lua
path: prepare-credit.lua
variables:
# styling options
colorlinks: true
Expand Down
218 changes: 218 additions & 0 deletions data/filters/prepare-credit.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
-- Checks if any contributor information is available

local roles = {
["conceptualization"] = {
name = "Conceptualization",
id = "8b73531f-db56-4914-9502-4cc4d4d8ed73",
uri = "https://credit.niso.org/contributor-roles/conceptualization/"
},
["data-curation"] = {
name = "Data curation",
id = "f93e0f44-f2a4-4ea1-824a-4e0853b05c9d",
uri = "https://credit.niso.org/contributor-roles/data-curation/"
},
["formal-analysis"] = {
name = "Formal analysis",
id = "95394cbd-4dc8-4735-b589-7e5f9e622b3f",
uri = "https://credit.niso.org/contributor-roles/formal-analysis/"
},
["funding-acquisition"] = {
name = "Funding acquisition",
id = "34ff6d68-132f-4438-a1f4-fba61ccf364a",
uri = "https://credit.niso.org/contributor-roles/funding-acquisition/"
},
["investigation"] = {
name = "Investigation",
id = "2451924d-425e-4778-9f4c-36c848ca70c2",
uri = "https://credit.niso.org/contributor-roles/investigation/"
},
["methodology"] = {
name = "Methodology",
id = "f21e2be9-4e38-4ab7-8691-d6f72d5d5843",
uri = "https://credit.niso.org/contributor-roles/methodology/"
},
["project-administration"] = {
name = "Project administration",
id = "a693fe76-ea33-49ad-9dcc-5e4f3ac5f938",
uri = "https://credit.niso.org/contributor-roles/project-administration/"
},
["resources"] = {
name = "Resources",
id = "ebd781f0-bf79-492c-ac21-b31b9c3c990c",
uri = "https://credit.niso.org/contributor-roles/resources/"
},
["software"] = {
name = "Software",
id = "f89c5233-01b0-4778-93e9-cc7d107aa2c8",
uri = "https://credit.niso.org/contributor-roles/software/"
},
["supervision"] = {
name = "Supervision",
id = "0c8ca7d4-06ad-4527-9cea-a8801fcb8746",
uri = "https://credit.niso.org/contributor-roles/supervision/"
},
["validation"] = {
name = "Validation",
id = "4b1bf348-faf2-4fc4-bd66-4cd3a84b9d44",
uri = "https://credit.niso.org/contributor-roles/validation/"
},
["visualization"] = {
name = "Visualization",
id = "76b9d56a-e430-4e0a-84c9-59c11be343ae",
uri = "https://credit.niso.org/contributor-roles/visualization/"
},
["writing-original-draft"] = {
name = "Writing – original draft",
id = "43ebbd94-98b4-42f1-866b-c930cef228ca",
uri = "https://credit.niso.org/contributor-roles/writing-original-draft/"
},
["writing-review-editing"] = {
name = "Writing – review & editing",
id = "d3aead86-f2a2-47f7-bb99-79de6421164d",
uri = "https://credit.niso.org/contributor-roles/writing-review-editing/"
}
}

degrees = pandoc.List {
"Lead",
"Supporting",
"Equal"
}

function invalidRole(str)
return roles[str] == nil
end

function invalidDegree(str)
return not degrees:includes(str)
end

function join_with_commas_and(list)
local len = #list
if len == 0 then
return ""
elseif len == 1 then
return list[1]
elseif len == 2 then
return list[1] .. " and " .. list[2]
else
local result = table.concat(list, ", ", 1, len - 1)
return result .. ", and " .. list[len]
end
end

function capitalize_first_letter(str)
return str:sub(1, 1):upper() .. str:sub(2)
end

function clean_role_dict(d)
if d.type then
return d
else
return { ["type"] = pandoc.utils.stringify(d) }
end
end

local function prepare_credit (meta)
meta.hasRoles = false
for _, author in ipairs(meta.authors or {}) do
if author.roles then
roleList = {}
for _, roleDict in ipairs(author.roles) do
roleDict = clean_role_dict(roleDict)
role = pandoc.utils.stringify(roleDict.type)
if invalidRole(role) then
print("invalid role for author " .. author.name .. ": " .. role)
elseif roleDict.degree then
degree = capitalize_first_letter(pandoc.utils.stringify(roleDict.degree))
if invalidDegree(degree) then
print("invalid degree for author " .. author.name .. ": " .. degree)
-- even though the degree is invalid, add the role anyway
table.insert(roleList, roles[role].name)
else
table.insert(roleList, roles[role].name .. " (" .. degree .. ")")
end
else
table.insert(roleList, roles[role].name)
end
end
if #roleList > 0 then
meta.hasRoles = true
author.rolesString = join_with_commas_and(roleList)
end
end
end
return meta
end

function Meta (meta)
local ok, result = pcall(prepare_credit, meta)
if ok then
return result
end
end

function assertEqual(expected, actual)
assert(expected == actual, "got \"" .. actual .. "\", expected \"" .. expected .. "\"")
end

function tests()
assert("" == join_with_commas_and({ }))
assert("foo" == join_with_commas_and({ "foo" }))
assert("foo and bar" == join_with_commas_and({ "foo", "bar" }))
assert("foo, bar, and baz" == join_with_commas_and({ "foo", "bar", "baz" }))

local m1 = {
["authors"] = {
{
["name"] = "Author 1",
["roles"] = {
"methodology"
}
},
{
["name"] = "Author 2",
['roles'] = {
{ ["type"] = "methodology" }
}
},
{
["name"] = "Author 3",
['roles'] = {
{ ["type"] = "methodology" },
{ ["type"] = "data-curation" },
{ ["type"] = "conceptualization" },
}
},
{
["name"] = "Author 4",
['roles'] = {
{ ["type"] = "methodology", ["degree"] = "lead" },
{ ["type"] = "data-curation", ["degree"] = "supporting" },
{ ["type"] = "conceptualization" },
}
},
}
}
local m1t = prepare_credit(m1)
assert(m1t.hasRoles, "hasRoles should be set to true")
assertEqual("Methodology", m1t['authors'][1].rolesString)
assertEqual("Methodology", m1t['authors'][2].rolesString)
assertEqual("Methodology, Data curation, and Conceptualization", m1t['authors'][3].rolesString)
assertEqual("Methodology (Lead), Data curation (Supporting), and Conceptualization", m1t['authors'][4].rolesString)

local m2 = {
["authors"] = {
{
["name"] = "Author 1"
},
{
["name"] = "Author 2"
}
}
}
local m2t = prepare_credit(m2)
assert(not m2t.hasRoles, "hasRoles should be set to false")
end

tests()
11 changes: 11 additions & 0 deletions data/templates/default.latex
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,17 @@ $if(lof)$
$endif$
$body$

$if(hasRoles)$
\section{Author Contributions}\label{author-contributions}
\begin{enumerate}
$for(authors)$
$if(it.rolesString)$
\item $it.name$ - $it.rolesString$
$endif$
$endfor$
\end{enumerate}
$endif$

$if(natbib)$
$if(bibliography)$
$if(biblio-title)$
Expand Down
11 changes: 11 additions & 0 deletions data/templates/preprint.latex
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,17 @@ $if(has-frontmatter)$
$endif$
$body$

$if(hasRoles)$
\section{Author Contributions}\label{author-contributions}
\begin{enumerate}
$for(authors)$
$if(it.rolesString)$
\item $it.name$ - $it.rolesString$
$endif$
$endfor$
\end{enumerate}
$endif$

$if(has-frontmatter)$
\backmatter
$endif$
Expand Down
79 changes: 79 additions & 0 deletions example/paper.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,27 @@ authors:
affiliation: "1, 2, 4"
orcid: 0000-0002-9455-0796
corresponding: true
roles:
- type: software
degree: equal
- 'methodology'
- name: Juanjo Bazán
orcid: 0000-0001-7699-3983
affiliation: [1]
equal-contrib: true
roles:
- type: software
degree: equal
- name: Arfon M. Smith
orcid: 0000-0002-3957-2474
affiliation: [1, 3]
equal-contrib: true
roles:
- type: software
degree: equal
- type: supervision
degree: lead

affiliations:
- index: 1
name: Open Journals
Expand Down Expand Up @@ -365,6 +378,72 @@ authors:
<!-- given-names: 瀧 -->
<!-- surname: 立花 -->

## Contributor Roles

The [Contribution Role Taxonomy (CRediT)](https://credit.niso.org/contributor-roles) defines
fourteen standard roles of authors. Each author can be annotated with one or more contribution
roles.

1. [conceptualization](https://credit.niso.org/contributor-roles/conceptualization)
2. [data-curation](https://credit.niso.org/contributor-roles/data-curation)
3. [formal-analysis](https://credit.niso.org/contributor-roles/formal-analysis)
4. [funding-acquisition](https://credit.niso.org/contributor-roles/funding-acquisition)
5. [investigation](https://credit.niso.org/contributor-roles/investigation)
6. [methodology](https://credit.niso.org/contributor-roles/methodology)
7. [project-administration](https://credit.niso.org/contributor-roles/project-administration)
8. [resources](https://credit.niso.org/contributor-roles/resources)
9. [software](https://credit.niso.org/contributor-roles/software)
10. [supervision](https://credit.niso.org/contributor-roles/supervision)
11. [validation](https://credit.niso.org/contributor-roles/validation)
12. [visualization](https://credit.niso.org/contributor-roles/visualization)
13. [writing-original-draft](https://credit.niso.org/contributor-roles/writing-original-draft)
14. [writing-review-editing](https://credit.niso.org/contributor-roles/writing-review-editing)

JATS also specifies three degrees which can be used to quantify the impact of a contribution:

1. `Lead`
2. `Supporting`
3. `Equal` - for use if multiple equivalent leads

Together, these can be used to identify which authors materially contributed to the paper,
such as through `formal-analysis` or `data-curation` and which authors contributed immaterially,
such as through `supervision`. It also allows for saying if multiple people made the same
kind of contribution, who took the lead.

```yaml
authors:
- name: John Doe
affiliation: [ 1 ]
roles:
- type: 'formal-analysis'
degree: 'lead'
- name: John Boss
affiliation: [ 1 ]
roles:
- type: 'funding-acquisition'
degree: 'lead'
- type: 'supervision'
degree: 'lead'
```

Roles are optional, and within roles, degrees are optional. It's possible to shorthand
roles by using strings directly:

```yaml
authors:
- name: John Doe
affiliation: [ 1 ]
roles:
- 'formal-analysis'
- name: John Boss
affiliation: [ 1 ]
roles:
- 'funding-acquisition'
- 'supervision'
```

## Affiliations

Each affiliation requires an `index` and `name`.
Expand Down
Loading

0 comments on commit 6058039

Please sign in to comment.