caltechdata-api 1.6.0__tar.gz → 1.7.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.6.0 → caltechdata_api-1.7.0}/PKG-INFO +3 -3
  2. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/caltechdata_edit.py +2 -1
  3. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/caltechdata_write.py +5 -4
  4. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/cli.py +141 -54
  5. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/md_to_json.py +61 -4
  6. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api.egg-info/PKG-INFO +3 -3
  7. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api.egg-info/requires.txt +2 -2
  8. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/setup.py +5 -5
  9. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/LICENSE +0 -0
  10. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/README.md +0 -0
  11. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/__init__.py +0 -0
  12. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/customize_schema.py +0 -0
  13. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/download_file.py +0 -0
  14. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/get_files.py +0 -0
  15. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/get_metadata.py +0 -0
  16. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api/utils.py +0 -0
  17. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api.egg-info/SOURCES.txt +0 -0
  18. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api.egg-info/dependency_links.txt +0 -0
  19. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api.egg-info/entry_points.txt +0 -0
  20. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/caltechdata_api.egg-info/top_level.txt +0 -0
  21. {caltechdata_api-1.6.0 → caltechdata_api-1.7.0}/setup.cfg +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: caltechdata_api
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: Python wrapper for CaltechDATA API.
5
5
  Home-page: https://github.com/caltechlibrary/caltechdata_api
6
- Author: Thomas E Morrell
7
- Author-email: tmorrell@caltech.edu
6
+ Author: Thomas E Morrell, Bhattarai Rohan , Won Elizabeth
7
+ Author-email: tmorrell@caltech.edu, ,
8
8
  License: https://data.caltech.edu/license
9
9
  Platform: UNKNOWN
10
10
  Classifier: License :: OSI Approved :: BSD License
@@ -65,6 +65,7 @@ def caltechdata_edit(
65
65
  s3_link=None,
66
66
  default_preview=None,
67
67
  authors=False,
68
+ keepfiles=False,
68
69
  ):
69
70
  # Make a copy of the metadata to make sure our local changes don't leak
70
71
  metadata = copy.deepcopy(metadata)
