'''
This is a sample filewatcher written in python

Example setup:

create or alter job definition SYSTEM.'TEST'.'PROCESS_FILE'
with
	aging = none,
	min priority = none,
	children = none,
	dependency mode = all,
	environment = 'SERVER@LOCALHOST',
	errlog = '${JOBID}.log' NOTRUNC,
	group = 'ADMIN',
	inherit grants = (DROP, EDIT, MONITOR, OPERATE, SUBMIT, VIEW, RESOURCE),
	kill program = none,
	logfile = '${JOBID}.log' NOTRUNC,
	MASTER,
	priority = 50,
	parameters = (
		'OBJECT_NAME'
	),
	profile = 'STANDARD',
	required = none,
	rerun program = none,
	resource = none,
	runtime = 0,
	runtime final = 0,
	run program = 'SDMSPOPUP.BAT "\\"$EVENT_TYPE\\"" "\\"$OBJECT_NAME\\"" -c "\\"?:1=FAILURE:0=SUCCESS\\""',
	NOSUSPEND,
	type = JOB,
	workdir = none;

alter job definition SYSTEM.'TEST'.'PROCESS_FILE'
alter parameter = (
		'OBJECT_NAME'
			PARAMETER default = '[GROUPED]'
	);
	
create or alter job definition SYSTEM.'TEST'.'PROCESS_FILES'
with
	aging = none,
	min priority = none,
	children = none,
	dependency mode = all,
	errlog = none,
	group = 'ADMIN',
	inherit grants = (DROP, EDIT, MONITOR, OPERATE, SUBMIT, VIEW, RESOURCE),
	kill program = none,
	logfile = none,
	MASTER,
	nicevalue = 0,
	parameters = none,
	profile = 'STANDARD',
	required = none,
	rerun program = none,
	resource = none,
	runtime final = 0,
	run program = none,
	NOSUSPEND,
	type = BATCH,
	workdir = none;

alter job definition SYSTEM.'TEST'.'PROCESS_FILES'
add or alter children = (
	SYSTEM.'TEST'.'PROCESS_FILE'
		alias = none
		CHILDSUSPEND
		nicevalue = 0
		dynamic
		translation = none
		ignore dependency = none
);

create or alter job definition SYSTEM.'TEST'.'FILEWATCHER_LOCALHOST'
with
	aging = none,
	min priority = none,
	children = none,
	dependency mode = all,
	environment = 'SERVER@LOCALHOST',
	errlog = '${JOBID}.log' NOTRUNC,
	group = 'ADMIN',
	inherit grants = (DROP, EDIT, MONITOR, OPERATE, SUBMIT, VIEW, RESOURCE),
	kill program = 'taskkill /PID $PID /F',
	logfile = '${JOBID}.log' NOTRUNC,
	MASTER,
	priority = 50,
	parameters = none,
	profile = 'STANDARD',
	required = none,
	rerun program = none,
	resource = none,
	runtime = 0,
	runtime final = 0,
	run program = '"c:\\Python27\\python" "\\"y:\\independIT\\python file watcher\\filewatcher.py\\""',
	NOSUSPEND,
	type = JOB,
	workdir = none;

create or alter watch type filewatcher
with
        parameter = (
                config directory,
                config pattern,
                config cycle = '300',
                value size,
                value time
        );
create or alter object type TEMP_OUT_WATCHER watch type FILEWATCHER
with
	group = 'PUBLIC',
	recreate = CREATE,
	watcher = SYSTEM.'TEST'.'FILEWATCHER_LOCALHOST',
	parameter = (
		'DIRECTORY' = 'C:\\TEMP',
		'PATTERN' = '*.OUT',
		'CYCLE' = '200'
	),
	delete after 5 DAY,
	event delete after 5 DAY;

create or alter trigger 'PROCESS_FILES' on object monitor TEMP_OUT_WATCHER
with
	group = 'PUBLIC',
	active,
	master,
	nosuspend,
	events = (create, change, delete),
	group event,
	main SYSTEM.'TEST'.'PROCESS_FILES',
	submit SYSTEM.'TEST'.'PROCESS_FILE'
;
'''

import os
import fnmatch
import time
import sys

