Coverage for oc_ocdm / metadata / entities / dataset.py: 93%
137 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-28 18:52 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-28 18:52 +0000
1#!/usr/bin/python
3# SPDX-FileCopyrightText: 2020-2022 Simone Persiani <iosonopersia@gmail.com>
4#
5# SPDX-License-Identifier: ISC
7# -*- coding: utf-8 -*-
8from __future__ import annotations
10from typing import TYPE_CHECKING
12from oc_ocdm.decorators import accepts_only
13from oc_ocdm.metadata.metadata_entity import MetadataEntity
14from rdflib import Literal, XSD
16if TYPE_CHECKING:
17 from typing import List
18 from rdflib import URIRef
19 from oc_ocdm.metadata.entities.distribution import Distribution
22class Dataset(MetadataEntity):
23 """Dataset (short: not applicable and strictly dependent on the implementation of the
24 dataset infrastructure): a set of collected information about something."""
26 def _merge_properties(self, other: MetadataEntity) -> None:
27 """
28 The merge operation allows combining two ``Dataset`` entities into a single one,
29 by marking the second entity as to be deleted while also copying its data into the current
30 ``Dataset``. Moreover, every triple from the containing ``MetadataSet`` referring to the second
31 entity gets "redirected" to the current entity: **every other reference contained inside a
32 different source (e.g. a triplestore) must be manually handled by the user!**
34 In case of functional properties, values from the current entity get overwritten
35 by those coming from the second entity while, in all other cases, values from the
36 second entity are simply appended to those of the current entity. In this context,
37 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not.
39 :param other: The entity which will be marked as to be deleted and whose properties will
40 be merged into the current entity.
41 :type other: Dataset
42 :raises TypeError: if the parameter is of the wrong type
43 :return: None
44 """
45 super()._merge_properties(other)
46 assert isinstance(other, Dataset)
48 title: str | None = other.get_title()
49 if title is not None:
50 self.has_title(title)
52 description: str | None = other.get_description()
53 if description is not None:
54 self.has_description(description)
56 pub_date: str | None = other.get_publication_date()
57 if pub_date is not None:
58 self.has_publication_date(pub_date)
60 mod_date: str | None = other.get_modification_date()
61 if mod_date is not None:
62 self.has_modification_date(mod_date)
64 keywords_list: List[str] = other.get_keywords()
65 for cur_keyword in keywords_list:
66 self.has_keyword(cur_keyword)
68 subjects_list: List[URIRef] = other.get_subjects()
69 for cur_subject in subjects_list:
70 self.has_subject(cur_subject)
72 landing_page: URIRef | None = other.get_landing_page()
73 if landing_page is not None:
74 self.has_landing_page(landing_page)
76 sub_datasets_list: List[Dataset] = other.get_sub_datasets()
77 for cur_sub_dataset in sub_datasets_list:
78 self.has_sub_dataset(cur_sub_dataset)
80 sparql_endpoint: URIRef | None = other.get_sparql_endpoint()
81 if sparql_endpoint is not None:
82 self.has_sparql_endpoint(sparql_endpoint)
84 distributions_list: List[Distribution] = other.get_distributions()
85 for cur_distribution in distributions_list:
86 self.has_distribution(cur_distribution)
88 # HAS TITLE
89 def get_title(self) -> str | None:
90 """
91 Getter method corresponding to the ``dcterms:title`` RDF predicate.
93 :return: The requested value if found, None otherwise
94 """
95 return self._get_literal(MetadataEntity.iri_title)
97 @accepts_only('literal')
98 def has_title(self, string: str) -> None:
99 """
100 Setter method corresponding to the ``dcterms:title`` RDF predicate.
102 **WARNING: this is a functional property, hence any existing value will be overwritten!**
104 `The title of the dataset.`
106 :param string: The value that will be set as the object of the property related to this method
107 :type string: str
108 :raises TypeError: if the parameter is of the wrong type
109 :return: None
110 """
111 self.remove_title()
112 self._create_literal(MetadataEntity.iri_title, string)
114 def remove_title(self) -> None:
115 """
116 Remover method corresponding to the ``dcterms:title`` RDF predicate.
118 :return: None
119 """
120 self.g.remove((self.res, MetadataEntity.iri_title, None))
122 # HAS DESCRIPTION
123 def get_description(self) -> str | None:
124 """
125 Getter method corresponding to the ``dcterms:description`` RDF predicate.
127 :return: The requested value if found, None otherwise
128 """
129 return self._get_literal(MetadataEntity.iri_description)
131 @accepts_only('literal')
132 def has_description(self, string: str) -> None:
133 """
134 Setter method corresponding to the ``dcterms:description`` RDF predicate.
136 **WARNING: this is a functional property, hence any existing value will be overwritten!**
138 `A short textual description of the content of the dataset.`
140 :param string: The value that will be set as the object of the property related to this method
141 :type string: str
142 :raises TypeError: if the parameter is of the wrong type
143 :return: None
144 """
145 self.remove_description()
146 self._create_literal(MetadataEntity.iri_description, string)
148 def remove_description(self) -> None:
149 """
150 Remover method corresponding to the ``dcterms:description`` RDF predicate.
152 :return: None
153 """
154 self.g.remove((self.res, MetadataEntity.iri_description, None))
156 # HAS PUBLICATION DATE
157 def get_publication_date(self) -> str | None:
158 """
159 Getter method corresponding to the ``dcterms:issued`` RDF predicate.
161 :return: The requested value if found, None otherwise
162 """
163 return self._get_literal(MetadataEntity.iri_issued)
165 @accepts_only('literal')
166 def has_publication_date(self, string: str) -> None:
167 """
168 Setter method corresponding to the ``dcterms:issued`` RDF predicate.
170 **WARNING: this is a functional property, hence any existing value will be overwritten!**
172 `The date of first publication of the dataset.`
174 :param string: The value that will be set as the object of the property related to this method. **It must
175 be a string compliant with the** ``xsd:dateTime`` **datatype.**
176 :type string: str
177 :raises TypeError: if the parameter is of the wrong type
178 :return: None
179 """
180 self.remove_publication_date()
181 self._create_literal(MetadataEntity.iri_issued, string, XSD.dateTime, False)
183 def remove_publication_date(self) -> None:
184 """
185 Remover method corresponding to the ``dcterms:issued`` RDF predicate.
187 :return: None
188 """
189 self.g.remove((self.res, MetadataEntity.iri_issued, None))
191 # HAS MODIFICATION DATE
192 def get_modification_date(self) -> str | None:
193 """
194 Getter method corresponding to the ``dcterms:modified`` RDF predicate.
196 :return: The requested value if found, None otherwise
197 """
198 return self._get_literal(MetadataEntity.iri_modified)
200 @accepts_only('literal')
201 def has_modification_date(self, string: str) -> None:
202 """
203 Setter method corresponding to the ``dcterms:modified`` RDF predicate.
205 **WARNING: this is a functional property, hence any existing value will be overwritten!**
207 `The date on which the dataset has been modified.`
209 :param string: The value that will be set as the object of the property related to this method. **It must
210 be a string compliant with the** ``xsd:dateTime`` **datatype.**
211 :type string: str
212 :raises TypeError: if the parameter is of the wrong type
213 :return: None
214 """
215 self.remove_modification_date()
216 self._create_literal(MetadataEntity.iri_modified, string, XSD.dateTime, False)
218 def remove_modification_date(self) -> None:
219 """
220 Remover method corresponding to the ``dcterms:modified`` RDF predicate.
222 :return: None
223 """
224 self.g.remove((self.res, MetadataEntity.iri_modified, None))
226 # HAS KEYWORD
227 def get_keywords(self) -> List[str]:
228 """
229 Getter method corresponding to the ``dcat:keyword`` RDF predicate.
231 :return: A list containing the requested values if found, None otherwise
232 """
233 return self._get_multiple_literals(MetadataEntity.iri_keyword)
235 @accepts_only('literal')
236 def has_keyword(self, string: str) -> None:
237 """
238 Setter method corresponding to the ``dcat:keyword`` RDF predicate.
240 `A keyword or phrase describing the content of the dataset.`
242 :param string: The value that will be set as the object of the property related to this method
243 :type string: str
244 :raises TypeError: if the parameter is of the wrong type
245 :return: None
246 """
247 self._create_literal(MetadataEntity.iri_keyword, string)
249 @accepts_only('literal')
250 def remove_keyword(self, string: str | None = None) -> None:
251 """
252 Remover method corresponding to the ``dcat:keyword`` RDF predicate.
254 **WARNING: this is a non-functional property, hence, if the parameter
255 is None, any existing value will be removed!**
257 :param string: If not None, the specific object value that will be removed from the property
258 related to this method (defaults to None)
259 :type string: str
260 :raises TypeError: if the parameter is of the wrong type
261 :return: None
262 """
263 if string is not None:
264 self.g.remove((self.res, MetadataEntity.iri_keyword, Literal(string)))
265 else:
266 self.g.remove((self.res, MetadataEntity.iri_keyword, None))
268 # HAS SUBJECT
269 def get_subjects(self) -> List[URIRef]:
270 """
271 Getter method corresponding to the ``dcat:theme`` RDF predicate.
273 :return: A list containing the requested values if found, None otherwise
274 """
275 uri_list: List[URIRef] = self._get_multiple_uri_references(MetadataEntity.iri_subject)
276 return uri_list
278 @accepts_only('thing')
279 def has_subject(self, thing_res: URIRef) -> None:
280 """
281 Setter method corresponding to the ``dcat:theme`` RDF predicate.
283 `A concept describing the primary subject of the dataset.`
285 :param thing_res: The value that will be set as the object of the property related to this method
286 :type thing_res: URIRef
287 :raises TypeError: if the parameter is of the wrong type
288 :return: None
289 """
290 self.g.add((self.res, MetadataEntity.iri_subject, thing_res))
292 @accepts_only('thing')
293 def remove_subject(self, thing_res: URIRef | None = None) -> None:
294 """
295 Remover method corresponding to the ``dcat:theme`` RDF predicate.
297 **WARNING: this is a non-functional property, hence, if the parameter
298 is None, any existing value will be removed!**
300 :param thing_res: If not None, the specific object value that will be removed from the property
301 related to this method (defaults to None)
302 :type thing_res: URIRef
303 :raises TypeError: if the parameter is of the wrong type
304 :return: None
305 """
306 if thing_res is not None:
307 self.g.remove((self.res, MetadataEntity.iri_subject, thing_res))
308 else:
309 self.g.remove((self.res, MetadataEntity.iri_subject, None))
311 # HAS LANDING PAGE
312 def get_landing_page(self) -> URIRef | None:
313 """
314 Getter method corresponding to the ``dcat:landingPage`` RDF predicate.
316 :return: The requested value if found, None otherwise
317 """
318 return self._get_uri_reference(MetadataEntity.iri_landing_page)
320 @accepts_only('thing')
321 def has_landing_page(self, thing_res: URIRef) -> None:
322 """
323 Setter method corresponding to the ``dcat:landingPage`` RDF predicate.
325 **WARNING: this is a functional property, hence any existing value will be overwritten!**
327 `An HTML page (indicated by its URL) representing a browsable page for the dataset.`
329 :param thing_res: The value that will be set as the object of the property related to this method
330 :type thing_res: URIRef
331 :raises TypeError: if the parameter is of the wrong type
332 :return: None
333 """
334 self.remove_landing_page()
335 self.g.add((self.res, MetadataEntity.iri_landing_page, thing_res))
337 def remove_landing_page(self) -> None:
338 """
339 Remover method corresponding to the ``dcat:landingPage`` RDF predicate.
341 :return: None
342 """
343 self.g.remove((self.res, MetadataEntity.iri_landing_page, None))
345 # HAS SUB-DATASET
346 def get_sub_datasets(self) -> List[Dataset]:
347 """
348 Getter method corresponding to the ``void:subset`` RDF predicate.
350 :return: A list containing the requested values if found, None otherwise
351 """
352 uri_list: List[URIRef] = self._get_multiple_uri_references(MetadataEntity.iri_subset, '_dataset_')
353 result: List[Dataset] = []
354 for uri in uri_list:
355 result.append(self.m_set.add_dataset(self.dataset_name, self.resp_agent or "", self.source, uri))
356 return result
358 @accepts_only('_dataset_')
359 def has_sub_dataset(self, obj: Dataset) -> None:
360 """
361 Setter method corresponding to the ``void:subset`` RDF predicate.
363 `A link to a subset of the present dataset.`
365 :param obj: The value that will be set as the object of the property related to this method
366 :type obj: Dataset
367 :raises TypeError: if the parameter is of the wrong type
368 :return: None
369 """
370 self.g.add((self.res, MetadataEntity.iri_subset, obj.res))
372 @accepts_only('_dataset_')
373 def remove_sub_dataset(self, dataset_res: Dataset | None = None) -> None:
374 """
375 Remover method corresponding to the ``void:subset`` RDF predicate.
377 **WARNING: this is a non-functional property, hence, if the parameter
378 is None, any existing value will be removed!**
380 :param dataset_res: If not None, the specific object value that will be removed from the property
381 related to this method (defaults to None)
382 :type dataset_res: Dataset
383 :raises TypeError: if the parameter is of the wrong type
384 :return: None
385 """
386 if dataset_res is not None:
387 self.g.remove((self.res, MetadataEntity.iri_subset, dataset_res.res))
388 else:
389 self.g.remove((self.res, MetadataEntity.iri_subset, None))
391 # HAS SPARQL ENDPOINT
392 def get_sparql_endpoint(self) -> URIRef | None:
393 """
394 Getter method corresponding to the ``void:sparqlEndpoint`` RDF predicate.
396 :return: The requested value if found, None otherwise
397 """
398 uri: URIRef | None = self._get_uri_reference(MetadataEntity.iri_sparql_endpoint)
399 return uri
401 @accepts_only('thing')
402 def has_sparql_endpoint(self, thing_res: URIRef) -> None:
403 """
404 Setter method corresponding to the ``void:sparqlEndpoint`` RDF predicate.
406 **WARNING: this is a functional property, hence any existing value will be overwritten!**
408 `The link to the SPARQL endpoint for querying the dataset.`
410 :param thing_res: The value that will be set as the object of the property related to this method
411 :type thing_res: URIRef
412 :raises TypeError: if the parameter is of the wrong type
413 :return: None
414 """
415 self.remove_sparql_endpoint()
416 self.g.add((self.res, MetadataEntity.iri_sparql_endpoint, thing_res))
418 def remove_sparql_endpoint(self) -> None:
419 """
420 Remover method corresponding to the ``void:sparqlEndpoint`` RDF predicate.
422 :return: None
423 """
424 self.g.remove((self.res, MetadataEntity.iri_sparql_endpoint, None))
426 # HAS DISTRIBUTION (Distribution)
427 def get_distributions(self) -> List[Distribution]:
428 """
429 Getter method corresponding to the ``dcat:distribution`` RDF predicate.
431 :return: The requested value if found, None otherwise
432 """
433 uri_list: List[URIRef] = self._get_multiple_uri_references(MetadataEntity.iri_distribution, 'di')
434 result: List[Distribution] = []
435 for uri in uri_list:
436 result.append(self.m_set.add_di(self.dataset_name, self.resp_agent or "", self.source, uri))
437 return result
439 @accepts_only('di')
440 def has_distribution(self, obj: Distribution) -> None:
441 """
442 Setter method corresponding to the ``dcat:distribution`` RDF predicate.
444 `A distribution of the dataset.`
446 :param obj: The value that will be set as the object of the property related to this method
447 :type obj: Distribution
448 :raises TypeError: if the parameter is of the wrong type
449 :return: None
450 """
451 self.g.add((self.res, MetadataEntity.iri_distribution, obj.res))
453 @accepts_only('di')
454 def remove_distribution(self, di_res: Distribution | None = None) -> None:
455 """
456 Remover method corresponding to the ``dcat:distribution`` RDF predicate.
458 **WARNING: this is a non-functional property, hence, if the parameter
459 is None, any existing value will be removed!**
461 :param di_res: If not None, the specific object value that will be removed from the property
462 related to this method (defaults to None)
463 :type di_res: Distribution
464 :raises TypeError: if the parameter is of the wrong type
465 :return: None
466 """
467 if di_res is not None:
468 self.g.remove((self.res, MetadataEntity.iri_distribution, di_res.res))
469 else:
470 self.g.remove((self.res, MetadataEntity.iri_distribution, None))