Captain Slog
This is a simple workspace which logs times into a text file on the Desktop called “SHOW LOGGER”. You can copy and paste the example cues into your own workspaces.
How it works
The workspace uses four scripts which only vary in the words they add to the log. Shown below is the intermission script. The script is adapted from one found on http://www.macosxautomation.com/.
set themessage to "Intermission"
set this_data to ((current date) as string) & space & themessage & return
set target_file to (((path to desktop folder) as string) & "SHOW LOGGER")
set append_data to true
try
set the target_file to the target_file as string
set the open_target_file to open for access file target_file with write permission
if append_data is false then set eof of the open_target_file to 0
write this_data to the open_target_file starting at eof
close access the open_target_file
return true
on error
try
close access file target_file
end try
return false
end try
Logging with calculations
We can build on the above workspace to add calculated running times to the log.
You could try to log running times by using Script cues to start timers at the beginning of each act and stop them at the end, but this solution would fail if the workspace were ever panicked during the act; not an altogether unlikely situation.
A more resilient solution is to record the epoch time at each significant moment (top of each act, end of each act, etc.), and refer back to these recorded times when we’re done logging.
On Unix systems, of which macOS is one variety, the Epoch time is defined as the number of seconds since midnight at the start of January 1, 1970. While this is quite esoteric, it turns out to be a pretty convenient way to keep track of time.
We’re going to record times into the Notes field of various cues, so we’ll need to have a numbering convention, e.g. LOG1, LOG2, etc., so that we can retrieve each value when it’s time to do the calculations.
When we want to calculate the total running time, or the running time of any
section of the show, we’ll simply subtract the time stored in the log cue at the
beginning of the section from the time stored in the log cue at the end of the
section. For example, if we put cue LOG1
at the top of act one, and LOG2
at
the end of act one, then
((notes **of** _cue_ "LOG2") - (notes **of** _cue_ "LOG1"))
will give us the duration of act one measured in seconds. Following that logic,
((notes **of** _cue_ "LOG2") - (notes **of** _cue_ "LOG1")) + ((notes **of** _cue_ "LOG4") - (notes **of** _cue_ "LOG3"))
will give us the running time of a two-act play without intermission.
All we need to do then is convert from epoch seconds to a HH:MM:SS string.
Here’s the workspace:
And here’s the log output from that workspace:
Let’s break that down, script by script.
Show file name
The show file name is taken from the actual file name of the QLab workspace.
using terms from application "Finder"
set themessage to "-----------------------------------------------------------" & return & "SHOW FILE: " & name of front document of application id "com.figure53.qlab.3" as string
end using terms from
Then the data is written to the logging file:
set this_data to themessage & return & return
set target_file to (((path to desktop folder) as string) & "SHOW LOGGER")
set append_data to true
try
set the target_file to the target_file as string
set the open_target_file to open for access file target_file with write permission
if append_data is false then set eof of the open_target_file to 0
write this_data to the open_target_file starting at eof
close access the open_target_file
return true
on error
try
close access file target_file
end try
return false
end try
Logging cues
The logging cue scripts are all identical with the exception of the text which they log and cues which they reference. Here’s the script which logs the curtain up time:
set themessage to "Curtain Up"
set thedate to current date
set this_data to (thedate as string) & space & themessage & return
set target_file to (((path to desktop folder) as string) & "SHOW LOGGER")
set append_data to true
try
set the target_file to the target_file as string
set the open_target_file to open for access file target_file with write permission
if append_data is false then set eof of the open_target_file to 0
write this_data to the open_target_file starting at eof
close access the open_target_file
tell application id "com.figure53.qlab.3" to tell front workspace
set notes of cue "LOG1" to (do shell script "date +%s") as integer
end tell
return true
on error
try
close access file target_file
end try
return false
end try
In the above example, themessage
is set to “Curtain up”. The time as a string
is appended to this and the whole string is written to the log file. Then the
epoch seconds are obtained by running a shell script:
set notes of cue "LOG1" to (do shell script "date +%s") as integer
Which gets the epoch seconds and writes them to the Notes field in cue LOG1
.
Adding more logging cues
If we want to modify this script to add a log when the house opens, we first
copy and paste cue LOG1
to create a new log cue. We’ll number the new cue
LOG0
. Then, we change the first line of the script to:
set themessage to "House Open"
and the shell script line to:
set notes of cue "LOG0" to (do shell script "date +%s") as integer
Reporting cues
The reporting cues are again, similar:
tell application id "com.figure53.qlab.3" to tell front workspace
try
set thesecs to (notes of cue "LOG2") - (notes of cue "LOG1")
set theHours to (thesecs div 3600)
set theRemainderSeconds to (thesecs mod 3600) as integer
set theMinutes to (theRemainderSeconds div 60)
set theRemainderSeconds to (theRemainderSeconds mod 60)
if length of (theHours as text) = 1 then
set theHours to "0" & (theHours as text)
end if
if length of (theMinutes as text) = 1 then
set theMinutes to "0" & (theMinutes as text)
end if
if length of (theRemainderSeconds as text) = 1 then
set theRemainderSeconds to "0" & theRemainderSeconds as text
end if
set notes of cue "LOGA" to (theHours & ":" & theMinutes & ":" & theRemainderSeconds)
end try
set this_data to return & " ACT1 Running Time: " & notes of cue "LOGA" & return
set target_file to (((path to desktop folder) as string) & "SHOW LOGGER")
set append_data to true
try
set the target_file to the target_file as string
set the open_target_file to open for access file target_file with write permission
if append_data is false then set eof of the open_target_file to 0
write this_data to the open_target_file starting at eof
close access the open_target_file
return true
on error
try
close access file target_file
end try
return false
end try
end tell
A variable, thesecs
, is set to the value of the calculation. In the above
example, (notes of cue "LOG2") - (notes of cue "LOG1")
, gets the running time
of act one in seconds by subtracting the value of the notes field of cue LOG1
from the value of the notes of cue LOG2
. The result, which is in seconds, is
then converted to a string to give the duration in a more readable format,
HH:MM:SS. This is then put into a data string with another string identifying
what the duration refers to, and written to the log file.
Adding reporting cues
Again, these scripts are easily modified to report other durations. For example,
to create a cue which logs the length of the pre-show (house open to curtain
up), first copy and paste cue LOGA
to create a new cue which we’ll number
LOGZ
. Then change the calculation line to:
set thesecs to (notes of cue "LOG1") - (notes of cue "LOG0")
Also, replace the two instances of cue “LOGA” in the script to cue “LOGZ”. Then change the text “ACT1 Running Time: ” to “PRESHOW Running Time: ” (note the trailing spaces inside the quotation marks. Those are important.)
Wait times
To enable the logging and reporting cues to be cut and pasted anywhere in the cue list, every cue contains the full script instructions to open the log file, write the data, and close the log file. Because of this, log cues run back to back need time between them to allow the file to fully close before the second log cue re-opens it. If you want to run multiple log cues back to back, put then in a Group cue set to start all children simultaneously, and use pre-waits to separate them by a second or so.
Refining the scripts
The flexibility that comes from making each log cue self-contained and capable of doing everything it needs to do is useful. On the other hand, if we have a lot of log cues and we’re making complex reports, there would be quite a lot of duplicated code. If we wanted to make an adjustment, like changing the path or name of the log file, or add a feature like sending email notifications with durations as soon as they are logged, it would be a considerable amount of work to change all the duplicate scripts.
If we were writing a conventional computer program, we would modularize our code by separating each task into a function or subroutine. One function might handle writing to a file, another would convert seconds to an HH:MM:SS string, etc, and we’d pass data between these chunks of code using variables. Script cues in QLab, however, only preserve variables within themselves; there is no straightforward way to have blocks of AppleScript shared between Script cues.
However, much as we used the Notes field of cues to hold time data, we can also use Notes fields to pass variables between Script cues. This lets us treat individual Script cues as reusable blocks of code. The main disadvantage of working this way is that these Script cues become interdependent and cannot function without each other, and moving them between workspaces can be cumbersome. This isn’t hard to get around, and it comes with enough benefit that it’s worth the effort.
This third pass at logging is built on the idea of modular, interdependent Script cues. It also refines the logging and reporting cues so that any information that needs to be changed in duplicated cues is put at the top of the scripts, clearly annotated, to make it as straightforward as possible.
The new cue list with the added subroutine Script cues looks like this:
The new cues, CALC1
and WRITE
, can be anywhere in the workspace. You could
create a new cue list called something like “logging helpers” if you didn’t want
them in your main cue list. If you were going to use this extensively, you could
put the whole of this list in a separate logging cue list in your template
workspace, and then just cut and paste the LOG cues into the main cue list as
needed. Alternately, you could use Start cues in the main cue list to trigger
the Script cues as needed.
The cue CALC1
contains the script to convert epoch seconds to a string in
HH:MM:SS format:
set mycue to "CALC1"
tell application id "com.figure53.qlab.3" to tell front workspace
try
set thesecs to notes of cue mycue
set theHours to (thesecs div 3600)
set theRemainderSeconds to (thesecs mod 3600) as integer
set theMinutes to (theRemainderSeconds div 60)
set theRemainderSeconds to (theRemainderSeconds mod 60)
if length of (theHours as text) = 1 then
set theHours to "0" & (theHours as text)
end if
if length of (theMinutes as text) = 1 then
set theMinutes to "0" & (theMinutes as text)
end if
if length of (theRemainderSeconds as text) = 1 then
set theRemainderSeconds to "0" & theRemainderSeconds as text
end if
set notes of cue mycue to (theHours & ":" & theMinutes & ":" & theRemainderSeconds)
end try
end tell
It uses its own Notes field to receive the value from the cue which triggers it, and then places the calculated result back into its own Notes field.
The cue WRITE
works similarly; it writes the contents of its own Notes field
to the log file:
set mycue to "WRITE"
tell application id "com.figure53.qlab.3" to tell front workspace
set this_data to notes of cue mycue & return
set target_file to (((path to desktop folder) as string) & "SHOW LOGGER")
set append_data to true
try
set the target_file to the target_file as string
set the open_target_file to open for access file target_file with write permission
if append_data is false then set eof of the open_target_file to 0
write this_data to the open_target_file starting at eof
close access the open_target_file
return true
on error
try
close access file target_file
end try
return false
end try
end tell
Here’s the much shorter script for one of the logging cues:
set mycue to "LOG1"
set themessage to "Curtain Up"
tell application id "com.figure53.qlab.3" to tell front workspace
set notes of cue mycue to (do shell script "date +%s") as integer
set thedate to current date
set this_data to (thedate as string) & space & themessage & return
set the notes of cue "WRITE" to this_data
start cue "WRITE"
end tell
It formats the data it wants to write into a string, and sets the notes of cue
WRITE
to that string. It then starts cue WRITE
.
The reporting scripts look like this:
set mycue to "LOGA"
tell application id "com.figure53.qlab.3" to tell front workspace
try
set mystring to q name of cue mycue
repeat while the length of mystring is less than 50
set mystring to " " & mystring
end repeat
set thesecs to (notes of cue "LOG2") - (notes of cue "LOG1")
set the notes of cue "CALC1" to thesecs
start cue "CALC1"
delay 0.2
set the notes of cue mycue to the notes of cue "CALC1"
set the notes of cue "WRITE" to return & mystring & ": " & notes of cue mycue & return
start cue "WRITE"
end try
end tell
This script calculates duration just like the previous examples, and sets the
notes field of cue CALC1
to the result. Cue CALC1
is then started, which
converts the duration into an HH:MM:SS string then sets its own notes field to
that string.
The reporting cue then waits two tenths of a second to let CALC1
complete,
then grabs the result, formats it nicely with the leading spaces produced by the
repeat loop, and stores the result in the Notes field of the WRITE
cue. Then
it starts the WRITE
cue, which does the actual writing.