Coverage for oc_ocdm / graph / entities / bibliographic / resource_embodiment.py: 94%
66 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
10import re
11from typing import TYPE_CHECKING
13if TYPE_CHECKING:
14 from typing import Optional
15 from rdflib import URIRef
17from oc_ocdm.graph.graph_entity import GraphEntity
18from oc_ocdm.decorators import accepts_only
19from oc_ocdm.graph.entities.bibliographic_entity import BibliographicEntity
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 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: ResourceEmbodiment
42 :raises TypeError: if the parameter is of the wrong type
43 :return: None
44 """
45 super()._merge_properties(other, prefer_self)
46 assert isinstance(other, ResourceEmbodiment)
48 media_type: Optional[URIRef] = other.get_media_type()
49 if media_type is not None:
50 self.has_media_type(media_type)
52 starting_page: Optional[str] = other.get_starting_page()
53 if starting_page is not None:
54 self.has_starting_page(starting_page)
56 ending_page: Optional[str] = other.get_ending_page()
57 if ending_page is not None:
58 self.has_ending_page(ending_page)
60 url: Optional[URIRef] = other.get_url()
61 if url is not None:
62 self.has_url(url)
64 # HAS FORMAT
65 def get_media_type(self) -> Optional[URIRef]:
66 """
67 Getter method corresponding to the ``dcterms:format`` RDF predicate.
69 :return: The requested value if found, None otherwise
70 """
71 uri: Optional[URIRef] = self._get_uri_reference(GraphEntity.iri_has_format)
72 return uri
74 @accepts_only('thing')
75 def has_media_type(self, thing_res: URIRef) -> 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, 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 @accepts_only('literal')
109 def has_starting_page(self, string: str) -> None:
110 """
111 Setter method corresponding to the ``prism:startingPage`` RDF predicate.
113 The string gets internally preprocessed by eventually removing dashes and everything
114 that follows them (e.g. '22-45' becomes '22').
116 **WARNING: this is a functional property, hence any existing value will be overwritten!**
118 `The first page of the bibliographic resource according to the current embodiment.`
120 :param string: The value that will be set as the object of the property related to this method. **It must
121 be a string that starts with an integer number.**
122 :type string: str
123 :raises TypeError: if the parameter is of the wrong type
124 :return: None
125 """
126 self.remove_starting_page()
127 if re.search("[-–]+", string) is None:
128 page_number = string
129 else:
130 page_number = re.sub("[-–]+.*$", "", string)
131 self._create_literal(GraphEntity.iri_starting_page, page_number)
133 def remove_starting_page(self) -> None:
134 """
135 Remover method corresponding to the ``prism:startingPage`` RDF predicate.
137 :return: None
138 """
139 self.g.remove((self.res, GraphEntity.iri_starting_page, None))
141 # HAS LAST PAGE
142 def get_ending_page(self) -> Optional[str]:
143 """
144 Getter method corresponding to the ``prism:endingPage`` RDF predicate.
146 :return: The requested value if found, None otherwise
147 """
148 return self._get_literal(GraphEntity.iri_ending_page)
150 @accepts_only('literal')
151 def has_ending_page(self, string: str) -> None:
152 """
153 Setter method corresponding to the ``prism:endingPage`` RDF predicate.
155 The string gets internally preprocessed by eventually removing dashes and everything
156 that comes before them (e.g. '22-45' becomes '45').
158 **WARNING: this is a functional property, hence any existing value will be overwritten!**
160 `The last page of the bibliographic resource according to the current embodiment.`
162 :param string: The value that will be set as the object of the property related to this method. **It must
163 be a string that ends with an integer number.**
164 :type string: str
165 :raises TypeError: if the parameter is of the wrong type
166 :return: None
167 """
168 self.remove_ending_page()
169 if re.search("[-–]+", string) is None:
170 page_number = string
171 else:
172 page_number = re.sub("^.*[-–]+", "", string)
173 self._create_literal(GraphEntity.iri_ending_page, page_number)
175 def remove_ending_page(self) -> None:
176 """
177 Remover method corresponding to the ``prism:endingPage`` RDF predicate.
179 :return: None
180 """
181 self.g.remove((self.res, GraphEntity.iri_ending_page, None))
183 # HAS URL
184 def get_url(self) -> Optional[URIRef]:
185 """
186 Getter method corresponding to the ``frbr:exemplar`` RDF predicate.
188 :return: The requested value if found, None otherwise
189 """
190 uri: Optional[URIRef] = self._get_uri_reference(GraphEntity.iri_has_url)
191 return uri
193 @accepts_only('thing')
194 def has_url(self, thing_res: URIRef) -> None:
195 """
196 Setter method corresponding to the ``frbr:exemplar`` RDF predicate.
198 **WARNING: this is a functional property, hence any existing value will be overwritten!**
200 `The URL at which the embodiment of the bibliographic resource is available.`
202 :param thing_res: The value that will be set as the object of the property related to this method
203 :type thing_res: URIRef
204 :raises TypeError: if the parameter is of the wrong type
205 :return: None
206 """
207 self.remove_url()
208 self.g.add((self.res, GraphEntity.iri_has_url, thing_res))
210 def remove_url(self) -> None:
211 """
212 Remover method corresponding to the ``frbr:exemplar`` RDF predicate.
214 :return: None
215 """
216 self.g.remove((self.res, GraphEntity.iri_has_url, None))
218 # HAS TYPE
219 def create_digital_embodiment(self) -> None:
220 """
221 Setter method corresponding to the ``rdf:type`` RDF predicate.
222 It implicitly sets the object value ``fabio:DigitalManifestation``.
224 **WARNING: the OCDM specification admits at most two types for an entity.
225 The main type cannot be edited or removed. Any existing secondary type
226 will be overwritten!**
228 `It identifies the particular type of the embodiment, either digital or print.`
230 :return: None
231 """
232 self._create_type(GraphEntity.iri_digital_manifestation)
234 def create_print_embodiment(self) -> None:
235 """
236 Setter method corresponding to the ``rdf:type`` RDF predicate.
237 It implicitly sets the object value ``fabio:PrintObject``.
239 **WARNING: the OCDM specification admits at most two types for an entity.
240 The main type cannot be edited or removed. Any existing secondary type
241 will be overwritten!**
243 `It identifies the particular type of the embodiment, either digital or print.`
245 :return: None
246 """
247 self._create_type(GraphEntity.iri_print_object)