perf: nur ausgewählte Excel-Blätter vollständig importieren
This commit is contained in:
@@ -5,7 +5,7 @@ class DocumentationConfig(PluginConfig):
|
||||
name = "netbox_documentation"
|
||||
verbose_name = "NetBox Dokumentation"
|
||||
description = "Wiki und Office-Dokumentation direkt in NetBox"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
author = "LKE"
|
||||
base_url = "documentation"
|
||||
min_version = "4.0.0"
|
||||
|
||||
@@ -30,6 +30,7 @@ class ExcelSheetResult:
|
||||
markdown: str
|
||||
images: list[ImportedImage] = field(default_factory=list)
|
||||
body_format: str = "markdown"
|
||||
source_index: int = 0
|
||||
|
||||
|
||||
def import_document(upload, flatten_excel=False) -> ImportResult:
|
||||
@@ -100,7 +101,7 @@ def _xlsx(content, flatten=False):
|
||||
|
||||
def import_excel_sheets(
|
||||
upload, flatten=False, *, metadata_only=False, preview_max_rows=None,
|
||||
include_image_data=True,
|
||||
include_image_data=True, selected_indexes=None,
|
||||
) -> list[ExcelSheetResult]:
|
||||
"""Convert each non-empty worksheet into an individual document payload."""
|
||||
from openpyxl import load_workbook
|
||||
@@ -112,7 +113,10 @@ def import_excel_sheets(
|
||||
upload.seek(0)
|
||||
workbook = load_workbook(upload, read_only=False, data_only=True)
|
||||
results = []
|
||||
for sheet in workbook.worksheets:
|
||||
selected_indexes = set(selected_indexes) if selected_indexes is not None else None
|
||||
for source_index, sheet in enumerate(workbook.worksheets):
|
||||
if selected_indexes is not None and source_index not in selected_indexes:
|
||||
continue
|
||||
images = []
|
||||
for number, image in enumerate(getattr(sheet, "_images", ()), 1):
|
||||
image_format = (getattr(image, "format", None) or "png").lower()
|
||||
@@ -147,7 +151,7 @@ def import_excel_sheets(
|
||||
markdown = ""
|
||||
results.append(ExcelSheetResult(
|
||||
title=sheet.title, markdown=markdown, images=images,
|
||||
body_format="html",
|
||||
body_format="html", source_index=source_index,
|
||||
))
|
||||
workbook.close()
|
||||
upload.seek(0)
|
||||
|
||||
@@ -201,7 +201,10 @@ class DocumentImportView(PermissionRequiredMixin, View):
|
||||
user=request.user, category=form.cleaned_data["category"], file=upload,
|
||||
original_name=upload.name, content_type=upload.content_type or "",
|
||||
flatten=form.cleaned_data.get("flatten_excel_tables", False),
|
||||
sheet_metadata=[{"title": sheet.title, "image_count": len(sheet.images)} for sheet in sheets],
|
||||
sheet_metadata=[{
|
||||
"title": sheet.title, "image_count": len(sheet.images),
|
||||
"source_index": sheet.source_index,
|
||||
} for sheet in sheets],
|
||||
)
|
||||
return redirect("plugins:netbox_documentation:excel_import_preview", token=preview.pk)
|
||||
|
||||
@@ -213,13 +216,14 @@ class ExcelImportPreviewView(PermissionRequiredMixin, View):
|
||||
def get_preview(self, request, token):
|
||||
return get_object_or_404(ExcelImportPreview, pk=token, user=request.user)
|
||||
|
||||
def read_sheets(self, preview, *, for_preview=False):
|
||||
def read_sheets(self, preview, *, for_preview=False, selected_indexes=None):
|
||||
preview.file.open("rb")
|
||||
try:
|
||||
return import_excel_sheets(
|
||||
preview.file, flatten=preview.flatten,
|
||||
preview_max_rows=100 if for_preview else None,
|
||||
include_image_data=not for_preview,
|
||||
selected_indexes=selected_indexes,
|
||||
)
|
||||
finally:
|
||||
preview.file.close()
|
||||
@@ -270,15 +274,15 @@ class ExcelImportPreviewView(PermissionRequiredMixin, View):
|
||||
self.delete_preview(preview)
|
||||
messages.info(request, "Excel-Import wurde abgebrochen.")
|
||||
return redirect("plugins:netbox_documentation:document_import")
|
||||
try:
|
||||
sheets = self.read_sheets(preview)
|
||||
except (ImportFailure, Exception) as exc:
|
||||
self.delete_preview(preview)
|
||||
messages.error(request, f"Die Exceldatei konnte nicht erneut gelesen werden: {exc}")
|
||||
return redirect("plugins:netbox_documentation:document_import")
|
||||
formset = ExcelSheetSelectionFormSet(request.POST)
|
||||
if not formset.is_valid():
|
||||
return self.render_preview(request, preview, sheets, formset)
|
||||
try:
|
||||
preview_sheets = self.read_sheets(preview, for_preview=True)
|
||||
except (ImportFailure, Exception) as exc:
|
||||
self.delete_preview(preview)
|
||||
messages.error(request, f"Die Exceldatei konnte nicht erneut gelesen werden: {exc}")
|
||||
return redirect("plugins:netbox_documentation:document_import")
|
||||
return self.render_preview(request, preview, preview_sheets, formset)
|
||||
if not preview.category:
|
||||
self.delete_preview(preview)
|
||||
messages.error(request, "Der gewählte Zielordner existiert nicht mehr. Bitte den Import erneut starten.")
|
||||
@@ -287,10 +291,18 @@ class ExcelImportPreviewView(PermissionRequiredMixin, View):
|
||||
for form in formset.forms:
|
||||
data = form.cleaned_data
|
||||
if data.get("include"):
|
||||
if data["index"] < 0 or data["index"] >= len(sheets):
|
||||
if data["index"] < 0 or data["index"] >= len(preview.sheet_metadata):
|
||||
raise PermissionDenied
|
||||
selections[data["index"]] = data["title"].strip()
|
||||
result = self.create_documents(preview, sheets, selections)
|
||||
metadata = preview.sheet_metadata[data["index"]]
|
||||
source_index = metadata.get("source_index", data["index"])
|
||||
selections[source_index] = data["title"].strip()
|
||||
try:
|
||||
selected_sheets = self.read_sheets(preview, selected_indexes=selections)
|
||||
except (ImportFailure, Exception) as exc:
|
||||
messages.error(request, f"Die ausgewählten Excel-Blätter konnten nicht importiert werden: {exc}")
|
||||
return redirect("plugins:netbox_documentation:excel_import_preview", token=preview.pk)
|
||||
sheets_by_index = {sheet.source_index: sheet for sheet in selected_sheets}
|
||||
result = self.create_documents(preview, sheets_by_index, selections)
|
||||
self.delete_preview(preview)
|
||||
messages.success(request, (
|
||||
f"{result['documents']} Arbeitsblätter als Dokumentationen in „{result['category']}“ importiert; "
|
||||
@@ -302,11 +314,6 @@ class ExcelImportPreviewView(PermissionRequiredMixin, View):
|
||||
@transaction.atomic
|
||||
def create_documents(preview, sheets, selections):
|
||||
from django.core.files.base import ContentFile
|
||||
preview.file.open("rb")
|
||||
try:
|
||||
original_content = preview.file.read()
|
||||
finally:
|
||||
preview.file.close()
|
||||
created_documents, image_count = [], 0
|
||||
for index, title in selections.items():
|
||||
sheet = sheets[index]
|
||||
@@ -345,12 +352,16 @@ class ExcelImportPreviewView(PermissionRequiredMixin, View):
|
||||
created_documents.append(document)
|
||||
if settings.PLUGINS_CONFIG.get("netbox_documentation", {}).get("keep_imported_file", True):
|
||||
for document in created_documents:
|
||||
preview.file.open("rb")
|
||||
attachment = DocumentAttachment(
|
||||
document=document, original_name=preview.original_name,
|
||||
content_type=preview.content_type, size=len(original_content),
|
||||
content_type=preview.content_type, size=preview.file.size,
|
||||
)
|
||||
attachment.file.save(preview.original_name, ContentFile(original_content), save=False)
|
||||
attachment.save()
|
||||
try:
|
||||
attachment.file.save(preview.original_name, preview.file, save=False)
|
||||
attachment.save()
|
||||
finally:
|
||||
preview.file.close()
|
||||
return {"documents": len(created_documents), "images": image_count, "category": preview.category}
|
||||
|
||||
@staticmethod
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "netbox-documentation"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
description = "Integrated Markdown wiki and office document importer for NetBox"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
@@ -113,6 +113,29 @@ def test_excel_multi_sheet_import_creates_one_result_per_sheet():
|
||||
assert "Leitstelle" in results[1].markdown
|
||||
|
||||
|
||||
def test_excel_import_renders_only_selected_source_sheets():
|
||||
workbook = Workbook()
|
||||
first = workbook.active
|
||||
first.title = "Nicht gewählt"
|
||||
first.append(["Sehr viele Daten"])
|
||||
second = workbook.create_sheet("Gewählt")
|
||||
second.append(["Nur dieses Blatt"])
|
||||
third = workbook.create_sheet("Auch nicht gewählt")
|
||||
third.append(["Weitere Daten"])
|
||||
stream = BytesIO()
|
||||
workbook.save(stream)
|
||||
|
||||
results = import_excel_sheets(
|
||||
Upload(stream.getvalue(), "auswahl.xlsx"), selected_indexes={1},
|
||||
)
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].title == "Gewählt"
|
||||
assert results[0].source_index == 1
|
||||
assert "Nur dieses Blatt" in results[0].markdown
|
||||
assert "Sehr viele Daten" not in results[0].markdown
|
||||
|
||||
|
||||
def test_excel_metadata_phase_skips_document_rendering():
|
||||
workbook = Workbook()
|
||||
sheet = workbook.active
|
||||
|
||||
Reference in New Issue
Block a user