feat(xlsx): make Excel export production-grade

- Add merge cells for project/version groups.
- Add numeric Hours column and human-readable Spent Time.
- Add version/project/grand totals.
- Apply auto-width, freeze panes, auto-filter and styling.
- Respect --no-time: keep columns empty, skip totals.
- Pass no_time flag through formatter factory to all file formatters.
- Add tests for XLSX features and --no-time behavior.

Closes #38
This commit is contained in:
Кокос Артем Николаевич
2026-06-29 14:41:03 +07:00
parent 86f083aa79
commit 222d31730e
8 changed files with 300 additions and 33 deletions

View File

@@ -15,24 +15,28 @@ class CSVFormatter(Formatter):
корректного отображения кириллицы в Microsoft Excel (#26).
"""
def __init__(self, **_kwargs):
def __init__(self, no_time: bool = False, **_kwargs):
super().__init__()
self.no_time = no_time
def format(self, rows: List[ReportRow]) -> str:
output = io.StringIO()
writer = csv.writer(output, dialect="excel")
writer.writerow(["Project", "Version", "Issue ID", "Subject", "Status", "Spent Time"])
headers = ["Project", "Version", "Issue ID", "Subject", "Status"]
if not self.no_time:
headers.append("Spent Time")
writer.writerow(headers)
for r in rows:
writer.writerow(
[
r["project"],
r["version"],
r["issue_id"],
r["subject"],
r["status_ru"],
r["time_text"],
]
)
data = [
r["project"],
r["version"],
r["issue_id"],
r["subject"],
r["status_ru"],
]
if not self.no_time:
data.append(r["time_text"])
writer.writerow(data)
return output.getvalue()
def save(self, rows: List[ReportRow], output_path: str) -> None: