Coverage for oc_ocdm / graph / entities / bibliographic / resource_embodiment.py: 94%
62 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-07-06 20:05 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-07-06 20:05 +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
10import re
11from typing import TYPE_CHECKING
13if TYPE_CHECKING:
14 from typing import Optional
16from triplelite import RDFTerm
18from oc_ocdm.graph.entities.bibliographic_entity import BibliographicEntity
19from oc_ocdm.graph.graph_entity import GraphEntity
22class ResourceEmbodiment(BibliographicEntity):
23 """Resource embodiment (short: re): the particular physical or digital format in which a
24 bibliographic resource was made available by its publisher."""
26 def _merge_properties(self, other: GraphEntity, prefer_self: bool) -> None:
27 """
28 The merge operation allows combining two ``ResourceEmbodiment`` entities into a single one,
29 by marking the second entity as to be deleted while also copying its data into the current
30 ``ResourceEmbodiment``. Moreover, every triple from the containing ``GraphSet`` 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 By default, functional values from the current entity get overwritten by values
35 from the second entity. When ``prefer_self`` is True, existing functional values
36 from the current entity are kept. Non-functional values from the second entity
37 are appended to those of the current entity. In this context,
38 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not.
40 :param other: The entity which will be marked as to be deleted and whose properties will
41 be merged into the current entity.
42 :type other: ResourceEmbodiment
43 :raises TypeError: if the parameter is of the wrong type
44 :return: None
45 """
46 super()._merge_properties(other, prefer_self)
47 assert isinstance(other, ResourceEmbodiment)
49 media_type: Optional[str] = other.get_media_type()
50 if media_type is not None and self._should_merge_functional_property(self.get_media_type(), prefer_self):
51 self.has_media_type(media_type)
53 starting_page: Optional[str] = other.get_starting_page()
54 if starting_page is not None and self._should_merge_functional_property(self.get_starting_page(), prefer_self):
55 self.has_starting_page(starting_page)
57 ending_page: Optional[str] = other.get_ending_page()
58 if ending_page is not None and self._should_merge_functional_property(self.get_ending_page(), prefer_self):
59 self.has_ending_page(ending_page)
61 url: Optional[str] = other.get_url()
62 if url is not None and self._should_merge_functional_property(self.get_url(), prefer_self):
63 self.has_url(url)
65 # HAS FORMAT
66 def get_media_type(self) -> Optional[str]:
67 """
68 Getter method corresponding to the ``dcterms:format`` RDF predicate.
70 :return: The requested value if found, None otherwise
71 """
72 uri: Optional[str] = self._get_uri_reference(GraphEntity.iri_has_format)
73 return uri
75 def has_media_type(self, thing_res: str) -> None:
76 """
77 Setter method corresponding to the ``dcterms:format`` RDF predicate.
79 **WARNING: this is a functional property, hence any existing value will be overwritten!**
81 `It allows one to specify the IANA media type of the embodiment.`
83 :param thing_res: The value that will be set as the object of the property related to this method
84 :type thing_res: URIRef
85 :raises TypeError: if the parameter is of the wrong type
86 :return: None
87 """
88 self.remove_media_type()
89 self.g.add((self.res, GraphEntity.iri_has_format, RDFTerm("uri", str(thing_res))))
91 def remove_media_type(self) -> None:
92 """
93 Remover method corresponding to the ``dcterms:format`` RDF predicate.
95 :return: None
96 """
97 self.g.remove((self.res, GraphEntity.iri_has_format, None))
99 # HAS FIRST PAGE
100 def get_starting_page(self) -> Optional[str]:
101 """
102 Getter method corresponding to the ``prism:startingPage`` RDF predicate.
104 :return: The requested value if found, None otherwise
105 """
106 return self._get_literal(GraphEntity.iri_starting_page)
108 def has_starting_page(self, string: str) -> None:
109 """
110 Setter method corresponding to the ``prism:startingPage`` RDF predicate.
112 The string gets internally preprocessed by eventually removing dashes and everything
113 that follows them (e.g. '22-45' becomes '22').
115 **WARNING: this is a functional property, hence any existing value will be overwritten!**
117 `The first page of the bibliographic resource according to the current embodiment.`
119 :param string: The value that will be set as the object of the property related to this method. **It must
120 be a string that starts with an integer number.**
121 :type string: str
122 :raises TypeError: if the parameter is of the wrong type
123 :return: None
124 """
125 self.remove_starting_page()
126 if re.search("[-–]+", string) is None:
127 page_number = string
128 else:
129 page_number = re.sub("[-–]+.*$", "", string)
130 self._create_literal(GraphEntity.iri_starting_page, page_number)
132 def remove_starting_page(self) -> None:
133 """
134 Remover method corresponding to the ``prism:startingPage`` RDF predicate.
136 :return: None
137 """
138 self.g.remove((self.res, GraphEntity.iri_starting_page, None))
140 # HAS LAST PAGE
141 def get_ending_page(self) -> Optional[str]:
142 """
143 Getter method corresponding to the ``prism:endingPage`` RDF predicate.
145 :return: The requested value if found, None otherwise
146 """
147 return self._get_literal(GraphEntity.iri_ending_page)
149 def has_ending_page(self, string: str) -> None:
150 """
151 Setter method corresponding to the ``prism:endingPage`` RDF predicate.
153 The string gets internally preprocessed by eventually removing dashes and everything
154 that comes before them (e.g. '22-45' becomes '45').
156 **WARNING: this is a functional property, hence any existing value will be overwritten!**
158 `The last page of the bibliographic resource according to the current embodiment.`
160 :param string: The value that will be set as the object of the property related to this method. **It must
161 be a string that ends with an integer number.**
162 :type string: str
163 :raises TypeError: if the parameter is of the wrong type
164 :return: None
165 """
166 self.remove_ending_page()
167 if re.search("[-–]+", string) is None:
168 page_number = string
169 else:
170 page_number = re.sub("^.*[-–]+", "", string)
171 self._create_literal(GraphEntity.iri_ending_page, page_number)
173 def remove_ending_page(self) -> None:
174 """
175 Remover method corresponding to the ``prism:endingPage`` RDF predicate.
177 :return: None
178 """
179 self.g.remove((self.res, GraphEntity.iri_ending_page, None))
181 # HAS URL
182 def get_url(self) -> Optional[str]:
183 """
184 Getter method corresponding to the ``frbr:exemplar`` RDF predicate.
186 :return: The requested value if found, None otherwise
187 """
188 uri: Optional[str] = self._get_uri_reference(GraphEntity.iri_has_url)
189 return uri
191 def has_url(self, thing_res: str) -> None:
192 """
193 Setter method corresponding to the ``frbr:exemplar`` RDF predicate.
195 **WARNING: this is a functional property, hence any existing value will be overwritten!**
197 `The URL at which the embodiment of the bibliographic resource is available.`
199 :param thing_res: The value that will be set as the object of the property related to this method
200 :type thing_res: URIRef
201 :raises TypeError: if the parameter is of the wrong type
202 :return: None
203 """
204 self.remove_url()
205 self.g.add((self.res, GraphEntity.iri_has_url, RDFTerm("uri", str(thing_res))))
207 def remove_url(self) -> None:
208 """
209 Remover method corresponding to the ``frbr:exemplar`` RDF predicate.
211 :return: None
212 """
213 self.g.remove((self.res, GraphEntity.iri_has_url, None))
215 # HAS TYPE
216 def create_digital_embodiment(self) -> None:
217 """
218 Setter method corresponding to the ``rdf:type`` RDF predicate.
219 It implicitly sets the object value ``fabio:DigitalManifestation``.
221 **WARNING: the OCDM specification admits at most two types for an entity.
222 The main type cannot be edited or removed. Any existing secondary type
223 will be overwritten!**
225 `It identifies the particular type of the embodiment, either digital or print.`
227 :return: None
228 """
229 self._create_type(GraphEntity.iri_digital_manifestation)
231 def create_print_embodiment(self) -> None:
232 """
233 Setter method corresponding to the ``rdf:type`` RDF predicate.
234 It implicitly sets the object value ``fabio:PrintObject``.
236 **WARNING: the OCDM specification admits at most two types for an entity.
237 The main type cannot be edited or removed. Any existing secondary type
238 will be overwritten!**
240 `It identifies the particular type of the embodiment, either digital or print.`
242 :return: None
243 """
244 self._create_type(GraphEntity.iri_print_object)