Coverage for src/somesy/cff/writer.py: 100%

39 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-08-10 14:33 +0000

1"""Citation File Format (CFF) parser and saver.""" 

2import json 

3from pathlib import Path 

4from typing import Optional 

5 

6from cffconvert.cli.create_citation import create_citation 

7from ruamel.yaml import YAML 

8 

9from somesy.core.models import Person 

10from somesy.core.writer import ProjectMetadataWriter 

11 

12 

13class CFF(ProjectMetadataWriter): 

14 """Citation File Format (CFF) parser and saver.""" 

15 

16 def __init__( 

17 self, 

18 path: Path, 

19 create_if_not_exists: bool = True, 

20 ): 

21 """Citation File Format (CFF) parser. 

22 

23 See [somesy.core.writer.ProjectMetadataWriter.__init__][]. 

24 """ 

25 self._yaml = YAML() 

26 self._yaml.preserve_quotes = True 

27 

28 mappings = { 

29 "name": ["title"], 

30 "description": ["abstract"], 

31 "homepage": ["url"], 

32 "repository": ["repository-code"], 

33 "maintainers": ["contact"], 

34 } 

35 super().__init__( 

36 path, create_if_not_exists=create_if_not_exists, direct_mappings=mappings 

37 ) 

38 

39 def _init_new_file(self): 

40 """Initialize new CFF file.""" 

41 self._data = { 

42 "cff-version": "1.2.0", 

43 "message": "If you use this software, please cite it using these metadata.", 

44 "type": "software", 

45 } 

46 with open(self.path, "w") as f: 

47 self._yaml.dump(self._data, f) 

48 

49 def _load(self): 

50 """Load the CFF file.""" 

51 with open(self.path) as f: 

52 self._data = self._yaml.load(f) 

53 

54 def _validate(self): 

55 """Validate the CFF file.""" 

56 try: 

57 citation = create_citation(self.path, None) 

58 citation.validate() 

59 except ValueError as e: 

60 raise ValueError(f"CITATION.cff file is not valid!\n{e}") from e 

61 

62 def save(self, path: Optional[Path] = None) -> None: 

63 """Save the CFF object to a file.""" 

64 path = path or self.path 

65 self._yaml.dump(self._data, path) 

66 

67 @staticmethod 

68 def _from_person(person: Person): 

69 """Convert project metadata person object to cff dict for person format.""" 

70 json_str = person.json( 

71 exclude={ 

72 "contribution", 

73 "contribution_types", 

74 "contribution_begin", 

75 "contribution_end", 

76 "author", 

77 "maintainer", 

78 }, 

79 by_alias=True, # e.g. family_names -> family-names, etc. 

80 ) 

81 return json.loads(json_str) 

82 

83 @staticmethod 

84 def _to_person(person_obj) -> Person: 

85 """Parse CFF Person to a somesy Person.""" 

86 # construct (partial) Person while preserving key order from YAML 

87 Person._aliases() 

88 ret = Person.make_partial(person_obj) 

89 ret.set_key_order(list(person_obj.keys())) 

90 return ret