commonmeta-py 0.123__py3-none-any.whl → 0.125__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
commonmeta/__init__.py CHANGED
@@ -10,7 +10,7 @@ commonmeta-py is a Python library to convert scholarly metadata
10
10
  """
11
11
 
12
12
  __title__ = "commonmeta-py"
13
- __version__ = "0.123"
13
+ __version__ = "0.125"
14
14
  __author__ = "Martin Fenner"
15
15
  __license__ = "MIT"
16
16
 
commonmeta/api_utils.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """API Utils module for commonmeta-py"""
2
2
 
3
3
  from datetime import datetime as date
4
- from typing import Optional
4
+ from typing import Dict, Optional
5
5
 
6
6
  import jwt
7
7
  import requests
@@ -31,7 +31,7 @@ def generate_ghost_token(key: str) -> str:
31
31
 
32
32
  def update_ghost_post_via_api(
33
33
  _id: str, api_key: Optional[str] = None, api_url: Optional[str] = None
34
- ) -> dict[str, str]:
34
+ ) -> Dict[str, str]:
35
35
  """Update Ghost post via API"""
36
36
  # get post doi and url from Rogue Scholar API
37
37
  # post url is needed to find post via Ghost API
commonmeta/metadata.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """Metadata"""
2
2
 
3
3
  from os import path
4
- from typing import Optional, Union
4
+ from typing import Any, Dict, List, Optional, Union
5
5
 
6
6
  import orjson as json
7
7
  import yaml
@@ -69,7 +69,7 @@ from .writers.schema_org_writer import write_schema_org, write_schema_org_list
69
69
  class Metadata:
70
70
  """Metadata"""
71
71
 
72
- def __init__(self, string: Optional[Union[str, dict]], **kwargs):
72
+ def __init__(self, string: Optional[Union[str, Dict[str, Any]]], **kwargs):
73
73
  if string is None or not isinstance(string, (str, dict)):
74
74
  raise ValueError("No input found")
75
75
  self.via = kwargs.get("via", None)
@@ -147,7 +147,7 @@ class Metadata:
147
147
  # Default fallback
148
148
  raise ValueError("No metadata found")
149
149
 
150
- def _get_metadata_from_pid(self, pid, via) -> dict:
150
+ def _get_metadata_from_pid(self, pid, via) -> Dict[str, Any]:
151
151
  """Helper method to get metadata from a PID."""
152
152
  if via == "schema_org":
153
153
  return get_schema_org(pid)
@@ -170,7 +170,7 @@ class Metadata:
170
170
  else:
171
171
  return {"pid": pid}
172
172
 
173
- def _get_metadata_from_string(self, string, via) -> dict:
173
+ def _get_metadata_from_string(self, string, via) -> Dict[str, Any]:
174
174
  """Helper method to get metadata from a string."""
175
175
  try:
176
176
  # XML formats
@@ -214,7 +214,7 @@ class Metadata:
214
214
  except (TypeError, json.JSONDecodeError) as error:
215
215
  return {"error": str(error)}
216
216
 
217
- def read_metadata(self, data: dict, **kwargs) -> dict:
217
+ def read_metadata(self, data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
218
218
  """Read and parse metadata from various formats."""
219
219
  via = (isinstance(data, dict) and data.get("via")) or self.via
220
220
 
@@ -251,8 +251,8 @@ class Metadata:
251
251
  else:
252
252
  raise ValueError("No input format found")
253
253
 
254
- def write(self, to: str = "commonmeta", **kwargs) -> str:
255
- """Convert metadata into different formats."""
254
+ def write(self, to: str = "commonmeta", **kwargs) -> Union[str, bytes]:
255
+ """convert metadata list into different formats"""
256
256
  try:
257
257
  result = self._write_format(to, **kwargs)
258
258
  if result is None or result == "":
@@ -262,20 +262,9 @@ class Metadata:
262
262
  # More specific error message including the original JSONDecodeError details
263
263
  raise ValueError(f"Invalid JSON: {str(e)}")
264
264
 
265
- def _write_format(self, to: str, **kwargs) -> str:
265
+ def _write_format(self, to: str, **kwargs) -> Union[str, bytes]:
266
266
  """Helper method to handle writing to different formats."""
267
- # Split the format handling into multiple methods to reduce cyclomatic complexity
268
- if to in ["commonmeta", "datacite", "inveniordm", "schema_org"]:
269
- return self._write_json_format(to)
270
- elif to in ["bibtex", "csl", "citation", "ris"]:
271
- return self._write_text_format(to, **kwargs)
272
- elif to in ["crossref_xml"]:
273
- return self._write_xml_format(to, **kwargs)
274
- else:
275
- raise ValueError("No output format found")
276
-
277
- def _write_json_format(self, to: str) -> str:
278
- """Handle JSON-based output formats."""
267
+ # JSON-based output formats
279
268
  if to == "commonmeta":
280
269
  result = json.dumps(write_commonmeta(self))
281
270
  elif to == "datacite":
@@ -284,8 +273,22 @@ class Metadata:
284
273
  result = json.dumps(write_inveniordm(self))
285
274
  elif to == "schema_org":
286
275
  result = json.dumps(write_schema_org(self))
276
+ # Text-based output formats
277
+ elif to == "bibtex":
278
+ return write_bibtex(self)
279
+ elif to == "csl":
280
+ return self._write_csl(**kwargs)
281
+ elif to == "citation":
282
+ self.style = kwargs.get("style", "apa")
283
+ self.locale = kwargs.get("locale", "en-US")
284
+ return write_citation(self)
285
+ elif to == "ris":
286
+ return write_ris(self)
287
+ # XML-based output formats
288
+ elif to == "crossref_xml":
289
+ return self._write_crossref_xml(**kwargs)
287
290
  else:
288
- return "{}"
291
+ raise ValueError("No output format found")
289
292
 
290
293
  if isinstance(result, str):
291
294
  # Verify it's valid JSON
@@ -304,26 +307,6 @@ class Metadata:
304
307
  return "{}"
305
308
  return "{}"
306
309
 
307
- def _write_text_format(self, to: str, **kwargs) -> str:
308
- """Handle text-based output formats."""
309
- if to == "bibtex":
310
- return write_bibtex(self)
311
- elif to == "csl":
312
- return self._write_csl(**kwargs)
313
- elif to == "citation":
314
- self.style = kwargs.get("style", "apa")
315
- self.locale = kwargs.get("locale", "en-US")
316
- return write_citation(self)
317
- elif to == "ris":
318
- return write_ris(self)
319
- return ""
320
-
321
- def _write_xml_format(self, to: str, **kwargs) -> str:
322
- """Handle XML-based output formats."""
323
- if to == "crossref_xml":
324
- return self._write_crossref_xml(**kwargs)
325
- return ""
326
-
327
310
  def _write_csl(self, **kwargs) -> str:
328
311
  """Write in CSL format with error checking."""
329
312
  csl_output = write_csl(self)
@@ -366,8 +349,8 @@ class MetadataList:
366
349
  """MetadataList"""
367
350
 
368
351
  def __init__(
369
- self, dct: Optional[Union[str, dict]] = None, **kwargs
370
- ) -> Optional[dict]:
352
+ self, dct: Optional[Union[str, Dict[str, Any]]] = None, **kwargs
353
+ ) -> None:
371
354
  if dct is None or not isinstance(dct, (str, bytes, dict)):
372
355
  raise ValueError("No input found")
373
356
  if isinstance(dct, dict):
@@ -423,12 +406,12 @@ class MetadataList:
423
406
  else:
424
407
  raise ValueError("No input format found")
425
408
 
426
- def read_metadata_list(self, data: list, **kwargs) -> list:
409
+ def read_metadata_list(self, items, **kwargs) -> List[Metadata]:
427
410
  """read_metadata_list"""
428
411
  kwargs["via"] = kwargs.get("via", None) or self.via
429
- return [Metadata(i, **kwargs) for i in data]
412
+ return [Metadata(i, **kwargs) for i in items]
430
413
 
431
- def write(self, to: str = "commonmeta", **kwargs) -> str:
414
+ def write(self, to: str = "commonmeta", **kwargs) -> Union[str, bytes]:
432
415
  """convert metadata list into different formats"""
433
416
  if to == "bibtex":
434
417
  output = write_bibtex_list(self)
@@ -479,19 +462,23 @@ class MetadataList:
479
462
  else:
480
463
  raise ValueError("No valid output format found")
481
464
 
482
- def push(self, to: str = "commonmeta", **kwargs) -> str:
465
+ def push(self, to: str = "commonmeta", **kwargs) -> Union[str, bytes]:
483
466
  """push metadata list to external APIs"""
484
467
 
485
468
  if to == "crossref_xml":
486
469
  response = push_crossref_xml_list(
487
- self, login_id=self.login_id, login_passwd=self.login_passwd, legacy_key=self.legacy_key
470
+ self,
471
+ login_id=self.login_id,
472
+ login_passwd=self.login_passwd,
473
+ legacy_key=self.legacy_key,
488
474
  )
489
475
  return response
490
476
  elif to == "datacite":
491
477
  raise ValueError("Datacite not yet supported for metadata lists")
492
478
  elif to == "inveniordm":
479
+ kwargs = {"legacy_key": self.legacy_key}
493
480
  response = push_inveniordm_list(
494
- self, host=self.host, token=self.token, legacy_key=self.legacy_key
481
+ self, host=self.host, token=self.token, **kwargs
495
482
  )
496
483
  return response
497
484
  else:
@@ -99,22 +99,25 @@ def read_cff(data: Optional[dict], **kwargs) -> Commonmeta:
99
99
  state = "findable" if meta or read_options else "not_found"
100
100
 
101
101
  return {
102
- "id": _id,
103
- "type": _type,
104
- # 'identifiers' => identifiers,
105
- "url": url,
106
- "titles": titles,
107
- "contributors": presence(contributors),
108
- "publisher": publisher,
109
- "references": presence(references),
110
- "date": date,
111
- "descriptions": presence(descriptions),
112
- "license": license_,
113
- "version": meta.get("version", None),
114
- "subjects": presence(subjects),
115
- "provider": "DataCite" if _id else "GitHub",
116
- "state": state,
117
- } | read_options
102
+ **{
103
+ "id": _id,
104
+ "type": _type,
105
+ # 'identifiers' => identifiers,
106
+ "url": url,
107
+ "titles": titles,
108
+ "contributors": presence(contributors),
109
+ "publisher": publisher,
110
+ "references": presence(references),
111
+ "date": date,
112
+ "descriptions": presence(descriptions),
113
+ "license": license_,
114
+ "version": meta.get("version", None),
115
+ "subjects": presence(subjects),
116
+ "provider": "DataCite" if _id else "GitHub",
117
+ "state": state,
118
+ },
119
+ **read_options,
120
+ }
118
121
 
119
122
 
120
123
  def cff_contributors(contributors):
@@ -97,18 +97,21 @@ def read_codemeta(data: Optional[dict], **kwargs) -> Commonmeta:
97
97
  state = "findable" if meta or read_options else "not_found"
98
98
 
99
99
  return {
100
- "id": _id,
101
- "type": _type,
102
- "url": normalize_id(meta.get("codeRepository", None)),
103
- "identifiers": None,
104
- "titles": titles,
105
- "contributors": presence(contributors),
106
- "publisher": publisher,
107
- "date": compact(date),
108
- "descriptions": descriptions,
109
- "license": license_,
110
- "version": meta.get("version", None),
111
- "subjects": presence(subjects),
112
- "provider": provider,
113
- "state": state,
114
- } | read_options
100
+ **{
101
+ "id": _id,
102
+ "type": _type,
103
+ "url": normalize_id(meta.get("codeRepository", None)),
104
+ "identifiers": None,
105
+ "titles": titles,
106
+ "contributors": presence(contributors),
107
+ "publisher": publisher,
108
+ "date": compact(date),
109
+ "descriptions": descriptions,
110
+ "license": license_,
111
+ "version": meta.get("version", None),
112
+ "subjects": presence(subjects),
113
+ "provider": provider,
114
+ "state": state,
115
+ },
116
+ **read_options,
117
+ }
@@ -1,6 +1,6 @@
1
1
  """crossref reader for commonmeta-py"""
2
2
 
3
- from typing import Optional
3
+ from typing import List, Optional
4
4
  from xml.parsers.expat import ExpatError
5
5
 
6
6
  import requests
@@ -33,7 +33,7 @@ from ..utils import (
33
33
  )
34
34
 
35
35
 
36
- def get_crossref_list(query: dict, **kwargs) -> list[dict]:
36
+ def get_crossref_list(query: dict, **kwargs) -> List[dict]:
37
37
  """get_crossref list from Crossref API."""
38
38
  url = crossref_api_query_url(query, **kwargs)
39
39
  response = requests.get(url, timeout=30, **kwargs)
@@ -51,7 +51,7 @@ def get_crossref(pid: str, **kwargs) -> dict:
51
51
  response = requests.get(url, timeout=10, **kwargs)
52
52
  if response.status_code != 200:
53
53
  return {"state": "not_found"}
54
- return response.json().get("message", {}) | {"via": "crossref"}
54
+ return {**response.json().get("message", {}), "via": "crossref"}
55
55
 
56
56
 
57
57
  def read_crossref(data: Optional[dict], **kwargs) -> Commonmeta:
@@ -138,31 +138,34 @@ def read_crossref(data: Optional[dict], **kwargs) -> Commonmeta:
138
138
  )
139
139
 
140
140
  return {
141
- # required properties
142
- "id": _id,
143
- "type": _type,
144
- # recommended and optional properties
145
- "additionalType": additional_type,
146
- "archiveLocations": presence(archive_locations),
147
- "container": presence(container),
148
- "contributors": presence(contributors),
149
- "date": presence(date),
150
- "descriptions": presence(descriptions),
151
- "files": presence(files),
152
- "fundingReferences": presence(funding_references),
153
- "geoLocations": None,
154
- "identifiers": identifiers,
155
- "language": meta.get("language", None),
156
- "license": license_,
157
- "provider": "Crossref",
158
- "publisher": presence(publisher),
159
- "references": presence(references),
160
- "relations": presence(relations),
161
- "subjects": presence(subjects),
162
- "titles": presence(titles),
163
- "url": url,
164
- "version": meta.get("version", None),
165
- } | read_options
141
+ **{
142
+ # required properties
143
+ "id": _id,
144
+ "type": _type,
145
+ # recommended and optional properties
146
+ "additionalType": additional_type,
147
+ "archiveLocations": presence(archive_locations),
148
+ "container": presence(container),
149
+ "contributors": presence(contributors),
150
+ "date": presence(date),
151
+ "descriptions": presence(descriptions),
152
+ "files": presence(files),
153
+ "fundingReferences": presence(funding_references),
154
+ "geoLocations": None,
155
+ "identifiers": identifiers,
156
+ "language": meta.get("language", None),
157
+ "license": license_,
158
+ "provider": "Crossref",
159
+ "publisher": presence(publisher),
160
+ "references": presence(references),
161
+ "relations": presence(relations),
162
+ "subjects": presence(subjects),
163
+ "titles": presence(titles),
164
+ "url": url,
165
+ "version": meta.get("version", None),
166
+ },
167
+ **read_options,
168
+ }
166
169
 
167
170
 
168
171
  def get_titles(meta):
@@ -40,7 +40,7 @@ def get_datacite(pid: str, **kwargs) -> dict:
40
40
  response = requests.get(url, timeout=10, **kwargs)
41
41
  if response.status_code != 200:
42
42
  return {"state": "not_found"}
43
- return py_.get(response.json(), "data.attributes", {}) | {"via": "datacite"}
43
+ return {**py_.get(response.json(), "data.attributes", {}), "via": "datacite"}
44
44
  except ReadTimeout:
45
45
  return {"state": "timeout"}
46
46
 
@@ -114,30 +114,33 @@ def read_datacite(data: dict, **kwargs) -> Commonmeta:
114
114
  subjects = py_.uniq([format_subject(i) for i in wrap(meta.get("subjects", None))])
115
115
 
116
116
  return {
117
- # required properties
118
- "id": _id,
119
- "type": _type,
120
- # recommended and optional properties
121
- "additionalType": additional_type,
122
- "container": presence(container),
123
- "contributors": presence(contributors),
124
- "date": compact(date),
125
- "descriptions": presence(descriptions),
126
- "files": presence(files),
127
- "fundingReferences": presence(meta.get("fundingReferences", None)),
128
- "geoLocations": presence(geo_locations),
129
- "identifiers": presence(identifiers),
130
- "language": meta.get("language", None),
131
- "license": presence(license_),
132
- "provider": "DataCite",
133
- "publisher": publisher,
134
- "references": presence(references),
135
- "relations": presence(relations),
136
- "subjects": presence(subjects),
137
- "titles": presence(titles),
138
- "url": normalize_url(meta.get("url", None)),
139
- "version": meta.get("version", None),
140
- } | read_options
117
+ **{
118
+ # required properties
119
+ "id": _id,
120
+ "type": _type,
121
+ # recommended and optional properties
122
+ "additionalType": additional_type,
123
+ "container": presence(container),
124
+ "contributors": presence(contributors),
125
+ "date": compact(date),
126
+ "descriptions": presence(descriptions),
127
+ "files": presence(files),
128
+ "fundingReferences": presence(meta.get("fundingReferences", None)),
129
+ "geoLocations": presence(geo_locations),
130
+ "identifiers": presence(identifiers),
131
+ "language": meta.get("language", None),
132
+ "license": presence(license_),
133
+ "provider": "DataCite",
134
+ "publisher": publisher,
135
+ "references": presence(references),
136
+ "relations": presence(relations),
137
+ "subjects": presence(subjects),
138
+ "titles": presence(titles),
139
+ "url": normalize_url(meta.get("url", None)),
140
+ "version": meta.get("version", None),
141
+ },
142
+ **read_options,
143
+ }
141
144
 
142
145
 
143
146
  def get_identifiers(identifiers: list) -> list:
@@ -22,7 +22,7 @@ def get_datacite_xml(pid: str, **kwargs) -> dict:
22
22
  response = requests.get(url, timeout=10, **kwargs)
23
23
  if response.status_code != 200:
24
24
  return {"state": "not_found"}
25
- return py_.get(response.json(), "data.attributes", {}) | {"via": "datacite_xml"}
25
+ return {**py_.get(response.json(), "data.attributes", {}), "via": "datacite_xml"}
26
26
 
27
27
 
28
28
  def read_datacite_xml(data: dict, **kwargs) -> Commonmeta:
@@ -203,38 +203,41 @@ def read_datacite_xml(data: dict, **kwargs) -> Commonmeta:
203
203
  state = "findable" if _id or read_options else "not_found"
204
204
 
205
205
  return {
206
- # required properties
207
- "id": _id,
208
- "type": _type,
209
- "doi": doi_from_url(_id),
210
- "url": normalize_url(meta.get("url", None)),
211
- "contributors": presence(contributors),
212
- "titles": compact(titles),
213
- "publisher": publisher,
214
- "date": date,
215
- # recommended and optional properties
216
- "additionalType": presence(additional_type),
217
- "subjects": presence(subjects),
218
- "language": meta.get("language", None),
219
- "identifiers": identifiers,
220
- "version": meta.get("version", None),
221
- "license": presence(license_),
222
- "descriptions": presence(descriptions),
223
- "geoLocations": presence(geo_locations),
224
- "fundingReferences": presence(funding_references),
225
- "references": presence(references),
226
- "relations": presence(relations),
227
- # other properties
228
- "date_created": strip_milliseconds(meta.get("created", None)),
229
- "date_registered": strip_milliseconds(meta.get("registered", None)),
230
- "date_published": strip_milliseconds(meta.get("published", None)),
231
- "date_updated": strip_milliseconds(meta.get("updated", None)),
232
- "files": presence(files),
233
- "container": presence(meta.get("container", None)),
234
- "provider": "DataCite",
235
- "state": state,
236
- "schema_version": meta.get("xmlns", None),
237
- } | read_options
206
+ **{
207
+ # required properties
208
+ "id": _id,
209
+ "type": _type,
210
+ "doi": doi_from_url(_id),
211
+ "url": normalize_url(meta.get("url", None)),
212
+ "contributors": presence(contributors),
213
+ "titles": compact(titles),
214
+ "publisher": publisher,
215
+ "date": date,
216
+ # recommended and optional properties
217
+ "additionalType": presence(additional_type),
218
+ "subjects": presence(subjects),
219
+ "language": meta.get("language", None),
220
+ "identifiers": identifiers,
221
+ "version": meta.get("version", None),
222
+ "license": presence(license_),
223
+ "descriptions": presence(descriptions),
224
+ "geoLocations": presence(geo_locations),
225
+ "fundingReferences": presence(funding_references),
226
+ "references": presence(references),
227
+ "relations": presence(relations),
228
+ # other properties
229
+ "date_created": strip_milliseconds(meta.get("created", None)),
230
+ "date_registered": strip_milliseconds(meta.get("registered", None)),
231
+ "date_published": strip_milliseconds(meta.get("published", None)),
232
+ "date_updated": strip_milliseconds(meta.get("updated", None)),
233
+ "files": presence(files),
234
+ "container": presence(meta.get("container", None)),
235
+ "provider": "DataCite",
236
+ "state": state,
237
+ "schema_version": meta.get("xmlns", None),
238
+ },
239
+ **read_options,
240
+ }
238
241
 
239
242
 
240
243
  def get_xml_identifiers(identifiers: list) -> list:
@@ -122,31 +122,34 @@ def read_inveniordm(data: dict, **kwargs) -> Commonmeta:
122
122
  files = [get_file(i) for i in wrap(meta.get("files"))]
123
123
 
124
124
  return {
125
- # required properties
126
- "id": _id,
127
- "type": _type,
128
- "doi": doi_from_url(_id),
129
- "url": url,
130
- "contributors": presence(contributors),
131
- "titles": titles,
132
- "publisher": publisher,
133
- "date": compact(date),
134
- # recommended and optional properties
135
- # "additional_type": additional_type,
136
- "subjects": presence(subjects),
137
- "language": get_language(language),
138
- "version": py_.get(meta, "metadata.version"),
139
- "license": presence(license_),
140
- "descriptions": descriptions,
141
- "geoLocations": None,
142
- "fundingReferences": presence(funding_references),
143
- "references": presence(references),
144
- "relations": presence(relations),
145
- # other properties
146
- "files": files,
147
- "container": container,
148
- "provider": "DataCite",
149
- } | read_options
125
+ **{
126
+ # required properties
127
+ "id": _id,
128
+ "type": _type,
129
+ "doi": doi_from_url(_id),
130
+ "url": url,
131
+ "contributors": presence(contributors),
132
+ "titles": titles,
133
+ "publisher": publisher,
134
+ "date": compact(date),
135
+ # recommended and optional properties
136
+ # "additional_type": additional_type,
137
+ "subjects": presence(subjects),
138
+ "language": get_language(language),
139
+ "version": py_.get(meta, "metadata.version"),
140
+ "license": presence(license_),
141
+ "descriptions": descriptions,
142
+ "geoLocations": None,
143
+ "fundingReferences": presence(funding_references),
144
+ "references": presence(references),
145
+ "relations": presence(relations),
146
+ # other properties
147
+ "files": files,
148
+ "container": container,
149
+ "provider": "DataCite",
150
+ },
151
+ **read_options,
152
+ }
150
153
 
151
154
 
152
155
  def get_references(references: list) -> list:
@@ -2,15 +2,15 @@
2
2
 
3
3
  from pydash import py_
4
4
 
5
- from ..utils import normalize_url, normalize_doi, from_curie, from_kbase
6
- from ..base_utils import compact, wrap, presence, sanitize
7
5
  from ..author_utils import get_authors
8
- from ..date_utils import normalize_date_dict
9
- from ..doi_utils import doi_from_url, validate_doi
6
+ from ..base_utils import compact, presence, sanitize, wrap
10
7
  from ..constants import (
11
8
  COMMONMETA_RELATION_TYPES,
12
9
  Commonmeta,
13
10
  )
11
+ from ..date_utils import normalize_date_dict
12
+ from ..doi_utils import doi_from_url, validate_doi
13
+ from ..utils import from_curie, from_kbase, normalize_doi, normalize_url
14
14
 
15
15
 
16
16
  def read_kbase(data: dict, **kwargs) -> Commonmeta:
@@ -70,32 +70,35 @@ def read_kbase(data: dict, **kwargs) -> Commonmeta:
70
70
  state = "findable" if meta or read_options else "not_found"
71
71
 
72
72
  return {
73
- # required properties
74
- "id": _id,
75
- "type": _type,
76
- "doi": doi_from_url(_id),
77
- "url": normalize_url(meta.get("url", None)),
78
- "contributors": presence(contributors),
79
- "titles": titles,
80
- "publisher": publisher,
81
- "date": compact(date),
82
- # recommended and optional properties
83
- "additional_type": None,
84
- "subjects": None,
85
- "language": language,
86
- "identifiers": None,
87
- "version": py_.get(meta, "metadata.version"),
88
- "license": presence(license_),
89
- "descriptions": descriptions,
90
- "geo_locations": None,
91
- "fundingReferences": presence(funding_references),
92
- "references": presence(references),
93
- "relations": presence(relations),
94
- # other properties
95
- "files": presence(files),
96
- "container": container,
97
- "provider": "DataCite",
98
- } | read_options
73
+ **{
74
+ # required properties
75
+ "id": _id,
76
+ "type": _type,
77
+ "doi": doi_from_url(_id),
78
+ "url": normalize_url(meta.get("url", None)),
79
+ "contributors": presence(contributors),
80
+ "titles": titles,
81
+ "publisher": publisher,
82
+ "date": compact(date),
83
+ # recommended and optional properties
84
+ "additional_type": None,
85
+ "subjects": None,
86
+ "language": language,
87
+ "identifiers": None,
88
+ "version": py_.get(meta, "metadata.version"),
89
+ "license": presence(license_),
90
+ "descriptions": descriptions,
91
+ "geo_locations": None,
92
+ "fundingReferences": presence(funding_references),
93
+ "references": presence(references),
94
+ "relations": presence(relations),
95
+ # other properties
96
+ "files": presence(files),
97
+ "container": container,
98
+ "provider": "DataCite",
99
+ },
100
+ **read_options,
101
+ }
99
102
 
100
103
 
101
104
  def format_title(title: dict) -> dict:
@@ -67,21 +67,24 @@ def read_ris(data: Optional[str], **kwargs) -> Commonmeta:
67
67
  state = "findable" if meta.get("DO", None) or read_options else "not_found"
68
68
 
69
69
  return {
70
- "id": _id,
71
- "type": _type,
72
- "doi": doi_from_url(_id),
73
- "url": normalize_url(meta.get("UR", None)),
74
- "titles": [{"title": meta.get("T1", None)}],
75
- "descriptions": descriptions,
76
- "contributors": presence(contributors),
77
- "publisher": presence(publisher),
78
- "container": container,
79
- # 'related_identifiers': related_identifiers,
80
- "date": date,
81
- "subjects": subjects,
82
- "language": meta.get("LA", None),
83
- "state": state,
84
- } | read_options
70
+ **{
71
+ "id": _id,
72
+ "type": _type,
73
+ "doi": doi_from_url(_id),
74
+ "url": normalize_url(meta.get("UR", None)),
75
+ "titles": [{"title": meta.get("T1", None)}],
76
+ "descriptions": descriptions,
77
+ "contributors": presence(contributors),
78
+ "publisher": presence(publisher),
79
+ "container": container,
80
+ # 'related_identifiers': related_identifiers,
81
+ "date": date,
82
+ "subjects": subjects,
83
+ "language": meta.get("LA", None),
84
+ "state": state,
85
+ },
86
+ **read_options,
87
+ }
85
88
 
86
89
 
87
90
  def ris_meta(data):
@@ -1,13 +1,16 @@
1
1
  """Schema utils for commonmeta-py"""
2
2
 
3
3
  from os import path
4
+ from typing import Any, Dict, Optional, Union
4
5
 
5
6
  import orjson as json
6
7
  import xmlschema
7
8
  from jsonschema import Draft202012Validator, ValidationError
8
9
 
9
10
 
10
- def json_schema_errors(instance, schema: str = "commonmeta"):
11
+ def json_schema_errors(
12
+ instance: Dict[str, Any], schema: str = "commonmeta"
13
+ ) -> Optional[str]:
11
14
  """validate against JSON schema"""
12
15
  schema_map = {
13
16
  "commonmeta": "commonmeta_v0.16",
@@ -30,7 +33,9 @@ def json_schema_errors(instance, schema: str = "commonmeta"):
30
33
  return error.message
31
34
 
32
35
 
33
- def xml_schema_errors(instance, schema: str = "crossref_xml"):
36
+ def xml_schema_errors(
37
+ instance: Union[str, bytes], schema: str = "crossref_xml"
38
+ ) -> Optional[Union[bool, Exception]]:
34
39
  """validate against XML schema"""
35
40
  schema_map = {
36
41
  "crossref_xml": "crossref5.4.0",
@@ -40,8 +45,8 @@ def xml_schema_errors(instance, schema: str = "crossref_xml"):
40
45
  raise ValueError("No schema found")
41
46
  base_dir = path.join(path.dirname(__file__), "resources", "crossref")
42
47
  schema_path = path.join(base_dir, "crossref5.4.0.xsd")
43
- schema = xmlschema.XMLSchema(schema_path)
44
- return schema.validate(instance)
48
+ schema_obj = xmlschema.XMLSchema(schema_path)
49
+ return schema_obj.validate(instance)
45
50
  except xmlschema.validators.exceptions.XMLSchemaValidationError as error:
46
51
  print(error)
47
52
  return error
@@ -385,7 +385,9 @@ def write_crossref_xml_list(metalist) -> Optional[str]:
385
385
  return unparse_xml_list(crossref_xml_list, dialect="crossref", head=head)
386
386
 
387
387
 
388
- def push_crossref_xml_list(metalist, login_id: str, login_passwd: str, legacy_key:str=None) -> bytes:
388
+ def push_crossref_xml_list(
389
+ metalist, login_id: str, login_passwd: str, legacy_key: str = None
390
+ ) -> bytes:
389
391
  """Push crossref_xml list to Crossref API, returns the API response."""
390
392
 
391
393
  input = write_crossref_xml_list(metalist)
@@ -426,28 +428,21 @@ def push_crossref_xml_list(metalist, login_id: str, login_passwd: str, legacy_ke
426
428
  items = []
427
429
  for item in metalist.items:
428
430
  record = {
429
- "doi": item.id,
430
- "updated": datetime.now().isoformat("T", "seconds"),
431
- "status": "submitted",
432
- }
431
+ "doi": item.id,
432
+ "updated": datetime.now().isoformat("T", "seconds"),
433
+ "status": "submitted",
434
+ }
433
435
 
434
436
  # update rogue-scholar legacy record if legacy_key is provided
435
437
  if is_rogue_scholar_doi(item.id, ra="crossref") and legacy_key is not None:
436
- record["uuid"] = py_.get(item, "identifiers.0.identifier")
437
- record = update_legacy_record(record, legacy_key=legacy_key, field="doi")
438
+ record["uuid"] = py_.get(item, "identifiers.0.identifier")
439
+ record = update_legacy_record(record, legacy_key=legacy_key, field="doi")
438
440
  items.append(record)
439
441
 
440
442
  # Return JSON response
441
443
  return json.dumps(items, option=json.OPT_INDENT_2)
442
444
 
443
445
 
444
-
445
-
446
-
447
-
448
-
449
-
450
-
451
446
  def get_attributes(obj, **kwargs) -> dict:
452
447
  """Get root attributes"""
453
448
  return compact(
@@ -4,7 +4,6 @@ import logging
4
4
  import re
5
5
  from time import time
6
6
  from typing import Dict, Optional
7
- from urllib.parse import urlparse
8
7
 
9
8
  import orjson as json
10
9
  import pydash as py_
@@ -32,6 +31,7 @@ from ..utils import (
32
31
 
33
32
  logger = logging.getLogger(__name__)
34
33
 
34
+
35
35
  def write_inveniordm(metadata):
36
36
  """Write inveniordm"""
37
37
  if metadata is None or metadata.write_errors is not None:
@@ -387,123 +387,161 @@ def write_inveniordm_list(metalist):
387
387
  return [write_inveniordm(item) for item in metalist.items]
388
388
 
389
389
 
390
- def push_inveniordm(
391
- metadata: Commonmeta,
392
- host: str,
393
- token: str,
394
- legacy_key: str
395
- ) -> Dict:
390
+ def push_inveniordm(metadata: Commonmeta, host: str, token: str, **kwargs) -> Dict:
396
391
  """Push record to InvenioRDM"""
397
392
 
398
- record = {}
399
- output = write_inveniordm(metadata)
400
-
401
393
  try:
402
- # Remove IsPartOf relation with InvenioRDM community identifier after storing it
403
- # community_index = None
404
- # if hasattr(metadata, "relations") and metadata.relations:
405
- # for i, relation in enumerate(metadata.relations):
406
- # if relation.get("type") == "IsPartOf" and relation.get(
407
- # "id", ""
408
- # ).startswith("https://rogue-scholar.org/api/communities/"):
409
- # slug = relation.get("id").split("/")[5]
410
- # community_id, _ = search_by_slug(slug, "blog", host, token)
411
- # if community_id:
412
- # record["community"] = slug
413
- # record["community_id"] = community_id
414
- # community_index = i
415
-
416
- # # Remove the relation if we found and processed it
417
- # if community_index is not None and hasattr(metadata, "relations"):
418
- # metadata.relations.pop(community_index)
419
-
420
- # Remove InvenioRDM rid after storing it
421
- # rid_index = None
422
- # if hasattr(metadata, "identifiers") and metadata.identifiers:
423
- # for i, identifier in enumerate(metadata.identifiers):
424
- # if identifier.get("identifierType") == "RID" and identifier.get("identifier"):
425
- # record["id"] = identifier.get("identifier")
426
- # rid_index = i
427
- # elif identifier.get("identifierType") == "UUID" and identifier.get("identifier"):
428
- # record["uuid"] = identifier.get("identifier")
429
-
430
- # # Remove the identifier if we found and processed it
431
- # if rid_index is not None and hasattr(metadata, "identifiers"):
432
- # metadata.identifiers.pop(rid_index)
433
-
434
- # Check if record already exists in InvenioRDM
435
- record["id"] = search_by_doi(doi_from_url(metadata.id), host, token)
436
-
437
- if record["id"] is not None:
438
- # Create draft record from published record
439
- record = edit_published_record(record, host, token)
440
-
441
- # Update draft record
442
- record = update_draft_record(record, host, token, output)
443
- else:
444
- # Create draft record
445
- record = create_draft_record(record, host, token, output)
446
-
447
- # Publish draft record
448
- record = publish_draft_record(record, host, token)
449
-
450
- # Add record to blog community if blog community is specified and exists
451
- if record.get("community_id", None) is not None:
452
- record = add_record_to_community(
453
- record, host, token, record["community_id"]
454
- )
455
-
456
- # Add record to subject area community if subject area community is specified and exists
457
- # Subject area communities should exist for all subjects in the FOSMappings
458
-
459
- if hasattr(metadata, "subjects"):
460
- for subject in metadata.subjects:
461
- subject_name = subject.get("subject", "")
462
- slug = string_to_slug(subject_name)
463
- if slug in COMMUNITY_TRANSLATIONS:
464
- slug = COMMUNITY_TRANSLATIONS[slug]
465
-
466
- community_id = search_by_slug(slug, "topic", host, token)
467
- if community_id:
468
- record = add_record_to_community(record, host, token, community_id)
469
-
470
- # Add record to communities defined as IsPartOf relation in inveniordm metadata's RelatedIdentifiers
471
- related_identifiers = py_.get(input, "metadata.related_identifiers")
394
+ doi = normalize_doi(metadata.id)
395
+ if doi is None:
396
+ raise ValueError("no doi provided")
472
397
 
473
- for identifier in wrap(related_identifiers):
474
- if py_.get(identifier, "relation_type.id") == "ispartof":
475
- parsed_url = urlparse(identifier.get("identifier", ""))
476
- path_parts = parsed_url.path.split("/")
398
+ record = {
399
+ "doi": doi,
400
+ }
477
401
 
478
- if (
479
- parsed_url.netloc == urlparse(host).netloc
480
- and len(path_parts) == 3
481
- and path_parts[1] == "communities"
402
+ # extract optional information needed but not upserted to the InvenioRDM API:
403
+ # rid is the InvenioRDM record id,
404
+ # uuid is the Rogue Scholar uuid,
405
+ # community_id is the id of the primary community of the record
406
+ if hasattr(metadata, "identifiers") and metadata.identifiers:
407
+ rid_index = None
408
+ uuid_index = None
409
+ for i, identifier in enumerate(metadata.identifiers):
410
+ if identifier.get("identifierType") == "RID" and identifier.get(
411
+ "identifier"
412
+ ):
413
+ record["id"] = identifier.get("identifier")
414
+ rid_index = i
415
+ elif identifier.get("identifierType") == "UUID" and identifier.get(
416
+ "identifier"
482
417
  ):
483
- record = add_record_to_community(record, host, token, path_parts[2])
418
+ record["uuid"] = identifier.get("identifier")
419
+ uuid_index = i
420
+ if rid_index is not None:
421
+ metadata.identifiers.pop(rid_index)
422
+ if uuid_index is not None:
423
+ metadata.identifiers.pop(rid_index)
424
+
425
+ if hasattr(metadata, "relations") and metadata.relations:
426
+ community_index = None
427
+ for i, relation in enumerate(metadata.relations):
428
+ if relation.get("type") == "IsPartOf" and relation.get(
429
+ "id", ""
430
+ ).startswith(f"https://{host}/api/communities/"):
431
+ slug = relation.get("id").split("/")[5]
432
+ community_id = search_by_slug(slug, "blog", host, token)
433
+ if community_id:
434
+ record["community"] = slug
435
+ record["community_id"] = community_id
436
+ community_index = i
437
+
438
+ if community_index is not None:
439
+ metadata.relations.pop(community_index)
440
+
441
+ # upsert record via the InvenioRDM API
442
+ record = upsert_record(metadata, host, token, record)
443
+
444
+ # optionally add record to InvenioRDM communities
445
+ record = add_record_to_communities(metadata, host, token, record)
446
+
447
+ # optionally update external services
448
+ record = update_external_services(metadata, host, token, record, **kwargs)
484
449
 
485
- # optionally update rogue-scholar legacy record
486
- if host == "rogue-scholar.org" and legacy_key is not None:
487
- record = update_legacy_record(record, legacy_key=legacy_key, field="rid")
488
450
  except Exception as e:
489
- logger.error(f"Unexpected error in push_inveniordm: {str(e)}", exc_info=True, extra={
490
- "host": host,
491
- "record_id": record.get("id")
492
- })
451
+ logger.error(
452
+ f"Unexpected error in push_inveniordm: {str(e)}",
453
+ exc_info=True,
454
+ extra={"host": host, "record_id": record.get("id")},
455
+ )
493
456
  record["status"] = "error"
494
457
 
495
458
  return record
496
459
 
497
460
 
498
- def push_inveniordm_list(metalist, host: str, token: str, legacy_key: str) -> list:
461
+ def push_inveniordm_list(metalist, host: str, token: str, **kwargs) -> list:
499
462
  """Push inveniordm list to InvenioRDM, returns list of push results."""
500
463
 
501
464
  if metalist is None:
502
465
  return None
503
- items = [push_inveniordm(item, host, token, legacy_key) for item in metalist.items]
466
+ items = [push_inveniordm(item, host, token, **kwargs) for item in metalist.items]
504
467
  return json.dumps(items, option=json.OPT_INDENT_2)
505
468
 
506
469
 
470
+ def upsert_record(metadata: Commonmeta, host: str, token: str, record: dict) -> dict:
471
+ """Upsert InvenioRDM record, based on DOI"""
472
+
473
+ output = write_inveniordm(metadata)
474
+
475
+ # Check if record already exists in InvenioRDM
476
+ record["id"] = search_by_doi(doi_from_url(record.get("doi")), host, token)
477
+
478
+ if record["id"] is not None:
479
+ # Create draft record from published record
480
+ record = edit_published_record(record, host, token)
481
+
482
+ # Update draft record
483
+ record = update_draft_record(record, host, token, output)
484
+ else:
485
+ # Create draft record
486
+ record = create_draft_record(record, host, token, output)
487
+
488
+ # Publish draft record
489
+ record = publish_draft_record(record, host, token)
490
+
491
+ return record
492
+
493
+
494
+ def add_record_to_communities(
495
+ metadata: Commonmeta, host: str, token: str, record: dict
496
+ ) -> dict:
497
+ """Add record to one or more InvenioRDM communities"""
498
+
499
+ # Add record to primary community if primary community is specified
500
+ if record.get("community_id", None) is not None:
501
+ record = add_record_to_community(record, host, token, record["community_id"])
502
+
503
+ # Add record to subject area community if subject area community is specified
504
+ # Subject area communities should exist for all OECD subject areas
505
+
506
+ if hasattr(metadata, "subjects"):
507
+ for subject in metadata.subjects:
508
+ subject_name = subject.get("subject", "")
509
+ slug = string_to_slug(subject_name)
510
+ if slug in COMMUNITY_TRANSLATIONS:
511
+ slug = COMMUNITY_TRANSLATIONS[slug]
512
+
513
+ community_id = search_by_slug(slug, "topic", host, token)
514
+ if community_id:
515
+ record = add_record_to_community(record, host, token, community_id)
516
+
517
+ # Add record to communities defined as IsPartOf relation in InvenioRDM RelatedIdentifiers
518
+ if hasattr(metadata, "related_identifiers") and metadata.related_identifiers:
519
+ for identifier in metadata.related_identifiers:
520
+ if py_.get(identifier, "relation_type.id") == "ispartof" and identifier.get(
521
+ "identifier", ""
522
+ ).startswith(f"https://{host}/api/communities/"):
523
+ slug = identifier.get("identifier").split("/")[5]
524
+ community_id = search_by_slug(slug, "topic", host, token)
525
+ if community_id:
526
+ record = add_record_to_community(record, host, token, community_id)
527
+
528
+ return record
529
+
530
+
531
+ def update_external_services(
532
+ metadata: Commonmeta, host: str, token: str, record: dict, **kwargs
533
+ ) -> dict:
534
+ """Update external services with changes in InvenioRDM"""
535
+
536
+ # optionally update rogue-scholar legacy record
537
+ if host == "rogue-scholar.org" and kwargs.get("legacy_key", None) is not None:
538
+ record = update_legacy_record(
539
+ record, legacy_key=kwargs.get("legacy_key"), field="rid"
540
+ )
541
+
542
+ return record
543
+
544
+
507
545
  def search_by_doi(doi, host, token) -> Optional[str]:
508
546
  """Search for a record by DOI in InvenioRDM"""
509
547
  headers = {
@@ -539,7 +577,9 @@ def create_draft_record(record, host, token, output):
539
577
  record["status"] = "failed_rate_limited"
540
578
  return record
541
579
  if response.status_code != 201:
542
- logger.error(f"Failed to create draft record: {response.status_code} - {response.json()}")
580
+ logger.error(
581
+ f"Failed to create draft record: {response.status_code} - {response.json()}"
582
+ )
543
583
  record["status"] = "failed_create_draft"
544
584
  return record
545
585
  data = response.json()
@@ -570,7 +610,9 @@ def edit_published_record(record, host, token):
570
610
  record["status"] = "edited"
571
611
  return record
572
612
  except requests.exceptions.RequestException as e:
573
- logger.error(f"Error creating draft from published record: {str(e)}", exc_info=True)
613
+ logger.error(
614
+ f"Error creating draft from published record: {str(e)}", exc_info=True
615
+ )
574
616
  record["status"] = "error_edit_published_record"
575
617
  return record
576
618
 
@@ -616,12 +658,14 @@ def publish_draft_record(record, host, token):
616
658
  record["status"] = "failed_rate_limited"
617
659
  return record
618
660
  if response.status_code != 202:
619
- logger.error(f"Failed to publish draft record: {response.status_code} - {response.json()}")
661
+ logger.error(
662
+ f"Failed to publish draft record: {response.status_code} - {response.json()}"
663
+ )
620
664
  record["status"] = "error_publish_draft_record"
621
665
  return record
622
666
  data = response.json()
623
667
  record["uuid"] = py_.get(data, "metadata.identifiers.0.identifier")
624
- record["doi"] = doi_as_url(py_.get(data, "pids.doi.identifier")),
668
+ record["doi"] = (doi_as_url(py_.get(data, "pids.doi.identifier")),)
625
669
  record["created"] = data.get("created", None)
626
670
  record["updated"] = data.get("updated", None)
627
671
  record["status"] = "published"
@@ -651,7 +695,7 @@ def add_record_to_community(record, host, token, community_id):
651
695
  return record
652
696
 
653
697
 
654
- def update_legacy_record(record, legacy_key: str, field:str=None) -> dict:
698
+ def update_legacy_record(record, legacy_key: str, field: str = None) -> dict:
655
699
  """Update corresponding record in Rogue Scholar legacy database."""
656
700
 
657
701
  legacy_host = "bosczcmeodcrajtcaddf.supabase.co"
@@ -677,8 +721,8 @@ def update_legacy_record(record, legacy_key: str, field:str=None) -> dict:
677
721
  "archived": "true",
678
722
  }
679
723
  else:
680
- print(f"nothing to update for id {record.get("uuid")}")
681
- return record # nothing to update
724
+ print(f"nothing to update for id {record.get('uuid')}")
725
+ return record # nothing to update
682
726
 
683
727
  request_url = f"https://{legacy_host}/rest/v1/posts?id=eq.{record['uuid']}"
684
728
  headers = {
@@ -700,7 +744,7 @@ def update_legacy_record(record, legacy_key: str, field:str=None) -> dict:
700
744
  return record
701
745
 
702
746
 
703
- def search_by_slug(slug, type_value, host, token) -> Optional[str]:
747
+ def search_by_slug(slug: str, type_value: str, host: str, token: str) -> Optional[str]:
704
748
  """Search for a community by slug in InvenioRDM"""
705
749
  headers = {
706
750
  "Authorization": f"Bearer {token}",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commonmeta-py
3
- Version: 0.123
3
+ Version: 0.125
4
4
  Summary: Library for conversions to/from the Commonmeta scholarly metadata format
5
5
  Project-URL: Homepage, https://python.commonmeta.org
6
6
  Project-URL: Repository, https://github.com/front-matter/commonmeta-py
@@ -1,5 +1,5 @@
1
- commonmeta/__init__.py,sha256=X_mzqU-r9CtyULs66FmHskXoSh9s5lyZVR2mmzTOoV0,2098
2
- commonmeta/api_utils.py,sha256=y5KLfIOWOjde7LXZ36u-eneQJ-Q53yXUZg3hWpCBS2E,2685
1
+ commonmeta/__init__.py,sha256=kDEzO2vW7BmMnXyZBc8T6XAN6gxwn0MRTRDOeTYdME8,2098
2
+ commonmeta/api_utils.py,sha256=P8LMHHYiF4OTi97_5k4KstcBreooMkOAKZ4ebxsAv4o,2691
3
3
  commonmeta/author_utils.py,sha256=3lYW5s1rOUWNTKs1FP6XLfEUY3yCLOe_3L_VdJTDMp0,8585
4
4
  commonmeta/base_utils.py,sha256=-MGy9q2uTiJEkPWQUYOJMdq-3tRpNnvBwlLjvllQ5g8,11164
5
5
  commonmeta/cli.py,sha256=pdBpBosLNq3RS9buO-Voqawc9Ay1eSt-xP5O97iOft4,8480
@@ -7,25 +7,25 @@ commonmeta/constants.py,sha256=wSTEUiHeRdXLwjXEQD9AU2hxFyEKi5OTX2iHOKO6nF0,19844
7
7
  commonmeta/date_utils.py,sha256=H2cCobX0JREIUOT_cCigGd3MG7prGiQpXk1m4ZNrFwU,6318
8
8
  commonmeta/doi_utils.py,sha256=cOogLatKg6qea2jgMd3yLALSTfaTNUgr-IkBXIK4xZw,11498
9
9
  commonmeta/file_utils.py,sha256=eFYDWyR8Gr722nvFmp542hCm-TGmO_q4ciZ85IPHpjA,2893
10
- commonmeta/metadata.py,sha256=m9UtE95t9awrlo9w9qZtwF7Y9sRfABpA5JKUZdyz5b4,18921
11
- commonmeta/schema_utils.py,sha256=WGpmMj9cfNMg_55hhgwY9qpO0A1HSvTLQC2equjBftI,1770
10
+ commonmeta/metadata.py,sha256=90aTe47d071wHxwcNsOqU5lSVPKP8wAPnPHhddj3Fuo,18443
11
+ commonmeta/schema_utils.py,sha256=zn3gqAHciUOQmrw9okR68weFs-yqPPyORFt-Zl1D3Lw,1924
12
12
  commonmeta/translators.py,sha256=CBMK4jrXRmGZiAhCh6wsJjhbDJWbcsda8UvXFXxccAw,1363
13
13
  commonmeta/utils.py,sha256=pJnh3EzOU1E2nutnAZsopY_NsUX6zYmxoj5bIYqqWvE,50574
14
14
  commonmeta/readers/__init__.py,sha256=vOf7UsOKNoh_ZCuyexxhAmPMt8wjB-pF_CfpWRaN8pk,45
15
15
  commonmeta/readers/bibtex_reader.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- commonmeta/readers/cff_reader.py,sha256=kIG-OKGQz37oWL6cU3Cz7HQ83DnRCmQ2PKSjIDodSp8,6152
17
- commonmeta/readers/codemeta_reader.py,sha256=fF0bIYf3J2mpgfdtM0C1lbDexUuoVO7JS1cFf7P--2E,3655
16
+ commonmeta/readers/cff_reader.py,sha256=HMFK6QIg_XIlhmYIWL4EfIyuidYl5L_0TAwyG78XPlU,6244
17
+ commonmeta/readers/codemeta_reader.py,sha256=QmJjh9f6kLxOWCBaZqdqY-LPlA0g2cX90yvPt0p1Nw8,3743
18
18
  commonmeta/readers/commonmeta_reader.py,sha256=46XrCr2whkUP4uiaNiFXS7ABwowcRdWcLG-3OcfhVdk,303
19
- commonmeta/readers/crossref_reader.py,sha256=qdHI7PV2rHcwxxnL-xlpF_U-4Tedwolm0SiHWp2mEhQ,13138
19
+ commonmeta/readers/crossref_reader.py,sha256=ydyiFw3m4P8LgMuornQrwJPds8bWPBGR03wtUG3Or-Y,13273
20
20
  commonmeta/readers/crossref_xml_reader.py,sha256=A2iAFT2IbhTNcfL-Dx4xO2SFSNOu_HNyPisus0u4UJI,18719
21
21
  commonmeta/readers/csl_reader.py,sha256=OxzC2AZKfv43BCah4XGYvlK_LUK-5mxXFcjdzB5vv_o,3216
22
- commonmeta/readers/datacite_reader.py,sha256=M6gznf1kisR1WzDZaoR0pLJC6Q4Rtnerodfs8lU2khI,12094
23
- commonmeta/readers/datacite_xml_reader.py,sha256=zJSuN9pnWplYFH7V1eneh0OjKTFCNkOLmEMf6fU6_xg,13048
24
- commonmeta/readers/inveniordm_reader.py,sha256=6LkT6R20jSFqDdZqAzcREHbdAcIPHiYJvxKsK_mpDdw,8374
22
+ commonmeta/readers/datacite_reader.py,sha256=4b_AP8m_aOUNVnVB0nU9j4-a8WTpmYJA8QPr5v35qyw,12219
23
+ commonmeta/readers/datacite_xml_reader.py,sha256=nsPc7JBbIKgx6Yaauq8vmhUE-o6K0t_MAuExv6FO2AU,13205
24
+ commonmeta/readers/inveniordm_reader.py,sha256=DtSloEZDu8bL-QLQAqAW1aDsS6ESTDZyhme379IekjY,8502
25
25
  commonmeta/readers/jsonfeed_reader.py,sha256=zcPxxuyAGW8W7w0-VwP9AhpX97qVWHQJUIJ5p4bBbfE,15655
26
- commonmeta/readers/kbase_reader.py,sha256=KH3loJvuq2bm8zAYIUG7hTsr5-2Anj3NQvoJUDiqmss,6764
26
+ commonmeta/readers/kbase_reader.py,sha256=0Y9cHRNs_7kHyocN4IESXbgmXJiq4TXoxvGeUYGml1s,6896
27
27
  commonmeta/readers/openalex_reader.py,sha256=4HUkBsut_iUjhUcC5c1GHgxnKsYQc-fgY43QILgVZEg,12826
28
- commonmeta/readers/ris_reader.py,sha256=oQ3G7qQmNwhr4cNp-Gv5UW28J2K1oKpBlPh-tjRtnpQ,3678
28
+ commonmeta/readers/ris_reader.py,sha256=nwK8Eux0wPjwKqXFWS4Cfd6FAY7Id4Mi_hgkTwPntHs,3766
29
29
  commonmeta/readers/schema_org_reader.py,sha256=AlFMmuUovqlMYkwL9F1Um6bX5vIWzhqmreHCrzsC3rU,17275
30
30
  commonmeta/resources/cff_v1.2.0.json,sha256=MpfjDYgX7fN9PLiG54ISZ2uu9WItNqfh-yaRuTf6Ptg,46691
31
31
  commonmeta/resources/commonmeta_v0.12.json,sha256=HUSNReXh2JN3Q6YWSt7CE69js8dh50OlpMYGTyU98oU,16762
@@ -77,14 +77,14 @@ commonmeta/writers/__init__.py,sha256=47-snms6xBHkoEXKYV1DBtH1npAtlVtvY29Z4Zr45q
77
77
  commonmeta/writers/bibtex_writer.py,sha256=doAdyl1NEp60mPkHPo3GMH8B-HA6MzLAdlyNsIecTzU,4972
78
78
  commonmeta/writers/citation_writer.py,sha256=qs_4X3BjrSqHexmJFPvPDTp0mRIqzb0F70_Wuc7S9x0,2343
79
79
  commonmeta/writers/commonmeta_writer.py,sha256=QpfyhG__7o_XpsOTCPWxGymO7YKwZi2LQh8Zic44bdc,1365
80
- commonmeta/writers/crossref_xml_writer.py,sha256=cGrX9vacNwDdK7TbH1RWig68SH8hg5VusuqkI9gU1DY,33931
80
+ commonmeta/writers/crossref_xml_writer.py,sha256=rcPOfrGxU4mX7_fFywYWDW2FFUoKW9wD-JzW8nX1ipI,33915
81
81
  commonmeta/writers/csl_writer.py,sha256=4gDYs1EzK4_L2UIRTfs25wgHmYRwdRP2zmfxF9387oU,2779
82
82
  commonmeta/writers/datacite_writer.py,sha256=bcinpwhq7XnVthKHH8-sdXA34dSlvFH4ImYH768iaQU,6428
83
- commonmeta/writers/inveniordm_writer.py,sha256=Bm0gSNndX-jbGwd0O162Bq_C97QNegRZbTdP_tXxzVc,25772
83
+ commonmeta/writers/inveniordm_writer.py,sha256=ARNssMUhwckAGwzz2ugJoLjVSTezbyLrK2c-utdmins,26795
84
84
  commonmeta/writers/ris_writer.py,sha256=3SdyEvMRaPRP1SV1MB-MXBlunE7x6og7RF1zuWtetPc,2094
85
85
  commonmeta/writers/schema_org_writer.py,sha256=s18_x0ReXwAGBoEAwp2q-HCgFQ-h5qRg6JyAlqCoSFE,5871
86
- commonmeta_py-0.123.dist-info/METADATA,sha256=-lwvx310XyyLy8ZHJfrkraQmCdbf7kao6vsnwyX57e4,7656
87
- commonmeta_py-0.123.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
88
- commonmeta_py-0.123.dist-info/entry_points.txt,sha256=U4w4BoRuS3rN5t5Y-uYSyOeU5Lh_VRVMS9OIDzIgw4w,50
89
- commonmeta_py-0.123.dist-info/licenses/LICENSE,sha256=wsIvxF9Q9GC9vA_s79zTWP3BkXJdfUNRmALlU8GbW1s,1074
90
- commonmeta_py-0.123.dist-info/RECORD,,
86
+ commonmeta_py-0.125.dist-info/METADATA,sha256=x7HcPVeiOM03np_p5URA_xH84RGt-8HO7tF3oV7xoB4,7656
87
+ commonmeta_py-0.125.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
88
+ commonmeta_py-0.125.dist-info/entry_points.txt,sha256=U4w4BoRuS3rN5t5Y-uYSyOeU5Lh_VRVMS9OIDzIgw4w,50
89
+ commonmeta_py-0.125.dist-info/licenses/LICENSE,sha256=wsIvxF9Q9GC9vA_s79zTWP3BkXJdfUNRmALlU8GbW1s,1074
90
+ commonmeta_py-0.125.dist-info/RECORD,,