11from ipaddress import ip_address , ip_network
22
3+ from django .contrib .gis .db .models import PointField
34from django .core .cache import cache
45from django .core .exceptions import ValidationError
56from django .db import models , transaction
67from django .utils .translation import gettext_lazy as _
78from jsonfield import JSONField
9+ from swapper import load_model
810
911from openwisp_utils .base import TimeStampedEditableModel
1012
@@ -51,6 +53,12 @@ class AbstractWHOISInfo(TimeStampedEditableModel):
5153 blank = True ,
5254 help_text = _ ("CIDR" ),
5355 )
56+ coordinates = PointField (
57+ null = True ,
58+ blank = True ,
59+ help_text = _ ("Coordinates" ),
60+ srid = 4326 ,
61+ )
5462
5563 class Meta :
5664 abstract = True
@@ -74,6 +82,20 @@ def clean(self):
7482 raise ValidationError (
7583 {"cidr" : _ ("Invalid CIDR format: %(error)s" ) % {"error" : str (e )}}
7684 )
85+
86+ if self .coordinates :
87+ if not (- 90 <= self .coordinates .y <= 90 ):
88+ raise ValidationError (
89+ {"coordinates" : _ ("Latitude must be between -90 and 90 degrees." )}
90+ )
91+ if not (- 180 <= self .coordinates .x <= 180 ):
92+ raise ValidationError (
93+ {
94+ "coordinates" : _ (
95+ "Longitude must be between -180 and 180 degrees."
96+ )
97+ }
98+ )
7799 return super ().clean ()
78100
79101 @staticmethod
@@ -82,8 +104,18 @@ def device_whois_info_delete_handler(instance, **kwargs):
82104 Delete WHOIS information for a device when the last IP address is removed or
83105 when device is deleted.
84106 """
85- if instance ._get_organization__config_settings ().whois_enabled :
86- transaction .on_commit (lambda : delete_whois_record .delay (instance .last_ip ))
107+ Device = load_model ("config" , "Device" )
108+
109+ last_ip = instance .last_ip
110+ existing_devices = Device .objects .filter (_is_deactivated = False ).filter (
111+ last_ip = last_ip
112+ )
113+ if (
114+ last_ip
115+ and instance ._get_organization__config_settings ().whois_enabled
116+ and not existing_devices .exists ()
117+ ):
118+ transaction .on_commit (lambda : delete_whois_record .delay (last_ip ))
87119
88120 # this method is kept here instead of in OrganizationConfigSettings because
89121 # currently the caching is used only for WHOIS feature
@@ -113,3 +145,28 @@ def formatted_address(self):
113145 ],
114146 )
115147 )
148+
149+ @property
150+ def _location_name (self ):
151+ """
152+ Used to get location name based on the address and IP.
153+ """
154+ address = self .formatted_address
155+ if address :
156+ parts = [part .strip () for part in address .split ("," )[:2 ] if part .strip ()]
157+ location = ", " .join (parts )
158+ return _ (f"{ location } (Estimated Location: { self .ip_address } )" )
159+ return _ (f"Estimated Location: { self .ip_address } " )
160+
161+ def _get_defaults_for_estimated_location (self ):
162+ """
163+ Used to get default values for creating or updating
164+ an estimated location based on the WHOIS information.
165+ """
166+ return {
167+ "name" : self ._location_name ,
168+ "type" : "outdoor" ,
169+ "is_mobile" : False ,
170+ "geometry" : self .coordinates ,
171+ "address" : self .formatted_address ,
172+ }
0 commit comments