#!/usr/bin/env python

##
## BenchmarkFiller.py
##      - fills benchmark database from the specifie benchmark file or a
##      directory containing benchmark files ##

# $Id: BenchmarkFiller.py,v 1.5 2007/01/08 21:50:30 jarda Exp $


# import of required modules {{{
import os
import os.path
from optparse import OptionParser
import sys
import shutil

# added because of cxFreeze
import struct

from BenchmarkFileParser import BenchmarkFileParser
from Constants import Constants
import Database
from BenchConfig import BenchConfig
# }}}


def main():
    # DOC {{{
    """Parses the command line options and calls the processing methods.
    """
    # }}}

    # CODE {{{
    parser = OptionParser()

    parser.add_option("-f", "--file", dest = "fileName",
                      help = "read the specified file")
    parser.add_option("-i", "--benchmark-instance-id", dest = "benchmarkInstanceId",
                      help = "ID of benchmark instance")
    parser.add_option("-t", "--format", dest = "format",
                      help = "format of the file")
    parser.add_option("-d", "--directory", default = '.', dest = "directoryName",
                      help = "process the specified directory")
    parser.add_option("-s", "--benchmark-set",
                      dest = "benchmarkSet",
                      help = "benchmark set")
    parser.add_option("-r", "--recursive",
                      dest = "recursive",
                      action = "store_true",
                      help ="recursively process all suitable files in the directory")

    (options, args) = parser.parse_args()    

    if ((not options.benchmarkSet) and (not options.benchmarkInstanceId)):
        print "Benchmark set or benchmark instance ID must be specified"
        return

    if ((not options.fileName) and (not options.recursive)):
        print "Either file name or recursive processing must be specified"
        return

    if (options.recursive):
        processDirectory(options.directoryName, options.benchmarkSet)
    else:
        processFile(options.fileName, options.format, options.benchmarkInstanceId, options.benchmarkSet)
    # }}}


def processDirectory(directoryName, benchmarkSetName):
    # DOC {{{
    """Processes a directory.

    Parameters

        directoryName -- the directory name

        benchmarkSetName -- name of the benchmark set
    """
    # }}}

    # CODE {{{
    for path, directories, files in os.walk(directoryName):
        if (path[-1] != '/'):
            path = path + '/'
        
        for fileName in files:
            wholeFileName = path + fileName
            
            if (BenchmarkFileParser.isBenchmarkFile(wholeFileName)):
                processFile(wholeFileName, None, None, benchmarkSetName)
    # }}}


def processFile(fileName, formatName, benchmarkInstanceId, benchmarkSetName):
    # DOC {{{
    """Processes a benchmark file.

    Parameters

        fileName -- the processed file name

        formatName -- name of the processed format or None

        benchmarkInstanceId -- the benchmark instance ID

        benchmarkSetName -- the benchmark set name
    """
    # }}}

    # CODE {{{
    # XXX: remove?
    print 'Processing file', fileName
    
    if (not formatName):
        formatName = BenchmarkFileParser.getExtensionWithoutDot(fileName)

    parser = BenchmarkFileParser.getParserForFormatName(formatName)

    parser.parseBenchmarkFileContents(open(fileName).read())

    attributesDictionary = parser.getAttributesWithKnownValues()

    fillFileInformationToDatabase(benchmarkInstanceId, fileName, formatName, benchmarkSetName, attributesDictionary)
    # }}}


def fillFileInformationToDatabase(benchmarkInstanceId, fileName, formatName, benchmarkSetName, 
                                  attributesDictionary):
    # DOC {{{
    """Fills file information to the database.

    Parameters

        benchmarkInstanceId -- the benchmark instance ID

        fileName -- the processed file name
        
        formatName -- name of the processed format

        benchmarkSetName -- the benchmark set name

        attributesDictionary -- dictionary with values of the benchmark attributes
    """
    # }}}

    # CODE {{{
    db = Database.getDatabaseConnection()

    db.BeginTrans()

    if (not benchmarkInstanceId):
        benchmarkName = getBenchmarkNameFromFileName(fileName)
        benchmarkSetName, benchmarkSetId = getProperBenchmarkSetNameAndId(db, benchmarkSetName)
        uploadedFileName = uploadFile(fileName, benchmarkName, formatName, benchmarkSetName)
        benchmarkInstanceId = createBenchmarkInstance(db, uploadedFileName, benchmarkName, formatName, benchmarkSetId)
    else:
        benchmarkInstanceId = int(benchmarkInstanceId)
        
    fillBenchmarkInstanceAttributes(db, benchmarkInstanceId, attributesDictionary)

    db.CommitTrans()
    # }}}


