浏览代码

First step toward rearranging LDP and store layout methods.

Stefano Cossu 7 年之前
父节点
当前提交
e6d747d6a6

+ 1 - 2
lakesuperior/endpoints/ldp.py

@@ -13,7 +13,6 @@ from lakesuperior.exceptions import InvalidResourceError, \
 from lakesuperior.model.ldpr import Ldpr
 from lakesuperior.model.ldpr import Ldpr
 from lakesuperior.model.ldp_nr import LdpNr
 from lakesuperior.model.ldp_nr import LdpNr
 from lakesuperior.model.ldp_rs import Ldpc, LdpDc, LdpIc, LdpRs
 from lakesuperior.model.ldp_rs import Ldpc, LdpDc, LdpIc, LdpRs
-from lakesuperior.store_layouts.rdf.base_rdf_layout import BaseRdfLayout
 from lakesuperior.toolbox import Toolbox
 from lakesuperior.toolbox import Toolbox
 
 
 
 
@@ -211,7 +210,7 @@ def put_resource(uuid):
         except ServerManagedTermError as e:
         except ServerManagedTermError as e:
             return str(e), 412
             return str(e), 412
 
 
-    res_code = 201 if ret == BaseRdfLayout.RES_CREATED else 204
+    res_code = 201 if ret == Ldpr.RES_CREATED else 204
     return '', res_code, rsp_headers
     return '', res_code, rsp_headers
 
 
 
 

+ 1 - 1
lakesuperior/model/ldp_nr.py

@@ -68,7 +68,7 @@ class LdpNr(Ldpr):
         self._logger.debug('Persisting LDP-NR triples in {}'.format(
         self._logger.debug('Persisting LDP-NR triples in {}'.format(
             self.urn))
             self.urn))
         try:
         try:
-            rsrc = self.rdfly.create_rsrc(self.imr)
+            rsrc = self._create_rsrc(self.imr)
         except:
         except:
             self.nonrdfly.delete(file_uuid)
             self.nonrdfly.delete(file_uuid)
         else:
         else:

+ 14 - 10
lakesuperior/model/ldp_rs.py

@@ -98,20 +98,25 @@ class LdpRs(Ldpr):
 
 
         self.provided_imr = Resource(self._check_mgd_terms(g, handling),
         self.provided_imr = Resource(self._check_mgd_terms(g, handling),
                 self.urn)
                 self.urn)
-        self._add_srv_mgd_triples(create=True)
+
+        create = create_only or not self.is_stored
+        self._add_srv_mgd_triples(create)
         self._ensure_single_subject_rdf(self.provided_imr.graph)
         self._ensure_single_subject_rdf(self.provided_imr.graph)
-        cnf = self.rdfly.conf['referential_integrity']
-        if cnf != 'none':
-            self._check_ref_int(cnf)
+        ref_int = self.rdfly.conf['referential_integrity']
+        if ref_int:
+            self._check_ref_int(ref_int)
 
 
-        if create_only:
-            res = self.rdfly.create_rsrc(self.provided_imr)
+        if create:
+            ev_type = self._create_rsrc()
         else:
         else:
-            res = self.rdfly.create_or_replace_rsrc(self.provided_imr)
+            ev_type = self._replace_rsrc()
 
 
         self._set_containment_rel()
         self._set_containment_rel()
 
 
-        return res
+        return ev_type
+
+
+    ## PROTECTED METHODS ##
 
 
 
 
     def _check_mgd_terms(self, g, handling='strict'):
     def _check_mgd_terms(self, g, handling='strict'):
@@ -156,7 +161,7 @@ class LdpRs(Ldpr):
 
 
     def _add_srv_mgd_triples(self, create=False):
     def _add_srv_mgd_triples(self, create=False):
         '''
         '''
-        Add server-managed triples to a resource.
+        Add server-managed triples to a provided IMR.
 
 
         @param create (boolean) Whether the resource is being created.
         @param create (boolean) Whether the resource is being created.
         '''
         '''
@@ -166,7 +171,6 @@ class LdpRs(Ldpr):
                 URIRef('urn:sha1:{}'.format(cksum)))
                 URIRef('urn:sha1:{}'.format(cksum)))
 
 
         # Create and modify timestamp.
         # Create and modify timestamp.
