Coverage for apis_core/generic/serializers.py: 41%
101 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-06-25 10:00 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-06-25 10:00 +0000
1import re
3from AcdhArcheAssets.uri_norm_rules import get_rules
4from django.contrib.contenttypes.models import ContentType
5from django.shortcuts import get_object_or_404
6from rdflib import Graph, Literal, Namespace, URIRef
7from rdflib.namespace import OWL, RDF, RDFS
8from rest_framework.reverse import reverse
9from rest_framework.serializers import (
10 BaseSerializer,
11 CharField,
12 HyperlinkedModelSerializer,
13 HyperlinkedRelatedField,
14 IntegerField,
15 Serializer,
16 SerializerMethodField,
17)
19from apis_core.generic.utils.rdf_namespace import APPELLATION, ATTRIBUTES, CRM
20from apis_core.utils.settings import rdf_namespace_prefix
23class GenericHyperlinkedRelatedField(HyperlinkedRelatedField):
24 def get_url(self, obj, view_name, request, format):
25 contenttype = ContentType.objects.get_for_model(obj, for_concrete_model=True)
26 url_kwargs = {"contenttype": contenttype, "pk": obj.pk}
27 return reverse(
28 "apis_core:generic:genericmodelapi-detail",
29 kwargs=url_kwargs,
30 request=request,
31 format=format,
32 )
34 def use_pk_only_optimization(self):
35 # We have the complete object instance already. We don't need
36 # to run the 'only get the pk for this relationship' code.
37 return False
40class GenericHyperlinkedIdentityField(GenericHyperlinkedRelatedField):
41 def __init__(self, view_name=None, **kwargs):
42 assert view_name is not None, "The `view_name` argument is required."
43 kwargs["read_only"] = True
44 kwargs["source"] = "*"
45 super().__init__(view_name, **kwargs)
48class GenericHyperlinkedModelSerializer(HyperlinkedModelSerializer):
49 serializer_related_field = GenericHyperlinkedRelatedField
50 serializer_url_field = GenericHyperlinkedIdentityField
53def serializer_factory(
54 model,
55 serializer=GenericHyperlinkedModelSerializer,
56 action="detail",
57 fields="__all__",
58 **kwargs,
59):
60 defaultmeta = type(str("Meta"), (object,), {"fields": fields})
61 meta = getattr(serializer, "Meta", defaultmeta)
62 meta.model = model
63 serializer = type(
64 str("%s%sModelSerializer" % (model._meta.object_name, action)),
65 (serializer,),
66 {"Meta": meta},
67 )
68 return serializer
71class ContentTypeInstanceSerializer(Serializer):
72 id = IntegerField(required=True)
73 content_type = CharField(required=True)
75 def to_internal_value(self, data):
76 data = super().to_internal_value(data)
77 app_label, model = data.get("content_type").split(".")
78 content_type = get_object_or_404(ContentType, app_label=app_label, model=model)
79 return get_object_or_404(content_type.model_class(), pk=data.get("id"))
82class SimpleObjectSerializer(Serializer):
83 url = GenericHyperlinkedIdentityField(
84 view_name="apis_core:generic:genericmodelapi-detail"
85 )
86 label = SerializerMethodField()
87 content_type_key = SerializerMethodField()
88 content_type_name = SerializerMethodField()
90 class Meta:
91 fields = ["url", "label", "content_type_key", "content_type_name"]
93 def get_label(self, obj) -> str:
94 return str(obj)
96 def get_content_type_key(self, obj) -> str:
97 content_type = ContentType.objects.get_for_model(obj)
98 return f"{content_type.app_label}.{content_type.model}"
100 def get_content_type_name(self, obj) -> str:
101 content_type = ContentType.objects.get_for_model(obj)
102 return content_type.name
105class GenericModelCidocSerializer(BaseSerializer):
106 def __init__(self, *args, **kwargs):
107 self.rdf_nsp_base = rdf_namespace_prefix()
108 self.appellation_nsp_prefix = f"{self.rdf_nsp_base}-appellation"
109 self.attr_nsp_prefix = f"{self.rdf_nsp_base}-attr"
110 super().__init__(*args, **kwargs)
112 def create_sameas(self, g, instance):
113 # add the ID as APIS Identifier
114 apis_id = URIRef(ATTRIBUTES[f"apis-identifier_{instance.pk}"])
115 g.add((apis_id, RDF.type, CRM.E42_Identifier))
116 g.add((apis_id, RDFS.label, Literal(instance.pk)))
118 # APIS internal identifier type
119 apis_id_type = URIRef(ATTRIBUTES["apis-identifier_type"])
120 g.add((apis_id, CRM.P2_has_type, apis_id_type))
121 g.add((apis_id_type, RDF.type, CRM.E55_Type))
122 g.add((apis_id_type, RDFS.label, Literal("APIS internal identifier")))
123 g.add((self.instance_uri, CRM.P1_is_identified_by, apis_id))
125 for uri in instance.uri_set():
126 g.add((self.instance_uri, OWL.sameAs, URIRef(uri.uri)))
128 for x in get_rules():
129 if m := re.match(x["match"], uri.uri):
130 id_type = URIRef(ATTRIBUTES[x["name"] + "-identifier_type"])
131 g.add((id_type, RDF.type, CRM.E55_Type))
132 g.add((id_type, RDFS.label, Literal(x["name"] + " ID")))
133 id_uri = URIRef(
134 ATTRIBUTES[x["name"] + f"-identifier_{instance.pk}"]
135 )
136 g.add((id_uri, RDF.type, CRM.E42_Identifier))
137 g.add((id_uri, CRM.P2_has_type, id_type))
138 g.add(
139 (
140 self.instance_uri,
141 CRM.P1_is_identified_by,
142 id_uri,
143 )
144 )
145 g.add((id_uri, RDFS.label, Literal(m[1])))
147 return g
149 def to_representation(self, instance):
150 g = Graph()
152 g.namespace_manager.bind("crm", CRM, replace=True)
153 g.namespace_manager.bind("owl", OWL, replace=True)
155 g.namespace_manager.bind(self.appellation_nsp_prefix, APPELLATION, replace=True)
156 g.namespace_manager.bind(self.attr_nsp_prefix, ATTRIBUTES, replace=True)
158 self.instance_namespace = Namespace(instance.get_namespace_uri())
159 g.namespace_manager.bind(
160 instance.get_namespace_prefix(), self.instance_namespace
161 )
163 self.instance_uri = URIRef(self.instance_namespace[str(instance.id)])
165 # Add properties
166 self.appellation_uri = URIRef(APPELLATION[str(instance.id)])
167 g.add(
168 (
169 self.appellation_uri,
170 RDF.type,
171 CRM.E33_E41_Linguistic_Appellation,
172 )
173 )
174 g.add(
175 (
176 self.instance_uri,
177 CRM.P1_is_identified_by,
178 self.appellation_uri,
179 )
180 )
181 g.add((self.appellation_uri, RDFS.label, Literal(str(instance))))
182 g.add((self.instance_uri, RDFS.label, Literal(str(instance))))
183 # Add RDF types
184 for rdf_type in instance.get_rdf_types():
185 g.add((self.instance_uri, RDF.type, rdf_type))
186 # Add sameAs links
187 g = self.create_sameas(g, instance)
189 return g