def uploadFile(originalFileName, benchmarkName, formatName, benchmarkSetName):
    # DOC {{{
    """Uploads a file to the benchmark database directory.

    Parameters

        originalFileName -- the file name containing the benchmark data

        benchmarkName -- name of the benchmark

        formatName -- name of the format

        benchmarkSetName -- the name of the benchmark set
    """
    # }}}

    # CODE {{{
    wholeDirectoryName = ("%s/%s") % (BenchConfig.benchmarkFilesDirectory, benchmarkSetName,)

    if (not os.access(wholeDirectoryName, 0)):
        os.makedirs(wholeDirectoryName)
    
    uploadedFileName = ("%s.%s") % (benchmarkName, formatName,)
    wholeUploadedFileName = ("%s/%s") % (wholeDirectoryName, uploadedFileName,)
    index = 1
    while (os.access(wholeUploadedFileName, 0)):
        uploadedFileName = ("%s.%d.%s") % (benchmarkName, index, formatName,)
        wholeUploadedFileName = ("%s/%s") % (wholeDirectoryName, uploadedFileName,)
        index += 1

    shutil.copyfile(originalFileName, wholeUploadedFileName)

    return uploadedFileName
    # }}}


def getBenchmarkNameFromFileName(fileName):
    # DOC {{{
    """Extracts and returns benchmark name from file name.

    Parameters

        fileName -- the file name
    """
    # }}}

    # CODE {{{
    # strip the file name from respective path
    fileNameWithoutPath = os.path.split(fileName)[1]

    # return lowercase file name without extension
    return os.path.splitext(fileNameWithoutPath)[0].lower()
    # }}}


def createBenchmarkInstance(db, uploadedFileName, benchmarkName, formatName, benchmarkSetId):
    # DOC {{{
    """Creates a new benchmark instance.

    Parameters

        uploadedFileName -- the uploaded file name

        benchmarkName -- name of the benchmark

        formatName -- name of the format

        benchmarkSetId -- the benchmark set ID
    """
    # }}}

    # CODE {{{
    query = "SELECT id FROM benchmark WHERE name = '%(benchmarkName)s'" % {
        'benchmarkName' : benchmarkName,
    }
    
    recordSet = db.Execute(query)

    if (recordSet.RecordCount()):
        row = recordSet.GetRowAssoc(0)
        benchmarkId = row['id']
    else:
        benchmarkId = createBenchmark(db, benchmarkName)

    benchmarkInstanceId = Database.genId('benchmarkinstance_id_seq')

    formatId = getFormatId(db, formatName)

    query = ("INSERT INTO benchmarkInstance (id, benchmarkId, benchmarkSetId, formatId, fileName, description) "
            "VALUES (%(id)s, %(benchmarkId)s, %(benchmarkSetId)d, %(formatId)d, '%(fileName)s', '%(description)s')") % {
        'id'                : benchmarkInstanceId,
        'benchmarkId'       : benchmarkId,
        'benchmarkSetId'    : benchmarkSetId,
        'formatId'          : formatId,
        'fileName'          : uploadedFileName,
        'description'       : 'INSERTED BY SCRIPT',
    }

    db.Execute(query)

    return benchmarkInstanceId
    # }}}


def getProperBenchmarkSetNameAndId(db, benchmarkSetName):
    # DOC {{{
    """Returns benchmark set name and ID using a possibly mixed-case benchmark
    set name.

    Parameters

        benchmarkSetName -- the benchmark set name
    """
    # }}}

    # CODE {{{
    selectQuery = "SELECT name, id FROM benchmarkSet WHERE upper('%(benchmarkSetName)s') = upper(name)" % {
        'benchmarkSetName' : benchmarkSetName,
    }
    
    recordSet = db.Execute(selectQuery)

    if (not recordSet.RecordCount()):
        insertQuery = "INSERT INTO benchmarkSet (name) VALUES ('%(benchmarkSetName)s')" % {
            'benchmarkSetName' : benchmarkSetName,
        }

        db.Execute(insertQuery)

        recordSet = db.Execute(selectQuery)

    row = recordSet.GetRowAssoc(0)

    return row['name'], row['id']
    # }}}


