165 lines
5.4 KiB
Python
165 lines
5.4 KiB
Python
from collections import defaultdict
|
|
from datetime import datetime
|
|
import re
|
|
|
|
from openpyxl import Workbook
|
|
|
|
from django.core.management.base import BaseCommand
|
|
|
|
from assets.models import NatureGreenSurface
|
|
|
|
|
|
ILLEGAL_CHAR_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f]")
|
|
|
|
|
|
def sanitize(value):
|
|
if isinstance(value, str):
|
|
return ILLEGAL_CHAR_RE.sub("", value)
|
|
return value
|
|
|
|
|
|
def autosize_columns(ws):
|
|
for column in ws.columns:
|
|
max_length = 0
|
|
column_letter = column[0].column_letter
|
|
for cell in column:
|
|
value = "" if cell.value is None else str(cell.value)
|
|
if len(value) > max_length:
|
|
max_length = len(value)
|
|
ws.column_dimensions[column_letter].width = min(max(12, max_length + 2), 60)
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = (
|
|
"Exporte les assets NatureGreenSurface dans un fichier XLSX avec "
|
|
"une feuille de donnees brutes et une feuille de synthese par type de surface."
|
|
)
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument(
|
|
"--output",
|
|
type=str,
|
|
default=None,
|
|
help="Chemin du fichier XLSX de sortie.",
|
|
)
|
|
parser.add_argument(
|
|
"--include-removed",
|
|
action="store_true",
|
|
default=False,
|
|
help="Inclut les surfaces avec le statut 'removed'.",
|
|
)
|
|
|
|
def handle(self, *args, **options):
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
output_path = options["output"] or f"greensurfaces_export_{timestamp}.xlsx"
|
|
|
|
queryset = NatureGreenSurface.objects.select_related("model", "location").order_by("code")
|
|
if not options["include_removed"]:
|
|
queryset = queryset.exclude(status="removed")
|
|
|
|
wb = Workbook()
|
|
|
|
# Feuille 1: donnees brutes
|
|
ws_raw = wb.active
|
|
ws_raw.title = "Donnees"
|
|
raw_headers = [
|
|
"ID",
|
|
"Code",
|
|
"Nom FR",
|
|
"Nom NL",
|
|
"Statut",
|
|
"Type surface (code)",
|
|
"Type surface (nom FR)",
|
|
"Type surface (nom NL)",
|
|
"Categorie (code)",
|
|
"Categorie (nom FR)",
|
|
"Categorie (nom NL)",
|
|
"Localisation (code)",
|
|
"Localisation (nom FR)",
|
|
"Localisation (nom NL)",
|
|
"Superficie",
|
|
"Pente",
|
|
"Densite",
|
|
"Largeur",
|
|
"Longueur",
|
|
"Longitude",
|
|
"Latitude",
|
|
]
|
|
ws_raw.append(raw_headers)
|
|
|
|
summary = defaultdict(lambda: {"count": 0, "surface_sum": 0.0})
|
|
|
|
for surface in queryset:
|
|
model = surface.model
|
|
category = model.category if model and model.category else None
|
|
location = surface.location
|
|
|
|
model_key = (
|
|
model.code if model and model.code else "SANS_TYPE",
|
|
model.name_fr if model and model.name_fr else "Sans type",
|
|
model.name_nl if model and model.name_nl else "",
|
|
)
|
|
|
|
surface_area = float(surface.surface_area or 0)
|
|
summary[model_key]["count"] += 1
|
|
summary[model_key]["surface_sum"] += surface_area
|
|
|
|
ws_raw.append(
|
|
[
|
|
surface.id,
|
|
sanitize(surface.code),
|
|
sanitize(surface.name_fr),
|
|
sanitize(surface.name_nl),
|
|
sanitize(surface.status),
|
|
sanitize(model.code if model else None),
|
|
sanitize(model.name_fr if model else None),
|
|
sanitize(model.name_nl if model else None),
|
|
sanitize(category.code if category else None),
|
|
sanitize(category.name_fr if category else None),
|
|
sanitize(category.name_nl if category else None),
|
|
sanitize(location.code if location else None),
|
|
sanitize(location.name_fr if location else None),
|
|
sanitize(location.name_nl if location else None),
|
|
surface.surface_area,
|
|
surface.slope,
|
|
surface.density,
|
|
surface.width,
|
|
surface.length,
|
|
surface.lon,
|
|
surface.lat,
|
|
]
|
|
)
|
|
|
|
autosize_columns(ws_raw)
|
|
|
|
# Feuille 2: synthese
|
|
ws_summary = wb.create_sheet(title="Synthese")
|
|
summary_headers = [
|
|
"Type surface (code)",
|
|
"Type surface (nom FR)",
|
|
"Type surface (nom NL)",
|
|
"Nombre",
|
|
"Superficie totale",
|
|
]
|
|
ws_summary.append(summary_headers)
|
|
|
|
for (model_code, model_name_fr, model_name_nl), values in sorted(
|
|
summary.items(), key=lambda item: (item[0][1], item[0][0])
|
|
):
|
|
ws_summary.append(
|
|
[
|
|
sanitize(model_code),
|
|
sanitize(model_name_fr),
|
|
sanitize(model_name_nl),
|
|
values["count"],
|
|
round(values["surface_sum"], 2),
|
|
]
|
|
)
|
|
|
|
ws_summary.append(["TOTAL", "", "", f"=SUM(D2:D{ws_summary.max_row})", f"=SUM(E2:E{ws_summary.max_row})"])
|
|
|
|
autosize_columns(ws_summary)
|
|
|
|
wb.save(output_path)
|
|
self.stdout.write(self.style.SUCCESS(f"Export termine: {output_path}"))
|
|
self.stdout.write(self.style.SUCCESS(f"Nombre de surfaces exportees: {queryset.count()}"))
|