Skip to content

Commit f19ac51

Browse files
committed
[changes] Rename 'fuzzy' field, updated tests
Signed-off-by: DragnEmperor <[email protected]>
1 parent 95efe7c commit f19ac51

File tree

7 files changed

+69
-27
lines changed

7 files changed

+69
-27
lines changed

openwisp_controller/config/whois/tasks.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,15 @@ def manage_fuzzy_locations(
181181
"""
182182
Creates/updates fuzzy location for a device based on the latitude and longitude
183183
or attaches an existing location if `add_existing` is True.
184-
Existing location here means a location of a device whose last_ip matches
184+
Existing location here means a location of another device whose last_ip matches
185185
the given ip_address.
186+
187+
- If attaching existing location and current device has no location then the
188+
existing location is attached to the current device as well.
189+
- If attaching existing location and current device already has location, then
190+
current device's location is deleted and set same as existing device's location.
191+
- If not attaching existing then new location is created if no location exists for
192+
current device, or existing one is updated if it is approximate(fuzzy).
186193
"""
187194
Device = load_model("config", "Device")
188195
Location = load_model("geo", "Location")
@@ -199,9 +206,8 @@ def manage_fuzzy_locations(
199206

200207
# if attaching an existing location, the current device should not have
201208
# a location already set.
202-
# TODO: Do we do this if device location exists but is marked fuzzy?
203209
current_location = device_location.location
204-
if add_existing and (not current_location or current_location.fuzzy):
210+
if add_existing and (not current_location or current_location.is_approximate):
205211
existing_device_location = (
206212
Device.objects.select_related("devicelocation")
207213
.filter(last_ip=ip_address, devicelocation__location__isnull=False)
@@ -223,23 +229,31 @@ def manage_fuzzy_locations(
223229
coords = Point(longitude, latitude, srid=4326)
224230
# Create/update the device location mapping, updating existing location
225231
# if exists else create a new location
232+
location_name = (
233+
" ".join(address.split(",")[:2])
234+
if address
235+
else f"Approximate Location {ip_address}"
236+
)
226237
location_defaults = {
227-
"name": f"{device.name} Location",
238+
"name": location_name,
228239
"type": "outdoor",
229240
"organization_id": device.organization_id,
230241
"is_mobile": False,
231242
"geometry": coords,
232243
"address": address,
233244
}
234-
if device_location.location and device_location.location.fuzzy:
245+
if device_location.location and device_location.location.is_approximate:
235246
for attr, value in location_defaults.items():
236247
setattr(device_location.location, attr, value)
237248
device_location.location.full_clean()
238249
device_location.location.save()
239250
elif not device_location.location:
240-
location = Location(**location_defaults, fuzzy=True)
251+
location = Location(**location_defaults, is_approximate=True)
241252
location.full_clean()
242253
location.save()
243254
device_location.location = location
244255
device_location.full_clean()
245256
device_location.save()
257+
logger.info(
258+
f"Fuzzy location saved successfully for {device_pk} for IP: {ip_address}"
259+
)

openwisp_controller/config/whois/test_whois.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ def _task_called(self, mocked_task, task_name="WHOIS lookup"):
306306
mocked_task.reset_mock()
307307

308308
with self.subTest(
309-
f"{task_name} task called via DeviceChecksumView when a device has no WHOIS record"
309+
f"{task_name} task called via DeviceChecksumView for no WHOIS record"
310310
):
311311
WHOISInfo.objects.all().delete()
312312
response = self.client.get(
@@ -325,7 +325,9 @@ def test_whois_task_called(self, mocked_lookup_task):
325325

326326
Device.objects.all().delete() # Clear existing devices
327327
device = self._create_device()
328-
with self.subTest("WHOIS lookup task not called when last_ip has related WhoIsInfo"):
328+
with self.subTest(
329+
"WHOIS lookup task not called when last_ip has related WhoIsInfo"
330+
):
329331
device.last_ip = "172.217.22.14"
330332
self._create_whois_info(ip_address=device.last_ip)
331333
device.save()
@@ -460,7 +462,7 @@ def _verify_whois_details(instance, ip_address):
460462

461463
with self.subTest("Test WHOIS create when device is created"):
462464
device = self._create_device(last_ip="172.217.22.14")
463-
self.assertEqual(mock_info.call_count, 1)
465+
self.assertEqual(mock_info.call_count, 2)
464466
mock_info.reset_mock()
465467
device.refresh_from_db()
466468

@@ -474,7 +476,7 @@ def _verify_whois_details(instance, ip_address):
474476
old_ip_address = device.last_ip
475477
device.last_ip = "172.217.22.10"
476478
device.save()
477-
self.assertEqual(mock_info.call_count, 1)
479+
self.assertEqual(mock_info.call_count, 2)
478480
mock_info.reset_mock()
479481
device.refresh_from_db()
480482

@@ -528,7 +530,7 @@ def _verify_location_details(device, mocked_response):
528530
device = self._create_device(last_ip="172.217.22.14")
529531

530532
location = device.devicelocation.location
531-
self.assertEqual(location.fuzzy, True)
533+
self.assertEqual(location.is_approximate, True)
532534
self.assertEqual(location.is_mobile, False)
533535
self.assertEqual(location.type, "outdoor")
534536
_verify_location_details(device, mocked_response)
@@ -543,29 +545,29 @@ def _verify_location_details(device, mocked_response):
543545
device.refresh_from_db()
544546

545547
location = device.devicelocation.location
546-
self.assertEqual(location.fuzzy, True)
548+
self.assertEqual(location.is_approximate, True)
547549
self.assertEqual(location.is_mobile, False)
548550
self.assertEqual(location.type, "outdoor")
549551
_verify_location_details(device, mocked_response)
550552

551553
with self.subTest(
552-
"Test Location not updated if it is not fuzzy when last ip is updated"
554+
"Test Location not updated if it is not approximate when last ip is updated"
553555
):
554556
device.last_ip = "172.217.22.11"
555-
device.devicelocation.location.fuzzy = False
557+
device.devicelocation.location.is_approximate = False
556558
mock_client.return_value.city.return_value = self._mocked_client_response()
557559
device.devicelocation.location.save()
558560
device.save()
559561
device.refresh_from_db()
560562

561563
location = device.devicelocation.location
562-
self.assertEqual(location.fuzzy, False)
564+
self.assertEqual(location.is_approximate, False)
563565
self.assertEqual(location.is_mobile, False)
564566
self.assertEqual(location.type, "outdoor")
565567
_verify_location_details(device, mocked_response)
566568

567569
with self.subTest(
568-
"Test Location shared among devices with same last_ip when new device's location does not exist"
570+
"Test shared location for same IP when new device's location does not exist"
569571
):
570572
Device.objects.all().delete()
571573
device1 = self._create_device(last_ip="172.217.22.10")
@@ -580,7 +582,7 @@ def _verify_location_details(device, mocked_response):
580582
)
581583

582584
with self.subTest(
583-
"Test Location shared among devices with same last_ip when new device's location exist"
585+
"Test shared location for same IP when new device's location exist"
584586
):
585587
Device.objects.all().delete()
586588
device1 = self._create_device(last_ip="172.217.22.10")

openwisp_controller/geo/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class LocationAdmin(MultitenantAdminMixin, AbstractLocationAdmin):
9898
form = LocationForm
9999
inlines = [FloorPlanInline]
100100
list_select_related = ("organization",)
101+
readonly_fields = ("is_approximate",)
101102

102103

103104
LocationAdmin.list_display.insert(1, "organization")

openwisp_controller/geo/base/models.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111

1212

1313
class BaseLocation(OrgMixin, AbstractLocation):
14-
fuzzy = models.BooleanField(
14+
is_approximate = models.BooleanField(
1515
default=False,
16-
help_text=_(
17-
"If true, the location is considered fuzzy and "
18-
"may not have precise coordinates."
19-
),
16+
help_text=_("Whether the location's coordinates are approximate."),
2017
)
2118

2219
class Meta(AbstractLocation.Meta):

openwisp_controller/geo/migrations/0004_location_fuzzy.py renamed to openwisp_controller/geo/migrations/0004_location_is_approximate.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,10 @@ class Migration(migrations.Migration):
1212
operations = [
1313
migrations.AddField(
1414
model_name="location",
15-
name="fuzzy",
15+
name="is_approximate",
1616
field=models.BooleanField(
1717
default=False,
18-
help_text=(
19-
"If true, the location is considered fuzzy and "
20-
"may not have precise coordinates."
21-
),
18+
help_text=("Whether the location's coordinates are approximate."),
2219
),
2320
),
2421
]

openwisp_controller/geo/tests/test_admin.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,16 @@ def test_admin_menu_groups(self):
137137
html=True,
138138
)
139139

140+
def test_location_readonly_fields(self):
141+
location = self._create_location(
142+
name="location1org", type="indoor", organization=self._get_org()
143+
)
144+
self._create_admin()
145+
self._login()
146+
url = reverse(f"admin:{self.app_label}_location_change", args=[location.pk])
147+
response = self.client.get(url)
148+
self.assertNotContains(response, '<input type="checkbox" name="is_approximate"')
149+
140150

141151
class TestDeviceAdmin(
142152
TestImportExportMixin, TestAdminMixin, TestGeoMixin, TestOrganizationMixin, TestCase
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Generated by Django 5.2.1 on 2025-07-01 19:18
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("sample_geo", "0003_alter_devicelocation_floorplan_location"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="location",
15+
name="is_approximate",
16+
field=models.BooleanField(
17+
default=False,
18+
help_text="Whether the location's coordinates are approximate.",
19+
),
20+
),
21+
]

0 commit comments

Comments
 (0)