Source code for dbprocessing.DBfile

"""Database representations of data files and operations."""

from __future__ import absolute_import
from __future__ import print_function

import os
import shutil
import tarfile

from . import DBlogging
from . import Diskfile
from . import Utils


[docs]class DBfileError(Exception): """Exception that is raised by DBfile class""" pass
[docs]class DBfile(object): """Maps a physical file on disk to database file entry. DBfile class is an extension of Diskfile that takes a physical file on disk and maps it to the database file entry, this is not mapped to the file table in the DB but instead a bridge between the two. """
[docs] def __init__(self, diskfile, dbu, makeDiskFile=False): """ Setup a DBfile class. .. todo:: Do we need this keyword or the functionality? Parameters ---------- diskfile : :class:`str` or :class:`.Diskfile` A file name or diskfile instance to create a DBfile from dbu : :class:`~.DButils.DButils`, optional Current database connection. If not specified, creates a new connection. makeDiskFile : :class:`bool`, default False If true, ``diskfile`` is a filename and needs a :class:`.Diskfile` made. """ if makeDiskFile: diskfile = Diskfile.Diskfile(diskfile, dbu) if not isinstance(diskfile, Diskfile.Diskfile): raise DBfileError('Wrong input, must input a Diskfile object') self.dbu = dbu self.diskfile = diskfile
def __repr__(self): return "<DBfile.DBfile object: {0}>".format(self.diskfile.infile) __str__ = __repr__
[docs] def addFileToDB(self): """ Wrapper around :meth:`~.DButils.addFile` to take params dict to keywords Returns ------- :class:`int` :sql:column:`~file.file_id` of the newly added file """ return self.dbu.addFile(**self.diskfile.params)
[docs] def getDirectory(self): """ Query the DB and get the directory that the file should exist in Returns ------- :class:`str` The full path for the DBfile """ relative_path = self.dbu.session.query(self.dbu.Product.relative_path).filter_by( product_id=self.diskfile.params['product_id']) relative_path = relative_path.all() if len(relative_path) > 1: raise DBfileError('more than one rel path found') if len(relative_path) == 0: raise DBfileError('zero rel path found') basepath = self.dbu.getMissionDirectory() return os.path.join(basepath, relative_path[0][0])
[docs] def move(self): """ Move the DBfile from its current location to where it belongs If the file is a symbolic link it is assumed already in the target directory and not moved, the link is just removed Returns ------- :class:`tuple` of :class:`str` from and to arguments to move with full path info """ path = self.getDirectory() ## need to do path replacements path = Utils.dirSubs(path, self.diskfile.params['filename'], self.diskfile.params['utc_file_date'], self.diskfile.params['utc_start_time'], '{0}'.format(str(self.diskfile.params['version']))) # if the file is a link just remove the link and pretend we moved it, this means # that this file is tracked only as a dependency if os.path.islink(self.diskfile.infile): os.unlink(self.diskfile.infile) DBlogging.dblogger.info( "file {0} was a link, it was added and removed".format(os.path.basename(self.diskfile.infile))) else: try: shutil.move(self.diskfile.infile, os.path.join(path, self.diskfile.params['filename'])) except IOError: dirname = os.path.split(os.path.join(path, self.diskfile.params['filename']))[0] os.makedirs(dirname) DBlogging.dblogger.warning("created a directory to put the date into: {0}".format(dirname)) shutil.move(self.diskfile.infile, os.path.join(path, self.diskfile.params['filename'])) DBlogging.dblogger.info("file {0} moved to {1}".format(os.path.basename(self.diskfile.infile), os.path.dirname(os.path.join(path, self.diskfile.params[ 'filename'])))) DBlogging.dblogger.debug("self.diskfile.filename: {0}".format(self.diskfile.filename)) # if the file we are moving is a tgz file then we want to extract it in the place we moved it to and move the tgz file into a tgz directory try: tf = tarfile.open(os.path.join(path, self.diskfile.params['filename']), 'r:gz') if tf.getmembers(): # false if it does nat have any files inside i.e. is not a tarfile or empty, deal with empty later tf.extractall(path=os.path.join(path, '..')) # up one dir level tf.close() except tarfile.ReadError: pass return (self.diskfile.infile, os.path.join(path, self.diskfile.params['filename']))