-        # @TODO Use gunicorn to get request timestamp.
         ts = Literal(arrow.utcnow(), datatype=XSD.dateTime)
         ts = Literal(arrow.utcnow(), datatype=XSD.dateTime)
         if create:
         if create:
             self.provided_imr.set(nsc['fcrepo'].created, ts)
             self.provided_imr.set(nsc['fcrepo'].created, ts)

+ 69 - 97
lakesuperior/model/ldpr.py

@@ -106,6 +106,16 @@ class Ldpr(metaclass=ABCMeta):
     RETURN_SRV_MGD_RES_URI = nsc['fcrepo'].ServerManaged
     RETURN_SRV_MGD_RES_URI = nsc['fcrepo'].ServerManaged
     ROOT_NODE_URN = nsc['fcsystem'].root
     ROOT_NODE_URN = nsc['fcsystem'].root
 
 
+    RES_CREATED = 'Create'
+    RES_DELETED = 'Delete'
+    RES_UPDATED = 'Update'
+
+    protected_pred = (
+        nsc['fcrepo'].created,
+        nsc['fcrepo'].createdBy,
+        nsc['ldp'].contains,
+    )
+
     _logger = logging.getLogger(__name__)
     _logger = logging.getLogger(__name__)
 
 
 
 
@@ -127,35 +137,13 @@ class Ldpr(metaclass=ABCMeta):
                 current_app.config['store']['ldp_nr']['layout']
                 current_app.config['store']['ldp_nr']['layout']
 
 
         self.uuid = uuid
         self.uuid = uuid
-
-        self._urn = nsc['fcres'][uuid] if self.uuid is not None \
+        self.urn = nsc['fcres'][uuid] if self.uuid is not None \
                 else self.ROOT_NODE_URN
                 else self.ROOT_NODE_URN
+        self.uri = Toolbox().uuid_to_uri(self.uuid)
 
 
         self._imr_options = __class__.set_imr_options(repr_opts)
         self._imr_options = __class__.set_imr_options(repr_opts)
 
 
 
 
-    @property
-    def urn(self):
-        '''
-        The internal URI (URN) for the resource as stored in the triplestore.
-
-        This is a URN that needs to be converted to a global URI for the LDP
-        API.
-
-        @return rdflib.URIRef
-        '''
-        return self._urn
-
-    @property
-    def uri(self):
-        '''
-        The URI for the resource as published by the REST API.
-
-        @return rdflib.URIRef
-        '''
-        return Toolbox().uuid_to_uri(self.uuid)
-
-
     @property
     @property
     def rdfly(self):
     def rdfly(self):
         '''
         '''
@@ -176,7 +164,7 @@ class Ldpr(metaclass=ABCMeta):
         @return rdflib.resource.Resource
         @return rdflib.resource.Resource
         '''
         '''
         if not hasattr(self, '_rsrc'):
         if not hasattr(self, '_rsrc'):
-            self._rsrc = self.rdfly.rsrc(self.urn)
+            self._rsrc = self.rdfly.ds.resource(self.urn)
 
 
         return self._rsrc
         return self._rsrc
 
 
@@ -199,32 +187,6 @@ class Ldpr(metaclass=ABCMeta):
         return self._imr
         return self._imr
 
 
 
 
-    @property
-    def out_graph(self):
-        '''
-        Retun a globalized graph of the resource's IMR.
-
-        Internal URNs are replaced by global URIs using the endpoint webroot.
-        '''
-        # Remove digest hash.
-        self.imr.remove(nsc['premis'].hasMessageDigest)
-
-        if not self._imr_options.setdefault('incl_srv_mgd', False):
-            for p in srv_mgd_predicates:
-                self._logger.debug('Removing predicate: {}'.format(p))
-                self.imr.remove(p)
-            for t in srv_mgd_types:
-                self._logger.debug('Removing type: {}'.format(t))
-                self.imr.remove(RDF.type, t)
-
-        out_g = Toolbox().globalize_graph(self.imr.graph)
-        # Clear IMR because it's been pruned. In the rare case it is needed
-        # after this method, it will be retrieved again.
-        delattr(self, 'imr')
-
-        return out_g
-
-
     @property
     @property
     def stored_or_new_imr(self):
     def stored_or_new_imr(self):
         '''
         '''
@@ -255,6 +217,32 @@ class Ldpr(metaclass=ABCMeta):
         delattr(self, '_imr')
         delattr(self, '_imr')
 
 
 
 
