Metadata-Version: 2.4
Name: jira-time-tracker
Version: 0.1.0
Summary: Track how much estimated work (story points, hours, days…) came into being in a Jira project over a period, per issue and per person.
Keywords: jira,atlassian,story-points,estimates,time-tracking,cli
Author: codeonym-oss
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Office/Business
Classifier: Topic :: Software Development :: Bug Tracking
Classifier: Typing :: Typed
Requires-Dist: keyring>=24
Requires-Dist: platformdirs>=4
Requires-Dist: requests>=2.31
Requires-Dist: rich>=13
Requires-Dist: typer>=0.12
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/codeonym-oss/jira-time-tracker
Project-URL: Repository, https://github.com/codeonym-oss/jira-time-tracker
Project-URL: Issues, https://github.com/codeonym-oss/jira-time-tracker/issues
Project-URL: Changelog, https://github.com/codeonym-oss/jira-time-tracker/blob/main/CHANGELOG.md
Description-Content-Type: text/markdown

# jira-time-tracker

`jtt` answers one question about a Jira Cloud project: **how much estimated work
came into being over a period, on which issues, and by whom?**

The estimate is whatever field your organisation tracks. That could be story
points, an hours or days custom field, or Jira's own original estimate or time
spent. You pick the field once per project.

```
$ jtt contributions --from 2026-09-24 --to 2026-09-30 --by day
╭─ Contributions · DEMO ─────────────────────────────────────────────────────────╮
│  Field  Story point estimate (customfield_10016, points)                      │
│ Period  Thu 24 Sep 2026 included → Wed 30 Sep 2026 excluded  · 6 days         │
│ Credit  each change goes to whoever held the issue when it happened           │
╰───────────────────────────────────────────────────────────────────────────────╯
 Person          Net SP   Added   Share   Issues    Created       Done
 ─────────────────────────────────────────────────────────────────────────────────
 Alice Martin   +16 SP   16 SP    100%        3   4 (2 SP)   2 (8 SP)   ████████

Per day
 When          Net SP
 ─────────────────────────────────────────
 Thu 24 Sep     +5 SP   ██████████████████
 Fri 25 Sep   +8.5 SP   ██████████████████████████████
 Sat 26 Sep        ±0
 …
╭─ Statistics ───────────────────────────────────────────────────────────────────╮
│         Net added  +16 SP                                                      │
│ Per changed issue  5.33 SP average over 3                                      │
│           Biggest  DEMO-729 +8 SP Share a published report through a public link │
│    Done in period  2 issues (8 SP)                                             │
│       Daily trend  ▅█  ▂                                                       │
│       Busiest day  Fri 25 Sep (8.5 SP, 3 active days)                          │
╰────────────────────────────────────────────────────────────────────────────────╯
```

## Install

It needs Python 3.10 or newer and works the same on Linux, macOS and Windows.

```sh
uv tool install git+https://github.com/codeonym/jira-time-tracker   # or: pipx install git+…
jtt --help
```

To install from a local checkout, run `uv tool install .` or `pipx install .`.

## Getting started

```sh
jtt login     # site URL, account email, API token (typed hidden)
jtt init      # for each project: choose the field that holds the estimate, and its unit
jtt calculate --period last-week
```

1. **`jtt login`** checks the token against Jira before storing it. Create a
   token at <https://id.atlassian.com/manage-profile/security/api-tokens>. To
   switch account or rotate the token, run `jtt logout` first, then `jtt login`
   again.
2. **`jtt init`** walks through every project you can see and lists its numeric
   fields. A ★ marks the fields on the project's create screens, and those come
   first. You pick one and say what its numbers mean: points, seconds, minutes,
   hours, days or weeks. For time units you also pick the unit reports should
   show. Jira's built-in time tracking fields always hold seconds, so the tool
   sets that unit for you. Your last choice is offered as the default for the
   next project. Type `s` to skip a project or `q` to stop; progress is saved
   after each project.

   To skip the questions, pass everything as options:
   `jtt init -p DEMO --field customfield_10016 --unit points`.

## Commands

| Command | What it does |
|---|---|
| `login` / `logout [--purge]` / `whoami [--check]` | Manage the account. `logout` keeps project settings; `--purge` forgets them too |
| `init [-p KEY…] [--reconfigure]` | Choose each project's tracked field and unit |
| `config show \| path \| default KEY \| set KEY … \| remove KEY \| calendar …` | Inspect or edit settings without re-running `init` |
| `projects [--configured]` | Projects you can see and what each one tracks |
| `fields [-p KEY] [--counts]` | A project's numeric fields, optionally with how many issues hold a value |
| `fetch` | Issues touched in a period, with their current value (`--json` for raw output) |
| `calculate` | Net amount per issue for the whole project or one person, at four levels of detail |
| `collaborators` | People who can be assigned to the project, with their current open, in-progress and done issues |
| `contributions [--by day\|week]` | Per-person net, added, removed, share, issues created and issues finished, plus statistics and a daily trend |
| `export -o FILE [-k issues\|changes\|contributions\|daily]` | Write CSV (opens in Excel), JSON or Markdown |

Every report takes `-p KEY` (default: the project set by `init`), a period, and
`--as hours|days|…` to show time amounts in another unit.

### Periods

Periods are **half-open: the start day is included and the end day is
excluded.** `--from 2026-09-01 --to 2026-10-01` is all of September. Leave out
`--to` and the period runs through today.

You can also use a named period instead:
`--period today | yesterday | this-week | last-week | this-month | last-month | this-quarter | last-quarter | this-year | last-7-days | last-30-days`.
Weeks start on Monday.

Days are the calendar days Jira shows in *your* Jira profile's time zone, which
is also how Jira reads dates in queries. The tool never works out time zone
offsets itself, because local time zone data can disagree with Jira's. For
example, tzdata 2026d keeps Morocco at +00 after Ramadan 2026, while Jira still
shows +01.

### Choosing whose work to count (`--user`)

`--user` accepts `me`, an account id, or part of a name or email.

- `calculate --user X` (the default, `--attribution was`) counts every change
  made in the period on issues X held at some point in the period.
- `--attribution holder` only counts changes made while X held the issue.
- `contributions` always credits each change to whoever held the issue when it
  happened. The value an issue was created with goes to the first person
  assigned to it. That way the per-person totals add up to the project total.

### Levels of detail

`-v`, `-vv` (or `--vv`) and `-vvv` (or `--vvv`) work on `calculate` and
`contributions`:

- **`-v`** adds current value, type, status and current assignee (↪ marks a
  task that has since moved to someone else), and where each amount came from.
- **`-vv`** also lists issues with no change, and adds a per-issue timeline of
  each change: when it happened, the value before and after, and who made it.
- **`-vvv`** also shows the query sent to Jira, a trace of every API request,
  and changes outside the period (struck through).

## How the delta is computed

For each issue touched in the period, the tool reads its full change history
from Jira and adds up the changes to the tracked field that happened inside the
period. Two Jira behaviours shape this:

- **The query can't use "last updated" as an end date.** Jira only records when
  an issue was *last* updated. An issue edited inside the period and again
  afterwards would drop out of a query ending at the period's end. So the query
  only filters on that date from below, and the change history decides what
  falls inside the period.
- **A value entered when an issue is created leaves no history entry.** Jira's
  history starts at the first edit. For an issue created inside the period, the
  tool adds back the value it was created with. That value is the "before" side
  of its first later change, or its current value if the field was never
  edited.

Dates in queries are always sent with an explicit `00:00`. Tested on a live
site, bare dates did not give exact bounds: `assignee WAS X DURING ("2026-09-24",
"2026-09-25")` matched issues first assigned in the afternoon of the 25th, and
`DURING ("2026-09-25", "2026-09-25")` matched nothing.

## Where things are kept

| What | Where |
|---|---|
| Settings (site, projects, fields, units) | `config.json` in the user config directory; `jtt config path` prints it. On Linux: `~/.config/jira-time-tracker`, macOS: `~/Library/Application Support/jira-time-tracker`, Windows: `%LOCALAPPDATA%\jira-time-tracker` |
| API token | The system keyring: Windows Credential Manager, macOS Keychain, or Secret Service (GNOME Keyring / KWallet) on Linux. Where no keyring is available (headless servers, WSL, containers), it goes in `credentials.json` next to the settings, created **0600** inside a **0700** directory |

The token is never written to `config.json` or printed.

Environment overrides:
- `JTT_CREDENTIAL_BACKEND=file|keyring` forces where the token is stored.
- `JTT_API_TOKEN` supplies a token for CI; it is never stored.
- `JTT_CONFIG_DIR` moves the settings directory.

## Development

```sh
uv sync
uv run pytest
```

The tests run the real CLI and HTTP client against a local fake Jira that
replays issue data captured from a live site.

`legacy/jira_tasks.py` is the single-file script this project grew from, kept
unchanged.
