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:
@@ -1,5 +1,3 @@
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
|
||||
from openpyxl.utils import get_column_letter
|
||||
@@ -36,7 +34,7 @@ class XLSXFormatter(Formatter):
|
||||
super().__init__()
|
||||
self.no_time = no_time
|
||||
|
||||
def format(self, rows: List[ReportRow]) -> Workbook:
|
||||
def format(self, rows: list[ReportRow]) -> Workbook:
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Report"
|
||||
@@ -56,9 +54,9 @@ class XLSXFormatter(Formatter):
|
||||
grouped = group_rows_by_project_and_version(rows)
|
||||
|
||||
current_row = 2
|
||||
project_ranges: List[Tuple[int, int]] = []
|
||||
version_ranges: List[Tuple[int, int]] = []
|
||||
project_totals: Dict[str, float] = {}
|
||||
project_ranges: list[tuple[int, int]] = []
|
||||
version_ranges: list[tuple[int, int]] = []
|
||||
project_totals: dict[str, float] = {}
|
||||
|
||||
for project, versions in grouped.items():
|
||||
project_start_row = current_row
|
||||
@@ -175,11 +173,11 @@ class XLSXFormatter(Formatter):
|
||||
|
||||
return wb
|
||||
|
||||
def save(self, rows: List[ReportRow], output_path: str) -> None:
|
||||
def save(self, rows: list[ReportRow], output_path: str) -> None:
|
||||
wb = self.format(rows)
|
||||
wb.save(output_path)
|
||||
|
||||
def _style_header_row(self, ws: Worksheet, headers: List[str]) -> None:
|
||||
def _style_header_row(self, ws: Worksheet, headers: list[str]) -> None:
|
||||
for col_idx, _ in enumerate(headers, start=1):
|
||||
cell = ws.cell(row=1, column=col_idx)
|
||||
cell.font = Font(bold=True)
|
||||
@@ -193,9 +191,7 @@ class XLSXFormatter(Formatter):
|
||||
for col_idx in range(1, 8):
|
||||
cell = ws.cell(row=row, column=col_idx)
|
||||
cell.border = self._BORDER
|
||||
if col_idx in (1, 2):
|
||||
cell.alignment = Alignment(vertical="top", wrap_text=True)
|
||||
elif col_idx == 4:
|
||||
if col_idx in (1, 2) or col_idx == 4:
|
||||
cell.alignment = Alignment(vertical="top", wrap_text=True)
|
||||
else:
|
||||
cell.alignment = Alignment(vertical="top")
|
||||
@@ -210,7 +206,7 @@ class XLSXFormatter(Formatter):
|
||||
|
||||
def _apply_column_widths(self, ws: Worksheet) -> None:
|
||||
# Минимальные ширины по умолчанию
|
||||
widths: Dict[int, float] = {
|
||||
widths: dict[int, float] = {
|
||||
1: 18.0,
|
||||
2: 16.0,
|
||||
3: 12.0,
|
||||
|
||||
Reference in New Issue
Block a user