Blog
Why my timesheet tool now exists twice:
terminal and desktop
If you bill by the hour, you need a timesheet at the end of every month. The hours are already in Jira, booked on tickets, spread across weeks. Getting them from there into a table that is ready to sign is the same piece of busywork every single month.
That is exactly why I built jira-timesheet, an application for the terminal. By now it exists a second time: Jira Timesheet Desktop, a native interface built with Qt.
Terminal or desktop? Both.
Why build the same tool twice? Because the two versions serve different situations, and neither replaces the other.
The terminal version runs in any terminal, including over an SSH connection, and needs no graphical environment. On a remote machine or a Linux server without a desktop, it is the only one of the two that even starts. It is driven by the keyboard, and once you know the shortcuts it is quick.
The desktop version brings real windows, menus and dialogs. You drag columns wider with the mouse, file and print dialogs come from the operating system. If you expect an application that feels like an application, that is what you get.
Both run on Windows, macOS and Linux. They keep their data in separate places and do not get in each other’s way, so you can use them side by side 🙂
What the desktop version does
The core is the same in both versions. In the desktop interface it spreads across three views:
- Timesheet with calendar week, weekday and day groups. Day totals turn green above and red below the target.
- Month as a grid with one row per ticket and a target versus actual bar per day and per week. Past working days without a booking are marked, and the ticket numbers open the detail dialog.
- Year with twelve month tiles, progress bar, forecast and the most important tickets per month.
On top of that, the things that make up the end of a month: export to Excel with logo and signature line, to a PDF that can be signed in Adobe, and to JSON and Markdown. A print preview, German public holidays per federal state including gap detection, and a target versus actual comparison with a yearly forecast.
One point matters more to me than the rest:
Not every hour ends up as a worklog in Jira.
Meetings, phone calls, a look into a mailbox. You add those through a dialog, written the way you would note them down anyway: 3h 30m, 3:30, 3.5 and 45m are all understood.
Those entries live in their own SQLite file and never in the Jira cache. They count everywhere - day total, month, year, Excel and PDF - and are colour-marked, so it stays obvious at a glance what comes from Jira and what does not.
| Key | Action |
|---|---|
F5 |
Fetch the bookings for the month |
Ctrl+F |
Search |
Ctrl+N |
Record time manually |
Ctrl+E |
Export, the format is chosen in the save dialog |
Ctrl+P |
Print preview |
Ctrl +/- / 0 |
Zoom, mouse wheel works too |
What has been added since the first release
The application has grown beyond the timesheet. Three examples:
- Ticket analysis turns a single ticket into a report with a timeline, time spent in each status and the people involved. The result is one HTML file that works offline and can be passed on.
- My tickets groups all your tickets by whose turn it currently is and shows how long they have been waiting.
- My team gives the same view for colleagues, deliberately without charts. Throughput per month would turn into a performance metric about another person, and that is not what this is about.
The hardest decision: a copied core instead of a shared library
Both applications use the same functional core - Jira integration, timesheet logic, manual entries, holiday calendar, export. The obvious route would be to publish that core as a library and import it in both applications. That is exactly what the desktop version does not do. The core is copied.
The reason is simple. An import would have pulled the entire terminal framework into the desktop application. A Qt application that carries Textual around just to reach a few models and services would be the worse trade.
Copied code has a well-known price: it drifts apart. So that this does not happen unnoticed, there is a command that compares both cores and reports differences:
uv run poe core-sync
Elegant it is not.
The alternative would have been a third published library with its own version and its own release rhythm, for two applications from the same hand. That struck me as the bigger effort for the smaller gain.
Anonymising at the push of a button
If you want to show a tool that works on real records, you have a problem: every screenshot carries real ticket numbers, descriptions, names and the hostname of the Jira instance.
That is exactly why the View menu has an entry called Anonymize data. One click replaces tickets, descriptions, authors, status names and the Jira host with neutral placeholders across every view and the log. The real data stays in the cache and comes back the moment you switch it off.
Every screenshot on the project page was made that way. If you have to demonstrate something yourself, you can also start the application without any Jira access at all:
jira-timesheet-qt --demo
Installation and licence
A one-liner fetches the source of the latest release and sets it up with all dependencies. It requires Python 3.12 or later:
irm https://raw.githubusercontent.com/michaelblaess/jira-timesheet-qt/main/install.ps1 | iex
On Linux and macOS the same with curl ... | bash.
But watch out: unlike the terminal version, the desktop application is not under the Apache License but under the Business Source License 1.1. That means:
- Reading, building and using it yourself is free, as a private person including for your professional work.
- Ready-made packages that run without Python are available for a small fee. They may not be passed on.
- Organisations, and anyone offering the application as a paid service, need a commercial licence.
- Every version automatically becomes Mozilla Public License 2.0 four years after its release.
The reason is simply the effort. Building and maintaining a desktop application for three operating systems takes real time, and a small amount of money may flow in return. Strictly speaking, that makes the desktop version “source available” rather than “open source”: the source stays open, but commercial use is restricted.
What happens on first start
On its first start the program shows a notice that has to be confirmed, otherwise it exits.
The reason: the tool reads working hours out of a third-party system through the Jira interface. Which issues and bookings become visible is decided solely by the permissions of the account you use. Depending on how rights are assigned, those may include entries booked by other people. By confirming, you declare that you will only use the program against Jira instances you are authorised to access.
To me that notice is part of the tool. If you build a program that reads data out of somebody else’s system, the question of who is actually allowed to look in there should not be left to the user alone.
Links: project page with all screenshots · source on GitHub · the terminal version
“Jira” and “Atlassian” are registered trademarks of Atlassian Corporation. This project is not developed, supported or authorised by Atlassian and uses only the public Jira interface.