Better task scheduling in Windows OS
This is a work in progress, subject to many changes and new instabilities / brokenness.
-
Windows task scheduler sucks
- It uses XML (XML sucks)
- Debugging failed tasks sucks (it doesn't ever tell you why)
- You have to write some jank PowerShell / CMD script to execute anything else
- Doesn't tell you when something ran.
-
Punctual does not suck (or at least sucks a bit less)
- Uses YAML
- Has extensible logging via the Python Standard Library
logging
package - You provide the path to the file you want ran, and it runs it.
- Issues a Windows 10 'Toast' notification (optional)
# Find a good spot for the application
$PuncPath = "$ENV:LOCALAPPDATA\punctual"
New-Item -Path $PuncPath -ItemType Directory
Set-Location $PuncPath
# Download the executable
Invoke-WebRequest -Uri "https://github.com/camratchford/punctual-scheduler/files/10593845/punc.zip" -OutFile "$PuncPath\punctual.zip"
# Extract the zip file
Expand-Archive -Path "$PuncPath\punctual.zip" -DestinationPath $PuncPath
Remove-Item -Path "$PuncPath\punctual.zip"
❗ The executable to run will be
$PuncPath\punc.exe
# Find a good spot for the application
$PuncPath = "$ENV:LOCALAPPDATA\punctual"
New-Item -Path $PuncPath -ItemType Directory
Set-Location $PuncPath
# Create a virtual environment
python -m venv venv
# Git needs to be installed and in $ENV:PATH
.\venv\Scripts\pip.exe install git+https://github.com/camratchford/punctual-scheduler
❗ The executable to run will be
$PuncPath\venv\Scripts\punc.exe
New-Item -Path "$PuncPath\logs" -ItemType Directory
Create the task config, located in: $PuncPath\config.yml
logs_dir: C:\Users\testuser\AppData\Local\punctual\logs
# Python logging explained at https://docs.python.org/3/library/logging.config.html#dictionary-schema-details
log_config:
version: 1
disable_existing_loggers: True
formatters:
brief:
format: '%(message)s'
use_colors: True
verbose:
format: |
%(asctime)s:
Level: %(levelname)s
File: %(filename)s
LineNo: %(lineno)d
Msg: %(message)s
handlers:
file:
class : logging.handlers.RotatingFileHandler
formatter: verbose
filename: punctual.log
maxBytes: 1048576 # 1MB
backupCount: 3
console:
class : logging.StreamHandler
formatter: brief
level : DEBUG
stream : ext://sys.stdout
loggers:
ez_temp:
handlers:
- file
- console
level: DEBUG
propagate: False
- At this moment, punctual only executes arbitrary Python scripts.
- PowerShell / CMD scripts, CLI executables and other actionable resources are in the works.
- Passing arguments to the scripts is also currently in development.
# Create the scripts folder (or don't! you will be providing the absolute path anyways)
New-Item -Path $PuncPath\scripts -ItemType Directory
Create a Python file within your chosen scripts folder location.
# C:\Users\testuser\AppData\local\punctual\scripts\test.py
from datetime import datetime
def main():
with open(r"C:\Users\cameron\PycharmProjects\punctual\test\test_output.txt", "a+") as writer:
writer.write(f"The time is {str(datetime.now())}\n")
if __name__ == "__main__":
main()
Create the task file, located in: $PuncPath\tasks.yml
The application will continue to check the contents of the tasks file to add/remove tasks without restarting it.
❗ Information on format codes can be found in Python's 'datetime' documentation
❗ The value of action -> name must be unique
tasks:
- name: Test task
state: 'present' # Set it to 'absent' if you'd like to stop and remove and running task.
first_run:
value: 09/19/22 13:55:26
format: '%m/%d/%y %H:%M:%S'
frequency:
value: 10
format: '%S'
action:
name: time_tracker
path: C:\Users\testuser\AppData\local\punctual\scripts\test.py
toast:
title: Time Tracker
message: Time has been recorded
Before setting it up as a service, run the application (to see if everything is working with the outputs to stdout and stderr in front of you)
# Set the environment variable for the application folder
$ENV:PUNCPATH = $PuncPath
# If you downloaded the compiled binary
.\punc.exe
# If you installed it with pip in a virtual environment
.\venv\Scripts\punc.exe
If everything ran correctly
- This application is meant to be run as a service. You can just run it as a background task in PowerShell, and it works. Alternatively, you can execute it via a CMD script in the startup folder.
- There's not really a 'perfect' way to automate the creation of a service that runs the application.
- I've had success using NSSM to register it as one.
- The settings I use are:
- Environment:
PUNCPATH="<value of $PuncPath>"
- Start-up type: Automatic (Delayed start)
- Run as:
<the account you log in as>
- Environment:
- The rest of the settings are up to you