Skip to content

Commit 9acacdf

Browse files
author
a.pirogov
committed
feat: distinguish between author and publication_author
1 parent ff50074 commit 9acacdf

8 files changed

Lines changed: 105 additions & 23 deletions

File tree

.somesy.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ contribution_begin = "2023-06-01"
5454
contribution_end = "2023-06-30"
5555
contribution_types = ["ideas"]
5656

57+
publication_author = true
58+
5759
[[project.people]]
5860
family-names = "Sandfeld"
5961
given-names = "Stefan"
@@ -62,6 +64,8 @@ orcid = "https://orcid.org/0000-0001-9560-4728"
6264

6365
contribution_types = ["fundingFinding"]
6466

67+
publication_author = true
68+
6569
[config]
6670
no_sync_cff = false
6771
cff_file = "CITATION.cff"

CITATION.cff

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,22 @@ keywords:
1111
- metadata
1212
- FAIR
1313
authors:
14-
- email: m.soylu@fz-juelich.de
15-
family-names: Soylu
14+
- orcid: https://orcid.org/0000-0003-2637-0432
15+
email: m.soylu@fz-juelich.de
1616
given-names: Mustafa
17-
orcid: https://orcid.org/0000-0003-2637-0432
18-
- email: a.pirogov@fz-juelich.de
19-
family-names: Pirogov
17+
family-names: Soylu
18+
- orcid: https://orcid.org/0000-0002-5077-7497
19+
email: a.pirogov@fz-juelich.de
2020
given-names: Anton
21-
orcid: https://orcid.org/0000-0002-5077-7497
21+
family-names: Pirogov
22+
- email: v.hofmann@fz-juelich.de
23+
orcid: https://orcid.org/0000-0002-5149-603X
24+
family-names: Hofmann
25+
given-names: Volker
26+
- email: s.sandfeld@fz-juelich.de
27+
orcid: https://orcid.org/0000-0001-9560-4728
28+
family-names: Sandfeld
29+
given-names: Stefan
2230
contact:
2331
- email: m.soylu@fz-juelich.de
2432
family-names: Soylu

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ keywords = ["some", "descriptive", "keywords"]
7171
license = "MIT"
7272
repository = "https://github.com/username/my-amazing-project"
7373

74-
# This is you, the proud author of your project
74+
# This is you, the proud author of your project:
7575
[[project.people]]
7676
given-names = "Jane"
7777
family-names = "Doe"
@@ -80,12 +80,14 @@ orcid = "https://orcid.org/0000-0000-0000-0001"
8080
author = true # is a full author of the project (i.e. appears in citations)
8181
maintainer = true # currently maintains the project (i.e. is a contact person)
8282

83-
# this person is a acknowledged contributor, but not author or maintainer:
83+
# this person is an acknowledged contributor, but not author or maintainer:
8484
[[project.people]]
8585
given-names = "Another"
8686
family-names = "Contributor"
8787
email = "a.contributor@example.com"
8888
orcid = "https://orcid.org/0000-0000-0000-0002"
89+
# ... but for scientific publications, this contributor should be listed as author:
90+
publication_author = true
8991

9092
[config]
9193
verbose = true # show detailed information about what somesy is doing
@@ -151,17 +153,16 @@ Here is an overview of all the currently supported files and formats.
151153
| Input Formats | Status | | Target Formats | Status |
152154
| -------------- | ------ |-| ----------------------------- | ------ |
153155
| (.)somesy.toml | ✓ | | pyproject.toml _(poetry)_ | ✓ |
154-
| pyproject.toml | ✓(1.) | | pyproject.toml _(setuptools)_ | ✓(2.) |
155-
| package.json | ✓ | | package.json | ✓(3.) |
156+
| pyproject.toml | ✓ | | pyproject.toml _(setuptools)_ | ✓(1.) |
157+
| package.json | ✓ | | package.json | ✓(2.) |
156158
| | | | CITATION.cff | ✓ |
157-
| | | | codemeta.json | ✓(4.) |
159+
| | | | codemeta.json | ✓(3.) |
158160

