"""Prune database and disk of old images.

Parameters:  <older_than_sec> [<config_file>]

Disk is pruned to the next 60 s boundary. 
To prune all files use seconds value <= -60.
"""

import datetime as dt
import os
import sys
import shutil
import logging

import sqlalchemy as sa
import coils
import mapping
from log_format import getFormat

# Read command-line parameters.
SECONDS = float(sys.argv[1])
CONFIG = sys.argv[2] if len(sys.argv)>=3 else 'wabbit.cfg'

# Configure logging and get the logger object.
logging.basicConfig(
    stream=sys.stdout,
    format=getFormat(),
    level=logging.DEBUG,
    )
logger = logging.getLogger('prune')

# Load configuration file.
config = coils.Config(CONFIG)

# Connect to database engine and start a session.
engine = sa.create_engine(
    'mysql://{}:{}@{}/{}'.format(
        config['username'], config['password'], 
        config['host'], config['db_name']))
try:
    conn = engine.connect()
except sa.exc.OperationalError:
    logging.error('Failed to connect.')
    sys.exit(1)

# Compute the oldest timestamp allowed.
now = dt.datetime.now()
delta = dt.timedelta(seconds=SECONDS)
then = now - delta
logger.debug('Removing older than {}s ==> {}'.format(SECONDS, then))

# Remove from database.
Session = sa.orm.sessionmaker(bind=engine)
session = Session()
count = session.query(mapping.Image).filter(mapping.Image.time < then).delete()
logger.info('Deleted {} rows.'.format(count))
# Decrement the size.
session.query(mapping.Datum).\
    filter(mapping.Datum.name=='size').\
    update({'value':mapping.Datum.value-count})
session.commit()

# Define an error handler for deletion of entire directory tree.
def on_rmtree_error(func, path, excinfo):
    """Error handler for shutil.rmtree -- simply log the error details."""
    logger.error('rmtree error: func    {}'.format(str(func)))
    logger.error('              path    {}'.format(path))
    logger.error('              excinfo {}'.format(excinfo))
    
# Remove from disk.
levels = coils.time2levels(then)
dstack = list()  # Stack of descended subdirectories.
saved = os.getcwd()  # Pushd.
os.chdir(config['pics_dir'])
for level in levels:
    
    for entry in os.listdir('.'):
        try:
            int_entry = int(entry)
        except:
            continue
        if int_entry < int(level):
            full = os.path.join(os.getcwd(), entry)
            shutil.rmtree(full, ignore_errors=False, onerror=on_rmtree_error)
            logger.info('Removed {}'.format(full))

    # Skip if not a directory.
    if not os.path.isdir(level):
        break

    # Descend into subdirectory.
    os.chdir(level)  
    dstack.append(level)

# Delete empty subdirectories. 
for entry in reversed(dstack):
    if not os.listdir('.'):
        os.chdir('..')
        full = os.path.join(os.getcwd(), entry)
        shutil.rmtree(full)
        logger.info('Removed {}'.format(full))
        
os.chdir(saved)  # Popd.

@@ -1218,29 +1218,119 @@
 ))%0A%0A
-# Remove from databas
+%0A# Update the database.%0A# First remove rows from the image table,%0A# and then update size entry in the info tabl
 e.%0AS
@@ -1391,16 +1391,73 @@
 ssion()%0A
+timer = coils.Timer()%0Atry:%0A%0A    # Remove the images.%0A    
 count = 
@@ -1528,16 +1528,20 @@
 elete()%0A
+    
 logger.i
@@ -1578,16 +1578,21 @@
 count))%0A
+%0A    
 # Decrem
@@ -1605,16 +1605,20 @@
 e size.%0A
+    
 session.
@@ -1636,24 +1636,28 @@
 ng.Datum).%5C%0A
+    
     filter(m
@@ -1689,16 +1689,20 @@
 ).%5C%0A    
+ 
+   
 update(%7B
@@ -1736,16 +1736,214 @@
 -count%7D)
+%0A%0Aexcept sa.exc.OperationalError, ee:%0A%0A    logger.error('Failed to delete records.')%0A    logger.error('Exception: %7B%7D'.format(str(ee)))%0A%0Alogger.debug('Elapsed %7B%7D'.format(timer.get().total_seconds()))
 %0Asession
@@ -1953,16 +1953,17 @@
 mmit()%0A%0A
+%0A
 # Define

Add error checking to database update.