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

1#!/usr/bin/python 

2 

3# SPDX-FileCopyrightText: 2020-2022 Simone Persiani <iosonopersia@gmail.com> 

4# 

5# SPDX-License-Identifier: ISC 

6 

7# -*- coding: utf-8 -*- 

8from __future__ import annotations 

9 

10import re 

11from typing import TYPE_CHECKING 

12 

13if TYPE_CHECKING: 

14 from typing import Optional 

15 

16from triplelite import RDFTerm 

17 

18from oc_ocdm.graph.entities.bibliographic_entity import BibliographicEntity 

19from oc_ocdm.graph.graph_entity import GraphEntity 

20 

21 

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.""" 

25 

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!** 

33 

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. 

39 

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) 

48 

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) 

52 

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) 

56 

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) 

60 

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) 

64 

65 # HAS FORMAT 

66 def get_media_type(self) -> Optional[str]: 

67 """ 

68 Getter method corresponding to the ``dcterms:format`` RDF predicate. 

69 

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 

74 

75 def has_media_type(self, thing_res: str) -> None: 

76 """ 

77 Setter method corresponding to the ``dcterms:format`` RDF predicate. 

78 

79 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

80 

81 `It allows one to specify the IANA media type of the embodiment.` 

82 

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)))) 

90 

91 def remove_media_type(self) -> None: 

92 """ 

93 Remover method corresponding to the ``dcterms:format`` RDF predicate. 

94 

95 :return: None 

96 """ 

97 self.g.remove((self.res, GraphEntity.iri_has_format, None)) 

98 

99 # HAS FIRST PAGE 

100 def get_starting_page(self) -> Optional[str]: 

101 """ 

102 Getter method corresponding to the ``prism:startingPage`` RDF predicate. 

103 

104 :return: The requested value if found, None otherwise 

105 """ 

106 return self._get_literal(GraphEntity.iri_starting_page) 

107 

108 def has_starting_page(self, string: str) -> None: 

109 """ 

110 Setter method corresponding to the ``prism:startingPage`` RDF predicate. 

111 

112 The string gets internally preprocessed by eventually removing dashes and everything 

113 that follows them (e.g. '22-45' becomes '22'). 

114 

115 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

116 

117 `The first page of the bibliographic resource according to the current embodiment.` 

118 

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) 

131 

132 def remove_starting_page(self) -> None: 

133 """ 

134 Remover method corresponding to the ``prism:startingPage`` RDF predicate. 

135 

136 :return: None 

137 """ 

138 self.g.remove((self.res, GraphEntity.iri_starting_page, None)) 

139 

140 # HAS LAST PAGE 

141 def get_ending_page(self) -> Optional[str]: 

142 """ 

143 Getter method corresponding to the ``prism:endingPage`` RDF predicate. 

144 

145 :return: The requested value if found, None otherwise 

146 """ 

147 return self._get_literal(GraphEntity.iri_ending_page) 

148 

149 def has_ending_page(self, string: str) -> None: 

150 """ 

151 Setter method corresponding to the ``prism:endingPage`` RDF predicate. 

152 

153 The string gets internally preprocessed by eventually removing dashes and everything 

154 that comes before them (e.g. '22-45' becomes '45'). 

155 

156 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

157 

158 `The last page of the bibliographic resource according to the current embodiment.` 

159 

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) 

172 

173 def remove_ending_page(self) -> None: 

174 """ 

175 Remover method corresponding to the ``prism:endingPage`` RDF predicate. 

176 

177 :return: None 

178 """ 

179 self.g.remove((self.res, GraphEntity.iri_ending_page, None)) 

180 

181 # HAS URL 

182 def get_url(self) -> Optional[str]: 

183 """ 

184 Getter method corresponding to the ``frbr:exemplar`` RDF predicate. 

185 

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 

190 

191 def has_url(self, thing_res: str) -> None: 

192 """ 

193 Setter method corresponding to the ``frbr:exemplar`` RDF predicate. 

194 

195 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

196 

197 `The URL at which the embodiment of the bibliographic resource is available.` 

198 

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)))) 

206 

207 def remove_url(self) -> None: 

208 """ 

209 Remover method corresponding to the ``frbr:exemplar`` RDF predicate. 

210 

211 :return: None 

212 """ 

213 self.g.remove((self.res, GraphEntity.iri_has_url, None)) 

214 

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``. 

220 

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!** 

224 

225 `It identifies the particular type of the embodiment, either digital or print.` 

226 

227 :return: None 

228 """ 

229 self._create_type(GraphEntity.iri_digital_manifestation) 

230 

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``. 

235 

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!** 

239 

240 `It identifies the particular type of the embodiment, either digital or print.` 

241 

242 :return: None 

243 """ 

244 self._create_type(GraphEntity.iri_print_object)