Coverage for oc_ocdm / graph / entities / bibliographic / resource_embodiment.py: 94%

62 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-05-08 20:23 +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 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. 

38 

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) 

47 

48 media_type: Optional[str] = other.get_media_type() 

49 if media_type is not None: 

50 self.has_media_type(media_type) 

51 

52 starting_page: Optional[str] = other.get_starting_page() 

53 if starting_page is not None: 

54 self.has_starting_page(starting_page) 

55 

56 ending_page: Optional[str] = other.get_ending_page() 

57 if ending_page is not None: 

58 self.has_ending_page(ending_page) 

59 

60 url: Optional[str] = other.get_url() 

61 if url is not None: 

62 self.has_url(url) 

63 

64 # HAS FORMAT 

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

66 """ 

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

68 

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

70 """ 

71 uri: Optional[str] = self._get_uri_reference(GraphEntity.iri_has_format) 

72 return uri 

73 

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

75 """ 

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

77 

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

79 

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

81 

82 :param thing_res: The value that will be set as the object of the property related to this method 

83 :type thing_res: URIRef 

84 :raises TypeError: if the parameter is of the wrong type 

85 :return: None 

86 """ 

87 self.remove_media_type() 

88 self.g.add((self.res, GraphEntity.iri_has_format, RDFTerm("uri", str(thing_res)))) 

89 

90 def remove_media_type(self) -> None: 

91 """ 

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

93 

94 :return: None 

95 """ 

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

97 

98 # HAS FIRST PAGE 

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

100 """ 

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

102 

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

104 """ 

105 return self._get_literal(GraphEntity.iri_starting_page) 

106 

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

108 """ 

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

110 

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

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

113 

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

115 

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

117 

118 :param string: The value that will be set as the object of the property related to this method. **It must 

119 be a string that starts with an integer number.** 

120 :type string: str 

121 :raises TypeError: if the parameter is of the wrong type 

122 :return: None 

123 """ 

124 self.remove_starting_page() 

125 if re.search("[-–]+", string) is None: 

126 page_number = string 

127 else: 

128 page_number = re.sub("[-–]+.*$", "", string) 

129 self._create_literal(GraphEntity.iri_starting_page, page_number) 

130 

131 def remove_starting_page(self) -> None: 

132 """ 

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

134 

135 :return: None 

136 """ 

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

138 

139 # HAS LAST PAGE 

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

141 """ 

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

143 

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

145 """ 

146 return self._get_literal(GraphEntity.iri_ending_page) 

147 

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

149 """ 

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

151 

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

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

154 

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

156 

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

158 

159 :param string: The value that will be set as the object of the property related to this method. **It must 

160 be a string that ends with an integer number.** 

161 :type string: str 

162 :raises TypeError: if the parameter is of the wrong type 

163 :return: None 

164 """ 

165 self.remove_ending_page() 

166 if re.search("[-–]+", string) is None: 

167 page_number = string 

168 else: 

169 page_number = re.sub("^.*[-–]+", "", string) 

170 self._create_literal(GraphEntity.iri_ending_page, page_number) 

171 

172 def remove_ending_page(self) -> None: 

173 """ 

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

175 

176 :return: None 

177 """ 

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

179 

180 # HAS URL 

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

182 """ 

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

184 

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

186 """ 

187 uri: Optional[str] = self._get_uri_reference(GraphEntity.iri_has_url) 

188 return uri 

189 

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

191 """ 

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

193 

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

195 

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

197 

198 :param thing_res: The value that will be set as the object of the property related to this method 

199 :type thing_res: URIRef 

200 :raises TypeError: if the parameter is of the wrong type 

201 :return: None 

202 """ 

203 self.remove_url() 

204 self.g.add((self.res, GraphEntity.iri_has_url, RDFTerm("uri", str(thing_res)))) 

205 

206 def remove_url(self) -> None: 

207 """ 

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

209 

210 :return: None 

211 """ 

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

213 

214 # HAS TYPE 

215 def create_digital_embodiment(self) -> None: 

216 """ 

217 Setter method corresponding to the ``rdf:type`` RDF predicate. 

218 It implicitly sets the object value ``fabio:DigitalManifestation``. 

219 

220 **WARNING: the OCDM specification admits at most two types for an entity. 

221 The main type cannot be edited or removed. Any existing secondary type 

222 will be overwritten!** 

223 

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

225 

226 :return: None 

227 """ 

228 self._create_type(GraphEntity.iri_digital_manifestation) 

229 

230 def create_print_embodiment(self) -> None: 

231 """ 

232 Setter method corresponding to the ``rdf:type`` RDF predicate. 

233 It implicitly sets the object value ``fabio:PrintObject``. 

234 

235 **WARNING: the OCDM specification admits at most two types for an entity. 

236 The main type cannot be edited or removed. Any existing secondary type 

237 will be overwritten!** 

238 

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

240 

241 :return: None 

242 """ 

243 self._create_type(GraphEntity.iri_print_object)