commonmeta-py 0.118__py3-none-any.whl → 0.119__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.118"
13
+ __version__ = "0.119"
14
14
  __author__ = "Martin Fenner"
15
15
  __license__ = "MIT"
16
16
 
commonmeta/doi_utils.py CHANGED
@@ -342,7 +342,6 @@ def is_rogue_scholar_doi(doi: str, ra: str = "crossref") -> bool:
342
342
 
343
343
  def generate_wordpress_doi(prefix: str, slug: str, guid: str) -> str:
344
344
  """Generate a DOI from a WordPress GUID and slug"""
345
- import re
346
345
 
347
346
  if not prefix or not guid:
348
347
  return ""
commonmeta/file_utils.py CHANGED
@@ -44,16 +44,6 @@ def download_file(url: str) -> bytes:
44
44
  resp = requests.get(url, stream=True)
45
45
  resp.raise_for_status()
46
46
  return resp.content
47
- # # Progress bar
48
- # total = int(resp.headers.get("content-length", 0))
49
-
50
- # buf = io.BytesIO()
51
- # with tqdm(total=total, unit="B", unit_scale=True, desc="downloading") as bar:
52
- # for chunk in resp.iter_content(chunk_size=8192):
53
- # if chunk:
54
- # buf.write(chunk)
55
- # bar.update(len(chunk))
56
- # return buf.getvalue()
57
47
 
58
48
 
59
49
  def write_file(filename: str, output: bytes) -> None:
@@ -1,6 +1,7 @@
1
1
  """Crossref XML writer for commonmeta-py"""
2
2
 
3
3
  import io
4
+ import logging
4
5
  from datetime import datetime
5
6
  from time import time
6
7
  from typing import Optional
@@ -18,6 +19,8 @@ from ..constants import Commonmeta
18
19
  from ..doi_utils import doi_from_url, validate_doi
19
20
  from ..utils import validate_url
20
21
 
