import logging import pdb from rdflib import Graph from rdflib.namespace import RDF, XSD from rdflib.resource import Resource from rdflib.term import URIRef, Literal, Variable from lakesuperior import env from lakesuperior.dictionaries.namespaces import ns_collection as nsc from lakesuperior.model.ldp.ldpr import Ldpr from lakesuperior.model.ldp.ldp_rs import LdpRs nonrdfly = env.app_globals.nonrdfly logger = logging.getLogger(__name__) default_hash_algo = env.app_globals.config['application']['uuid']['algo'] class LdpNr(Ldpr): """LDP-NR (Non-RDF Source). Definition: https://www.w3.org/TR/ldp/#ldpnr """ def __init__(self, *args, stream=None, mimetype=None, disposition=None, prov_cksum_algo=None, prov_cksum=None, **kwargs): """ Extends :meth:`lakesuperior.model.Ldpr.__init__` by adding LDP-NR specific parameters. """ super().__init__(*args, **kwargs) self.base_types = super().base_types | { nsc['fcrepo'].Binary, nsc['ldp'].NonRDFSource, } self.stream = stream if mimetype: self.mimetype = mimetype else: self.mimetype = ( str(self.metadata.value(nsc['ebucore'].hasMimeType)) if self.is_stored else 'application/octet-stream') self.disposition = disposition self.prov_cksum_algo = prov_cksum_algo self.prov_cksum = prov_cksum @property def filename(self): """ File name of the original uploaded file. :rtype: str """ return self.metadata.value(nsc['ebucore'].filename) @property def content(self): """ Binary content. :return: File handle of the resource content. :rtype: io.BufferedReader """ return open(self.local_path, 'rb') @property def content_size(self): """ Byte size of the binary content. :rtype: int """ return int(self.metadata.value(nsc['premis'].hasSize)) @property def local_path(self): """ Path on disk of the binary content. :rtype: str """ cksum_term = self.metadata.value(nsc['premis'].hasMessageDigest) cksum = str(cksum_term).replace(f'urn:{default_hash_algo}:','') return nonrdfly.__class__.local_path( nonrdfly.root, cksum, nonrdfly.bl, nonrdfly.bc) def create_or_replace(self, create_only=False): """ Create a new binary resource with a corresponding RDF representation. :param bool create_only: Whether the resource is being created or updated. """ # Persist the stream. self.digest, self.size = nonrdfly.persist( self.uid, self.stream, prov_cksum_algo=self.prov_cksum_algo, prov_cksum=self.prov_cksum) # Try to persist metadata. If it fails, delete the file. logger.debug('Persisting LDP-NR triples in {}'.format(self.uri)) try: ev_type = super().create_or_replace(create_only) except: # self.digest is also the file UID. nonrdfly.delete(self.digest) raise else: return ev_type ## PROTECTED METHODS ## def _add_srv_mgd_triples(self, *args, **kwargs): """ Add all metadata for the RDF representation of the LDP-NR. :param BufferedIO stream: The uploaded data stream. :param str mimetype: MIME type of the uploaded file. :param defaultdict disposition: The ``Content-Disposition`` header content, parsed through ``parse_rfc7240``. """ super()._add_srv_mgd_triples(*args, **kwargs) # File size. logger.debug('Data stream size: {}'.format(self.size)) self.provided_imr.set(( self.uri, nsc['premis'].hasSize, Literal(self.size))) # Checksum. cksum_term = URIRef(f'urn:{default_hash_algo}:{self.digest}') self.provided_imr.set(( self.uri, nsc['premis'].hasMessageDigest, cksum_term)) # MIME type. self.provided_imr.set(( self.uri, nsc['ebucore']['hasMimeType'], Literal(self.mimetype))) # File name. logger.debug('Disposition: {}'.format(self.disposition)) try: self.provided_imr.set(( self.uri, nsc['ebucore']['filename'], Literal( self.disposition['attachment']['parameters']['filename']))) except (KeyError, TypeError) as e: pass