Python program to automate exporting my Markdown timesheets to Odoo
Parallel work is logged as overlapping entries. resolve_overlaps() splits the shared time equally using the midpoint of the overlap region: - Partial overlap: the midpoint becomes the boundary between the two entries (earlier entry trimmed, later entry delayed). - Full containment: the containing entry is split into two pieces surrounding the contained one, with the midpoint rule applied to the overlap region. Open entries (no end time) are passed through unchanged. resolve_overlaps() is called automatically in filter_rows_by_date, filter_week_sections, and the --input single-day path in cli.py, so all subcommands benefit without further changes. |
||
|---|---|---|
| src/timesheets | ||
| tests | ||
| .coverage | ||
| .gitignore | ||
| .python-version | ||
| AGENTS.md | ||
| project_map.json | ||
| pyproject.toml | ||
| README.md | ||
| timesheets.example.toml | ||
| uv.lock | ||
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
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:
# 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
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
uv run timesheets csv --joplin # stdout
uv run timesheets csv --joplin -o output.csv # write to file
stories
uv run timesheets stories --joplin # today
uv run timesheets stories -w --joplin # full week
status
uv run timesheets status --joplin
Config file
Copy timesheets.example.toml to timesheets.toml in the working directory
(or pass --config /path/to/file.toml):
[joplin]
token = "your_api_token_here"
[projects]
map = "/path/to/project_map.json"
[work]
daily_hours = 8.0
weekly_hours = 40.0
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.