Add date_format config key for csv command
- Add get_date_format() to config.py: reads [csv] date_format, returns None when absent (falls back to the default %d/%m/%y) - _cmd_csv applies the format to both single-day and weekly date strings - Add 3 tests for get_date_format in TestGetters - Update timesheets.example.toml and README with [csv] date_format key
This commit is contained in:
parent
0204accd05
commit
8b6f0b24e2
5 changed files with 29 additions and 3 deletions
|
|
@ -100,6 +100,9 @@ map = "/path/to/project_map.json"
|
||||||
[work]
|
[work]
|
||||||
daily_hours = 8.0
|
daily_hours = 8.0
|
||||||
weekly_hours = 40.0
|
weekly_hours = 40.0
|
||||||
|
|
||||||
|
[csv]
|
||||||
|
date_format = "%d/%m/%y" # default; any strftime format string
|
||||||
```
|
```
|
||||||
|
|
||||||
Priority order: **CLI flag > config file > environment variable > default**.
|
Priority order: **CLI flag > config file > environment variable > default**.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ from datetime import date
|
||||||
from .config import (
|
from .config import (
|
||||||
find_default_config,
|
find_default_config,
|
||||||
get_daily_target,
|
get_daily_target,
|
||||||
|
get_date_format,
|
||||||
get_map_path,
|
get_map_path,
|
||||||
get_token,
|
get_token,
|
||||||
get_weekly_target,
|
get_weekly_target,
|
||||||
|
|
@ -422,6 +423,10 @@ def _cmd_stories(args: argparse.Namespace, config: dict) -> None:
|
||||||
def _cmd_csv(args: argparse.Namespace, config: dict) -> None:
|
def _cmd_csv(args: argparse.Namespace, config: dict) -> None:
|
||||||
target_date, date_str = _resolve_date(args)
|
target_date, date_str = _resolve_date(args)
|
||||||
project_map = _resolve_project_map(args, config)
|
project_map = _resolve_project_map(args, config)
|
||||||
|
date_fmt = get_date_format(config)
|
||||||
|
|
||||||
|
def _fmt(d: date) -> str:
|
||||||
|
return d.strftime(date_fmt) if date_fmt else format_date(d)
|
||||||
|
|
||||||
if args.weekly:
|
if args.weekly:
|
||||||
day_sections_raw = _resolve_week_sections(args, config, target_date)
|
day_sections_raw = _resolve_week_sections(args, config, target_date)
|
||||||
|
|
@ -430,7 +435,7 @@ def _cmd_csv(args: argparse.Namespace, config: dict) -> None:
|
||||||
return
|
return
|
||||||
day_sections = [
|
day_sections = [
|
||||||
(
|
(
|
||||||
format_date(day),
|
_fmt(day),
|
||||||
to_csv_entries(rows) if args.raw else aggregate_rows(rows),
|
to_csv_entries(rows) if args.raw else aggregate_rows(rows),
|
||||||
)
|
)
|
||||||
for day, rows in day_sections_raw
|
for day, rows in day_sections_raw
|
||||||
|
|
@ -448,10 +453,10 @@ def _cmd_csv(args: argparse.Namespace, config: dict) -> None:
|
||||||
entries = to_csv_entries(rows) if args.raw else aggregate_rows(rows)
|
entries = to_csv_entries(rows) if args.raw else aggregate_rows(rows)
|
||||||
if args.output:
|
if args.output:
|
||||||
with open(args.output, "w", newline="", encoding="utf-8") as f:
|
with open(args.output, "w", newline="", encoding="utf-8") as f:
|
||||||
write_csv(entries, f, date_str, project_map)
|
write_csv(entries, f, _fmt(target_date), project_map)
|
||||||
print(f"Written to {args.output}", file=sys.stderr)
|
print(f"Written to {args.output}", file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
write_csv(entries, sys.stdout, date_str, project_map)
|
write_csv(entries, sys.stdout, _fmt(target_date), project_map)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,11 @@ def get_map_path(config: dict) -> str | None:
|
||||||
return config.get("projects", {}).get("map")
|
return config.get("projects", {}).get("map")
|
||||||
|
|
||||||
|
|
||||||
|
def get_date_format(config: dict) -> str | None:
|
||||||
|
"""Extract csv.date_format from config, or None to use the default."""
|
||||||
|
return config.get("csv", {}).get("date_format")
|
||||||
|
|
||||||
|
|
||||||
def get_daily_target(config: dict) -> float:
|
def get_daily_target(config: dict) -> float:
|
||||||
"""Extract work.daily_hours from config, defaulting to 8.0."""
|
"""Extract work.daily_hours from config, defaulting to 8.0."""
|
||||||
return float(config.get("work", {}).get("daily_hours", 8.0))
|
return float(config.get("work", {}).get("daily_hours", 8.0))
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import pytest
|
||||||
from timesheets.config import (
|
from timesheets.config import (
|
||||||
DEFAULT_CONFIG_FILENAME,
|
DEFAULT_CONFIG_FILENAME,
|
||||||
find_default_config,
|
find_default_config,
|
||||||
|
get_date_format,
|
||||||
get_map_path,
|
get_map_path,
|
||||||
get_token,
|
get_token,
|
||||||
load_config,
|
load_config,
|
||||||
|
|
@ -86,3 +87,12 @@ class TestGetters:
|
||||||
|
|
||||||
def test_get_map_path_missing_key(self):
|
def test_get_map_path_missing_key(self):
|
||||||
assert get_map_path({"projects": {}}) is None
|
assert get_map_path({"projects": {}}) is None
|
||||||
|
|
||||||
|
def test_get_date_format_present(self):
|
||||||
|
assert get_date_format({"csv": {"date_format": "%Y-%m-%d"}}) == "%Y-%m-%d"
|
||||||
|
|
||||||
|
def test_get_date_format_missing_section(self):
|
||||||
|
assert get_date_format({}) is None
|
||||||
|
|
||||||
|
def test_get_date_format_missing_key(self):
|
||||||
|
assert get_date_format({"csv": {}}) is None
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,6 @@ map = "/path/to/project_map.json"
|
||||||
[work]
|
[work]
|
||||||
daily_hours = 8.0
|
daily_hours = 8.0
|
||||||
weekly_hours = 40.0
|
weekly_hours = 40.0
|
||||||
|
|
||||||
|
[csv]
|
||||||
|
# date_format = "%Y-%m-%d" # default is "%d/%m/%y" (e.g. 28/05/26)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue