Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

timer

A personal time-tracking tray app for Linux, built around a single plain-text worklog file. The tray icon shows whether a task is running; a click opens a text editor over this month's worklog so you log work by typing a line. The file is the whole database -- editable by hand, in any editor, while the app runs.

Install and run

Requires Python 3 with PyGObject (gi, GTK 3), pystray, and Pillow (PIL). There is no build step or packaging.

./timer.py                              # launch the tray app

Worklog files live in ~/worklog/ (override with the WORKLOG_DIR environment variable), one file per month, named YYYY-MM.txt. The directory and files are created on demand.

Command line

./timer.py --task <token>               # segments logged for one task token
./timer.py --project [prefix]           # list project prefixes, or segments for one
./timer.py day|week|month|year|YYYY-MM-DD [prefix]   # time report
./stat.py [period] [prefix]             # same report, standalone
./analyze_worklog.py <file>             # report for a single worklog file

Using the tray

  • Left click opens the editor on this month's file, scrolled to the bottom with a fresh HH:MM line seeded under today's header and the cursor waiting on it. Type (token) what you started to begin a task, or plain text to jot a note. Close the window (or Ctrl+Enter, or Esc) to save; an untouched seeded line is discarded.
  • A strip above the text draws today's task spans on an hourly timeline, one colour per task, the running task outlined.
  • [ ] / [x] checkboxes in the text toggle on click.
  • Right click allows to stop the running task, and shows today's per-task totals.
  • The icon is a red disc while a task runs. If a task has two-letter prefix (like pr-sometask), that prefix shows over the red disc. Full task name shows in the tooltip.

The worklog format

UTF-8 text, read line by line. Three line kinds carry meaning; everything else is a free note kept for display but ignored for time accounting.

# 2026-03-02 optional day comment       a day header; lines below belong to it
9:00 (ap-321) fixing the importer       open task ap-321 at 9:00
9:20 found the off-by-one               a timestamped note on the open task
[x] making arbitrary notes with some [ ] clickable boxes
9:45 (bp-7) switch projects             close ap-321, open bp-7
10:30 (*) coffee                        stop: close the task, start an idle gap
  • A time entry with a (token) is a boundary: it closes the open segment and opens a new one. Without a token it is a note on the open segment and does not split time. The token * closes the segment into an idle gap that is never counted.
  • A task token's project is everything before the first - (ap-321 -> ap). If that prefix is two letters, it is also shown on the tray icon. There is no separate project or issue field -- the token is the whole identity.
  • Times are always minute-resolution.
  • A timed line in the future is a reminder: the app reopens the editor on that line when its moment arrives.

To fix or delete anything, edit the file -- the app has no delete command and picks up external edits within about a second.

Architecture

Layered by responsibility, one file per concern:

  • worklog.py -- the (only) format engine: parsing and aggregation. parse(text) returns a Journal of Segments plus the open segment and a list of parse problems.
  • store.py -- the only module that touches disk: file location, month rollover, atomic append and whole-file replace, and an mtime change token.
  • timer.py -- the tray App (pystray icon, callable menu, 1s tick) and the EditorWindow (GTK text view plus the timeline), and the CLI.
  • stat.py, analyze_worklog.py -- report front-ends over the same parser: across all months, and over one file.

Run the self-tests with python3 worklog.py and python3 store.py.

Core design

  • The worklog text file is the single source of truth. Apart from the UI, the state of the time tracker is reperesented in the current text file.
  • The whole tray interaction is one editor window over the month file, seeding a line to type into. This interaction draws user focus to the worklog, and is preferred over automated operations.
  • The only remaining automation is a quick "end task" action to be used when there's no time write text notes.
  • The project code is derived from the token rather than stored, to prevent any references in data storage.
  • Minute resolution is deliberate, as a minimum that deserves user attention.
  • "Running now" is an open segment dated today; an open segment on an earlier day is not a running task but a surfaced parse problem.
  • Parsing never throws, and does not attempt to overwrite the file. A malformed or mid-edit file yields the last good state plus a list of problems.

Options

  1. File watching: mtime polling or Gio.FileMonitor. Polling is immune to rename-and-replace saves and reuses the clock tick; FileMonitor would give sub-second response but needs care around inode-replacing saves.
  2. A task left open across midnight or a month boundary: surfaced as an unclosed-earlier-day problem, or handled automatically by splitting at the boundary. Surfacing keeps the parser simple and matches the "close the day before leaving" workflow; auto-splitting would remove a documented sharp edge at the cost of engine complexity.

About

Simplistic time tracker for Gtk 3.0 system panel

Resources

Stars

Watchers

Forks

Contributors

Languages