Skip to content

Commit 6058039

Browse files
committed
Enable annotating author roles with CRediT
1 parent de44627 commit 6058039

File tree

14 files changed

+749
-0
lines changed

14 files changed

+749
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
## UNRELEASED
66

7+
- Support for annotating author roles with the Contribution Role Taxonomy (CRediT) (https://github.com/openjournals/inara/pull/87)
78
- Fix bug in the height of ROR logos (https://github.com/openjournals/inara/pull/90)
89
- Fix bug in application of `prepare-affiliations.lua` filter (Charles Tapley Hoyt)
910
- Fix a bug in the injection of `SOURCE_DATE_EPOCH` in tests (https://github.com/openjournals/inara/pull/86)

data/defaults/pdf.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ filters:
1111
path: self-citation.lua
1212
- type: lua
1313
path: fix-bibentry-spacing.lua
14+
- type: lua
15+
path: prepare-credit.lua
1416
variables:
1517
# styling options
1618
colorlinks: true

data/defaults/preprint.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
to: latex
22
output-file: paper.preprint.tex
33
template: preprint.latex
4+
filters:
5+
- type: lua
6+
path: prepare-credit.lua
47
variables:
58
# styling options
69
colorlinks: true

data/defaults/tex.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ filters:
1111
path: self-citation.lua
1212
- type: lua
1313
path: fix-bibentry-spacing.lua
14+
- type: lua
15+
path: prepare-credit.lua
1416
variables:
1517
# styling options
1618
colorlinks: true

data/filters/prepare-credit.lua

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
-- Checks if any contributor information is available
2+
3+
local roles = {
4+
["conceptualization"] = {
5+
name = "Conceptualization",
6+
id = "8b73531f-db56-4914-9502-4cc4d4d8ed73",
7+
uri = "https://credit.niso.org/contributor-roles/conceptualization/"
8+
},
9+
["data-curation"] = {
10+
name = "Data curation",
11+
id = "f93e0f44-f2a4-4ea1-824a-4e0853b05c9d",
12+
uri = "https://credit.niso.org/contributor-roles/data-curation/"
13+
},
14+
["formal-analysis"] = {
15+
name = "Formal analysis",
16+
id = "95394cbd-4dc8-4735-b589-7e5f9e622b3f",
17+
uri = "https://credit.niso.org/contributor-roles/formal-analysis/"
18+
},
19+
["funding-acquisition"] = {
20+
name = "Funding acquisition",
21+
id = "34ff6d68-132f-4438-a1f4-fba61ccf364a",
22+
uri = "https://credit.niso.org/contributor-roles/funding-acquisition/"
23+
},
24+
["investigation"] = {
25+
name = "Investigation",
26+
id = "2451924d-425e-4778-9f4c-36c848ca70c2",
27+
uri = "https://credit.niso.org/contributor-roles/investigation/"
28+
},
29+
["methodology"] = {
30+
name = "Methodology",
31+
id = "f21e2be9-4e38-4ab7-8691-d6f72d5d5843",
32+
uri = "https://credit.niso.org/contributor-roles/methodology/"
33+
},
34+
["project-administration"] = {
35+
name = "Project administration",
36+
id = "a693fe76-ea33-49ad-9dcc-5e4f3ac5f938",
37+
uri = "https://credit.niso.org/contributor-roles/project-administration/"
38+
},
39+
["resources"] = {
40+
name = "Resources",
41+
id = "ebd781f0-bf79-492c-ac21-b31b9c3c990c",
42+
uri = "https://credit.niso.org/contributor-roles/resources/"
43+
},
44+
["software"] = {
45+
name = "Software",
46+
id = "f89c5233-01b0-4778-93e9-cc7d107aa2c8",
47+
uri = "https://credit.niso.org/contributor-roles/software/"
48+
},
49+
["supervision"] = {
50+
name = "Supervision",
51+
id = "0c8ca7d4-06ad-4527-9cea-a8801fcb8746",
52+
uri = "https://credit.niso.org/contributor-roles/supervision/"
53+
},
54+
["validation"] = {
55+
name = "Validation",
56+
id = "4b1bf348-faf2-4fc4-bd66-4cd3a84b9d44",
57+
uri = "https://credit.niso.org/contributor-roles/validation/"
58+
},
59+
["visualization"] = {
60+
name = "Visualization",
61+
id = "76b9d56a-e430-4e0a-84c9-59c11be343ae",
62+
uri = "https://credit.niso.org/contributor-roles/visualization/"
63+
},
64+
["writing-original-draft"] = {
65+
name = "Writing – original draft",
66+
id = "43ebbd94-98b4-42f1-866b-c930cef228ca",
67+
uri = "https://credit.niso.org/contributor-roles/writing-original-draft/"
68+
},
69+
["writing-review-editing"] = {
70+
name = "Writing – review & editing",
71+
id = "d3aead86-f2a2-47f7-bb99-79de6421164d",
72+
uri = "https://credit.niso.org/contributor-roles/writing-review-editing/"
73+
}
74+
}
75+
76+
degrees = pandoc.List {
77+
"Lead",
78+
"Supporting",
79+
"Equal"
80+
}
81+
82+
function invalidRole(str)
83+
return roles[str] == nil
84+
end
85+
86+
function invalidDegree(str)
87+
return not degrees:includes(str)
88+
end
89+
90+
function join_with_commas_and(list)
91+
local len = #list
92+
if len == 0 then
93+
return ""
94+
elseif len == 1 then
95+
return list[1]
96+
elseif len == 2 then
97+
return list[1] .. " and " .. list[2]
98+
else
99+
local result = table.concat(list, ", ", 1, len - 1)
100+
return result .. ", and " .. list[len]
101+
end
102+
end
103+
104+
function capitalize_first_letter(str)
105+
return str:sub(1, 1):upper() .. str:sub(2)
106+
end
107+
108+
function clean_role_dict(d)
109+
if d.type then
110+
return d
111+
else
112+
return { ["type"] = pandoc.utils.stringify(d) }
113+
end
114+
end
115+
116+
local function prepare_credit (meta)
117+
meta.hasRoles = false
118+
for _, author in ipairs(meta.authors or {}) do
119+
if author.roles then
120+
roleList = {}
121+
for _, roleDict in ipairs(author.roles) do
122+
roleDict = clean_role_dict(roleDict)
123+
role = pandoc.utils.stringify(roleDict.type)
124+
if invalidRole(role) then
125+
print("invalid role for author " .. author.name .. ": " .. role)
126+
elseif roleDict.degree then
127+
degree = capitalize_first_letter(pandoc.utils.stringify(roleDict.degree))
128+
if invalidDegree(degree) then
129+
print("invalid degree for author " .. author.name .. ": " .. degree)
130+
-- even though the degree is invalid, add the role anyway
131+
table.insert(roleList, roles[role].name)
132+
else
133+
table.insert(roleList, roles[role].name .. " (" .. degree .. ")")
134+
end
135+
else
136+
table.insert(roleList, roles[role].name)
137+
end
138+
end
139+
if #roleList > 0 then
140+
meta.hasRoles = true
141+
author.rolesString = join_with_commas_and(roleList)
142+
end
143+
end
144+
end
145+
return meta
146+
end
147+
148+
function Meta (meta)
149+
local ok, result = pcall(prepare_credit, meta)
150+
if ok then
151+
return result
152+
end
153+
end
154+
155+
function assertEqual(expected, actual)
156+
assert(expected == actual, "got \"" .. actual .. "\", expected \"" .. expected .. "\"")
157+
end
158+
159+
function tests()
160+
assert("" == join_with_commas_and({ }))
161+
assert("foo" == join_with_commas_and({ "foo" }))
162+
assert("foo and bar" == join_with_commas_and({ "foo", "bar" }))
163+
assert("foo, bar, and baz" == join_with_commas_and({ "foo", "bar", "baz" }))
164+
165+
local m1 = {
166+
["authors"] = {
167+
{
168+
["name"] = "Author 1",
169+
["roles"] = {
170+
"methodology"
171+
}
172+
},
173+
{
174+
["name"] = "Author 2",
175+
['roles'] = {
176+
{ ["type"] = "methodology" }
177+
}
178+
},
179+
{
180+
["name"] = "Author 3",
181+
['roles'] = {
182+
{ ["type"] = "methodology" },
183+
{ ["type"] = "data-curation" },
184+
{ ["type"] = "conceptualization" },
185+
}
186+
},
187+
{
188+
["name"] = "Author 4",
189+
['roles'] = {
190+
{ ["type"] = "methodology", ["degree"] = "lead" },
191+
{ ["type"] = "data-curation", ["degree"] = "supporting" },
192+
{ ["type"] = "conceptualization" },
193+
}
194+
},
195+
}
196+
}
197+
local m1t = prepare_credit(m1)
198+
assert(m1t.hasRoles, "hasRoles should be set to true")
199+
assertEqual("Methodology", m1t['authors'][1].rolesString)
200+
assertEqual("Methodology", m1t['authors'][2].rolesString)
201+
assertEqual("Methodology, Data curation, and Conceptualization", m1t['authors'][3].rolesString)
202+
assertEqual("Methodology (Lead), Data curation (Supporting), and Conceptualization", m1t['authors'][4].rolesString)
203+
204+
local m2 = {
205+
["authors"] = {
206+
{
207+
["name"] = "Author 1"
208+
},
209+
{
210+
["name"] = "Author 2"
211+
}
212+
}
213+
}
214+
local m2t = prepare_credit(m2)
215+
assert(not m2t.hasRoles, "hasRoles should be set to false")
216+
end
217+
218+
tests()

data/templates/default.latex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,17 @@ $if(lof)$
554554
$endif$
555555
$body$
556556

557+
$if(hasRoles)$
558+
\section{Author Contributions}\label{author-contributions}
559+
\begin{enumerate}
560+
$for(authors)$
561+
$if(it.rolesString)$
562+
\item $it.name$ - $it.rolesString$
563+
$endif$
564+
$endfor$
565+
\end{enumerate}
566+
$endif$
567+
557568
$if(natbib)$
558569
$if(bibliography)$
559570
$if(biblio-title)$

data/templates/preprint.latex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,17 @@ $if(has-frontmatter)$
435435
$endif$
436436
$body$
437437

438+
$if(hasRoles)$
439+
\section{Author Contributions}\label{author-contributions}
440+
\begin{enumerate}
441+
$for(authors)$
442+
$if(it.rolesString)$
443+
\item $it.name$ - $it.rolesString$
444+
$endif$
445+
$endfor$
446+
\end{enumerate}
447+
$endif$
448+
438449
$if(has-frontmatter)$
439450
\backmatter
440451
$endif$

example/paper.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,27 @@ authors:
77
affiliation: "1, 2, 4"
88
orcid: 0000-0002-9455-0796
99
corresponding: true
10+
roles:
11+
- type: software
12+
degree: equal
13+
- 'methodology'
1014
- name: Juanjo Bazán
1115
orcid: 0000-0001-7699-3983
1216
affiliation: [1]
1317
equal-contrib: true
18+
roles:
19+
- type: software
20+
degree: equal
1421
- name: Arfon M. Smith
1522
orcid: 0000-0002-3957-2474
1623
affiliation: [1, 3]
1724
equal-contrib: true
25+
roles:
26+
- type: software
27+
degree: equal
28+
- type: supervision
29+
degree: lead
30+
1831
affiliations:
1932
- index: 1
2033
name: Open Journals
@@ -365,6 +378,72 @@ authors:
365378
<!-- given-names: 瀧 -->
366379
<!-- surname: 立花 -->
367380

381+
## Contributor Roles
382+
383+
The [Contribution Role Taxonomy (CRediT)](https://credit.niso.org/contributor-roles) defines
384+
fourteen standard roles of authors. Each author can be annotated with one or more contribution
385+
roles.
386+
387+
1. [conceptualization](https://credit.niso.org/contributor-roles/conceptualization)
388+
2. [data-curation](https://credit.niso.org/contributor-roles/data-curation)
389+
3. [formal-analysis](https://credit.niso.org/contributor-roles/formal-analysis)
390+
4. [funding-acquisition](https://credit.niso.org/contributor-roles/funding-acquisition)
391+
5. [investigation](https://credit.niso.org/contributor-roles/investigation)
392+
6. [methodology](https://credit.niso.org/contributor-roles/methodology)
393+
7. [project-administration](https://credit.niso.org/contributor-roles/project-administration)
394+
8. [resources](https://credit.niso.org/contributor-roles/resources)
395+
9. [software](https://credit.niso.org/contributor-roles/software)
396+
10. [supervision](https://credit.niso.org/contributor-roles/supervision)
397+
11. [validation](https://credit.niso.org/contributor-roles/validation)
398+
12. [visualization](https://credit.niso.org/contributor-roles/visualization)
399+
13. [writing-original-draft](https://credit.niso.org/contributor-roles/writing-original-draft)
400+
14. [writing-review-editing](https://credit.niso.org/contributor-roles/writing-review-editing)
401+
402+
JATS also specifies three degrees which can be used to quantify the impact of a contribution:
403+
404+
1. `Lead`
405+
2. `Supporting`
406+
3. `Equal` - for use if multiple equivalent leads
407+
408+
Together, these can be used to identify which authors materially contributed to the paper,
409+
such as through `formal-analysis` or `data-curation` and which authors contributed immaterially,
410+
such as through `supervision`. It also allows for saying if multiple people made the same
411+
kind of contribution, who took the lead.
412+
413+
```yaml
414+
authors:
415+
- name: John Doe
416+
affiliation: [ 1 ]
417+
roles:
418+
- type: 'formal-analysis'
419+
degree: 'lead'
420+
421+
- name: John Boss
422+
affiliation: [ 1 ]
423+
roles:
424+
- type: 'funding-acquisition'
425+
degree: 'lead'
426+
- type: 'supervision'
427+
degree: 'lead'
428+
```
429+
430+
Roles are optional, and within roles, degrees are optional. It's possible to shorthand
431+
roles by using strings directly:
432+
433+
```yaml
434+
authors:
435+
- name: John Doe
436+
affiliation: [ 1 ]
437+
roles:
438+
- 'formal-analysis'
439+
440+
- name: John Boss
441+
affiliation: [ 1 ]
442+
roles:
443+
- 'funding-acquisition'
444+
- 'supervision'
445+
```
446+
368447
## Affiliations
369448

370449
Each affiliation requires an `index` and `name`.

0 commit comments

Comments
 (0)