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')
self.assertIn("EXP01A-000001", 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):
from django.urls import reverse
@ -4098,6 +4100,41 @@ class ExportAssetsTest(TestCase):
content = response.content.decode('utf-8')
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 ''
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):
if dt is None:
return ''
@ -56,7 +71,7 @@ def _fmt_date(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)
loc_code = location.code 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 '')
)
warranty = getattr(asset, 'warranty_duration', None)
return [
row = [
model_name,
asset.id,
asset.code or '',
asset.get_name() if hasattr(asset, 'get_name') else '',
asset.category.get_name() if getattr(asset, 'category', None) else '',
_get_asset_model_display(asset),
]
if include_signpanel_type:
row.append(_get_asset_signpanel_type_display(asset))
row.extend([
status_display,
obsolescence_display,
loc_code,
@ -90,29 +109,36 @@ def _get_row(model_name, asset):
getattr(asset, 'note', '') or '',
getattr(asset, 'lon', '') if getattr(asset, 'lon', 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"),
]
EXPORT_HEADERS = [
_("Type"),
_("ID"),
_("Code"),
_("Nom"),
_("Catégorie"),
_("Modèle"),
_("Statut"),
_("Obsolescence"),
_("Code localisation"),
_("Nom localisation"),
_("Date d'installation"),
_("Date de déinstallation"),
_("Numéro de série"),
_("Garantie (mois)"),
_("Marque"),
_("Note"),
_("Longitude"),
_("Latitude"),
]
if include_signpanel_type:
headers.append(_("Type de panneau"))
headers.extend([
_("Statut"),
_("Obsolescence"),
_("Code localisation"),
_("Nom localisation"),
_("Date d'installation"),
_("Date de déinstallation"),
_("Numéro de série"),
_("Garantie (mois)"),
_("Marque"),
_("Note"),
_("Longitude"),
_("Latitude"),
])
return [str(h) for h in headers]
@login_required
@ -164,14 +190,25 @@ def export_assets(request):
break
if model_class is None:
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):
items.append((model_name, asset))
except Exception:
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')
headers = [str(h) for h in EXPORT_HEADERS]
headers = _get_export_headers(include_signpanel_type=has_signpanels)
if fmt == 'xlsx':
try:
@ -189,7 +226,7 @@ def export_assets(request):
ws.title = 'Assets'
ws.append(headers)
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()
wb.save(output)
@ -211,6 +248,6 @@ def export_assets(request):
writer = csv.writer(resp, delimiter=';')
writer.writerow(headers)
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