Add group_rows_by_project_and_version() to report_builder.py and replace the duplicated inline grouping in both html.py and odt.py with a single call to the shared function. This ensures consistent grouping behavior across formatters and removes a maintenance burden — any change to grouping logic now happens in one place. Closes #19
99 lines
3.6 KiB
Python
99 lines
3.6 KiB
Python
from typing import Dict, List, Tuple, cast
|
||
|
||
from redminelib.resources import Issue
|
||
|
||
from .types import ReportRow
|
||
from .utils import get_version, hours_to_human
|
||
|
||
STATUS_TRANSLATION = {
|
||
"New": "В работе",
|
||
"In Progress": "В работе",
|
||
"Feedback": "В работе",
|
||
"Re-opened": "В работе",
|
||
"Code Review": "Решена",
|
||
"Wait Release": "Закрыто",
|
||
"Pending": "Ожидание",
|
||
"Resolved": "Решена",
|
||
"Testing": "Решена",
|
||
"Confirming": "Ожидание",
|
||
"Closed": "Закрыто",
|
||
"Rejected": "Закрыто",
|
||
"Frozen": "Ожидание",
|
||
}
|
||
|
||
|
||
def build_grouped_report(
|
||
issue_hours: List[Tuple[Issue, float]],
|
||
fill_time: bool = True,
|
||
) -> List[ReportRow]:
|
||
"""
|
||
Преобразует список задач с затраченным временем в плоский список строк отчёта,
|
||
с учётом группировки по проекту и версии (пустые ячейки для повторяющихся значений).
|
||
|
||
Предусловие: issue_hours должен быть отсортирован по (project, version).
|
||
Функция выполняет сортировку самостоятельно для защиты от несортированного ввода.
|
||
"""
|
||
|
||
# Защитная сортировка -- гарантирует корректную группировку независимо от порядка на входе
|
||
issue_hours = sorted(issue_hours, key=lambda x: (str(x[0].project), get_version(x[0]), x[0].id))
|
||
|
||
rows: List[ReportRow] = []
|
||
prev_project: str = ""
|
||
prev_version: str = ""
|
||
|
||
for issue, hours in issue_hours:
|
||
project = str(issue.project)
|
||
version = get_version(issue)
|
||
status_en = str(issue.status)
|
||
status_ru = STATUS_TRANSLATION.get(status_en, status_en)
|
||
time_text = hours_to_human(hours) if fill_time else ""
|
||
|
||
display_project = project if project != prev_project else ""
|
||
display_version = version if (project != prev_project or version != prev_version) else ""
|
||
|
||
rows.append(
|
||
cast(
|
||
ReportRow,
|
||
{
|
||
"project": project,
|
||
"version": version,
|
||
"display_project": display_project,
|
||
"display_version": display_version,
|
||
"issue_id": issue.id,
|
||
"subject": issue.subject,
|
||
"status_ru": status_ru,
|
||
"time_text": time_text,
|
||
},
|
||
)
|
||
)
|
||
|
||
prev_project = project
|
||
prev_version = version
|
||
|
||
return rows
|
||
|
||
|
||
def group_rows_by_project_and_version(
|
||
rows: List[ReportRow],
|
||
) -> Dict[str, Dict[str, List[ReportRow]]]:
|
||
"""
|
||
Группирует плоский список строк отчёта в иерархию project → version → [rows].
|
||
|
||
Предполагается, что rows уже отсортирован (build_grouped_report это гарантирует).
|
||
Возвращает обычный dict, сохраняющий порядок вставки (Python 3.7+).
|
||
Используется форматтерами HTML и ODT для объединения ячеек.
|
||
"""
|
||
projects: Dict[str, Dict[str, List[ReportRow]]] = {}
|
||
for r in rows:
|
||
project = r["project"]
|
||
version = r["version"]
|
||
|
||
if project not in projects:
|
||
projects[project] = {}
|
||
if version not in projects[project]:
|
||
projects[project][version] = []
|
||
|
||
projects[project][version].append(r)
|
||
|
||
return projects
|