Tighten configuration and export handling

This commit is contained in:
Кокос Артем Николаевич
2026-05-22 17:41:56 +07:00
parent 8bc8181ce3
commit 2db0ab1f0b
20 changed files with 423 additions and 350 deletions

View File

@@ -1,5 +1,6 @@
from abc import ABC, abstractmethod
from typing import Any, List
from ..types import ReportRow

View File

@@ -1,7 +1,9 @@
from typing import List
from tabulate import tabulate
from .base import Formatter
from ..types import ReportRow
from .base import Formatter
class TableFormatter(Formatter):

View File

@@ -1,8 +1,9 @@
import csv
import io
from typing import List
from .base import Formatter
from ..types import ReportRow
from .base import Formatter
class CSVFormatter(Formatter):
@@ -14,9 +15,7 @@ class CSVFormatter(Formatter):
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"]
)
writer.writerow(["Project", "Version", "Issue ID", "Subject", "Status", "Spent Time"])
for r in rows:
writer.writerow(
[

View File

@@ -1,10 +1,11 @@
from typing import Dict, Type, Optional
from typing import Dict, Optional, Type
from .base import Formatter
from .console import TableFormatter, CompactFormatter
from .console import CompactFormatter, TableFormatter
from .csv import CSVFormatter
from .html import HTMLFormatter
from .markdown import MarkdownFormatter
from .odt import ODTFormatter
from .html import HTMLFormatter
# Словарь для сопоставления расширений файлов с классами форматтеров
FORMATTER_MAP: Dict[str, Type[Formatter]] = {

View File

@@ -1,6 +1,8 @@
from html import escape
from typing import Dict, List
from .base import Formatter
from ..types import ReportRow
from .base import Formatter
class HTMLFormatter(Formatter):
@@ -36,34 +38,38 @@ class HTMLFormatter(Formatter):
]
for project, versions in projects.items():
project_text = escape(project)
total_project_rows = sum(len(tasks) for tasks in versions.values())
first_version_in_project = True
for version, task_rows in versions.items():
version_text = escape(version)
row_span_version = len(task_rows)
first_row_in_version = True
for r in task_rows:
task_cell = escape(f"{r['issue_id']}. {r['subject']}")
status_text = escape(r["status_ru"])
time_text = escape(r["time_text"])
lines.append(" <tr>")
# Ячейка "Проект" - только в первой строке проекта
if first_version_in_project and first_row_in_version:
lines.append(
f' <td rowspan="{total_project_rows}" style="vertical-align: top;">{project}</td>'
f' <td rowspan="{total_project_rows}" style="vertical-align: top;">{project_text}</td>'
)
# Ячейка "Версия" - только в первой строке версии
if first_row_in_version:
lines.append(
f' <td rowspan="{row_span_version}" style="vertical-align: top;">{version}</td>'
f' <td rowspan="{row_span_version}" style="vertical-align: top;">{version_text}</td>'
)
first_row_in_version = False
# Остальные колонки
task_cell = f"{r['issue_id']}. {r['subject']}"
lines.append(f" <td>{task_cell}</td>")
lines.append(f" <td>{r['status_ru']}</td>")
lines.append(f" <td>{r['time_text']}</td>")
lines.append(f" <td>{status_text}</td>")
lines.append(f" <td>{time_text}</td>")
lines.append(" </tr>")

View File

@@ -1,6 +1,11 @@
from typing import List
from .base import Formatter
from ..types import ReportRow
from .base import Formatter
def _escape_markdown_table_cell(value: object) -> str:
return str(value).replace("\\", "\\\\").replace("|", "\\|").replace("\n", "<br>")
class MarkdownFormatter(Formatter):
@@ -15,10 +20,13 @@ class MarkdownFormatter(Formatter):
"|--------|--------|--------|--------|-----------|",
]
for r in rows:
task_cell = f"{r['issue_id']}. {r['subject']}"
task_cell = _escape_markdown_table_cell(f"{r['issue_id']}. {r['subject']}")
lines.append(
f"| {r['display_project']} | {r['display_version']} "
f"| {task_cell} | {r['status_ru']} | {r['time_text']} |"
f"| {_escape_markdown_table_cell(r['display_project'])} "
f"| {_escape_markdown_table_cell(r['display_version'])} "
f"| {task_cell} "
f"| {_escape_markdown_table_cell(r['status_ru'])} "
f"| {_escape_markdown_table_cell(r['time_text'])} |"
)
return "\n".join(lines)

View File

@@ -1,12 +1,14 @@
from importlib import resources
from typing import List
from typing import Dict, List
from odf.opendocument import OpenDocument, load
from odf.style import Style, TableCellProperties, TableColumnProperties
from odf.table import Table, TableCell, TableColumn, TableRow
from odf.text import P
from odf.table import Table, TableColumn, TableRow, TableCell
from odf.style import Style, TableColumnProperties, TableCellProperties
from .base import Formatter
from ..types import ReportRow
from ..utils import get_month_name_from_range
from .base import Formatter
class ODTFormatter(Formatter):
@@ -25,11 +27,7 @@ class ODTFormatter(Formatter):
Форматирует данные в объект OpenDocument.
"""
with (
resources.files("redmine_reporter")
.joinpath("templates", "template.odt")
.open("rb") as f
):
with resources.files("redmine_reporter").joinpath("templates/template.odt").open("rb") as f:
doc = load(f)
para_style_name = "Standard"
@@ -43,9 +41,7 @@ class ODTFormatter(Formatter):
# Стиль ячеек
cell_style_name = "TableCellStyle"
cell_style = Style(name=cell_style_name, family="table-cell")
cell_props = TableCellProperties(
padding="0.04in", border="0.05pt solid #000000"
)
cell_props = TableCellProperties(padding="0.04in", border="0.05pt solid #000000")
cell_style.addElement(cell_props)
doc.automaticstyles.addElement(cell_style)
@@ -73,7 +69,7 @@ class ODTFormatter(Formatter):
header_row.addElement(cell)
table.addElement(header_row)
projects = {}
projects: Dict[str, Dict[str, List[ReportRow]]] = {}
for r in rows:
project = r["project"]
version = r["version"]
@@ -103,9 +99,7 @@ class ODTFormatter(Formatter):
# Ячейка "Проект" - только в первой строке всего проекта
if first_version_in_project and first_row_in_version:
cell_project = TableCell(stylename=cell_style_name)
cell_project.setAttribute(
"numberrowsspanned", str(total_project_rows)
)
cell_project.setAttribute("numberrowsspanned", str(total_project_rows))
p = P(stylename=para_style_name, text=project)
cell_project.addElement(p)
row.addElement(cell_project)
@@ -113,9 +107,7 @@ class ODTFormatter(Formatter):
# Ячейка "Версия" - только в первой строке каждой версии
if first_row_in_version:
cell_version = TableCell(stylename=cell_style_name)
cell_version.setAttribute(
"numberrowsspanned", str(row_span_version)
)
cell_version.setAttribute("numberrowsspanned", str(row_span_version))
p = P(stylename=para_style_name, text=version)
cell_version.addElement(p)
row.addElement(cell_version)