@@ -246,7 +247,7 @@ def caltechdata_edit(
246
247
  if result.status_code != 200:
247
248
  raise Exception(result.text)
248
249
  file_link = result.json()["links"]["files"]
249
- write_files_rdm(files, file_link, headers, f_headers)
250
+ write_files_rdm(files, file_link, headers, f_headers, keepfiles=keepfiles)
250
251
 
251
252
  else:
252
253
  # Check for existing draft
@@ -9,7 +9,7 @@ from caltechdata_api import customize_schema
9
9
  from caltechdata_api.utils import humanbytes
10
10
 
11
11
 
12
- def write_files_rdm(files, file_link, headers, f_headers, s3=None):
12
+ def write_files_rdm(files, file_link, headers, f_headers, s3=None, keepfiles=False):
13
13
  f_json = []
14
14
  f_list = {}
15
15
  fnames = []
@@ -59,9 +59,10 @@ def write_files_rdm(files, file_link, headers, f_headers, s3=None):
59
59
  raise Exception(result.text)
60
60
  else:
61
61
  # Delete any files not included in this write command
62
- result = requests.delete(self, headers=f_headers)
63
- if result.status_code != 204:
64
- raise Exception(result.text)
62
+ if keepfiles == False:
63
+ result = requests.delete(self, headers=f_headers)
64
+ if result.status_code != 204:
65
+ raise Exception(result.text)
65
66
 
66
67
 
67
68
  def add_file_links(
@@ -5,7 +5,7 @@ from caltechdata_api import caltechdata_write, caltechdata_edit
5
5
  from .md_to_json import parse_readme_to_json
6
6
  import json
7
7
  import os
8
- import configparser
8
+ from cryptography.fernet import Fernet
9
9
 
10
10
  CALTECHDATA_API = "https://data.caltech.edu/api/names?q=identifiers.identifier:{}"
11
11
  ORCID_API = "https://orcid.org/"
@@ -22,25 +22,61 @@ funderIdentifierType = ""
22
22
  funderName = ""
23
23
 
24
24
 
25
- CONFIG_FILE = "caltechdata_config.ini"
25
+ home_directory = os.path.expanduser("~")
26
+ caltechdata_directory = os.path.join(home_directory, ".caltechdata")
26
27
 
27
28
 
28
- def get_or_set_token():
29
- config = configparser.ConfigParser()
29
+ if not os.path.exists(caltechdata_directory):
30
+ os.makedirs(caltechdata_directory)
31
+
32
+
33
+ def generate_key():
34
+ return Fernet.generate_key()
30
35
 
31
- if os.path.isfile(CONFIG_FILE):
32
- config.read(CONFIG_FILE)
33
- if "CaltechDATA" in config and "token" in config["CaltechDATA"]:
34
- return config["CaltechDATA"]["token"]
36
+
37
+ # Load the key from a file or generate a new one if not present
38
+ def load_or_generate_key():
39
+ key_file = os.path.join(caltechdata_directory, "key.key")
40
+ if os.path.exists(key_file):
41
+ with open(key_file, "rb") as f:
42
+ return f.read()
35
43
  else:
44
+ key = generate_key()
45
+ with open(key_file, "wb") as f:
46
+ f.write(key)
47
+ return key
48
+
49
+
50
+ # Encrypt the token
51
+ def encrypt_token(token, key):
52
+ f = Fernet(key)
53
+ return f.encrypt(token.encode())
54
+
55
+
56
+ # Decrypt the token
57
+ def decrypt_token(encrypted_token, key):
58
+ f = Fernet(key)
59
+ return f.decrypt(encrypted_token).decode()
60
+
61
+
62
+ # Function to get or set token
63
+ def get_or_set_token():
64
+
65
+ key = load_or_generate_key()
66
+ token_file = os.path.join(caltechdata_directory, "token.txt")
67
+ try:
68
+ with open(token_file, "rb") as f:
69
+ encrypted_token = f.read()
70
+ token = decrypt_token(encrypted_token, key)
71
+ return token
72
+ except FileNotFoundError:
36
73
  while True:
37
- token = get_user_input("Enter your CaltechDATA token: ")
38
- confirm_token = get_user_input("Confirm your CaltechDATA token: ")
74
+ token = input("Enter your CaltechDATA token: ").strip()
75
+ confirm_token = input("Confirm your CaltechDATA token: ").strip()
39
76
  if token == confirm_token:
40
- config.add_section("CaltechDATA")
41
- config.set("CaltechDATA", "token", token)
42
- with open(CONFIG_FILE, "w") as configfile:
43
- config.write(configfile)
77
+ encrypted_token = encrypt_token(token, key)
78
+ with open(token_file, "wb") as f:
79
+ f.write(encrypted_token)
44
80
  return token
45
81
  else:
46
82
  print("Tokens do not match. Please try again.")
@@ -216,26 +252,42 @@ def get_names(orcid):
216
252
  return family_name, given_name
217
253
 
218
254
 
255
+ def write_s3cmd_config(access_key, secret_key, endpoint):
256
+ configf = os.path.join(home_directory, ".s3cfg")
257
+ if not os.path.exists(key_file):
258
+ with open(configf, "w") as file:
259
+ file.write(
260
+ f"""[default]
261
+ access_key = {access_key}
262
+ host_base = {endpoint}
263
+ host_bucket = %(bucket).{endpoint}
264
+ secret_key = {secret_key}
265
+ """
266
+ )
267
+
268
+
219
269
  def upload_supporting_file(record_id=None):
220
270
  filepath = ""
271
+ filepaths = []
221
272
  file_link = ""
273
+ file_links = []
222
274
  while True:
223
275
  choice = get_user_input(
224
276
  "Do you want to upload or link data files? (upload/link/n): "
225
277
  ).lower()
226
278
  if choice == "link":
227
- endpoint = "https://sdsc.osn.xsede.org/"
279
+ endpoint = "sdsc.osn.xsede.org"
228
280
  path = "ini230004-bucket01/"
229
-
230
281
  if not record_id:
231
- record_id = get_user_input("Folder where OSN files are uploaded")
232
-
282
+ access_key = get_user_input("Enter the access key: ")
283
+ secret_key = get_user_input("Enter the secret key: ")
284
+ write_s3cmd_config(access_key, secret_key, endpoint)
285
+ print("""S3 connection configured.""")
286
+ break
287
+ endpoint = f"https://{endpoint}/"
233
288
  s3 = s3fs.S3FileSystem(anon=True, client_kwargs={"endpoint_url": endpoint})
234
289
  # Find the files
235
290
  files = s3.glob(path + record_id + "/*")
236
-
237
- file_links = []
238
-
239
291
  for link in files:
240
292
  fname = link.split("/")[-1]
241
293
  if "." not in fname:
@@ -264,33 +316,38 @@ def upload_supporting_file(record_id=None):
264
316
  f for f in os.listdir() if not f.endswith(".json") and os.path.isfile(f)
265
317
  ]
266
318
  print("\n".join(files))
267
- filename = get_user_input(
268
- "Enter the filename to upload as a supporting file: "
269
- )
270
- if filename in files:
271
- file_size = os.path.getsize(filename)
272
- if file_size > 1024 * 1024 * 1024:
273
- file_link = get_user_input(
274
- "Enter the S3 link to the file (File size is more than 1GB): "
275
- )
276
- if file_link:
277
- return filepath, file_link
319
+ while True:
320
+ filename = get_user_input(
321
+ "Enter the filename to upload as a supporting file (or 'n' to finish): "
322
+ )
323
+ if filename == "n":
324
+ break
325
+ if filename in files:
326
+ file_size = os.path.getsize(filename)
327
+ if file_size > 1024 * 1024 * 1024:
328
+ print(
329
+ """The file is greater than 1 GB. Please upload the
330
+ metadata to CaltechDATA, and you'll be provided
331
+ instructions to upload the files to S3 directly."""
332
+ )
278
333
  else:
279
- print("Link is required for files larger than 1GB.")
280
- continue
334
+ filepath = os.path.abspath(filename)
335
+ filepaths.append(filepath)
281
336
  else:
282
- filepath = os.path.abspath(filename)
337
+ print(
338
+ f"Error: File '{filename}' not found. Please enter a valid filename."
339
+ )
340
+ add_more = get_user_input(
341
+ "Do you want to add more files? (y/n): "
342
+ ).lower()
343
+ if add_more != "y":
283
344
  break
284
- else:
285
- print(
286
- f"Error: File '{filename}' not found. Please enter a valid filename."
287
- )
345
+ break
288
346
  elif choice == "n":
289
347
  break
290
348
  else:
291
349
  print("Invalid input. Please enter 'link' or 'upload' or 'n'.")
292
-
293
- return filepath, file_link
350
+ return filepaths, file_links
294
351
 
295
352
 
296
353
  def upload_data_from_file():
@@ -344,7 +401,7 @@ def create_record():
344
401
  if existing_data:
345
402
  if filepath != "":
346
403
  response = caltechdata_write(
347
- existing_data, token, filepath, production=False, publish=False
404
+ existing_data, token, filepath, production=True, publish=False
348
405
  )
349
406
  elif file_link != "":
350
407
  response = caltechdata_write(
@@ -352,16 +409,19 @@ def create_record():
352
409
  token,
353
410
  file_links=[file_link],
354
411
  s3_link=file_link,
355
- production=False,
412
+ production=True,
356
413
  publish=False,
357
414
  )
358
415
  else:
359
416
  response = caltechdata_write(
360
- existing_data, token, production=False, publish=False
417
+ existing_data, token, production=True, publish=False
361
418
  )
362
419
  rec_id = response
363
420
  print(
364
- f"You can view and publish this record at https://data.caltechlibrary.dev/uploads/{rec_id}"
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}/"""
365
425
  )
366
426
  break
367
427
  else:
@@ -408,23 +468,26 @@ def create_record():
408
468
  if confirm_upload():
409
469
  if filepath != "":
410
470
  response = caltechdata_write(
411
- metadata, token, filepath, production=False, publish=False
471
+ metadata, token, filepath, production=True, publish=False
412
472
  )
413
473
  elif file_link != "":
414
474
  response = caltechdata_write(
415
475
  metadata,
416
476
  token,
417
477
  file_links=[file_link],
418
- production=False,
478
+ production=True,
419
479
  publish=False,
420
480
  )
421
481
  else:
422
482
  response = caltechdata_write(
423
- metadata, token, production=False, publish=False
483
+ metadata, token, production=True, publish=False
424
484
  )
425
485
  rec_id = response
426
486
  print(
427
- f"You can view and publish this record at https://data.caltechlibrary.dev/uploads/{rec_id}"
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}/"""
428
491
  )
