Friday, August 17, 2007

pymv

pymv reads file names listed in an xml file and moves them to a directory. It can also move files not listed in the xml file to the directory.

"""moves files listed in xml file to dst directory."""

import sys, os, shutil, optparse, re
from xml.dom import minidom

def domv(src, dst, simulate=True):
    "mv src dst"
    if (not simulate) and os.path.exists(src):
        try:
            shutil.move(src,dst)
        except IOError, err:
            print err.message
        except OSError, err:
            print err.message

def mkre(s):
    """*.mp3 => ^.*\.mp3$"""
    return '^%s$' % re.escape(s).replace('\*', '.*')

def get_nodes(xmldoc, base, tag, attrib, value, no_attrib, from_attrib):
    """retrieves file names from nodes that looks like:
    <tag attrib="value">filename.txt</tag> => base/filename.txt
    when no_attrib=True, attrib="value" is not tested."""
    elements = xmldoc.getElementsByTagName(tag)

    if (attrib is None) or (value is None): #attribute not given.
        no_attrib = True #don't filter using attribute
        from_attrib = False #don't get filename from attribute

    if no_attrib: #don't want to filter using attribute
        attrib_test = lambda x: True
    else: #want to filter using attribute
        print mkre(value)
        attrib_test = lambda x: re.match(mkre(value), str(x))

    if from_attrib: #get filenames from attrib
        result = []
        for x in elements:
            attribute = x.getAttribute(attrib)
            if attrib_test(attribute):
                result.append(os.path.join(base, attribute))
        return result

    #get filenames from node.data
    return [os.path.join(base, x.firstChild.data)
            for x in elements
            if attrib_test(x.getAttribute(attrib)) and x.hasChildNodes()]

def main(argv=None):
    if argv is None:
        argv = sys.argv

    cliparser = optparse.OptionParser(
            usage='Usage: %prog [options] xml destdir')
    cliparser.add_option('-f', '--flip'
            , action="store_true"
            , help="moves files not listed in xml [default: %default]")
    cliparser.add_option('-b', '--base'
            , help="location of files listed in xml [default: where xml is located]")
    cliparser.add_option('-t', '--tag'
            , help="tag name to be searched for file names [default: %default]")
    cliparser.add_option('-a', '--attrib'
            , help="attributes to match [default: %default]")
    cliparser.add_option('-v', '--attrib-val'
            , help="attribute value to match. * matches everything. *.mp3 matches attrib=\"anything.mp3\" [default: %default]")
    cliparser.add_option('-n', '--no-attrib'
            , action="store_true"
            , help="don't test for attrib when selecting xml node [default: %default]")
    cliparser.add_option('-s', '--simulate'
            , action="store_true"
            , help="simulate. don't actually move files [default: %default]")
    cliparser.add_option('--from-attrib'
            , action="store_true"
            , help="get filename from attribute, not from node.data [default: %default]")

    cliparser.set_defaults(flip=False
            , tag="file"
            , attrib=None
            , attrib_val=None
            , no_attrib=False
            , simulate=False
            , from_attrib=False)

    (options, args) = cliparser.parse_args()

    arg_len = len(args)

    if arg_len < 2:
        cliparser.error("should pass xml, destdir")

    xmlf = os.path.abspath(args[0])
    dst = os.path.abspath(args[1])
    if options.base:
        base = os.path.abspath(options.base)
    else:
        #set to where xmlf is located
        base = os.path.dirname(xmlf)
    tag = options.tag
    attrib = options.attrib
    value = options.attrib_val
    no_attrib = options.no_attrib
    simulate = options.simulate
    from_attrib = options.from_attrib

    xmldoc = minidom.parse(xmlf)

    print 'Searching nodes: <%s %s="%s">' % (tag, attrib, value)
    filenames = get_nodes(xmldoc, base, tag, attrib, value
            , no_attrib, from_attrib)

    if simulate:
        for x in filenames:
            print x

    if not os.path.exists(dst):
        print "%(dst)s doesn't exist. Creating %(dst)s" % {'dst':dst}
        if not simulate:
            os.mkdir(dst)
    elif not os.path.isdir(dst): #dst exists but not directory.
        print """%(dst)s exists. And it's not a directory.
        Make sure %(dst)s is a directory.""" % {'dst':dst}
        sys.exit(1)

    if options.flip:
        for x in os.listdir(base):
            src = os.path.join(base, x)
            if os.path.isfile(src)\
                    and (src not in filenames)\
                    and (not src == xmlf):
                print "moving %(x)s to %(dst)s..." % {'x':x, 'dst':dst}
                domv(src, dst, simulate)
    else:
        for x in filenames:
            print "moving %(x)s to %(dst)s..." % {'x':x, 'dst':dst}
            domv(x, dst, simulate)

if __name__ == "__main__":
    main()

No comments:

Post a Comment