# needs PYTHONPATH to be set to $BICSUITEHOME\zope
import sdms

__author__ = 'independIT integrative Technologies GmbH'

verbose = False
args = sys.argv
if len(args) == 2:
    if args[1] in ['-v','--verbose']:
        verbose = True

lasttimes = {}

ERRORCYCLE = 10 # retry commands after 10 seconds on error

def watch(name):
    if not lasttimes.has_key(name):
        lasttimes.update ( { name : 0 } )

    monitor = sdms.SDMSCommandWithSoc(soc,"SHOW OBJECT MONITOR '" + name + "'")
    if monitor.has_key('ERROR'):
        print "Error: " + str(result['ERROR'])
        sys.stdout.flush()
        sdms.SDMSCommandWithSoc(soc,"ALTER JOB WITH WARNING = 'Error fetching object monitor data!'")
	return ERRORCYCLE

    conf = {}
    for parameter in monitor['DATA']['RECORD']['PARAMETERS']['TABLE']:
        conf.update( { parameter['NAME'] : parameter['VALUE'] } )

    now = int(time.time())
    lasttime = lasttimes[name]
    cycle = int(conf['CYCLE'])
    nexttime = lasttime + cycle
    if nexttime > now:
        return nexttime - now
    else:
        lasttimes.update( { name : now } )

    if verbose:
    	print "Processing for Object Monitor " + name        
        sys.stdout.flush()

    # get file entries
    i = 0
    files = []
    directory = os.path.expandvars(conf['DIRECTORY'])

    if verbose:
    	print "Directory : " + directory
        sys.stdout.flush()

    for filename in os.listdir(directory):
        if fnmatch.fnmatch(filename, conf['PATTERN']):
            path = os.path.abspath(conf['DIRECTORY'] + '/' + filename)
            file = { 'NAME' : path,
                     'SIZE' : str(os.path.getsize(path)),
                     'TIME' : str(os.path.getmtime(path))
            }
            if verbose:
            	print "Found file " + filename + " (SIZE = " + file['SIZE'] + ", TIME = " + file['TIME'] + ")"
                sys.stdout.flush()

            files.append(file)
            i = i + 1
    if verbose:
    	print str(i) + " files found"
    	sys.stdout.flush()

    # report to object monitoring
    cmd = "ALTER OBJECT MONITOR '" + name + "' INSTANCE = ("
    sep = ""
    for file in files:
        cmd = cmd + sep + \
              "'" + file['NAME'] + \
              "' ('SIZE' = '" + file['SIZE'] + \
              "', 'TIME' = '" + file['TIME'] + "')"
        sep = ","
    cmd = cmd + ")"
    result = sdms.SDMSCommandWithSoc(soc,cmd)
    if result.has_key('ERROR'):
        print "Error: " + str(result['ERROR'])
        sys.stdout.flush()
        sdms.SDMSCommandWithSoc(soc,"ALTER JOB WITH WARNING = 'Error reporting objects to object monitoring!'")
	return ERRORCYCLE

    return cycle

# get environment
host  = os.getenv('SDMSHOST')
port  = os.getenv('SDMSPORT')
jobid = os.getenv('JOBID')
key   = os.getenv('KEY')

while True:
    # open connection
    soc = sdms.SDMSJobConnectionOpen(host,port,jobid,key,session='filewatcher.py')

    # get the object monitors
    monitors = sdms.SDMSCommandWithSoc(soc,"LIST OBJECT MONITOR")
    if monitors.has_key('ERROR'):
        print "Error: " + str(result['ERROR'])
        sys.stdout.flush()
        sdms.SDMSCommandWithSoc(soc,"ALTER JOB WITH WARNING = 'Error getting list of object monitors to watch!'")
        minsleeptime = ERRORCYCLE
    else:
        minsleeptime = 60 * 60 # we wake up at least every hour 
        for line in monitors['DATA']['TABLE']:
            sleeptime = watch(line['NAME'])
            if sleeptime < minsleeptime:
                minsleeptime = sleeptime

    # close connection
    sdms.SDMSConnectionClose(soc)

    if verbose:
        print "Sleeping for " + str(minsleeptime) + " seconds ..."
        sys.stdout.flush()

    time.sleep(minsleeptime)
    
