- Add config.py with load_config(), find_default_config(), get_token(), and get_map_path() - Auto-discover timesheets.toml in cwd; override with --config flag - Priority: CLI flag > config file > env var / cwd default - Add timesheets.example.toml as a committed reference template - Add timesheets.toml to .gitignore to prevent accidental secret leakage - Document config file format in AGENTS.md
171 lines
4.9 KiB
Markdown
171 lines
4.9 KiB
Markdown
# 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`](https://docs.astral.sh/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
|
|
|
|
```sh
|
|
# Print CSV to stdout (date defaults to today)
|
|
uv run timesheets --input input.md
|
|
|
|
# Specify a day (positional, accepts YYYY-MM-DD, MM-DD, or DD-MM; - or / separator)
|
|
uv run timesheets 2026-05-22 --input input.md
|
|
uv run timesheets 05-22 --input input.md
|
|
|
|
# Write CSV to a file
|
|
uv run timesheets --input input.md -o output.csv
|
|
|
|
# Override the day (accepts YYYY-MM-DD, MM-DD, or DD-MM; - or / separator)
|
|
uv run timesheets --input input.md --day 2026-05-22
|
|
uv run timesheets --input input.md --day 05-22
|
|
|
|
# Use a specific project map file
|
|
uv run timesheets --input input.md --map /path/to/project_map.json
|
|
|
|
# Print a human-readable summary instead of CSV
|
|
uv run timesheets --input input.md --summary
|
|
|
|
# Read from stdin
|
|
cat input.md | uv run timesheets --input -
|
|
|
|
# Fetch today's entries from Joplin (token via env var)
|
|
JOPLIN_TOKEN=your_token uv run timesheets --joplin
|
|
|
|
# Fetch entries for a specific day from Joplin
|
|
uv run timesheets 2026-05-22 --joplin --token your_token
|
|
```
|
|
|
|
The `--joplin` flag and the file `input` argument are mutually exclusive.
|
|
When `--joplin` is used, only entries matching the target day (positional arg,
|
|
or today) are returned, filtered by the `# ... YYYY-MM-DD` day heading in the note.
|
|
|
|
The API token can be provided via:
|
|
- `--token <token>` CLI flag
|
|
- `JOPLIN_TOKEN` environment variable
|
|
|
|
`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`):
|
|
|
|
```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.
|
|
|
|
```sh
|
|
# 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.py` → `tests/test_parser.py`).
|
|
|
|
2. **Run the full test suite before finishing** and confirm it passes with no
|
|
failures:
|
|
```sh
|
|
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.
|