I have an AppleScript that I run once a minute via a cron job that tracks my current desktop. I use this solution for the following purposes:
- I use each desktop for work on a different project and switch between them to work on those different projects. (Thus, the ability to track time spent on a desktop is a great stand-in for project time tracking.)
- Each project uses a mostly overlapping assortment of the same set of apps, so app trackers are wholly insufficient for app tracking being a stand-in for work on a project.
There are a number of hurdles to overcome when you want to track time spent on different desktops:
- Desktops cannot be named or otherwise identified by the OS via AppleScript. You can't even get the desktop number.
- There are no reliable third party tools to track time spent on each desktop.
- Using windows of different apps on each desktop to "label" desktops cannot typically be read in mission control to identify desktops manually and updating the desktop image is not an easy solution when the projects on the desktops frequently change in name and concurrent number.
- If using app windows to label desktops, some apps do not return their windows to their previous desktop after reboot.
- Window-based "desktop labels" can get buried among all the windows on a desktop, making it difficult to always be able to identify a desktop manually.
To label desktops, I chose to put the project name in a huge font in various Stickies windows, so that it's easily legible in the desktop thumbnails in Mission Control. This can be used to mitigate a number of the issues above:
- I hide the stickie window behind my Dock so that it's rarely buried by other windows in mission control.
- I duplicate the desktop name in small superscripted text that pokes out from under the left side of the dock so that I can almost always identify the current desktop outside of mission control.
- In order to identify the correct stickies window (using AppleScript) that "labels" a desktop, I append "dtop" to the project name in an extremely (i.e. invisible) font, so if I have multiple Stickies windows on a desktop, I know which one has the project name.
- The titlebar in Stickies is small enough that you can still see the superscripted label when you put the Stickie under the dock. And in mission control, the dock is not visible in the desktop thumbnails, making the large font easily legible in each thumbnail.
Here is an example screenshot of what the desktop thumbnails look like with the stickies. Note how all the app windows are typically above the dock, making the stickie windows easily viewed and legible:
The only issue with Stickies (in my experience) is that after reboot, all Stickies windows end up on the first desktop. I wrote a separate AppleScript to restore their desktop distribution (but that's a separate question).
To track the current desktop, I have a rather sophisticated script. I won't get into the details, because they are ancillary to the question, but this is the "function" I use to get the current desktop. Note, this assumes that the project name contains no spaces (i.e. it's just one word). You can customize it to change this, if you wish. Here is the AppleScript that, when run, results in the desktop name. To keep things simple, I removed the error handling.
set dtop to my getCurrentDesktop()
on getCurrentDesktop()
-- dtopstr = the string anywhere on the first line of the sticky that identifies it as containing the desktop name
set dtopstr to "dtop"
set dname to "unmonitored" -- Default desktop name (i.e. "no stickie")
tell application "System Events"
--obtain the stickie with the desktop name
set dstr to name of first item of (windows of application process "Stickies" of application "System Events" whose name contains dtopstr)
--Parse the desktop name from the stickie
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to " "
set dname to first item of (dstr's text items)
set AppleScript's text item delimiters to astid
end tell
return dname
end getCurrentDesktop
Note that there are a number of fancy features I added to the script. I track cursor movement to identify idle time. I designated a few apps that are project independent, such as:
- Zoom
- Mail
- Slack
- Calendar
- Certain Safari windows with certain names, like ones associated with HR, etc
I categorize those apps using a data structure and count the time in those apps based on whether the apps are simply running or are "frontmost". Here is an example of that datastructure initialization in AppleScript:
set customStatusChecks to {¬
{"meeting", ¬
{¬
{"running", "zoom.us", {"Zoom Meeting"}}}}, ¬
{"communication", ¬
{¬
{"front", "Mail", {}}, ¬
{"front", "gen-help", {}}, ¬
{"front", "Slack", {}}}}, ¬
{"planning", ¬
{¬
{"front", "Calendar", {}}, ¬
{"front", "Trello", {}}}}, ¬
{"overhead", ¬
{¬
{"front", "Princeton IT Self Service", {}}, ¬
{"front", "Safari", {"Holiday Schedule", "OIT Store", "Self Service", "TigerCard", "Tiger Transit"}}}}}
I don't want to get into all of the details of the fancy features I added, but to summarize them, just have a look at the main body of code, which is fairly short. I set the return to a different variable and at the end, I just join every datum as a tab-delimited string:
set customStatuses to my getCustomStatuses(customStatusChecks)
set dtop to my getCurrentDesktop()
set secs to my getSecondsSinceEpoch()
set {mpos, mntr} to my getMousePosition()
set {capp, cwin} to my getCurrentApp()
set lmsg to join(tab, {secs, mpos, dtop, mntr, capp, cwin, (every item of customStatuses)})
Here is a sample of the project log file:
1660158662 1240,362 TRACEBASE DELL P2721Q, (1) Safari Update README.md by aleifer · Pull Request #109 · PrincetonUniversity/neuron-graph none none none none
1660158722 1664,471 TRACEBASE DELL P2721Q, (1) Safari Update README.md by aleifer · Pull Request #109 · PrincetonUniversity/neuron-graph none none none none
1660158782 150,909 TRACEBASE DELL P2721Q, (1) Electron peak_group_label.py — tracebase none none none none
1660158842 329,332 TRACEBASE DELL P2721Q, (1) Terminal Terminal — ttys000 none none none none
1660158902 822,532 TRACEBASE DELL P2721Q, (1) Terminal Terminal — ttys000 none none none none
1660158962 974,305 TRACEBASE DELL P2721Q, (1) Electron test_models.py — tracebase none none none none
1660159023 1617,1361 TRACEBASE DELL P2721Q, (1) Slack Slack | funconn | Princeton NPLC none communication none none
1660159083 1823,610 PRODUCTIVITY DELL P2721Q, (1) TextEdit Untitled 67 none none none none
1660159143 504,875 PRODUCTIVITY DELL P2721Q, (1) TextEdit project_time_tracking_notes.txt none none none none
Note that is supports multiple monitors (reported in the cursor position) and it reports the status of each custom-tracked app/window from the customStatusChecks
data structure as the last set of columns.
To set it up via cron job for weekdays, you add this line to crontab -e
:
* * * * MON-FRI osascript /Users/<username>/logProjectActivity.scpt >> /Users/<username>/project_log.txt 2>> /Users/<username>/project_log.err
This runs the script once a minute on weekdays.
To be able to use the data, I wrote a perl script to turn the tab-delimited output project_log.txt
into different types of bar plots to depict how much time I spend on each project (i.e. desktop) over a period of time (e.g. the past week). Here's an example of the output (also a separate question):
Note that in the second plot, I set a limit for the length of each day. It automatically fills with idle time so that each day is the same length, but if I work overtime, you can see that the length of that day is longer.
I really should make a github repo for this code. PM me to get a full copy of the script. I haven't edited it yet to make it distributable. It has a few hard-coded paths in it and it has my app categorizations that pertain specifically to my job. All that would have to be customized per person.
As a side-note, I haven't checked on my project log in some time, and it hasn't apparently been logging for awhile because one of the directory names changed in the path to the log, but I'll be fixing that today.^
^ I had a "typo" in the path I used. My project log is still good and active. Though I will note that there is an issue with the identification of some apps. For example, the "VS Code" app reports as "Electron", and my Fluid.app apps, while they have different names, all report as "Fluid". I have fixed this in some of my other AppleScripts, just not this one.
UPDATE 2/28/2024: I just posted the log script as a gist. I have not yet published the perl script that creates the plots:
~logProjectActivity.osa~
See UPDATE 3/17/2024
below.
UPDATE 3/7/2024: I just pulled up my perl script that generates the plots and I noted that it processes the customStatusChecks
using left to right precedence. I'd forgotten about that. It takes the first non-"none", non-"unknown" custom state on each line as the "active project" for that minute. If they are all "none", "unknown", or empty, it falls back to the desktop project name.
The perl script is named timeTrackingPlots.pl
. It's usage:
$ perl timeTrackingPlots.pl
timeTrackingPlots.pl [OPTIONS]
<file*...>... Input file(s).
-c <sfx> [.1709822965.days.txt] Outfile suffix appended to file names supplied to [-i].
-p <sfx> [.1709822965.projects.txt] Outfile suffix appended to file names supplied to [-i].
-d <int> [7] Number of days to include.
--run Run the script.
--help Print general info and file formats.
--extended [<cnt>] Print detailed usage.
Example:
tail -n 11000 project_log.txt | perl timeTrackingPlots.pl
(I tail the log file because I haven't implemented log file cycling, and it's huge. A line for every minute means 10,800 lines per week, so I tailed 11,000.)
It needs a few documentation updates and an installer (because it uses my personal CommandLineInterface.pm
package, which I haven't independently published yet, and has a dependency on gnuplot
and the macOS open
command (because it automatically opens the plots in Preview.app
)).
I developed this whole "overhead project" during the pandemic, and using the plotting script now, I realize that having a hybrid work environment presents a new challenge: incorporating time data from 2 different computers, each with their own activity data. Ugh.
I will eventually get around to putting this all in a github repo, but if anyone wants a copy of the plotting script, let me know.
UPDATE 3/17/2024:
GitHub repo is up: https://github.com/hepcat72/desktopTimeTracker/