+    @property
+    def out_graph(self):
+        '''
+        Retun a globalized graph of the resource's IMR.
+
+        Internal URNs are replaced by global URIs using the endpoint webroot.
+        '''
+        # Remove digest hash.
+        self.imr.remove(nsc['premis'].hasMessageDigest)
+
+        if not self._imr_options.setdefault('incl_srv_mgd', False):
+            for p in srv_mgd_predicates:
+                self._logger.debug('Removing predicate: {}'.format(p))
+                self.imr.remove(p)
+            for t in srv_mgd_types:
+                self._logger.debug('Removing type: {}'.format(t))
+                self.imr.remove(RDF.type, t)
+
+        out_g = Toolbox().globalize_graph(self.imr.graph)
+        # Clear IMR because it's been pruned. In the rare case it is needed
+        # after this method, it will be retrieved again.
+        delattr(self, 'imr')
+
+        return out_g
+
+
     @property
     @property
     def is_stored(self):
     def is_stored(self):
         return self.rdfly.ask_rsrc_exists(self.urn)
         return self.rdfly.ask_rsrc_exists(self.urn)
@@ -267,7 +255,7 @@ class Ldpr(metaclass=ABCMeta):
         @return set(rdflib.term.URIRef)
         @return set(rdflib.term.URIRef)
         '''
         '''
         if not hasattr(self, '_types'):
         if not hasattr(self, '_types'):
-            self._types = set(self.rsrc[RDF.type])
+            self._types = set(self.imr[RDF.type])
 
 
         return self._types
         return self._types
 
 
@@ -287,51 +275,6 @@ class Ldpr(metaclass=ABCMeta):
         return self._ldp_types
         return self._ldp_types
 
 
 
 
-    @property
-    def containment(self):
-        if not hasattr(self, '_containment'):
-            q = '''
-            SELECT ?container ?contained {
-              {
-                ?s ldp:contains ?contained .
-              } UNION {
-                ?container ldp:contains ?s .
-              }
-            }
-            '''
-            qres = self.rsrc.graph.query(q, initBindings={'s' : self.urn})
-
-            # There should only be one container.
-            for t in qres:
-                if t[0]:
-                    container = self.rdfly.ds.resource(t[0])
-
-            contains = ( self.rdfly.ds.resource(t[1]) for t in qres if t[1] )
-
-            self._containment = {
-                    'container' : container, 'contains' : contains}
-
-        return self._containment
-
-
-    @containment.deleter
-    def containment(self):
-        '''
-        Reset containment variable when changing containment triples.
-        '''
-        del self._containment
-
-
-    @property
-    def container(self):
-        return self.containment['container']
-
-
-    @property
-    def contains(self):
-        return self.containment['contains']
-
-
     ## STATIC & CLASS METHODS ##
     ## STATIC & CLASS METHODS ##
 
 
     @classmethod
     @classmethod
@@ -541,6 +484,35 @@ class Ldpr(metaclass=ABCMeta):
 
 
     ## PROTECTED METHODS ##
     ## PROTECTED METHODS ##
 
 
+    def _create_rsrc(self):
+        '''
+        Create a new resource by comparing an empty graph with the provided
+        IMR graph.
+        '''
+        self.rdfly.modify_dataset(Graph(), self.provided_imr.graph)
+
+        return self.RES_CREATED
+
+
+    def _replace_rsrc(self):
+        '''
+        Replace a resource.
+
+        The existing resource graph is removed except for the protected terms.
+        '''
+        # The extracted IMR is used as a "minus" delta, so protected predicates
+        # must be removed.
+        for p in self.protected_pred:
+            self.imr.remove(p)
+
+        self.rdfly.modify_dataset(self.imr.graph, self.provided_imr.graph)
+
+        # Reset the IMR because it has changed.
+        delattr(self, 'imr')
+
+        return self.RES_CREATED
+
+
     def _set_containment_rel(self):
     def _set_containment_rel(self):
         '''Find the closest parent in the path indicated by the UUID and
         '''Find the closest parent in the path indicated by the UUID and
         establish a containment triple.
         establish a containment triple.

+ 13 - 48
lakesuperior/store_layouts/rdf/base_rdf_layout.py

@@ -52,10 +52,6 @@ class BaseRdfLayout(metaclass=ABCMeta):
     # N.B. This is Fuseki-specific.
     # N.B. This is Fuseki-specific.
     UNION_GRAPH_URI = URIRef('urn:x-arq:UnionGraph')
     UNION_GRAPH_URI = URIRef('urn:x-arq:UnionGraph')
 
 
-    RES_CREATED = 'Create'
-    RES_UPDATED = 'Update'
-    RES_DELETED = 'Delete'
-
     _logger = logging.getLogger(__name__)
     _logger = logging.getLogger(__name__)
 
 
 
 
@@ -95,30 +91,8 @@ class BaseRdfLayout(metaclass=ABCMeta):
         return self._ds
         return self._ds
 
 
 
 
-    @property
-    def protected_pred(self):
-        '''
-        Predicated that are not deleted from an existing resources when it is
-        replaced, e.g. by a PUT operation.
-        '''
-        return {
-            nsc['fcrepo'].created,
-            nsc['fcrepo'].createdBy,
-            nsc['ldp'].contains,
-        }
-
-
     ## PUBLIC METHODS ##
     ## PUBLIC METHODS ##
 
 
-    def rsrc(self, urn):
-        '''
-        Reference to a live data set that can be updated. This exposes the
-        whole underlying triplestore structure and is used to update a
-        resource.
-        '''
-        return self.ds.resource(urn)
-
-
     def create_or_replace_rsrc(self, imr):
     def create_or_replace_rsrc(self, imr):
         '''Create a resource graph in the main graph if it does not exist.
         '''Create a resource graph in the main graph if it does not exist.
 
 
@@ -132,16 +106,16 @@ class BaseRdfLayout(metaclass=ABCMeta):
         else:
         else:
             ev_type = self.create_rsrc(imr)
             ev_type = self.create_rsrc(imr)
 
 
-        self._msg.send(
-            imr.identifier,
-            ev_type,
-            time=imr.value(nsc['fcrepo'].lastModified),
-            type=list(imr.graph.objects(imr.identifier, RDF.type)),
-            data=imr.graph,
-            metadata={
-                'actor' : imr.value(nsc['fcrepo'].lastModifiedBy),
-            }
-        )
+        #self._msg.send(
+        #    imr.identifier,
+        #    ev_type,
+        #    time=imr.value(nsc['fcrepo'].lastModified),
+        #    type=list(imr.graph.objects(imr.identifier, RDF.type)),
+        #    data=imr.graph,
+        #    metadata={
+        #        'actor' : imr.value(nsc['fcrepo'].lastModifiedBy),
+        #    }
+        #)
 
 
         return ev_type
         return ev_type
 
 
@@ -158,7 +132,7 @@ class BaseRdfLayout(metaclass=ABCMeta):
         '''
         '''
         inbound = inbound if self.conf['referential_integrity'] == 'none' \
         inbound = inbound if self.conf['referential_integrity'] == 'none' \
                 else True
                 else True
-        rsrc = self.rsrc(urn)
+        rsrc = self.ds.resource(urn)
         children = rsrc[nsc['ldp'].contains * '+'] if delete_children else []
         children = rsrc[nsc['ldp'].contains * '+'] if delete_children else []
 
 
         self._do_delete_rsrc(rsrc, inbound)
         self._do_delete_rsrc(rsrc, inbound)
@@ -276,6 +250,8 @@ class BaseRdfLayout(metaclass=ABCMeta):
 
 
         NOTE: This method should NOT indiscriminately wipe all triples about
         NOTE: This method should NOT indiscriminately wipe all triples about
         the subject. Some other metadata may be left for some good reason.
         the subject. Some other metadata may be left for some good reason.
+
+        NOTE: This operation does not emit a message.
         '''
         '''
         pass
         pass
 
 
@@ -290,14 +266,3 @@ class BaseRdfLayout(metaclass=ABCMeta):
         '''
         '''
         pass
         pass
 
 
-
-    ## PROTECTED METHODS  ##
-
-    def _set_msg_digest(self):
-        '''
-        Add a message digest to the current resource.
-        '''
-        cksum = Toolbox().rdf_cksum(self.rsrc.graph)
-        self.rsrc.set(nsc['premis'].hasMessageDigest,
-                URIRef('urn:sha1:{}'.format(cksum)))
-

+ 15 - 16
lakesuperior/store_layouts/rdf/simple_layout.py

