feat: set up modularized version of project with testing
This commit is contained in:
commit
7bea08ddac
19 changed files with 1138 additions and 0 deletions
85
src/timesheets/cli.py
Normal file
85
src/timesheets/cli.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from datetime import date
|
||||
|
||||
from .output import print_summary, write_csv
|
||||
from .parser import aggregate_rows, detect_has_duration_column, parse_table
|
||||
from .projects import load_project_map
|
||||
from .utils import format_date
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Parse a markdown timesheet table and output a CSV file."
|
||||
)
|
||||
parser.add_argument(
|
||||
"input",
|
||||
help="Path to the markdown file containing the timesheet table, or '-' to read from stdin.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o", "--output",
|
||||
help="Path to the output CSV file. Defaults to stdout.",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--date",
|
||||
help="Date to use in the output (DD/MM/YY). Defaults to today.",
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--map",
|
||||
help=(
|
||||
"Path to a JSON file mapping project keys to Project+Task pairs. "
|
||||
"Defaults to project_map.json in the current working directory if it exists."
|
||||
),
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--summary",
|
||||
action="store_true",
|
||||
help="Print a human-readable summary instead of writing CSV.",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = build_parser().parse_args()
|
||||
|
||||
date_str = args.date or format_date(date.today())
|
||||
|
||||
if args.input == "-":
|
||||
content = sys.stdin.read()
|
||||
else:
|
||||
try:
|
||||
with open(args.input, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: file not found: {args.input}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
lines = content.splitlines()
|
||||
rows = parse_table(lines, has_duration_col=detect_has_duration_column(lines))
|
||||
|
||||
if not rows:
|
||||
print("Warning: no timesheet rows found in input.", file=sys.stderr)
|
||||
|
||||
aggregated = aggregate_rows(rows)
|
||||
|
||||
# Resolve project map: explicit --map flag, else project_map.json in cwd
|
||||
map_path = args.map
|
||||
if map_path is None:
|
||||
default_map = os.path.join(os.getcwd(), "project_map.json")
|
||||
if os.path.exists(default_map):
|
||||
map_path = default_map
|
||||
|
||||
project_map = load_project_map(map_path)
|
||||
|
||||
if args.summary:
|
||||
print_summary(aggregated, project_map)
|
||||
elif args.output:
|
||||
with open(args.output, "w", newline="", encoding="utf-8") as f:
|
||||
write_csv(aggregated, f, date_str, project_map)
|
||||
print(f"Written to {args.output}", file=sys.stderr)
|
||||
else:
|
||||
write_csv(aggregated, sys.stdout, date_str, project_map)
|
||||
Loading…
Add table
Add a link
Reference in a new issue