Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .github/scripts/generate_metadata_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""Generate CITATION.cff and bioschemas.yml from metadata.yml."""

import json

import yaml

METADATA_FILE = "metadata.yml"
CITATION_FILE = "CITATION.cff"
BIOSCHEMAS_FILE = "bioschemas.yml"

TITLE_SUFFIX = " - CodeRefinery lesson"

SPDX_LICENSE_URLS = {
"CC-BY-4.0": "https://creativecommons.org/licenses/by/4.0/",
}


def load_yaml(path):
with open(path, encoding="utf-8") as f:
return yaml.safe_load(f)


def license_url(spdx_id):
return SPDX_LICENSE_URLS.get(spdx_id, spdx_id)


def bioschemas_authors(authors):
result = []
for author in authors:
if "family-names" in author:
result.append(
{
"@type": "Person",
"name": f"{author['given-names']} {author['family-names']}",
}
)
else:
result.append({"@type": "Organization", "name": author["name"]})
return result


def write_citation_cff(meta):
citation = {
"cff-version": "1.2.0",
"message": (
"If you use this lesson material, please cite it using "
"these metadata."
),
"authors": meta["authors"],
"title": meta["title"] + TITLE_SUFFIX,
"type": "dataset",
"abstract": meta.get("abstract", ""),
"version": meta["version"],
"doi": meta["doi"],
"date-released": meta["version"],
"url": meta["url"],
"license": meta["license"],
"repository-code": meta["repository-code"],
}

if meta.get("maintainers"):
citation["contact"] = meta["maintainers"]

with open(CITATION_FILE, "w", encoding="utf-8") as f:
yaml.safe_dump(citation, f, sort_keys=False, allow_unicode=True)


def write_bioschemas(meta):
bioschemas = {
"@context": "https://schema.org/",
"@type": "LearningResource",
"@id": meta["url"],
"description": meta.get("abstract", ""),
"keywords": ", ".join(meta.get("keywords", [])),
"name": meta["title"] + TITLE_SUFFIX,
"author": bioschemas_authors(meta["authors"]),
# Disabled for now.
# "maintainer": bioschemas_authors(meta.get("maintainers", [])),
"about": meta.get("abstract", ""),
"audience": meta.get("audience", ""),
"competencyRequired": meta.get("competencyRequired", ""),
"educationalLevel": meta.get("educationalLevel", ""),
"identifier": f"https://doi.org/{meta['doi']}",
"inLanguage": meta.get("inLanguage", ""),
"learningResourceType": meta.get("learningResourceType", ""),
"license": license_url(meta["license"]),
"teaches": meta.get("teaches", ""),
"url": meta["url"],
"accessibilitySummary": meta.get("accessibilitySummary", ""),
"isPartOf": meta.get("isPartOf", ""),
"version": meta["version"],
}

with open(BIOSCHEMAS_FILE, "w", encoding="utf-8") as f:
json.dump(bioschemas, f, indent=4)
f.write("\n")


def main():
meta = load_yaml(METADATA_FILE)
write_citation_cff(meta)
write_bioschemas(meta)


if __name__ == "__main__":
main()
128 changes: 56 additions & 72 deletions .github/scripts/publish_to_zenodo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

BASE_URL = "https://zenodo.org/api"

TITLE_SUFFIX = " - CodeRefinery lesson"


json_headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}",
Expand All @@ -23,47 +25,50 @@
"Authorization": f"Bearer {ACCESS_TOKEN}"
}

def load_citation():
with open("CITATION.cff", encoding="utf-8") as f:
def load_metadata():
with open("metadata.yml", encoding="utf-8") as f:
return yaml.safe_load(f)


def cff_to_creators(authors):
def authors_to_creators(authors):
creators = []

for author in authors:
if "family-names" in author:
creators.append(
{
"name": (
f"{author['family-names']}, "
f"{author['given-names']}"
)
}
)
creator = {
"name": (
f"{author['family-names']}, "
f"{author['given-names']}"
)
}
else:
creators.append({"name": author["name"]})
creator = {"name": author["name"]}

if author.get("orcid"):
creator["orcid"] = author["orcid"].removeprefix("https://orcid.org/")

creators.append(creator)

return creators



# Load metadata from CITATION.cff
# Load metadata from metadata.yml

cff = load_citation()
meta = load_metadata()

description = cff.get("abstract", "")
description = meta.get("abstract", "")

if cff.get("repository-code"):
if meta.get("repository-code"):
description += (
f'<br><p>Source code: '
f'<a href="{cff["repository-code"]}">{cff["repository-code"]}</a></p>'
f'<a href="{meta["repository-code"]}">{meta["repository-code"]}</a></p>'
)

if cff.get("url"):
if meta.get("url"):
description += (
f'<br><p>Lesson website: '
f'<a href="{cff["url"]}">{cff["url"]}</a></p>'
f'<a href="{meta["url"]}">{meta["url"]}</a></p>'
)

description += (
Expand All @@ -72,15 +77,21 @@ def cff_to_creators(authors):
)

metadata = {
"title": cff["title"],
"upload_type": "dataset",
"title": meta["title"] + TITLE_SUFFIX,
"upload_type": "lesson",
"description": description,
"creators": cff_to_creators(cff["authors"]),
"keywords": cff.get("keywords", []),
"version": str(cff.get("version")) if cff.get("version") is not None else None,
"license": cff.get("license"),
"creators": authors_to_creators(meta["authors"]),
"keywords": meta.get("keywords", []),
"version": meta.get("version"),
"license": meta.get("license"),
}

if meta.get("maintainers"):
contributors = authors_to_creators(meta["maintainers"])
for contributor in contributors:
contributor["type"] = "ContactPerson"
metadata["contributors"] = contributors


# Discard any unpublished draft left over from a previous failed run.
# Zenodo only allows one unpublished new-version draft per concept at
Expand Down Expand Up @@ -161,45 +172,14 @@ def cff_to_creators(authors):

print(f"Removed inherited file {f['filename']}")


# Upload release archive

# Upload lesson PDF from the gh-pages branch
# (built by sphinx.yml as OUTPUT_BASENAME.pdf, OUTPUT_BASENAME being
# "{owner}-{repo}")

tag = os.environ["GITHUB_REF_NAME"]
repo_name = os.environ['GITHUB_REPOSITORY']
owner, repo = repo_name.split("/", 1)


archive_url = (
f"https://github.com/"
f"{repo_name}"
f"/archive/refs/tags/{tag}.zip"
)

archive_name = f"{owner}-{repo}-{tag}.zip"

download = requests.get(archive_url)
download.raise_for_status()

with open(archive_name, "wb") as fp:
fp.write(download.content)

with open(archive_name, "rb") as fp:
r = requests.put(
f"{bucket_url}/{archive_name}",
data=fp,
headers=upload_headers
)

r.raise_for_status()

print(f"Uploaded {archive_name}")


# Upload lesson PDF from the gh-pages branch
# (built by sphinx.yml as OUTPUT_BASENAME.pdf, OUTPUT_BASENAME being
# "{owner}-{repo}")

pdf_source_name = f"{owner}-{repo}.pdf"
pdf_name = f"{owner}-{repo}-{tag}.pdf"

Expand All @@ -225,29 +205,33 @@ def cff_to_creators(authors):

print(f"Uploaded {pdf_name}")

# Upload release archive

# Show the PDF first (Zenodo previews the first file in the list).

r = requests.get(
f"{BASE_URL}/deposit/depositions/{deposition_id}/files",
headers=json_headers,
archive_url = (
f"https://github.com/"
f"{repo_name}"
f"/archive/refs/tags/{tag}.zip"
)

r.raise_for_status()
archive_name = f"{owner}-{repo}-{tag}.zip"

files = r.json()
download = requests.get(archive_url)
download.raise_for_status()

sort_order = sorted(files, key=lambda f: f["filename"] != pdf_name)
with open(archive_name, "wb") as fp:
fp.write(download.content)

r = requests.put(
f"{BASE_URL}/deposit/depositions/{deposition_id}/files/sort",
data=json.dumps([{"id": f["id"]} for f in sort_order]),
headers=json_headers,
)
with open(archive_name, "rb") as fp:
r = requests.put(
f"{bucket_url}/{archive_name}",
data=fp,
headers=upload_headers
)

r.raise_for_status()

print(f"File order: {[f['filename'] for f in sort_order]}")
print(f"Uploaded {archive_name}")


# Update metadata
Expand Down
32 changes: 32 additions & 0 deletions .github/workflows/generate-metadata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Generate metadata files

on:
push:
branches: [main]
paths:
- metadata.yml

jobs:
generate:
name: Generate CITATION.cff and bioschemas.yml
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install dependencies
run: pip install pyyaml

- name: Generate CITATION.cff and bioschemas.yml
run: python .github/scripts/generate_metadata_files.py

- name: Commit generated files
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add CITATION.cff bioschemas.yml
git diff --staged --quiet || git commit -m "Regenerate CITATION.cff and bioschemas.yml from metadata.yml"
git push
20 changes: 0 additions & 20 deletions .github/workflows/validate-cff.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ _build/
venv/
.*.sw?
*DS_Store
*__pycache__
7 changes: 4 additions & 3 deletions content/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys
sys.path.insert(0, os.path.abspath('.'))


# -- Project information -----------------------------------------------------
Expand Down Expand Up @@ -40,6 +40,7 @@
"sphinx_rtd_theme_ext_color_contrast",
"sphinx_coderefinery_branding",
"sphinx_bioschemas",
"lesson_metadata",
]

# Settings for myst_nb:
Expand Down
Loading
Loading