subdirs.py

'''\
List sub-directories.

commands:
    sizes           - List directory sizes
    recent          - Scan for recently modified files
    list            - Generate a text listing
    xml             - Generate an XML listing

    main            - Main function (normal use)
    demo            - Demo function (show off and test app)
    test            - Test function (development)

usage:
    module.dispatch({command}, args={arguments})    -- calling module from code

    $PYTHON module_name {command} {arguments}       -- executing module externally

Developer@Sonnack.com
April 2016
'''
####################################################################################################
from __future__ import print_function
from sys import argv, exc_info
from traceback import extract_tb
from os import path, listdir
from datetime import datetime, timedelta
from time import localtime
from logger import logger, info, debug, trace
from filelist import fileobj, ListFiles, ListFolder, ListFolders
from zoo import add_commas
####################################################################################################
Log = logger('subdirs')

BasePath = r'C:\CJS\prj\Python\app'
DemoFilepath = r'C:\cjs\www\root\pub'


##================================================================================================##
def create_xml_listing (*args):
    '''\
List files in XML format.

arguments:
    base-path
    output-file

'''
    basepath = str(args[0]) if 0 < len(args) else DemoFilepath
    listname = str(args[1]) if 1 < len(args) else 'filelist.xml'

    Log.debug('xml-listing/parameters: %d' % len(args))
    Log.info('xml-listing/basepath: %s' % basepath)
    Log.info('xml-listing/listname: %s' % listname)

    fn = path.join(basepath, listname)
    fp = open(fn, 'w')
    try:
        print('<?xml version="1.0" encoding="UTF-8"?>', file=fp)
        print('<filelist basepath="%s">' % basepath, file=fp)
        _create_xml_listing(basepath, fp)
        print('</filelist>', file=fp)
        Log.info('wrote: %s' % fn)
    except:
        raise
    finally:
        fp.close()
    return [basepath, fn]
##------------------------------------------------------------------------------------------------##
def _create_xml_listing (pathname, fp):
    Log.trace('xml-listing: %s' % pathname)
    fs = []  # list of files
    ds = []  # list of directories
    # Get list of files in this directory...
    ns = listdir(pathname)
    for n in ns:
        fn = path.join(pathname, n)
        if path.isfile(fn):
            fs.append(fn)
            continue
        if path.isdir(fn):
            ds.append(fn)
            continue

    print('<directory name="%s">' % pathname, file=fp)
    # Process sub-directories...
    for dn in sorted(ds):
        # RECURSE...
        _create_xml_listing(dn, fp)

    for fn in sorted(fs):
        tm = localtime(path.getctime(fn))
        tc = datetime(tm[0],tm[1],tm[2],tm[3],tm[4],tm[5])
        tm = localtime(path.getmtime(fn))
        tu = datetime(tm[0],tm[1],tm[2],tm[3],tm[4],tm[5])
        t = (fn, path.getsize(fn), tc, tu)
        s = '<file name="%s" size="%d" cdate="%s" mdate="%s" />'
        print(s % t, file=fp)

    print('</directory>', file=fp)

##================================================================================================##
def list_directory_sizes (*args):
    '''\
List Directory Sizes.

arguments:
    base-path
    output-file

'''
    basepath = args[0] if 0 < len(args) else DemoFilepath
    listname = args[1] if 1 < len(args) else 'dir-sizes.list'

    Log.debug('list-directory-sizes/parameters: %d' % len(args))
    Log.info('list-directory-sizes/basepath: %s' % basepath)
    Log.info('list-directory-sizes/listname: %s' % listname)

    result = []
    t_bytes = _list_directory_sizes(basepath, result)

    fn = path.join(basepath, listname)
    fp = open(fn, 'w')
    try:
        print('   Total Bytes   Files     Dir Bytes  Files   Directory Name', file=fp)
        print('==============  ======  ============  =====   =========================================', file=fp)
        for d in result:
            s1 = add_commas(d[1]) if d[1] != d[3] else '-'
            s2 = add_commas(d[2]) if d[2] != d[4] else '-'
            s3 = add_commas(d[3])
            s4 = add_commas(d[4])
            print('%14s %7s %13s %6s   %s' % (s1, s2, s3, s4, d[0]), file=fp)
        Log.info('wrote: %s' % fn)
    except:
        raise
    finally:
        fp.close()
    return result
##------------------------------------------------------------------------------------------------##
def _list_directory_sizes (pathname, result):
    Log.trace('list-directory-size: %s' % pathname)

    ds = []  # list of directories
    dt = [pathname, 0L, 0L, 0L, 0L] # [dirname, total-size, total-count, dir-size, dir-count]
    result.append(dt)
    # Get list of files in this directory...
    ns = listdir(pathname)
    for n in ns:
        fn = path.join(pathname, n)
        if path.isfile(fn):
            dt[3] += path.getsize(fn)
            dt[4] += 1
            continue
        if path.isdir(fn):
            ds.append(fn)
            continue

    # Process sub-directories...
    dt[1] += dt[3]
    dt[2] += dt[4]
    for dn in sorted(ds):
        # RECURSE...
        t = _list_directory_sizes(dn, result)
        # Add sub-dir size to this one's total...
        dt[1] += t[1]
        dt[2] += t[2]

    return dt