22
+ logger = logging.getLogger(__name__)
23
+
21
24
  POSTED_CONTENT_TYPES = [
22
25
  "preprint",
23
26
  "working_paper",
@@ -342,20 +345,25 @@ def write_crossref_xml(metadata: Commonmeta) -> Optional[str]:
342
345
 
343
346
  data = convert_crossref_xml(metadata)
344
347
  if data is None:
348
+ logger.error(f"Could not convert metadata to Crossref XML: {metadata.id}")
345
349
  return None
350
+
351
+ # Use the marshmallow schema to dump the data
346
352
  schema = CrossrefXMLSchema()
347
353
  crossref_xml = schema.dump(data)
348
354
 
349
- # Ensure the order of fields in the XML matches the expected order
355
+ # Ensure consistent field ordering through the defined mapping
350
356
  field_order = [MARSHMALLOW_MAP.get(k, k) for k in list(data.keys())]
351
357
  crossref_xml = {k: crossref_xml[k] for k in field_order if k in crossref_xml}
358
+
352
359
  # Convert to XML
353
360
  return unparse_xml(crossref_xml, dialect="crossref")
354
361
 
355
362
 
356
- def write_crossref_xml_list(metalist):
363
+ def write_crossref_xml_list(metalist) -> Optional[str]:
357
364
  """Write crossref_xml list"""
358
365
  if metalist is None or not metalist.is_valid:
366
+ logger.error("Invalid metalist provided for Crossref XML generation")
359
367
  return None
360
368
 
361
369
  schema = CrossrefXMLSchema()
@@ -380,15 +388,17 @@ def push_crossref_xml_list(metalist, login_id: str, login_passwd: str) -> bytes:
380
388
  """Push crossref_xml list to Crossref API, returns the API response."""
381
389
 
382
390
  input = write_crossref_xml_list(metalist)
391
+ if not input:
392
+ logger.error("Failed to generate XML for upload")
393
+ return "{}"
383
394
 
384
395
  # Convert string to bytes if necessary
385
396
  if isinstance(input, str):
386
397
  input = input.encode("utf-8")
387
398
 
388
- # The filename displayed in the Crossref admin interface, using the current UNIX timestamp
399
+ # The filename displayed in the Crossref admin interface
389
400
  filename = f"{int(time())}"
390
401
 
391
- # Create multipart form data
392
402
  multipart_data = MultipartEncoder(
393
403
  fields={
394
404
  "fname": (filename, io.BytesIO(input), "application/xml"),
@@ -398,38 +408,32 @@ def push_crossref_xml_list(metalist, login_id: str, login_passwd: str) -> bytes:
398
408
  }
399
409
  )
400
410
 
401
- # Set up the request
402
411
  post_url = "https://doi.crossref.org/servlet/deposit"
403
412
  headers = {"Content-Type": multipart_data.content_type}
413
+ resp = requests.post(post_url, data=multipart_data, headers=headers, timeout=10)
414
+ resp.raise_for_status()
415
+
416
+ # Parse the response
417
+ response = parse_xml(resp.content)
418
+ status = py_.get(response, "html.body.h2")
419
+ if status == "SUCCESS":
420
+ items = []
421
+ for item in metalist.items:
422
+ items.append(
423
+ {
424
+ "doi": item.id,
425
+ "updated": datetime.now().isoformat("T", "seconds"),
426
+ "status": "submitted",
427
+ }
428
+ )
404
429
 
405
- try:
406
- # Send the request
407
- resp = requests.post(post_url, data=multipart_data, headers=headers, timeout=10)
408
- resp.raise_for_status()
409
-
410
- # Parse the response
411
- response = parse_xml(resp.content)
412
- status = py_.get(response, "html.body.h2")
413
- if status == "SUCCESS":
414
- items = []
415
- for item in metalist.items:
416
- items.append(
417
- {
418
- "doi": item.id,
419
- "updated": datetime.now().isoformat("T", "seconds"),
420
- "status": "submitted",
421
- }
422
- )
423
-
424
- # orjson has different options
425
- return json.dumps(items, option=json.OPT_INDENT_2)
426
-
427
- # if there is an error
428
- message = py_.get(response, "html.body.p")
429
- raise CrossrefError(f"Error uploading batch: {message}")
430
+ # Return JSON response
431
+ return json.dumps(items, option=json.OPT_INDENT_2)
430
432
 
431
- except requests.exceptions.RequestException as e:
432
- raise CrossrefError(f"Error uploading batch: {str(e)}") from e
433
+ # Handle error response
434
+ message = py_.get(response, "html.body.p")
435
+ logger.error(f"Crossref API error: {message}")
436
+ return "{}"
433
437
 
434
438
 
435
439
  def get_attributes(obj, **kwargs) -> dict:
@@ -539,15 +543,16 @@ def get_institution(obj) -> Optional[dict]:
539
543
  def get_titles(obj) -> Optional[dict]:
540
544
  """get titles"""
541
545
 
542
- title = {}
546
+ titles = {}
543
547
  for t in wrap(py_.get(obj, "titles", [])):
544
548
  if isinstance(t, str):
545
- title["title"] = t
549
+ titles["title"] = t
546
550
  elif isinstance(t, dict) and t.get("titleType", None) == "Subtitle":
547
- title["subtitle"] = t.get("title", None)
548
- elif isinstance(title, dict):
549
- title["title"] = t.get("title", None)
550
- return title
551
+ titles["subtitle"] = t.get("title", None)
552
+ elif isinstance(t, dict):
553
+ titles["title"] = t.get("title", None)
554
+
555
+ return titles if titles else None
551
556
 
552
557
 
553
558
  def get_contributors(obj) -> Optional[dict]:
@@ -3,7 +3,7 @@
3
3
  import logging
4
4
  import re
5
5
  from time import time
6
- from typing import Any, Dict, Optional, Tuple
6
+ from typing import Dict, Optional
7
7
  from urllib.parse import urlparse
8
8
 
9
9
  import orjson as json
@@ -392,11 +392,11 @@ def push_inveniordm(
392
392
  host: str,
393
393
  token: str,
394
394
  legacy_key: str
395
- ) -> Tuple[Dict[str, Any], Optional[Exception]]:
395
+ ) -> Dict:
396
396
  """Push record to InvenioRDM"""
397
397
 
398
398
  record = {}
399
- input = write_inveniordm(metadata)
399
+ output = write_inveniordm(metadata)
400
400
 
401
401
  try:
402
402
  # Remove IsPartOf relation with InvenioRDM community identifier after storing it
