caltechdata-api 2.2.2__py3-none-any.whl → 2.2.4__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.
@@ -14,7 +14,7 @@ def caltechdata_unembargo(token, ids, production=False):
14
14
  print("caltechdaua_unembargo is not yet re-implemented")
15
15
 
16
16
 
17
- def caltechdata_accept(ids, token=None, production=False):
17
+ def caltechdata_accept(ids, token=None, production=False, verify=True):
18
18
  # Accept a record into a community. Only accepts the first community
19
19
  # request
20
20
 
@@ -34,11 +34,15 @@ def caltechdata_accept(ids, token=None, production=False):
34
34
 
35
35
  for idv in ids:
36
36
  result = requests.get(
37
- url + "/api/records/" + idv + "/draft/review", headers=headers
37
+ url + "/api/records/" + idv + "/draft/review",
38
+ headers=headers,
39
+ verify=verify,
38
40
  )
39
41
  if result.status_code != 200:
40
42
  result = requests.get(
41
- url + "/api/records/" + idv + "/requests", headers=headers
43
+ url + "/api/records/" + idv + "/requests",
44
+ headers=headers,
45
+ verify=verify,
42
46
  )
43
47
  if result.status_code != 200:
44
48
  raise Exception(result.text)
@@ -51,7 +55,7 @@ def caltechdata_accept(ids, token=None, production=False):
51
55
  "format": "html",
52
56
  }
53
57
  }
54
- result = requests.post(accept_link, json=data, headers=headers)
58
+ result = requests.post(accept_link, json=data, headers=headers, verify=verify)
55
59
  if result.status_code != 200:
56
60
  raise Exception(result.text)
57
61
 
@@ -117,6 +121,7 @@ def caltechdata_edit(
117
121
  keepfiles=False,
118
122
  return_id=False,
119
123
  local=False,
124
+ verify=True,
120
125
  ):
121
126
  # Make a copy of the metadata to make sure our local changes don't leak
122
127
  metadata = copy.deepcopy(metadata)
@@ -159,28 +164,32 @@ def caltechdata_edit(
159
164
  url + "/api/records/" + idv + "/communities",
160
165
  headers=headers,
161
166
  data=json.dumps({"communities": [{"id": community}]}),
167
+ verify=verify,
162
168
  )
163
169
  if result.status_code != 200:
164
170
  print(result.url)
165
171
  raise Exception(result.text)
166
- caltechdata_accept([idv], token, production)
172
+ caltechdata_accept([idv], token, production, verify=verify)
167
173
 
168
174
  # Check status
169
175
  existing = requests.get(
170
176
  url + "/api/records/" + idv,
171
177
  headers=headers,
178
+ verify=verify,
172
179
  )
173
180
  if existing.status_code != 200:
174
181
  # Might have a draft
175
182
  existing = requests.get(
176
183
  url + "/api/records/" + idv + "/draft",
177
184
  headers=headers,
185
+ verify=verify,
178
186
  )
179
187
  if existing.status_code != 200:
180
188
  # Try back again to the record
181
189
  existing = requests.get(
182
190
  url + "/api/records/" + idv,
183
191
  headers=headers,
192
+ verify=verify,
184
193
  )
185
194
 
186
195
  if existing.status_code != 200:
