Ok, here it is, my completed script. As it is posted, the script asks the user to select a job pool (we make a new pool for each project or job in the shop) and then it returns all the total render time for all completed jobs in that pool. In a meeting there was the question of “can we get a little bit of human double checking to make sure this thing is staying on course”? So the user running the script can verify the number of completed jobs that the Deadline Monitor is displaying vs the # of completed jobs the script found. (This is an optional step, but made some feel more comfortable that our computer friends weren’t just making stuff up!) Finally the script can email and/or save the spreadsheet that was created to the user’s specified locations or address.
There are only 2 prerequisites that I can think of:
- Running a version of Deadline that can make use of Python .net (5.1 or later I believe)
- Install xlwt libraries from python-excel.org, I installed these into the Python folder on the repository and added a pointer to the folder in the Repository settings (its discussed in earlier posts in this thread).
Feel free to use it how ever you wish, or if you are a novice you might learn a thing or 2 by poking through it since I’m a Python novice myself. Also, I’d love feedback if you anyone wants to school me on how to do things better, faster, smarter! I’m sure there are a million better ways to integrate Deadline with other billing tools, but for our small shop this gets the jobs done (for now).
brad
MY PROJECT!
[code]#Python.NET
from System.Collections.Specialized import *
from System.Drawing import *
from System.IO import *
from Deadline.Scripting import *
import os
import xlwt
from xlwt import Workbook, easyxf, Borders
from datetime import datetime
import shutil
Global variables
scriptDialog = None
location = ‘\\queue\DeadlineRepository\scripts\General\RenderBilling\output\’
filename = None
job = None
poollist = []
jobtimelist = []
totaltimelist = []
cmp = None
tt = None
runtime2 = None
def main():
global scriptDialog
job-pool selection window
scriptDialog = DeadlineScriptEngine.GetScriptDialog()
scriptDialog.SetSize( 400, 150 )
scriptDialog.SetTitle( "Job Selection" )
scriptDialog.SetIcon( Path.Combine( GetRootDirectory(), "scripts/General/RenderBilling/RenderBilling.ico" ) )
scriptDialog.AddRow()
scriptDialog.AddControl( "DefineTool1", "LabelControl", " * * BEFORE RUNNING SCRIPT, SET FILTER JOB POOL TO DESIRED JOB * *", 380, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DefineTool2", "LabelControl", "This tool will calculate the total render time for job billing.", 380, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "PoolLabel", "LabelControl", "Select the job:", 100, -1 )
scriptDialog.AddControl( "PoolBox", "PoolComboControl", "job", 200, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DummyLabel1", "LabelControl", "", 205, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DummyLabel2", "LabelControl", "Once you hit Select, be patient. Another window will appear with your total.", 385, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DummyLabel3", "LabelControl", "", 175, -1 )
selectButton = scriptDialog.AddControl( "SelectButton", "ButtonControl", "Select", 100, -1 )
selectButton.ValueModified += SelectButtonPressed
closeButton = scriptDialog.AddControl( "CloseButton", "ButtonControl", "Close", 100, -1 )
closeButton.ValueModified += CloseButtonPressed
scriptDialog.EndRow()
scriptDialog.ShowDialog( False )
def SelectButtonPressed( *args ):
global job
get job from selection box
job = scriptDialog.GetValue( "PoolBox" )
check to make sure a job was selected
if job == "none":
# pop up with error
scriptDialog.ShowMessageBox( " Please select a job, you can't calculate NONE. ", "Error Message")
else:
CalculateJob()
def CalculateJob( *args ):
global scriptDialog
global job
global location
global filename
global poollist
global jobtimelist
global totaltimelist
global cmp
global tt
scriptDialog.CloseDialog()
begin = datetime.now()
get list of job IDs for the pool that are completed, archived or not
filter = "jobPool="
filter += job
poolArgs = StringCollection()
poolArgs.Add("GetJobIdsFilterAnd")
poolArgs.Add( filter )
poolArgs.Add( "status=Completed" )
poollist = ClientUtils.ExecuteCommandAndGetOutput( poolArgs )
poollist = poollist.split()
if poollist == []:
NoHours(job)
else:
declaring some variables
tt = 0 # total job time tallied
jt = 0 # job time
cmp = len(poollist) # number of jobs that are completed
get time for each job
for i in range(len(poollist)):
jobid = str(poollist[i])
jobArgs = StringCollection()
jobArgs.Add("GetJobTaskTotalTime")
jobArgs.Add( jobid )
jobtime = ClientUtils.ExecuteCommandAndGetOutput( jobArgs )
d = float(jobtime[20:22])
h = float(jobtime[24:26])
m = float(jobtime[28:30])
s = float(jobtime[32:34])
jt = (86400*d+3600*h+60*m+s)
jt /= 3600
tt += jt
add job time to list
jobtimelist.append(jt)
add total job time to list
totaltimelist.append(tt)
pop up if no hours exist
if tt == 0:
NoHours()
how long did this process take
end = datetime.now()
runtime = end - begin
round total render time to 2 decimal points
ttr = str(round(tt,2))
results and email window
scriptDialog = DeadlineScriptEngine.GetScriptDialog()
scriptDialog.SetSize( 450, 390 )
scriptDialog.SetTitle( "Results for job " + job )
scriptDialog.SetIcon( Path.Combine( GetRootDirectory(), "scripts/General/RenderBilling/RenderBilling.ico" ) )
scriptDialog.AddRow()
scriptDialog.AddControl( "DummyLabel9", "LabelControl", "", 8, -1 )
scriptDialog.AddControl( "QuickTotalLabel", "LabelControl", "Total render hours: ", 110, -1)
scriptDialog.AddControl( "QuickTotal", "ReadOnlyTextControl", ttr, 50, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DummyLabel8", "LabelControl", "", 100, -1 )
scriptDialog.EndRow()
scriptDialog.AddGroupBox( "GroupBox1", "Verification Information", True )
scriptDialog.AddRow()
scriptDialog.AddControl( "WhoLabel", "LabelControl", "Enter your name or initials: ", 140, -1)
scriptDialog.AddControl( "Whoisthis", "TextControl", "", 240, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "CheckNumbersLabel", "LabelControl", "Enter the number of completed jobs Deadline shows in the Monitor:", 330, -1)
scriptDialog.AddControl( "CheckNumbers", "TextControl", "", 50, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "ReadOnlyTextLabel", "LabelControl", "The script found this many completed jobs: ", 330, -1 )
scriptDialog.AddControl( "ReadOnlyTextBox", "ReadOnlyTextControl", str(cmp), 50, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddSelectionControl( "CheckBox", "CheckBoxControl", False, " Check the box if the script and Deadline match.", 400, -1 )
scriptDialog.EndRow()
scriptDialog.EndGroupBox( False )
scriptDialog.AddRow()
scriptDialog.AddControl( "DummyLabel7", "LabelControl", "", 100, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "ComboLabel", "LabelControl", "Select An Action:", 110, -1 )
scriptDialog.AddComboControl( "CBox", "ComboControl", "Save and Email", ("Save and Email","Save only","Email only","Discard results"), 320, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "SaveFolderLabel", "LabelControl", "Select folder:", 110, -1 )
scriptDialog.AddSelectionControl( "SaveFolder", "FolderBrowserControl", "", "", 320, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "EmailLabel", "LabelControl", "Enter email address:", 110, -1 )
scriptDialog.AddControl( "EmailAddress", "TextControl", "", 320, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "MultipleEmailSpacer", "LabelControl", "", 110, -1 )
scriptDialog.AddControl( "MultipleEmail", "LabelControl", "(Multiple addresses need to be comma seperated.)", 285, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DummyLabel5", "LabelControl", "", 205, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DefineTool10", "LabelControl", "Script run time: " + str(runtime)[:7], 380, -1)
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DummyLabel6", "LabelControl", "", 225, -1 )
selectButton = scriptDialog.AddControl( "SendButton", "ButtonControl", "OK", 100, -1 )
selectButton.ValueModified += SendButtonPressed
closeButton = scriptDialog.AddControl( "DiscardButton", "ButtonControl", "Discard", 100, -1 )
closeButton.ValueModified += DiscardButtonPressed
scriptDialog.EndRow()
scriptDialog.ShowDialog( False )
def SendButtonPressed( *args ):
global scriptDialog
global poollist
global jobtimelist
global totaltimelist
global cmp
global tt
global runtime2
begin = datetime.now()
email = scriptDialog.GetValue( “EmailAddress” )
action = scriptDialog.GetValue( “CBox” )
save = scriptDialog.GetValue( “SaveFolder” )
who = scriptDialog.GetValue( “Whoisthis” )
checknumbers = scriptDialog.GetValue( “CheckNumbers” )
checkbox = scriptDialog.GetValue( “CheckBox” )
scriptDialog.CloseDialog()
jobname = []
plugname = []
username = []
cmpdate = []
get time for each job
for i in range(len(poollist)):
jobid = str(poollist[i])
add job name to list
nameArgs = StringCollection()
nameArgs.Add("GetJobSetting")
nameArgs.Add(jobid)
nameArgs.Add("name")
jobname.append( ClientUtils.ExecuteCommandAndGetOutput( nameArgs ))
add plug-in to list
plugArgs = StringCollection()
plugArgs.Add("GetJobSetting")
plugArgs.Add(jobid)
plugArgs.Add("pluginname")
plugname.append( ClientUtils.ExecuteCommandAndGetOutput( plugArgs ))
add artist to list
userArgs = StringCollection()
userArgs.Add("GetJobSetting")
userArgs.Add(jobid)
userArgs.Add("username")
username.append( ClientUtils.ExecuteCommandAndGetOutput( userArgs ))
add completion date to list
dateArgs = StringCollection()
dateArgs.Add("GetJobSetting")
dateArgs.Add(jobid)
dateArgs.Add("completeddatetime")
cmpdate.append( ClientUtils.ExecuteCommandAndGetOutput( dateArgs ))
##### WRITE EXCEL DOC
define excel doc and sheet
wb = xlwt.Workbook()
ws = wb.add_sheet('RenderStats')
style1 = xlwt.XFStyle()
style1.num_format_str = ‘D-MMM-YY’
font2 = xlwt.Font()
font2.colour_index = 1
style2 = xlwt.XFStyle()
style2.font = font2
define a style for a bottom border
borders = Borders()
borders.bottom = Borders.MEDIUM
styleB = xlwt.XFStyle()
styleB.borders = borders
avgrentime = round(tt/cmp,2)
datemin = min(cmpdate)[:10]
datemax = max(cmpdate)[:10]
line01 = "Job: " + job
line02 = "Report date: " + str(datetime.now().strftime('%Y-%m-%d')) + " @ " + str(datetime.now().strftime('%H:%M'))
line03 = "Total render time: " + str(round(tt,2)) + " hours"
line04 = "Number of jobs submitted: " + str(cmp) + " jobs"
line05 = "Average render time: " + str(avgrentime) + " hours"
line06 = "Project dates: " + str(datemin) + " to " + str(datemax)
ws.write(0, 1, line01)
ws.write(1, 1, line02)
ws.write(2, 1, line03)
ws.write(3, 1, line04)
ws.write(4, 1, line05)
ws.write(5, 1, line06)
ws.write(1, 5, "Validation by: " + str(who))
ws.write(2, 5, "User entered job count: " + str(int(checknumbers)))
if int(checknumbers) == int(cmp):
ws.write(3, 5, "These numbers match")
else:
mistmatch = abs(int(checknumbers) - int(cmp))
ws.write(3, 5, "There is a mismatch of " + str(int(mistmatch)) + " jobs")
if checkbox == True:
ws.write(4, 5, str(who) + " verifies this data")
if checkbox == False:
ws.write(4, 5, "Data does not have user verification")
maxCell = len(poollist)
maxCell += 8
formulaCheck = "IF(COUNTIF(J9:J" + str(maxCell) + ",\"yes\")>0,\"Time discrepancies: Yes\",\"Time discrepancies: No\")"
ws.write(5, 5, xlwt.Formula(formulaCheck))
establish table headers
ws.write(7, 0, "#", styleB)
ws.write(7, 1, "ID", styleB)
ws.write(7, 2, "Name", styleB)
ws.write(7, 3, "Application", styleB)
ws.write(7, 4, "Artist", styleB)
ws.write(7, 5, "Date", styleB)
ws.write(7, 6, "Time", styleB)
ws.write(7, 7, "Deadline Tally", styleB)
ws.write(7, 8, "Excel Tally", styleB)
ws.write(7, 9, "Discrepancies?", styleB)
declare max length variables
maxId = 20
maxName = 8
maxApp = 8
maxArtist = 5
maxDate = 8
maxTime = 4
maxTallyDl = 10
maxTallyEx = 8
maxDscp = 10
start for loop here
for r in range(len(poollist)):
myRow = r + 8
myCell = r + 9
jobCounter = r+1
formula1 = "round(sum(L9:L" + str(myCell) + "),2)"
formula2 = "if(H" + str(myCell) + "-I" + str(myCell) + "=0,\"\",\"yes\")"
formulaJT = "round(L" + str(myCell) + ",2)"
formulaTT = "round(M" + str(myCell) + ",2)"
ws.write(myRow, 0, jobCounter)
ws.write(myRow, 1, poollist[r])
ws.write(myRow, 2, jobname[r])
ws.write(myRow, 3, plugname[r])
ws.write(myRow, 4, username[r])
ws.write(myRow, 5, cmpdate[r][:10])
ws.write(myRow, 6, xlwt.Formula(formulaJT))
ws.write(myRow, 7, xlwt.Formula(formulaTT))
ws.write(myRow, 11, jobtimelist[r], style2)
ws.write(myRow, 12, totaltimelist[r], style2)
ws.write(myRow, 8, xlwt.Formula(formula1))
discrepancy
ws.write(myRow, 9, xlwt.Formula(formula2))
find max lengths
if maxName < len(jobname[r]):
maxName = len(jobname[r])
if maxApp < len(plugname[r]):
maxApp = len(plugname[r])
if maxArtist < len(username[r]):
maxArtist = len(username[r])
if maxTime < len(str(round(jobtimelist[r],2))):
maxTime = len(str(round(jobtimelist[r],2)))
set column widths
ws.col(0).width = 1250
ws.col(1).width = (maxId+4)*256 # max letter length + 4
ws.col(2).width = (maxName+1)*256
ws.col(3).width = (maxApp+4)*256
ws.col(4).width = (maxArtist+4)*256
ws.col(5).width = (maxDate+4)*256
ws.col(6).width = (maxTime+4)*256
ws.col(7).width = (maxTallyDl+4)*256
ws.col(8).width = (maxTallyEx+4)*256
ws.col(9).width = (maxDscp+4)*256
spreadsheet NOT SAVED yet
filename = job + '_' + str(datetime.now().strftime('%Y-%m-%d-%H-%M')) + '.xls'
###### EXCEL FILE DONE
if action == "Save and Email":
wb.save(location + filename)
shutil.copy2(location + filename,save + filename)
emailArgs = StringCollection()
emailArgs.Add("-SendEmail")
emailArgs.Add("-subject")
emailArgs.Add("Render farm totals for " + job)
emailArgs.Add("-to")
emailArgs.Add( email )
emailArgs.Add("-message")
emailArgs.Add(line01 + "\r" + line02 + "\r" + line03 + "\r" + line04 + "\r" + line05 + "\r" + line06)
emailArgs.Add("-attach")
emailArgs.Add(location + filename)
ClientUtils.ExecuteCommand( emailArgs )
how long did this process take
end = datetime.now()
runtime2 = end - begin
TaskCompleted()
if action == "Save only":
wb.save(location + filename)
shutil.copy2(location + filename,save + filename)
how long did this process take
end = datetime.now()
runtime2 = end - begin
TaskCompleted()
if action == "Email only":
wb.save(location + filename)
emailArgs = StringCollection()
emailArgs.Add("-SendEmail")
emailArgs.Add("-subject")
emailArgs.Add("Render farm totals for " + job)
emailArgs.Add("-to")
emailArgs.Add( email )
emailArgs.Add("-message")
emailArgs.Add(line01 + "\r" + line02 + "\r" + line03 + "\r" + line04 + "\r" + line05 + "\r" + line06)
emailArgs.Add("-attach")
emailArgs.Add(location + filename)
ClientUtils.ExecuteCommand( emailArgs )
how long did this process take
end = datetime.now()
runtime2 = end - begin
TaskCompleted()
if action == "Discard results":
CloseButtonPressed()
def DiscardButtonPressed( *args ):
global scriptDialog
scriptDialog.CloseDialog()
def CloseButtonPressed( *args ):
global scriptDialog
scriptDialog.CloseDialog()
def NoHours( *args ):
global scriptDialog
global job
scriptDialog.CloseDialog()
scriptDialog.ShowMessageBox( " There are no completed renders for job " + x + " ", “No Render Time”)
def TaskCompleted():
global scriptDialog
global runtime2
scriptDialog.CloseDialog()
scriptDialog.ShowMessageBox( " Task completed successfully. (Script run time: " + str(runtime2)[:7] + ") ", “Finished!!!”) [/code]