#!/usr/bin/python
"""
Utility for working with django projects. Currently supports various
functions for dumping user-entered data. This file can live wherever
you like, and is designed to be run from within the root of a django
project.
"""

import os, sys
from shutil import copy
import commands

# Add current directory and directory above it into python path:
projectDir=os.getcwd()
sys.path.append(projectDir)
sys.path.append(projectDir[0:projectDir.rfind('/')])

backupDir = os.path.join(projectDir, 'backup')
projectName=projectDir.split('/').pop()

os.environ['DJANGO_SETTINGS_MODULE'] = '%s.settings' % projectName

try:
    import settings
except ImportError:
    print "This script is designed to be run from the root of a django project. It doesn't look like that's where you are."
    sys.exit()
try:
    from django.db.models.loading import get_models
except ImportError:
    print "Sorry, there was an error. I think it's probably because you don't have the django source in your python path."

def model_sqldump(model):
#    print "dumping %s" % model._meta.db_table
    return mysqldump(model._meta.db_table)

def backup_sql():
    """
    dump sql for all models
    """
    sqlfile = os.path.join(backupDir, '%s.sql' % projectName)
    print "writing sql to %s" % sqlfile

    if not os.path.isdir(backupDir):
        os.makedirs(backupDir)
    fh=open(sqlfile, 'w')
    for model in get_models():
        fh.write(model_sqldump(model))
    fh.close()

def backup_media():
    """
    Copy all uploaded files that have an entry in the database
    """
    from settings import MEDIA_ROOT
    all_models = get_models()
    uploadedFiles=[]
    for model in all_models:
        # make a list of fields which upload files
        uploadFields=[]
        for f in model._meta.fields:
            if hasattr(f, 'upload_to'):
                uploadFields.append(f.name)

        # make a list of uploaded files
        for row in model.objects.all():
            for f in uploadFields:
                if getattr(row, f):
                    uploadedFiles.append(getattr(row, f))

    if not os.path.isdir(backupDir):
        os.makedirs(backupDir)
    backupRoot=os.path.join(backupDir, 'app-media')
    print "backing up media to %s" % backupRoot
    for f in uploadedFiles:
        src = os.path.join(MEDIA_ROOT, f)
        if not os.path.isfile(src):
            print "Warning: missing file:\n%s" % src
            continue
        dest = os.path.join(backupRoot, f)
        if not os.path.isdir(os.path.dirname(dest)):
            os.makedirs(os.path.dirname(dest))
        copy(src, dest)

def restore_sql():
    dumpFile = os.path.join(backupDir, '%s.sql' % projectName)
    if os.path.isfile(dumpFile):
        print "restoring database"
        x = commands.getstatusoutput('mysql %s < %s' % ( settings.DATABASE_NAME,
                                                         dumpFile) )         
    else:
        print "Sorry, no database dump found"

def restore_media():
    print "restoring media"
    appMediaDir=os.path.join(backupDir, 'app-media')
    for dir in os.listdir(appMediaDir):
        copytree(os.path.join(appMediaDir, dir), os.path.join(settings.MEDIA_ROOT, dir))

def copytree(src, dest):
    """
    Copy a tree into an existing tree, so extant files
    are either replaced or left alone, and non-extant
    ones are created
    """
    if not os.path.isdir(dest):
        os.mkdir(dest)
    for f in os.listdir(src):
        srcfile=os.path.join(src, f)
        destfile=os.path.join(dest, f)
        if os.path.isdir(srcfile):
            copytree(srcfile, destfile)
        else:
            copy(srcfile, destfile)

def mysql_authentication_string():
    """
    return the username and password bits that need to be passed to mysql / mysqldump
    """
    if settings.DATABASE_PASSWORD:
        return '-u%s -p%s' % (settings.DATABASE_USER, settings.DATABASE_PASSWORD)
    else:
        return '-u%s' % (settings.DATABASE_USER)


def mysqldump(tableNames=''):
    # Return a db dump. Tablenames is a space-separated list of tables to dump
    x = commands.getstatusoutput('mysqldump %s %s %s' % ( mysql_authentication_string(),
                                                       settings.DATABASE_NAME,
                                                       tableNames) )
    if x[0] != 0:
        return False
    else:
        return x[1]

def tarball():
    """
    Make a tarball of the backup dir
    """
    import tarfile
    tarFilePath = os.path.join(projectDir, '%s.tgz' % projectName)
    os.chdir(projectDir)
    tar = tarfile.open(tarFilePath, "w:gz")
    tar.add('backup')
    tar.close()

def parseCommandLine():
    """Examines options and does preliminary checking"""
    from optparse import OptionParser

    parser = OptionParser("django-projectadmin [options]")

    parser.add_option("-v", "--verbose",
                      action="store_true", dest="verbose", default=False,
                      help="Print information about what is going on")

    parser.add_option("-c", "--create-archive",
                      action="store_true", dest="create", default=False,
                      help="archive project media / sql")

    parser.add_option("-x", "--exhume-archive",
                      action="store_true", dest="exhume", default=False,
                      help="restore project media / sql")

    parser.add_option("-s", "--no-media",
                      action="store_true", dest="nomedia", default=False,
                      help="don't archive project media")

    parser.add_option("-m", "--no-sql",
                      action="store_true", dest="nosql", default=False,
                      help="don't archive project sql")

    parser.add_option("-z", "--gzip",
                      action="store_true", dest="gzip", default=False,
                      help="tar / gzip archive directory after creation")

    parser.add_option("--spewsql",
                      action="store_true", dest="spewsql", default=False,
                      help="dump sql for the project's db")

    options, args = parser.parse_args()

    return (options, args)

def check_no_backupdir():
    if os.path.isdir(backupDir):
        print "Backup directory already exists, and I'm too cowardly to delete it. Please delete it yourself."
        sys.exit()
    return

def main():
    """parse command line and do the stuff"""
    options, args = parseCommandLine()

    if options.spewsql:
        print mysqldump()
        sys.exit()
    if options.create:
        check_no_backupdir()
        if not options.nosql:
            backup_sql()
        if not options.nomedia:
            backup_media()
        if options.gzip:
            tarball()
    if options.exhume:
        if not options.nosql:
            restore_sql()
        if not options.nomedia:
            restore_media()

if __name__=='__main__':
    main()
