Skip to content

Commit

Permalink
fix: make learningresourcetype consistently a list
Browse files Browse the repository at this point in the history
`LOMMetadata` already builds it as a list, which is behavior as per spec
other places erroneously build it as dict instead of list of dicts
  • Loading branch information
martinobersteiner committed Nov 19, 2024
1 parent 5365e50 commit 44a7ba7
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 9 deletions.
2 changes: 1 addition & 1 deletion invenio_records_lom/fixtures/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def create_fake_educational(fake: Faker) -> dict:

return {
"interactivitytype": vocabularify(fake, interactivity_types),
"learningresourcetype": create_fake_learningresourcetype(fake),
"learningresourcetype": [create_fake_learningresourcetype(fake)],
"interactivitylevel": vocabularify(fake, levels),
"semanticdensity": vocabularify(fake, levels),
"intendedenduserrole": vocabularify(fake, end_user_roles),
Expand Down
10 changes: 6 additions & 4 deletions invenio_records_lom/resources/serializers/oai/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,12 @@ def dump_default(cls) -> dict:
class EducationalSchema(ExcludeUnknownOrderedSchema):
"""Schema for LOM-UIBK's `educational` category."""

learningresourcetype = fields.Nested(
LearningResourceTypeSchema(),
required=True,
dump_default=LearningResourceTypeSchema.dump_default,
learningresourcetype = fields.List(
fields.Nested(
LearningResourceTypeSchema(),
required=True,
dump_default=LearningResourceTypeSchema.dump_default,
),
)


Expand Down
6 changes: 5 additions & 1 deletion invenio_records_lom/services/schemas/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,11 @@ class LearningResourceTypeSchema(Schema):
class EducationalSchema(Schema):
"""Schema for LOM's `educational` category."""

learningresourcetype = fields.Nested(LearningResourceTypeSchema, required=True)
learningresourcetype = fields.List(
fields.Nested(LearningResourceTypeSchema, required=True),
required=True,
validate=validate.Length(min=1),
)


class CopyrightAndOtherSchema(Schema):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export class LOMDepositRecordSerializer extends DepositRecordSerializer {

// deserialize resource-type
form.resourcetype = {
value: _get(metadata, "educational.learningresourcetype.id", ""),
value: _get(metadata, "educational.learningresourcetype.0.id", ""),
};

// deserialize contributors
Expand Down Expand Up @@ -289,7 +289,7 @@ export class LOMDepositRecordSerializer extends DepositRecordSerializer {

// serialize resource-type
const resourcetypeUrl = _get(metadata, "form.resourcetype.value", "");
_set(metadata, "educational.learningresourcetype", {
_set(metadata, "educational.learningresourcetype.0", {
source: {
langstring: {
"#text": "https://w3id.org/kim/hcrt/scheme",
Expand Down Expand Up @@ -364,7 +364,7 @@ export class LOMDepositRecordSerializer extends DepositRecordSerializer {
setErrors(/^metadata\.rights\.url/, "license.value");
setErrors(/^metadata\.technical\.format/, "format.value");
setErrors(
/^metadata\.educational\.learningresourcetype\.id$/,
/^metadata\.educational\.learningresourcetype\.\d+\.id$/,
"resourcetype.value"
);

Expand Down
100 changes: 100 additions & 0 deletions invenio_records_lom/upgrade_scripts/migrate_0_17_to_0_18.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 Graz University of Technology.
#
# invenio-records-lom is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Record migration script from invenio-records-lom v0.17 to v0.18.
To upgrade, call the whole file from within app-context.
(e.g. via `pipenv run invenio shell \
/path/to/invenio_records_lom/upgrade_scripts/migrate_0_17_to_0_18.py`)
(e.g. via `pipenv run invenio shell \
$(find $(pipenv --venv)/lib/*/site-packages/invenio_records_lom \
-name migrate_0_17_to_0_18.py))`)
"""

from copy import deepcopy

from click import secho
from invenio_db import db

from invenio_records_lom.records.api import LOMDraft, LOMRecord


def execute_upgrade() -> None:
"""Exceute upgrade from `invenio-records-lom` `v0.17` to `v0.18`."""
secho(
"LOM upgrade: ensuring `metadata.educational.learningresourcetype` is a list",
fg="green",
)

def get_new_json(old_json: dict) -> dict | None:
"""Get updated json from current json.
if update is necessary, return updated JSON
otherwise, return `None`
"""
current_learningresourcetype = (
old_json.get("metadata", {})
.get("educational", {})
.get("learningresourcetype")
)
if current_learningresourcetype is None:
# no learningresourcetype (which is allowed) - nothing to update
return None
if isinstance(current_learningresourcetype, list):
# already a list (which is correct) - no need to update
return None

# not a list-of-things but rather a thing itself - pack into list
new_json = deepcopy(old_json)
new_json["metadata"]["educational"]["learningresourcetype"] = [
current_learningresourcetype,
]

return new_json

# update drafts
for draft in LOMDraft.model_cls.query.all():
old_json = draft.json
if not old_json:
continue # for published/deleted drafts, json is None/empty

new_json = get_new_json(old_json)
if new_json is None:
# `None` means "no update necessary"
continue

draft.json = new_json
db.session.merge(draft)

# update records
for record in LOMRecord.model_cls.query.all():
old_json = record.json
if not old_json:
continue # for records with new-version/embargo, json is None/empty

new_json = get_new_json(old_json)
if new_json is None:
# `None` means "no update necessary"
continue

record.json = new_json
db.session.merge(record)

db.session.commit()
secho(
"Successfully turned `metadata.educational.learningresourcetype` into a list where necessary!",
fg="green",
)
secho(
"NOTE: this only updated the SQL-database, you'll have to reindex records with opensearch",
fg="red",
)


if __name__ == "__main__":
# gets executed when file is called directly, but not when imported
execute_upgrade()

0 comments on commit 44a7ba7

Please sign in to comment.