#!/usr/bin/env sampy
"""
An example script showing how to find all of the descendants
of a specified SAM data file.

Usage:

   CLI:
   ./GetFileDescendants.py [--dataTier=<value>] \
                           [--appName=<value>] [--appVersion=<value>] \
                           [--before=<date>] [--since=<date>] \
                           fileName

   API:  # make sure that $SAM_DIR/examples is in your python path
   #!/usr/bin/env sampy
   from GetFileDescendants import getFileDescendants
   gfd = getFileDescendants()
   physicalFileAttributeList = gfd(args='fileName',
                                   dataTier='dataTierHere',
                                   before='dd-mmm-yyyy',
                                   since='dd-mmm-yyyy',
                                   appName='appNameHere',
                                   appVersion='v_whatever_here')

"""
import os
import sys
import string

import SAM
from Sam import sam
from SamUtility.DbDerivedClient import DbDerivedClient
from SamStruct.SamTime import SamTime
from SamStruct.DataFilePhysicalAttributes import DataFilePhysicalAttributes
from SamStruct.DataFilePhysicalAttributesList import DataFilePhysicalAttributesList
from SamException import SamExceptions

from SamUserApi_CommandInterface import SamUserApi_CommandInterface

class getFileDescendants(SamUserApi_CommandInterface):
    def __init__(self):
        SamUserApi_CommandInterface.__init__(
            self,
            commandParameters = {
            'verb' : ['get children'],
            'allowedOptions' : ['%s=' % SAM.attrDataTier,
                                '%s=' % SAM.attrAppName,
                                '%s=' % SAM.attrAppVersion,
                                'since=',
                                'before=',
                                ],
            'allowedArgCount' : 1,
            'apiReturns' : DataFilePhysicalAttributesList,
            'description' : {'args' : 'filename for which you which to see the specified descendants',
                             SAM.attrDataTier : 'restrict the list to the descendants with this dataTier',
                             SAM.attrAppName : 'restrict the list to the descendants produced by this appName',
                             SAM.attrAppVersion : 'restrict the list to the descendants produced by this appVersion',
                             'since' : 'restrict the list to those files created since this date',
                             'before' : 'restrict the list to those files created before this date',
                             },
            'restrictedTypeOptions' : {'since' : SamTime,
                                       'before' : SamTime},
            'helpText' : """
            Get a list of descendants of the given file, filtered by
            dataTier, application, and/or creation date.
            """,
            })

    def cliProcessResults(self, listOfFiles):
        if listOfFiles:
            listOfFileNames = map(lambda(x): x.getFileName(), listOfFiles)
            stringifiedListOfFiles = string.join(listOfFileNames, '\n')
        else:
            stringifiedListOfFiles = "No files found."
        return SamUserApi_CommandInterface.cliProcessResults(self, stringifiedListOfFiles)

    def implementation(self, argDict, argList):
        self.db = DbDerivedClient('DbDataFiles')
        parentFile = argList[0]
        dataTier = argDict.get(SAM.attrDataTier)
        appName = argDict.get(SAM.attrAppName)
        appVersion = argDict.get(SAM.attrAppVersion)
        since = argDict.get('since')
        before = argDict.get('before')

        theWhereDict = {}

        # which filters?
        if dataTier is not None:
            dbDataTiers = DbDerivedClient('DbDataTiers')
            try:
                dataTierId = dbDataTiers.getOne(['dataTierId'], {'dataTier':dataTier})
            except SamExceptions.DbMinRowsException:
                raise SamExceptions.DataTierNotFound("No such dataTier: '%s'" % dataTier)
            theWhereDict.update({'dataTierId':dataTierId})

        if appName is not None or appVersion is not None:
            dbAppFamilies = DbDerivedClient('DbApplicationFamilies')
            if appName is not None and appVersion is not None:
                rows = dbAppFamilies.get(['applFamilyId'], {'applName':appName,
                                                            'version' :appVersion})
            elif appName is not None:
                rows = dbAppFamilies.get(['applFamilyId'], {'applName':appName})
            else:
                rows = dbAppFamilies.get(['applFamilyId'], {'version' :appVersion})
            if rows and rows[0]:
                applFamilyIdList = map(lambda(x): x[0], rows)
                theWhereDict.update({'applFamilyId': {'oper' : 'in (%s)' %
                                                      string.join(map(lambda(x): str(x), applFamilyIdList), ', ')}})

        if before is not None or since is not None:
            if before is not None and since is not None:
                theWhereDict.update({'createDate' : {'oper' : "between '%s' and '%s'" %
                                                     (since.byFormat(SAM.SamTimeFormat_DateFormat),
                                                      before.byFormat(SAM.SamTimeFormat_DateFormat))}})
            elif before is not None:
                theWhereDict.update({'createDate' : {'oper' : "< '%s'" % before.byFormat(SAM.SamTimeFormat_DateFormat)}})
            else:
                theWhereDict.update({'createDate' : {'oper' : "> '%s'" % since.byFormat(SAM.SamTimeFormat_DateFormat)}})
            

        # get the parentFileId
        try:
            parentFileId = self.db.getOne(['fileId'], {'fileName':parentFile})
        except SamExceptions.DbMinRowsException:
            raise SamExceptions.DataFileNotFound("No such file: '%s'" % parentFile)

        # all children of all generations:
        allGenerations = []

        # loop through the parents and all children:
        children = self._getChildrenOf(parentFileId)
        allGenerations += children

        while children != []:
            new_children = []
            for fileId in children:
                parent = fileId
                offspring = self._getChildrenOf(parent)
                allGenerations += offspring
                new_children += offspring
            children = new_children

        # ok, now we have a list of allGenerations, filter
        returnList = DataFilePhysicalAttributesList()
        if allGenerations:
            allGenerationsFileIdList = string.join(map(lambda(x): str(x), allGenerations), ', ')
            theWhereDict.update({'fileId' : {'oper' : 'in (%s)' % allGenerationsFileIdList}})

            fileIdList = []
            rows = self.db.get(['fileId'], theWhereDict)

            # now convert to datafilePhysicalAttributesList
            if rows and rows[0]:
                fileIdList = map(lambda(x): x[0], rows)

                theQuery = """
                select distinct df.file_id, df.file_name, df.file_size_in_bytes, df.crc_value, crc.crc_type_desc, df.event_count
                  from data_files df, crc_types crc
                 where df.file_id in (%s)
                   and df.crc_type_id = crc.crc_type_id
                   """ % string.join(map(lambda(x): str(x), fileIdList), ', ')
                rows = self.db.query(theQuery)
                if rows and rows[0]:
                    returnList = map(lambda(x): DataFilePhysicalAttributes(fileId=x[0],
                                                                           fileName=x[1],
                                                                           fileSize="%sB" % x[2],
                                                                           crcValue=x[3],
                                                                           crcType=x[4],
                                                                           eventCount=x[5]), rows)
        return returnList

    def _getChildrenOf(self, fileId):
        """
        Get all direct children of the specified fileId
        """
        returnList = []
        theQuery = """
        select fl.file_id_dest
          from file_lineages fl
         where fl.file_id_source = %s
         """ % fileId
        rows = self.db.query(theQuery)
        if rows and rows[0]: returnList = map(lambda(x): x[0], rows)
        return returnList


def main(args): return getFileDescendants().dispatch(args)
if __name__ == "__main__": sys.exit(main(sys.argv[1:]))