commonmeta-py 0.107__py3-none-any.whl → 0.108__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.
Files changed (47) hide show
  1. commonmeta/__init__.py +12 -15
  2. commonmeta/api_utils.py +3 -2
  3. commonmeta/base_utils.py +186 -3
  4. commonmeta/cli.py +114 -34
  5. commonmeta/constants.py +20 -0
  6. commonmeta/file_utils.py +112 -0
  7. commonmeta/metadata.py +102 -42
  8. commonmeta/readers/codemeta_reader.py +1 -1
  9. commonmeta/readers/crossref_reader.py +23 -10
  10. commonmeta/readers/crossref_xml_reader.py +1 -1
  11. commonmeta/readers/datacite_reader.py +6 -4
  12. commonmeta/readers/{json_feed_reader.py → jsonfeed_reader.py} +12 -12
  13. commonmeta/resources/crossref/common5.4.0.xsd +1264 -0
  14. commonmeta/resources/crossref/{crossref5.3.1.xsd → crossref5.4.0.xsd} +286 -88
  15. commonmeta/resources/crossref/doi_resources5.4.0.xsd +117 -0
  16. commonmeta/resources/crossref/fundingdata5.4.0.xsd +59 -0
  17. commonmeta/resources/crossref/fundref.xsd +29 -19
  18. commonmeta/resources/crossref/languages5.4.0.xsd +8119 -0
  19. commonmeta/resources/crossref/mediatypes5.4.0.xsd +2207 -0
  20. commonmeta/resources/crossref/module-ali.xsd +14 -6
  21. commonmeta/resources/crossref/standard-modules/mathml3/mathml3-common.xsd +101 -0
  22. commonmeta/resources/crossref/standard-modules/mathml3/mathml3-content.xsd +683 -0
  23. commonmeta/resources/crossref/standard-modules/mathml3/mathml3-presentation.xsd +2092 -0
  24. commonmeta/resources/crossref/standard-modules/mathml3/mathml3-strict-content.xsd +186 -0
  25. commonmeta/resources/crossref/standard-modules/mathml3/mathml3.xsd +9 -0
  26. commonmeta/resources/crossref/standard-modules/mathml3/module-ali.xsd +47 -0
  27. commonmeta/resources/crossref/standard-modules/module-ali.xsd +47 -0
  28. commonmeta/resources/crossref/standard-modules/xlink.xsd +100 -0
  29. commonmeta/resources/crossref/standard-modules/xml.xsd +287 -0
  30. commonmeta/resources/crossref/xml.xsd +287 -0
  31. commonmeta/schema_utils.py +25 -0
  32. commonmeta/utils.py +25 -9
  33. commonmeta/writers/bibtex_writer.py +5 -5
  34. commonmeta/writers/commonmeta_writer.py +4 -17
  35. commonmeta/writers/crossref_xml_writer.py +1031 -4
  36. commonmeta/writers/csl_writer.py +1 -2
  37. commonmeta/writers/datacite_writer.py +8 -4
  38. commonmeta/writers/inveniordm_writer.py +277 -2
  39. commonmeta/writers/ris_writer.py +3 -3
  40. commonmeta/writers/schema_org_writer.py +10 -5
  41. {commonmeta_py-0.107.dist-info → commonmeta_py-0.108.dist-info}/METADATA +4 -2
  42. {commonmeta_py-0.107.dist-info → commonmeta_py-0.108.dist-info}/RECORD +45 -31
  43. commonmeta/crossref_utils.py +0 -662
  44. commonmeta/resources/crossref/common5.3.1.xsd +0 -1538
  45. {commonmeta_py-0.107.dist-info → commonmeta_py-0.108.dist-info}/WHEEL +0 -0
  46. {commonmeta_py-0.107.dist-info → commonmeta_py-0.108.dist-info}/entry_points.txt +0 -0
  47. {commonmeta_py-0.107.dist-info → commonmeta_py-0.108.dist-info}/licenses/LICENSE +0 -0