429
492
  with open(response + ".json", "w") as file:
430
493
  json.dump(metadata, file, indent=2)
@@ -445,7 +508,7 @@ def edit_record():
445
508
  with open(file_name, "r") as file:
446
509
  metadata = json.load(file)
447
510
  response = caltechdata_edit(
448
- record_id, metadata, token, production=False, publish=False
511
+ record_id, metadata, token, production=True, publish=False
449
512
  )
450
513
  if response:
451
514
  print("Metadata edited successfully.")
@@ -457,11 +520,33 @@ def edit_record():
457
520
  print("No metadata file found.")
458
521
  choice = get_user_input("Do you want to add files? (y/n): ").lower()
459
522
  if choice == "y":
523
+ API_URL_TEMPLATE = "https://data.caltech.edu/api/records/{record_id}/files"
524
+ url = API_URL_TEMPLATE.format(record_id=record_id)
525
+
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)
460
532
  filepath, file_link = upload_supporting_file(record_id)
461
533
  print(file_link)
534
+ if response.status_code == 404 and response2.status_code == 404:
535
+ keepfile = False
536
+ 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
462
542
  if filepath != "":
463
543
  response = caltechdata_edit(
464
- record_id, token=token, files=filepath, production=False, publish=False
544
+ record_id,
545
+ token=token,
546
+ files=filepath,
547
+ production=True,
548
+ publish=False,
549
+ keepfiles=keepfile,
465
550
  )
