Skip to content
Draft
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
26 changes: 26 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Lint

on:
push:
pull_request:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: '.python-version'
cache: pip

- name: Install dependencies
run: |
pip install -r requirements.txt -r dev-requirements.txt types-jsonschema

- name: Run pre-commit hooks
run: |
pre-commit run --all-files
21 changes: 14 additions & 7 deletions .github/workflows/update-metadata.yml
Original file line number Diff line number Diff line change
@@ -1,41 +1,48 @@
name: Update Metadata

on:
push:
workflow_run:
workflows: ["Lint"]
types:
- completed
workflow_dispatch:

# Prevent infinite loops when workflow commits changes
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.ref }}
cancel-in-progress: true

jobs:
update-metadata:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
# Need write permission to push changes
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
ref: ${{ github.event.workflow_run.head_sha || github.ref }}
fetch-depth: 0

- name: Extract version from tag (if triggered by tag)
id: extract_version
run: |
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
TAG_NAME=${GITHUB_REF#refs/tags/}
# Remove 'v' prefix if present (v1.0.0 -> 1.0.0)
REF="${{ github.event.workflow_run.head_branch || github.ref }}"
if [[ "$REF" == refs/tags/v* ]] || git tag --points-at HEAD | grep -q '^v'; then
TAG_NAME=$(git tag --points-at HEAD | grep '^v' | head -1)
if [[ -z "$TAG_NAME" && "$REF" == refs/tags/v* ]]; then
TAG_NAME=${REF#refs/tags/}
fi
VERSION=${TAG_NAME#v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
echo "is_tag=true" >> $GITHUB_OUTPUT
echo "Triggered by version tag: $TAG_NAME (version: $VERSION)"
else
echo "is_tag=false" >> $GITHUB_OUTPUT
echo "Triggered by regular push to: $GITHUB_REF"
echo "Triggered by regular push to: $REF"
fi

- name: Set up Python
Expand Down
20 changes: 20 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
repos:
- repo: local
hooks:
- id: ruff
name: ruff
entry: bash -c 'ruff check --fix --target-version=py$(cat .python-version | tr -d "\n." ) "$@"' --
language: system
types: [python]

- id: ruff-format
name: ruff-format
entry: bash -c 'ruff format --target-version=py$(cat .python-version | tr -d "\n.") "$@"' --
language: system
types: [python]

- id: mypy
name: mypy
entry: bash -c 'mypy --python-version=$(cat .python-version | tr -d "\n") "$@"' --
language: system
types: [python]
2 changes: 1 addition & 1 deletion .zenodo.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"affiliation": "Universität Potsdam"
}
],
"description": "<p>Dieses interaktive Lehrbuch führt in die Erstellung von Open Educational Resources (OER) nach den Empfehlungen und Vorgaben des Datenkompetenzzentrumgs QUADRIGA als <a href=\"https://jupyterbook.org\">Jupyter Book</a> ein. Diese Empfehlungen und Vorgaben sind einerseits inhaltlicher und didaktischer andererseits technischer Natur. Diese OER richtet sich vorrangig an OER-Erstellende und kann als Startpunkt (Template) für die Erstellung eigener OER genutzt werden. Einzelne (Unter-)Kapitel bieten zudem Erläuterungen zu Funktionalitäten für Lernende.</p>\n<p>Dieses interaktive Lehrbuch kann als <a href=\"https://quadriga-dk.github.io/Book_Template/\" target=\"_blank\">Web-Version</a> verwendet, zur individuellen Anpassung heruntergeladen werden und steht darüber hinaus auch auf <a href=\"https://github.com/quadriga-dk/Book_Template\" target=\"_blank\">GitHub</a> zur Verfügung.</p>\n<p>Die QUADRIGA-OER sind nach einem einheitlichen <a href=\"https://quadriga-dk.github.io/Book_Template\" target=\"_blank\">Template</a> gestaltet, werden nach einem standardisierten Verfahren <a href=\"https://doi.org/10.5281/zenodo.18953896\" target=\"_blank\">qualitätsgeprüft</a> und mit Metadaten nach dem <a href=\"https://doi.org/10.5281/zenodo.18184772\" target=\"_blank\">QUADRIGA-Metadatenschema</a> ausgezeichnet.</p>\n<h5>QUADRIGA Datenkompetenzzentrum</h5>\n<p>QUADRIGA ist das Datenkompetenzzentrum der Wissenschaftsregion Berlin-Brandenburg. Für die beiden Anwendungsdomänen Digital Humanities und Verwaltungswissenschaft entstehen unter der Einbindung der Expertise der beiden Disziplinen Informatik und Informationswissenschaft Selbstlernangebote, die als OER in Form von Jupyter Books zur freien Nachnutzung zur Verfügung gestellt werden. Um den Forschungsprozess möglichst realistisch abzubilden, basieren die OER auf Fallstudien, denen wiederum ein eigens für das Projekt entwickeltes <a href=\"https://doi.org/10.5281/zenodo.14747822\" target=\"_blank\">Datenkompetenzframework</a> zugrunde liegt. Die Fallstudien nehmen drei für die Anwendungsdomänen repräsentativen Datentypen in den Blick: Bewegtes Bild, Tabelle und Text.</p>\n<p>Zu den Zielgruppen von QUADRIGA zählen insbesondere promovierende und promovierte Wissenschaftler*innen der genannten Disziplinen, die den Umgang mit digitalen Daten, Methoden und Werkzeugen erlernen und weiterentwickeln wollen.</p>\n<p>QUADRIGA ist eins von 11 Datenkompetenzzentren in Deutschland und wird vom <a href=\"https://www.bmftr.bund.de/DE/Forschung/Wissenschaftssystem/Forschungsdaten/DatenkompetenzenInDerWissenschaft/datenkompetenzeninderwissenschaft.html\" target=\"_blank\">Bundesministerium für Forschung, Technologie und Raumfahrt (BMFTR)</a> und von der Europäischen Union im Rahmen von <a href=\"https://next-generation-eu.europa.eu/index_de\" target=\"_blank\">NextGenerationEU</a> finanziert. Zu den Verbundpartnern zählen:\n <ul>\n <li>Universität Potsdam (Verbundkoordination) <i>(Förderkennzeichen: 16DKZ2034A)</i></li>\n <li>Filmuniversität Babelsberg KONRAD WOLF <i>(Förderkennzeichen: 16DKZ2034B)</i></li>\n <li>Fachhochschule Potsdam <i>(Förderkennzeichen: 16DKZ2034C)</i></li>\n <li>Fraunhofer FOKUS <i>(Förderkennzeichen: 16DKZ2034D)</i></li>\n <li>Freie Universität Berlin <i>(Förderkennzeichen: 16DKZ2034E)</i></li>\n <li>Technische Universität Berlin <i>(Förderkennzeichen: 16DKZ2034F)</i></li>\n <li>Gesellschaft für Informatik <i>(Förderkennzeichen: 16DKZ2034G)</i></li>\n <li>Humboldt-Universität zu Berlin <i>(Förderkennzeichen: 16DKZ2034H)</i></li>\n </ul>\n</p>\n\n<p>Mehr zum Aufbau und zur Umsetzung des Projekts können Sie im <a href=\"https://doi.org/10.5281/zenodo.10805015\" target=\"_blank\">Umsetzungskonzept</a> erfahren.</p>\n\n<p>Weitere Informationen sowie Publikationen finden Sie auf der <a href=\"https://www.quadriga-dk.de\" target=\"_blank\">Webseite</a>, in der <a href=\"https://zenodo.org/communities/quadriga\" target=\"_blank\">Zenodo-Community</a> und der <a href=\"https://github.com/quadriga-dk\" target=\"_blank\">GitHub-Organisation</a> des Projekts.</p>\n",
"description": "<p>Dieses interaktive Lehrbuch führt in die Erstellung von Open Educational Resources (OER) nach den Empfehlungen und Vorgaben des Datenkompetenzzentrumgs QUADRIGA als <a href=\"https://jupyterbook.org\">Jupyter Book</a> ein. Diese Empfehlungen und Vorgaben sind einerseits inhaltlicher und didaktischer andererseits technischer Natur. Diese OER richtet sich vorrangig an OER-Erstellende und kann als Startpunkt (Template) für die Erstellung eigener OER genutzt werden. Einzelne (Unter-)Kapitel bieten zudem Erläuterungen zu Funktionalitäten für Lernende.</p>\n<p>Dieses interaktive Lehrbuch kann als <a href=\"https://quadriga-dk.github.io/Book_Template/\" target=\"_blank\">Web-Version</a> verwendet, zur individuellen Anpassung heruntergeladen werden und steht darüber hinaus auch auf <a href=\"https://github.com/quadriga-dk/Book_Template\" target=\"_blank\">GitHub</a> zur Verfügung.</p>\n<p>Die QUADRIGA-OER sind nach einem einheitlichen <a href=\"https://quadriga-dk.github.io/Book_Template\" target=\"_blank\">Template</a> gestaltet, werden nach einem standardisierten Verfahren <a href=\"https://doi.org/10.5281/zenodo.18953896\" target=\"_blank\">qualitätsgeprüft</a> und mit Metadaten nach dem <a href=\"https://doi.org/10.5281/zenodo.18184772\" target=\"_blank\">QUADRIGA-Metadatenschema</a> ausgezeichnet.</p>\n<h5>QUADRIGA Datenkompetenzzentrum</h5>\n<p>QUADRIGA ist das Datenkompetenzzentrum der Wissenschaftsregion Berlin-Brandenburg. Für die beiden Anwendungsdomänen Digital Humanities und Verwaltungswissenschaft entstehen unter der Einbindung der Expertise der beiden Disziplinen Informatik und Informationswissenschaft Selbstlernangebote, die als OER in Form von Jupyter Books zur freien Nachnutzung zur Verfügung gestellt werden. Um den Forschungsprozess möglichst realistisch abzubilden, basieren die OER auf Fallstudien, denen wiederum ein eigens für das Projekt entwickeltes <a href=\"https://doi.org/10.5281/zenodo.14747822\" target=\"_blank\">Datenkompetenzframework</a> zugrunde liegt. Die Fallstudien nehmen drei für die Anwendungsdomänen repräsentativen Datentypen in den Blick: Bewegtes Bild, Tabelle und Text.</p>\n<p>Zu den Zielgruppen von QUADRIGA zählen insbesondere promovierende und promovierte Wissenschaftler*innen der genannten Disziplinen, die den Umgang mit digitalen Daten, Methoden und Werkzeugen erlernen und weiterentwickeln wollen.</p>\n<p>QUADRIGA ist eins von 11 Datenkompetenzzentren in Deutschland und wird vom <a href=\"https://www.bmftr.bund.de/DE/Forschung/Wissenschaftssystem/Forschungsdaten/DatenkompetenzenInDerWissenschaft/datenkompetenzeninderwissenschaft.html\" target=\"_blank\">Bundesministerium für Forschung, Technologie und Raumfahrt (BMFTR)</a> und von der Europäischen Union im Rahmen von <a href=\"https://next-generation-eu.europa.eu/index_de\" target=\"_blank\">NextGenerationEU</a> finanziert. Zu den Verbundpartnern zählen:\n <ul>\n <li>Universität Potsdam (Verbundkoordination) <i>(Förderkennzeichen: 16DKZ2034A)</i></li>\n <li>Filmuniversität Babelsberg KONRAD WOLF <i>(Förderkennzeichen: 16DKZ2034B)</i></li>\n <li>Fachhochschule Potsdam <i>(Förderkennzeichen: 16DKZ2034C)</i></li>\n <li>Fraunhofer FOKUS <i>(Förderkennzeichen: 16DKZ2034D)</i></li>\n <li>Freie Universität Berlin <i>(Förderkennzeichen: 16DKZ2034E)</i></li>\n <li>Technische Universität Berlin <i>(Förderkennzeichen: 16DKZ2034F)</i></li>\n <li>Gesellschaft für Informatik <i>(Förderkennzeichen: 16DKZ2034G)</i></li>\n <li>Humboldt-Universität zu Berlin <i>(Förderkennzeichen: 16DKZ2034H)</i></li>\n </ul>\n</p>\n\n<p>Mehr zum Aufbau und zur Umsetzung des Projekts können Sie im <a href=\"https://doi.org/10.5281/zenodo.10805015\" target=\"_blank\">Umsetzungskonzept</a> erfahren.</p>\n\n<p>Weitere Informationen sowie Publikationen finden Sie auf der <a href=\"https://www.quadriga-dk.de\" target=\"_blank\">Webseite</a>, in der <a href=\"https://zenodo.org/communities/quadriga\" target=\"_blank\">Zenodo-Community</a> und der <a href=\"https://github.com/quadriga-dk\" target=\"_blank\">GitHub-Organisation</a> des Projekts.</p>\n",
"publication_date": "2026-03-11",
"keywords": [
"Open Educational Resources",
Expand Down
2 changes: 2 additions & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pyyaml
mypy
ruff
pre-commit
types-PyYAML
types-jsonschema
rdflib
jupytext
jsonschema
88 changes: 56 additions & 32 deletions quadriga/assessment.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from IPython.display import HTML
import json
import uuid

from IPython.display import HTML


def create_answer_box(question_id, rows=4):
def create_answer_box(question_id: str, rows: int = 4) -> HTML:
"""Create an answer box with a submit button."""
return HTML(f"""
<div style="padding: 5px; border-radius: 5px; margin: 10px 0;">
Expand All @@ -16,7 +17,7 @@ def create_answer_box(question_id, rows=4):
class DragDropQuiz:
"""
A simple drag-and-drop quiz generator for Jupyter Books.

Usage:
quiz = DragDropQuiz()
quiz.create_matching_quiz(
Expand All @@ -26,14 +27,23 @@ class DragDropQuiz:
correct_mapping={"Description 1": "Option A", "Description 2": "Option B", "Description 3": "Option C"}
)
"""
def __init__(self):

def __init__(self) -> None:
self.quiz_counter = 0

def create_matching_quiz(self, title, descriptions, options, correct_mapping, show_feedback=True, feedback_messages=None):

def create_matching_quiz(
self,
title: str,
descriptions: list[str],
options: list[str],
correct_mapping: dict,
*,
show_feedback: bool = True,
feedback_messages: dict | None = None,
) -> HTML:
"""
Create a drag-and-drop matching quiz.

Parameters:
- title (str): The quiz title/question
- descriptions (list): List of items to be matched (static labels)
Expand All @@ -44,76 +54,92 @@ def create_matching_quiz(self, title, descriptions, options, correct_mapping, sh
"""
self.quiz_counter += 1
quiz_id = f"drag_drop_quiz_{self.quiz_counter}_{uuid.uuid4().hex[:8]}"

# Set default feedback messages if none provided
if feedback_messages is None:
feedback_messages = {
"correct": "Perfekt! Alle {total} Zuordnungen sind korrekt!",
"incorrect": "Leider sind keine Zuordnungen korrekt. Versuchen Sie es noch einmal!",
"partial": "Teilweise richtig: {correct} von {total} Zuordnungen sind korrekt."
"partial": "Teilweise richtig: {correct} von {total} Zuordnungen sind korrekt.",
}

# Convert correct mapping to use indices for easier JavaScript handling
desc_to_idx = {desc: i for i, desc in enumerate(descriptions)}
opt_to_idx = {opt: i for i, opt in enumerate(options)}

correct_pairs = []
for desc, opt in correct_mapping.items():
if desc in desc_to_idx and opt in opt_to_idx:
correct_pairs.append([desc_to_idx[desc], opt_to_idx[opt]])

html_content = self._generate_html(
quiz_id, title, descriptions, options, correct_pairs, show_feedback, feedback_messages
quiz_id,
title,
descriptions,
options,
correct_pairs,
show_feedback=show_feedback,
feedback_messages=feedback_messages,
)

return HTML(html_content)

def _generate_html(self, quiz_id, title, descriptions, options, correct_pairs, show_feedback, feedback_messages):

def _generate_html(
self,
quiz_id: str,
title: str,
descriptions: list[str],
options: list[str],
correct_pairs: list[list[int]],
*,
show_feedback: bool,
feedback_messages: dict,
) -> str:
"""Generate the complete HTML for the drag-and-drop quiz."""

# Generate static description labels with drop zones
description_zones = ""
for i, desc in enumerate(descriptions):
description_zones += f'''
description_zones += f"""
<div class="description-zone">
<div class="description-label">{desc}</div>
<div class="drop-area" data-zone-id="{i}" id="{quiz_id}_drop_{i}">
<span class="drop-placeholder">Hier ablegen</span>
</div>
</div>
'''
"""

# Generate draggable options
draggable_options = ""
for i, option in enumerate(options):
draggable_options += f'''
draggable_options += f"""
<div class="draggable-option" draggable="true" data-item-id="{i}" id="{quiz_id}_option_{i}">
{option}
</div>
'''
return f'''
"""

return f"""
<div class="drag-drop-quiz" id="{quiz_id}">
<div class="quiz-title">{title}</div>

<div class="quiz-main">
<div class="descriptions-container">
{description_zones}
</div>
</div>

<div class="options-container">
<div class="options-title">Ziehen Sie diese zu den passenden Beschreibungen</div>
<div class="options-list" id="{quiz_id}_options">
{draggable_options}
</div>
</div>

<div class="quiz-controls">
<button class="quiz-button" onclick="checkAnswer_{quiz_id}()">Antwort prüfen</button>
<button class="quiz-button reset-button" onclick="resetQuiz_{quiz_id}()">Zurücksetzen</button>
</div>

<div id="{quiz_id}_feedback"></div>
</div>
<script>
Expand All @@ -130,6 +156,4 @@ def _generate_html(self, quiz_id, title, descriptions, options, correct_pairs, s
}}
}});
</script>
'''


"""
12 changes: 4 additions & 8 deletions quadriga/metadata/create_rdfxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from pathlib import Path
from typing import Any

from rdflib import RDF, Graph, Literal, Namespace, URIRef # type: ignore[import-not-found]
from rdflib.namespace import DCTERMS, SKOS, XSD # type: ignore[import-not-found]
from rdflib import RDF, Graph, Literal, Namespace, URIRef
from rdflib.namespace import DCTERMS, SKOS, XSD

from .utils import extract_keywords, get_file_path, get_repo_root, load_yaml_file

Expand Down Expand Up @@ -153,9 +153,7 @@ def add_person(
graph.add((orcid_node, RDF.type, SCHEMA.PropertyValue))
graph.add((orcid_node, SCHEMA.propertyID, Literal("ORCID")))
graph.add((orcid_node, SCHEMA.value, Literal(clean_orcid_id)))
graph.add(
(orcid_node, SCHEMA.url, URIRef(f"https://orcid.org/{clean_orcid_id}"))
)
graph.add((orcid_node, SCHEMA.url, URIRef(f"https://orcid.org/{clean_orcid_id}")))
graph.add((person_uri, SCHEMA.identifier, orcid_node))

# affiliation -> schema:affiliation (mapped in both author and contributor)
Expand Down Expand Up @@ -290,9 +288,7 @@ def add_chapter(
# learning-objectives -> educationalAlignment with AlignmentObject
if chapter_data.get("learning-objectives"):
for obj_index, obj_data in enumerate(chapter_data["learning-objectives"]):
obj_uri = add_learning_objective(
graph, obj_data, base_uri, chapter_index, obj_index
)
obj_uri = add_learning_objective(graph, obj_data, base_uri, chapter_index, obj_index)
if obj_uri:
graph.add((chapter_uri, SCHEMA.educationalAlignment, obj_uri))

Expand Down
Loading
Loading