@@ -1,662 +0,0 @@
1
- """Crossref utils module for commonmeta-py"""
2
-
3
- import uuid
4
- from datetime import datetime
5
- from typing import Optional
6
-
7
- import pydash as py_
8
- from dateutil.parser import parse
9
- from furl import furl
10
- from lxml import etree
11
-
12
- from .constants import ROR_TO_CROSSREF_FUNDER_ID_TRANSLATIONS, Commonmeta
13
- from .doi_utils import doi_from_url, validate_doi
14
- from .utils import compact, normalize_id, normalize_orcid, validate_url, wrap
15
-
16
-
17
- def generate_crossref_xml(metadata: Commonmeta) -> Optional[str]:
18
- """Generate Crossref XML. First checks for write errors (JSON schema validation)"""
19
- xml = crossref_root()
20
- head = etree.SubElement(xml, "head")
21
- # we use a uuid as batch_id
22
- etree.SubElement(head, "doi_batch_id").text = str(uuid.uuid4())
23
- etree.SubElement(head, "timestamp").text = datetime.now().strftime("%Y%m%d%H%M%S")
24
- depositor = etree.SubElement(head, "depositor")
25
- etree.SubElement(depositor, "depositor_name").text = metadata.depositor
26
- etree.SubElement(depositor, "email_address").text = metadata.email
27
- etree.SubElement(head, "registrant").text = metadata.registrant
28
-
29
- body = etree.SubElement(xml, "body")
30
- body = insert_crossref_work(metadata, body)
31
- return etree.tostring(
32
- xml,
33
- doctype='<?xml version="1.0" encoding="UTF-8"?>',
34
- pretty_print=True,
35
- )
36
-
37
-
38
- def insert_crossref_work(metadata, xml):
39
- """Insert crossref work"""
40
- if metadata.type not in ["JournalArticle", "Article", "BlogPost"]:
41
- return xml
42
- if doi_from_url(metadata.id) is None or metadata.url is None:
43
- return xml
44
- if metadata.type == "JournalArticle":
45
- xml = insert_journal(metadata, xml)
46
- elif metadata.type in ["Article", "BlogPost"]:
47
- xml = insert_posted_content(metadata, xml)
48
-
49
-
50
- def insert_journal(metadata, xml):
51
- """Insert journal"""
52
- journal = etree.SubElement(xml, "journal")
53
- if metadata.language is not None:
54
- journal_metadata = etree.SubElement(
55
- journal, "journal_metadata", {"language": metadata.language[:2]}
56
- )
57
- else:
58
- journal_metadata = etree.SubElement(journal, "journal_metadata")
59
- if (
60
- metadata.container is not None
61
- and metadata.container.get("title", None) is not None
62
- ):
63
- etree.SubElement(journal_metadata, "full_title").text = metadata.container.get(
64
- "title"
65
- )
66
- journal_metadata = insert_group_title(metadata, journal_metadata)
67
- journal_article = etree.SubElement(
68
- journal, "journal_article", {"publication_type": "full_text"}
69
- )
70
- journal_article = insert_crossref_titles(metadata, journal_article)
71
- journal_article = insert_crossref_contributors(metadata, journal_article)
72
- journal_article = insert_crossref_publication_date(metadata, journal_article)
73
- journal_article = insert_crossref_abstract(metadata, journal_article)
74
- journal_article = insert_crossref_issn(metadata, journal_article)
75
- journal_article = insert_item_number(metadata, journal_article)
76
- journal_article = insert_funding_references(metadata, journal_article)
77
- journal_article = insert_crossref_access_indicators(metadata, journal_article)
78
- journal_article = insert_crossref_relations(metadata, journal_article)
79
- journal_article = insert_archive_locations(metadata, journal_article)
80
- journal_article = insert_doi_data(metadata, journal_article)
81
- journal_article = insert_citation_list(metadata, journal_article)
82
-
83
- return journal
84
-
85
-
86
- def insert_posted_content(metadata, xml):
87
- """Insert posted content"""
88
- if metadata.language is not None:
89
- posted_content = etree.SubElement(
90
- xml, "posted_content", {"type": "other", "language": metadata.language[:2]}
91
- )
92
- else:
93
- posted_content = etree.SubElement(xml, "posted_content", {"type": "other"})
94
-
95
- posted_content = insert_group_title(metadata, posted_content)
96
- posted_content = insert_crossref_contributors(metadata, posted_content)
97
- posted_content = insert_crossref_titles(metadata, posted_content)
98
- posted_content = insert_posted_date(metadata, posted_content)
99
- posted_content = insert_institution(metadata, posted_content)
100
- posted_content = insert_item_number(metadata, posted_content)
101
- posted_content = insert_crossref_abstract(metadata, posted_content)
102
- posted_content = insert_funding_references(metadata, posted_content)
103
- posted_content = insert_crossref_access_indicators(metadata, posted_content)
104
- posted_content = insert_crossref_relations(metadata, posted_content)
105
- posted_content = insert_archive_locations(metadata, posted_content)
106
- posted_content = insert_doi_data(metadata, posted_content)
107
- posted_content = insert_citation_list(metadata, posted_content)
108
-
109
- return xml
110
-
111
-
112
- def insert_group_title(metadata, xml):
113
- """Insert group title"""
114
- if metadata.subjects is None or len(metadata.subjects) == 0:
115
- return xml
116
- group_title = metadata.subjects[0].get("subject", None)
117
- # strip optional FOS (Field of Science) prefix
118
- if group_title.startswith("FOS: "):
119
- group_title = group_title[5:]
120
- etree.SubElement(xml, "group_title").text = group_title
121
- return xml
122
-
123
-
124
- def insert_crossref_contributors(metadata, xml):
125
- """Insert crossref contributors"""
126
- if metadata.contributors is None or len(metadata.contributors) == 0:
127
- return xml
128
- contributors = etree.SubElement(xml, "contributors")
129
- con = [
130
- c
131
- for c in metadata.contributors
132
- if c.get("contributorRoles", None) == ["Author"]
133
- or c.get("contributorRoles", None) == ["Editor"]
134
- ]
135
- for num, contributor in enumerate(con):
136
- contributor_role = (
137
- "author" if "Author" in contributor.get("contributorRoles") else None
138
- )
139
- if contributor_role is None:
140
- contributor_role = (
141
- "editor" if "Editor" in contributor.get("contributorRoles") else None
142
- )
143
- sequence = "first" if num == 0 else "additional"
144
- if (
145
- contributor.get("type", None) == "Organization"
146
- and contributor.get("name", None) is not None
147
- ):
148
- etree.SubElement(
149
- contributors,
150
- "organization",
151
- {"contributor_role": contributor_role, "sequence": sequence},
152
- ).text = contributor.get("name")
153
- elif (
154
- contributor.get("givenName", None) is not None
155
- or contributor.get("familyName", None) is not None
156
- ):
157
- person_name = etree.SubElement(
158
- contributors,
159
- "person_name",
160
- {"contributor_role": contributor_role, "sequence": sequence},
161
- )
162
- person_name = insert_crossref_person(contributor, person_name)
163
- elif contributor.get("affiliations", None) is not None:
164
- anonymous = etree.SubElement(
165
- contributors,
166
- "anonymous",
167
- {"contributor_role": contributor_role, "sequence": sequence},
168
- )
169
- anonymous = insert_crossref_anonymous(contributor, anonymous)
170
- else:
171
- etree.SubElement(
172
- contributors,
173
- "anonymous",
174
- {"contributor_role": contributor_role, "sequence": sequence},
175
- )
176
- return xml
177
-
178
-
179
- def insert_crossref_person(contributor, xml):
180
- """Insert crossref person"""
181
- if contributor.get("givenName", None) is not None:
182
- etree.SubElement(xml, "given_name").text = contributor.get("givenName")
183
- if contributor.get("familyName", None) is not None:
184
- etree.SubElement(xml, "surname").text = contributor.get("familyName")
185
-
186
- if contributor.get("affiliations", None) is not None:
187
- affiliations = etree.SubElement(xml, "affiliations")
188
- institution = etree.SubElement(affiliations, "institution")
189
- if py_.get(contributor, "affiliations.0.name") is not None:
190
- etree.SubElement(institution, "institution_name").text = py_.get(
191
- contributor, "affiliations.0.name"
192
- )
193
- if py_.get(contributor, "affiliations.0.id") is not None:
194
- etree.SubElement(
195
- institution, "institution_id", {"type": "ror"}
196
- ).text = py_.get(contributor, "affiliations.0.id")
197
- orcid = normalize_orcid(contributor.get("id", None))
198
- if orcid is not None:
199
- etree.SubElement(xml, "ORCID").text = orcid
200
- return xml
201
-
202
-
203
- def insert_crossref_anonymous(contributor, xml):
204
- """Insert crossref anonymous"""
205
- if contributor.get("affiliations", None) is None:
206
- return xml
207
- affiliations = etree.SubElement(xml, "affiliations")
208
- institution = etree.SubElement(affiliations, "institution")
209
- if py_.get(contributor, "affiliations.0.name") is not None:
210
- etree.SubElement(institution, "institution_name").text = py_.get(
211
- contributor, "affiliations.0.name"
212
- )
213
- return xml
214
-
215
-
216
- def insert_crossref_titles(metadata, xml):
217
- """Insert crossref titles"""
218
- titles = etree.SubElement(xml, "titles")
219
- for title in wrap(metadata.titles):
220
- if isinstance(title, dict):
221
- etree.SubElement(titles, "title").text = title.get("title", None)
222
- else:
223
- etree.SubElement(titles, "title").text = title
224
- return xml
225
-
226
-
227
- def insert_citation_list(metadata, xml):
228
- """Insert citation list"""
229
- if metadata.references is None or len(metadata.references) == 0:
230
- return xml
231
-
232
- citation_list = etree.SubElement(xml, "citation_list")
233
- for i, ref in enumerate(metadata.references):
234
- if ref.get("id", None) is None:
235
- continue
236
- citation = etree.SubElement(
237
- citation_list, "citation", {"key": ref.get("key", f"ref{i + 1}")}
238
- )
239
- if ref.get("journal_title", None) is not None:
240
- etree.SubElement(citation, "journal_article").text = ref.get(
241
- "journal_title"
242
- )
243
- if ref.get("author", None) is not None:
244
- etree.SubElement(citation, "author").text = ref.get("author")
245
- if ref.get("volume", None) is not None:
246
- etree.SubElement(citation, "volume").text = ref.get("volume")
247
- if ref.get("first_page", None) is not None:
248
- etree.SubElement(citation, "first_page").text = ref.get("first_page")
249
- if ref.get("publicationYear", None) is not None:
250
- etree.SubElement(citation, "cYear").text = ref.get("publicationYear")
251
- if ref.get("title", None) is not None:
252
- etree.SubElement(citation, "article_title").text = ref.get("title")
253
- if ref.get("id", None) is not None:
254
- etree.SubElement(citation, "doi").text = doi_from_url(ref.get("id"))
255
- if ref.get("unstructured", None) is not None:
256
- etree.SubElement(citation, "unstructured_citation").text = ref.get(
257
- "unstructured"
258
- )
259
- print(xml)
260
- return xml
261
-
262
-
263
- def insert_crossref_access_indicators(metadata, xml):
264
- """Insert crossref access indicators"""
265
- rights_uri = (
266
- metadata.license.get("url", None) if metadata.license is not None else None
267
- )
268
- if rights_uri is None:
269
- return xml
270
- program = etree.SubElement(
271
- xml,
272
- "program",
273
- {
274
- "xmlns": "http://www.crossref.org/AccessIndicators.xsd",
275
- "name": "AccessIndicators",
276
- },
277
- )
278
- etree.SubElement(program, "license_ref", {"applies_to": "vor"}).text = rights_uri
279
- etree.SubElement(program, "license_ref", {"applies_to": "tdm"}).text = rights_uri
280
- return xml
281
-
282
-
283
- def insert_crossref_relations(metadata, xml):
284
- """Insert crossref relations"""
285
- if metadata.relations is None or len(metadata.relations) == 0:
286
- return xml
287
- program = etree.SubElement(
288
- xml,
289
- "program",
290
- {
291
- "xmlns": "http://www.crossref.org/relations.xsd",
292
- "name": "relations",
293
- },
294
- )
295
- for relation in metadata.relations:
296
- if relation.get("type", None) in [
297
- "IsPartOf",
298
- "HasPart",
299
- "IsReviewOf",
300
- "HasReview",
301
- "IsRelatedMaterial",
302
- "HasRelatedMaterial",
303
- ]:
304
- group = "inter_work_relation"
305
- elif relation.get("type", None) in [
306
- "IsIdenticalTo",
307
- "IsPreprintOf",
308
- "HasPreprint",
309
- "IsTranslationOf",
310
- "HasTranslation",
311
- "IsVersionOf",
312
- "HasVersion",
313
- ]:
314
- group = "intra_work_relation"
315
- else:
316
- continue
317
-
318
- related_item = etree.SubElement(program, "related_item")
319
- f = furl(relation.get("id", None))
320
- if validate_doi(relation.get("id", None)):
321
- identifier_type = "doi"
322
- _id = doi_from_url(relation.get("id", None))
323
- elif f.host == "portal.issn.org":
324
- identifier_type = "issn"
325
- _id = f.path.segments[-1]
326
- elif validate_url(relation.get("id", None)) == "URL":
327
- identifier_type = "uri"
328
- _id = relation.get("id", None)
329
- else:
330
- identifier_type = "other"
331
- _id = relation.get("id", None)
332
-
333
- etree.SubElement(
334
- related_item,
335
- group,
336
- {
337
- "relationship-type": py_.lower_first(relation.get("type"))
338
- if relation.get("type", None) is not None
339
- else None,
340
- "identifier-type": identifier_type,
341
- },
342
- ).text = _id
343
-
344
- return xml
345
-
346
-
347
- def insert_funding_references(metadata, xml):
348
- """Insert funding references"""
349
- if metadata.funding_references is None or len(metadata.funding_references) == 0:
350
- return xml
351
- program = etree.SubElement(
352
- xml,
353
- "program",
354
- {
355
- "xmlns": "http://www.crossref.org/fundref.xsd",
356
- "name": "fundref",
357
- },
358
- )
359
- for funding_reference in metadata.funding_references:
360
- assertion = etree.SubElement(program, "assertion", {"name": "fundgroup"})
361
- funder_name = etree.SubElement(
362
- assertion,
363
- "assertion",
364
- {"name": "funder_name"},
365
- )
366
- if funding_reference.get("funderIdentifier", None) is not None:
367
- funder_identifier = funding_reference.get("funderIdentifier", None)
368
-
369
- # translate ROR to Crossref funder ID until Crossref supports ROR
370
- funder_identifier = ROR_TO_CROSSREF_FUNDER_ID_TRANSLATIONS.get(
371
- funder_identifier, funder_identifier
372
- )
373
-
374
- etree.SubElement(
375
- funder_name,
376
- "assertion",
377
- {"name": "funder_identifier"},
378
- ).text = funder_identifier
379
- if funding_reference.get("awardNumber", None) is not None:
380
- etree.SubElement(
381
- assertion,
382
- "assertion",
383
- {"name": "award_number"},
384
- ).text = funding_reference.get("awardNumber", None)
385
- funder_name.text = funding_reference.get("funderName", None)
386
- return xml
387
-
388
-
389
- def insert_crossref_subjects(metadata, xml):
390
- """Insert crossref subjects"""
391
- if metadata.subjects is None:
392
- return xml
393
- subjects = etree.SubElement(xml, "subjects")
394
- for subject in metadata.subjects:
395
- if isinstance(subject, dict):
396
- etree.SubElement(subjects, "subject").text = subject.get("subject", None)
397
- else:
398
- etree.SubElement(subjects, "subject").text = subject
399
- return xml
400
-
401
-
402
- def insert_crossref_language(metadata, xml):
403
- """Insert crossref language"""
404
- if metadata.language is None:
405
- return xml
406
- etree.SubElement(xml, "language").text = metadata.language
407
- return xml
408
-
409
-
410
- def insert_crossref_publication_date(metadata, xml):
411
- """Insert crossref publication date"""
412
- pub_date = parse(metadata.date.get("published", None))
413
- if pub_date is None:
414
- return xml
415
-
416
- publication_date = etree.SubElement(
417
- xml, "publication_date", {"media_type": "online"}
418
- )
419
- etree.SubElement(publication_date, "month").text = f"{pub_date.month:d}"
420
- etree.SubElement(publication_date, "day").text = f"{pub_date.day:d}"
421
- etree.SubElement(publication_date, "year").text = str(pub_date.year)
422
- return xml
423
-
424
-
425
- def insert_posted_date(metadata, xml):
426
- """Insert posted date"""
427
- pub_date = parse(metadata.date.get("published", None))
428
- if pub_date is None:
429
- return xml
430
-
431
- posted_date = etree.SubElement(xml, "posted_date", {"media_type": "online"})
432
- etree.SubElement(posted_date, "month").text = f"{pub_date.month:d}"
433
- etree.SubElement(posted_date, "day").text = f"{pub_date.day:d}"
434
- etree.SubElement(posted_date, "year").text = str(pub_date.year)
435
- return xml
436
-
437
-
438
- def insert_institution(metadata, xml):
439
- """Insert institution"""
440
- if metadata.publisher.get("name", None) is None:
441
- return xml
442
- institution = etree.SubElement(xml, "institution")
443
- etree.SubElement(institution, "institution_name").text = metadata.publisher.get(
444
- "name"
445
- )
446
- return xml
447
-
448
-
449
- def insert_item_number(metadata, xml):
450
- """Insert item number"""
451
- if metadata.identifiers is None:
452
- return xml
453
- for identifier in metadata.identifiers:
454
- if identifier.get("identifierType", None) == "UUID":
455
- # strip hyphen from UUIDs, as item_number can only be 32 characters long (UUIDv4 is 36 characters long)
456
- if identifier.get("identifierType", None) == "UUID":
457
- identifier["identifier"] = identifier.get("identifier", "").replace(
458
- "-", ""
459
- )
460
- etree.SubElement(
461
- xml,
462
- "item_number",
463
- {"item_number_type": identifier.get("identifierType", "").lower()},
464
- ).text = identifier.get("identifier", None)
465
- else:
466
- continue
467
- return xml
468
-
469
-
470
- def insert_archive_locations(metadata, xml):
471
- """Insert archive locations"""
472
- if metadata.archive_locations is None:
473
- return xml
474
- archive_locations = etree.SubElement(xml, "archive_locations")
475
- for archive_location in metadata.archive_locations:
476
- etree.SubElement(archive_locations, "archive", {"name": archive_location})
477
- return xml
478
-
479
-
480
- def insert_doi_data(metadata, xml):
481
- """Insert doi data"""
482
- if doi_from_url(metadata.id) is None or metadata.url is None:
483
- return xml
484
- doi_data = etree.SubElement(xml, "doi_data")
485
- etree.SubElement(doi_data, "doi").text = doi_from_url(metadata.id)
486
- etree.SubElement(doi_data, "resource").text = metadata.url
487
- collection = etree.SubElement(doi_data, "collection", {"property": "text-mining"})
488
- item = etree.SubElement(collection, "item")
489
- etree.SubElement(item, "resource", {"mime_type": "text/html"}).text = metadata.url
490
- if metadata.files is None:
491
- return xml
492
- for file in metadata.files:
493
- # Crossref schema currently doesn't support text/markdown
494
- if file.get("mimeType", None) == "text/markdown":
495
- file["mimeType"] = "text/plain"
496
- item = etree.SubElement(collection, "item")
497
- etree.SubElement(
498
- item, "resource", {"mime_type": file.get("mimeType", "")}
499
- ).text = file.get("url", None)
500
- return xml
501
-
502
-
503
- def insert_crossref_license(metadata, xml):
504
- """Insert crossref license"""
505
- if metadata.license is None:
506
- return xml
507
- license_ = etree.SubElement(xml, "license")
508
- if isinstance(metadata.license, dict):
509
- r = metadata.license
510
- else:
511
- r = {}
512
- r["rights"] = metadata.license
513
- r["rightsUri"] = normalize_id(metadata.license)
514
- attributes = compact(
515
- {
516
- "rightsURI": r.get("rightsUri", None),
517
- "rightsIdentifier": r.get("rightsIdentifier", None),
518
- "rightsIdentifierScheme": r.get("rightsIdentifierScheme"),
519
- "schemeURI": r.get("schemeUri", None),
520
- "xml:lang": r.get("lang", None),
521
- }
522
- )
523
- etree.SubElement(license_, "rights", attributes).text = r.get("rights", None)
524
- return xml
525
-
526
-
527
- def insert_crossref_issn(metadata, xml):
528
- """Insert crossref issn"""
529
- if (
530
- metadata.container is None
531
- or metadata.container.get("identifierType", None) != "ISSN"
532
- ):
533
- return xml
534
- etree.SubElement(xml, "issn").text = metadata.container.get("identifier", None)
535
- return xml
536
-
537
-
538
- def insert_crossref_abstract(metadata, xml):
539
- """Insert crossref abstrac"""
540
- if metadata.descriptions is None:
541
- return xml
542
- if isinstance(metadata.descriptions[0], dict):
543
- d = metadata.descriptions[0]
544
- else:
545
- d = {}
546
- d["description"] = metadata.descriptions[0]
547
- abstract = etree.SubElement(
548
- xml, "abstract", {"xmlns": "http://www.ncbi.nlm.nih.gov/JATS1"}
549
- )
550
- etree.SubElement(abstract, "p").text = d.get("description", None)
551
- return xml
552
-
553
-
554
- def crossref_root():
555
- """Crossref root with namespaces"""
556
- doi_batch = """<doi_batch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.crossref.org/schema/5.3.1" xmlns:jats="http://www.ncbi.nlm.nih.gov/JATS1" xmlns:fr="http://www.crossref.org/fundref.xsd" xmlns:mml="http://www.w3.org/1998/Math/MathML" xsi:schemaLocation="http://www.crossref.org/schema/5.3.1 https://www.crossref.org/schemas/crossref5.3.1.xsd" version="5.3.1"></doi_batch>"""
557
- return etree.fromstring(doi_batch)
558
-
559
-
560
- def generate_crossref_xml_list(metalist) -> Optional[str]:
561
- """Generate Crossref XML list."""
562
- if not metalist.is_valid:
563
- return None
564
- xml = crossref_root()
565
- head = etree.SubElement(xml, "head")
566
- # we use a uuid as batch_id
567
- etree.SubElement(head, "doi_batch_id").text = str(uuid.uuid4())
568
- etree.SubElement(head, "timestamp").text = datetime.now().strftime("%Y%m%d%H%M%S")
569
- depositor = etree.SubElement(head, "depositor")
570
- etree.SubElement(depositor, "depositor_name").text = metalist.depositor or "test"
571
- etree.SubElement(depositor, "email_address").text = (
572
- metalist.email or "info@example.org"
573
- )
574
- etree.SubElement(head, "registrant").text = metalist.registrant or "test"
575
-
576
- body = etree.SubElement(xml, "body")
577
- body = [insert_crossref_work(item, body) for item in metalist.items]
578
- return etree.tostring(
579
- xml,
580
- doctype='<?xml version="1.0" encoding="UTF-8"?>',
581
- pretty_print=True,
582
- )
583
-
584
-
585
- """Errors for the Crossref XML API.
586
-
587
- Error responses will be converted into an exception from this module.
588
- """
589
-
590
-
591
- class HttpError(Exception):
592
- """Exception raised when a connection problem happens."""
593
-
594
-
595
- class CrossrefError(Exception):
596
- """Exception raised when the server returns a known HTTP error code.
597
-
598
- Known HTTP error codes include:
599
-
600
- * 204 No Content
601
- * 400 Bad Request
602
- * 401 Unauthorized
603
- * 403 Forbidden
604
- * 404 Not Found
605
- * 410 Gone (deleted)
606
- """
607
-
608
- @staticmethod
609
- def factory(err_code, *args):
610
- """Create exceptions through a Factory based on the HTTP error code."""
611
- if err_code == 204:
612
- return CrossrefNoContentError(*args)
613
- elif err_code == 400:
614
- return CrossrefBadRequestError(*args)
615
- elif err_code == 401:
616
- return CrossrefUnauthorizedError(*args)
617
- elif err_code == 403:
618
- return CrossrefForbiddenError(*args)
619
- elif err_code == 404:
620
- return CrossrefNotFoundError(*args)
621
- else:
622
- return CrossrefServerError(*args)
623
-
624
-
625
- class CrossrefServerError(CrossrefError):
626
- """An internal server error happened on the Crossref end. Try later.
627
-
628
- Base class for all 5XX-related HTTP error codes.
629
- """
630
-
631
-
632
- class CrossrefRequestError(CrossrefError):
633
- """A Crossref request error. You made an invalid request.
634
-
635
- Base class for all 4XX-related HTTP error codes as well as 204.
636
- """
637
-
638
-
639
- class CrossrefNoContentError(CrossrefRequestError):
640
- """DOI is known to Crossref, but not resolvable.
641
-
642
- This might be due to handle's latency.
643
- """
644
-
645
-
646
- class CrossrefBadRequestError(CrossrefRequestError):
647
- """Bad request error.
648
-
649
- Bad requests can include e.g. invalid XML, wrong domain, wrong prefix.
650
- """
651
-
652
-
653
- class CrossrefUnauthorizedError(CrossrefRequestError):
654
- """Bad username or password."""
655
-
656
-
657
- class CrossrefForbiddenError(CrossrefRequestError):
658
- """Login problem, record belongs to another party or quota exceeded."""
659
-
660
-
661
- class CrossrefNotFoundError(CrossrefRequestError):
662
- """DOI does not exist in the database."""