- Go 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
The arrow between the week dates is drawn at 75% of the row height, gets a 1.5px stroke and 25% deeper heads, and the start/end date text drops the green/red coloring for the plain on-surface color. The now-unused week tokens and classes are removed. |
||
| .forgejo/workflows | ||
| docs/decisions | ||
| internal | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| README.md | ||
timesheet-manager
Summarize weekly Primavera timesheet HTML reports into monthly summaries, written as HTML or CSV.
Scope
- Identify
.htmlfiles in a directory as Primavera timesheet reports, based on HTML content markers (not filenames). - Parse an identified report into an in-memory week: employee, timesheet period, status, per-hour activity rows, day totals, grand total, and time off.
- Rename reports to the canonical filename pattern.
- Summarize the weeks into a monthly total, splitting weeks that cross a month boundary by calendar day, and write the summary as HTML or CSV.
Usage
timesheet-manager <command> [options]
rename
timesheet-manager rename <dir> [--dry-run] [--verbose]
Scans dir for Primavera timesheet HTML reports (identified by content, not filename; only .html files are examined) and renames each to the canonical Timesheet Report <YYMMDD>-<YYMMDD>.html, derived from the parsed timesheet period — never from the existing filename.
- The rename is idempotent: a file already carrying its canonical name is reported as a no-op and left untouched.
- With
--dry-run, nothing is written; planned renames are printed aswould rename <old> -> <new>. - A rename that would overwrite an existing file, or that would collide with another report's canonical name, is reported as an error and skipped (fail-safe).
- An identified report that fails to parse is reported as an error; the command still processes the remaining reports and exits non-zero if any errors occurred.
The command prints a summary line of the form rename: <renamed> renamed, <no-ops> no-op, <skipped> skipped, <errors> errors.
summarize
timesheet-manager summarize <dir> [--month YYYY-MM] [--output <path>] [--format html|csv] [--dry-run] [--partial] [--verbose]
Parses every identified timesheet report in dir, aggregates the in-month hours (weeks crossing a month boundary are split by calendar day), and writes monthly summary file(s).
- Month selection:
--month YYYY-MMsummarizes exactly that month. Without--month, every month touched by the source reports' periods is discovered and summarized in ascending order — one summary per month that passes the coverage gate; a month failing the gate is skipped with a note on stderr (or generated with the partial-coverage notice when--partialis given). - Format selection:
--format htmlor--format csvpicks the output format. A--outputpath ending in.htmlor.csvselects the format by extension; an explicit--formatthat disagrees with the extension is an error. The default format ishtml. - Default output:
<dir>/Timesheet Summary <YYMM>.htmlor.csv(e.g.Timesheet Summary 2609.html), one per summarized month, unless--outputis given.--outputis only valid when exactly one summary is written; otherwise the command errors and writes nothing — pass--month YYYY-MMto pick one. - With
--dry-run, nothing is written; the command printswould write <path> (billable ... h, time off ... h). - Any identified report that fails to parse aborts the summary: the command prints the failing path(s) and exits non-zero, rather than writing a partial-month summary that understates the totals.
- Without
--partial, an explicitly selected month (--month) that is not fully covered makes the command exit non-zero; with--partial, the summary is generated anyway and carries a partial-coverage notice. In month-discovery mode (no--month) the command keeps going instead: incomplete months are skipped with a note on stderr and do not affect the exit code. - If no timesheet reports are found, the command exits non-zero with
no timesheet reports found in <dir>. summarizeassumes one employee per directory: the summary is attributed to the employee of the parsed weeks; mixed employees are not detected.
The HTML summary includes a per-week Weekly contribution table whose Source column records each week's traceability as <filename> (<7-char sha256>) — the source report's filename plus the first 7 characters of its SHA-256 digest. The CSV summary has the header Project ID,Activity ID,Activity Name,Hours, one row per activity, and a final Total row, with LF line endings.
Canonical filename patterns
- Timesheet reports:
Timesheet Report <YYMMDD>-<YYMMDD>.html(period start–end, e.g.Timesheet Report 260802-260808.html). - Monthly summaries:
Timesheet Summary <YYMM>.htmlorTimesheet Summary <YYMM>.csv(e.g.Timesheet Summary 2609.csv).
Development
go build ./...
go vet ./...
go test ./...
- Go version: 1.27+ (see
go.mod). - Parsing uses
golang.org/x/net/html; HTML summary output useshtml/template; CSV output usesencoding/csv. - Library choices: see
docs/decisions/0002-html-libraries.md(supersedingdocs/decisions/0001-libraries.md). - Tests are self-contained: they build synthetic HTML strings in code, with no test fixtures and no identifying data.
License
AGPL-3.0 — see LICENSE.