fix: Tabellenfarben beim geglätteten Excel-Import erhalten
This commit is contained in:
@@ -5,7 +5,7 @@ class DocumentationConfig(PluginConfig):
|
|||||||
name = "netbox_documentation"
|
name = "netbox_documentation"
|
||||||
verbose_name = "NetBox Dokumentation"
|
verbose_name = "NetBox Dokumentation"
|
||||||
description = "Wiki und Office-Dokumentation direkt in NetBox"
|
description = "Wiki und Office-Dokumentation direkt in NetBox"
|
||||||
version = "0.7.7"
|
version = "0.7.8"
|
||||||
author = "LKE"
|
author = "LKE"
|
||||||
base_url = "documentation"
|
base_url = "documentation"
|
||||||
min_version = "4.0.0"
|
min_version = "4.0.0"
|
||||||
|
|||||||
@@ -91,11 +91,11 @@ def _xlsx(content, flatten=False):
|
|||||||
rows.pop()
|
rows.pop()
|
||||||
if not rows:
|
if not rows:
|
||||||
continue
|
continue
|
||||||
rendered = _render_flattened_worksheet(sheet) if flatten else _render_excel_worksheet_html(sheet, workbook)
|
rendered = _render_flattened_worksheet(sheet, workbook) if flatten else _render_excel_worksheet_html(sheet, workbook)
|
||||||
sections.append(f"## {sheet.title}\n\n{rendered}" if flatten else f"<h2>{escape(sheet.title)}</h2>\n{rendered}")
|
sections.append(f"<h2>{escape(sheet.title)}</h2>\n{rendered}")
|
||||||
if not sections:
|
if not sections:
|
||||||
raise ImportFailure("Die Arbeitsmappe enthält keine Daten.")
|
raise ImportFailure("Die Arbeitsmappe enthält keine Daten.")
|
||||||
return ImportResult("\n\n".join(sections), body_format="markdown" if flatten else "html")
|
return ImportResult("\n\n".join(sections), body_format="html")
|
||||||
|
|
||||||
|
|
||||||
def import_excel_sheets(
|
def import_excel_sheets(
|
||||||
@@ -140,14 +140,14 @@ def import_excel_sheets(
|
|||||||
markdown = ""
|
markdown = ""
|
||||||
elif has_content:
|
elif has_content:
|
||||||
markdown = (
|
markdown = (
|
||||||
_render_flattened_worksheet(sheet, max_rows=preview_max_rows) if flatten else
|
_render_flattened_worksheet(sheet, workbook, max_rows=preview_max_rows) if flatten else
|
||||||
_render_excel_worksheet_html(sheet, workbook, max_rows=preview_max_rows)
|
_render_excel_worksheet_html(sheet, workbook, max_rows=preview_max_rows)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
markdown = ""
|
markdown = ""
|
||||||
results.append(ExcelSheetResult(
|
results.append(ExcelSheetResult(
|
||||||
title=sheet.title, markdown=markdown, images=images,
|
title=sheet.title, markdown=markdown, images=images,
|
||||||
body_format="markdown" if flatten else "html",
|
body_format="html",
|
||||||
))
|
))
|
||||||
workbook.close()
|
workbook.close()
|
||||||
upload.seek(0)
|
upload.seek(0)
|
||||||
@@ -278,19 +278,26 @@ def _excel_table_cell_styles(sheet, workbook, max_row, max_column):
|
|||||||
return styles
|
return styles
|
||||||
|
|
||||||
|
|
||||||
def _render_excel_worksheet_html(sheet, workbook, max_rows=None):
|
def _render_excel_worksheet_html(sheet, workbook, max_rows=None, bounds=None):
|
||||||
"""Render worksheet values and the most relevant visual Excel cell formatting."""
|
"""Render worksheet values and the most relevant visual Excel cell formatting."""
|
||||||
from openpyxl.utils import get_column_letter
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
if bounds:
|
||||||
|
start_column, start_row, bound_max_column, bound_max_row = bounds
|
||||||
|
else:
|
||||||
|
start_column, start_row, bound_max_column, bound_max_row = 1, 1, sheet.max_column, sheet.max_row
|
||||||
last_row = last_column = 0
|
last_row = last_column = 0
|
||||||
for row in sheet.iter_rows():
|
for row in sheet.iter_rows(
|
||||||
|
min_row=start_row, max_row=bound_max_row,
|
||||||
|
min_col=start_column, max_col=bound_max_column,
|
||||||
|
):
|
||||||
for cell in row:
|
for cell in row:
|
||||||
if cell.value not in (None, ""):
|
if cell.value not in (None, ""):
|
||||||
last_row = max(last_row, cell.row)
|
last_row = max(last_row, cell.row)
|
||||||
last_column = max(last_column, cell.column)
|
last_column = max(last_column, cell.column)
|
||||||
if not last_row:
|
if not last_row:
|
||||||
return "<p><em>Keine Inhalte</em></p>"
|
return "<p><em>Keine Inhalte</em></p>"
|
||||||
max_row = min(last_row, max_rows) if max_rows else last_row
|
max_row = min(last_row, start_row + max_rows - 1) if max_rows else last_row
|
||||||
max_column = last_column
|
max_column = last_column
|
||||||
table_styles = _excel_table_cell_styles(sheet, workbook, max_row, max_column)
|
table_styles = _excel_table_cell_styles(sheet, workbook, max_row, max_column)
|
||||||
merged_starts, merged_children = {}, set()
|
merged_starts, merged_children = {}, set()
|
||||||
@@ -306,16 +313,16 @@ def _render_excel_worksheet_html(sheet, workbook, max_rows=None):
|
|||||||
if (row, column) != (merged.min_row, merged.min_col):
|
if (row, column) != (merged.min_row, merged.min_col):
|
||||||
merged_children.add((row, column))
|
merged_children.add((row, column))
|
||||||
columns = []
|
columns = []
|
||||||
for column in range(1, max_column + 1):
|
for column in range(start_column, max_column + 1):
|
||||||
width = sheet.column_dimensions[get_column_letter(column)].width
|
width = sheet.column_dimensions[get_column_letter(column)].width
|
||||||
columns.append(f'<col style="width: {max(3, min(float(width or 13), 80)):.2f}ch">')
|
columns.append(f'<col style="width: {max(3, min(float(width or 13), 80)):.2f}ch">')
|
||||||
output = ['<table class="doc-table-bordered"><colgroup>', *columns, "</colgroup><tbody>"]
|
output = ['<table class="doc-table-bordered"><colgroup>', *columns, "</colgroup><tbody>"]
|
||||||
for row in range(1, max_row + 1):
|
for row in range(start_row, max_row + 1):
|
||||||
row_style = ""
|
row_style = ""
|
||||||
if sheet.row_dimensions[row].height:
|
if sheet.row_dimensions[row].height:
|
||||||
row_style = f' style="height: {float(sheet.row_dimensions[row].height):g}pt"'
|
row_style = f' style="height: {float(sheet.row_dimensions[row].height):g}pt"'
|
||||||
output.append(f"<tr{row_style}>")
|
output.append(f"<tr{row_style}>")
|
||||||
for column in range(1, max_column + 1):
|
for column in range(start_column, max_column + 1):
|
||||||
if (row, column) in merged_children:
|
if (row, column) in merged_children:
|
||||||
continue
|
continue
|
||||||
cell = sheet.cell(row=row, column=column)
|
cell = sheet.cell(row=row, column=column)
|
||||||
@@ -336,12 +343,12 @@ def _render_excel_worksheet_html(sheet, workbook, max_rows=None):
|
|||||||
output.append(f"<td{' ' if attributes else ''}{' '.join(attributes)}>{value}</td>")
|
output.append(f"<td{' ' if attributes else ''}{' '.join(attributes)}>{value}</td>")
|
||||||
output.append("</tr>")
|
output.append("</tr>")
|
||||||
output.append("</tbody></table>")
|
output.append("</tbody></table>")
|
||||||
if max_rows and last_row > max_rows:
|
if max_rows and last_row > start_row + max_rows - 1:
|
||||||
output.append(f"<p><em>Vorschau auf {max_rows} von {last_row} Zeilen begrenzt.</em></p>")
|
output.append(f"<p><em>Vorschau auf {max_rows} Zeilen begrenzt.</em></p>")
|
||||||
return "".join(output)
|
return "".join(output)
|
||||||
|
|
||||||
|
|
||||||
def _render_flattened_worksheet(sheet, max_rows=None):
|
def _render_flattened_worksheet(sheet, workbook, max_rows=None):
|
||||||
"""Flatten ordinary cells while preserving explicitly defined Excel tables."""
|
"""Flatten ordinary cells while preserving explicitly defined Excel tables."""
|
||||||
from openpyxl.utils.cell import range_boundaries
|
from openpyxl.utils.cell import range_boundaries
|
||||||
|
|
||||||
@@ -373,21 +380,17 @@ def _render_flattened_worksheet(sheet, max_rows=None):
|
|||||||
continue
|
continue
|
||||||
value = sheet.cell(row=row_number, column=column_number).value
|
value = sheet.cell(row=row_number, column=column_number).value
|
||||||
if value not in (None, ""):
|
if value not in (None, ""):
|
||||||
ordinary_values.append(_escape_markdown_text(value))
|
ordinary_values.append(escape(str(value)).replace("\n", "<br>"))
|
||||||
if ordinary_values:
|
if ordinary_values:
|
||||||
blocks.append(" \n".join(ordinary_values))
|
blocks.append("<p>" + "<br>".join(ordinary_values) + "</p>")
|
||||||
for min_col, min_row, max_col, max_row in tables_starting_here:
|
for min_col, min_row, max_col, max_row in tables_starting_here:
|
||||||
rows = []
|
blocks.append(_render_excel_worksheet_html(
|
||||||
for table_row in sheet.iter_rows(
|
sheet, workbook,
|
||||||
min_row=min_row, max_row=min(max_row, rendered_max_row),
|
bounds=(min_col, min_row, max_col, min(max_row, rendered_max_row)),
|
||||||
min_col=min_col, max_col=max_col, values_only=True
|
))
|
||||||
):
|
|
||||||
rows.append(["" if value is None else str(value) for value in table_row])
|
|
||||||
if rows:
|
|
||||||
blocks.append(_render_excel_rows(rows, flatten=False))
|
|
||||||
if max_rows and sheet.max_row > max_rows:
|
if max_rows and sheet.max_row > max_rows:
|
||||||
blocks.append(f"_Vorschau auf {max_rows} von {sheet.max_row} Zeilen begrenzt._")
|
blocks.append(f"<p><em>Vorschau auf {max_rows} von {sheet.max_row} Zeilen begrenzt.</em></p>")
|
||||||
return "\n\n".join(blocks) or "_Keine Inhalte_"
|
return "\n".join(blocks) or "<p><em>Keine Inhalte</em></p>"
|
||||||
|
|
||||||
|
|
||||||
def _pdf(content):
|
def _pdf(content):
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "netbox-documentation"
|
name = "netbox-documentation"
|
||||||
version = "0.7.7"
|
version = "0.7.8"
|
||||||
description = "Integrated Markdown wiki and office document importer for NetBox"
|
description = "Integrated Markdown wiki and office document importer for NetBox"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
+28
-5
@@ -145,7 +145,7 @@ def test_excel_preview_limits_rows_without_affecting_sheet_detection():
|
|||||||
include_image_data=False,
|
include_image_data=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert "Vorschau auf 100 von 151 Zeilen begrenzt" in results[0].markdown
|
assert "Vorschau auf 100 Zeilen begrenzt" in results[0].markdown
|
||||||
assert ">99<" in results[0].markdown
|
assert ">99<" in results[0].markdown
|
||||||
assert ">100<" not in results[0].markdown
|
assert ">100<" not in results[0].markdown
|
||||||
|
|
||||||
@@ -160,10 +160,11 @@ def test_excel_flatten_turns_cells_into_document_flow():
|
|||||||
|
|
||||||
results = import_excel_sheets(Upload(stream.getvalue(), "server.xlsx"), flatten=True)
|
results = import_excel_sheets(Upload(stream.getvalue(), "server.xlsx"), flatten=True)
|
||||||
|
|
||||||
assert results[0].markdown == "Name \nIP\n\ndb01 \n10.0.0.2"
|
assert results[0].body_format == "html"
|
||||||
|
assert results[0].markdown == "<p>Name<br>IP</p>\n<p>db01<br>10.0.0.2</p>"
|
||||||
assert "Datensatz" not in results[0].markdown
|
assert "Datensatz" not in results[0].markdown
|
||||||
assert "Feld" not in results[0].markdown
|
assert "Feld" not in results[0].markdown
|
||||||
assert "|" not in results[0].markdown
|
assert "<table" not in results[0].markdown
|
||||||
|
|
||||||
|
|
||||||
def test_excel_flatten_preserves_explicit_excel_tables_only():
|
def test_excel_flatten_preserves_explicit_excel_tables_only():
|
||||||
@@ -182,6 +183,28 @@ def test_excel_flatten_preserves_explicit_excel_tables_only():
|
|||||||
markdown = results[0].markdown
|
markdown = results[0].markdown
|
||||||
|
|
||||||
assert "Normaler Hinweis" in markdown
|
assert "Normaler Hinweis" in markdown
|
||||||
assert "| Name" in markdown
|
assert "<table" in markdown
|
||||||
assert "| web01" in markdown
|
assert "Name" in markdown
|
||||||
|
assert "web01" in markdown
|
||||||
assert "Datensatz" not in markdown
|
assert "Datensatz" not in markdown
|
||||||
|
|
||||||
|
|
||||||
|
def test_excel_flatten_preserves_colors_of_explicit_excel_tables():
|
||||||
|
workbook = Workbook()
|
||||||
|
sheet = workbook.active
|
||||||
|
sheet["A1"] = "Standort"
|
||||||
|
sheet["B1"] = "Adresse"
|
||||||
|
sheet["A2"] = "Verwaltung"
|
||||||
|
sheet["B2"] = "10.1.2.17"
|
||||||
|
for cell in (sheet["A1"], sheet["B1"]):
|
||||||
|
cell.fill = PatternFill(fill_type="solid", fgColor="A5BF60")
|
||||||
|
cell.font = Font(color="FFFFFF", bold=True)
|
||||||
|
sheet.add_table(Table(displayName="Standorte", ref="A1:B2"))
|
||||||
|
stream = BytesIO()
|
||||||
|
workbook.save(stream)
|
||||||
|
|
||||||
|
results = import_excel_sheets(Upload(stream.getvalue(), "farben.xlsx"), flatten=True)
|
||||||
|
|
||||||
|
assert results[0].body_format == "html"
|
||||||
|
assert results[0].markdown.count("background-color: #a5bf60") == 2
|
||||||
|
assert results[0].markdown.count("color: #ffffff") == 2
|
||||||
|
|||||||
Reference in New Issue
Block a user