odoo-timesheets/AGENTS.md
Jef Roosens 6915d8d764
refactor(cli): split into summary and csv subcommands
- Add subparsers for 'summary' and 'csv' replacing the --summary flag
- Extract _add_shared_args(), _resolve_date(), _resolve_rows(), and
  _resolve_project_map() as shared helpers used by both subcommands
- Both subcommands support -o/--output to write to a file instead of stdout
- Update AGENTS.md with new subcommand usage examples
2026-06-02 09:31:06 +02:00

4.7 KiB

Timesheets — Agent Guide

Project overview

A Python CLI tool that parses markdown pipe-delimited timesheet tables and exports them to CSV for import into Odoo (or similar tools). It also supports a human-readable summary view and can fetch notes directly from Joplin.

Package layout

timesheets/
├── pyproject.toml                  # package metadata, entry point, dev dependencies
├── AGENTS.md
└── src/timesheets/
    ├── cli.py        # argument parsing, main() entry point
    ├── parser.py     # markdown table parsing, aggregation, date filtering
    ├── projects.py   # project_map.json loading and key resolution
    ├── output.py     # CSV writing and summary printing
    ├── config.py     # TOML config file loading and key extraction
    ├── joplin.py     # Joplin API integration (notebook traversal, note fetching)
    └── utils.py      # shared low-level helpers (duration parsing, formatting, etc.)

Tests live in tests/, one file per source module:

tests/
├── test_utils.py
├── test_parser.py
├── test_projects.py
├── test_config.py
├── test_output.py
└── test_joplin.py

Package manager — uv

All dependency management and script execution is done via uv. Do not use pip or python directly.

Task Command
Install / sync dependencies uv sync
Add a runtime dependency uv add <package>
Add a dev-only dependency uv add --dev <package>
Run the CLI uv run timesheets <args>
Run any Python script uv run python <script>

CLI usage

The CLI has two subcommands: summary and csv. Both accept the same arguments.

# Human-readable summary for today (from Joplin)
uv run timesheets summary --joplin

# Human-readable summary for a specific day
uv run timesheets summary 2026-05-22 --joplin
uv run timesheets summary yesterday --joplin
uv run timesheets summary 3 days ago --joplin

# Export today's entries as CSV to stdout
uv run timesheets csv --joplin

# Export to a file
uv run timesheets csv --joplin -o output.csv

# Use a local markdown file instead of Joplin
uv run timesheets summary --input timesheet.md
uv run timesheets csv --input timesheet.md -o output.csv

# Read from stdin
cat timesheet.md | uv run timesheets csv --input -

# Specify a day (positional, accepts YYYY-MM-DD, MM-DD, or DD-MM; - or / separator)
uv run timesheets csv 2026-05-22 --input input.md
uv run timesheets csv 05-22 --input input.md

# Use a specific project map file
uv run timesheets csv --input input.md --map /path/to/project_map.json

# Fetch entries for a specific day from Joplin
uv run timesheets csv 2026-05-22 --joplin --token your_token

project_map.json is auto-discovered in the current working directory if --map is not provided.


Config file

Create timesheets.toml in your working directory (or pass --config /path/to/file.toml):

[joplin]
token = "your_api_token_here"

[projects]
map = "/path/to/project_map.json"

Priority order for each value: CLI flag > config file > environment variable / default.


Joplin notebook structure

The --joplin flag expects the following notebook hierarchy in Joplin:

Work/
└── Timesheets/
    └── YYYY/
        └── YYYY - WNN/         ← notebook per week
            └── YYYY - WNN      ← note with the same title as the notebook

The note body contains one markdown table per day, each preceded by a heading of the form # <weekday> - YYYY-MM-DD.


Testing

The test suite uses pytest with pytest-cov for coverage reporting.

# Run all tests
uv run pytest

# Run with coverage report
uv run pytest --cov

# Run a specific test file
uv run pytest tests/test_parser.py

# Run a specific test
uv run pytest tests/test_parser.py::TestParseTable::test_empty_input

Rules for adding or changing functionality

  1. Always update or add tests when introducing new behaviour or modifying existing behaviour. Tests live in the tests/ file that corresponds to the module being changed (e.g. changes to parser.pytests/test_parser.py).

  2. Run the full test suite before finishing and confirm it passes with no failures:

    uv run pytest --cov
    
  3. Do not reduce coverage. Every new function or branch should have at least one test covering the happy path. Edge cases and error paths should be covered where the logic is non-trivial.

  4. cli.py is intentionally excluded from unit tests — it is thin glue code. All logic worth testing belongs in the other modules.

  5. Joplin integration tests in test_joplin.py must mock ClientApi — do not require a live Joplin instance.