full_provenance_layout.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import arrow
  2. from uuid import uuid4
  3. from rdflib import Dataset, Graph
  4. from rdflib.namespace import FOAF, RDF, XSD
  5. from rdflib.plugins.sparql import prepareQuery
  6. from rdflib.plugins.stores.sparqlstore import SPARQLUpdateStore
  7. from rdflib.term import URIRef, Literal
  8. from lakesuperior.dictionaries.namespaces import ns_collection as nsc
  9. from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
  10. from lakesuperior.store_layouts.rdf.base_rdf_layout import BaseRdfLayout
  11. class FullProvenanceLayout(BaseRdfLayout):
  12. '''This is an implementation of the
  13. [graph-per-resource pattern](http://patterns.dataincubator.org/book/graph-per-resource.html)
  14. which stores each LDP resource in a separate graph, with a "main" graph
  15. to keep track of resource metadata.
  16. '''
  17. DEFAULT_AGENT_URI = nsc['lake'].defaultAgent
  18. MAIN_GRAPH_URI = nsc['fcg'].meta
  19. ## MAGIC METHODS ##
  20. def __init__(self):
  21. self.main_graph = self.ds.graph(self.MAIN_GRAPH_URI)
  22. ## PUBLIC METHODS ##
  23. def ask_rsrc_exists(self, uuid):
  24. '''Return whether the resource exists.
  25. @param uuid Resource UUID.
  26. @retrn boolean
  27. '''
  28. res = self.ds.graph(self.UNION_GRAPH_URI).resource(nsc['fcres'][uuid])
  29. return len(res) > 0
  30. def get_rsrc(self, uuid):
  31. '''Get a resource graph.
  32. '''
  33. res = self.ds.graph(self.UNION_GRAPH_URI).query(
  34. 'CONSTRUCT WHERE { ?s ?p ?o }',
  35. initBindings={'s' : nsc['fcres'][uuid]}
  36. )
  37. return self.globalize_graph(res.graph)
  38. def put_rsrc(self, uuid, data, format='text/turtle', base_types=None,
  39. agent=None):
  40. '''Create a resource graph.
  41. If the resource UUID exists already, it is either overwritten or a
  42. version snapshot is created, depending on the parameters.
  43. '''
  44. if agent is None:
  45. agent = self.DEFAULT_AGENT_URI
  46. res_urn = nsc['fcres'][uuid]
  47. # If there is a statement by this agent about this resource, replace
  48. # its contents.
  49. if self._get_res_stmt_by_agent(res_urn, agent):
  50. pass # @TODO
  51. # If the graph URI does not exist, create a new resource.
  52. else:
  53. # Create a new UUID for the statement set.
  54. stmset_uri = nsc['stmset'][str(uuid4())]
  55. # Create a temp graph to store the loaded data. For some reason,
  56. # loading directly into the stored graph throws an assertion error.
  57. tmp_g = Graph()
  58. tmp_g.parse(data=data.decode('utf-8'), format=format,
  59. publicID=str(res_urn))
  60. # Create the graph and add the data.
  61. g = self.ds.graph(stmset_uri)
  62. g += tmp_g
  63. # Add metadata.
  64. ts = arrow.utcnow()
  65. main_graph = self.ds.graph(self.MAIN_GRAPH_URI)
  66. main_graph.add((stmset_uri, FOAF.primaryTopic, res_urn))
  67. main_graph.add((stmset_uri, RDF.type, nsc['prov'].Entity))
  68. main_graph.add(
  69. (stmset_uri, nsc['prov'].generatedAtTime,
  70. Literal(ts, datatype=XSD.dateTime)))
  71. main_graph.add(
  72. (stmset_uri, nsc['prov'].wasAttributedTo, agent))
  73. #self.create_version(res_urn)
  74. if base_types:
  75. for type_uri in self.base_types:
  76. main_graph.add((stmset_uri, RDF.type, type_uri))
  77. # @TODO Create containment triples
  78. self.conn.store.commit()
  79. #def create_version(self, res_urn):
  80. # '''Swap out previous version if existing, and create new version
  81. # dependency.'''
  82. # main_graph = ds.graph(URIRef('urn:lake:' + self.MAIN_GRAPH_NAME))
  83. # prv_res_urn = self.select_current_graph_for_res(res_urn)
  84. # if prv_res_urn:
  85. # main_graph.remove((prv_res_urn, RDF.type, nsc['lake'].Resource))
  86. # main_graph.add((prv_res_urn, RDF.type, nsc['lake'].Snapshot))
  87. # main_graph.add((res_urn, RDF.type, nsc['lake'].Resource))
  88. # main_graph.add((res_urn, nsc['lake'].previousVersion, prv_res_urn))
  89. #def select_current_graph_for_res(self, urn):
  90. # '''Select the current graph URI for a given resource.'''
  91. # qry = '''
  92. # SELECT ?g {
  93. # GRAPH ?mg { ?g a ?gt . }
  94. # GRAPH ?g { ?s ?p ?o . }
  95. # }
  96. # LIMIT 1
  97. # '''
  98. # rsp = self.ds.query(qry, initBindings={
  99. # 'mg' : URIRef('urn:lake:' + self.MAIN_GRAPH_NAME),
  100. # 'gt' : RESOURCE_TYPE_URI,
  101. # 's' : urn
  102. # })
  103. # return list(rsp[0][0])
  104. def _ask_res_stmt_by_agent_exists(self, res_urn, agent):
  105. '''Ask if any statements have been made by a certain agent about a
  106. certain resource.
  107. @param rdflib.term.URIRef res_urn Resource URN.
  108. @param rdflib.term.URIRef agent Agent URI.
  109. @return boolean
  110. '''
  111. return self.query('''
  112. ASK {
  113. GRAPH ?mg {
  114. ?g prov:wasAttributedTo ?a .
  115. }
  116. GRAPH ?g {
  117. ?s ?p ?o .
  118. }
  119. }
  120. ''', initBindings={
  121. 'a' : agent,
  122. 's' : res_urn,
  123. })