Coverage for oc_ocdm/metadata/entities/dataset.py: 53%
141 statements
« prev ^ index » next coverage.py v6.5.0, created at 2025-05-30 22:05 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2025-05-30 22:05 +0000
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2016, Silvio Peroni <essepuntato@gmail.com>
4#
5# Permission to use, copy, modify, and/or distribute this software for any purpose
6# with or without fee is hereby granted, provided that the above copyright notice
7# and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
12# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
13# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15# SOFTWARE.
16from __future__ import annotations
18from typing import TYPE_CHECKING
20from oc_ocdm.decorators import accepts_only
21from oc_ocdm.metadata.metadata_entity import MetadataEntity
22from rdflib import XSD
24if TYPE_CHECKING:
25 from typing import Optional, List
26 from rdflib import URIRef
27 from oc_ocdm.metadata.entities.distribution import Distribution
30class Dataset(MetadataEntity):
31 """Dataset (short: not applicable and strictly dependent on the implementation of the
32 dataset infrastructure): a set of collected information about something."""
34 @accepts_only('_dataset_')
35 def merge(self, other: Dataset) -> None:
36 """
37 The merge operation allows combining two ``Dataset`` entities into a single one,
38 by marking the second entity as to be deleted while also copying its data into the current
39 ``Dataset``. Moreover, every triple from the containing ``MetadataSet`` referring to the second
40 entity gets "redirected" to the current entity: **every other reference contained inside a
41 different source (e.g. a triplestore) must be manually handled by the user!**
43 In case of functional properties, values from the current entity get overwritten
44 by those coming from the second entity while, in all other cases, values from the
45 second entity are simply appended to those of the current entity. In this context,
46 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not.
48 :param other: The entity which will be marked as to be deleted and whose properties will
49 be merged into the current entity.
50 :type other: Dataset
51 :raises TypeError: if the parameter is of the wrong type
52 :return: None
53 """
54 super(Dataset, self).merge(other)
56 title: Optional[str] = other.get_title()
57 if title is not None:
58 self.has_title(title)
60 description: Optional[str] = other.get_description()
61 if description is not None:
62 self.has_description(description)
64 pub_date: Optional[str] = other.get_publication_date()
65 if pub_date is not None:
66 self.has_publication_date(pub_date)
68 mod_date: Optional[str] = other.get_modification_date()
69 if mod_date is not None:
70 self.has_modification_date(mod_date)
72 keywords_list: List[str] = other.get_keywords()
73 for cur_keyword in keywords_list:
74 self.has_keyword(cur_keyword)
76 subjects_list: List[URIRef] = other.get_subjects()
77 for cur_subject in subjects_list:
78 self.has_subject(cur_subject)
80 landing_page: Optional[URIRef] = other.get_landing_page()
81 if landing_page is not None:
82 self.has_landing_page(landing_page)
84 sub_datasets_list: List[Dataset] = other.get_sub_datasets()
85 for cur_sub_dataset in sub_datasets_list:
86 self.has_sub_dataset(cur_sub_dataset)
88 sparql_endpoint: Optional[URIRef] = other.get_sparql_endpoint()
89 if sparql_endpoint is not None:
90 self.has_sparql_endpoint(sparql_endpoint)
92 distributions_list: List[Distribution] = other.get_distributions()
93 for cur_distribution in distributions_list:
94 self.has_distribution(cur_distribution)
96 # HAS TITLE
97 def get_title(self) -> Optional[str]:
98 """
99 Getter method corresponding to the ``dcterms:title`` RDF predicate.
101 :return: The requested value if found, None otherwise
102 """
103 return self._get_literal(MetadataEntity.iri_title)
105 @accepts_only('literal')
106 def has_title(self, string: str) -> None:
107 """
108 Setter method corresponding to the ``dcterms:title`` RDF predicate.
110 **WARNING: this is a functional property, hence any existing value will be overwritten!**
112 `The title of the dataset.`
114 :param string: The value that will be set as the object of the property related to this method
115 :type string: str
116 :raises TypeError: if the parameter is of the wrong type
117 :return: None
118 """
119 self.remove_title()
120 self._create_literal(MetadataEntity.iri_title, string)
122 def remove_title(self) -> None:
123 """
124 Remover method corresponding to the ``dcterms:title`` RDF predicate.
126 :return: None
127 """
128 self.g.remove((self.res, MetadataEntity.iri_title, None))
130 # HAS DESCRIPTION
131 def get_description(self) -> Optional[str]:
132 """
133 Getter method corresponding to the ``dcterms:description`` RDF predicate.
135 :return: The requested value if found, None otherwise
136 """
137 return self._get_literal(MetadataEntity.iri_description)
139 @accepts_only('literal')
140 def has_description(self, string: str) -> None:
141 """
142 Setter method corresponding to the ``dcterms:description`` RDF predicate.
144 **WARNING: this is a functional property, hence any existing value will be overwritten!**
146 `A short textual description of the content of the dataset.`
148 :param string: The value that will be set as the object of the property related to this method
149 :type string: str
150 :raises TypeError: if the parameter is of the wrong type
151 :return: None
152 """
153 self.remove_description()
154 self._create_literal(MetadataEntity.iri_description, string)
156 def remove_description(self) -> None:
157 """
158 Remover method corresponding to the ``dcterms:description`` RDF predicate.
160 :return: None
161 """
162 self.g.remove((self.res, MetadataEntity.iri_description, None))
164 # HAS PUBLICATION DATE
165 def get_publication_date(self) -> Optional[str]:
166 """
167 Getter method corresponding to the ``dcterms:issued`` RDF predicate.
169 :return: The requested value if found, None otherwise
170 """
171 return self._get_literal(MetadataEntity.iri_issued)
173 @accepts_only('literal')
174 def has_publication_date(self, string: str) -> None:
175 """
176 Setter method corresponding to the ``dcterms:issued`` RDF predicate.
178 **WARNING: this is a functional property, hence any existing value will be overwritten!**
180 `The date of first publication of the dataset.`
182 :param string: The value that will be set as the object of the property related to this method. **It must
183 be a string compliant with the** ``xsd:dateTime`` **datatype.**
184 :type string: str
185 :raises TypeError: if the parameter is of the wrong type
186 :return: None
187 """
188 self.remove_publication_date()
189 self._create_literal(MetadataEntity.iri_issued, string, XSD.dateTime, False)
191 def remove_publication_date(self) -> None:
192 """
193 Remover method corresponding to the ``dcterms:issued`` RDF predicate.
195 :return: None
196 """
197 self.g.remove((self.res, MetadataEntity.iri_issued, None))
199 # HAS MODIFICATION DATE
200 def get_modification_date(self) -> Optional[str]:
201 """
202 Getter method corresponding to the ``dcterms:modified`` RDF predicate.
204 :return: The requested value if found, None otherwise
205 """
206 return self._get_literal(MetadataEntity.iri_modified)
208 @accepts_only('literal')
209 def has_modification_date(self, string: str) -> None:
210 """
211 Setter method corresponding to the ``dcterms:modified`` RDF predicate.
213 **WARNING: this is a functional property, hence any existing value will be overwritten!**
215 `The date on which the dataset has been modified.`
217 :param string: The value that will be set as the object of the property related to this method. **It must
218 be a string compliant with the** ``xsd:dateTime`` **datatype.**
219 :type string: str
220 :raises TypeError: if the parameter is of the wrong type
221 :return: None
222 """
223 self.remove_modification_date()
224 self._create_literal(MetadataEntity.iri_modified, string, XSD.dateTime, False)
226 def remove_modification_date(self) -> None:
227 """
228 Remover method corresponding to the ``dcterms:modified`` RDF predicate.
230 :return: None
231 """
232 self.g.remove((self.res, MetadataEntity.iri_modified, None))
234 # HAS KEYWORD
235 def get_keywords(self) -> List[str]:
236 """
237 Getter method corresponding to the ``dcat:keyword`` RDF predicate.
239 :return: A list containing the requested values if found, None otherwise
240 """
241 return self._get_multiple_literals(MetadataEntity.iri_keyword)
243 @accepts_only('literal')
244 def has_keyword(self, string: str) -> None:
245 """
246 Setter method corresponding to the ``dcat:keyword`` RDF predicate.
248 `A keyword or phrase describing the content of the dataset.`
250 :param string: The value that will be set as the object of the property related to this method
251 :type string: str
252 :raises TypeError: if the parameter is of the wrong type
253 :return: None
254 """
255 self._create_literal(MetadataEntity.iri_keyword, string)
257 @accepts_only('literal')
258 def remove_keyword(self, string: str = None) -> None:
259 """
260 Remover method corresponding to the ``dcat:keyword`` RDF predicate.
262 **WARNING: this is a non-functional property, hence, if the parameter
263 is None, any existing value will be removed!**
265 :param string: If not None, the specific object value that will be removed from the property
266 related to this method (defaults to None)
267 :type string: str
268 :raises TypeError: if the parameter is of the wrong type
269 :return: None
270 """
271 if string is not None:
272 self.g.remove((self.res, MetadataEntity.iri_keyword, string))
273 else:
274 self.g.remove((self.res, MetadataEntity.iri_keyword, None))
276 # HAS SUBJECT
277 def get_subjects(self) -> List[URIRef]:
278 """
279 Getter method corresponding to the ``dcat:theme`` RDF predicate.
281 :return: A list containing the requested values if found, None otherwise
282 """
283 uri_list: List[URIRef] = self._get_multiple_uri_references(MetadataEntity.iri_subject)
284 return uri_list
286 @accepts_only('thing')
287 def has_subject(self, thing_res: URIRef) -> None:
288 """
289 Setter method corresponding to the ``dcat:theme`` RDF predicate.
291 `A concept describing the primary subject of the dataset.`
293 :param thing_res: The value that will be set as the object of the property related to this method
294 :type thing_res: URIRef
295 :raises TypeError: if the parameter is of the wrong type
296 :return: None
297 """
298 self.g.add((self.res, MetadataEntity.iri_subject, thing_res))
300 @accepts_only('thing')
301 def remove_subject(self, thing_res: URIRef = None) -> None:
302 """
303 Remover method corresponding to the ``dcat:theme`` RDF predicate.
305 **WARNING: this is a non-functional property, hence, if the parameter
306 is None, any existing value will be removed!**
308 :param thing_res: If not None, the specific object value that will be removed from the property
309 related to this method (defaults to None)
310 :type thing_res: URIRef
311 :raises TypeError: if the parameter is of the wrong type
312 :return: None
313 """
314 if thing_res is not None:
315 self.g.remove((self.res, MetadataEntity.iri_subject, thing_res))
316 else:
317 self.g.remove((self.res, MetadataEntity.iri_subject, None))
319 # HAS LANDING PAGE
320 def get_landing_page(self) -> Optional[URIRef]:
321 """
322 Getter method corresponding to the ``dcat:landingPage`` RDF predicate.
324 :return: The requested value if found, None otherwise
325 """
326 return self._get_literal(MetadataEntity.iri_landing_page)
328 @accepts_only('thing')
329 def has_landing_page(self, thing_res: URIRef) -> None:
330 """
331 Setter method corresponding to the ``dcat:landingPage`` RDF predicate.
333 **WARNING: this is a functional property, hence any existing value will be overwritten!**
335 `An HTML page (indicated by its URL) representing a browsable page for the dataset.`
337 :param thing_res: The value that will be set as the object of the property related to this method
338 :type thing_res: URIRef
339 :raises TypeError: if the parameter is of the wrong type
340 :return: None
341 """
342 self.remove_landing_page()
343 self.g.add((self.res, MetadataEntity.iri_landing_page, thing_res))
345 def remove_landing_page(self) -> None:
346 """
347 Remover method corresponding to the ``dcat:landingPage`` RDF predicate.
349 :return: None
350 """
351 self.g.remove((self.res, MetadataEntity.iri_landing_page, None))
353 # HAS SUB-DATASET
354 def get_sub_datasets(self) -> List[Dataset]:
355 """
356 Getter method corresponding to the ``void:subset`` RDF predicate.
358 :return: A list containing the requested values if found, None otherwise
359 """
360 uri_list: List[URIRef] = self._get_multiple_uri_references(MetadataEntity.iri_subset, '_dataset_')
361 result: List[Dataset] = []
362 for uri in uri_list:
363 result.append(self.m_set.add_dataset(self.resp_agent, self.source, uri))
364 return result
366 @accepts_only('_dataset_')
367 def has_sub_dataset(self, obj: Dataset) -> None:
368 """
369 Setter method corresponding to the ``void:subset`` RDF predicate.
371 `A link to a subset of the present dataset.`
373 :param obj: The value that will be set as the object of the property related to this method
374 :type obj: Dataset
375 :raises TypeError: if the parameter is of the wrong type
376 :return: None
377 """
378 self.g.add((self.res, MetadataEntity.iri_subset, obj.res))
380 @accepts_only('_dataset_')
381 def remove_sub_dataset(self, dataset_res: Dataset = None) -> None:
382 """
383 Remover method corresponding to the ``void:subset`` RDF predicate.
385 **WARNING: this is a non-functional property, hence, if the parameter
386 is None, any existing value will be removed!**
388 :param dataset_res: If not None, the specific object value that will be removed from the property
389 related to this method (defaults to None)
390 :type dataset_res: Dataset
391 :raises TypeError: if the parameter is of the wrong type
392 :return: None
393 """
394 if dataset_res is not None:
395 self.g.remove((self.res, MetadataEntity.iri_subset, dataset_res.res))
396 else:
397 self.g.remove((self.res, MetadataEntity.iri_subset, None))
399 # HAS SPARQL ENDPOINT
400 def get_sparql_endpoint(self) -> Optional[URIRef]:
401 """
402 Getter method corresponding to the ``void:sparqlEndpoint`` RDF predicate.
404 :return: The requested value if found, None otherwise
405 """
406 uri: Optional[URIRef] = self._get_uri_reference(MetadataEntity.iri_sparql_endpoint)
407 return uri
409 @accepts_only('thing')
410 def has_sparql_endpoint(self, thing_res: URIRef) -> None:
411 """
412 Setter method corresponding to the ``void:sparqlEndpoint`` RDF predicate.
414 **WARNING: this is a functional property, hence any existing value will be overwritten!**
416 `The link to the SPARQL endpoint for querying the dataset.`
418 :param thing_res: The value that will be set as the object of the property related to this method
419 :type thing_res: URIRef
420 :raises TypeError: if the parameter is of the wrong type
421 :return: None
422 """
423 self.remove_sparql_endpoint()
424 self.g.add((self.res, MetadataEntity.iri_sparql_endpoint, thing_res))
426 def remove_sparql_endpoint(self) -> None:
427 """
428 Remover method corresponding to the ``void:sparqlEndpoint`` RDF predicate.
430 :return: None
431 """
432 self.g.remove((self.res, MetadataEntity.iri_sparql_endpoint, None))
434 # HAS DISTRIBUTION (Distribution)
435 def get_distributions(self) -> List[Distribution]:
436 """
437 Getter method corresponding to the ``dcat:distribution`` RDF predicate.
439 :return: The requested value if found, None otherwise
440 """
441 uri_list: List[URIRef] = self._get_multiple_uri_references(MetadataEntity.iri_distribution, 'di')
442 result: List[Distribution] = []
443 for uri in uri_list:
444 result.append(self.m_set.add_di(self.resp_agent, self.source, uri))
445 return result
447 @accepts_only('di')
448 def has_distribution(self, obj: Distribution) -> None:
449 """
450 Setter method corresponding to the ``dcat:distribution`` RDF predicate.
452 `A distribution of the dataset.`
454 :param obj: The value that will be set as the object of the property related to this method
455 :type obj: Distribution
456 :raises TypeError: if the parameter is of the wrong type
457 :return: None
458 """
459 self.g.add((self.res, MetadataEntity.iri_distribution, obj.res))
461 @accepts_only('di')
462 def remove_distribution(self, di_res: Distribution = None) -> None:
463 """
464 Remover method corresponding to the ``dcat:distribution`` RDF predicate.
466 **WARNING: this is a non-functional property, hence, if the parameter
467 is None, any existing value will be removed!**
469 :param di_res: If not None, the specific object value that will be removed from the property
470 related to this method (defaults to None)
471 :type di_res: Distribution
472 :raises TypeError: if the parameter is of the wrong type
473 :return: None
474 """
475 if di_res is not None:
476 self.g.remove((self.res, MetadataEntity.iri_distribution, di_res.res))
477 else:
478 self.g.remove((self.res, MetadataEntity.iri_distribution, None))