@@ -35,10 +35,8 @@ class SimpleLayout(BaseRdfLayout):
         '''
         '''
         See base_rdf_layout.extract_imr.
         See base_rdf_layout.extract_imr.
         '''
         '''
-        inbound_construct = '\n?s1 ?p1 {} .'.format(uri.n3()) \
-                if incl_inbound else ''
-        inbound_qry = '\nOPTIONAL {{ ?s1 ?p1 {} . }} .'.format(uri.n3()) \
-                if incl_inbound else ''
+        inbound_construct = '\n?s1 ?p1 ?s .' if incl_inbound else ''
+        inbound_qry = '\nOPTIONAL { ?s1 ?p1 ?s . } .' if incl_inbound else ''
 
 
         # Include and/or embed children.
         # Include and/or embed children.
         embed_children_trp = embed_children_qry = ''
         embed_children_trp = embed_children_qry = ''
@@ -50,26 +48,26 @@ class SimpleLayout(BaseRdfLayout):
                 embed_children_trp = '?c ?cp ?co .'
                 embed_children_trp = '?c ?cp ?co .'
                 embed_children_qry = '''
                 embed_children_qry = '''
                 OPTIONAL {{
                 OPTIONAL {{
-                  {0} ldp:contains ?c .
-                  {1}
+                  ?s ldp:contains ?c .
+                  {}
                 }}
                 }}
-                '''.format(uri.n3(), embed_children_trp)
+                '''.format(embed_children_trp)
         else:
         else:
             incl_children_qry = '\nFILTER ( ?p != ldp:contains )' \
             incl_children_qry = '\nFILTER ( ?p != ldp:contains )' \
 
 
         q = '''
         q = '''
         CONSTRUCT {{
         CONSTRUCT {{
-            {uri} ?p ?o .{inb_cnst}
+            ?s ?p ?o .{inb_cnst}
             {embed_chld_t}
             {embed_chld_t}
         }} WHERE {{
         }} WHERE {{
-            {uri} ?p ?o .{inb_qry}{incl_chld}{embed_chld}
+            ?s ?p ?o .{inb_qry}{incl_chld}{embed_chld}
         }}
         }}
-        '''.format(uri=uri.n3(), inb_cnst=inbound_construct,
+        '''.format(inb_cnst=inbound_construct,
                 inb_qry=inbound_qry, incl_chld=incl_children_qry,
                 inb_qry=inbound_qry, incl_chld=incl_children_qry,
                 embed_chld_t=embed_children_trp, embed_chld=embed_children_qry)
                 embed_chld_t=embed_children_trp, embed_chld=embed_children_qry)
 
 
         try:
         try:
-            qres = self._conn.query(q)
+            qres = self._conn.query(q, initBindings={'s' : uri})
         except ResultException:
         except ResultException:
             # RDFlib bug: https://github.com/RDFLib/rdflib/issues/775
             # RDFlib bug: https://github.com/RDFLib/rdflib/issues/775
             g = Graph()
             g = Graph()
@@ -103,7 +101,8 @@ class SimpleLayout(BaseRdfLayout):
         '''
         '''
         self._logger.info('Checking if resource exists: {}'.format(urn))
         self._logger.info('Checking if resource exists: {}'.format(urn))
 
 
-        return self._conn.query('ASK {{ {} ?p ?o . }}'.format(urn.n3()))
+        return self._conn.query('ASK { ?s ?p ?o . }', initBindings={
+            's' : urn})
 
 
 
 
     def create_rsrc(self, imr):
     def create_rsrc(self, imr):
@@ -146,10 +145,10 @@ class SimpleLayout(BaseRdfLayout):
         '''
         '''
         See base_rdf_layout.update_rsrc.
         See base_rdf_layout.update_rsrc.
         '''
         '''
-        #self._logger.debug('Remove triples: {}'.format(
-        #        remove_trp.serialize(format='turtle').decode('utf-8')))
-        #self._logger.debug('Add triples: {}'.format(
-        #        add_trp.serialize(format='turtle').decode('utf-8')))
+        self._logger.debug('Remove triples: {}'.format(
+                remove_trp.serialize(format='turtle').decode('utf-8')))
+        self._logger.debug('Add triples: {}'.format(
+                add_trp.serialize(format='turtle').decode('utf-8')))
 
 
         for t in remove_trp:
         for t in remove_trp:
             self.ds.remove(t)
             self.ds.remove(t)