feat: add dynamic signpanel type column to asset export and optimize query performance

This commit is contained in:
kdeterme 2026-08-28 11:09:12 +02:00
parent 33ef94902b
commit 96492a9726
2 changed files with 102 additions and 28 deletions

View file

@ -4077,6 +4077,8 @@ class ExportAssetsTest(TestCase):
content = response.content.decode('utf-8') content = response.content.decode('utf-8')
self.assertIn("EXP01A-000001", content) self.assertIn("EXP01A-000001", content)
self.assertIn("Asset Pour Export", content) self.assertIn("Asset Pour Export", content)
from django.utils.translation import gettext as _
self.assertNotIn(_("Type de panneau"), content)
def test_export_assets_with_duplicate_content_types(self): def test_export_assets_with_duplicate_content_types(self):
from django.urls import reverse from django.urls import reverse
@ -4098,6 +4100,41 @@ class ExportAssetsTest(TestCase):
content = response.content.decode('utf-8') content = response.content.decode('utf-8')
self.assertIn("EXP01A-000001", content) self.assertIn("EXP01A-000001", content)
def test_export_signpanel_with_type(self):
from django.urls import reverse
from assets.models import SignPanel, SignPanelType, SignPole, SignStreet
thematic_sign = Thematic.objects.get_or_create(code="sign", defaults={"name_fr": "Signalisation"})[0]
UserThematics.objects.create(
user_config=self.user_config,
thematic=thematic_sign,
can_view_assets=True,
can_edit_assets=True,
)
street = SignStreet.objects.create(name_fr="Rue du Test", code="STR01")
pole = SignPole.objects.create(code="POL01", street=street)
panel_type = SignPanelType.objects.create(code="C15", name_fr="Accès interdit", name_nl="Verboden toegang")
panel = SignPanel.objects.create(
code="PAN01",
pole=pole,
street=street,
signpanel_type=panel_type,
status="active",
)
self.client.force_login(self.user)
url = reverse('assets:export_assets')
ids_json = json.dumps([f"signpanel:{panel.id}"])
response = self.client.post(url, {'ids': ids_json, 'format': 'csv'})
self.assertEqual(response.status_code, 200)
content = response.content.decode('utf-8')
from django.utils.translation import gettext as _
self.assertIn(_("Type de panneau"), content)
self.assertIn("C15 - Accès interdit", content)
self.assertIn("PAN01", content)

View file

