feat: add dynamic signpanel type column to asset export and optimize query performance
This commit is contained in:
parent
33ef94902b
commit
96492a9726
2 changed files with 102 additions and 28 deletions
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,16 +109,22 @@ 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
|
||||||
|
|
||||||
|
|
||||||
EXPORT_HEADERS = [
|
def _get_export_headers(include_signpanel_type=False):
|
||||||
|
headers = [
|
||||||
_("Type"),
|
_("Type"),
|
||||||
_("ID"),
|
_("ID"),
|
||||||
_("Code"),
|
_("Code"),
|
||||||
_("Nom"),
|
_("Nom"),
|
||||||
_("Catégorie"),
|
_("Catégorie"),
|
||||||
_("Modèle"),
|
_("Modèle"),
|
||||||
|
]
|
||||||
|
if include_signpanel_type:
|
||||||
|
headers.append(_("Type de panneau"))
|
||||||
|
headers.extend([
|
||||||
_("Statut"),
|
_("Statut"),
|
||||||
_("Obsolescence"),
|
_("Obsolescence"),
|
||||||
_("Code localisation"),
|
_("Code localisation"),
|
||||||
|
|
@ -112,7 +137,8 @@ EXPORT_HEADERS = [
|
||||||
_("Note"),
|
_("Note"),
|
||||||
_("Longitude"),
|
_("Longitude"),
|
||||||
_("Latitude"),
|
_("Latitude"),
|
||||||
]
|
])
|
||||||
|
return [str(h) for h in headers]
|
||||||
|
|
||||||
|
|
||||||
@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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue