caltechdata-api 1.8.1__tar.gz → 1.9.0__tar.gz

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 (21) hide show
  1. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/PKG-INFO +3 -1
  2. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/README.md +2 -0
  3. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/caltechdata_write.py +4 -1
  4. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/cli.py +7 -3
  5. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/customize_schema.py +171 -42
  6. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/get_metadata.py +29 -8
  7. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api.egg-info/PKG-INFO +3 -1
  8. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/LICENSE +0 -0
  9. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/__init__.py +0 -0
  10. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/caltechdata_edit.py +0 -0
  11. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/download_file.py +0 -0
  12. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/get_files.py +0 -0
  13. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/md_to_json.py +0 -0
  14. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api/utils.py +0 -0
  15. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api.egg-info/SOURCES.txt +0 -0
  16. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api.egg-info/dependency_links.txt +0 -0
  17. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api.egg-info/entry_points.txt +0 -0
  18. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api.egg-info/requires.txt +0 -0
  19. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/caltechdata_api.egg-info/top_level.txt +0 -0
  20. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/setup.cfg +0 -0
  21. {caltechdata_api-1.8.1 → caltechdata_api-1.9.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: caltechdata_api
3
- Version: 1.8.1
3
+ Version: 1.9.0
4
4
  Summary: Python wrapper for CaltechDATA API.
5
5
  Home-page: https://github.com/caltechlibrary/caltechdata_api
6
6
  Author: Thomas E Morrell, Bhattarai Rohan , Won Elizabeth
@@ -20,6 +20,8 @@ License-File: LICENSE
20
20
 
21
21
  # CaltechDATA API Python Library
22
22
 
23
+ [![DOI](https://img.shields.io/badge/dynamic/json.svg?label=DOI&query=$.pids.doi.identifier&uri=https://data.caltech.edu/api/records/wfjr5-kw507/versions/latest)](https://data.caltech.edu/records/wfjr5-kw507/latest)
24
+
23
25
  The `caltechdata_api` Python library provides a convenient interface for interacting with the CaltechDATA API. It allows users to write files, create DataCite 4 standard JSON records, edit existing records, and retrieve metadata from the CaltechDATA repository.
24
26
 
25
27
  ## Features
@@ -1,5 +1,7 @@
1
1
  # CaltechDATA API Python Library
2
2
 
3
+ [![DOI](https://img.shields.io/badge/dynamic/json.svg?label=DOI&query=$.pids.doi.identifier&uri=https://data.caltech.edu/api/records/wfjr5-kw507/versions/latest)](https://data.caltech.edu/records/wfjr5-kw507/latest)
4
+
3
5
  The `caltechdata_api` Python library provides a convenient interface for interacting with the CaltechDATA API. It allows users to write files, create DataCite 4 standard JSON records, edit existing records, and retrieve metadata from the CaltechDATA repository.
4
6
 
5
7
  ## Features
@@ -248,7 +248,10 @@ def caltechdata_write(
248
248
  # Make draft and publish
249
249
  result = requests.post(url + "/api/records", headers=headers, json=data)
250
250
  if result.status_code != 201:
251
- raise Exception(result.text)
251
+ if result.status_code == 400 and "Referer checking failed" in result.text:
252
+ raise Exception("Token is incorrect or missing referer.")
253
+ else:
254
+ raise Exception(result.text)
252
255
  idv = result.json()["id"]
253
256
  publish_link = result.json()["links"]["publish"]
254
257
 
@@ -2,7 +2,7 @@ import argparse
2
2
  import requests
3
3
  import s3fs
4
4
  from caltechdata_api import caltechdata_write, caltechdata_edit
5
- from .md_to_json import parse_readme_to_json
5
+ from md_to_json import parse_readme_to_json
6
6
  import json
7
7
  import os
8
8
  from cryptography.fernet import Fernet
@@ -71,6 +71,11 @@ def get_or_set_token(production=True):
71
71
  with open(token_file, "rb") as f:
72
72
  encrypted_token = f.read()
73
73
  token = decrypt_token(encrypted_token, key)
74
+ print(
75
+ "Using saved CaltechDATA production token."
76
+ if production
77
+ else "Using saved CaltechDATA test token."
78
+ )
74
79
  return token
75
80
  except FileNotFoundError:
76
81
  while True:
@@ -422,8 +427,6 @@ def main():
422
427
 
423
428
  def create_record(production):
424
429
  token = get_or_set_token(production)
425
- # keep_file = input("Do you want to keep your existing files? (yes/no): ").lower() == "yes"
426
- print("Using CaltechDATA token:", token)
427
430
  while True:
428
431
  choice = get_user_input(
429
432
  "Do you want to use metadata from an existing file or create new metadata? (existing/create): "
@@ -466,6 +469,7 @@ def create_record(production):
466
469
  "descriptions": [
467
470
  {"description": args["description"], "descriptionType": "Abstract"}
468
471
  ],
472
+ "publisher": "CaltechDATA",
469
473
  "creators": [
470
474
  {
471
475
  "affiliation": [
@@ -1,5 +1,4 @@
1
- # Convert a DataCite 4 or 4.3 standard schema json record to the customized internal
2
- # schema used by TIND in CaltechDATA
1
+ # Convert a DataCite 4.3 standard schema json record to the InvenioRDM schema
3
2
  import argparse
4
3
  import json
5
4
  from datetime import date
@@ -81,7 +80,7 @@ def rdm_creators_contributors(person_list, peopleroles):
81
80
  else:
82
81
  print(f"Name type {ntype} not known")
83
82
  else:
84
- # We default to organizational if not known
83
+ # We default to personal if not known
85
84
  cre["type"] = "personal"
86
85
  change_label(cre, "givenName", "given_name")
87
86
  change_label(cre, "familyName", "family_name")
@@ -253,7 +252,7 @@ def customize_schema_rdm(json_record):
253
252
 
254
253
  if "identifiers" in json_record:
255
254
  identifiers = []
256
- system_pids = ["DOI", "oai"]
255
+ system_pids = ["oai"]
257
256
  for identifier in json_record["identifiers"]:
258
257
  if identifier["identifierType"] not in system_pids:
259
258
  identifier["scheme"] = identifiertypes[identifier.pop("identifierType")]
@@ -394,20 +393,12 @@ def validate_metadata(json_record):
394
393
  """
395
394
  errors = []
396
395
 
397
- # Check for 'types' and 'resourceTypeGeneral'
398
- if "types" not in json_record:
399
- errors.append("'types' field is missing.")
400
- elif not isinstance(json_record["types"], dict):
401
- errors.append("'types' field should be a dictionary.")
402
- elif "resourceTypeGeneral" not in json_record["types"]:
403
- errors.append("'resourceTypeGeneral' field is missing in 'types'.")
404
-
405
- # Check for 'title'
406
396
  if "titles" not in json_record:
407
397
  errors.append("'titles' field is missing.")
408
398
  elif not isinstance(json_record["titles"], list) or len(json_record["titles"]) == 0:
409
399
  errors.append("'titles' should be a non-empty list.")
410
400
  else:
401
+
411
402
  # Ensure each title is a dictionary with 'title' field
412
403
  for title in json_record["titles"]:
413
404
  if not isinstance(title, dict) or "title" not in title:
@@ -417,32 +408,6 @@ def validate_metadata(json_record):
417
408
 
418
409
  # Publication date is handled by customize function
419
410
 
420
- # Check for 'creators'
421
- if "creators" not in json_record:
422
- errors.append("'creators' field is missing.")
423
- elif (
424
- not isinstance(json_record["creators"], list)
425
- or len(json_record["creators"]) == 0
426
- ):
427
- errors.append("'creators' should be a non-empty list.")
428
- else:
429
- for creator in json_record["creators"]:
430
- if not isinstance(creator, dict) or "name" not in creator:
431
- errors.append(
432
- "Each creator in 'creators' must be a dictionary with a 'name' key."
433
- )
434
-
435
- # Check for 'contributors'
436
- if "contributors" in json_record:
437
- if not isinstance(json_record["contributors"], list):
438
- errors.append("'contributors' should be a list.")
439
- else:
440
- for contributor in json_record["contributors"]:
441
- if not isinstance(contributor, dict) or "name" not in contributor:
442
- errors.append(
443
- "Each contributor must be a dictionary with a 'name' key."
444
- )
445
-
446
411
  # Check for 'resourceType'
447
412
  if "resourceType" not in json_record["types"]:
448
413
  errors.append("'resourceType' field is missing in 'types'.")
@@ -481,6 +446,7 @@ def validate_metadata(json_record):
481
446
  errors.append("'relatedIdentifiers' should be a list.")
482
447
  else:
483
448
  for related_id in json_record["relatedIdentifiers"]:
449
+
484
450
  if (
485
451
  not isinstance(related_id, dict)
486
452
  or "relatedIdentifier" not in related_id
@@ -494,17 +460,180 @@ def validate_metadata(json_record):
494
460
  if not isinstance(json_record["rightsList"], list):
495
461
  errors.append("'rightsList' should be a list.")
496
462
  else:
497
- for rights in json_record["rightsList"]:
498
- if not isinstance(rights, dict) or "rights" not in rights:
463
+
464
+ for right in json_record["rightsList"]:
465
+ if not isinstance(right, dict) or "rights" not in right:
466
+ errors.append("Each 'rightsList' entry must have 'rights'.")
467
+ if "rightsURI" in right and not isinstance(right["rightsURI"], str):
468
+ errors.append("'rightsURI' should be a string.")
469
+
470
+ # Check for 'subjects'
471
+ if "subjects" in json_record:
472
+ if not isinstance(json_record["subjects"], list):
473
+ errors.append("'subjects' should be a list.")
474
+ else:
475
+ for subject in json_record["subjects"]:
476
+ if not isinstance(subject, dict) or "subject" not in subject:
477
+ errors.append("Each 'subject' must have a 'subject' key.")
478
+
479
+ # Check for 'dates'
480
+ if "dates" not in json_record:
481
+ errors.append("'dates' field is missing.")
482
+ elif not isinstance(json_record["dates"], list) or len(json_record["dates"]) == 0:
483
+ errors.append("'dates' should be a non-empty list.")
484
+ else:
485
+ for date in json_record["dates"]:
486
+ if (
487
+ not isinstance(date, dict)
488
+ or "date" not in date
489
+ or "dateType" not in date
490
+ ):
491
+ errors.append("Each 'date' must have 'date' and 'dateType'.")
492
+
493
+ # Check for 'identifiers'
494
+ if "identifiers" not in json_record:
495
+ errors.append("'identifiers' field is missing.")
496
+ elif (
497
+ not isinstance(json_record["identifiers"], list)
498
+ or len(json_record["identifiers"]) == 0
499
+ ):
500
+ errors.append("'identifiers' should be a non-empty list.")
501
+ else:
502
+ for identifier in json_record["identifiers"]:
503
+ if (
504
+ not isinstance(identifier, dict)
505
+ or "identifier" not in identifier
506
+ or "identifierType" not in identifier
507
+ ):
508
+ errors.append(
509
+ "Each 'identifier' must have 'identifier' and 'identifierType'."
510
+ )
511
+
512
+ # Check for 'creators'
513
+ if "creators" not in json_record:
514
+ errors.append("'creators' field is missing.")
515
+ elif (
516
+ not isinstance(json_record["creators"], list)
517
+ or len(json_record["creators"]) == 0
518
+ ):
519
+ errors.append("'creators' should be a non-empty list.")
520
+ else:
521
+ for creator in json_record["creators"]:
522
+ if not isinstance(creator, dict):
523
+ errors.append("Each 'creator' must be a dictionry")
524
+ if "nameType" in creator:
525
+ if creator["nameType"] == "Organizational":
526
+ if "name" not in creator:
527
+ errors.append("Each organizational 'creator' must have 'name'.")
528
+ else:
529
+ if "familyName" not in creator:
499
530
  errors.append(
500
- "Each entry in 'rightsList' must be a dictionary with a 'rights' key."
531
+ "Each 'creator' must have a 'familyName' or have type Organizational"
501
532
  )
533
+ if "affiliation" in creator:
534
+ if not isinstance(creator["affiliation"], list):
535
+ errors.append("'affiliation' in 'creators' should be a list.")
536
+ for affiliation in creator["affiliation"]:
537
+ if not isinstance(affiliation, dict) or "name" not in affiliation:
538
+ errors.append(
539
+ "Each 'affiliation' in 'creators' must have a 'name'."
540
+ )
541
+
542
+ # Check for 'contributors'
543
+ if "contributors" in json_record:
544
+ if (
545
+ not isinstance(json_record["contributors"], list)
546
+ or len(json_record["contributors"]) == 0
547
+ ):
548
+ errors.append("'creators' should be a non-empty list.")
549
+ else:
550
+ for contributor in json_record["contributors"]:
551
+ if not isinstance(contributor, dict):
552
+ errors.append("Each 'contributor' must be a dictionry")
553
+ if "nameType" in contributor:
554
+ if contributor["nameType"] == "Organizational":
555
+ if "name" not in creator:
556
+ errors.append(
557
+ "Each organizational 'contributor' must have 'name'."
558
+ )
559
+ else:
560
+ if "familyName" not in contributor:
561
+ errors.append(
562
+ "Each 'contributor' must have a 'familyName' or have type Organizational"
563
+ )
564
+ if "affiliation" in contributor:
565
+ if not isinstance(contributor["affiliation"], list):
566
+ errors.append(
567
+ "'affiliation' in 'contributors' should be a list."
568
+ )
569
+ for affiliation in contributor["affiliation"]:
570
+ if (
571
+ not isinstance(affiliation, dict)
572
+ or "name" not in affiliation
573
+ ):
574
+ errors.append(
575
+ "Each 'affiliation' in 'contributors' must have a 'name'."
576
+ )
502
577
 
503
578
  # Check for 'geoLocations'
504
579
  if "geoLocations" in json_record:
505
580
  if not isinstance(json_record["geoLocations"], list):
506
581
  errors.append("'geoLocations' should be a list.")
507
582
  else:
583
+
584
+ for geo_loc in json_record["geoLocations"]:
585
+ if not isinstance(geo_loc, dict) or "geoLocationPlace" not in geo_loc:
586
+ errors.append("Each 'geoLocation' must have 'geoLocationPlace'.")
587
+ if "geoLocationPoint" in geo_loc:
588
+ point = geo_loc["geoLocationPoint"]
589
+ if (
590
+ not isinstance(point, dict)
591
+ or "pointLatitude" not in point
592
+ or "pointLongitude" not in point
593
+ ):
594
+ errors.append(
595
+ "'geoLocationPoint' must have 'pointLatitude' and 'pointLongitude'."
596
+ )
597
+
598
+ # Check for 'formats'
599
+ if "formats" in json_record and (
600
+ not isinstance(json_record["formats"], list) or len(json_record["formats"]) == 0
601
+ ):
602
+ errors.append("'formats' should be a non-empty list.")
603
+
604
+ # Check for 'language'
605
+ if "language" in json_record:
606
+ if not isinstance(json_record["language"], str):
607
+ errors.append("'language' should be a string.")
608
+
609
+ # Check for 'version'
610
+ if "version" in json_record and not isinstance(json_record["version"], str):
611
+ errors.append("'version' should be a string.")
612
+
613
+ # Check for 'publisher'
614
+ if "publisher" not in json_record:
615
+ errors.append("'publisher' field is missing.")
616
+ elif not isinstance(json_record["publisher"], str):
617
+ errors.append("'publisher' should be a string.")
618
+
619
+ # Check for 'publicationYear'
620
+ if "publicationYear" not in json_record:
621
+ errors.append("'publicationYear' field is missing.")
622
+ elif not isinstance(json_record["publicationYear"], str):
623
+ errors.append("'publicationYear' should be a string.")
624
+
625
+ # Check for 'types'
626
+ if "types" not in json_record:
627
+ errors.append("'types' field is missing.")
628
+ elif not isinstance(json_record["types"], dict):
629
+ errors.append("'types' should be a dictionary.")
630
+ else:
631
+ if "resourceTypeGeneral" not in json_record["types"]:
632
+ errors.append("'types' must have 'resourceTypeGeneral'.")
633
+ if "resourceType" in json_record["types"] and not isinstance(
634
+ json_record["types"]["resourceType"], str
635
+ ):
636
+ errors.append("'resourceType' should be a string if provided.")
508
637
  for location in json_record["geoLocations"]:
509
638
  if not isinstance(location, dict):
510
639
  errors.append("Each entry in 'geoLocations' must be a dictionary.")
@@ -8,26 +8,43 @@ from datacite import schema43
8
8
 
9
9
 
10
10
  def get_metadata(
11
- idv, production=True, validate=True, emails=False, schema="43", token=False
11
+ idv,
12
+ production=True,
13
+ validate=True,
14
+ emails=False,
15
+ schema="43",
16
+ token=False,
17
+ authors=False,
12
18
  ):
13
19
  # Returns just DataCite metadata or DataCite metadata with emails
14
20
 
15
21
  if production == True:
16
- url = "https://data.caltech.edu/api/records/"
22
+ if authors:
23
+ url = "https://authors.library.caltech.edu/api/records/"
24
+ else:
25
+ url = "https://data.caltech.edu/api/records/"
17
26
  verify = True
18
27
  else:
19
- url = "https://data.caltechlibrary.dev/api/records/"
28
+ if authors:
29
+ url = "https://authors.caltechlibrary.dev/api/records/"
30
+ else:
31
+ url = "https://data.caltechlibrary.dev/api/records/"
20
32
  verify = True
21
33
 
22
- headers = {
23
- "accept": "application/vnd.datacite.datacite+json",
24
- }
34
+ if authors:
35
+ headers = {
36
+ "accept": "application/json",
37
+ }
38
+ validate = False
39
+ else:
40
+ headers = {
41
+ "accept": "application/vnd.datacite.datacite+json",
42
+ }
25
43
 
26
44
  if token:
27
45
  headers["Authorization"] = "Bearer %s" % token
28
46
 
29
47
  response = requests.get(url + idv, headers=headers, verify=verify)
30
- print(response.headers)
31
48
  if response.status_code != 200:
32
49
  raise Exception(response.text)
33
50
  else:
@@ -59,6 +76,7 @@ if __name__ == "__main__":
59
76
  help="The CaltechDATA ID for each record of interest",
60
77
  )
61
78
  parser.add_argument("-test", dest="production", action="store_false")
79
+ parser.add_argument("-authors", dest="authors", action="store_true")
62
80
  parser.add_argument("-xml", dest="save_xml", action="store_true")
63
81
  parser.add_argument(
64
82
  "-skip_validate",
@@ -72,6 +90,7 @@ if __name__ == "__main__":
72
90
 
73
91
  production = args.production
74
92
  schema = args.schema
93
+ authors = args.authors
75
94
  skip_validate = args.skip_validate
76
95
  if skip_validate:
77
96
  validate = False
@@ -79,7 +98,9 @@ if __name__ == "__main__":
79
98
  validate = True
80
99
 
81
100
  for idv in args.ids:
82
- metadata = get_metadata(idv, production, validate, schema)
101
+ metadata = get_metadata(
102
+ idv, production, validate, schema=schema, authors=authors
103
+ )
83
104
  outfile = open(str(idv) + ".json", "w")
84
105
  outfile.write(json.dumps(metadata, indent=4))
85
106
  outfile.close()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: caltechdata-api
3
- Version: 1.8.1
3
+ Version: 1.9.0
4
4
  Summary: Python wrapper for CaltechDATA API.
5
5
  Home-page: https://github.com/caltechlibrary/caltechdata_api
6
6
  Author: Thomas E Morrell, Bhattarai Rohan , Won Elizabeth
@@ -20,6 +20,8 @@ License-File: LICENSE
20
20
 
21
21
  # CaltechDATA API Python Library
22
22
 
23
+ [![DOI](https://img.shields.io/badge/dynamic/json.svg?label=DOI&query=$.pids.doi.identifier&uri=https://data.caltech.edu/api/records/wfjr5-kw507/versions/latest)](https://data.caltech.edu/records/wfjr5-kw507/latest)
24
+
23
25
  The `caltechdata_api` Python library provides a convenient interface for interacting with the CaltechDATA API. It allows users to write files, create DataCite 4 standard JSON records, edit existing records, and retrieve metadata from the CaltechDATA repository.
24
26
 
25
27
  ## Features
File without changes