@ -47,6 +47,21 @@ def _get_asset_model_display(asset):
return '' return ''
def _get_asset_signpanel_type_display(asset):
"""Return the signpanel type code and/or name (e.g. 'C15' or 'C15 - Accès interdit...') if applicable."""
try:
spt = getattr(asset, 'signpanel_type', None)
if spt:
code = (getattr(spt, 'code', '') or '').strip()
name = (spt.get_name() if hasattr(spt, 'get_name') else getattr(spt, 'name_fr', '')) or ''
if code and name:
return f"{code} - {name}"
return code or name or ''
except Exception:
pass
return ''
def _fmt_date(dt): def _fmt_date(dt):
if dt is None: if dt is None:
return '' return ''
@ -56,7 +71,7 @@ def _fmt_date(dt):
return str(dt) return str(dt)
def _get_row(model_name, asset): def _get_row(model_name, asset, include_signpanel_type=False):
location = _get_asset_location(asset) location = _get_asset_location(asset)
loc_code = location.code if location else '' loc_code = location.code if location else ''
loc_name = location.get_name() if location else '' loc_name = location.get_name() if location else ''
@ -71,13 +86,17 @@ def _get_row(model_name, asset):
else (getattr(asset, 'obsolescence', '') or '') else (getattr(asset, 'obsolescence', '') or '')
) )
warranty = getattr(asset, 'warranty_duration', None) warranty = getattr(asset, 'warranty_duration', None)
return [ row = [
model_name, model_name,
asset.id, asset.id,
asset.code or '', asset.code or '',
asset.get_name() if hasattr(asset, 'get_name') else '', asset.get_name() if hasattr(asset, 'get_name') else '',
asset.category.get_name() if getattr(asset, 'category', None) else '', asset.category.get_name() if getattr(asset, 'category', None) else '',
_get_asset_model_display(asset), _get_asset_model_display(asset),
]
if include_signpanel_type:
row.append(_get_asset_signpanel_type_display(asset))
row.extend([
status_display, status_display,
obsolescence_display, obsolescence_display,
loc_code, loc_code,
@ -90,29 +109,36 @@ def _get_row(model_name, asset):
getattr(asset, 'note', '') or '', getattr(asset, 'note', '') or '',
getattr(asset, 'lon', '') if getattr(asset, 'lon', None) is not None else '', getattr(asset, 'lon', '') if getattr(asset, 'lon', None) is not None else '',
getattr(asset, 'lat', '') if getattr(asset, 'lat', None) is not None else '', getattr(asset, 'lat', '') if getattr(asset, 'lat', None) is not None else '',
])
return row
def _get_export_headers(include_signpanel_type=False):
headers = [
_("Type"),
_("ID"),
_("Code"),
_("Nom"),
_("Catégorie"),
_("Modèle"),
] ]
if include_signpanel_type:
headers.append(_("Type de panneau"))
EXPORT_HEADERS = [ headers.extend([
_("Type"), _("Statut"),
_("ID"), _("Obsolescence"),
_("Code"), _("Code localisation"),
_("Nom"), _("Nom localisation"),
_("Catégorie"), _("Date d'installation"),
_("Modèle"), _("Date de déinstallation"),
_("Statut"), _("Numéro de série"),
_("Obsolescence"), _("Garantie (mois)"),
_("Code localisation"), _("Marque"),
_("Nom localisation"), _("Note"),
_("Date d'installation"), _("Longitude"),
_("Date de déinstallation"), _("Latitude"),
_("Numéro de série"), ])
_("Garantie (mois)"), return [str(h) for h in headers]
_("Marque"),
_("Note"),
_("Longitude"),
_("Latitude"),
]
@login_required @login_required
@ -164,14 +190,25 @@ def export_assets(request):
break break
if model_class is None: if model_class is None:
continue continue
for asset in model_class.objects.filter(pk__in=asset_ids): qs = model_class.objects.filter(pk__in=asset_ids)
for fk in ('category', 'model', 'signpanel_type'):
if any(f.name == fk for f in model_class._meta.fields):
try:
qs = qs.select_related(fk)
except Exception:
pass
for asset in qs:
if can_view_asset(request.user, asset): if can_view_asset(request.user, asset):
items.append((model_name, asset)) items.append((model_name, asset))
except Exception: except Exception:
continue continue
has_signpanels = any(
model_name == 'signpanel' or hasattr(asset, 'signpanel_type')
for model_name, asset in items
)
timestamp = localtime(now()).strftime('%Y%m%d_%H%M%S') timestamp = localtime(now()).strftime('%Y%m%d_%H%M%S')
headers = [str(h) for h in EXPORT_HEADERS] headers = _get_export_headers(include_signpanel_type=has_signpanels)
if fmt == 'xlsx': if fmt == 'xlsx':
try: try:
@ -189,7 +226,7 @@ def export_assets(request):
ws.title = 'Assets' ws.title = 'Assets'
ws.append(headers) ws.append(headers)
for model_name, asset in items: for model_name, asset in items:
ws.append([_sanitize(v) for v in _get_row(model_name, asset)]) ws.append([_sanitize(v) for v in _get_row(model_name, asset, include_signpanel_type=has_signpanels)])
output = BytesIO() output = BytesIO()
wb.save(output) wb.save(output)
@ -211,6 +248,6 @@ def export_assets(request):
writer = csv.writer(resp, delimiter=';') writer = csv.writer(resp, delimiter=';')
writer.writerow(headers) writer.writerow(headers)
for model_name, asset in items: for model_name, asset in items:
writer.writerow(_get_row(model_name, asset)) writer.writerow(_get_row(model_name, asset, include_signpanel_type=has_signpanels))
return resp return resp