feat: set up modularized version of project with testing

This commit is contained in:
Jef Roosens 2026-05-22 10:09:59 +02:00
commit 7bea08ddac
Signed by: Jef Roosens
GPG key ID: 119385BCAA005C21
19 changed files with 1138 additions and 0 deletions

54
src/timesheets/output.py Normal file
View file

@ -0,0 +1,54 @@
import csv
import sys
from collections import OrderedDict
from typing import IO
from .projects import resolve_project_task
from .utils import decimal_to_hhmm
def write_csv(
aggregated: list[dict],
output: IO[str],
date_str: str,
project_map: dict,
) -> None:
"""Write the aggregated timesheet data as CSV."""
writer = csv.writer(output)
writer.writerow(["Date*", "Project*", "Task", "Description", "Quantity"])
for entry in aggregated:
project, task = resolve_project_task(entry["project"], project_map)
writer.writerow(
[
date_str,
project,
task,
entry["description"],
f"{entry['quantity']:.2f}",
]
)
def print_summary(aggregated: list[dict], project_map: dict) -> None:
"""Print a human-readable summary of time blocks to stdout."""
grouped: dict[str, list[dict]] = OrderedDict()
for entry in aggregated:
project, task = resolve_project_task(entry["project"], project_map)
label = f"{project} / {task}" if task else project
grouped.setdefault(label, []).append(entry)
total_all = sum(e["quantity"] for e in aggregated)
all_descs = [e["description"] for e in aggregated]
desc_width = max(max((len(d) for d in all_descs), default=40), 40)
separator = "-" * (desc_width + 16)
for label, entries in grouped.items():
project_total = sum(e["quantity"] for e in entries)
print(f"\n {label} ({decimal_to_hhmm(project_total)})")
print(" " + separator)
for entry in entries:
print(f" {entry['description']:<{desc_width}} {decimal_to_hhmm(entry['quantity'])}")
print(" " + separator)
print(f"\n {'TOTAL':<{desc_width + 2}} {decimal_to_hhmm(total_all)}\n")