watrs: A time tracker built in Rust
watrs
is a Rust-based implementation of the
Watson CLI tool, which is written
in Python. Unfortunately, Watson hasn’t been maintained for many years
and there are many open issues and pull requests that have not been
addressed. This project aims to provide a modern alternative with
similar functionality, but built in Rust.
Why Rust? Because I want to learn it and building something that already exists is an easy way to get started. Plus, I use Watson daily for work and over the years of using it, I’ve found many small issues that I would like to fix or things to improve on. Since the original Watson is not actively maintained, it doesn’t make sense to contribute to it. Want to learn more about how I’ve made this different from Watson? See the design document.
As a warning, since this is a way for me to learn Rust, this package is very experimental, may change a lot at any time, and is designed around my needs and use cases. So, if you want to try it out, just be aware of the situation! :)
Installing
For now, the only way to install it is a development version on GitHub
with cargo
:
cargo install --git https://github.com/lwjohnst86/watrs
Design
This document outlines the design principles and interface for the
watrs
time tracker, as well as some differences from the original
Watson. This document is mainly a way to help keep me focused on what to
actually implement and when I will know what “done” looks like.
Princples
My main aim is to build a simplified version of the Watson time tracker. And I want to follow a few principles to guide the design of it:
- Empower the user to be familiar with their data by encouraging them to interact directly with the timesheet data, e.g. via the terminal or editor.
- Save timesheet data in a format and structure that is human-readable and editable.
- Structure timesheet data to be importable by different tools (for other analyses).
- Assume Git will be used to keep backups.
- Have a simple interface with a few actions.
- Keep timesheet statistics to a minimal, instead referring to use other specialised tools for more advanced analyses.
- Output statistics to the terminal in a table format (columns and rows).
Interface
-
start [<project>] [<tags>]
: Starts the timer for a given project with optional tags. Creates a new entry in the timesheet file with an emptystop
field. If a timer is already running for a different project, it will stop the timer on the other project and start a new one for the given project. If no project is given, it defaults to starting the last project (along with the same tags used).It will not add a new entry if the timesheet file can’t be parsed/read, if there are duplicate
id
values, or if there arestart
entries later than thestop
entries. -
stop
: Stops the current timer. Adds a timestamp to thestop
value in the timesheet file with an entry that has an emptystop
value. It will only stop and add the timestamp if there is a timer running (the most recentstart
timestamp without astop
timestamp). Can’t stop if the currentstart
entry is in the future (e.g. if the person incorrectly edited the file).It has the same constraints as
start
for when it won’t work. -
edit
: Opens the timesheet file in the user’s editor (e.g.vim
) for them to edit themselves. Use this to fix a mistake in an entry, remove an entry, fix a merge conflict, or cancel the currently running entry. Since this will “simply” open the file in the default editor, going to the necessary entry can be done by searching for the timestamp, project or tag, or going to the end of the file for the most recent entry.If you edit the file, it will only save if it passes the same constraints as the
start
command. -
stats [<subcommand>] [<options>]
: This command contains several subcommands that provide basic statistics for various aspects of the timesheet data.-
projects
: Lists all projects with the total time spent for each project. Options include--unit <unit>
to specify the unit of time to display (only units bigger thanday
, which is the default),--number-of-units <n>
to show then
number of units the the statistics (e.g.--number-of-units 3
to show the last 3 days), as well as--include-tags
to include the time spent on each tag within each project, grouped by the unit. -
tags
: Lists all tags with the total time spent for each tag. Options include the--unit
and--number-of-units
found inprojects
.
-
-
today
: Mainly is a helper command to get an idea about the day. Lists the entries for the current day, including the entry that is currently running. -
WATRS_TIMESHEET_PATH
: Environment variable to set the location of the timesheet. If not set, it defaults to~/.watrs/timesheet.json
.
Data
Data is stored in a JSON file, found in the file provided by
WATRS_TIMESHEET_PATH
. All entries have fields with keys for id
,
start
, stop
, project
, and tags
. The id
is a unique identifier
for the entry, generated using an UUID and is used to ensure an
identifiable entry for analyses. The start
and stop
fields are in
ISO 8601 format (stored in UTC timezone), which is a standard for
representing date and time (without microseconds). All fields can be
imported as strings.
While including both field keys and a character string for date will make the file larger, it also makes it easier to read and edit. And given that Polars will be reading from the file, size isn’t really a computational concern nor is it a concern for disk storage as most modern computers have plenty of storage space. At the cost of a larger file size, the benefit is a more human-readable and -editable structure.
The JSON structure looks a bit like this, for each timesheet entry:
{
"id": "unique-id-12345",
# Following the ISO 8601 format for date and time
"start": "2023-10-01T12:00:00Z",
"stop": "2023-10-01T14:00:00Z",
"project": "example_project",
"tags": ["tag1", "tag2"]
}
Since the data is structured as a JSON file with clear keys and all strings, doing further analyses or wrangling on it is as easy as reading it with Polars (or DuckDB) via Python, R, or the shell.
Differences from Watson
Data format
Watson stored it’s data in a []
bracketed JSON file, with fields that
had values but no keys (they were unnamed). So that means if you wanted
to directly edit the file, you would have to know the order of the
fields. This is not very user-friendly, especially if you e.g. have a
merge conflict from using Git to track your timesheet. It also made it
much more difficult to read into other analysis tools.
Commands
Some commands in Watson I almost never used or always misunderstood
their action/how to use them, often having to review the documentation
multiple time to (re)learn it (e.g. report
vs aggregate
). I decided
to drop or heavily modify/merge many commands:
- Dropped:
restart
,sync
,help
(that’s built into the Rust CLI),merge
,frames
,add
- Merged/modified into
stats
:report
,aggregate
,status
,projects
,tags
- Merged/modified into
edit
:edit
,cancel
,remove
Options
Most Watson commands had several options or behaviours for each command that I rarely used, nor liked. So I decided to:
- Remove all “confirm new project/tags” options/behaviour, such as in
start
andstop
.