Skip to content

Commit

Permalink
Merge pull request #3426 from GeotrekCE/fix_intervention_list_if_no_t…
Browse files Browse the repository at this point in the history
…arget

🐛 Fix intervention datatable list when no target defined
  • Loading branch information
LePetitTim authored Jan 26, 2023
2 parents fa9fc49 + 5b57372 commit e6523e4
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 134 deletions.
3 changes: 3 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ CHANGELOG
2.95.0+dev (XXXX-XX-XX)
-----------------------

**Bug fixes**

- Fix intervention datatable list if one intervention has no target


2.95.0 (2023-01-24)
Expand Down
129 changes: 49 additions & 80 deletions geotrek/maintenance/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@
from mapentity.filters import PolygonFilter, PythonPolygonFilter

from geotrek.altimetry.filters import AltimetryPointFilterSet
from geotrek.core.models import Topology
from geotrek.authent.filters import StructureRelatedFilterSet
from geotrek.common.filters import OptionalRangeFilter, RightFilter
from geotrek.feedback.models import Report
from geotrek.zoning.filters import (IntersectionFilterCity, IntersectionFilterDistrict,
IntersectionFilterRestrictedArea, IntersectionFilterRestrictedAreaType,
ZoningFilterSet)
from geotrek.zoning.models import City, District, RestrictedArea, RestrictedAreaType

from .models import Intervention, Project
from geotrek.core.models import Topology

if 'geotrek.signage' in settings.INSTALLED_APPS:
from geotrek.signage.models import Blade
from geotrek.signage.models import Blade, Signage

if 'geotrek.outdoor' in settings.INSTALLED_APPS:
from geotrek.outdoor.models import Site, Course
Expand All @@ -34,52 +35,52 @@ def filter(self, qs, values):
if not isinstance(values, list):
values = [values]

lookup = self.lookup_expr
content_type_exclude = []
if 'geotrek.signage' in settings.INSTALLED_APPS:
blade_content_type = ContentType.objects.get_for_model(Blade)
content_type_exclude.append(blade_content_type)
if 'geotrek.outdoor' in settings.INSTALLED_APPS:
site_content_type = ContentType.objects.get_for_model(Site)
course_content_type = ContentType.objects.get_for_model(Course)
content_type_exclude.append(site_content_type)
content_type_exclude.append(course_content_type)
topologies = []
sites = []
courses = []
for value in values:
topologies += Topology.objects.filter(**{'geom__%s' % lookup: self.get_geom(value)}).values_list('id', flat=True)

if 'geotrek.outdoor' in settings.INSTALLED_APPS:
sites += Site.objects.filter(**{'geom__%s' % lookup: self.get_geom(value)}).values_list('id', flat=True)
courses += Course.objects.filter(**{'geom__%s' % lookup: self.get_geom(value)}).values_list('id', flat=True)
topologies_intervention = Intervention.objects.existing().filter(target_id__in=topologies).exclude(
target_type__in=content_type_exclude).distinct('pk').values_list('id', flat=True)
signages = []
target_types = qs.values_list('target_type', flat=True).exclude(target_type=blade_content_type)
interventions = []
for target_type in target_types:
model = ContentType.objects.get(pk=target_type).model_class()
elements_in_bbox = []
for value in values:
elements_in_bbox.extend(
model.objects.filter(**{'geom__%s' % self.lookup_expr: self.get_geom(value)}).values_list('id', flat=True)
)
if 'geotrek.outdoor' in settings.INSTALLED_APPS and issubclass(model, Site) or issubclass(model, Course):
interventions.extend(qs.values_list('id', flat=True).filter(target_type=target_type).exclude(
target_id__in=model.objects.values_list('id', flat=True)
))
if 'geotrek.feedback' in settings.INSTALLED_APPS and issubclass(model, Report):
interventions.extend(qs.values_list('id', flat=True).filter(target_type=target_type).exclude(
target_id__in=model.objects.values_list('id', flat=True)))
if 'geotrek.signage' in settings.INSTALLED_APPS and issubclass(model, Topology) or issubclass(model, Signage):
signages = elements_in_bbox
interventions += qs.values_list('id', flat=True).filter(target_type=target_type,
target_id__in=elements_in_bbox)

interventions = list(topologies_intervention)
if 'geotrek.signage' in settings.INSTALLED_APPS:
blades = list(Blade.objects.filter(signage__in=topologies).values_list('id', flat=True))
blades_intervention = Intervention.objects.existing().filter(target_id__in=blades,
target_type=blade_content_type).values_list('id',
flat=True)
blades = list(Blade.objects.filter(signage__in=signages).values_list('id', flat=True))

