commonmeta-py 0.106__py3-none-any.whl → 0.107__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 +13 -1
- commonmeta/crossref_utils.py +80 -1
- commonmeta/utils.py +70 -11
- commonmeta/writers/citation_writer.py +10 -5
- commonmeta/writers/commonmeta_writer.py +1 -0
- commonmeta/writers/crossref_xml_writer.py +1 -0
- commonmeta/writers/csl_writer.py +5 -4
- commonmeta/writers/datacite_writer.py +5 -4
- commonmeta/writers/inveniordm_writer.py +9 -8
- {commonmeta_py-0.106.dist-info → commonmeta_py-0.107.dist-info}/METADATA +2 -1
- {commonmeta_py-0.106.dist-info → commonmeta_py-0.107.dist-info}/RECORD +14 -14
- {commonmeta_py-0.106.dist-info → commonmeta_py-0.107.dist-info}/WHEEL +0 -0
- {commonmeta_py-0.106.dist-info → commonmeta_py-0.107.dist-info}/entry_points.txt +0 -0
- {commonmeta_py-0.106.dist-info → commonmeta_py-0.107.dist-info}/licenses/LICENSE +0 -0
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.
|
13
|
+
__version__ = "0.107"
|
14
14
|
__author__ = "Martin Fenner"
|
15
15
|
__license__ = "MIT"
|
16
16
|
|
@@ -31,6 +31,18 @@ from .base_utils import (
|
|
31
31
|
unwrap,
|
32
32
|
wrap,
|
33
33
|
)
|
34
|
+
from .crossref_utils import (
|
35
|
+
CrossrefBadRequestError,
|
36
|
+
CrossrefError,
|
37
|
+
CrossrefForbiddenError,
|
38
|
+
CrossrefNoContentError,
|
39
|
+
CrossrefNotFoundError,
|
40
|
+
CrossrefRequestError,
|
41
|
+
CrossrefServerError,
|
42
|
+
CrossrefUnauthorizedError,
|
43
|
+
HttpError,
|
44
|
+
generate_crossref_xml,
|
45
|
+
)
|
34
46
|
from .date_utils import (
|
35
47
|
get_date_from_crossref_parts,
|
36
48
|
get_date_from_date_parts,
|
commonmeta/crossref_utils.py
CHANGED
@@ -231,7 +231,6 @@ def insert_citation_list(metadata, xml):
|
|
231
231
|
|
232
232
|
citation_list = etree.SubElement(xml, "citation_list")
|
233
233
|
for i, ref in enumerate(metadata.references):
|
234
|
-
print(i)
|
235
234
|
if ref.get("id", None) is None:
|
236
235
|
continue
|
237
236
|
citation = etree.SubElement(
|
@@ -581,3 +580,83 @@ def generate_crossref_xml_list(metalist) -> Optional[str]:
|
|
581
580
|
doctype='<?xml version="1.0" encoding="UTF-8"?>',
|
582
581
|
pretty_print=True,
|
583
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."""
|
commonmeta/utils.py
CHANGED
@@ -408,14 +408,36 @@ def openalex_api_url(id: str, identifier_type: str, **kwargs) -> str:
|
|
408
408
|
|
409
409
|
def openalex_api_query_url(query: dict) -> str:
|
410
410
|
"""Return the OpenAlex API query URL"""
|
411
|
+
# Define allowed types
|
412
|
+
types = [
|
413
|
+
"article", "book-chapter", "dataset", "preprint", "dissertation",
|
414
|
+
"book", "review", "paratext", "libguides", "letter", "other",
|
415
|
+
"reference-entry", "report", "editorial", "peer-review", "erratum",
|
416
|
+
"standard", "grant", "supplementary-materials", "retraction",
|
417
|
+
]
|
418
|
+
|
411
419
|
url = "https://api.openalex.org/works"
|
412
420
|
f = furl(url)
|
413
|
-
|
414
|
-
|
421
|
+
|
422
|
+
# Handle pagination and sample parameters
|
423
|
+
number = max(1, min(1000, int(query.get("number", query.get("rows", 10)))))
|
424
|
+
page = max(1, int(query.get("page", 1)))
|
425
|
+
|
426
|
+
sample = query.get("sample", False)
|
427
|
+
if sample:
|
428
|
+
f.args["sample"] = str(number)
|
429
|
+
else:
|
430
|
+
f.args["per-page"] = str(number)
|
431
|
+
f.args["page"] = str(page)
|
432
|
+
# Sort results by published date in descending order
|
433
|
+
f.args["sort"] = "publication_date:desc"
|
434
|
+
|
435
|
+
# Build filters
|
415
436
|
filters = []
|
437
|
+
queries = []
|
416
438
|
_query = None
|
417
|
-
_filter = None
|
418
439
|
|
440
|
+
# Handle query parameters
|
419
441
|
if query.get("query", None) is not None:
|
420
442
|
queries += [query.get("query")]
|
421
443
|
for key, value in query.items():
|
@@ -428,23 +450,60 @@ def openalex_api_query_url(query: dict) -> str:
|
|
428
450
|
queries += [f"{key}:{value}"]
|
429
451
|
if queries:
|
430
452
|
_query = ",".join(queries)
|
453
|
+
f.args["query"] = _query
|
454
|
+
|
455
|
+
# Member/IDs filter
|
456
|
+
ids = query.get("ids", query.get("member", ""))
|
457
|
+
if ids:
|
458
|
+
filters.append(f"member:{ids}")
|
459
|
+
|
460
|
+
# Type filter
|
461
|
+
type_ = query.get("type_", query.get("type", ""))
|
462
|
+
if type_ and type_ in types:
|
463
|
+
filters.append(f"type:{type_}")
|
431
464
|
|
465
|
+
# ROR filter
|
466
|
+
ror = query.get("ror", "")
|
467
|
+
if ror:
|
468
|
+
r = validate_ror(ror)
|
469
|
+
if r:
|
470
|
+
filters.append(f"authorships.institutions.ror:{r}")
|
471
|
+
|
472
|
+
# ORCID filter
|
473
|
+
orcid = query.get("orcid", "")
|
474
|
+
if orcid:
|
475
|
+
o = validate_orcid(orcid)
|
476
|
+
if o:
|
477
|
+
filters.append(f"authorships.author.id:{o}")
|
478
|
+
|
479
|
+
# Year filter
|
480
|
+
year = query.get("year", query.get("publication_year", ""))
|
481
|
+
if year:
|
482
|
+
filters.append(f"publication_year:{year}")
|
483
|
+
|
484
|
+
# Other filters from the original function
|
432
485
|
for key, value in query.items():
|
433
486
|
if key in [
|
434
487
|
"prefix",
|
435
|
-
"member",
|
436
|
-
"type",
|
437
488
|
"has-full-text",
|
438
|
-
"has-references",
|
439
|
-
"has-orcid",
|
440
489
|
"has-funder",
|
441
490
|
"has-license",
|
442
491
|
]:
|
443
|
-
filters
|
444
|
-
|
445
|
-
|
492
|
+
filters.append(f"{key}:{value}")
|
493
|
+
|
494
|
+
# Boolean filters
|
495
|
+
# if query.get("hasORCID", query.get("has-orcid", False)):
|
496
|
+
# filters.append("has-orcid:true")
|
497
|
+
|
498
|
+
# if query.get("hasReferences", query.get("has-references", False)):
|
499
|
+
# filters.append("has-references:true")
|
446
500
|
|
447
|
-
|
501
|
+
# if query.get("hasAbstract", query.get("has-abstract", False)):
|
502
|
+
# filters.append("has-abstract:true")
|
503
|
+
|
504
|
+
# Add filters to params if any exist
|
505
|
+
if filters:
|
506
|
+
f.args["filter"] = ",".join(filters)
|
448
507
|
|
449
508
|
return f.url
|
450
509
|
|
@@ -1,13 +1,18 @@
|
|
1
1
|
"""Citation writer for commonmeta-py"""
|
2
2
|
|
3
|
-
import orjson as json
|
4
3
|
import re
|
5
|
-
|
6
|
-
|
7
|
-
from citeproc import
|
8
|
-
|
4
|
+
|
5
|
+
import orjson as json
|
6
|
+
from citeproc import (
|
7
|
+
Citation,
|
8
|
+
CitationItem,
|
9
|
+
CitationStylesBibliography,
|
10
|
+
CitationStylesStyle,
|
11
|
+
formatter,
|
12
|
+
)
|
9
13
|
from citeproc.source.json import CiteProcJSON
|
10
14
|
from citeproc_styles import get_style_filepath
|
15
|
+
from pydash import py_
|
11
16
|
|
12
17
|
|
13
18
|
def write_citation(metadata):
|
commonmeta/writers/csl_writer.py
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
"""CSL-JSON writer for commonmeta-py"""
|
2
2
|
|
3
|
-
import orjson as json
|
4
3
|
from typing import Optional
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
import orjson as json
|
6
|
+
|
7
|
+
from ..base_utils import compact, parse_attributes, presence, wrap
|
8
|
+
from ..constants import CM_TO_CSL_TRANSLATIONS, Commonmeta
|
8
9
|
from ..date_utils import get_date_parts
|
9
10
|
from ..doi_utils import doi_from_url
|
10
|
-
from ..
|
11
|
+
from ..utils import pages_as_string, to_csl
|
11
12
|
|
12
13
|
|
13
14
|
def write_csl(metadata: Commonmeta) -> Optional[str]:
|
@@ -1,19 +1,20 @@
|
|
1
1
|
"""DataCite writer for commonmeta-py"""
|
2
2
|
|
3
|
-
import orjson as json
|
4
3
|
from typing import Optional, Union
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
import orjson as json
|
6
|
+
|
7
|
+
from ..base_utils import compact, wrap
|
8
8
|
from ..constants import (
|
9
9
|
CM_TO_BIB_TRANSLATIONS,
|
10
|
-
CM_TO_CSL_TRANSLATIONS,
|
11
10
|
CM_TO_CR_TRANSLATIONS,
|
11
|
+
CM_TO_CSL_TRANSLATIONS,
|
12
12
|
CM_TO_DC_TRANSLATIONS,
|
13
13
|
CM_TO_RIS_TRANSLATIONS,
|
14
14
|
CM_TO_SO_TRANSLATIONS,
|
15
15
|
Commonmeta,
|
16
16
|
)
|
17
|
+
from ..doi_utils import doi_from_url, normalize_doi
|
17
18
|
|
18
19
|
|
19
20
|
def write_datacite(metadata: Commonmeta) -> Optional[Union[str, dict]]:
|
@@ -1,23 +1,24 @@
|
|
1
1
|
"""InvenioRDM writer for commonmeta-py"""
|
2
2
|
|
3
|
-
import orjson as json
|
4
3
|
from typing import Optional
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
from ..
|
5
|
+
import orjson as json
|
6
|
+
|
7
|
+
from ..base_utils import compact, parse_attributes, presence, wrap
|
9
8
|
from ..constants import (
|
10
9
|
CM_TO_INVENIORDM_TRANSLATIONS,
|
11
|
-
INVENIORDM_IDENTIFIER_TYPES,
|
12
10
|
CROSSREF_FUNDER_ID_TO_ROR_TRANSLATIONS,
|
11
|
+
INVENIORDM_IDENTIFIER_TYPES,
|
13
12
|
)
|
13
|
+
from ..date_utils import get_iso8601_date
|
14
|
+
from ..doi_utils import doi_from_url, normalize_doi
|
14
15
|
from ..utils import (
|
16
|
+
FOS_MAPPINGS,
|
15
17
|
get_language,
|
16
|
-
validate_orcid,
|
17
|
-
validate_ror,
|
18
18
|
id_from_url,
|
19
19
|
normalize_url,
|
20
|
-
|
20
|
+
validate_orcid,
|
21
|
+
validate_ror,
|
21
22
|
)
|
22
23
|
|
23
24
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: commonmeta-py
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.107
|
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
|
@@ -22,6 +22,7 @@ Requires-Dist: edtf<6,>=5.0.0
|
|
22
22
|
Requires-Dist: furl<3,>=2.1.3
|
23
23
|
Requires-Dist: jsonschema~=4.21
|
24
24
|
Requires-Dist: lxml>=4.8
|
25
|
+
Requires-Dist: marshmallow-utils>=0.10.0
|
25
26
|
Requires-Dist: nameparser<2,>=1.1.1
|
26
27
|
Requires-Dist: nh3<0.3,>=0.2.14
|
27
28
|
Requires-Dist: orjson<4,>=3.9.14
|
@@ -1,16 +1,16 @@
|
|
1
|
-
commonmeta/__init__.py,sha256=
|
1
|
+
commonmeta/__init__.py,sha256=Vz7vv934Mmzs1qxlSSx2bkuMVYtdkFJvvikHMihyzAA,2220
|
2
2
|
commonmeta/api_utils.py,sha256=jkM1d4jgvdZ5k0gkLeg8NDXsizPEesiHNB9sF7P-gFA,2687
|
3
3
|
commonmeta/author_utils.py,sha256=3lYW5s1rOUWNTKs1FP6XLfEUY3yCLOe_3L_VdJTDMp0,8585
|
4
4
|
commonmeta/base_utils.py,sha256=TCaMENjyySStUxFowg6R08Vt5eLkHfW7WjdEHVcAZWo,3752
|
5
5
|
commonmeta/cli.py,sha256=_sMYNzfiEgQ1qx6tZPMbOp1GZ9BBiWe0fYF3jJlamKU,6251
|
6
6
|
commonmeta/constants.py,sha256=NuugDPtaAlYx7d-0WKPHNO-qnjt4rzcj-EbzL2vSi2Q,19259
|
7
|
-
commonmeta/crossref_utils.py,sha256=
|
7
|
+
commonmeta/crossref_utils.py,sha256=odkcP_Ivk0Fs_BvUzxdPQ4F2098RIsKQT_NbZGQXo4A,24354
|
8
8
|
commonmeta/date_utils.py,sha256=H2cCobX0JREIUOT_cCigGd3MG7prGiQpXk1m4ZNrFwU,6318
|
9
9
|
commonmeta/doi_utils.py,sha256=ZztajfOLtnASk1BbQ1Y2Q4B_xxlnbujn7Opx5a1U5vY,9582
|
10
10
|
commonmeta/metadata.py,sha256=qgqbrvxPEbYn0pKIArhpaGjIeiHeuubc6nF37QgWp2Y,16702
|
11
11
|
commonmeta/schema_utils.py,sha256=yEb8Bt7WqjfQW5y15TV1uaZZdK_k9Ufz6V7Nq7Ywhi0,917
|
12
12
|
commonmeta/translators.py,sha256=CBMK4jrXRmGZiAhCh6wsJjhbDJWbcsda8UvXFXxccAw,1363
|
13
|
-
commonmeta/utils.py,sha256=
|
13
|
+
commonmeta/utils.py,sha256=G4BL_YQ-Ug0XwBHU4L83OViRQCiyOqZXg5uJC7WQZ2A,50462
|
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
16
|
commonmeta/readers/cff_reader.py,sha256=kIG-OKGQz37oWL6cU3Cz7HQ83DnRCmQ2PKSjIDodSp8,6152
|
@@ -61,16 +61,16 @@ commonmeta/resources/styles/modern-language-association.csl,sha256=HI2iU4krze1aH
|
|
61
61
|
commonmeta/resources/styles/vancouver.csl,sha256=lun3_i2oTilgsANk4LjFao2UDPQlGj_hgFgKAWC_DF8,12878
|
62
62
|
commonmeta/writers/__init__.py,sha256=47-snms6xBHkoEXKYV1DBtH1npAtlVtvY29Z4Zr45qI,45
|
63
63
|
commonmeta/writers/bibtex_writer.py,sha256=RjxpFt19N2PjX7cNtvN8kMYTxLWp_KhLYTsQNxYiC-4,4972
|
64
|
-
commonmeta/writers/citation_writer.py,sha256=
|
65
|
-
commonmeta/writers/commonmeta_writer.py,sha256=
|
66
|
-
commonmeta/writers/crossref_xml_writer.py,sha256=
|
67
|
-
commonmeta/writers/csl_writer.py,sha256=
|
68
|
-
commonmeta/writers/datacite_writer.py,sha256=
|
69
|
-
commonmeta/writers/inveniordm_writer.py,sha256=
|
64
|
+
commonmeta/writers/citation_writer.py,sha256=qs_4X3BjrSqHexmJFPvPDTp0mRIqzb0F70_Wuc7S9x0,2343
|
65
|
+
commonmeta/writers/commonmeta_writer.py,sha256=erIIcAGBy9cZb6FWSR6P0qzxr1c6iS3oCgKbGAsA94o,1871
|
66
|
+
commonmeta/writers/crossref_xml_writer.py,sha256=REORiWPyT2x13Y66IT1RNtOwB9NWutrzNCt_HLeQrsY,494
|
67
|
+
commonmeta/writers/csl_writer.py,sha256=IU70HwS77DD-NhQjc4TEGQVSaF-1TgIvyVR9E_zOqy0,2809
|
68
|
+
commonmeta/writers/datacite_writer.py,sha256=vijXscY411UMC72WL9VUYJhBtH6ZCPQTk8xtCo1jYtQ,6306
|
69
|
+
commonmeta/writers/inveniordm_writer.py,sha256=vGCItVrxkzPgvV-_m-cYnxTSux6mc3eer4JpI-10JhU,11430
|
70
70
|
commonmeta/writers/ris_writer.py,sha256=CXd9MiSHOMLReeGmr1moqteiq4x8AtLNYVS60s-aq8I,2094
|
71
71
|
commonmeta/writers/schema_org_writer.py,sha256=J2Y-krzyhgBZ6n__S_YvdjUtwiCm-RUtpGA_esFZUwg,5742
|
72
|
-
commonmeta_py-0.
|
73
|
-
commonmeta_py-0.
|
74
|
-
commonmeta_py-0.
|
75
|
-
commonmeta_py-0.
|
76
|
-
commonmeta_py-0.
|
72
|
+
commonmeta_py-0.107.dist-info/METADATA,sha256=1jO_8bgd6pHyUSLCr_LKKRwEYy73kXIgJJaEBowdalw,7552
|
73
|
+
commonmeta_py-0.107.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
74
|
+
commonmeta_py-0.107.dist-info/entry_points.txt,sha256=U4w4BoRuS3rN5t5Y-uYSyOeU5Lh_VRVMS9OIDzIgw4w,50
|
75
|
+
commonmeta_py-0.107.dist-info/licenses/LICENSE,sha256=wsIvxF9Q9GC9vA_s79zTWP3BkXJdfUNRmALlU8GbW1s,1074
|
76
|
+
commonmeta_py-0.107.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|