@@ -202,6 +211,7 @@ def caltechdata_edit(
202
211
  result = requests.post(
203
212
  url + "/api/records/" + idv + "/versions",
204
213
  headers=headers,
214
+ verify=verify,
205
215
  )
206
216
  if result.status_code != 201:
207
217
  raise Exception(result.text)
@@ -304,12 +314,14 @@ def caltechdata_edit(
304
314
  result = requests.get(
305
315
  url + "/api/records/" + idv + "/draft",
306
316
  headers=headers,
317
+ verify=verify,
307
318
  )
308
319
  if result.status_code != 200:
309
320
  # We make a draft
310
321
  result = requests.post(
311
322
  url + "/api/records/" + idv + "/draft",
312
323
  headers=headers,
324
+ verify=verify,
313
325
  )
314
326
  if result.status_code != 201:
315
327
  raise Exception(result.text)
@@ -324,16 +336,28 @@ def caltechdata_edit(
324
336
  url + "/api/records/" + idv + "/draft",
325
337
  headers=headers,
326
338
  json=data,
339
+ verify=verify,
327
340
  )
328
341
  if result.status_code != 200:
329
342
  raise Exception(result.text)
330
343
  file_upload_link = result.json()["links"]["files"]
331
344
  if files:
332
345
  write_files_rdm(
333
- files, file_upload_link, headers, f_headers, keepfiles=keepfiles
346
+ files,
347
+ file_upload_link,
348
+ headers,
349
+ f_headers,
350
+ keepfiles=keepfiles,
351
+ verify=verify,
334
352
  )
335
353
  if file_links:
336
- add_file_links(file_upload_link, file_links, headers, keepfiles=keepfiles)
354
+ add_file_links(
355
+ file_upload_link,
356
+ file_links,
357
+ headers,
358
+ keepfiles=keepfiles,
359
+ verify=verify,
360
+ )
337
361
 
338
362
  else:
339
363
  # We want files to stay the same as the existing record
@@ -345,6 +369,7 @@ def caltechdata_edit(
345
369
  url + "/api/records/" + idv + "/draft",
346
370
  headers=headers,
347
371
  json=data,
372
+ verify=verify,
348
373
  )
349
374
  if result.status_code != 200:
350
375
  time.sleep(3)
@@ -352,16 +377,17 @@ def caltechdata_edit(
352
377
  url + "/api/records/" + idv + "/draft",
353
378
  headers=headers,
354
379
  json=data,
380
+ verify=verify,
355
381
  )
356
382
  if result.status_code != 200:
357
383
  raise Exception(result.text)
358
384
 
359
385
  if publish:
360
386
  publish_link = f"{url}/api/records/{idv}/draft/actions/publish"
361
- result = requests.post(publish_link, headers=headers)
387
+ result = requests.post(publish_link, headers=headers, verify=verify)
362
388
  if result.status_code != 202:
363
389
  time.sleep(3)
364
- result = requests.post(publish_link, headers=headers)
390
+ result = requests.post(publish_link, headers=headers, verify=verify)
365
391
  if result.status_code != 202:
366
392
  raise Exception(result.text)
367
393
  if return_id:
@@ -67,16 +67,21 @@ def write_files_rdm(
67
67
  raise Exception(result.text)
68
68
 
69
69
 
70
- def add_file_links(file_upload_link, file_links, headers, keepfiles=False):
70
+ def add_file_links(file_upload_link, file_links, headers, keepfiles=False, verify=True):
71
71
  # Currently configured for S3 links, assuming all are at the same endpoint
72
+ # Links are urls, or (file name, url) pairs when the url file names are not
73
+ # unique across the bucket
72
74
  link_string = ""
73
- endpoint = "https://" + file_links[0].split("/")[2]
75
+ file_links = [
76
+ link if isinstance(link, (list, tuple)) else (link.split("/")[-1], link)
77
+ for link in file_links
78
+ ]
79
+ endpoint = "https://" + file_links[0][1].split("/")[2]
74
80
  s3 = s3fs.S3FileSystem(anon=True, client_kwargs={"endpoint_url": endpoint})
75
81
  index = 0
76
82
  file_json = []
77
83
  f_list = []
78
- for link in file_links:
79
- file = link.split("/")[-1]
84
+ for file, link in file_links:
80
85
  f_list.append(file)
81
86
  path = link.split(endpoint)[1]
82
87
  size = s3.info(path)["size"]
@@ -85,20 +90,27 @@ def add_file_links(file_upload_link, file_links, headers, keepfiles=False):
85
90
  {"key": file, "size": size, "transfer": {"type": "R", "url": link}}
86
91
  )
87
92
  # Now we see if any existing files need to be replaced
88
- result = requests.get(file_upload_link, headers=headers)
93
+ result = requests.get(file_upload_link, headers=headers, verify=verify)
89
94
  if result.status_code == 200:
90
95
  ex_files = result.json()["entries"]
91
96
  for ex in ex_files:
92
97
  if ex["key"] in f_list:
93
- result = requests.delete(ex["links"]["self"], headers=headers)
98
+ result = requests.delete(
99
+ ex["links"]["self"], headers=headers, verify=verify
100
+ )
94
101
  if result.status_code != 204:
95
102
  raise Exception(result.text)
96
103
  else:
97
104
  if keepfiles == False:
98
- result = requests.delete(ex["links"]["self"], headers=headers)
105
+ result = requests.delete(
106
+ ex["links"]["self"], headers=headers, verify=verify
107
+ )
99
108
  if result.status_code != 204:
100
109
  raise Exception(result.text)
101
- result = requests.post(file_upload_link, headers=headers, json=file_json)
110
+
111
+ result = requests.post(
112
+ file_upload_link, headers=headers, json=file_json, verify=verify
113
+ )
102
114
  if result.status_code != 201:
103
115
  raise Exception(result.text)
104
116
 
@@ -19,7 +19,9 @@ from urllib.parse import urlsplit
19
19
  from typing import Any, Dict, Optional, Sequence
20
20
 
21
21
 
22
- def get_base_url(production: bool = True, authors: bool = False) -> str:
22
+ def get_base_url(
23
+ production: bool = True, authors: bool = False, local: bool = False
24
+ ) -> str:
23
25
  """Get the base URL of the repository to download from.
24
26
 
25
27
  Parameters
@@ -31,6 +33,11 @@ def get_base_url(production: bool = True, authors: bool = False) -> str:
31
33
  authors
32
34
  Whether to use CaltechAUTHORS instead of CaltechDATA.
33
35
 
36
+ local
37
+ Whether to use a local instance, at ``https://127.0.0.1:5000``,
38
+ instead of the test (development) instance. Ignored when
39
+ ``production`` is ``True``, as in :func:`caltechdata_write`.
40
+
34
41
  Returns
35
42
  -------
36
43
  str
@@ -40,6 +47,8 @@ def get_base_url(production: bool = True, authors: bool = False) -> str:
40
47
  if authors:
41
48
  return "https://authors.library.caltech.edu"
42
49
  return "https://data.caltech.edu"
50
+ elif local:
51
+ return "https://127.0.0.1:5000"
43
52
  else:
44
53
  if authors:
45
54
  return "https://authors.caltechlibrary.dev"
@@ -51,6 +60,8 @@ def get_files_from_record(
51
60
  production: bool = True,
52
61
  authors: bool = False,
53
62
  token: Optional[str] = None,
63
+ local: bool = False,
64
+ verify: bool = True,
54
65
  ) -> Dict[str, Any]:
55
66
  """Get a dictionary of files associated with a record.
56
67
 
@@ -74,25 +85,37 @@ def get_files_from_record(
74
85
  token is given and the published record is not found, the
75
86
  record's draft is tried as well.
76
87
 
88
+ local
89
+ Whether to query a local instance, at ``https://127.0.0.1:5000``,
90
+ instead of the test (development) instance.
91
+
92
+ verify
93
+ Whether to verify the server's TLS certificate. Use ``False``
94
+ for a local instance with a self-signed certificate.
95
+
77
96
  Returns
78
97
  -------
79
98
  dict
80
99
  A dictionary with the file names as keys and the metadata
81
100
  associated with those files as values.
82
101
  """
83
- base_url = get_base_url(production=production, authors=authors)
102
+ base_url = get_base_url(production=production, authors=authors, local=local)
84
103
  headers = {}
85
104
  if token is not None:
86
105
  headers["Authorization"] = "Bearer %s" % token
87
106
 
88
- r = requests.get(f"{base_url}/api/records/{record_id}/files", headers=headers)
107
+ r = requests.get(
108
+ f"{base_url}/api/records/{record_id}/files", headers=headers, verify=verify
109
+ )
89
110
  try:
90
111
  if r.status_code == 404 and token is not None:
91
112
  # An unpublished record has no published version to return,
92
113
  # so fall back to its draft, which requires authentication.
93
114
  r.close()
94
115
  r = requests.get(
95
- f"{base_url}/api/records/{record_id}/draft/files", headers=headers
116
+ f"{base_url}/api/records/{record_id}/draft/files",
117
+ headers=headers,
118
+ verify=verify,
96
119
  )
97
120
 
98
121
  r.raise_for_status()
@@ -114,6 +137,8 @@ def download_files_from_record(
114
137
  production: bool = True,
115
138
  authors: bool = False,
116
139
  token: Optional[str] = None,
140
+ local: bool = False,
141
+ verify: bool = True,
117
142
  ):
118
143
  """Download one or more files from a record.
119
144
 
@@ -157,12 +182,28 @@ def download_files_from_record(
157
182
  files from restricted records. If omitted, the requests are
158
183
  made without authentication and only public records are
159
184
  available.
185
+
186
+ local
187
+ Whether to download from a local instance, at
188
+ ``https://127.0.0.1:5000``, instead of the test (development)
189
+ instance.
190
+
191
+ verify
192
+ Whether to verify the server's TLS certificate, for requests to
193
+ the instance itself. Use ``False`` for a local instance with a
194
+ self-signed certificate. Redirects to other hosts are always
195
+ verified.
160
196
  """
161
197
  if not os.path.isdir(output_path):
162
198
  raise IOError(f"{output_path} is not an extant directory")
163
199
 
164
200
  files = get_files_from_record(
165
- record_id, production=production, authors=authors, token=token
201
+ record_id,
202
+ production=production,
203
+ authors=authors,
204
+ token=token,
205
+ local=local,
206
+ verify=verify,
166
207
  )
167
208
  if filenames is None:
168
209
  filenames = sorted(files.keys())
@@ -181,7 +222,11 @@ def download_files_from_record(
181
222
 
182
223
  content_url = entry["links"]["content"]
183
224
  download_content(
184
- content_url, output_file, max_redirects=max_redirects, token=token
225
+ content_url,
226
+ output_file,
227
+ max_redirects=max_redirects,
228
+ token=token,
229
+ verify=verify,
185
230
  )
186
231
 
187
232
 
@@ -190,6 +235,7 @@ def download_content(
190
235
  fname: os.PathLike,
191
236
  max_redirects=5,
192
237
  token: Optional[str] = None,
238
+ verify: bool = True,
193
239
  ):
194
240
  """Download the contents of a file.
195
241
 
@@ -216,6 +262,11 @@ def download_content(
216
262
  content from restricted records. If omitted, the request is
217
263
  made without authentication. The token is only sent to the
218
264
  host of ``content_url``, not to hosts we are redirected to.
265
+
266
+ verify
267
+ Whether to verify the server's TLS certificate. Like the token,
268
+ it applies only to the host of ``content_url``: a redirect to
269
+ another host is always verified.
219
270
  """
220
271
  url = content_url
221
272
  host = urlsplit(content_url).netloc
@@ -228,7 +279,9 @@ def download_content(
228
279
  if token is not None and urlsplit(url).netloc == host:
229
280
  headers["Authorization"] = "Bearer %s" % token
230
281
 
231
- with requests.get(url, stream=True, headers=headers) as r:
282
+ # A redirect to another host (the file store) is always verified
283
+ verify_url = verify if urlsplit(url).netloc == host else True
284
+ with requests.get(url, stream=True, headers=headers, verify=verify_url) as r:
232
285
  r.raise_for_status()
233
286
  if "Location" in r.headers:
234
287
  # Redirection - follow to the next URL
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: caltechdata_api
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: Python wrapper for CaltechDATA API.
5
5
  Author: Rohan Bhattarai, Elizabeth Won
6
- Author-email: Thomas E Morrell <tmorrell@caltech.edu>, Alexander A Abakah <aabakah@caltech.edu>, Kshemaahna Nagi <knagi@caltech.edu>
6
+ Author-email: Thomas E Morrell <tmorrell@caltech.edu>, Alexander A Abakah <aabakah@caltech.edu>, Kshemaahna Nagi <knagi@caltech.edu>, Robert Doiel <rsdoiel@caltech.edu>
7
7
  Maintainer-email: Thomas E Morrell <tmorrell@caltech.edu>
8
8
  License-Expression: BSD-3-Clause
9
9
  Project-URL: Homepage, https://github.com/caltechlibrary/caltechdata_api
@@ -1,10 +1,10 @@
1
1
  caltechdata_api/__init__.py,sha256=bzrm-nSBmVz7k73oI3hniolXeKbzlJFOMhoyqQ61DwI,585
2
- caltechdata_api/caltechdata_edit.py,sha256=pXYkIPwRvhbG_x_6hZIJnP38AdJdnq3nsv3XTYr6KCY,12043
3
- caltechdata_api/caltechdata_write.py,sha256=C-MZwZi8d7OhreBJaB4C51DRH5gQcLo5zEZCPjFUbB0,10338
2
+ caltechdata_api/caltechdata_edit.py,sha256=GjsSvln0rn1Gi5WGdsHtHWnXZ4wfEJyu9zpO-xx8-6g,12697
3
+ caltechdata_api/caltechdata_write.py,sha256=BrcXMJIhPctYdR18oXcjnrFKMK_DY8Q7nfQ7hWVbNkU,10732
4
4
  caltechdata_api/cli.py,sha256=4hoUJK5U6dWWKgvIATG8VR85Q1JOuNG38UWYu5QlX1c,31788
5
5
  caltechdata_api/customize_schema.py,sha256=3aLbbIlG2PTmP6yXUGuTp56PwFso2qiMgDmy6-qahHs,26510
6
6
  caltechdata_api/download_file.py,sha256=IXObPtkS7mKFUViY1ik2X-nBiNB7V6k-Zj8zf01qAQI,1983
7
- caltechdata_api/download_from_record.py,sha256=gtZRdlUbWevxyLIPUAFILoOpmWVx-ya7UPAuzlR2a3o,10320
7
+ caltechdata_api/download_from_record.py,sha256=9WgvqM3Bh_1S_4L_jKvVi-s-dMH6HdFuqTYxdUZS0C0,12000
8
8
  caltechdata_api/get_metadata.py,sha256=IIneLJEx6n6OFGdD_RNz1Ed0mVuacy046UY1TmNG3Ys,4544
9
9
  caltechdata_api/md_to_json.py,sha256=T4rDiKPCKcOdkx1sa4kxoWL_rUQYu_QYBzn6FuR0vVc,7745
10
10
  caltechdata_api/utils.py,sha256=yD7PIiLFV9xxOWZl4v1YhRI5ChHmEBjXBjc23gfm2pA,2741
@@ -17,9 +17,9 @@ caltechdata_api/vocabularies/relation_types.yaml,sha256=gTPawCQrdeG8VDVZOtbCa7tP
17
17
  caltechdata_api/vocabularies/resource_types.yaml,sha256=w2KCOeQ0F3rHPJRHOhoPJn8gRDWn4AgwZ_rhYdYidqg,16372
18
18
  caltechdata_api/vocabularies/roles.yaml,sha256=Lgf_P4mDUpe_1GyZlbM3-PC6LAdl4kmNqCsVSqNP1uk,1815
19
19
  caltechdata_api/vocabularies/title_types.yaml,sha256=J3qHCGqTadwujy8cU3lP32PfnpIiA6E-nbQYspaDoLU,332
20
- caltechdata_api-2.2.2.dist-info/licenses/LICENSE,sha256=QvFsgOiOJGxfQjx34g5Fpw2twHYgiIqrcsp1AXxmZiM,1523
21
- caltechdata_api-2.2.2.dist-info/METADATA,sha256=X10yM3dEN4-KwTe3EgJHEOqem6-CtbEl84VAbiBYKYU,4290
22
- caltechdata_api-2.2.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
23
- caltechdata_api-2.2.2.dist-info/entry_points.txt,sha256=AYk_A9Pq_6I4jbRZ5rd_Mxbw2W_981mhyXLYgk1oGcI,61
24
- caltechdata_api-2.2.2.dist-info/top_level.txt,sha256=giC9aULvRY9HlV2IvaPZmwn1b46eLM1rbT3XqYiYOI0,16
25
- caltechdata_api-2.2.2.dist-info/RECORD,,
20
+ caltechdata_api-2.2.4.dist-info/licenses/LICENSE,sha256=QvFsgOiOJGxfQjx34g5Fpw2twHYgiIqrcsp1AXxmZiM,1523
21
+ caltechdata_api-2.2.4.dist-info/METADATA,sha256=PRX0x0R_F5j-e1eNJ2Zx8YN7A_Jnl1M4LzVMtHiClXw,4326
22
+ caltechdata_api-2.2.4.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
23
+ caltechdata_api-2.2.4.dist-info/entry_points.txt,sha256=AYk_A9Pq_6I4jbRZ5rd_Mxbw2W_981mhyXLYgk1oGcI,61
24
+ caltechdata_api-2.2.4.dist-info/top_level.txt,sha256=giC9aULvRY9HlV2IvaPZmwn1b46eLM1rbT3XqYiYOI0,16
25
+ caltechdata_api-2.2.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (84.0.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5