Summarize weekly Primavera timesheet HTML reports into monthly HTML or CSV summaries
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
emidab 8595b96597 style(report): lighter week arrow and plain default date colors
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.
2026-10-03 19:15:45 +00:00
.forgejo/workflows chore: disable CI workflow until runners exist 2026-10-03 12:23:20 +00:00
docs/decisions docs: describe HTML input and HTML/CSV summary output 2026-10-03 07:12:29 +00:00
internal style(report): lighter week arrow and plain default date colors 2026-10-03 19:15:45 +00:00
.gitignore refactor: remove all PDF handling for HTML/CSV rework 2026-10-02 21:19:07 +00:00
go.mod feat(parse): parse Primavera timesheet HTML into model.Week 2026-10-02 22:09:49 +00:00
go.sum feat(parse): parse Primavera timesheet HTML into model.Week 2026-10-02 22:09:49 +00:00
LICENSE chore: license the project under AGPL-3.0 2026-10-03 12:20:06 +00:00
main.go refactor: move the CLI entrypoint to the repo root 2026-10-03 07:23:14 +00:00
README.md chore: license the project under AGPL-3.0 2026-10-03 12:20:06 +00:00

timesheet-manager

Summarize weekly Primavera timesheet HTML reports into monthly summaries, written as HTML or CSV.

Scope

  • Identify .html files 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 as would 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-MM summarizes 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 --partial is given).
  • Format selection: --format html or --format csv picks the output format. A --output path ending in .html or .csv selects the format by extension; an explicit --format that disagrees with the extension is an error. The default format is html.
  • Default output: <dir>/Timesheet Summary <YYMM>.html or .csv (e.g. Timesheet Summary 2609.html), one per summarized month, unless --output is given. --output is only valid when exactly one summary is written; otherwise the command errors and writes nothing — pass --month YYYY-MM to pick one.
  • With --dry-run, nothing is written; the command prints would 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>.
  • summarize assumes 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>.html or Timesheet 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 uses html/template; CSV output uses encoding/csv.
  • Library choices: see docs/decisions/0002-html-libraries.md (superseding docs/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.