14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 | class CFF(ProjectMetadataWriter):
"""Citation File Format (CFF) parser and saver."""
def __init__(
self,
path: Path,
create_if_not_exists: bool = True,
pass_validation: bool = False,
):
"""Citation File Format (CFF) parser.
See [somesy.core.writer.ProjectMetadataWriter.__init__][].
"""
self._yaml = YAML()
self._yaml.preserve_quotes = True
self._yaml.indent(mapping=2, sequence=4, offset=2)
mappings: FieldKeyMapping = {
"name": ["title"],
"description": ["abstract"],
"homepage": ["url"],
"repository": ["repository-code"],
"documentation": IgnoreKey(),
"maintainers": ["contact"],
}
super().__init__(
path,
create_if_not_exists=create_if_not_exists,
direct_mappings=mappings,
pass_validation=pass_validation,
)
def _init_new_file(self):
"""Initialize new CFF file."""
self._data = {
"cff-version": "1.2.0",
"message": "If you use this software, please cite it using these metadata.",
"type": "software",
}
with open(self.path, "w") as f:
self._yaml.dump(self._data, f)
def _load(self):
"""Load the CFF file."""
with open(self.path) as f:
self._data = self._yaml.load(f)
def _validate(self) -> None:
"""Validate the CFF file."""
if self.pass_validation:
return
try:
citation = create_citation(self.path, None)
citation.validate()
except ValueError as e:
raise ValueError(f"CITATION.cff file is not valid!\n{e}") from e
def save(self, path: Optional[Path] = None) -> None:
"""Save the CFF object to a file."""
path = path or self.path
self._yaml.dump(self._data, path)
def _sync_authors(self, metadata: ProjectMetadata) -> None:
"""Ensure that publication authors are added all into author list."""
self.authors = self._sync_person_list(
self.authors, metadata.publication_authors()
)
@staticmethod
def _from_person(person: Union[Person, Entity]):
"""Convert project metadata person or entity object to cff dict for person format."""
json_str = person.model_dump_json(
exclude={
"contribution",
"contribution_types",
"contribution_begin",
"contribution_end",
"author",
"publication_author",
"maintainer",
},
by_alias=True, # e.g. family_names -> family-names, etc.
)
return json.loads(json_str)
@staticmethod
def _to_person(person_obj) -> Union[Person, Entity]:
"""Parse CFF Person to a somesy Person or entity."""
# if the object has key name, it is an entity
if "name" in person_obj:
Entity._aliases()
ret = Entity.make_partial(person_obj)
else:
Person._aliases()
ret = Person.make_partial(person_obj)
# construct (partial) Person while preserving key order from YAML
ret.set_key_order(list(person_obj.keys()))
return ret
|