@@ -439,10 +439,10 @@ def push_inveniordm(
439
439
  record = edit_published_record(record, host, token)
440
440
 
441
441
  # Update draft record
442
- record = update_draft_record(record, host, token, input)
442
+ record = update_draft_record(record, host, token, output)
443
443
  else:
444
444
  # Create draft record
445
- record = create_draft_record(record, host, token, input)
445
+ record = create_draft_record(record, host, token, output)
446
446
 
447
447
  # Publish draft record
448
448
  record = publish_draft_record(record, host, token)
@@ -525,7 +525,7 @@ def search_by_doi(doi, host, token) -> Optional[str]:
525
525
  return None
526
526
 
527
527
 
528
- def create_draft_record(record, host, token, input):
528
+ def create_draft_record(record, host, token, output):
529
529
  """Create a new draft record in InvenioRDM"""
530
530
  headers = {
531
531
  "Authorization": f"Bearer {token}",
@@ -533,7 +533,7 @@ def create_draft_record(record, host, token, input):
533
533
  }
534
534
  try:
535
535
  response = requests.post(
536
- f"https://{host}/api/records", headers=headers, json=input
536
+ f"https://{host}/api/records", headers=headers, json=output
537
537
  )
538
538
  if response.status_code == 429:
539
539
  record["status"] = "failed_rate_limited"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commonmeta-py
3
- Version: 0.118
3
+ Version: 0.119
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
@@ -36,7 +36,7 @@ Requires-Dist: pyyaml>=5.4
36
36
  Requires-Dist: requests-toolbelt>=1.0.0
37
37
  Requires-Dist: requests>=2.31.0
38
38
  Requires-Dist: requests>=2.32.3
39
- Requires-Dist: setuptools>=70.3.0
39
+ Requires-Dist: setuptools<81,>=70.3.0
40
40
  Requires-Dist: simplejson~=3.18
41
41
  Requires-Dist: types-beautifulsoup4<5,>=4.11
42
42
  Requires-Dist: types-dateparser~=1.1
@@ -1,12 +1,12 @@
1
- commonmeta/__init__.py,sha256=RbBIG26IxRjdvGzHH77sogy-rtAEHC51It7TQ6wPjS0,2098
1
+ commonmeta/__init__.py,sha256=V-Y0A2HSjqUF0gh_o1M-VRLMSbZAFWTSfKunBeX02UE,2098
2
2
  commonmeta/api_utils.py,sha256=y5KLfIOWOjde7LXZ36u-eneQJ-Q53yXUZg3hWpCBS2E,2685
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
6
6
  commonmeta/constants.py,sha256=wSTEUiHeRdXLwjXEQD9AU2hxFyEKi5OTX2iHOKO6nF0,19844
7
7
  commonmeta/date_utils.py,sha256=H2cCobX0JREIUOT_cCigGd3MG7prGiQpXk1m4ZNrFwU,6318
8
- commonmeta/doi_utils.py,sha256=kS9wBoZQHvV-fqFoW9j-_aN_7Kj1I6sQdqnqK3Nno0M,11512
9
- commonmeta/file_utils.py,sha256=tGvXxScjh-PPo5YvLDyk4sqwY5Q50N0zAmBHVaUOLeU,3268
8
+ commonmeta/doi_utils.py,sha256=cOogLatKg6qea2jgMd3yLALSTfaTNUgr-IkBXIK4xZw,11498
9
+ commonmeta/file_utils.py,sha256=eFYDWyR8Gr722nvFmp542hCm-TGmO_q4ciZ85IPHpjA,2893
10
10
  commonmeta/metadata.py,sha256=U9uYOkKAdgqLiQwQKXMXjrAFvO6avMWp9heJG4cNAAY,18893
11
11
  commonmeta/schema_utils.py,sha256=WGpmMj9cfNMg_55hhgwY9qpO0A1HSvTLQC2equjBftI,1770
12
12
  commonmeta/translators.py,sha256=CBMK4jrXRmGZiAhCh6wsJjhbDJWbcsda8UvXFXxccAw,1363
@@ -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=d-Rb2Vd_g3UW8GM4APIT7fivSQ5GMssZ6Ubi3OykHaw,33479
80
+ commonmeta/writers/crossref_xml_writer.py,sha256=1zEln2yJwcY62jFde5X-6BHWfnKOqadRRjx5z8Nt5CY,33540
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=el4Eapa9HHg1ceddHb9rd4d1MAsM1ZBZ1F4jQUV8mbI,25717
83
+ commonmeta/writers/inveniordm_writer.py,sha256=mmCCxNi1OVSALeWdw-1g0ZzB84GGOcH3r7va3Abj9W4,25672
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.118.dist-info/METADATA,sha256=BD1a2ouuh4hYizCty5tBGQuQM3m6LWVf1zfmTQwnFPU,7652
87
- commonmeta_py-0.118.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
88
- commonmeta_py-0.118.dist-info/entry_points.txt,sha256=U4w4BoRuS3rN5t5Y-uYSyOeU5Lh_VRVMS9OIDzIgw4w,50
89
- commonmeta_py-0.118.dist-info/licenses/LICENSE,sha256=wsIvxF9Q9GC9vA_s79zTWP3BkXJdfUNRmALlU8GbW1s,1074
90
- commonmeta_py-0.118.dist-info/RECORD,,
86
+ commonmeta_py-0.119.dist-info/METADATA,sha256=Jq7ojg3cdi-sxDsoujwL8SzWLGzulwv9rFIZole3yJ4,7656
87
+ commonmeta_py-0.119.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
88
+ commonmeta_py-0.119.dist-info/entry_points.txt,sha256=U4w4BoRuS3rN5t5Y-uYSyOeU5Lh_VRVMS9OIDzIgw4w,50
89
+ commonmeta_py-0.119.dist-info/licenses/LICENSE,sha256=wsIvxF9Q9GC9vA_s79zTWP3BkXJdfUNRmALlU8GbW1s,1074
90
+ commonmeta_py-0.119.dist-info/RECORD,,