import os import sys import argparse from typing import List, Optional from redminelib.resources import Issue from .config import Config from .client import fetch_issues_with_spent_time from .report_builder import build_grouped_report from .formatters.factory import get_formatter_by_extension, get_console_formatter def parse_date_range(date_arg: str) -> tuple[str, str]: if "--" not in date_arg: raise ValueError("Date range must be in format YYYY-MM-DD--YYYY-MM-DD") parts = date_arg.split("--", 1) if len(parts) != 2: raise ValueError("Invalid date range format") return parts[0].strip(), parts[1].strip() def main(argv: Optional[List[str]] = None) -> int: parser = argparse.ArgumentParser( prog="redmine-reporter", description="Generate Redmine issue report based on your time entries.", ) parser.add_argument( "--date", default=Config.get_default_date_range(), # help="Date range in format YYYY-MM-DD--YYYY-MM-DD (default: %(default)s)" help="Date range in format YYYY-MM-DD--YYYY-MM-DD (default from .env or %(default)s)", ) parser.add_argument( "--compact", action="store_true", help="Use compact plain-text output instead of table" ) parser.add_argument( "--output", help="Path to output .odt file (e.g., report.odt). If omitted, prints to stdout.", ) parser.add_argument( "--author", default="", help="Override author name from .env (REDMINE_AUTHOR)" ) parser.add_argument( "--no-time", action="store_true", help="Do not include spent time into table" ) args = parser.parse_args(argv) try: Config.validate() except ValueError as e: print(f"❌ Configuration error: {e}", file=sys.stderr) return 1 try: from_date, to_date = parse_date_range(args.date) except ValueError as e: print(f"❌ Date error: {e}", file=sys.stderr) return 1 try: issue_hours = fetch_issues_with_spent_time(from_date, to_date) except Exception as e: print(f"❌ Redmine API error: {e}", file=sys.stderr) return 1 if issue_hours is None: print("ℹ️ No time entries found in the given period.", file=sys.stderr) return 0 print(f"✅ Total issues: {len(issue_hours)} [{args.date}]") rows = build_grouped_report(issue_hours, fill_time=not args.no_time) if args.output: output_ext = os.path.splitext(args.output)[1].lower() formatter = get_formatter_by_extension( output_ext, author=Config.get_author(args.author), from_date=from_date, to_date=to_date ) if not formatter: print(f"❌ Неизвестный формат файла: {output_ext}", file=sys.stderr) return 1 try: formatter.save(rows, args.output) print(f"✅ Report saved to {args.output}") except ImportError as e: if output_ext == ".odt": print("❌ odfpy is not installed. Install with: pip install odfpy", file=sys.stderr) else: print(f"❌ Import error: {e}", file=sys.stderr) return 1 except Exception as e: fmt = "ODT" if output_ext == ".odt" else ("CSV" if output_ext == ".csv" else "Markdown") print(f"❌ {fmt} export error: {e}", file=sys.stderr) return 1 else: if args.compact: formatter = get_console_formatter("compact") else: formatter = get_console_formatter("table") if not formatter: print("❌ Неизвестный тип консольного форматтера.", file=sys.stderr) return 1 try: output = formatter.format(rows) print(output) except Exception as e: print(f"❌ Formatting error: {e}", file=sys.stderr) return 1 return 0 if __name__ == "__main__": sys.exit(main())