blades_intervention = Intervention.objects.filter(target_id__in=blades,
target_type=blade_content_type).values_list('id',
flat=True)
interventions.extend(blades_intervention)
if 'geotrek.outdoor' in settings.INSTALLED_APPS:
sites_intervention = Intervention.objects.existing() \
.filter(target_id__in=sites, target_type=site_content_type) \
.values_list('id', flat=True)
interventions.extend(sites_intervention)
courses_intervention = Intervention.objects.existing() \
.filter(target_id__in=courses, target_type=course_content_type) \
.values_list('id', flat=True)
interventions.extend(courses_intervention)
if hasattr(self, 'lookup_queryset_in'):
lookup_queryset = self.lookup_queryset_in
else:
lookup_queryset = 'pk__in'
qs = qs.filter(**{'%s' % lookup_queryset: interventions})
qs = qs.filter(pk__in=interventions).existing()
return qs


class PolygonProjectFilterMixin(PolygonInterventionFilterMixin):
def get_geom(self, value):
return value.geom

def filter(self, qs, values):
if not values:
return qs
interventions = Intervention.objects.all()
return qs.filter(interventions__in=super().filter(interventions, values).values_list('id', flat=True))


class InterventionIntersectionFilterRestrictedAreaType(PolygonInterventionFilterMixin,
IntersectionFilterRestrictedAreaType):

Expand Down Expand Up @@ -115,59 +116,27 @@ class PolygonTopologyFilter(PolygonInterventionFilterMixin, PolygonFilter):
pass


class ProjectIntersectionFilterCity(PolygonInterventionFilterMixin, RightFilter):
class ProjectIntersectionFilterCity(PolygonProjectFilterMixin, RightFilter):
model = City

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lookup_expr = 'intersects'
self.lookup_queryset_in = 'interventions__in'

def get_geom(self, value):
return value.geom


class ProjectIntersectionFilterDistrict(PolygonInterventionFilterMixin, RightFilter):
class ProjectIntersectionFilterDistrict(PolygonProjectFilterMixin, RightFilter):
model = District

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lookup_expr = 'intersects'
self.lookup_queryset_in = 'interventions__in'

def get_geom(self, value):
return value.geom


class ProjectIntersectionFilterRestrictedArea(PolygonInterventionFilterMixin, RightFilter):
class ProjectIntersectionFilterRestrictedArea(PolygonProjectFilterMixin, RightFilter):
model = RestrictedArea

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lookup_expr = 'intersects'
self.lookup_queryset_in = 'interventions__in'

def get_geom(self, value):
return value.geom


class ProjectIntersectionFilterRestrictedAreaType(PolygonInterventionFilterMixin, RightFilter):
class ProjectIntersectionFilterRestrictedAreaType(PolygonProjectFilterMixin, RightFilter):
model = RestrictedAreaType

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lookup_expr = 'intersects'
self.lookup_queryset_in = 'interventions__in'

def filter(self, qs, values):
restricted_areas = RestrictedArea.objects.filter(area_type__in=values)
if not restricted_areas and values:
return qs.none()
return super().filter(qs, list(restricted_areas))

def get_geom(self, value):
return value.geom


class AltimetryInterventionFilterSet(AltimetryPointFilterSet):
length_3d = OptionalRangeFilter(field_name='length', label=_('length 3d'))
Expand Down Expand Up @@ -208,10 +177,10 @@ class ProjectFilterSet(StructureRelatedFilterSet):
label=_("Year of activity"), method='filter_year',
choices=lambda: Project.objects.year_choices() # Could change over time
)
city = ProjectIntersectionFilterCity(label=_('City'), required=False)
district = ProjectIntersectionFilterDistrict(label=_('District'), required=False)
area_type = ProjectIntersectionFilterRestrictedAreaType(label=_('Restricted area type'), required=False)
area = ProjectIntersectionFilterRestrictedArea(label=_('Restricted area'), required=False)
city = ProjectIntersectionFilterCity(label=_('City'), lookup_expr='intersects', required=False)
district = ProjectIntersectionFilterDistrict(label=_('District'), lookup_expr='intersects', required=False)
area_type = ProjectIntersectionFilterRestrictedAreaType(label=_('Restricted area type'), lookup_expr='intersects', required=False)
area = ProjectIntersectionFilterRestrictedArea(label=_('Restricted area'), lookup_expr='intersects', required=False)

class Meta(StructureRelatedFilterSet.Meta):
model = Project
Expand Down
66 changes: 42 additions & 24 deletions geotrek/maintenance/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,41 +130,57 @@ def target_verbose_name(cls):
def target_display(self):
icon = 'path'
title = _('Paths')
if not self.target._meta.model_name == "topology":
icon = self.target._meta.model_name
title = self.target.name_display
return '<img src="%simages/%s-16.png"> %s' % (settings.STATIC_URL,
icon,
title)
if self.target_type:
model = self.target_type.model_class()

