odoo-timesheets/README.md
Jef Roosens de46399010
Add ~ marker to exclude entries from CSV export
Prefix the project name or note column with ~ to mark an entry as
count-but-don't-export. Marked entries are included in summary and
status totals but omitted from all csv output (both --raw and
aggregated, single-day and weekly).

  | 09:00 | 17:00 | 8:00 | ~Leave |  | Day off  |
  | 09:00 | 17:00 | 8:00 |  Leave |  | ~Day off |

The ~ is stripped from whichever field carries it before any
downstream processing, so project map resolution is unaffected.

Implementation:
- parse_table sets skip_csv=True on marked rows and strips the ~
- new filter_skip_csv() helper in parser.py
- to_csv_entries() skips skip_csv rows
- _cmd_csv calls filter_skip_csv() before aggregate_rows()
2026-06-02 09:31:16 +02:00

145 lines
4.4 KiB
Markdown

# Timesheets
A Python CLI tool that parses markdown pipe-delimited timesheet tables and
exports them to CSV for import into Odoo. It also supports a human-readable
summary view, a stories list, a work status dashboard, and can fetch notes
directly from Joplin.
## Package layout
```
timesheets/
├── pyproject.toml # package metadata, entry point, dev dependencies
├── timesheets.example.toml # example config file
└── 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, summary, stories, and status printing
├── config.py # TOML config file loading and key extraction
├── joplin.py # Joplin API integration (notebook traversal, note fetching)
├── status.py # day/week status calculations
└── utils.py # shared low-level helpers (duration parsing, formatting, etc.)
```
## Installation
```sh
cd timesheets
uv sync
```
## Subcommands
| Command | Description |
|---|---|
| `summary` | Human-readable summary of time spent per project |
| `csv` | Export timesheet entries as CSV for Odoo import |
| `stories` | List stories worked on, grouped by project |
| `status` | Show hours remaining today and projected week total |
All subcommands accept the same source and day arguments:
```sh
# Source: Joplin (recommended) or a local file
uv run timesheets <cmd> --joplin
uv run timesheets <cmd> --input timesheet.md
# Day: defaults to today, accepts YYYY-MM-DD, MM-DD, natural language
uv run timesheets <cmd> 2026-05-22 --joplin
uv run timesheets <cmd> yesterday --joplin
uv run timesheets <cmd> 3 days ago --joplin
```
### summary
```sh
uv run timesheets summary --joplin # today, full detail
uv run timesheets summary -w --joplin # full week
uv run timesheets summary -s --joplin # short: one line per project
uv run timesheets summary -w -s --joplin # weekly short: per-day project totals
uv run timesheets summary -w -ss --joplin # weekly totals only: one line per day
```
### csv
```sh
uv run timesheets csv --joplin # stdout
uv run timesheets csv --joplin -o output.csv # write to file
uv run timesheets csv --raw --joplin # one row per entry, no aggregation
uv run timesheets csv -w --joplin # full week
uv run timesheets csv -w --raw --joplin # full week, no aggregation
```
### stories
```sh
uv run timesheets stories --joplin # today
uv run timesheets stories -w --joplin # full week
uv run timesheets stories -w --joplin -o stories.md # write to file
```
### status
```sh
uv run timesheets status --joplin
```
## Timesheet table format
Each day's entries live in a markdown pipe table with the following columns:
```markdown
| Start | End | Duration | Project | Story | Note |
|-------|-------|----------|---------|-----------|------------|
| 09:00 | 10:00 | 1:00 | acme | PROJ-123 | Fix bug |
| 10:00 | 10:15 | 0:15 | scrum | | dsu |
```
### Excluding entries from CSV export
Prefix the project name with `~` to mark an entry as **count-but-don't-export**.
The entry is included in `summary` and `status` totals, but omitted from `csv` output.
This is useful for time that is already pre-entered in Odoo (e.g. holidays, days off).
```markdown
| 09:00 | 17:00 | 8:00 | ~Leave | | Public holiday |
```
The `~` is stripped before project name resolution, so `~Leave` maps to the same
project as `Leave` in your `project_map.json`.
## Config file
Copy `timesheets.example.toml` to `timesheets.toml` in the working directory
(or pass `--config /path/to/file.toml`):
```toml
[joplin]
token = "your_api_token_here"
[projects]
map = "/path/to/project_map.json"
[work]
daily_hours = 8.0
weekly_hours = 40.0
[csv]
date_format = "%d/%m/%y" # default; any strftime format string
```
Priority order: **CLI flag > config file > environment variable > default**.
## Joplin notebook structure
```
Work/
└── Timesheets/
└── YYYY/
└── YYYY - WNN ← note per week, one table per day
```
Each table is preceded by a heading of the form `# <weekday> - YYYY-MM-DD`.
Open entries (start time present, end time absent) are used by the `status`
command to calculate expected end time.