diff --git a/apis_core/urls.py b/apis_core/urls.py index 67b4fe1d8..914caa1b5 100644 --- a/apis_core/urls.py +++ b/apis_core/urls.py @@ -88,4 +88,5 @@ ), path("api/dumpdata", Dumpdata.as_view()), path("", include("apis_core.generic.urls", namespace="generic")), + path("vocabs/", include("apis_core.vocabs.urls")), ] diff --git a/apis_core/vocabs/apps.py b/apis_core/vocabs/apps.py new file mode 100644 index 000000000..9e13c3424 --- /dev/null +++ b/apis_core/vocabs/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class VocabsConfig(AppConfig): + default_auto_field = "django.db.models.AutoField" + name = "apis_core.vocabs" diff --git a/apis_core/vocabs/migrations/0001_initial.py b/apis_core/vocabs/migrations/0001_initial.py new file mode 100644 index 000000000..8cbfef7a6 --- /dev/null +++ b/apis_core/vocabs/migrations/0001_initial.py @@ -0,0 +1,101 @@ +# Generated by Django 4.2.8 on 2023-12-21 11:54 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("contenttypes", "0002_remove_content_type_name"), + ] + + operations = [ + migrations.CreateModel( + name="SkosCollection", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField( + help_text="Collection label or name", + max_length=300, + verbose_name="skos:prefLabel", + ), + ), + ( + "label_lang", + models.CharField( + blank=True, + default="en", + help_text="Language of preferred label given above", + max_length=3, + verbose_name="skos:prefLabel language", + ), + ), + ( + "creator", + models.TextField( + blank=True, + help_text="Person or organisation that created this collectionIf more than one list all using a semicolon ;", + verbose_name="dc:creator", + ), + ), + ( + "contributor", + models.TextField( + blank=True, + help_text="Person or organisation that made contributions to the collectionIf more than one list all using a semicolon ;", + verbose_name="dc:contributor", + ), + ), + ( + "parent", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="vocabs.skoscollection", + ), + ), + ], + ), + migrations.CreateModel( + name="SkosCollectionContentObject", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.PositiveIntegerField()), + ( + "collection", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="vocabs.skoscollection", + ), + ), + ( + "content_type", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="contenttypes.contenttype", + ), + ), + ], + ), + ] diff --git a/apis_core/vocabs/migrations/0002_alter_skoscollection_parent.py b/apis_core/vocabs/migrations/0002_alter_skoscollection_parent.py new file mode 100644 index 000000000..fbecc1204 --- /dev/null +++ b/apis_core/vocabs/migrations/0002_alter_skoscollection_parent.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.8 on 2024-01-15 17:20 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('vocabs', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='skoscollection', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='vocabs.skoscollection'), + ), + ] diff --git a/apis_core/vocabs/migrations/__init__.py b/apis_core/vocabs/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apis_core/vocabs/models.py b/apis_core/vocabs/models.py new file mode 100644 index 000000000..9efb82ab2 --- /dev/null +++ b/apis_core/vocabs/models.py @@ -0,0 +1,56 @@ +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType +from django.db import models + + +class SkosCollection(models.Model): + """ + SKOS collections are labeled and/or ordered groups of SKOS concepts. + Collections are useful where a group of concepts shares something in common, + and it is convenient to group them under a common label, or + where some concepts can be placed in a meaningful order. + + Miles, Alistair, and Sean Bechhofer. "SKOS simple knowledge + organization system reference. W3C recommendation (2009)." + + """ + + parent = models.ForeignKey("self", null=True, on_delete=models.CASCADE, blank=True) + name = models.CharField( + max_length=300, + verbose_name="skos:prefLabel", + help_text="Collection label or name", + ) + label_lang = models.CharField( + max_length=3, + blank=True, + default="en", + verbose_name="skos:prefLabel language", + help_text="Language of preferred label given above", + ) + creator = models.TextField( + blank=True, + verbose_name="dc:creator", + help_text="Person or organisation that created this collection" + "If more than one list all using a semicolon ;", + ) + contributor = models.TextField( + blank=True, + verbose_name="dc:contributor", + help_text="Person or organisation that made contributions to the collection" + "If more than one list all using a semicolon ;", + ) + + def __str__(self): + return self.name + + +class SkosCollectionContentObject(models.Model): + collection = models.ForeignKey(SkosCollection, on_delete=models.CASCADE) + + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey("content_type", "object_id") + + def __str__(self): + return f"{self.content_object} -> {self.collection}" diff --git a/apis_core/vocabs/templates/vocabs/toggle_skoscollection.html b/apis_core/vocabs/templates/vocabs/toggle_skoscollection.html new file mode 100644 index 000000000..3940eb00b --- /dev/null +++ b/apis_core/vocabs/templates/vocabs/toggle_skoscollection.html @@ -0,0 +1,6 @@ + + {{ collection.name }} + diff --git a/apis_core/vocabs/templates/vocabs/toggle_skoscollection_children.html b/apis_core/vocabs/templates/vocabs/toggle_skoscollection_children.html new file mode 100644 index 000000000..6b67285fe --- /dev/null +++ b/apis_core/vocabs/templates/vocabs/toggle_skoscollection_children.html @@ -0,0 +1,4 @@ +{% load vocabs %} +{% for child in children %} +{% toggle_skoscollection model child %} +{% endfor %} diff --git a/apis_core/vocabs/templatetags/vocabs.py b/apis_core/vocabs/templatetags/vocabs.py new file mode 100644 index 000000000..e1d4c8249 --- /dev/null +++ b/apis_core/vocabs/templatetags/vocabs.py @@ -0,0 +1,39 @@ +from django import template +from django.contrib.contenttypes.models import ContentType + +from apis_core.vocabs.models import SkosCollectionContentObject, SkosCollection + + +register = template.Library() + + +@register.inclusion_tag("vocabs/toggle_skoscollection.html", takes_context=True) +def toggle_skoscollection(context, model, collection): + content_type = ContentType.objects.get_for_model(model) + context["content_type_id"] = content_type.id + context["object_id"] = model.id + context["collection"] = collection + context["exists"] = SkosCollectionContentObject.objects.filter( + object_id=model.id, content_type=content_type, collection=collection + ).exists() + return context + + +@register.inclusion_tag("vocabs/toggle_skoscollection.html", takes_context=True) +def toggle_skoscollection_by_name(context, model, collectionname): + collection = SkosCollection.objects.get(name=collectionname) + return toggle_skoscollection(context, model, collection) + + +@register.inclusion_tag("vocabs/toggle_skoscollection_children.html", takes_context=True) +def toggle_skoscollection_children(context, model, collection): + context["children"] = SkosCollection.objects.filter(parent=collection) + context["model"] = model + context["collection"] = collection + return context + + +@register.inclusion_tag("vocabs/toggle_skoscollection_children.html", takes_context=True) +def toggle_skoscollection_children_by_name(context, model, collectionname): + collection = SkosCollection.objects.get(name=collectionname) + return toggle_skoscollection_children(context, model, collection) diff --git a/apis_core/vocabs/urls.py b/apis_core/vocabs/urls.py new file mode 100644 index 000000000..264944089 --- /dev/null +++ b/apis_core/vocabs/urls.py @@ -0,0 +1,11 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path( + "togglecollectionobject///", + views.ToggleCollectionObject.as_view(), + name="togglecollectionobject", + ), +] diff --git a/apis_core/vocabs/utils.py b/apis_core/vocabs/utils.py new file mode 100644 index 000000000..e69de29bb diff --git a/apis_core/vocabs/views.py b/apis_core/vocabs/views.py new file mode 100644 index 000000000..fd62b415b --- /dev/null +++ b/apis_core/vocabs/views.py @@ -0,0 +1,32 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.contenttypes.models import ContentType +from django.views.generic.base import TemplateView +from django.shortcuts import redirect + +from .models import SkosCollection, SkosCollectionContentObject + + +class ToggleCollectionObject(LoginRequiredMixin, TemplateView): + template_name = "vocabs/toggle_skoscollection.html" + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + context["exists"] = self.created + context["collection"] = SkosCollection.objects.get(pk=kwargs["collection"]) + context["content_type_id"] = kwargs["content_type_id"] + context["object_id"] = kwargs["object_id"] + return context + + def get(self, *args, **kwargs): + collection = SkosCollection.objects.get(pk=kwargs["collection"]) + content_type = ContentType.objects.get(pk=kwargs["content_type_id"]) + scco, self.created = SkosCollectionContentObject.objects.get_or_create( + collection=collection, + content_type=content_type, + object_id=kwargs["object_id"], + ) + if not self.created: + scco.delete() + if redirect_to := self.request.GET.get("to", False): + return redirect(redirect_to) + return super().get(*args, **kwargs)