소스 검색

Merge utils in Toolbox class with non-static methods.

Stefano Cossu 6 년 전
부모
커밋
df7faa33e1

+ 5 - 5
lakesuperior/endpoints/ldp.py

@@ -14,7 +14,7 @@ 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.util.translator import Translator
+from lakesuperior.toolbox import Toolbox
 
 
 logger = logging.getLogger(__name__)
@@ -88,7 +88,7 @@ def get_resource(uuid, force_rdf=False):
     out_headers = std_headers
     repr_options = defaultdict(dict)
     if 'prefer' in request.headers:
-        prefer = Translator.parse_rfc7240(request.headers['prefer'])
+        prefer = Toolbox().parse_rfc7240(request.headers['prefer'])
         logger.debug('Parsed Prefer header: {}'.format(prefer))
         if 'return' in prefer:
             repr_options = prefer['return']
@@ -144,7 +144,7 @@ def post_resource(parent):
 
     if cls == LdpNr:
         try:
-            cont_disp = Translator.parse_rfc7240(
+            cont_disp = Toolbox().parse_rfc7240(
                     request.headers['content-disposition'])
         except KeyError:
             cont_disp = None
@@ -180,7 +180,7 @@ def put_resource(uuid):
     if cls == LdpNr:
         try:
             logger.debug('Headers: {}'.format(request.headers))
-            cont_disp = Translator.parse_rfc7240(
+            cont_disp = Toolbox().parse_rfc7240(
                     request.headers['content-disposition'])
         except KeyError:
             cont_disp = None
@@ -195,7 +195,7 @@ def put_resource(uuid):
             return _tombstone_response(e, uuid)
     else:
         if 'prefer' in request.headers:
-            prefer = Translator.parse_rfc7240(request.headers['prefer'])
+            prefer = Toolbox().parse_rfc7240(request.headers['prefer'])
             logger.debug('Parsed Prefer header: {}'.format(prefer))
             if 'handling' in prefer:
                 pref_handling = prefer['handling']['value']

+ 0 - 1
lakesuperior/model/ldp_nr.py

@@ -7,7 +7,6 @@ from lakesuperior.config_parser import config
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.model.ldpr import Ldpr, transactional, must_exist
 from lakesuperior.model.ldp_rs import LdpRs
-from lakesuperior.util.digest import Digest
 
 class LdpNr(Ldpr):
     '''LDP-NR (Non-RDF Source).

+ 3 - 4
lakesuperior/model/ldp_rs.py

@@ -15,8 +15,7 @@ from lakesuperior.dictionaries.srv_mgd_terms import  srv_mgd_subjects, \
 from lakesuperior.model.ldpr import Ldpr, transactional, must_exist
 from lakesuperior.exceptions import ResourceNotExistsError, \
         ServerManagedTermError, SingleSubjectError
-from lakesuperior.util.digest import Digest
-from lakesuperior.util.translator import Translator
+from lakesuperior.toolbox import Toolbox
 
 class LdpRs(Ldpr):
     '''LDP-RS (LDP RDF source).
@@ -39,7 +38,7 @@ class LdpRs(Ldpr):
         '''
         https://www.w3.org/TR/ldp/#ldpr-HTTP_GET
         '''
-        return Translator.globalize_rsrc(self.imr)
+        return Toolbox().globalize_rsrc(self.imr)
 
 
     @transactional
@@ -162,7 +161,7 @@ class LdpRs(Ldpr):
         @param create (boolean) Whether the resource is being created.
         '''
         # Message digest.
-        cksum = Digest.rdf_cksum(self.provided_imr.graph)
+        cksum = Toolbox().rdf_cksum(self.provided_imr.graph)
         self.provided_imr.set(nsc['premis'].hasMessageDigest,
                 URIRef('urn:sha1:{}'.format(cksum)))
 

+ 4 - 4
lakesuperior/model/ldpr.py

@@ -17,7 +17,7 @@ from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.exceptions import InvalidResourceError, \
         ResourceNotExistsError, ServerManagedTermError
 from lakesuperior.store_layouts.rdf.base_rdf_layout import BaseRdfLayout
-from lakesuperior.util.translator import Translator
+from lakesuperior.toolbox import Toolbox
 
 
 def transactional(fn):
@@ -147,7 +147,7 @@ class Ldpr(metaclass=ABCMeta):
 
         @return rdflib.URIRef
         '''
-        return Translator.uuid_to_uri(self.uuid)
+        return Toolbox().uuid_to_uri(self.uuid)
 
 
     @property
@@ -314,7 +314,7 @@ class Ldpr(metaclass=ABCMeta):
         layout_cls = getattr(cls, '{}_store_layout'.format(type))
         store_mod = import_module('lakesuperior.store_layouts.{0}.{1}'.format(
                 type, layout_cls))
-        layout_cls = getattr(store_mod, Translator.camelcase(layout_cls))
+        layout_cls = getattr(store_mod, Toolbox().camelcase(layout_cls))
 
         return layout_cls()
 
@@ -596,7 +596,7 @@ class Ldpr(metaclass=ABCMeta):
         self._logger.debug('Parent predicates: {}'.format(cont_p))
 
         if self.MBR_RSRC_URI in cont_p and self.MBR_REL_URI in cont_p:
-            s = Translator.localize_term(
+            s = Toolbox().localize_term(
                     cont_imr.value(self.MBR_RSRC_URI).identifier)
             p = cont_imr.value(self.MBR_REL_URI).identifier
 

+ 2 - 1
lakesuperior/store_layouts/rdf/base_rdf_layout.py

@@ -12,6 +12,7 @@ from lakesuperior.config_parser import config
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
 from lakesuperior.exceptions import ResourceNotExistsError
+from lakesuperior.toolbox import Toolbox
 
 
 #def needs_rsrc(fn):
@@ -322,7 +323,7 @@ class BaseRdfLayout(metaclass=ABCMeta):
         '''
         Add a message digest to the current resource.
         '''
-        cksum = Digest.rdf_cksum(self.rsrc.graph)
+        cksum = Toolbox().rdf_cksum(self.rsrc.graph)
         self.rsrc.set(nsc['premis'].hasMessageDigest,
                 URIRef('urn:sha1:{}'.format(cksum)))
 

+ 3 - 3
lakesuperior/store_layouts/rdf/simple_layout.py

@@ -15,7 +15,7 @@ from lakesuperior.dictionaries.srv_mgd_terms import  srv_mgd_subjects, \
 from lakesuperior.exceptions import InvalidResourceError, \
         ResourceNotExistsError, TombstoneError
 from lakesuperior.store_layouts.rdf.base_rdf_layout import BaseRdfLayout
-from lakesuperior.util.translator import Translator
+from lakesuperior.toolbox import Toolbox
 
 
 class SimpleLayout(BaseRdfLayout):
@@ -88,12 +88,12 @@ class SimpleLayout(BaseRdfLayout):
         # Check if resource is a tombstone.
         if rsrc[RDF.type : nsc['fcsystem'].Tombstone]:
             raise TombstoneError(
-                    Translator.uri_to_uuid(rsrc.identifier),
+                    Toolbox().uri_to_uuid(rsrc.identifier),
                     rsrc.value(nsc['fcrepo'].created))
         elif rsrc.value(nsc['fcsystem'].tombstone):
             tombstone_rsrc = rsrc.value(nsc['fcsystem'].tombstone)
             raise TombstoneError(
-                    Translator.uri_to_uuid(rsrc.identifier),
+                    Toolbox().uri_to_uuid(rsrc.identifier),
                     tombstone_rsrc.value(nsc['fcrepo'].created))
 
         return rsrc

+ 63 - 38
lakesuperior/util/translator.py → lakesuperior/toolbox.py

@@ -1,29 +1,32 @@
 import logging
+import pickle
 
 from collections import defaultdict
+from hashlib import sha1
 
 from flask import request, g
-from rdflib.term import URIRef
+from rdflib.term import Literal, URIRef, Variable
 
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.store_layouts.rdf.base_rdf_layout import BaseRdfLayout
 
 
-class Translator:
+class Toolbox:
     '''
-    Utility class to perform translations of strings and their wrappers.
-    All static methods.
+    Utility class to translate and generate strings and other objects.
     '''
 
     _logger = logging.getLogger(__name__)
 
-    @staticmethod
-    def base_url():
-        return request.host_url + g.url_prefix
+    def __init__():
+        '''
+        Set the base URL for the requests. This class has to be instantiated
+        within a request context.
+       '''
+        self.base_url = request.host_url + g.url_prefix
 
 
-    @staticmethod
-    def camelcase(word):
+    def camelcase(self, word):
         '''
         Convert a string with underscores with a camel-cased one.
 
@@ -32,17 +35,15 @@ class Translator:
         return ''.join(x.capitalize() or '_' for x in word.split('_'))
 
 
-    @staticmethod
-    def uuid_to_uri(uuid):
+    def uuid_to_uri(self, uuid):
         '''Convert a UUID to a URI.
 
         @return URIRef
         '''
-        return URIRef('{}/{}'.format(Translator.base_url(), uuid))
+        return URIRef('{}/{}'.format(self.base_url, uuid))
 
 
-    @staticmethod
-    def uri_to_uuid(uri):
+    def uri_to_uuid(self, uri):
         '''Convert an absolute URI (internal or external) to a UUID.
 
         @return string
@@ -50,23 +51,21 @@ class Translator:
         if uri.startswith(nsc['fcres']):
             return str(uri).replace(nsc['fcres'], '')
         else:
-            return str(uri).replace(Translator.base_url(), '')
+            return str(uri).replace(self.base_url, '')
 
 
-    @staticmethod
-    def localize_string(s):
+    def localize_string(self, s):
         '''Convert URIs into URNs in a string using the application base URI.
 
         @param string s Input string.
 
         @return string
         '''
-        return s.replace(Translator.base_url()+'/', str(nsc['fcres']))\
-                .replace(Translator.base_url(), str(nsc['fcres']))
+        return s.replace(self.base_url+'/', str(nsc['fcres']))\
+                .replace(self.base_url, str(nsc['fcres']))
 
 
-    @staticmethod
-    def localize_term(uri):
+    def localize_term(self, uri):
         '''
         Convert an URI into an URN.
 
@@ -74,25 +73,24 @@ class Translator:
 
         @return rdflib.term.URIRef
         '''
-        Translator._logger.debug('Input URI: {}'.format(uri))
-        if uri.strip('/') == Translator.base_url():
+        self._logger.debug('Input URI: {}'.format(uri))
+        if uri.strip('/') == self.base_url:
             return BaseRdfLayout.ROOT_NODE_URN
-        return URIRef(Translator.localize_string(str(uri)))
 
+        return URIRef(self.localize_string(str(uri)))
 
-    @staticmethod
-    def globalize_string(s):
+
+    def globalize_string(self, s):
         '''Convert URNs into URIs in a string using the application base URI.
 
         @param string s Input string.
 
         @return string
         '''
-        return s.replace(str(nsc['fcres']), Translator.base_url() + '/')
+        return s.replace(str(nsc['fcres']), self.base_url + '/')
 
 
-    @staticmethod
-    def globalize_term(urn):
+    def globalize_term(self, urn):
         '''
         Convert an URN into an URI using the application base URI.
 
@@ -102,7 +100,8 @@ class Translator:
         '''
         if urn == BaseRdfLayout.ROOT_NODE_URN:
             urn = nsc['fcres']
-        return URIRef(Translator.globalize_string(str(urn)))
+
+        return URIRef(self.globalize_string(str(urn)))
 
 
     @staticmethod
@@ -129,8 +128,8 @@ class Translator:
         flt_g = g.query(q)
 
         for t in flt_g:
-            global_s = Translator.globalize_term(t[0])
-            global_o = Translator.globalize_term(t[2]) \
+            global_s = self.globalize_term(t[0])
+            global_o = self.globalize_term(t[2]) \
                     if isinstance(t[2], URIRef) \
                     else t[2]
             g.remove(t)
@@ -139,22 +138,20 @@ class Translator:
         return g
 
 
-    @staticmethod
-    def globalize_rsrc(rsrc):
+    def globalize_rsrc(self, rsrc):
         '''
         Globalize a resource.
         '''
         g = rsrc.graph
         urn = rsrc.identifier
 
-        global_g = Translator.globalize_graph(g)
-        global_uri = Translator.globalize_term(urn)
+        global_g = self.globalize_graph(g)
+        global_uri = self.globalize_term(urn)
 
         return global_g.resource(global_uri)
 
 
-    @staticmethod
-    def parse_rfc7240(h_str):
+    def parse_rfc7240(self, h_str):
         '''
         Parse `Prefer` header as per https://tools.ietf.org/html/rfc7240
 
@@ -191,3 +188,31 @@ class Translator:
         return parsed_hdr
 
 
+    def rdf_cksum(self, g):
+        '''
+        Generate a checksum for a graph.
+
+        This is not straightforward because a graph is derived from an
+        unordered data structure (RDF).
+
+        What this method does is ordering the graph by subject, predicate,
+        object, then creating a pickle string and a checksum of it.
+
+        N.B. The context of the triples is ignored, so isomorphic graphs would
+        have the same checksum regardless of the context(s) they are found in.
+
+        @TODO This can be later reworked to use a custom hashing algorithm.
+
+        @param rdflib.Graph g The graph to be hashed.
+
+        @return string SHA1 checksum.
+        '''
+        # Remove the messageDigest property, which very likely reflects the
+        # previous state of the resource.
+        g.remove((Variable('s'), nsc['premis'].messageDigest, Variable('o')))
+
+        ord_g = sorted(list(g), key=lambda x : (x[0], x[1], x[2]))
+        hash = sha1(pickle.dumps(ord_g)).hexdigest()
+
+        return hash
+

+ 0 - 0
toolbox/README → util/README


+ 0 - 0
toolbox/bootstrap.py → util/bootstrap.py


+ 0 - 0
toolbox/ingest_random_image.py → util/ingest_random_image.py


+ 0 - 0
toolbox/mkdoc.sh → util/mkdoc.sh