Python program to automate exporting my Markdown timesheets to Odoo
Find a file
2026-06-02 09:31:17 +02:00
src/timesheets Round overlap resolution midpoint to nearest 5 minutes 2026-06-02 09:31:17 +02:00
tests Round overlap resolution midpoint to nearest 5 minutes 2026-06-02 09:31:17 +02:00
.gitignore Add --raw flag to csv command to skip aggregation 2026-06-02 09:31:13 +02:00
.python-version feat: set up modularized version of project with testing 2026-06-02 09:31:01 +02:00
AGENTS.md docs: require README updates alongside functionality changes 2026-06-02 09:31:12 +02:00
project_map.json docs: move project description to README, point AGENTS.md to it 2026-06-02 09:31:10 +02:00
pyproject.toml feat(cli): add natural language date parsing via dateparser 2026-06-02 09:31:05 +02:00
README.md Add ~ marker to exclude entries from CSV export 2026-06-02 09:31:16 +02:00
timesheets.example.toml Add date_format config key for csv command 2026-06-02 09:31:15 +02:00
uv.lock feat(cli): add natural language date parsing via dateparser 2026-06-02 09:31:05 +02:00

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
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

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

uv run timesheets status --joplin

Timesheet table format

Each day's entries live in a markdown pipe table with the following columns:

| 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).

| 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):

[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.