feat: JSON, Excel export and time summary
- #23: add JSONFormatter and XLSXFormatter - add openpyxl dependency for .xlsx export - #22: add --summary flag and calculate_summary() in report_builder - ReportRow now carries raw hours for summary calculations - update CLI help and README with .json/.xlsx formats and --summary - add tests for new formatters and summary computation Closes #22, closes #23
This commit is contained in:
@@ -10,7 +10,7 @@ from . import __version__
|
||||
from .client import fetch_issues_with_spent_time
|
||||
from .config import Config
|
||||
from .formatters.factory import get_console_formatter, get_formatter_by_extension
|
||||
from .report_builder import build_grouped_report
|
||||
from .report_builder import build_grouped_report, calculate_summary
|
||||
|
||||
|
||||
def parse_date_range(date_arg: str) -> tuple[str, str]:
|
||||
@@ -52,7 +52,7 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
help="Path to output file (.odt, .csv, .md, .html). If omitted, prints to stdout.",
|
||||
help="Path to output file (.odt, .csv, .md, .html, .json, .xlsx). If omitted, prints to stdout.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--author", default="", help="Override author name from .env (REDMINE_AUTHOR)"
|
||||
@@ -71,6 +71,11 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
version=f"%(prog)s {__version__}",
|
||||
help="Show version and exit",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--summary",
|
||||
action="store_true",
|
||||
help="Print summary (total hours by project/version) to stderr",
|
||||
)
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
# CLI-переопределения имеют приоритет над .env/env.
|
||||
@@ -113,12 +118,20 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
|
||||
rows = build_grouped_report(issue_hours, fill_time=not args.no_time)
|
||||
|
||||
if args.summary:
|
||||
summary = calculate_summary(rows)
|
||||
print(f"⏱️ Total time: {summary['total']}h", file=sys.stderr)
|
||||
for key, value in summary.items():
|
||||
if key.startswith("project:"):
|
||||
project = key.split(":", 1)[1]
|
||||
print(f" {project}: {value}h", file=sys.stderr)
|
||||
|
||||
if args.output:
|
||||
output_ext = os.path.splitext(args.output)[1].lower()
|
||||
|
||||
if not output_ext:
|
||||
print(
|
||||
"❌ Файл без расширения. Укажите расширение: .odt, .csv, .md или .html",
|
||||
"❌ Файл без расширения. Укажите расширение: .odt, .csv, .md, .html, .json или .xlsx",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
@@ -137,7 +150,7 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
known_exts = ", ".join([".odt", ".csv", ".md", ".html"])
|
||||
known_exts = ", ".join([".odt", ".csv", ".md", ".html", ".json", ".xlsx"])
|
||||
print(
|
||||
f"❌ Неизвестный формат файла: {output_ext!r}. "
|
||||
f"Поддерживаются: {known_exts}",
|
||||
|
||||
Reference in New Issue
Block a user