From c82af7c1b831c4c4508ee206da3c7c54b9c0b7a3 Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Fri, 25 Oct 2024 13:35:26 -0700 Subject: [PATCH 01/15] Update customize_schema.py --- caltechdata_api/customize_schema.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/caltechdata_api/customize_schema.py b/caltechdata_api/customize_schema.py index 20e0b69..5403244 100644 --- a/caltechdata_api/customize_schema.py +++ b/caltechdata_api/customize_schema.py @@ -1,5 +1,4 @@ -# Convert a DataCite 4 or 4.3 standard schema json record to the customized internal -# schema used by TIND in CaltechDATA +# Convert a DataCite 4.3 standard schema json record to the InvenioRDM schema import argparse import json from datetime import date From 2fde1bbb6d6c31ed62ba6b6af1a45448c804d31c Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Tue, 29 Oct 2024 14:38:49 -0700 Subject: [PATCH 02/15] Remove token printing --- caltechdata_api/cli.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/caltechdata_api/cli.py b/caltechdata_api/cli.py index 800d6f0..ecb54a3 100644 --- a/caltechdata_api/cli.py +++ b/caltechdata_api/cli.py @@ -71,6 +71,11 @@ def get_or_set_token(production=True): with open(token_file, "rb") as f: encrypted_token = f.read() token = decrypt_token(encrypted_token, key) + print( + "Using saved CaltechDATA production token." + if production + else "Using saved CaltechDATA test token." + ) return token except FileNotFoundError: while True: @@ -422,8 +427,6 @@ def main(): def create_record(production): token = get_or_set_token(production) - # keep_file = input("Do you want to keep your existing files? (yes/no): ").lower() == "yes" - print("Using CaltechDATA token:", token) while True: choice = get_user_input( "Do you want to use metadata from an existing file or create new metadata? (existing/create): " From bb46bfcd23e7158909e51ef672c71935af7b2e63 Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Tue, 29 Oct 2024 16:11:33 -0700 Subject: [PATCH 03/15] Update iga and codemeta2cff --- .github/workflows/codemeta2cff.yml | 26 +++++++++++++ .github/workflows/iga.yaml | 59 ++++++++++++++---------------- caltechdata_api/get_metadata.py | 1 - 3 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/codemeta2cff.yml diff --git a/.github/workflows/codemeta2cff.yml b/.github/workflows/codemeta2cff.yml new file mode 100644 index 0000000..ddfb8f5 --- /dev/null +++ b/.github/workflows/codemeta2cff.yml @@ -0,0 +1,26 @@ +name: CodeMeta2CFF +run-name: Run CodeMeta2CFF after ${{github.event_name}} by ${{github.actor}} + +on: + push: + paths: ['codemeta.json'] + workflow_dispatch: + inputs: + reason: + description: 'Reason' + required: false + default: 'Manual trigger' + +jobs: + CodeMeta2CFF: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Convert CFF + uses: caltechlibrary/codemeta2cff@main + - name: Commit CFF + uses: EndBug/add-and-commit@v9 + with: + message: 'Add updated CITATION.cff from codemeta.json file' + add: 'CITATION.cff' diff --git a/.github/workflows/iga.yaml b/.github/workflows/iga.yaml index 7488c00..6bf8a35 100644 --- a/.github/workflows/iga.yaml +++ b/.github/workflows/iga.yaml @@ -1,4 +1,4 @@ -name: InvenioRDM GitHub Archiver and CodeMeta2CFF +name: InvenioRDM GitHub Archiver env: INVENIO_SERVER: https://data.caltech.edu @@ -10,47 +10,43 @@ env: parent_record: "6qhkm-7n074" debug: false -# ~~~~~~~~~~ The rest of this file should be left as-is ~~~~~~~~~~ +# ╭────────────────────────────────────────────╮ +# │ The rest of this file should be left as-is │ +# ╰────────────────────────────────────────────╯ + +name: InvenioRDM GitHub Archiver on: release: types: [published] workflow_dispatch: inputs: release_tag: - description: "The tag of the release to archive:" + description: The release tag (empty = latest) + parent_record: + description: ID of parent record (for versioning) + community: + description: Name of InvenioRDM community (if any) draft: - default: false - description: "Mark the record as a draft:" + description: Mark the record as a draft + type: boolean all_assets: - default: false - description: "Attach all GitHub assets:" + description: Attach all GitHub assets + type: boolean all_metadata: - default: false - description: "Include additional GitHub metadata:" - community: - description: "Send record to InvenioRDM community:" - parent_record: - description: "ID of parent record (for versioning):" + description: Include additional GitHub metadata + type: boolean + debug: + description: Print debug info in the GitHub log + type: boolean + +run-name: Archive ${{inputs.release_tag || 'latest release'}} in InvenioRDM jobs: - CodeMeta2CFF: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Convert CFF - uses: caltechlibrary/codemeta2cff@main - - name: Commit CFF - uses: EndBug/add-and-commit@v9 - with: - message: 'Add CITATION.cff for release' - add: "['CITATION.cff']" - push: origin HEAD:main run_iga: - name: "Send to ${{needs.get_repository.outputs.server}}" + name: Send to ${{needs.get_repository.outputs.server}} runs-on: ubuntu-latest - needs: [get_repository, CodeMeta2CFF] + needs: get_repository steps: - - uses: caltechlibrary/iga@main + - uses: caltechlibrary/iga@v1 with: INVENIO_SERVER: ${{env.INVENIO_SERVER}} INVENIO_TOKEN: ${{secrets.INVENIO_TOKEN}} @@ -62,10 +58,11 @@ jobs: parent_record: ${{github.event.inputs.parent_record || env.parent_record}} release_tag: ${{github.event.inputs.release_tag || 'latest'}} get_repository: - name: "Get repository name" + name: Get repository name runs-on: ubuntu-latest outputs: server: ${{steps.parse.outputs.host}} steps: - - id: parse + - name: Extract name from INVENIO_SERVER + id: parse run: echo "host=$(cut -d'/' -f3 <<< ${{env.INVENIO_SERVER}} | cut -d':' -f1)" >> $GITHUB_OUTPUT diff --git a/caltechdata_api/get_metadata.py b/caltechdata_api/get_metadata.py index a371f4f..b864073 100644 --- a/caltechdata_api/get_metadata.py +++ b/caltechdata_api/get_metadata.py @@ -27,7 +27,6 @@ def get_metadata( headers["Authorization"] = "Bearer %s" % token response = requests.get(url + idv, headers=headers, verify=verify) - print(response.headers) if response.status_code != 200: raise Exception(response.text) else: From 27b842c707f6ae8f53e7ca5e85bbdd1099e035ee Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Tue, 29 Oct 2024 16:13:34 -0700 Subject: [PATCH 04/15] Update iga and codemeta2cff --- .github/workflows/iga.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/iga.yaml b/.github/workflows/iga.yaml index 6bf8a35..4215dd5 100644 --- a/.github/workflows/iga.yaml +++ b/.github/workflows/iga.yaml @@ -1,4 +1,3 @@ -name: InvenioRDM GitHub Archiver env: INVENIO_SERVER: https://data.caltech.edu From cd3d2ec87609ef3ca34a5cb3446fea618ad282b3 Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Wed, 30 Oct 2024 10:32:45 -0700 Subject: [PATCH 05/15] Add authors to get_metadata and edit example --- caltechdata_api/get_metadata.py | 36 ++++++++++++++++++++++++++------- edit.py | 4 +++- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/caltechdata_api/get_metadata.py b/caltechdata_api/get_metadata.py index b864073..79143f3 100644 --- a/caltechdata_api/get_metadata.py +++ b/caltechdata_api/get_metadata.py @@ -8,20 +8,38 @@ def get_metadata( - idv, production=True, validate=True, emails=False, schema="43", token=False + idv, + production=True, + validate=True, + emails=False, + schema="43", + token=False, + authors=False, ): # Returns just DataCite metadata or DataCite metadata with emails if production == True: - url = "https://data.caltech.edu/api/records/" + if authors: + url = "https://authors.library.caltech.edu/api/records/" + else: + url = "https://data.caltech.edu/api/records/" verify = True else: - url = "https://data.caltechlibrary.dev/api/records/" + if authors: + url = "https://authors.caltechlibrary.dev/api/records/" + else: + url = "https://data.caltechlibrary.dev/api/records/" verify = True - headers = { - "accept": "application/vnd.datacite.datacite+json", - } + if authors: + headers = { + "accept": "application/json", + } + validate = False + else: + headers = { + "accept": "application/vnd.datacite.datacite+json", + } if token: headers["Authorization"] = "Bearer %s" % token @@ -58,6 +76,7 @@ def get_metadata( help="The CaltechDATA ID for each record of interest", ) parser.add_argument("-test", dest="production", action="store_false") + parser.add_argument("-authors", dest="authors", action="store_true") parser.add_argument("-xml", dest="save_xml", action="store_true") parser.add_argument( "-skip_validate", @@ -71,6 +90,7 @@ def get_metadata( production = args.production schema = args.schema + authors = args.authors skip_validate = args.skip_validate if skip_validate: validate = False @@ -78,7 +98,9 @@ def get_metadata( validate = True for idv in args.ids: - metadata = get_metadata(idv, production, validate, schema) + metadata = get_metadata( + idv, production, validate, schema=schema, authors=authors + ) outfile = open(str(idv) + ".json", "w") outfile.write(json.dumps(metadata, indent=4)) outfile.close() diff --git a/edit.py b/edit.py index 786fbeb..205beb0 100644 --- a/edit.py +++ b/edit.py @@ -15,6 +15,7 @@ parser.add_argument("-fnames", nargs="*", help="New Files") parser.add_argument("-flinks", nargs="*", help="New File Links") parser.add_argument("-schema", default="43", help="Metadata Schema") +parser.add_argument("-authors", action="store_true", help="Edit CaltechAUTHORS") args = parser.parse_args() # Get access token set as environment variable with source token.bash @@ -26,7 +27,7 @@ else: metadata = {} -production = False +production = True publish = True response = caltechdata_edit( @@ -38,5 +39,6 @@ args.schema, publish, args.flinks, + authors=args.authors, ) print(response) From f06956c4c473f385fcb75049f82bdfdafd6ed413 Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Tue, 5 Nov 2024 11:10:12 -0800 Subject: [PATCH 06/15] Update iga.yaml --- .github/workflows/iga.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/iga.yaml b/.github/workflows/iga.yaml index 4215dd5..7d9edc9 100644 --- a/.github/workflows/iga.yaml +++ b/.github/workflows/iga.yaml @@ -9,6 +9,11 @@ env: parent_record: "6qhkm-7n074" debug: false + # This variable is a setting for post-archiving CodeMeta file updates. + # If you don't have a CodeMeta file, you can remove the add_doi_codemeta + # job at the bottom of this file. + ref: main + # ╭────────────────────────────────────────────╮ # │ The rest of this file should be left as-is │ # ╰────────────────────────────────────────────╯ @@ -44,8 +49,11 @@ jobs: name: Send to ${{needs.get_repository.outputs.server}} runs-on: ubuntu-latest needs: get_repository + outputs: + record_doi: ${{steps.iga.outputs.record_doi}} steps: - uses: caltechlibrary/iga@v1 + id: iga with: INVENIO_SERVER: ${{env.INVENIO_SERVER}} INVENIO_TOKEN: ${{secrets.INVENIO_TOKEN}} @@ -65,3 +73,21 @@ jobs: - name: Extract name from INVENIO_SERVER id: parse run: echo "host=$(cut -d'/' -f3 <<< ${{env.INVENIO_SERVER}} | cut -d':' -f1)" >> $GITHUB_OUTPUT + add_doi_codemeta: + name: "Add ${{needs.run_iga.outputs.record_doi}} to codemeta.json" + needs: run_iga + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ env.ref }} + - name: Install sde + run: pip install sde + - name: Add DOI to CodeMeta File + run: sde identifier ${{needs.run_iga.outputs.record_doi}} codemeta.json + - name: Commit CFF + uses: EndBug/add-and-commit@v9 + with: + message: 'Add DOI to codemeta.json file' + add: 'codemeta.json' From 8b3110eb13f5ed6845496ad07c048d8de63adb3f Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Tue, 5 Nov 2024 12:15:47 -0800 Subject: [PATCH 07/15] Update iga.yaml --- .github/workflows/iga.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/iga.yaml b/.github/workflows/iga.yaml index 7d9edc9..ca832b7 100644 --- a/.github/workflows/iga.yaml +++ b/.github/workflows/iga.yaml @@ -49,7 +49,7 @@ jobs: name: Send to ${{needs.get_repository.outputs.server}} runs-on: ubuntu-latest needs: get_repository - outputs: + outputs: record_doi: ${{steps.iga.outputs.record_doi}} steps: - uses: caltechlibrary/iga@v1 From d245fe4973525e7aa7d72bcd802db413bc0d66df Mon Sep 17 00:00:00 2001 From: tmorrell Date: Tue, 5 Nov 2024 20:22:03 +0000 Subject: [PATCH 08/15] Add DOI to codemeta.json file --- codemeta.json | 121 ++++++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 59 deletions(-) mode change 100644 => 100755 codemeta.json diff --git a/codemeta.json b/codemeta.json old mode 100644 new mode 100755 index a06a1d6..2848a84 --- a/codemeta.json +++ b/codemeta.json @@ -1,72 +1,75 @@ { - "@context": "https://doi.org/10.5063/schema/codemeta-2.0", - "@type": "SoftwareSourceCode", - "description": "Python wrapper for CaltechDATA API.", - "name": "caltechdata_api", - "codeRepository": "https://github.com/caltechlibrary/caltechdata_api", - "issueTracker": "https://github.com/caltechlibrary/caltechdata_api/issues", - "license": "https://data.caltech.edu/license", - "version": "1.8.1", - "author": [ - { - "@type": "Person", - "givenName": "Thomas E", - "familyName": "Morrell", - "affiliation": { + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "@type": "SoftwareSourceCode", + "description": "Python wrapper for CaltechDATA API.", + "name": "caltechdata_api", + "codeRepository": "https://github.com/caltechlibrary/caltechdata_api", + "issueTracker": "https://github.com/caltechlibrary/caltechdata_api/issues", + "license": "https://data.caltech.edu/license", + "version": "1.8.1", + "author": [ + { + "@type": "Person", + "givenName": "Thomas E", + "familyName": "Morrell", + "affiliation": { "@type": "Organization", "name": "Caltech Library" }, - "email": "tmorrell@caltech.edu", - "@id": "https://orcid.org/0000-0001-9266-5146" - }, - { - "@type": "Person", - "givenName": "Bhattarai", - "familyName": "Rohan ", - "affiliation": { + "email": "tmorrell@caltech.edu", + "@id": "https://orcid.org/0000-0001-9266-5146" + }, + { + "@type": "Person", + "givenName": "Bhattarai", + "familyName": "Rohan ", + "affiliation": { "@type": "Organization", "name": "Caltech" }, - "@id": "https://orcid.org/0009-0007-0323-4733" - }, - { - "@type": "Person", - "givenName": "Won", - "familyName": "Elizabeth", - "affiliation": { + "@id": "https://orcid.org/0009-0007-0323-4733" + }, + { + "@type": "Person", + "givenName": "Won", + "familyName": "Elizabeth", + "affiliation": { "@type": "Organization", "name": "Caltech" } - }], - "developmentStatus": "active", - "downloadUrl": "https://github.com/caltechlibrary/caltechdata_api/archive/1.8.1.zip", - "keywords": [ - "GitHub", - "metadata", - "software", - "InvenioRDM" - ], - "maintainer": [ - { - "@type": "Person", - "givenName": "Thomas E", - "familyName": "Morrell", - "affiliation": { + } + ], + "developmentStatus": "active", + "downloadUrl": "https://github.com/caltechlibrary/caltechdata_api/archive/1.8.1.zip", + "keywords": [ + "GitHub", + "metadata", + "software", + "InvenioRDM" + ], + "maintainer": [ + { + "@type": "Person", + "givenName": "Thomas E", + "familyName": "Morrell", + "affiliation": { "@type": "Organization", "name": "Caltech Library" }, - "email": "tmorrell@caltech.edu", - "@id": "https://orcid.org/0000-0001-9266-5146" - }], - "funding": { - "@type": "Grant", - "identifier":"2322420", - "name": "CC* Data Storage: Closing Caltech's data storage gap: from ad-hoc to well-managed stewardship of large-scale datasets", - "funder": { - "@id": "https://doi.org/10.13039/100000001", - "@type": "Organization", - "name": "National Science Foundation" - } - }, - "programmingLanguage": "Python" -} + "email": "tmorrell@caltech.edu", + "@id": "https://orcid.org/0000-0001-9266-5146" + } + ], + "funding": { + "@type": "Grant", + "identifier": "2322420", + "name": "CC* Data Storage: Closing Caltech's data storage gap: from ad-hoc to well-managed stewardship of large-scale datasets", + "funder": { + "@id": "https://doi.org/10.13039/100000001", + "@type": "Organization", + "name": "National Science Foundation" + } + }, + "programmingLanguage": "Python", + "identifier": "10.22002/5rbqw-9cc91" +} \ No newline at end of file From e9865ac673894f59ae08924e8eb1f9f4f25dc996 Mon Sep 17 00:00:00 2001 From: tmorrell Date: Tue, 5 Nov 2024 20:43:29 +0000 Subject: [PATCH 09/15] Add updated CITATION.cff from codemeta.json file --- CITATION.cff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CITATION.cff b/CITATION.cff index 8e88ccb..d932e5d 100755 --- a/CITATION.cff +++ b/CITATION.cff @@ -18,4 +18,4 @@ keywords: - metadata - software - InvenioRDM -date-released: 2024-10-18 +date-released: 2024-11-05 From 4047c6f4eaa1575ddd5aef7e8a1cdb7d33938fb5 Mon Sep 17 00:00:00 2001 From: tmorrell Date: Tue, 5 Nov 2024 23:34:37 +0000 Subject: [PATCH 10/15] Add DOI to codemeta.json file --- codemeta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemeta.json b/codemeta.json index 2848a84..c94d842 100755 --- a/codemeta.json +++ b/codemeta.json @@ -71,5 +71,5 @@ } }, "programmingLanguage": "Python", - "identifier": "10.22002/5rbqw-9cc91" + "identifier": "10.22002/p0vnk-bc640" } \ No newline at end of file From 3109fd6d48857366bb33c730399999cbd241dc48 Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Wed, 6 Nov 2024 09:17:02 -0800 Subject: [PATCH 11/15] Update codemeta.json --- codemeta.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codemeta.json b/codemeta.json index c94d842..b89e0f3 100755 --- a/codemeta.json +++ b/codemeta.json @@ -6,7 +6,7 @@ "codeRepository": "https://github.com/caltechlibrary/caltechdata_api", "issueTracker": "https://github.com/caltechlibrary/caltechdata_api/issues", "license": "https://data.caltech.edu/license", - "version": "1.8.1", + "version": "1.8.2", "author": [ { "@type": "Person", @@ -72,4 +72,4 @@ }, "programmingLanguage": "Python", "identifier": "10.22002/p0vnk-bc640" -} \ No newline at end of file +} From 775af05c587c27224ca437a17cafeac7cee67bfc Mon Sep 17 00:00:00 2001 From: tmorrell Date: Wed, 6 Nov 2024 17:17:20 +0000 Subject: [PATCH 12/15] Add updated CITATION.cff from codemeta.json file --- CITATION.cff | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index d932e5d..e626019 100755 --- a/CITATION.cff +++ b/CITATION.cff @@ -11,11 +11,11 @@ authors: abstract: Python wrapper for CaltechDATA API. repository-code: "https://github.com/caltechlibrary/caltechdata_api" type: software -version: 1.8.1 +version: 1.8.2 license-url: "https://data.caltech.edu/license" keywords: - GitHub - metadata - software - InvenioRDM -date-released: 2024-11-05 +date-released: 2024-11-06 From 4239313f8f742eee82ce0874b85a6b20eaab5030 Mon Sep 17 00:00:00 2001 From: tmorrell Date: Wed, 6 Nov 2024 17:22:45 +0000 Subject: [PATCH 13/15] Add DOI to codemeta.json file --- codemeta.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codemeta.json b/codemeta.json index b89e0f3..b4fce39 100755 --- a/codemeta.json +++ b/codemeta.json @@ -71,5 +71,5 @@ } }, "programmingLanguage": "Python", - "identifier": "10.22002/p0vnk-bc640" -} + "identifier": "10.22002/wfjr5-kw507" +} \ No newline at end of file From 86571b345d3e6eec8b0ea985ea18ac3681f37403 Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Thu, 7 Nov 2024 15:09:26 -0800 Subject: [PATCH 14/15] Update iga.yaml --- .github/workflows/iga.yaml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/iga.yaml b/.github/workflows/iga.yaml index ca832b7..f70bddb 100644 --- a/.github/workflows/iga.yaml +++ b/.github/workflows/iga.yaml @@ -9,9 +9,9 @@ env: parent_record: "6qhkm-7n074" debug: false - # This variable is a setting for post-archiving CodeMeta file updates. + # This variable is a setting for post-archiving CodeMeta file updates. # If you don't have a CodeMeta file, you can remove the add_doi_codemeta - # job at the bottom of this file. + # and Coremeta2CFF jobs at the bottom of this file. ref: main # ╭────────────────────────────────────────────╮ @@ -91,3 +91,18 @@ jobs: with: message: 'Add DOI to codemeta.json file' add: 'codemeta.json' + CodeMeta2CFF: + runs-on: ubuntu-latest + needs: add_doi_codemeta + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ env.ref }} + - name: Convert CFF + uses: caltechlibrary/codemeta2cff@main + - name: Commit CFF + uses: EndBug/add-and-commit@v9 + with: + message: 'Add updated CITATION.cff from codemeta.json file' + add: 'CITATION.cff' From faa360a395a8581b0d1297ea23564405355ccbb0 Mon Sep 17 00:00:00 2001 From: Tom Morrell Date: Thu, 7 Nov 2024 15:45:48 -0800 Subject: [PATCH 15/15] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d0fb215..140a130 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # CaltechDATA API Python Library +[![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) + 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. ## Features