123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- from abc import ABCMeta
- from importlib import import_module
- import arrow
- from rdflib import Graph
- from rdflib.namespace import XSD
- from rdflib.term import Literal
- from lakesuperior.config_parser import config
- from lakesuperior.connectors.filesystem_connector import FilesystemConnector
- from lakesuperior.core.namespaces import ns_collection as nsc
- class Ldpr(metaclass=ABCMeta):
- '''LDPR (LDP Resource).
- Definition: https://www.w3.org/TR/ldp/#ldpr-resource
- This class and related subclasses contain the implementation pieces of
- the vanilla LDP specifications. This is extended by the
- `lakesuperior.fcrepo.Resource` class.
- Inheritance graph: https://www.w3.org/TR/ldp/#fig-ldpc-types
- Note: Even though LdpNr (which is a subclass of Ldpr) handles binary files,
- it still has an RDF representation in the triplestore. Hence, some of the
- RDF-related methods are defined in this class rather than in the LdpRs
- class.
- Convention notes:
- All the methods in this class handle internal UUIDs (URN). Public-facing
- URIs are converted from URNs and passed by these methods to the methods
- handling HTTP negotiation.
- The data passed to the store strategy for processing should be in a graph.
- All conversion from request payload strings is done here.
- '''
- LDP_NR_TYPE = nsc['ldp'].NonRDFSource
- LDP_RS_TYPE = nsc['ldp'].RDFSource
- store_strategy = config['application']['store']['ldp_rs']['strategy']
- ## MAGIC METHODS ##
- def __init__(self, uuid):
- '''Instantiate an in-memory LDP resource that can be loaded from and
- persisted to storage.
- @param uuid (string) UUID of the resource.
- '''
- # Dynamically load the store strategy indicated in the configuration.
- store_mod = import_module(
- 'lakesuperior.store_strategies.rdf.{}'.format(
- self.store_strategy))
- store_cls = getattr(store_mod, self._camelcase(self.store_strategy))
- self.gs = store_cls()
- # Same thing coud be done for the filesystem store strategy, but we
- # will keep it simple for now.
- self.fs = FilesystemConnector()
- self.uuid = uuid
- @property
- def urn(self):
- return nsc['fcres'][self.uuid]
- @property
- def uri(self):
- return self.gs.uuid_to_uri(self.uuid)
- @property
- def types(self):
- '''The LDP types.
- @return tuple(rdflib.term.URIRef)
- '''
- return self.gs.list_types(self.uuid)
- ## LDP METHODS ##
- def get(self):
- '''
- https://www.w3.org/TR/ldp/#ldpr-HTTP_GET
- '''
- ret = self.gs.get_rsrc(self.uuid)
- return ret
- def post(self, data, format='text/turtle'):
- '''
- https://www.w3.org/TR/ldp/#ldpr-HTTP_POST
- '''
- # @TODO Use gunicorn to get request timestamp.
- ts = Literal(arrow.utcnow(), datatype=XSD.dateTime)
- g = Graph()
- g.parse(data=data, format=format, publicID=self.urn)
- data.add((self.urn, nsc['fedora'].lastUpdated, ts))
- data.add((self.urn, nsc['fedora'].lastUpdatedBy,
- Literal('BypassAdmin')))
- self.gs.create_rsrc(self.urn, g, ts)
- self.gs.conn.store.commit()
- def put(self, data, format='text/turtle'):
- '''
- https://www.w3.org/TR/ldp/#ldpr-HTTP_PUT
- '''
- # @TODO Use gunicorn to get request timestamp.
- ts = Literal(arrow.utcnow(), datatype=XSD.dateTime)
- g = Graph()
- g.parse(data=data, format=format, publicID=self.urn)
- g.add((self.urn, nsc['fedora'].lastUpdated, ts))
- g.add((self.urn, nsc['fedora'].lastUpdatedBy,
- Literal('BypassAdmin')))
- self.gs.create_or_replace_rsrc(self.urn, g, ts)
- self.gs.conn.store.commit()
- def delete(self):
- '''
- https://www.w3.org/TR/ldp/#ldpr-HTTP_DELETE
- '''
- self.gs.delete_rsrc(self.urn, commit=True)
- ## PROTECTED METHODS ##
- def _create_containment_rel(self):
- '''Find the closest parent in the path indicated by the UUID and
- establish a containment triple.
- E.g. If ONLY urn:res:a exist:
- - If a/b is being created, a becomes container of a/b.
- - If a/b/c/d is being created, a becomes container of a/b/c/d.
- - If e is being created, the root node becomes container of e.
- (verify if this is useful or necessary in any way).
- - If only a and a/b/c/d exist, and therefore a contains a/b/c/d, and
- a/b is created:
- - a ceases to be the container of a/b/c/d
- - a becomes container of a/b
- - a/b becomes container of a/b/c/d.
- '''
- if self.gs.list_containment_statements(self.urn):
- pass
- def _camelcase(self, word):
- '''
- Convert a string with underscores with a camel-cased one.
- Ripped from https://stackoverflow.com/a/6425628
- '''
- return ''.join(x.capitalize() or '_' for x in word.split('_'))
- class LdpRs(Ldpr):
- '''LDP-RS (LDP RDF source).
- Definition: https://www.w3.org/TR/ldp/#ldprs
- '''
- base_types = {
- nsc['ldp'].RDFSource
- }
- def patch(self, data):
- '''
- https://www.w3.org/TR/ldp/#ldpr-HTTP_PATCH
- '''
- ts = Literal(arrow.utcnow(), datatype=XSD.dateTime)
- self.gs.patch_rsrc(self.urn, data, ts)
- self.gs.ds.add((self.urn, nsc['fedora'].lastUpdated, ts))
- self.gs.ds.add((self.urn, nsc['fedora'].lastUpdatedBy,
- Literal('BypassAdmin')))
- self.gs.conn.store.commit()
- class LdpNr(LdpRs):
- '''LDP-NR (Non-RDF Source).
- Definition: https://www.w3.org/TR/ldp/#ldpnr
- '''
- pass
- class Ldpc(LdpRs):
- '''LDPC (LDP Container).'''
- def __init__(self, uuid):
- super().__init__(uuid)
- self.base_types.update({
- nsc['ldp'].Container,
- })
- class LdpBc(Ldpc):
- '''LDP-BC (LDP Basic Container).'''
- pass
- class LdpDc(Ldpc):
- '''LDP-DC (LDP Direct Container).'''
- def __init__(self, uuid):
- super().__init__(uuid)
- self.base_types.update({
- nsc['ldp'].DirectContainer,
- })
- class LdpIc(Ldpc):
- '''LDP-IC (LDP Indirect Container).'''
- def __init__(self, uuid):
- super().__init__(uuid)
- self.base_types.update({
- nsc['ldp'].IndirectContainer,
- })
|