Переглянути джерело

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.ldp_nr import LdpNr
 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
 
 
@@ -211,7 +210,7 @@ def put_resource(uuid):
         except ServerManagedTermError as e:
             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
 
 

+ 1 - 1
lakesuperior/model/ldp_nr.py

@@ -68,7 +68,7 @@ class LdpNr(Ldpr):
         self._logger.debug('Persisting LDP-NR triples in {}'.format(
             self.urn))
         try:
-            rsrc = self.rdfly.create_rsrc(self.imr)
+            rsrc = self._create_rsrc(self.imr)
         except:
             self.nonrdfly.delete(file_uuid)
         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.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)
-        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:
-            res = self.rdfly.create_or_replace_rsrc(self.provided_imr)
+            ev_type = self._replace_rsrc()
 
         self._set_containment_rel()
 
-        return res
+        return ev_type
+
+
+    ## PROTECTED METHODS ##
 
 
     def _check_mgd_terms(self, g, handling='strict'):
@@ -156,7 +161,7 @@ class LdpRs(Ldpr):
 
     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.
         '''
@@ -166,7 +171,6 @@ class LdpRs(Ldpr):
                 URIRef('urn:sha1:{}'.format(cksum)))
 
         # Create and modify timestamp.
-        # @TODO Use gunicorn to get request timestamp.
         ts = Literal(arrow.utcnow(), datatype=XSD.dateTime)
         if create:
             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
     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__)
 
 
@@ -127,35 +137,13 @@ class Ldpr(metaclass=ABCMeta):
                 current_app.config['store']['ldp_nr']['layout']
 
         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
+        self.uri = Toolbox().uuid_to_uri(self.uuid)
 
         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
     def rdfly(self):
         '''
@@ -176,7 +164,7 @@ class Ldpr(metaclass=ABCMeta):
         @return rdflib.resource.Resource
         '''
         if not hasattr(self, '_rsrc'):
-            self._rsrc = self.rdfly.rsrc(self.urn)
+            self._rsrc = self.rdfly.ds.resource(self.urn)
 
         return self._rsrc
 
@@ -199,32 +187,6 @@ class Ldpr(metaclass=ABCMeta):
         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
     def stored_or_new_imr(self):
         '''
@@ -255,6 +217,32 @@ class Ldpr(metaclass=ABCMeta):
         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
     def is_stored(self):
         return self.rdfly.ask_rsrc_exists(self.urn)
@@ -267,7 +255,7 @@ class Ldpr(metaclass=ABCMeta):
         @return set(rdflib.term.URIRef)
         '''
         if not hasattr(self, '_types'):
-            self._types = set(self.rsrc[RDF.type])
+            self._types = set(self.imr[RDF.type])
 
         return self._types
 
@@ -287,51 +275,6 @@ class Ldpr(metaclass=ABCMeta):
         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 ##
 
     @classmethod
@@ -541,6 +484,35 @@ class Ldpr(metaclass=ABCMeta):
 
     ## 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):
         '''Find the closest parent in the path indicated by the UUID and
         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.
     UNION_GRAPH_URI = URIRef('urn:x-arq:UnionGraph')
 
-    RES_CREATED = 'Create'
-    RES_UPDATED = 'Update'
-    RES_DELETED = 'Delete'
-
     _logger = logging.getLogger(__name__)
 
 
@@ -95,30 +91,8 @@ class BaseRdfLayout(metaclass=ABCMeta):
         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 ##
 
-    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):
         '''Create a resource graph in the main graph if it does not exist.
 
@@ -132,16 +106,16 @@ class BaseRdfLayout(metaclass=ABCMeta):
         else:
             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
 
@@ -158,7 +132,7 @@ class BaseRdfLayout(metaclass=ABCMeta):
         '''
         inbound = inbound if self.conf['referential_integrity'] == 'none' \
                 else True
-        rsrc = self.rsrc(urn)
+        rsrc = self.ds.resource(urn)
         children = rsrc[nsc['ldp'].contains * '+'] if delete_children else []
 
         self._do_delete_rsrc(rsrc, inbound)
@@ -276,6 +250,8 @@ class BaseRdfLayout(metaclass=ABCMeta):
 
         NOTE: This method should NOT indiscriminately wipe all triples about
         the subject. Some other metadata may be left for some good reason.
+
+        NOTE: This operation does not emit a message.
         '''
         pass
 
@@ -290,14 +266,3 @@ class BaseRdfLayout(metaclass=ABCMeta):
         '''
         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.
         '''
-        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.
         embed_children_trp = embed_children_qry = ''
@@ -50,26 +48,26 @@ class SimpleLayout(BaseRdfLayout):
                 embed_children_trp = '?c ?cp ?co .'
                 embed_children_qry = '''
                 OPTIONAL {{
-                  {0} ldp:contains ?c .
-                  {1}
+                  ?s ldp:contains ?c .
+                  {}
                 }}
-                '''.format(uri.n3(), embed_children_trp)
+                '''.format(embed_children_trp)
         else:
             incl_children_qry = '\nFILTER ( ?p != ldp:contains )' \
 
         q = '''
         CONSTRUCT {{
-            {uri} ?p ?o .{inb_cnst}
+            ?s ?p ?o .{inb_cnst}
             {embed_chld_t}
         }} 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,
                 embed_chld_t=embed_children_trp, embed_chld=embed_children_qry)
 
         try:
-            qres = self._conn.query(q)
+            qres = self._conn.query(q, initBindings={'s' : uri})
         except ResultException:
             # RDFlib bug: https://github.com/RDFLib/rdflib/issues/775
             g = Graph()
@@ -103,7 +101,8 @@ class SimpleLayout(BaseRdfLayout):
         '''
         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):
@@ -146,10 +145,10 @@ class SimpleLayout(BaseRdfLayout):
         '''
         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:
             self.ds.remove(t)