feat: add --my-tasks mode for open assigned issues (#71)
List open Redmine issues assigned to the current (or overridden) user, sorted by priority rank (P1..P5) then version due date with a stable id tie-break. Renders an OSC 8 hyperlinked console table on stdout and a counts/hours summary line on stderr; version due dates are fetched once per unique version, unavailable versions degrade to an empty due date. Also migrates the codebase to ruff 0.17.3 defaults (PEP 585/604 annotations, targeted noqa for intentional blind excepts and CLI root logging). Behavior-neutral: 408 tests pass with -W error.
This commit is contained in:
44
redmine_reporter/formatters/task_table.py
Normal file
44
redmine_reporter/formatters/task_table.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from tabulate import tabulate
|
||||
|
||||
from ..types import TaskRow
|
||||
|
||||
|
||||
class TaskTableFormatter:
|
||||
"""Консольный форматтер таблицы открытых задач пользователя (режим --my-tasks).
|
||||
|
||||
Не входит в фабрику файловых форматтеров: таблица всегда печатается
|
||||
в stdout, поэтому сохранение в файл не поддержано.
|
||||
"""
|
||||
|
||||
def __init__(self, base_url: str) -> None:
|
||||
self.base_url = base_url
|
||||
|
||||
def _hyperlink(self, issue_id: int) -> str:
|
||||
# OSC 8-гиперссылка: номер задачи остаётся видимым терминалом без
|
||||
# поддержки ссылок, а поддерживающие делают его кликабельным.
|
||||
url = f"{self.base_url.rstrip('/')}/issues/{issue_id}"
|
||||
return f"\x1b]8;;{url}\x1b\\{issue_id}\x1b]8;;\x1b\\"
|
||||
|
||||
def format(self, rows: list[TaskRow]) -> str:
|
||||
table_rows = [["ID", "Тема", "Статус", "Приор.", "Версия", "Срок"]]
|
||||
for row in rows:
|
||||
due_text = row["due_text"]
|
||||
if row["overdue"]:
|
||||
due_text = f"{due_text} (!)"
|
||||
table_rows.append(
|
||||
[
|
||||
self._hyperlink(row["issue_id"]),
|
||||
row["subject"],
|
||||
row["status_ru"],
|
||||
row["priority"],
|
||||
row["version_name"] or "-",
|
||||
due_text or "-",
|
||||
]
|
||||
)
|
||||
return tabulate(table_rows, headers="firstrow", tablefmt="simple")
|
||||
|
||||
def save(self, rows: list[TaskRow], output_path: str) -> None:
|
||||
# Консольные форматтеры не умеют сохранять в файл напрямую.
|
||||
raise NotImplementedError(
|
||||
"TaskTableFormatter не поддерживает сохранение в файл."
|
||||
)
|
||||
Reference in New Issue
Block a user