159161
**Notes:**
160162

161-
1. information must be placed inside a `tool.somesy` section (as explained above)
162-
2. note that `somesy` does not support setuptools *dynamic fields*
163-
3. `package.json` only supports one author, so `somesy` will pick the *first* listed author
164-
4. unlike other targets, `somesy` will *re-create* the `codemeta.json` (i.e. do not edit it by hand!)
163+
1. note that `somesy` does not support setuptools *dynamic fields*
164+
2. `package.json` only supports one author, so `somesy` will pick the *first* listed author
165+
3. unlike other targets, `somesy` will *re-create* the `codemeta.json` (i.e. do not edit it by hand!)
165166

166167
<!-- --8<-- [end:quickstart] -->
167168

codemeta.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,26 @@
1111
"audience": [
1212
{
1313
"@type": "Audience",
14-
"audienceType": "Science/Research"
14+
"audienceType": "Developers"
1515
},
1616
{
1717
"@type": "Audience",
18-
"audienceType": "Developers"
18+
"audienceType": "Science/Research"
1919
}
2020
],
2121
"author": [
22+
{
23+
"@id": "https://orcid.org/0000-0002-5149-603X",
24+
"@type": "Person",
25+
"familyName": "Hofmann",
26+
"givenName": "Volker"
27+
},
28+
{
29+
"@id": "https://orcid.org/0000-0001-9560-4728",
30+
"@type": "Person",
31+
"familyName": "Sandfeld",
32+
"givenName": "Stefan"
33+
},
2234
{
2335
"@id": "https://orcid.org/0000-0003-2637-0432",
2436
"@type": "Person",
@@ -49,14 +61,14 @@
4961
"identifier": "'version':",
5062
"name": "'version':",
5163
"runtimePlatform": "Python 3",
52-
"version": "'^0.7.0'}"
64+
"version": "'^1.9.2'}"
5365
},
5466
{
5567
"@type": "SoftwareApplication",
5668
"identifier": "'version':",
5769
"name": "'version':",
5870
"runtimePlatform": "Python 3",
59-
"version": "'^1.9.2'}"
71+
"version": "'^0.7.0'}"
6072
},
6173
{
6274
"@type": "SoftwareApplication",

docs/manual.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,21 @@ hood, you use `somesy` (or anything like it) or not - they can use the
406406
corresponding files they already know to get the information they need.
407407
So there is no "risk" involved in adopting `somesy`, because it does not try to
408408
abolish any other formats or standards or becoming such.
409+
410+
### In my project, the effective authors and the publication authors are not the same! What to do?
411+
412+
The `author` flag in `somesy` is intended to mark people who significantly contributed
413+
to the project in a hands-on way and are closely familiar with details, i.e. can answer
414+
specific questions. A reason to stick with this strict understanding of "author"
415+
is that a user will be usually interested in contacting such a person to help
416+
them with problems.
417+
418+
However, we are aware that acknowledgement practices in different scientific
419+
communities vary and current practices in academic publication do not allow for
420+
sufficiently granular distinction of contributor roles.
421+
Even though the proper solution to problem would be improving community practices,
422+
`somesy` supports the `publication_author` flag, that can be set independently of the
423+
`author` flag and will make sure that certain contributors **will** appear as authors
424+
in an academic citation context (i.e. reflected in the `CITATION.cff` file, which can be
425+
used for [Zenodo publications](https://docs.software-metadata.pub/en/latest/tutorials/automated-publication-with-ci.html)), but **will not** appear as authors in a technical context
426+
(such as the metadata in a software registry like [PyPI](https://pypi.org)).

src/somesy/cff/writer.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from cffconvert.cli.create_citation import create_citation
77
from ruamel.yaml import YAML
88

9-
from somesy.core.models import Person
9+
from somesy.core.models import Person, ProjectMetadata
1010
from somesy.core.writer import ProjectMetadataWriter
1111

1212

@@ -64,6 +64,12 @@ def save(self, path: Optional[Path] = None) -> None:
6464
path = path or self.path
6565
self._yaml.dump(self._data, path)
6666

67+
def _sync_authors(self, metadata: ProjectMetadata) -> None:
68+
"""Ensure that publication authors are added all into author list."""
69+
self.authors = self._sync_person_list(
70+
self.authors, metadata.publication_authors()
71+
)
72+
6773
@staticmethod
6874
def _from_person(person: Person):
6975
"""Convert project metadata person object to cff dict for person format."""
@@ -74,6 +80,7 @@ def _from_person(person: Person):
7480
"contribution_begin",
7581
"contribution_end",
7682
"author",
83+
"publication_author",
7784
"maintainer",
7885
},
7986
by_alias=True, # e.g. family_names -> family-names, etc.

src/somesy/core/models.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,15 @@ class Person(SomesyBaseModel):
274274
author: Annotated[
275275
bool,
276276
Field(
277-
description="Indicates whether the person is an author of the project (i.e. for citations)."
277+
description="Indicates whether the person is an author of the project (i.e. significant contributor)."
278278
),
279279
] = False
280+
publication_author: Annotated[
281+
Optional[bool],
282+
Field(
283+
description="Indicates whether the person is to be listed as an author in academic citations."
284+
),
285+
]
280286
maintainer: Annotated[
281287
bool,
282288
Field(
@@ -303,6 +309,17 @@ class Person(SomesyBaseModel):
303309
Optional[date], Field(description="Ending date of the contribution.")
304310
]
305311

312+
@root_validator
313+
def author_implies_publication(cls, values):
314+
"""Ensure consistency of author and publication_author."""
315+
if values["author"]:
316+
# NOTE: explicitly check for False (different case from None = missing!)
317+
if values["publication_author"] == False:
318+
msg = "Combining author=true and publication_author=false is invalid!"
319+
raise ValueError(msg)
320+
values["publication_author"] = True
321+
return values
322+
306323
# helper methods
307324

308325
@property
@@ -394,9 +411,16 @@ def at_least_one_author(cls, people):
394411
)
395412

396413
def authors(self):
397-
"""Return people marked as authors."""
414+
"""Return people explicitly marked as authors."""
398415
return [p for p in self.people if p.author]
399416

417+
def publication_authors(self):
418+
"""Return people marked as publication authors.
419+
420+
This always includes people marked as authors.
421+
"""
422+
return [p for p in self.people if p.publication_author]
423+
400424
def maintainers(self):
401425
"""Return people marked as maintainers."""
402426
return [p for p in self.people if p.maintainer]

src/somesy/core/writer.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,14 @@ def _sync_person_list(self, old: List[Any], new: List[Person]) -> List[Any]:
183183
old_people: List[Person] = self._parse_people(old)
184184
return self._merge_person_metadata(old_people, new)
185185

186+
def _sync_authors(self, metadata: ProjectMetadata) -> None:
187+
"""Sync output file authors with authors from metadata.
188+
189+
This method is existing for the publication_author special case
190+
when synchronizing to CITATION.cff.
191+
"""
192+
self.authors = self._sync_person_list(self.authors, metadata.authors())
193+
186194
def sync(self, metadata: ProjectMetadata) -> None:
187195
"""Sync output file with other metadata files."""
188196
self.name = metadata.name
@@ -194,7 +202,7 @@ def sync(self, metadata: ProjectMetadata) -> None:
194202
if metadata.keywords:
195203
self.keywords = metadata.keywords
196204

197-
self.authors = self._sync_person_list(self.authors, metadata.authors())
205+
self._sync_authors(metadata)
198206
self.maintainers = self._sync_person_list(
199207
self.maintainers, metadata.maintainers()
200208
)

0 commit comments

Comments
 (0)