def createBenchmark(db, benchmarkName):
    # DOC {{{
    """Creates a new benchmark.

    Parameters

        benchmarkName -- name of the benchmark
    """
    # }}}

    # CODE {{{
    benchmarkId = Database.genId('benchmark_id_seq')

    query = ("INSERT INTO benchmark (id, name, description) " +
            "VALUES (%(id)s, '%(name)s', '%(description)s')") % {
        'id'            : benchmarkId,
        'name'          : benchmarkName,
        'description'   : 'INSERTED BY SCRIPT',
    }

    db.Execute(query)

    return benchmarkId
    # }}}


def getFormatId(db, formatName):
    # DOC {{{
    """Returns ID of the specified format.

    Parameters

        formatName -- name of the format
    """
    # }}}

    # CODE {{{
    query = "SELECT id FROM format WHERE upper('%(formatName)s') = upper(name)" % {
        'formatName' : formatName,
    }
    
    recordSet = db.Execute(query)

    if (not recordSet.RecordCount()):
        print >> sys.stderr, 'Unknown format: ', formatName
        sys.exit(1)

    row = recordSet.GetRowAssoc(0)

    return row['id']
    # }}}


def fillBenchmarkInstanceAttributes(db, benchmarkInstanceId, attributesDictionary):
    # DOC {{{
    """Fills benchmark instance attributes to the database.

    Parameters

        db -- the database connection

        benchmarkInstanceId -- the benchmark instance ID

        attributesDictionary -- dictionary with values of the benchmark attributes
    """
    # }}}

    # CODE {{{
    for benchmarkAttributeId, value in attributesDictionary.iteritems():
        attributeName = Constants.READABLE_BA_NAME[benchmarkAttributeId]
        attributeId = getAttributeIdByAttributeName(db, attributeName)
        if (not setBenchmarkInstanceAttributeValue(db, benchmarkInstanceId, attributeId, value)):
            insertBenchmarkInstanceAttribute(db, benchmarkInstanceId, attributeId, value)
    # }}}


def getAttributeIdByAttributeName(db, attributeName):
    # DOC {{{
    """Returns attribute ID by attribute name.

    Parameters

        db -- the database connection

        attributeName -- the attribute name
    """
    # }}}

    # CODE {{{
    query = "SELECT id FROM attribute WHERE name = '%(name)s'" % {
        'name' : attributeName,
    }

    recordSet = db.Execute(query)

    row = recordSet.GetRowAssoc(0)

    return row['id']
    # }}}


def setBenchmarkInstanceAttributeValue(db, benchmarkInstanceId, attributeId, value):
    # DOC {{{
    """Sets a new value to the specified benchmark instance attribute.

    Parameters

        benchmarkInstanceId -- the benchmark instance ID

        attributeId -- the attribute ID

        value -- the new value
    """
    # }}}

    # CODE {{{
    query = ("SELECT attributeId FROM benchmarkInstanceAttribute " +
            "WHERE attributeId = %(attributeId)d AND benchmarkInstanceId = %(benchmarkInstanceId)d") % {
        'attributeId'           : attributeId,
        'benchmarkInstanceId'   : benchmarkInstanceId,
    }

    recordSet = db.Execute(query)

    if (not recordSet.RecordCount()):
        return False

    query = ("UPDATE benchmarkInstanceAttribute SET value = '%(value)s' " +
            "WHERE benchmarkInstanceId = %(benchmarkInstanceId)s AND attributeId = %(attributeId)d") % {
        'benchmarkInstanceId'   : benchmarkInstanceId,
        'attributeId'           : attributeId,
        'value'                 : value,
    }

    db.Execute(query)
    
    return True
    # }}}


def insertBenchmarkInstanceAttribute(db, benchmarkInstanceId, attributeId, value):
    # DOC {{{
    """Inserts a new benchmark instance attribute to the database.

    Parameters

        db -- the database connection

        benchmarkInstanceId -- the benchmark instance ID

        attributeId -- the attribute ID

        value -- the value
    """
    # }}}

    # CODE {{{
    query = ("INSERT INTO benchmarkInstanceAttribute "
            "(benchmarkInstanceId, attributeId, value) " +
            "VALUES (%(benchmarkInstanceId)s, %(attributeId)d, %(value)s)") % {
        'benchmarkInstanceId'   : benchmarkInstanceId,
        'attributeId'           : attributeId,
        'value'                 : value,
    }

    db.Execute(query)
    # }}}


if (__name__ == '__main__'):
    main()