if not self.target:
title = model._meta.verbose_name + f' {self.target_id}'
return '<i>' + _('Deleted') + ' :</i><img src="%simages/%s-16.png"> <i>%s<i/>' % (settings.STATIC_URL, icon, title)
if not model._meta.model_name == "topology":
title = self.target.name_display
icon = model._meta.model_name
return '<img src="%simages/%s-16.png"> %s' % (settings.STATIC_URL,
icon,
title)
return '-'

@property
def target_csv_display(self):
if self.target._meta.model_name == "topology":
title = _('Path')
return ", ".join(["%s: %s (%s)" % (title, path, path.pk) for path in self.target.paths.all()])
return "%s: %s (%s)" % (
_(self.target._meta.verbose_name),
self.target,
self.target.pk)
if self.target_type:
model = self.target_type.model_class()
if not self.target:
title = model._meta.verbose_name + f' {self.target_id}'
return _('Deleted') + title
if model._meta.model_name == "topology":
title = _('Path')
return ", ".join(["%s: %s (%s)" % (title, path, path.pk) for path in self.target.paths.all()])
return "%s: %s (%s)" % (
_(self.target._meta.verbose_name),
self.target,
self.target.pk)
return '-'

@property
def in_project(self):
return self.project is not None

@property
def paths(self):
if self.target._meta.model_name == 'blade':
return self.target.signage.paths.all()
if self.target:
return self.target.paths.all()
if self.target_type:
model = self.target_type.model_class()
if model._meta.model_name == 'blade':
return self.target.signage.paths.all()
if self.target and hasattr(self.target, 'paths'):
return self.target.paths.all()
return Path.objects.none()

@property
def trails(self):
s = []
for p in self.target.paths.all():
for t in p.trails.all():
s.append(t.pk)
if hasattr(self.target, 'paths'):
for p in self.target.paths.all():
for t in p.trails.all():
s.append(t.pk)

return Trail.objects.filter(pk__in=s)

Expand Down Expand Up @@ -436,16 +452,18 @@ def __init__(self, *args, **kwargs):
def paths(self):
s = []
for i in self.interventions.existing():
s += i.paths
if hasattr(i, 'paths'):
s += i.paths
return Path.objects.filter(pk__in=[p.pk for p in set(s)])

@property
def trails(self):
s = []
for i in self.interventions.existing():
for p in i.target.paths.all():
for t in p.trails.all():
s.append(t.pk)
if i.target and hasattr(i.target, 'paths'):
for p in i.target.paths.all():
for t in p.trails.all():
s.append(t.pk)

return Trail.objects.filter(pk__in=s)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ BEGIN
END;
$$ LANGUAGE plpgsql;

CREATE FUNCTION {{ schema_geotrek }}.delete_related_intervention_report() RETURNS trigger SECURITY DEFINER AS $$
BEGIN
UPDATE maintenance_intervention SET deleted = NEW.deleted WHERE target_id = NEW.id AND target_type_id IN (SELECT id FROM django_content_type AS ct WHERE ct.model = 'report');
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE FUNCTION {{ schema_geotrek }}.delete_related_intervention_blade() RETURNS trigger SECURITY DEFINER AS $$
BEGIN
UPDATE maintenance_intervention SET deleted = NEW.deleted WHERE target_id = NEW.id AND target_type_id IN (SELECT id FROM django_content_type AS ct WHERE ct.model = 'blade');
Expand All @@ -37,6 +44,10 @@ CREATE TRIGGER maintenance_topology_interventions_d_tgr
AFTER UPDATE OF deleted ON core_topology
FOR EACH ROW EXECUTE PROCEDURE delete_related_intervention();

CREATE TRIGGER maintenance_report_interventions_d_tgr
AFTER UPDATE OF deleted ON feedback_report
FOR EACH ROW EXECUTE PROCEDURE delete_related_intervention_report();

-------------------------------------------------------------------------------
-- Denormalized altimetry information
-------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ DROP FUNCTION IF EXISTS update_altimetry_topology_intervention() CASCADE;
DROP FUNCTION IF EXISTS update_altimetry_intervention() CASCADE;
DROP FUNCTION IF EXISTS update_area_intervention() CASCADE;
DROP FUNCTION IF EXISTS delete_related_intervention_blade() CASCADE;
DROP FUNCTION IF EXISTS delete_related_intervention_report() CASCADE;

-- 20

Expand Down
Loading

0 comments on commit e6523e4

Please sign in to comment.