1+ import re
2+
13from django .contrib .gis .db import models
24from django .core .exceptions import ValidationError
5+ from django .utils .translation import gettext
36from django .utils .translation import gettext_lazy as _
47from django_loci .base .models import (
58 AbstractFloorPlan ,
811)
912from swapper import get_model_name
1013
14+ from openwisp_controller .config .whois .service import WHOISService
1115from openwisp_users .mixins import OrgMixin , ValidateOrgMixin
1216
13- from ..estimated_location .utils import check_estimate_location_configured
14-
1517
1618class BaseLocation (OrgMixin , AbstractLocation ):
19+ _changed_checked_fields = ["is_estimated" , "address" , "geometry" ]
20+
1721 is_estimated = models .BooleanField (
1822 default = False ,
1923 help_text = _ ("Whether the location's coordinates are estimated." ),
@@ -24,15 +28,25 @@ class Meta(AbstractLocation.Meta):
2428
2529 def __init__ (self , * args , ** kwargs ):
2630 super ().__init__ (* args , ** kwargs )
27- self ._initial_is_estimated = self .is_estimated
31+ self ._set_initial_values_for_changed_checked_fields ()
32+
33+ def _set_initial_values_for_changed_checked_fields (self ):
34+ deferred_fields = self .get_deferred_fields ()
35+ for field in self ._changed_checked_fields :
36+ if field in deferred_fields :
37+ setattr (self , f"_initial_{ field } " , models .DEFERRED )
38+ else :
39+ setattr (self , f"_initial_{ field } " , getattr (self , field ))
2840
2941 def clean (self ):
3042 # Raise validation error if `is_estimated` is True but estimated feature is
3143 # disabled.
3244 if (
3345 (self ._state .adding or self ._initial_is_estimated != self .is_estimated )
3446 and self .is_estimated
35- and not check_estimate_location_configured (self .organization_id )
47+ and not WHOISService .check_estimate_location_configured (
48+ self .organization_id
49+ )
3650 ):
3751 raise ValidationError (
3852 {
@@ -43,6 +57,35 @@ def clean(self):
4357 )
4458 return super ().clean ()
4559
60+ def save (self , * args , _set_estimated = False , ** kwargs ):
61+ """
62+ Save the location object with special handling for estimated locations.
63+
64+ Parameters:
65+ _set_estimated: Boolean flag to indicate if this save is being performed
66+ by the estimated location system. When False (default),
67+ manual edits will clear the estimated status.
68+ *args, **kwargs: Arguments passed to the parent save method.
69+
70+ Returns:
71+ The result of the parent save method.
72+ """
73+ if WHOISService .check_estimate_location_configured (self .organization_id ):
74+ if not _set_estimated and (
75+ self ._initial_address != self .address
76+ or self ._initial_geometry != self .geometry
77+ ):
78+ self .is_estimated = False
79+ estimated_string = gettext ("Estimated Location" )
80+ if self .name and estimated_string in self .name :
81+ # remove string starting with "(Estimated Location"
82+ self .name = re .sub (
83+ rf"\s\({ estimated_string } .*" , "" , self .name , flags = re .IGNORECASE
84+ )
85+ else :
86+ self .is_estimated = self ._initial_is_estimated
87+ return super ().save (* args , ** kwargs )
88+
4689
4790class BaseFloorPlan (OrgMixin , AbstractFloorPlan ):
4891 location = models .ForeignKey (get_model_name ("geo" , "Location" ), models .CASCADE )
0 commit comments