default_layout.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. from copy import deepcopy
  2. from pprint import pformat
  3. from flask import current_app, g, request
  4. from rdflib import Graph
  5. from rdflib.namespace import RDF, XSD
  6. from rdflib.query import ResultException
  7. from rdflib.resource import Resource
  8. from rdflib.term import Literal, URIRef, Variable
  9. from lakesuperior.dictionaries.namespaces import ns_collection as nsc
  10. from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
  11. from lakesuperior.dictionaries.srv_mgd_terms import (srv_mgd_subjects,
  12. srv_mgd_predicates, srv_mgd_types)
  13. from lakesuperior.exceptions import (InvalidResourceError, InvalidTripleError,
  14. ResourceNotExistsError, TombstoneError)
  15. from lakesuperior.store_layouts.ldp_rs.base_rdf_layout import BaseRdfLayout
  16. class DefaultLayout(BaseRdfLayout):
  17. '''
  18. This is the default layout.
  19. Main triples are stored in a `main` graph; metadata in the `meta` graph;
  20. and historic snapshots (versions) in `historic`.
  21. '''
  22. HIST_GRAPH_URI = nsc['fcg'].historic
  23. MAIN_GRAPH_URI = nsc['fcg'].main
  24. META_GRAPH_URI = nsc['fcg'].metadata
  25. # @TODO This will allow routing triples to certain named graphs depending
  26. # on predicates and types.
  27. term_routes = {
  28. 'p': {
  29. nsc['fcrepo'].contains: META_GRAPH_URI,
  30. nsc['fcrepo'].hasVersion: META_GRAPH_URI,
  31. nsc['fcrepo'].hasVersionLabel: META_GRAPH_URI,
  32. nsc['fcsystem'].fragmentOf: META_GRAPH_URI,
  33. nsc['premis'].hasMessageDigest: META_GRAPH_URI,
  34. },
  35. 't': {
  36. },
  37. }
  38. def extract_imr(self, uri, strict=True, incl_inbound=False,
  39. incl_children=True, embed_children=False, incl_srv_mgd=True):
  40. '''
  41. See base_rdf_layout.extract_imr.
  42. '''
  43. inbound_construct = '\n?s1 ?p1 ?s .' if incl_inbound else ''
  44. inbound_qry = '\nOPTIONAL { ?s1 ?p1 ?s . } .' if incl_inbound else ''
  45. # Include and/or embed children.
  46. embed_children_trp = embed_children_qry = ''
  47. if incl_srv_mgd and incl_children:
  48. incl_children_qry = ''
  49. # Embed children.
  50. if embed_children:
  51. embed_children_trp = '?c ?cp ?co .'
  52. embed_children_qry = '''
  53. OPTIONAL {{
  54. ?s ldp:contains ?c .
  55. {}
  56. }}
  57. '''.format(embed_children_trp)
  58. else:
  59. incl_children_qry = '\nFILTER ( ?p != ldp:contains )' \
  60. q = '''
  61. CONSTRUCT {{
  62. ?s ?p ?o .{inb_cnst}
  63. {embed_chld_t}
  64. ?s fcrepo:writable true .
  65. ?f ?fp ?fo .
  66. }}
  67. WHERE {{
  68. GRAPH ?g {{
  69. ?s ?p ?o .{inb_qry}{incl_chld}{embed_chld}
  70. OPTIONAL {{
  71. ?f fcsystem:fragmentOf ?s ;
  72. ?fp ?fo .
  73. }}
  74. }}
  75. }}
  76. '''.format(inb_cnst=inbound_construct,
  77. inb_qry=inbound_qry, incl_chld=incl_children_qry,
  78. embed_chld_t=embed_children_trp, embed_chld=embed_children_qry)
  79. try:
  80. qres = self._conn.query(q, initBindings={'s': uri})
  81. except ResultException:
  82. # RDFlib bug: https://github.com/RDFLib/rdflib/issues/775
  83. gr = Graph()
  84. else:
  85. gr = qres.graph
  86. #self._logger.debug('Found resource: {}'.format(
  87. # gr.serialize(format='turtle').decode('utf-8')))
  88. if strict and not len(gr):
  89. raise ResourceNotExistsError(uri)
  90. rsrc = Resource(gr, uri)
  91. # Check if resource is a tombstone.
  92. if rsrc[RDF.type : nsc['fcsystem'].Tombstone]:
  93. if strict:
  94. raise TombstoneError(
  95. g.tbox.uri_to_uuid(rsrc.identifier),
  96. rsrc.value(nsc['fcrepo'].created))
  97. else:
  98. self._logger.info('No resource found: {}'.format(uri))
  99. elif rsrc.value(nsc['fcsystem'].tombstone):
  100. if strict:
  101. raise TombstoneError(
  102. g.tbox.uri_to_uuid(
  103. rsrc.value(nsc['fcsystem'].tombstone).identifier),
  104. rsrc.value(nsc['fcrepo'].created))
  105. else:
  106. self._logger.info('Tombstone found: {}'.format(uri))
  107. return rsrc
  108. def ask_rsrc_exists(self, urn):
  109. '''
  110. See base_rdf_layout.ask_rsrc_exists.
  111. '''
  112. self._logger.info('Checking if resource exists: {}'.format(urn))
  113. return bool(self._conn.query(
  114. 'ASK { GRAPH ?g { ?s a fcrepo:Resource . }}', initBindings={
  115. 's': urn, 'g': self.MAIN_GRAPH_URI}))
  116. def get_version_info(self, urn):
  117. '''
  118. See base_rdf_layout.get_version_info.
  119. '''
  120. q = '''
  121. CONSTRUCT {
  122. ?s fcrepo:hasVersion ?v .
  123. ?v ?p ?o .
  124. } WHERE {
  125. GRAPH fcg:metadata {
  126. ?s fcrepo:hasVersion ?v .
  127. ?v ?p ?o .
  128. }
  129. }
  130. '''
  131. try:
  132. rsp = self.ds.query(q, initBindings={'s': urn})
  133. except ResultException:
  134. # RDFlib bug: https://github.com/RDFLib/rdflib/issues/775
  135. rsp = Graph()
  136. if not len(rsp):
  137. raise ResourceNotExistsError(
  138. urn, 'No version found for this resource.')
  139. else:
  140. return rsp.graph
  141. def get_version(self, urn, ver_uid):
  142. '''
  143. See base_rdf_layout.get_version.
  144. '''
  145. q = '''
  146. CONSTRUCT {
  147. ?v ?p ?o .
  148. } WHERE {
  149. GRAPH fcg:metadata {
  150. ?s fcrepo:hasVersion ?v .
  151. ?v fcrepo:hasVersionLabel ?uid .
  152. }
  153. GRAPH fcg:historic {
  154. ?v ?p ?o .
  155. }
  156. }
  157. '''
  158. try:
  159. rsp = self.ds.query(q, initBindings={
  160. 's': urn, 'uid': Literal(ver_uid)})
  161. except ResultException:
  162. # RDFlib bug: https://github.com/RDFLib/rdflib/issues/775
  163. rsp = Graph()
  164. if not len(rsp):
  165. raise ResourceNotExistsError(
  166. urn,
  167. 'No version found for this resource with the given label.')
  168. else:
  169. return rsp.graph
  170. def modify_dataset(self, remove_trp=Graph(), add_trp=Graph(),
  171. types={nsc['fcrepo'].Resource}):
  172. '''
  173. See base_rdf_layout.update_rsrc.
  174. '''
  175. #self._logger.debug('Remove triples: {}'.format(pformat(
  176. # set(remove_trp))))
  177. #self._logger.debug('Add triples: {}'.format(pformat(
  178. # set(add_trp))))
  179. if not types:
  180. # @FIXME This is terrible, but I can't get Fuseki to update the
  181. # default graph without using a variable.
  182. #target_gr = self.ds.graph(self.UNION_GRAPH_URI)
  183. target_gr = {
  184. self.ds.graph(self.HIST_GRAPH_URI),
  185. self.ds.graph(self.META_GRAPH_URI),
  186. self.ds.graph(self.MAIN_GRAPH_URI),
  187. }
  188. elif nsc['fcrepo'].Metadata in types:
  189. target_gr = {self.ds.graph(self.META_GRAPH_URI)}
  190. elif nsc['fcrepo'].Version in types:
  191. target_gr = {self.ds.graph(self.HIST_GRAPH_URI)}
  192. else:
  193. target_gr = {self.ds.graph(self.MAIN_GRAPH_URI)}
  194. for gr in target_gr:
  195. gr -= remove_trp
  196. gr += add_trp
  197. # @TODO Override by triple.
  198. #for t in add_trp:
  199. # # Override target graph by triple.
  200. # if t[1] in self.term_routes['p']:
  201. # trp_target_gr = self.ds.graph(self.term_routes['p'][t[1]])
  202. # elif t[1] == RDF.type and t[2] in self.term_routes['t']:
  203. # trp_target_gr = self.ds.graph(self.term_routes['t'][t[2]])
  204. # else:
  205. # trp_target_gr = target_gr
  206. # trp_target_gr.add(t)