From 5a5ee00726f208e67c3c8b9bb08f0e7dd385cd10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D0=BA=D0=BE=D1=81=20=D0=90=D1=80=D1=82=D0=B5?= =?UTF-8?q?=D0=BC=20=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8?= =?UTF-8?q?=D1=87?= Date: Wed, 21 Jan 2026 14:00:13 +0700 Subject: [PATCH] Config: Report author name --- redmine_reporter/cli.py | 11 +++++++++-- redmine_reporter/config.py | 10 ++++++++++ redmine_reporter/formatter_odt.py | 11 +++++++++-- redmine_reporter/utils.py | 25 +++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/redmine_reporter/cli.py b/redmine_reporter/cli.py index b974ac8..b10f1c2 100644 --- a/redmine_reporter/cli.py +++ b/redmine_reporter/cli.py @@ -2,9 +2,11 @@ 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 .formatter import format_compact, format_table +from .formatter_odt import format_odt def parse_date_range(date_arg: str) -> tuple[str, str]: @@ -36,6 +38,11 @@ def main(argv: Optional[List[str]] = None) -> int: "--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)" + ) args = parser.parse_args(argv) try: @@ -67,8 +74,8 @@ def main(argv: Optional[List[str]] = None) -> int: print("❌ Output file must end with .odt", file=sys.stderr) return 1 try: - from .formatter_odt import format_odt - doc = format_odt(issue_hours) + author = Config.get_author(args.author) + doc = format_odt(issue_hours, author=author, from_date=from_date, to_date=to_date) doc.save(args.output) print(f"✅ Report saved to {args.output}") except ImportError: diff --git a/redmine_reporter/config.py b/redmine_reporter/config.py index b943837..e029256 100644 --- a/redmine_reporter/config.py +++ b/redmine_reporter/config.py @@ -9,9 +9,19 @@ class Config: REDMINE_URL = os.getenv("REDMINE_URL", "").rstrip("/") REDMINE_USER = os.getenv("REDMINE_USER") REDMINE_PASSWORD = os.getenv("REDMINE_PASSWORD") + REDMINE_AUTHOR = os.getenv("REDMINE_AUTHOR") DEFAULT_FROM_DATE = os.getenv("DEFAULT_FROM_DATE") DEFAULT_TO_DATE = os.getenv("DEFAULT_TO_DATE") + @classmethod + def get_author(cls, cli_author: str = "") -> str: + """Возвращает автора: из CLI если задан, иначе из .env, иначе — заглушку.""" + if cli_author: + return cli_author + if cls.REDMINE_AUTHOR: + return cls.REDMINE_AUTHOR + return "" + @classmethod def get_default_date_range(cls) -> str: if cls.DEFAULT_FROM_DATE and cls.DEFAULT_TO_DATE: diff --git a/redmine_reporter/formatter_odt.py b/redmine_reporter/formatter_odt.py index 8ad9a60..17294f7 100644 --- a/redmine_reporter/formatter_odt.py +++ b/redmine_reporter/formatter_odt.py @@ -6,9 +6,15 @@ from odf.text import P from odf.table import Table, TableColumn, TableRow, TableCell from .formatter import get_version, hours_to_human, STATUS_TRANSLATION +from .utils import get_month_name_from_range -def format_odt(issue_hours: List[Tuple[Issue, float]]) -> "OpenDocument": +def format_odt( + issue_hours: List[Tuple[Issue, float]], + author: str = "", + from_date: str = "", + to_date: str = "" +) -> "OpenDocument": template_path = "template.odt" if not os.path.exists(template_path): raise FileNotFoundError("Шаблон template.odt не найден...") @@ -17,7 +23,8 @@ def format_odt(issue_hours: List[Tuple[Issue, float]]) -> "OpenDocument": para_style_name = "Standard" # Заголовок - header_text = "Кокос Артём Николаевич. Отчет за месяц Июль." + month_name = get_month_name_from_range(from_date, to_date) + header_text = f"{author}. Отчет за месяц {month_name}." header_paragraph = P(stylename=para_style_name, text=header_text) doc.text.addElement(header_paragraph) diff --git a/redmine_reporter/utils.py b/redmine_reporter/utils.py index 3d44ad2..8424e04 100644 --- a/redmine_reporter/utils.py +++ b/redmine_reporter/utils.py @@ -1,2 +1,27 @@ +from datetime import datetime + + +def get_month_name_from_range(from_date: str, to_date: str) -> str: + """Определяет название месяца по диапазону дат. + Если from == to — возвращает месяц этой даты. + Если диапазон охватывает несколько месяцев — возвращает 'период'. + """ + + try: + start = datetime.strptime(from_date, "%Y-%m-%d") + end = datetime.strptime(to_date, "%Y-%m-%d") + except ValueError: + return "период" + + if start.year == end.year and start.month == end.month: + months = [ + "", "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", + "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" + ] + return months[start.month] + else: + return "период" + + def get_version(issue) -> str: return str(getattr(issue, 'fixed_version', ''))