Add --raw flag to csv command to skip aggregation
- Add `to_csv_entries()` to output.py: converts raw rows to write_csv entries one-for-one, without merging by (project, description) - Add `--raw` flag to the csv subparser; _cmd_csv branches on it - Add TestToCsvEntries with 6 tests - Update README with --raw usage example - Add .coverage and htmlcov/ to .gitignore
This commit is contained in:
parent
91ce81a65f
commit
985ee28113
6 changed files with 97 additions and 4 deletions
|
|
@ -19,6 +19,7 @@ from .output import (
|
|||
print_summary_weekly,
|
||||
print_summary_weekly_short,
|
||||
print_summary_weekly_totals,
|
||||
to_csv_entries,
|
||||
write_csv,
|
||||
)
|
||||
from .parser import (
|
||||
|
|
@ -130,6 +131,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
help="Export timesheet entries as CSV.",
|
||||
)
|
||||
_add_shared_args(csv_parser)
|
||||
csv_parser.add_argument(
|
||||
"--raw",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=(
|
||||
"Skip aggregation: output one CSV row per timesheet entry instead of "
|
||||
"combining entries that share the same project and story."
|
||||
),
|
||||
)
|
||||
|
||||
status_parser = subparsers.add_parser(
|
||||
"status",
|
||||
|
|
@ -389,15 +399,15 @@ def _cmd_csv(args: argparse.Namespace, config: dict) -> None:
|
|||
rows = _resolve_rows(args, config, target_date)
|
||||
if not rows:
|
||||
print("Warning: no timesheet rows found in input.", file=sys.stderr)
|
||||
aggregated = aggregate_rows(rows)
|
||||
entries = to_csv_entries(rows) if args.raw else aggregate_rows(rows)
|
||||
project_map = _resolve_project_map(args, config)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w", newline="", encoding="utf-8") as f:
|
||||
write_csv(aggregated, f, date_str, project_map)
|
||||
write_csv(entries, f, date_str, project_map)
|
||||
print(f"Written to {args.output}", file=sys.stderr)
|
||||
else:
|
||||
write_csv(aggregated, sys.stdout, date_str, project_map)
|
||||
write_csv(entries, sys.stdout, date_str, project_map)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -11,6 +11,26 @@ from .projects import resolve_project_task
|
|||
from .utils import decimal_to_hhmm
|
||||
|
||||
|
||||
def to_csv_entries(rows: list[dict]) -> list[dict]:
|
||||
"""Convert raw parsed rows to write_csv-compatible entries without aggregating.
|
||||
|
||||
Each row becomes its own entry. Open entries (duration_hours is None) are
|
||||
skipped. Rows are not combined, even if they share the same project and
|
||||
description.
|
||||
"""
|
||||
from .parser import build_description
|
||||
|
||||
return [
|
||||
{
|
||||
"project": row["project"].strip(),
|
||||
"description": build_description(row["story"], row["note"]),
|
||||
"quantity": row["duration_hours"],
|
||||
}
|
||||
for row in rows
|
||||
if row["duration_hours"] is not None
|
||||
]
|
||||
|
||||
|
||||
def write_csv(
|
||||
aggregated: list[dict],
|
||||
output: IO[str],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue