caltechdata-api 2.1.0__tar.gz → 2.2.1__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.
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/PKG-INFO +1 -1
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/__init__.py +4 -0
- caltechdata_api-2.2.1/caltechdata_api/download_from_record.py +296 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api.egg-info/PKG-INFO +1 -1
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api.egg-info/SOURCES.txt +1 -1
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/pyproject.toml +1 -1
- caltechdata_api-2.1.0/caltechdata_api/get_files.py +0 -49
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/LICENSE +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/README.md +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/caltechdata_edit.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/caltechdata_write.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/cli.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/customize_schema.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/download_file.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/get_metadata.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/md_to_json.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/utils.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/date_types.yaml +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/description_types.yaml +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/identifier_types.yaml +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/licenses.csv +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/relation_types.yaml +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/resource_types.yaml +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/roles.yaml +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/title_types.yaml +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies.yaml +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api.egg-info/dependency_links.txt +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api.egg-info/entry_points.txt +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api.egg-info/requires.txt +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api.egg-info/top_level.txt +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/setup.cfg +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/tests/test_download.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/tests/test_rdm.py +0 -0
- {caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/tests/test_unit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: caltechdata_api
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Python wrapper for CaltechDATA API.
|
|
5
5
|
Author: Rohan Bhattarai, Elizabeth Won
|
|
6
6
|
Author-email: Thomas E Morrell <tmorrell@caltech.edu>, Alexander A Abakah <aabakah@caltech.edu>, Kshemaahna Nagi <knagi@caltech.edu>
|
|
@@ -13,5 +13,9 @@ from .caltechdata_edit import (
|
|
|
13
13
|
from .customize_schema import customize_schema, validate_metadata
|
|
14
14
|
from .get_metadata import get_metadata
|
|
15
15
|
from .download_file import download_file, download_url
|
|
16
|
+
from .download_from_record import (
|
|
17
|
+
get_files_from_record,
|
|
18
|
+
download_files_from_record,
|
|
19
|
+
)
|
|
16
20
|
from .utils import humanbytes
|
|
17
21
|
from .md_to_json import parse_readme_to_json
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""Functions to download files directly from a CaltechData record.
|
|
2
|
+
|
|
3
|
+
A CaltechData record ID is the ten characters, separated into two groups
|
|
4
|
+
of five by a dash, found at the end of a CaltechData URL or default DOI.
|
|
5
|
+
For instance, in the URL ``https://data.caltech.edu/records/4rgh7-zss31``
|
|
6
|
+
or DOI ``10.22002/4rgh7-zss31``, the record ID is ``4rgh7-zss31``.
|
|
7
|
+
|
|
8
|
+
The same functions can download from CaltechAUTHORS by passing
|
|
9
|
+
``authors=True``, and from the test (development) instances of either
|
|
10
|
+
repository by passing ``production=False``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import os
|
|
15
|
+
import requests
|
|
16
|
+
from tqdm.auto import tqdm
|
|
17
|
+
from urllib.parse import urlsplit
|
|
18
|
+
|
|
19
|
+
from typing import Any, Dict, Optional, Sequence
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_base_url(production: bool = True, authors: bool = False) -> str:
|
|
23
|
+
"""Get the base URL of the repository to download from.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
production
|
|
28
|
+
Whether to use the production repository. If ``False``, the
|
|
29
|
+
test (development) instance is used instead.
|
|
30
|
+
|
|
31
|
+
authors
|
|
32
|
+
Whether to use CaltechAUTHORS instead of CaltechDATA.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
str
|
|
37
|
+
The base URL, without a trailing slash.
|
|
38
|
+
"""
|
|
39
|
+
if production:
|
|
40
|
+
if authors:
|
|
41
|
+
return "https://authors.library.caltech.edu"
|
|
42
|
+
return "https://data.caltech.edu"
|
|
43
|
+
else:
|
|
44
|
+
if authors:
|
|
45
|
+
return "https://authors.caltechlibrary.dev"
|
|
46
|
+
return "https://data.caltechlibrary.dev"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_files_from_record(
|
|
50
|
+
record_id: str,
|
|
51
|
+
production: bool = True,
|
|
52
|
+
authors: bool = False,
|
|
53
|
+
token: Optional[str] = None,
|
|
54
|
+
) -> Dict[str, Any]:
|
|
55
|
+
"""Get a dictionary of files associated with a record.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
record_id
|
|
60
|
+
The record ID for which to obtain the list of files. See
|
|
61
|
+
the module documentation for how to find the record ID.
|
|
62
|
+
|
|
63
|
+
production
|
|
64
|
+
Whether to query the production repository. If ``False``, the
|
|
65
|
+
test (development) instance is used instead.
|
|
66
|
+
|
|
67
|
+
authors
|
|
68
|
+
Whether to query CaltechAUTHORS instead of CaltechDATA.
|
|
69
|
+
|
|
70
|
+
token
|
|
71
|
+
A personal access token for the repository, used to access
|
|
72
|
+
restricted records. If omitted, the request is made without
|
|
73
|
+
authentication and only public records are available. When a
|
|
74
|
+
token is given and the published record is not found, the
|
|
75
|
+
record's draft is tried as well.
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
dict
|
|
80
|
+
A dictionary with the file names as keys and the metadata
|
|
81
|
+
associated with those files as values.
|
|
82
|
+
"""
|
|
83
|
+
base_url = get_base_url(production=production, authors=authors)
|
|
84
|
+
headers = {}
|
|
85
|
+
if token is not None:
|
|
86
|
+
headers["Authorization"] = "Bearer %s" % token
|
|
87
|
+
|
|
88
|
+
r = requests.get(f"{base_url}/api/records/{record_id}/files", headers=headers)
|
|
89
|
+
try:
|
|
90
|
+
if r.status_code == 404 and token is not None:
|
|
91
|
+
# An unpublished record has no published version to return,
|
|
92
|
+
# so fall back to its draft, which requires authentication.
|
|
93
|
+
r.close()
|
|
94
|
+
r = requests.get(
|
|
95
|
+
f"{base_url}/api/records/{record_id}/draft/files", headers=headers
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
r.raise_for_status()
|
|
99
|
+
files = dict()
|
|
100
|
+
for entry in r.json().get("entries", []):
|
|
101
|
+
key = entry.pop("key")
|
|
102
|
+
files[key] = entry
|
|
103
|
+
|
|
104
|
+
return files
|
|
105
|
+
finally:
|
|
106
|
+
r.close()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def download_files_from_record(
|
|
110
|
+
record_id: str,
|
|
111
|
+
output_path: os.PathLike,
|
|
112
|
+
filenames: Optional[Sequence[str]] = None,
|
|
113
|
+
max_redirects: int = 5,
|
|
114
|
+
production: bool = True,
|
|
115
|
+
authors: bool = False,
|
|
116
|
+
token: Optional[str] = None,
|
|
117
|
+
):
|
|
118
|
+
"""Download one or more files from a record.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
record_id
|
|
123
|
+
The record ID from which to download the file. See
|
|
124
|
+
the module documentation for how to find the record ID.
|
|
125
|
+
|
|
126
|
+
output_path
|
|
127
|
+
Where to download the files. Must be an existing directory.
|
|
128
|
+
If you want control over exactly how each file is named,
|
|
129
|
+
use :func:`get_files_from_record` to get the URLs for each
|
|
130
|
+
file, then manually call :func:`download_content` for each
|
|
131
|
+
file you wish to download.
|
|
132
|
+
|
|
133
|
+
filenames
|
|
134
|
+
The name of the files in the record. Must match exactly.
|
|
135
|
+
If you are not sure of the filenames in the record,
|
|
136
|
+
they are the keys of the dictionary returned by
|
|
137
|
+
:func:`get_files_from_record`. If omitted, all files
|
|
138
|
+
are downloaded.
|
|
139
|
+
|
|
140
|
+
max_redirects
|
|
141
|
+
If the record's link to the file content returns a
|
|
142
|
+
redirection to another location, this function will follow
|
|
143
|
+
that redirection. This parameter sets the maximum number of
|
|
144
|
+
hops that the function can take. Usually, only a single
|
|
145
|
+
redirection should be necessary (from the record to the file
|
|
146
|
+
provider), but the default allows for a few extra hops.
|
|
147
|
+
|
|
148
|
+
production
|
|
149
|
+
Whether to download from the production repository. If ``False``,
|
|
150
|
+
the test (development) instance is used instead.
|
|
151
|
+
|
|
152
|
+
authors
|
|
153
|
+
Whether to download from CaltechAUTHORS instead of CaltechDATA.
|
|
154
|
+
|
|
155
|
+
token
|
|
156
|
+
A personal access token for the repository, used to download
|
|
157
|
+
files from restricted records. If omitted, the requests are
|
|
158
|
+
made without authentication and only public records are
|
|
159
|
+
available.
|
|
160
|
+
"""
|
|
161
|
+
if not os.path.isdir(output_path):
|
|
162
|
+
raise IOError(f"{output_path} is not an extant directory")
|
|
163
|
+
|
|
164
|
+
files = get_files_from_record(
|
|
165
|
+
record_id, production=production, authors=authors, token=token
|
|
166
|
+
)
|
|
167
|
+
if filenames is None:
|
|
168
|
+
filenames = sorted(files.keys())
|
|
169
|
+
|
|
170
|
+
for filename in filenames:
|
|
171
|
+
output_file = os.path.join(output_path, filename)
|
|
172
|
+
if filename not in files:
|
|
173
|
+
raise KeyError(f'File "{filename}" not found in record {record_id}')
|
|
174
|
+
|
|
175
|
+
entry = files[filename]
|
|
176
|
+
if "links" not in entry or "content" not in entry["links"]:
|
|
177
|
+
raise NotImplementedError(
|
|
178
|
+
f'Metadata for file "{filename}" in record {record_id} is '
|
|
179
|
+
"missing the content link"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
content_url = entry["links"]["content"]
|
|
183
|
+
download_content(
|
|
184
|
+
content_url, output_file, max_redirects=max_redirects, token=token
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def download_content(
|
|
189
|
+
content_url: str,
|
|
190
|
+
fname: os.PathLike,
|
|
191
|
+
max_redirects=5,
|
|
192
|
+
token: Optional[str] = None,
|
|
193
|
+
):
|
|
194
|
+
"""Download the contents of a file.
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
content_url
|
|
199
|
+
The URL pointing to the contents of the file. In the dictionary
|
|
200
|
+
returned by :func:`get_files_from_record` (call it ``D``), this
|
|
201
|
+
is usually the value at ``D[FILENAME]["links"]["content"]``.
|
|
202
|
+
|
|
203
|
+
fname
|
|
204
|
+
The path to which to save the file contents. Will be overwritten!
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
max_redirects
|
|
208
|
+
If the ``content_url`` returns a redirection to another location,
|
|
209
|
+
this function will follow that redirection. This parameter sets
|
|
210
|
+
the maximum number of hops that the function can take. Usually,
|
|
211
|
+
only a single redirection should be necessary (from the record to
|
|
212
|
+
the file provider), but the default allows for a few extra hops.
|
|
213
|
+
|
|
214
|
+
token
|
|
215
|
+
A personal access token for the repository, used to download
|
|
216
|
+
content from restricted records. If omitted, the request is
|
|
217
|
+
made without authentication. The token is only sent to the
|
|
218
|
+
host of ``content_url``, not to hosts we are redirected to.
|
|
219
|
+
"""
|
|
220
|
+
url = content_url
|
|
221
|
+
host = urlsplit(content_url).netloc
|
|
222
|
+
# If max_redirects == 0, then this loop will never run. Since the
|
|
223
|
+
# likely expected behavior for ``max_redirects = 0`` is to only
|
|
224
|
+
# look at the link directly given back by the record, we add 1
|
|
225
|
+
# for the loop to ensure it runs once in that case.
|
|
226
|
+
for _ in range(max_redirects + 1):
|
|
227
|
+
headers = {}
|
|
228
|
+
if token is not None and urlsplit(url).netloc == host:
|
|
229
|
+
headers["Authorization"] = "Bearer %s" % token
|
|
230
|
+
|
|
231
|
+
with requests.get(url, stream=True, headers=headers) as r:
|
|
232
|
+
r.raise_for_status()
|
|
233
|
+
if "Location" in r.headers:
|
|
234
|
+
# Redirection - follow to the next URL
|
|
235
|
+
url = r.headers["Location"]
|
|
236
|
+
else:
|
|
237
|
+
with open(fname, "wb") as f:
|
|
238
|
+
content_length = r.headers.get("content-length", None)
|
|
239
|
+
if content_length is not None:
|
|
240
|
+
content_length = int(content_length)
|
|
241
|
+
pbar = tqdm(total=content_length // 1024, unit="kB")
|
|
242
|
+
for chunk in r.iter_content(chunk_size=1024):
|
|
243
|
+
if chunk:
|
|
244
|
+
pbar.update()
|
|
245
|
+
f.write(chunk)
|
|
246
|
+
print(f"Download to {fname} complete.")
|
|
247
|
+
return
|
|
248
|
+
raise RuntimeError(f"Exceeded maximum number of redirects ({max_redirects})")
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
if __name__ == "__main__":
|
|
252
|
+
parser = argparse.ArgumentParser(
|
|
253
|
+
description="download_from_record downloads the files attached to a "
|
|
254
|
+
"CaltechDATA or CaltechAUTHORS record"
|
|
255
|
+
)
|
|
256
|
+
parser.add_argument(
|
|
257
|
+
"record_id", help="The record ID for the record to download files from"
|
|
258
|
+
)
|
|
259
|
+
parser.add_argument(
|
|
260
|
+
"filenames",
|
|
261
|
+
nargs="*",
|
|
262
|
+
default=None,
|
|
263
|
+
help="The names of the files to download. If omitted, all files in "
|
|
264
|
+
"the record are downloaded.",
|
|
265
|
+
)
|
|
266
|
+
parser.add_argument(
|
|
267
|
+
"-output_path",
|
|
268
|
+
default=".",
|
|
269
|
+
help="Existing directory to download the files into",
|
|
270
|
+
)
|
|
271
|
+
parser.add_argument(
|
|
272
|
+
"-max_redirects",
|
|
273
|
+
type=int,
|
|
274
|
+
default=5,
|
|
275
|
+
help="Maximum number of redirects to follow per file",
|
|
276
|
+
)
|
|
277
|
+
parser.add_argument("-test", dest="production", action="store_false")
|
|
278
|
+
parser.add_argument("-authors", dest="authors", action="store_true")
|
|
279
|
+
parser.add_argument(
|
|
280
|
+
"-token",
|
|
281
|
+
default=os.environ.get("RDMTOK"),
|
|
282
|
+
help="Personal access token, needed to download restricted records. "
|
|
283
|
+
"Defaults to the RDMTOK environment variable, if set.",
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
args = parser.parse_args()
|
|
287
|
+
|
|
288
|
+
download_files_from_record(
|
|
289
|
+
args.record_id,
|
|
290
|
+
args.output_path,
|
|
291
|
+
filenames=args.filenames or None,
|
|
292
|
+
max_redirects=args.max_redirects,
|
|
293
|
+
production=args.production,
|
|
294
|
+
authors=args.authors,
|
|
295
|
+
token=args.token,
|
|
296
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: caltechdata_api
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Python wrapper for CaltechDATA API.
|
|
5
5
|
Author: Rohan Bhattarai, Elizabeth Won
|
|
6
6
|
Author-email: Thomas E Morrell <tmorrell@caltech.edu>, Alexander A Abakah <aabakah@caltech.edu>, Kshemaahna Nagi <knagi@caltech.edu>
|
|
@@ -7,7 +7,7 @@ caltechdata_api/caltechdata_write.py
|
|
|
7
7
|
caltechdata_api/cli.py
|
|
8
8
|
caltechdata_api/customize_schema.py
|
|
9
9
|
caltechdata_api/download_file.py
|
|
10
|
-
caltechdata_api/
|
|
10
|
+
caltechdata_api/download_from_record.py
|
|
11
11
|
caltechdata_api/get_metadata.py
|
|
12
12
|
caltechdata_api/md_to_json.py
|
|
13
13
|
caltechdata_api/utils.py
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import requests
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def get_files(idv, production=True):
|
|
6
|
-
# Returns file block
|
|
7
|
-
|
|
8
|
-
if production == True:
|
|
9
|
-
api_url = "https://data.caltech.edu/api/records/"
|
|
10
|
-
else:
|
|
11
|
-
api_url = "https://data.caltechlibrary.dev/api/records/"
|
|
12
|
-
|
|
13
|
-
r = requests.get(api_url + str(idv) + "/files")
|
|
14
|
-
r_data = r.json()
|
|
15
|
-
if "message" in r_data:
|
|
16
|
-
raise AssertionError(
|
|
17
|
-
"id "
|
|
18
|
-
+ str(idv)
|
|
19
|
-
+ " expected http status 200, got "
|
|
20
|
-
+ str(r.status_code)
|
|
21
|
-
+ " "
|
|
22
|
-
+ r_data["message"]
|
|
23
|
-
)
|
|
24
|
-
if not "entries" in r_data:
|
|
25
|
-
raise AssertionError("expected as entries property in response, got " + r_data)
|
|
26
|
-
return r_data["entries"]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if __name__ == "__main__":
|
|
30
|
-
parser = argparse.ArgumentParser(
|
|
31
|
-
description="get_files queries the caltechDATA (Invenio 3) API\
|
|
32
|
-
and returns file information"
|
|
33
|
-
)
|
|
34
|
-
parser.add_argument(
|
|
35
|
-
"ids",
|
|
36
|
-
metavar="ID",
|
|
37
|
-
type=str,
|
|
38
|
-
nargs="+",
|
|
39
|
-
help="The CaltechDATA ID for each record of interest",
|
|
40
|
-
)
|
|
41
|
-
parser.add_argument("-test", dest="production", action="store_false")
|
|
42
|
-
|
|
43
|
-
args = parser.parse_args()
|
|
44
|
-
|
|
45
|
-
production = args.production
|
|
46
|
-
|
|
47
|
-
for idv in args.ids:
|
|
48
|
-
metadata = get_files(idv, production)
|
|
49
|
-
print(metadata)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/date_types.yaml
RENAMED
|
File without changes
|
{caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/description_types.yaml
RENAMED
|
File without changes
|
{caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/identifier_types.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/relation_types.yaml
RENAMED
|
File without changes
|
{caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/resource_types.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api/vocabularies/title_types.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{caltechdata_api-2.1.0 → caltechdata_api-2.2.1}/caltechdata_api.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|