caltechdata-api 1.7.0__tar.gz → 1.8.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.7.0 → caltechdata_api-1.8.0}/PKG-INFO +2 -3
  2. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/README.md +1 -2
  3. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/caltechdata_edit.py +11 -0
  4. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/caltechdata_write.py +3 -5
  5. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/cli.py +121 -54
  6. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/customize_schema.py +165 -1
  7. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api.egg-info/PKG-INFO +2 -3
  8. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/LICENSE +0 -0
  9. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/__init__.py +0 -0
  10. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/download_file.py +0 -0
  11. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/get_files.py +0 -0
  12. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/get_metadata.py +0 -0
  13. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/md_to_json.py +0 -0
  14. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api/utils.py +0 -0
  15. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api.egg-info/SOURCES.txt +0 -0
  16. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api.egg-info/dependency_links.txt +0 -0
  17. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api.egg-info/entry_points.txt +0 -0
  18. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api.egg-info/requires.txt +0 -0
  19. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/caltechdata_api.egg-info/top_level.txt +0 -0
  20. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/setup.cfg +0 -0
  21. {caltechdata_api-1.7.0 → caltechdata_api-1.8.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: caltechdata_api
3
- Version: 1.7.0
3
+ Version: 1.8.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
@@ -33,8 +33,7 @@ The `caltechdata_api` Python library provides a convenient interface for interac
33
33
 
34
34
  ## Requirements
35
35
 
36
- - Python 3 (Anaconda is recommended)
37
- - `requests` library
36
+ - Python 3.6+
38
37
 
39
38
  ## Installation
40
39
 
@@ -13,8 +13,7 @@ The `caltechdata_api` Python library provides a convenient interface for interac
13
13
 
14
14
  ## Requirements
15
15
 
16
- - Python 3 (Anaconda is recommended)
17
- - `requests` library
16
+ - Python 3.6+
18
17
 
19
18
  ## Installation
20
19
 
@@ -211,6 +211,17 @@ def caltechdata_edit(
211
211
  "provider": "datacite",
212
212
  "client": "datacite",
213
213
  }
214
+ # If we are making a new version, we only want to look for custom DOIs
215
+ if version == True and "identifiers" in metadata:
216
+ for identifier in metadata["identifiers"]:
217
+ if identifier["identifierType"] == "DOI":
218
+ doi = identifier["identifier"]
219
+ prefix = doi.split("/")[0]
220
+ if prefix != repo_prefix:
221
+ pids["doi"] = {
222
+ "identifier": doi,
223
+ "provider": "external",
224
+ }
214
225
 
215
226
  # If no metadata is provided, use existing. Otherwise customize provided
216
227
  # metadata
@@ -1,7 +1,7 @@
1
1
  import copy
2
2
  import json
3
- import os, requests
4
-
3
+ import os
4
+ import requests
5
5
  import s3fs
6
6
  from requests import session
7
7
  from json.decoder import JSONDecodeError
@@ -49,8 +49,6 @@ def write_files_rdm(files, file_link, headers, f_headers, s3=None, keepfiles=Fal
49
49
  infile = open(name, "rb")
50
50
  else:
51
51
  infile = open(f_list[name], "rb")
52
- # size = infile.seek(0, 2)
53
- # infile.seek(0, 0) # reset at beginning
54
52
  result = requests.put(link, headers=f_headers, data=infile)
55
53
  if result.status_code != 200:
56
54
  raise Exception(result.text)
@@ -68,7 +66,7 @@ def write_files_rdm(files, file_link, headers, f_headers, s3=None, keepfiles=Fal
68
66
  def add_file_links(
69
67
  metadata, file_links, file_descriptions=[], additional_descriptions="", s3_link=None
70
68
  ):
71
- # Currently configured for S3 links, assuming all are at same endpoint
69
+ # Currently configured for S3 links, assuming all are at the same endpoint
72
70
  link_string = ""
73
71
  endpoint = "https://" + file_links[0].split("/")[2]
74
72
  s3 = s3fs.S3FileSystem(anon=True, client_kwargs={"endpoint_url": endpoint})
@@ -59,11 +59,14 @@ def decrypt_token(encrypted_token, key):
59
59
  return f.decrypt(encrypted_token).decode()
60
60
 
61
61
 
62
- # Function to get or set token
63
- def get_or_set_token():
64
-
62
+ # Function to get or set token with support for test system
63
+ def get_or_set_token(production=True):
65
64
  key = load_or_generate_key()
66
- token_file = os.path.join(caltechdata_directory, "token.txt")
65
+
66
+ # Use different token files for production and test environments
67
+ token_filename = "token.txt" if production else "token_test.txt"
68
+ token_file = os.path.join(caltechdata_directory, token_filename)
69
+
67
70
  try:
68
71
  with open(token_file, "rb") as f:
69
72
  encrypted_token = f.read()
@@ -71,8 +74,12 @@ def get_or_set_token():
71
74
  return token
72
75
  except FileNotFoundError:
73
76
  while True:
74
- token = input("Enter your CaltechDATA token: ").strip()
75
- confirm_token = input("Confirm your CaltechDATA token: ").strip()
77
+ token = input(
78
+ f"Enter your {'Production' if production else 'Test'} CaltechDATA token: "
79
+ ).strip()
80
+ confirm_token = input(
81
+ f"Confirm your {'Production' if production else 'Test'} CaltechDATA token: "
82
+ ).strip()
76
83
  if token == confirm_token:
77
84
  encrypted_token = encrypt_token(token, key)
78
85
  with open(token_file, "wb") as f:
@@ -254,7 +261,7 @@ def get_names(orcid):
254
261
 
255
262
  def write_s3cmd_config(access_key, secret_key, endpoint):
256
263
  configf = os.path.join(home_directory, ".s3cfg")
257
- if not os.path.exists(key_file):
264
+ if not os.path.exists(configf):
258
265
  with open(configf, "w") as file:
259
266
  file.write(
260
267
  f"""[default]
@@ -376,20 +383,35 @@ def upload_data_from_file():
376
383
  print(f"Error: Invalid JSON format in the file '{filename}'. {str(e)}")
377
384
 
378
385
 
386
+ def parse_args():
387
+ """Parse command-line arguments."""
388
+ parser = argparse.ArgumentParser(description="CaltechDATA CLI tool.")
389
+ parser.add_argument(
390
+ "-test", action="store_true", help="Use test mode, sets production to False"
391
+ )
392
+ args = parser.parse_args()
393
+ return args
394
+
395
+
379
396
  def main():
397
+ args = parse_args()
398
+
399
+ production = not args.test # Set production to False if -test flag is provided
400
+
380
401
  choice = get_user_input(
381
402
  "Do you want to create or edit a CaltechDATA record? (create/edit): "
382
403
  ).lower()
383
404
  if choice == "create":
384
- create_record()
405
+ create_record(production)
385
406
  elif choice == "edit":
386
- edit_record()
407
+ edit_record(production)
387
408
  else:
388
409
  print("Invalid choice. Please enter 'create' or 'edit'.")
389
410
 
390
411
 
391
- def create_record():
392
- token = get_or_set_token()
412
+ def create_record(production):
413
+ token = get_or_set_token(production)
414
+ # keep_file = input("Do you want to keep your existing files? (yes/no): ").lower() == "yes"
393
415
  print("Using CaltechDATA token:", token)
394
416
  while True:
395
417
  choice = get_user_input(
@@ -401,7 +423,11 @@ def create_record():
401
423
  if existing_data:
402
424
  if filepath != "":
403
425
  response = caltechdata_write(
404
- existing_data, token, filepath, production=True, publish=False
426
+ existing_data,
427
+ token,
428
+ filepath,
429
+ production=production,
430
+ publish=False,
405
431
  )
406
432
  elif file_link != "":
407
433
  response = caltechdata_write(
@@ -414,15 +440,10 @@ def create_record():
414
440
  )
415
441
  else:
416
442
  response = caltechdata_write(
417
- existing_data, token, production=True, publish=False
443
+ existing_data, token, production=production, publish=False
418
444
  )
419
445
  rec_id = response
420
- print(
421
- f"""You can view and publish this record at
422
- https://data.caltech.edu/uploads/{rec_id}
423
- If you need to upload large files to S3, you can type
424
- `s3cmd put DATA_FILE s3://ini230004-bucket01/{rec_id}/"""
425
- )
446
+ print_upload_message(rec_id, production)
426
447
  break
427
448
  else:
428
449
  print("Going back to the main menu.")
@@ -468,27 +489,23 @@ def create_record():
468
489
  if confirm_upload():
469
490
  if filepath != "":
470
491
  response = caltechdata_write(
471
- metadata, token, filepath, production=True, publish=False
492
+ metadata, token, filepath, production=production, publish=False
472
493
  )
473
494
  elif file_link != "":
474
495
  response = caltechdata_write(
475
496
  metadata,
476
497
  token,
477
498
  file_links=[file_link],
478
- production=True,
499
+ production=production,
479
500
  publish=False,
480
501
  )
481
502
  else:
482
503
  response = caltechdata_write(
483
- metadata, token, production=True, publish=False
504
+ metadata, token, production=production, publish=False
484
505
  )
485
506
  rec_id = response
486
- print(
487
- f"""You can view and publish this record at
488
- https://data.caltech.edu/uploads/{rec_id}
489
- If you need to upload large files to S3, you can type
490
- `s3cmd put DATA_FILE s3://ini230004-bucket01/{rec_id}/"""
491
- )
507
+
508
+ print_upload_message(rec_id, production)
492
509
  with open(response + ".json", "w") as file:
493
510
  json.dump(metadata, file, indent=2)
494
511
  break
@@ -498,17 +515,32 @@ def create_record():
498
515
  print("Invalid choice. Please enter 'existing' or 'create'.")
499
516
 
500
517
 
501
- def edit_record():
518
+ def print_upload_message(rec_id, production):
519
+ base_url = (
520
+ "https://data.caltech.edu/uploads/"
521
+ if production
522
+ else "https://data.caltechlibrary.dev/uploads/"
523
+ )
524
+ print(
525
+ f"""You can view and publish this record at
526
+ {base_url}{rec_id}
527
+ If you need to upload large files to S3, you can type
528
+ `s3cmd put DATA_FILE s3://ini230004-bucket01/{rec_id}/`"""
529
+ )
530
+
531
+
532
+ def edit_record(production):
502
533
  record_id = input("Enter the CaltechDATA record ID: ")
503
- token = get_or_set_token()
534
+ token = get_or_set_token(production)
504
535
  file_name = download_file_by_id(record_id, token)
536
+
505
537
  if file_name:
506
538
  try:
507
539
  # Read the edited metadata file
508
540
  with open(file_name, "r") as file:
509
541
  metadata = json.load(file)
510
542
  response = caltechdata_edit(
511
- record_id, metadata, token, production=True, publish=False
543
+ record_id, metadata, token, production=production, publish=False
512
544
  )
513
545
  if response:
514
546
  print("Metadata edited successfully.")
@@ -520,31 +552,54 @@ def edit_record():
520
552
  print("No metadata file found.")
521
553
  choice = get_user_input("Do you want to add files? (y/n): ").lower()
522
554
  if choice == "y":
523
- API_URL_TEMPLATE = "https://data.caltech.edu/api/records/{record_id}/files"
555
+ if production:
556
+ API_URL_TEMPLATE = "https://data.caltech.edu/api/records/{record_id}/files"
557
+ API_URL_TEMPLATE_DRAFT = (
558
+ "https://data.caltech.edu/api/records/{record_id}/draft/files"
559
+ )
560
+ else:
561
+ API_URL_TEMPLATE = (
562
+ "https://data.caltechlibrary.dev/api/records/{record_id}/files"
563
+ )
564
+ API_URL_TEMPLATE_DRAFT = (
565
+ "https://data.caltechlibrary.dev/api/records/{record_id}/draft/files"
566
+ )
567
+
524
568
  url = API_URL_TEMPLATE.format(record_id=record_id)
569
+ url_draft = API_URL_TEMPLATE_DRAFT.format(record_id=record_id)
525
570
 
526
- API_URL_TEMPLATE2 = (
527
- "https://data.caltech.edu/api/records/{record_id}/draft/files"
528
- )
529
- url2 = API_URL_TEMPLATE2.format(record_id=record_id)
530
- response = requests.get(url)
531
- response2 = requests.get(url2)
532
- filepath, file_link = upload_supporting_file(record_id)
533
- print(file_link)
534
- if response.status_code == 404 and response2.status_code == 404:
571
+ headers = {
572
+ "accept": "application/json",
573
+ }
574
+
575
+ if token:
576
+ headers["Authorization"] = "Bearer %s" % token
577
+
578
+ response = requests.get(url, headers=headers)
579
+ response_draft = requests.get(url_draft, headers=headers)
580
+ data = response.json()
581
+ data_draft = response_draft.json()
582
+ # Check if 'entries' exists and its length
583
+ if (
584
+ len(data.get("entries", [])) == 0
585
+ and len(data_draft.get("entries", [])) == 0
586
+ ):
535
587
  keepfile = False
536
588
  else:
537
- keepfile = input("Do you want to keep existing files? y/n: ")
538
- if keepfile == "y":
539
- keepfile = True
540
- else:
541
- keepfile = False
589
+ keepfile = (
590
+ input("Do you want to keep existing files? (y/n): ").lower() == "y"
591
+ )
592
+
593
+ filepath, file_link = upload_supporting_file(record_id)
594
+ if file_link:
595
+ print(file_link)
596
+
542
597
  if filepath != "":
543
598
  response = caltechdata_edit(
544
599
  record_id,
545
600
  token=token,
546
601
  files=filepath,
547
- production=True,
602
+ production=production,
548
603
  publish=False,
549
604
  keepfiles=keepfile,
550
605
  )
@@ -554,14 +609,13 @@ def edit_record():
554
609
  metadata,
555
610
  token=token,
556
611
  file_links=file_link,
557
- production=True,
612
+ production=production,
558
613
  publish=False,
559
- keepfile=keepfile,
614
+ keepfiles=keepfile,
560
615
  )
616
+
561
617
  rec_id = response
562
- print(
563
- f"You can view and publish this record at https://data.caltech.edu/uploads/{rec_id}\n"
564
- )
618
+ print_upload_message(rec_id, production)
565
619
 
566
620
 
567
621
  def download_file_by_id(record_id, token=None):
@@ -576,7 +630,6 @@ def download_file_by_id(record_id, token=None):
576
630
 
577
631
  try:
578
632
  response = requests.get(url, headers=headers)
579
-
580
633
  if response.status_code != 200:
581
634
  # Might have a draft
582
635
  response = requests.get(
@@ -584,7 +637,21 @@ def download_file_by_id(record_id, token=None):
584
637
  headers=headers,
585
638
  )
586
639
  if response.status_code != 200:
587
- raise Exception(f"Record {record_id} does not exist, cannot edit")
640
+ url = f"https://data.caltechlibrary.dev/api/records/{record_id}"
641
+ response = requests.get(
642
+ url,
643
+ headers=headers,
644
+ )
645
+ if response.status_code != 200:
646
+ # Might have a draft
647
+ response = requests.get(
648
+ url + "/draft",
649
+ headers=headers,
650
+ )
651
+ if response.status_code != 200:
652
+ raise Exception(
653
+ f"Record {record_id} does not exist, cannot edit"
654
+ )
588
655
  file_content = response.content
589
656
  file_name = f"downloaded_data_{record_id}.json"
590
657
  with open(file_name, "wb") as file:
@@ -134,8 +134,9 @@ def rdm_creators_contributors(person_list, peopleroles):
134
134
 
135
135
  def customize_schema_rdm(json_record):
136
136
  # Get vocabularies used in InvenioRDM
137
- vocabularies = get_vocabularies()
138
137
 
138
+ vocabularies = get_vocabularies()
139
+ validate_metadata(json_record)
139
140
  peopleroles = vocabularies["crr"]
140
141
  resourcetypes = vocabularies["rsrct"]
141
142
  descriptiontypes = vocabularies["dty"]
@@ -386,6 +387,169 @@ def customize_schema_rdm(json_record):
386
387
  return final
387
388
 
388
389
 
390
+ def validate_metadata(json_record):
391
+ """
392
+ Validates the presence and structure of required fields in a CaltechDATA JSON record.
393
+ Raises an exception if any required field is missing or structured incorrectly.
394
+ """
395
+ errors = []
396
+
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
+ if "titles" not in json_record:
407
+ errors.append("'titles' field is missing.")
408
+ elif not isinstance(json_record["titles"], list) or len(json_record["titles"]) == 0:
409
+ errors.append("'titles' should be a non-empty list.")
410
+ else:
411
+ # Ensure each title is a dictionary with 'title' field
412
+ for title in json_record["titles"]:
413
+ if not isinstance(title, dict) or "title" not in title:
414
+ errors.append(
415
+ "Each entry in 'titles' must be a dictionary with a 'title' key."
416
+ )
417
+
418
+ # Check for 'publication_date'
419
+ if "publicationYear" not in json_record and "dates" not in json_record:
420
+ errors.append(
421
+ "A publication date is required ('publicationYear' or 'dates' field is missing)."
422
+ )
423
+ if "dates" in json_record:
424
+ if not isinstance(json_record["dates"], list):
425
+ errors.append("'dates' should be a list.")
426
+ else:
427
+ for date_entry in json_record["dates"]:
428
+ if (
429
+ not isinstance(date_entry, dict)
430
+ or "dateType" not in date_entry
431
+ or "date" not in date_entry
432
+ ):
433
+ errors.append(
434
+ "Each entry in 'dates' must be a dictionary with 'dateType' and 'date' keys."
435
+ )
436
+
437
+ # Check for 'creators'
438
+ if "creators" not in json_record:
439
+ errors.append("'creators' field is missing.")
440
+ elif (
441
+ not isinstance(json_record["creators"], list)
442
+ or len(json_record["creators"]) == 0
443
+ ):
444
+ errors.append("'creators' should be a non-empty list.")
445
+ else:
446
+ for creator in json_record["creators"]:
447
+ if not isinstance(creator, dict) or "name" not in creator:
448
+ errors.append(
449
+ "Each creator in 'creators' must be a dictionary with a 'name' key."
450
+ )
451
+
452
+ # Check for 'contributors'
453
+ if "contributors" in json_record:
454
+ if not isinstance(json_record["contributors"], list):
455
+ errors.append("'contributors' should be a list.")
456
+ else:
457
+ for contributor in json_record["contributors"]:
458
+ if not isinstance(contributor, dict) or "name" not in contributor:
459
+ errors.append(
460
+ "Each contributor must be a dictionary with a 'name' key."
461
+ )
462
+
463
+ # Check for 'resourceType'
464
+ if "resourceType" not in json_record["types"]:
465
+ errors.append("'resourceType' field is missing in 'types'.")
466
+ elif not isinstance(json_record["types"]["resourceType"], str):
467
+ errors.append("'resourceType' should be a string.")
468
+
469
+ # Check for 'identifiers'
470
+ if "identifiers" in json_record:
471
+ if not isinstance(json_record["identifiers"], list):
472
+ errors.append("'identifiers' should be a list.")
473
+ else:
474
+ for identifier in json_record["identifiers"]:
475
+ if (
476
+ not isinstance(identifier, dict)
477
+ or "identifier" not in identifier
478
+ or "identifierType" not in identifier
479
+ ):
480
+ errors.append(
481
+ "Each identifier must be a dictionary with 'identifier' and 'identifierType' keys."
482
+ )
483
+
484
+ # Check for 'subjects'
485
+ if "subjects" in json_record:
486
+ if not isinstance(json_record["subjects"], list):
487
+ errors.append("'subjects' should be a list.")
488
+ else:
489
+ for subject in json_record["subjects"]:
490
+ if not isinstance(subject, dict) or "subject" not in subject:
491
+ errors.append(
492
+ "Each subject must be a dictionary with a 'subject' key."
493
+ )
494
+
495
+ # Check for 'relatedIdentifiers'
496
+ if "relatedIdentifiers" in json_record:
497
+ if not isinstance(json_record["relatedIdentifiers"], list):
498
+ errors.append("'relatedIdentifiers' should be a list.")
499
+ else:
500
+ for related_id in json_record["relatedIdentifiers"]:
501
+ if (
502
+ not isinstance(related_id, dict)
503
+ or "relatedIdentifier" not in related_id
504
+ ):
505
+ errors.append(
506
+ "Each relatedIdentifier must be a dictionary with a 'relatedIdentifier' key."
507
+ )
508
+
509
+ # Check for 'rightsList'
510
+ if "rightsList" in json_record:
511
+ if not isinstance(json_record["rightsList"], list):
512
+ errors.append("'rightsList' should be a list.")
513
+ else:
514
+ for rights in json_record["rightsList"]:
515
+ if not isinstance(rights, dict) or "rights" not in rights:
516
+ errors.append(
517
+ "Each entry in 'rightsList' must be a dictionary with a 'rights' key."
518
+ )
519
+
520
+ # Check for 'geoLocations'
521
+ if "geoLocations" in json_record:
522
+ if not isinstance(json_record["geoLocations"], list):
523
+ errors.append("'geoLocations' should be a list.")
524
+ else:
525
+ for location in json_record["geoLocations"]:
526
+ if not isinstance(location, dict):
527
+ errors.append("Each entry in 'geoLocations' must be a dictionary.")
528
+ elif (
529
+ "geoLocationPoint" not in location
530
+ and "geoLocationBox" not in location
531
+ and "geoLocationPlace" not in location
532
+ ):
533
+ errors.append(
534
+ "Each geoLocation entry must contain at least one of 'geoLocationPoint', 'geoLocationBox', or 'geoLocationPlace'."
535
+ )
536
+
537
+ # Check for 'fundingReferences'
538
+ if "fundingReferences" in json_record:
539
+ if not isinstance(json_record["fundingReferences"], list):
540
+ errors.append("'fundingReferences' should be a list.")
541
+ else:
542
+ for funding in json_record["fundingReferences"]:
543
+ if not isinstance(funding, dict):
544
+ errors.append("Each funding reference must be a dictionary.")
545
+ if "funderName" not in funding:
546
+ errors.append("Each funding reference must contain 'funderName'.")
547
+
548
+ # Return errors if any are found
549
+ if errors:
550
+ raise ValueError(f"Validation errors in metadata: {', '.join(errors)}")
551
+
552
+
389
553
  if __name__ == "__main__":
390
554
  # Read in from file for demo purposes
391
555
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: caltechdata-api
3
- Version: 1.7.0
3
+ Version: 1.8.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
@@ -33,8 +33,7 @@ The `caltechdata_api` Python library provides a convenient interface for interac
33
33
 
34
34
  ## Requirements
35
35
 
36
- - Python 3 (Anaconda is recommended)
37
- - `requests` library
36
+ - Python 3.6+
38
37
 
39
38
  ## Installation
40
39
 
File without changes