466
551
  elif file_link != "":
467
552
  response = caltechdata_edit(
@@ -469,17 +554,18 @@ def edit_record():
469
554
  metadata,
470
555
  token=token,
471
556
  file_links=file_link,
472
- production=False,
557
+ production=True,
473
558
  publish=False,
559
+ keepfile=keepfile,
474
560
  )
475
561
  rec_id = response
476
562
  print(
477
- f"You can view and publish this record at https://data.caltechlibrary.dev/uploads/{rec_id}"
563
+ f"You can view and publish this record at https://data.caltech.edu/uploads/{rec_id}\n"
478
564
  )
479
565
 
480
566
 
481
567
  def download_file_by_id(record_id, token=None):
482
- url = f"https://data.caltechlibrary.dev/api/records/{record_id}"
568
+ url = f"https://data.caltech.edu/api/records/{record_id}"
483
569
 
484
570
  headers = {
485
571
  "accept": "application/vnd.datacite.datacite+json",
@@ -490,6 +576,7 @@ def download_file_by_id(record_id, token=None):
490
576
 
491
577
  try:
492
578
  response = requests.get(url, headers=headers)
579
+
493
580
  if response.status_code != 200:
494
581
  # Might have a draft
495
582
  response = requests.get(
@@ -33,6 +33,7 @@ def expand_special_keys(key, value):
33
33
  {
34
34
  "nameIdentifier": orcid,
35
35
  "nameIdentifierScheme": "ORCID",
36
+ "schemeUri": f"https://orcid.org/{value}",
36
37
  }
37
38
  ]
38
39
  return value
@@ -55,13 +56,20 @@ def parse_readme_to_json(readme_path):
55
56
  else:
56
57
  json_data["titles"] = [{"title": title_line.replace("# ", "")}]
57
58
 
59
+ contributors = []
60
+ identifiers = []
61
+ item_list = []
62
+
58
63
  section_pattern = re.compile(r"^##\s+(.*)$")
59
64
  key_value_pattern = re.compile(r"^-\s+(.*?):\s+(.*)$")
60
65
  link_pattern = re.compile(r"\[.*?\]\((.*?)\)")
61
66
 
62
67
  for line_number, line in enumerate(lines, 1):
63
68
  if not line.strip():
64
- if current_object and current_section:
69
+ if item_list and current_section:
70
+ json_data[current_section] = item_list
71
+ item_list = []
72
+ elif current_object and current_section:
65
73
  if current_section == "types":
66
74
  json_data[current_section] = current_object
67
75
  elif len(current_object) == 1:
@@ -70,6 +78,12 @@ def parse_readme_to_json(readme_path):
70
78
  json_data[current_section] = value
71
79
  else:
72
80
  json_data[current_section].append(current_object)
81
+ elif current_section in ["creators", "contributors"]:
82
+ contributors.append(current_object)
83
+ current_object = {}
84
+ elif current_section == "identifiers":
85
+ identifiers.append(current_object)
86
+ current_object = {}
73
87
  else:
74
88
  json_data[current_section].append(current_object)
75
89
  current_object = {}
@@ -77,7 +91,30 @@ def parse_readme_to_json(readme_path):
77
91
 
78
92
  section_match = section_pattern.match(line)
79
93
  if section_match:
80
- if current_section and current_object:
94
+ if item_list:
95
+ json_data[current_section] = item_list
96
+ elif current_object:
97
+ if current_section in json_data:
98
+ if isinstance(json_data[current_section], list):
99
+ json_data[current_section].append(current_object)
100
+ elif isinstance(json_data[current_section], dict):
101
+ json_data[current_section].update(current_object)
102
+ else:
103
+ json_data[current_section] = (
104
+ [current_object]
105
+ if current_section != "types"
106
+ else current_object
107
+ )
108
+ current_object = {}
109
+
110
+ elif contributors and current_section in ["creators", "contributors"]:
111
+ json_data[current_section] = contributors
112
+ contributors = []
113
+ elif identifiers and current_section == "identifiers":
114
+ json_data[current_section] = identifiers
115
+ identifiers = []
116
+
117
+ elif current_section and current_object:
81
118
  if current_section == "types":
82
119
  json_data[current_section] = current_object
83
120
  elif len(current_object) == 1:
@@ -100,6 +137,22 @@ def parse_readme_to_json(readme_path):
100
137
 
101
138
  if key in ["affiliation", "nameIdentifiers"]:
102
139
  value = expand_special_keys(key, value)
140
+ elif (
141
+ key == "nameType"
142
+ and current_object
143
+ and current_section in ["creators", "contributors"]
144
+ ):
145
+ contributors.append(current_object)
146
+ current_object = {}
147
+ elif current_section in ["subjects"]:
148
+ item_list.append({key: value})
149
+ elif current_section == "dates":
150
+ if key == "date":
151
+ current_object["date"] = value
152
+ elif key == "dateType":
153
+ current_object["dateType"] = value
154
+ item_list.append(current_object)
155
+ current_object = {}
103
156
  else:
104
157
  link_match = link_pattern.search(value)
105
158
  if link_match:
@@ -112,7 +165,11 @@ def parse_readme_to_json(readme_path):
112
165
  f"Incorrect format detected at line {line_number}: {line}"
113
166
  )
