407 lines
17 KiB
Python
407 lines
17 KiB
Python
import json
|
|
from django.test import TestCase
|
|
from django.contrib.auth import get_user_model
|
|
from django.urls import reverse
|
|
from django.contrib.gis.geos import Point, Polygon
|
|
from common.models import UserConfig, Role
|
|
from assets.models import NatureRWIAsset
|
|
from interventions.models import Intervention
|
|
|
|
class GIEPAssetsAJAXTests(TestCase):
|
|
def setUp(self):
|
|
from django.db import connection
|
|
with connection.cursor() as cursor:
|
|
cursor.execute("CREATE EXTENSION IF NOT EXISTS unaccent;")
|
|
cursor.execute("""
|
|
CREATE OR REPLACE FUNCTION public.unaccent_immutable(text)
|
|
RETURNS text
|
|
LANGUAGE sql
|
|
IMMUTABLE PARALLEL SAFE
|
|
AS $function$
|
|
SELECT public.unaccent('public.unaccent', $1)
|
|
$function$
|
|
""")
|
|
|
|
User = get_user_model()
|
|
self.user = User.objects.create_user(username='giepadmin', password='pwd')
|
|
self.role_admin, _ = Role.objects.get_or_create(name='admin')
|
|
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
|
|
self.user_config.roles.add(self.role_admin)
|
|
|
|
# Create correct geospatial polygon geometries by transforming to SRID 3812
|
|
geom1 = Polygon(((4.3570, 50.8475), (4.3570, 50.8479), (4.3574, 50.8479), (4.3574, 50.8475), (4.3570, 50.8475)), srid=4326)
|
|
geom1.transform(3812)
|
|
|
|
geom2 = Polygon(((4.3640, 50.8520), (4.3640, 50.8524), (4.3644, 50.8524), (4.3644, 50.8520), (4.3640, 50.8520)), srid=4326)
|
|
geom2.transform(3812)
|
|
|
|
self.asset1 = NatureRWIAsset.objects.create(
|
|
code="BI-01",
|
|
status="active",
|
|
structure_type="evc",
|
|
geom=geom1,
|
|
lon=4.3572,
|
|
lat=50.8477,
|
|
comment="Bouche d'injection EVC"
|
|
)
|
|
|
|
self.asset2 = NatureRWIAsset.objects.create(
|
|
code="MA-02",
|
|
status="active",
|
|
structure_type="oed",
|
|
geom=geom2,
|
|
lon=4.3642,
|
|
lat=50.8522,
|
|
comment="Massif drainant OED"
|
|
)
|
|
|
|
# Removed asset should be excluded
|
|
geom_removed = Polygon(((4.3570, 50.8475), (4.3570, 50.8479), (4.3574, 50.8479), (4.3574, 50.8475), (4.3570, 50.8475)), srid=4326)
|
|
geom_removed.transform(3812)
|
|
|
|
self.asset_removed = NatureRWIAsset.objects.create(
|
|
code="BI-REMOVED",
|
|
status="removed",
|
|
structure_type="evc",
|
|
geom=geom_removed,
|
|
lon=4.3572,
|
|
lat=50.8477
|
|
)
|
|
|
|
# Create intervention
|
|
self.intervention = Intervention.objects.create(
|
|
title="Inspection GIEP",
|
|
status="to_be_processed",
|
|
lon=4.3572,
|
|
lat=50.8477
|
|
)
|
|
|
|
self.client.login(username='giepadmin', password='pwd')
|
|
|
|
def test_autocomplete_giep_assets(self):
|
|
url = reverse('interventions:autocomplete_giep_assets', kwargs={'intervention_id': self.intervention.id})
|
|
|
|
# Test query for 'BI'
|
|
resp = self.client.get(url, {'q': 'BI'})
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
self.assertEqual(len(data), 1)
|
|
self.assertEqual(data[0]['code'], 'BI-01')
|
|
|
|
# Test query for 'removed' should be empty
|
|
resp = self.client.get(url, {'q': 'REMOVED'})
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
self.assertEqual(len(data), 0)
|
|
|
|
def test_nearby_giep_assets(self):
|
|
url = reverse('interventions:nearby_giep_assets', kwargs={'intervention_id': self.intervention.id})
|
|
|
|
resp = self.client.get(url)
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
|
|
# Should return both active assets, closest first (BI-01 at distance ~0)
|
|
self.assertTrue(len(data) >= 2)
|
|
self.assertEqual(data[0]['code'], 'BI-01')
|
|
self.assertEqual(data[1]['code'], 'MA-02')
|
|
|
|
def test_nearby_giep_assets_geojson(self):
|
|
url = reverse('interventions:nearby_giep_assets_geojson', kwargs={'intervention_id': self.intervention.id})
|
|
|
|
resp = self.client.get(url)
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
|
|
self.assertEqual(data['type'], 'FeatureCollection')
|
|
self.assertTrue(len(data['features']) >= 2)
|
|
|
|
# Check GeoJSON properties
|
|
codes = [feat['properties']['code'] for feat in data['features']]
|
|
self.assertIn('BI-01', codes)
|
|
self.assertIn('MA-02', codes)
|
|
self.assertNotIn('BI-REMOVED', codes)
|
|
|
|
def test_autocomplete_giep_locations(self):
|
|
from assets.models import NatureRWIZ
|
|
# Create a GIEP location (NatureRWIZ)
|
|
rwiz = NatureRWIZ.objects.create(
|
|
code="RWIZ-TEST-99",
|
|
name_fr="Zone d'Infiltration Test",
|
|
name_nl="Infiltratiezone Test",
|
|
lon=4.35,
|
|
lat=50.85
|
|
)
|
|
|
|
# Test location autocomplete view with query matching the GIEP code
|
|
url = reverse('assets:locations_autocomplete')
|
|
resp = self.client.get(url, {'q': 'RWIZ-TEST', 'thematic': 'water'})
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
|
|
# Verify it was returned
|
|
self.assertTrue(len(data) >= 1)
|
|
codes = [item['code'] for item in data]
|
|
self.assertIn('RWIZ-TEST-99', codes)
|
|
|
|
def test_autocomplete_prioritize_giep_locations(self):
|
|
from assets.models import NatureRWIZ, NatureLocation
|
|
# Create a GIEP location (NatureRWIZ)
|
|
rwiz = NatureRWIZ.objects.create(
|
|
code="RWIZ-TEST-99",
|
|
name_fr="Bassin Infiltration",
|
|
name_nl="Infiltratiebekken",
|
|
lon=4.35,
|
|
lat=50.85
|
|
)
|
|
# Create a standard nature location (NatureLocation)
|
|
nat_loc = NatureLocation.objects.create(
|
|
code="NAT-TEST-11",
|
|
name_fr="Arbre remarquable",
|
|
name_nl="Opmerkelijke boom",
|
|
lon=4.35,
|
|
lat=50.85
|
|
)
|
|
|
|
url = reverse('assets:locations_autocomplete')
|
|
|
|
# Test without prioritize_giep (alphabetical sorting by label: "Arbre..." before "Bassin...")
|
|
resp = self.client.get(url, {'q': 'TEST', 'thematic': 'nature,water'})
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
codes = [item['code'] for item in data]
|
|
self.assertEqual(codes[0], 'NAT-TEST-11')
|
|
self.assertEqual(codes[1], 'RWIZ-TEST-99')
|
|
|
|
# Test with prioritize=giep (GIEP/NatureRWIZ sorted first)
|
|
resp = self.client.get(url, {'q': 'TEST', 'thematic': 'nature,water', 'prioritize': 'giep'})
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
codes = [item['code'] for item in data]
|
|
self.assertEqual(codes[0], 'RWIZ-TEST-99')
|
|
self.assertEqual(codes[1], 'NAT-TEST-11')
|
|
|
|
# Test with prioritize=naturerwiz (GIEP/NatureRWIZ sorted first)
|
|
resp = self.client.get(url, {'q': 'TEST', 'thematic': 'nature,water', 'prioritize': 'naturerwiz'})
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
codes = [item['code'] for item in data]
|
|
self.assertEqual(codes[0], 'RWIZ-TEST-99')
|
|
self.assertEqual(codes[1], 'NAT-TEST-11')
|
|
|
|
# Test with prioritize=NAT_RWI (GIEP/NatureRWIZ sorted first, simulating production GIEP category code)
|
|
resp = self.client.get(url, {'q': 'TEST', 'thematic': 'nature,water', 'prioritize': 'NAT_RWI'})
|
|
self.assertEqual(resp.status_code, 200)
|
|
data = json.loads(resp.content)
|
|
codes = [item['code'] for item in data]
|
|
self.assertEqual(codes[0], 'RWIZ-TEST-99')
|
|
self.assertEqual(codes[1], 'NAT-TEST-11')
|
|
|
|
def test_zirw_detail_view_and_hierarchy(self):
|
|
from assets.models import NatureRWIZ, NatureRWIAsset
|
|
from django.contrib.gis.geos import Polygon, MultiPolygon
|
|
|
|
# Create a GIEP Zone polygon that intersects with asset1 (geom1)
|
|
# geom1 is around (4.3570, 50.8475) to (4.3574, 50.8479)
|
|
zone_poly = Polygon(((4.3568, 50.8474), (4.3568, 50.8480), (4.3576, 50.8480), (4.3576, 50.8474), (4.3568, 50.8474)), srid=4326)
|
|
zone_poly.transform(3812)
|
|
zone_geom = MultiPolygon(zone_poly, srid=3812)
|
|
|
|
rwiz = NatureRWIZ.objects.create(
|
|
code="RWIZ-TEST-DETAIL",
|
|
name_fr="Zone d'Infiltration Test Detail",
|
|
name_nl="Infiltratiezone Test Detail",
|
|
geom=zone_geom,
|
|
lon=4.3572,
|
|
lat=50.8477
|
|
)
|
|
|
|
# Create a point asset that intersects with both the zone and the polygon asset1
|
|
point_geom = Polygon(((4.3571, 50.8476), (4.3571, 50.8478), (4.3573, 50.8478), (4.3573, 50.8476), (4.3571, 50.8476)), srid=4326)
|
|
point_geom.transform(3812)
|
|
|
|
child_point_asset = NatureRWIAsset.objects.create(
|
|
code="BI-CHILD-01",
|
|
status="active",
|
|
structure_type="bouche injection",
|
|
geometry_type="point",
|
|
geom=point_geom,
|
|
lon=4.3572,
|
|
lat=50.8477,
|
|
comment="Bouche d'injection child"
|
|
)
|
|
|
|
# Ensure asset1 is categorized as a polygon
|
|
self.asset1.geometry_type = "polygon"
|
|
self.asset1.save()
|
|
|
|
# Check model methods
|
|
active_assets = list(rwiz.get_active_assets())
|
|
self.assertIn(self.asset1, active_assets)
|
|
self.assertIn(child_point_asset, active_assets)
|
|
self.assertNotIn(self.asset2, active_assets)
|
|
|
|
# Call the detail view
|
|
url = reverse('assets:zirw_detail', kwargs={'location_id': rwiz.id})
|
|
resp = self.client.get(url)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertContains(resp, "RWIZ-TEST-DETAIL")
|
|
self.assertContains(resp, "BI-CHILD-01")
|
|
|
|
# Test document config URL
|
|
config_url = reverse('assets:zirw_document_config', kwargs={'location_id': rwiz.id})
|
|
config_resp = self.client.get(config_url)
|
|
self.assertEqual(config_resp.status_code, 200)
|
|
config_json = config_resp.json()
|
|
self.assertIn('use_managed_documents', config_json)
|
|
self.assertIn('can_add_unique_tag_documents', config_json)
|
|
self.assertIn('tags', config_json)
|
|
self.assertIn('folders', config_json)
|
|
|
|
# Test document history URL
|
|
history_url = reverse('assets:zirw_document_history')
|
|
history_resp = self.client.get(f"{history_url}?location_id={rwiz.id}&tag_slug=test")
|
|
self.assertEqual(history_resp.status_code, 200)
|
|
history_json = history_resp.json()
|
|
self.assertIn('html', history_json)
|
|
|
|
|
|
class GIEPInspectionFormTests(TestCase):
|
|
def setUp(self):
|
|
from django.db import connection
|
|
with connection.cursor() as cursor:
|
|
cursor.execute("CREATE EXTENSION IF NOT EXISTS unaccent;")
|
|
cursor.execute("""
|
|
CREATE OR REPLACE FUNCTION public.unaccent_immutable(text)
|
|
RETURNS text
|
|
LANGUAGE sql
|
|
IMMUTABLE PARALLEL SAFE
|
|
AS $function$
|
|
SELECT public.unaccent('public.unaccent', $1)
|
|
$function$
|
|
""")
|
|
|
|
User = get_user_model()
|
|
self.user = User.objects.create_user(username='inspectadmin', password='pwd')
|
|
self.role_admin, _ = Role.objects.get_or_create(name='admin')
|
|
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
|
|
self.user_config.roles.add(self.role_admin)
|
|
|
|
from common.models import Thematic
|
|
self.thematic, _ = Thematic.objects.get_or_create(
|
|
code='water',
|
|
defaults={'name_fr': 'Eau', 'name_nl': 'Water'}
|
|
)
|
|
|
|
from interventions.models import InspectionConfiguration
|
|
self.config_with_equip = InspectionConfiguration.objects.create(
|
|
name_fr="Inspection GIEP Avec Equipement",
|
|
name_nl="Inspectie GIEP Met Apparatuur",
|
|
thematic=self.thematic,
|
|
show_equipment=True
|
|
)
|
|
self.config_no_equip = InspectionConfiguration.objects.create(
|
|
name_fr="Inspection GIEP Sans Equipement",
|
|
name_nl="Inspectie GIEP Zonder Apparatuur",
|
|
thematic=self.thematic,
|
|
show_equipment=False
|
|
)
|
|
|
|
self.client.login(username='inspectadmin', password='pwd')
|
|
self.client.cookies.load({'django_language': 'fr'})
|
|
|
|
def test_get_add_inspection_with_equipment(self):
|
|
url = reverse('interventions:intervention_add', kwargs={'thematic_code': 'water'})
|
|
# GET with config showing equipment
|
|
resp = self.client.get(url, {'inspection_config_id': self.config_with_equip.id})
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertContains(resp, "Nouvelle inspection - GIEP Avec Equipement")
|
|
# Asset category field should be present but in d-none block
|
|
self.assertContains(resp, "class=\"mb-3 d-none\"")
|
|
# Sub-category select should have display:none !important;
|
|
self.assertContains(resp, "display:none !important;")
|
|
# Verify status is pre-filled as 'assigned'
|
|
self.assertContains(resp, 'value="assigned" selected')
|
|
# Verify "Numéro de zone" is rendered instead of "Numéro de section"
|
|
self.assertContains(resp, "Numéro de zone")
|
|
self.assertNotContains(resp, "Numéro de section")
|
|
# Verify that the "Equipement:" summary label is omitted
|
|
self.assertNotContains(resp, "Equipement:")
|
|
|
|
def test_get_add_inspection_without_equipment(self):
|
|
url = reverse('interventions:intervention_add', kwargs={'thematic_code': 'water'})
|
|
# GET with config hiding equipment
|
|
resp = self.client.get(url, {'inspection_config_id': self.config_no_equip.id})
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertContains(resp, "Nouvelle inspection - GIEP Sans Equipement")
|
|
# Verify equipment container is hidden
|
|
self.assertContains(resp, "class=\"mb-3 d-none\"")
|
|
# Verify "Numéro de zone" is rendered instead of "Numéro de section"
|
|
self.assertContains(resp, "Numéro de zone")
|
|
self.assertNotContains(resp, "Numéro de section")
|
|
# Verify that the "Equipement:" summary label is omitted
|
|
self.assertNotContains(resp, "Equipement:")
|
|
|
|
def test_post_create_assigned_inspection_auto_assigns_user(self):
|
|
from contracts.models import Company, CompanyMember, CompanyTeam
|
|
company = Company.objects.create(name="Test Company")
|
|
team = CompanyTeam.objects.create(name="Team Admin", company=company)
|
|
member = CompanyMember.objects.create(user=self.user, name="Inspect Admin")
|
|
member.teams.add(team)
|
|
|
|
url = reverse('interventions:intervention_add', kwargs={'thematic_code': 'water'})
|
|
post_data = {
|
|
'inspection_config_id': self.config_no_equip.id,
|
|
'title': 'Test GIEP Inspection Title',
|
|
'status': 'assigned',
|
|
'type': 'inspection',
|
|
'maintain_type': 'preventive',
|
|
'priority': '3',
|
|
'lat': '50.8477',
|
|
'lon': '4.3572',
|
|
}
|
|
resp = self.client.post(url, post_data)
|
|
self.assertEqual(resp.status_code, 302)
|
|
|
|
# Verify intervention was created and assigned to the user
|
|
created_itv = Intervention.objects.filter(title='Test GIEP Inspection Title').first()
|
|
self.assertIsNotNone(created_itv)
|
|
self.assertEqual(created_itv.status, 'assigned')
|
|
self.assertEqual(created_itv.assigned_member, member)
|
|
self.assertEqual(created_itv.assigned_team, team)
|
|
|
|
def test_resolve_nature_rwiz_location(self):
|
|
from assets.models import NatureRWIZ
|
|
from interventions.models import InterventionLocation
|
|
from interventions.utils import resolve_intervention_localisations
|
|
|
|
# Create an intervention
|
|
intervention = Intervention.objects.create(
|
|
title="Test RWIZ Intervention",
|
|
status="assigned"
|
|
)
|
|
|
|
# Create NatureRWIZ location
|
|
rwiz = NatureRWIZ.objects.create(
|
|
code="RWIZ-006",
|
|
name_fr="Bassin d'infiltration",
|
|
name_nl="Infiltratiebekken"
|
|
)
|
|
|
|
# Associate location to intervention
|
|
from django.contrib.contenttypes.models import ContentType
|
|
ct = ContentType.objects.get_for_model(NatureRWIZ)
|
|
InterventionLocation.objects.create(
|
|
intervention=intervention,
|
|
content_type=ct,
|
|
object_id=rwiz.id
|
|
)
|
|
|
|
# Call resolve_intervention_localisations
|
|
locs = resolve_intervention_localisations(intervention)
|
|
self.assertEqual(len(locs), 1)
|
|
self.assertEqual(locs[0]['name'], "RWIZ-006 - Bassin d'infiltration")
|
|
self.assertIn("zirw", locs[0]['url'])
|
|
|
|
|
|
|