##================================================================================================##
def create_text_listing (*args):
    '''\
List files in a nice text format.

arguments:
    base-path
    output-file

'''
    basepath = str(args[0]) if 0 < len(args) else DemoFilepath
    listname = str(args[1]) if 1 < len(args) else 'files.list'

    Log.debug('text-listing/parameters: %d' % len(args))
    Log.info('text-listing/basepath: %s' % basepath)
    Log.info('text-listing/listname: %s' % listname)

    ts = ListFolder(basepath)
    Log.debug('text-listing/found: %d' % len(ts))

    fn = path.join(basepath, listname)
    fp = open(fn, 'w')
    try:
        for t in ts:
            Log.trace('Dir: %s' % t[0])
            print(t[0], file=fp)
            for f in t[1]:
                fname = path.join(t[0], f)
                fo = fileobj(fname)
                Log.trace('File: %s' % f)
                print('%-40s %9d %s %s' % (f, fo.fsize, fo.created, fo.updated), file=fp)
            print(file=fp)
        Log.trace()
        Log.info('wrote: %s' % fn)
    except:
        raise
    finally:
        fp.close()
    return ts


##================================================================================================##
def find_recent_files (*args):
    '''\
Find Recent Files.

arguments:
    nbr-days-back
    base-path
    output-file

'''
    datespan = int(args[0]) if 0 < len(args) else 3
    basepath = str(args[1]) if 1 < len(args) else DemoFilepath
    listname = str(args[2]) if 2 < len(args) else 'recent-files.list'

    Log.debug('find-recent-files/parameters: %d' % len(args))
    Log.info('find-recent-files/datespan: %d' % datespan)
    Log.info('find-recent-files/basepath: %s' % basepath)
    Log.info('find-recent-files/listname: %s' % listname)

    dt = datetime.now() - timedelta(datespan)
    ts = ListFolder(basepath)

    fs = []
    for t in ts:
        Log.trace('Dir: %s' % t[0])
        for f in t[1]:
            fn = path.join(t[0], f)
            fo = fileobj(fn)
            if fo.updated < dt:
                continue
            Log.trace('File: %s  [%s, %d]' % (f, fo.updated, fo.fsize))
            fs.append(fn)
    Log.trace()

    fn = path.join(basepath, listname)
    fp = open(fn, 'w')
    try:
        for f in fs:
            print(f, file=fp)
        Log.info('wrote: %s' % fn)
    except:
        raise
    finally:
        fp.close()
    return fs



##================================================================================================##
def dump_filelist (flist):
    Log.trace()
    Log.trace('Total Dirs: %d' % len(flist))
    Log.trace('Total Files: %d' % sum(map(lambda t: len(t[1]), flist)))
    Log.trace()
    for t in flist:
        Log.trace('Dir: %s  (%d files)' % (t[0], len(t[1])))
        for f in t[1]:
            Log.trace('. File: %s' % path.join(t[0], f))
    Log.trace()
##================================================================================================##
def do_test (*args):
    Log.trace('test/parameters: %s' % str(args))
    basepath = args[0] if 0 < len(args) else DemoFilepath
    ts = ListFolder(basepath)
    dump_filelist(ts)
    return 'Done!'
##================================================================================================##
def do_demo (*args):
    Log.trace('demo/parameters: %s' % str(args))
    ts = ListFolders(args)
    dump_filelist(ts)
    return 'Done!'
##================================================================================================##
def do_main (*args):
    Log.trace('main/parameters: %s' % str(args))
    ts = ListFolders(args)
    dump_filelist(ts)
    return 'Done!'
####################################################################################################
def dispatch (cmd, *args):
    Log.trace('command: %s' % cmd)
    Log.trace('arguments: %d' % len(args))
    if cmd == 'sizes': return list_directory_sizes(*args)
    if cmd == 'recent': return find_recent_files(*args)
    if cmd == 'list': return create_text_listing(*args)
    if cmd == 'xml': return create_xml_listing(*args)
    if cmd == 'test': return do_test(*args)
    if cmd == 'demo': return do_demo(*args)
    if cmd == 'main': return do_main(*args)
    return 'Nothing to do!'
####################################################################################################
if __name__ == '__main__':
    print('autorun: %s' % argv[0])
    Log.start(path.join(BasePath,'subdirs.log'))
    Log.level(info())
    cmd = argv[1] if 1 < len(argv) else ''
    etc = argv[2:] if 2 < len(argv) else []
    try:
        obj = dispatch(cmd, *etc)
        print(obj)
        Log.info(obj)
    except:
        etype, evalue, tb = exc_info()
        ts = extract_tb(tb)
        Log.error('%s: %s' % (etype.__name__,str(evalue)))
        for t in ts[-3:]:
            Log.error('[%d] %s  (%s)' % (t[1], t[2], t[0]))
            Log.error('    %s' % t[3])
        raise
    finally:
        Log.end()
####################################################################################################
'''eof'''