114
167
 
115
- if current_section and current_object:
168
+ if contributors and current_section in ["creators", "contributors"]:
169
+ json_data[current_section] = contributors
170
+ elif identifiers and current_section == "identifiers":
171
+ json_data[current_section] = identifiers
172
+ elif current_section and current_object:
116
173
  if current_section == "types":
117
174
  json_data[current_section] = current_object
118
175
  elif len(current_object) == 1:
@@ -128,7 +185,7 @@ def parse_readme_to_json(readme_path):
128
185
 
129
186
 
130
187
  if __name__ == "__main__":
131
- readme_path = "exampleREADME.md"
188
+ readme_path = "/Users/elizabethwon/downloads/exampleREADME.md"
132
189
  try:
133
190
  json_data = parse_readme_to_json(readme_path)
134
191
  output_json_path = "output1.json"
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: caltechdata-api
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: Python wrapper for CaltechDATA API.
5
5
  Home-page: https://github.com/caltechlibrary/caltechdata_api
6
- Author: Thomas E Morrell
7
- Author-email: tmorrell@caltech.edu
6
+ Author: Thomas E Morrell, Bhattarai Rohan , Won Elizabeth
7
+ Author-email: tmorrell@caltech.edu, ,
8
8
  License: https://data.caltech.edu/license
9
9
  Platform: UNKNOWN
10
10
  Classifier: License :: OSI Approved :: BSD License
@@ -3,5 +3,5 @@ datacite>1.1.0
3
3
  tqdm>=4.62.3
4
4
  pyyaml
5
5
  s3fs
6
- configparser
7
- awscli
6
+ cryptography
7
+ s3cmd
@@ -40,9 +40,9 @@ version = meta["version"]
40
40
  author = ""
41
41
  author_email = ""
42
42
  for obj in meta["author"]:
43
- given = obj["givenName"]
44
- family = obj["familyName"]
45
- email = obj["email"]
43
+ given = obj.get("givenName", "")
44
+ family = obj.get("familyName", "")
45
+ email = obj.get("email", "")
46
46
  if len(author) == 0:
47
47
  author = given + " " + family
48
48
  else:
@@ -66,8 +66,8 @@ REQUIRED = [
66
66
  "tqdm>=4.62.3",
67
67
  "pyyaml",
68
68
  "s3fs",
69
- "configparser",
70
- "awscli",
69
+ "cryptography",
70
+ "s3cmd",
71
71
  ]
72
72
 
73
73
  # What packages are optional?
File without changes