From 51769764e00042ea6a3516016ef4870ef57f42be Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 9 Jan 2025 17:51:30 +0200 Subject: [PATCH] Cleanup use of word boundary --- temba/api/v2/views.py | 8 +- .../management/commands/import_geojson.py | 38 +++---- temba/locations/models.py | 103 ------------------ temba/locations/tests/test_geojson.py | 32 +++--- temba/locations/tests/test_location.py | 28 ++--- temba/locations/urls.py | 4 +- temba/locations/views.py | 26 ++--- temba/orgs/models.py | 1 + temba/orgs/tests/test_org.py | 2 +- temba/tests/mailroom.py | 20 ++-- 10 files changed, 80 insertions(+), 182 deletions(-) diff --git a/temba/api/v2/views.py b/temba/api/v2/views.py index a58eee7b90c..ea082fe0ed1 100644 --- a/temba/api/v2/views.py +++ b/temba/api/v2/views.py @@ -408,11 +408,11 @@ class BoundariesEndpoint(ListAPIMixin, BaseEndpoint): A `GET` returns the boundaries for your organization with the following fields. To include geometry, specify `geometry=true`. - * **osm_id** - the OSM ID for this boundary prefixed with the element type (string). - * **name** - the name of the administrative boundary (string). - * **parent** - the id of the containing parent of this boundary or null if this boundary is a country (string). + * **osm_id** - the OSM ID for this location prefixed with the element type (string). + * **name** - the name of the administrative location (string). + * **parent** - the id of the containing parent of this location or null if this location is a country (string). * **level** - the level: 0 for country, 1 for state, 2 for district (int). - * **geometry** - the geometry for this boundary, which will usually be a MultiPolygon (GEOJSON). + * **geometry** - the geometry for this location, which will usually be a MultiPolygon (GEOJSON). **Note that including geometry may produce a very large result so it is recommended to cache the results on the client side.** diff --git a/temba/locations/management/commands/import_geojson.py b/temba/locations/management/commands/import_geojson.py index 17e70af9c29..4dd9daa84a3 100644 --- a/temba/locations/management/commands/import_geojson.py +++ b/temba/locations/management/commands/import_geojson.py @@ -16,7 +16,7 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument("files", nargs="+") parser.add_argument( - "--country", dest="country", default=None, help="Only process the boundary files for this country osm id" + "--country", dest="country", default=None, help="Only process the location files for this country osm id" ) def import_file(self, filename, file): @@ -79,11 +79,11 @@ def import_file(self, filename, file): continue # try to find existing admin level by osm_id - boundary = Location.objects.filter(osm_id=osm_id) + location = Location.objects.filter(osm_id=osm_id) # didn't find it? what about by name? - if not boundary: - boundary = Location.objects.filter(parent=parent, name__iexact=name) + if not location: + location = Location.objects.filter(parent=parent, name__iexact=name) # skip over items with no geometry if not feature["geometry"] or not feature["geometry"]["coordinates"]: @@ -105,18 +105,18 @@ def import_file(self, filename, file): kwargs["simplified_geometry"] = geometry # if this is an update, just update with those fields - if boundary: + if location: if not parent: kwargs["path"] = name else: kwargs["path"] = parent.path + Location.PADDED_PATH_SEPARATOR + name self.stdout.write(self.style.SUCCESS(f" ** updating {name} ({osm_id})")) - boundary = boundary.first() - boundary.update(**kwargs) + location = location.first() + location.update(**kwargs) # update any children - boundary.update_path() + location.update_path() # otherwise, this is new, so create it else: @@ -126,19 +126,19 @@ def import_file(self, filename, file): # keep track of this osm_id seen_osm_ids.add(osm_id) - # now remove any unseen boundaries + # now remove any unseen locations if osm_id: - last_boundary = Location.objects.filter(osm_id=osm_id).first() - if last_boundary: - self.stdout.write(self.style.SUCCESS(f" ** removing unseen boundaries ({osm_id})")) - country = last_boundary.get_root() - unseen_boundaries = country.get_descendants().filter(level=level).exclude(osm_id__in=seen_osm_ids) + last_location = Location.objects.filter(osm_id=osm_id).first() + if last_location: + self.stdout.write(self.style.SUCCESS(f" ** removing unseen locations ({osm_id})")) + country = last_location.get_root() + unseen_locations = country.get_descendants().filter(level=level).exclude(osm_id__in=seen_osm_ids) deleted_count = 0 - for unseen_boundary in unseen_boundaries: - unseen_boundary.release() + for unseen_location in unseen_locations: + unseen_location.release() deleted_count += 1 if deleted_count > 0: - self.stdout.write(f" ** Unseen boundaries removed: {deleted_count}") + self.stdout.write(f" ** Unseen locations removed: {deleted_count}") return country, seen_osm_ids else: @@ -193,7 +193,7 @@ def handle(self, *args, **options): if country is None: return - # remove all other unseen boundaries from the database for the country + # remove all other unseen locations from the database for the country with connection.cursor() as cursor: cursor.execute( """ @@ -217,7 +217,7 @@ def handle(self, *args, **options): """, (country.id, list(updated_osm_ids)), ) - self.stdout.write(self.style.SUCCESS(f"Other unseen boundaries removed: {cursor.rowcount}")) + self.stdout.write(self.style.SUCCESS(f"Other unseen locations removed: {cursor.rowcount}")) if country: self.stdout.write(self.style.SUCCESS((f" ** updating paths for all of {country.name}"))) diff --git a/temba/locations/models.py b/temba/locations/models.py index 2957b566de4..5a829a44f73 100644 --- a/temba/locations/models.py +++ b/temba/locations/models.py @@ -46,73 +46,6 @@ class AdminBoundary(MPTTModel, models.Model): objects = NoGeometryManager() geometries = GeometryManager() - @staticmethod - def get_geojson_dump(name, features): - # build a feature collection - feature_collection = geojson.FeatureCollection(features) - return geojson.dumps({"name": name, "geometry": feature_collection}) - - def as_json(self, org): - result = dict(osm_id=self.osm_id, name=self.name, level=self.level, aliases="", path=self.path) - - if self.parent: - result["parent_osm_id"] = self.parent.osm_id - - aliases = "\n".join(sorted([alias.name for alias in self.aliases.filter(org=org)])) - result["aliases"] = aliases - return result - - def get_geojson_feature(self): - return geojson.Feature( - properties=dict(name=self.name, osm_id=self.osm_id, id=self.pk, level=self.level), - zoomable=True if self.children.all() else False, - geometry=None if not self.simplified_geometry else geojson.loads(self.simplified_geometry.geojson), - ) - - def get_geojson(self): - return AdminBoundary.get_geojson_dump(self.name, [self.get_geojson_feature()]) - - def get_children_geojson(self): - children = [] - for child in self.children.all(): - children.append(child.get_geojson_feature()) - return AdminBoundary.get_geojson_dump(self.name, children) - - def update(self, **kwargs): - AdminBoundary.objects.filter(id=self.id).update(**kwargs) - - # update our object values so that self is up to date - for key, value in kwargs.items(): - setattr(self, key, value) - - def update_path(self): - if self.level == 0: - self.path = self.name - self.save(update_fields=("path",)) - - def _update_child_paths(boundary): - boundaries = AdminBoundary.objects.filter(parent=boundary).only("name", "parent__path") - boundaries.update( - path=Concat(Value(boundary.path), Value(" %s " % AdminBoundary.PATH_SEPARATOR), F("name")) - ) - for boundary in boundaries: - _update_child_paths(boundary) - - _update_child_paths(self) - - def update_aliases(self, org, user, aliases: list): - siblings = self.parent.children.all() - - self.aliases.filter(org=org).delete() # delete any existing aliases for this workspace - - for new_alias in aliases: - assert new_alias and len(new_alias) < AdminBoundary.MAX_NAME_LEN - - # aliases are only allowed to exist on one boundary with same parent at a time - BoundaryAlias.objects.filter(name=new_alias, boundary__in=siblings, org=org).delete() - - BoundaryAlias.create(org, user, self, new_alias) - def release(self): for child_boundary in AdminBoundary.objects.filter(parent=self): # pragma: no cover child_boundary.release() @@ -120,42 +53,6 @@ def release(self): self.aliases.all().delete() self.delete() - @classmethod - def create(cls, osm_id, name, level, parent=None, **kwargs): - """ - Create method that takes care of creating path based on name and parent - """ - path = name - if parent is not None: - path = parent.path + AdminBoundary.PADDED_PATH_SEPARATOR + name - - return AdminBoundary.objects.create(osm_id=osm_id, name=name, level=level, parent=parent, path=path, **kwargs) - - @classmethod - def strip_last_path(cls, path): - """ - Strips the last part of the passed in path. Throws if there is no separator - """ - parts = path.split(AdminBoundary.PADDED_PATH_SEPARATOR) - if len(parts) <= 1: # pragma: no cover - raise Exception("strip_last_path called without a path to strip") - - return AdminBoundary.PADDED_PATH_SEPARATOR.join(parts[:-1]) - - @classmethod - def get_by_path(cls, org, path): - cache = getattr(org, "_abs", {}) - - if not cache: - setattr(org, "_abs", cache) - - boundary = cache.get(path) - if not boundary: - boundary = AdminBoundary.objects.filter(path=path).first() - cache[path] = boundary - - return boundary - def __str__(self): return self.name diff --git a/temba/locations/tests/test_geojson.py b/temba/locations/tests/test_geojson.py index c2a4107f3e8..8df23ce751b 100644 --- a/temba/locations/tests/test_geojson.py +++ b/temba/locations/tests/test_geojson.py @@ -50,7 +50,7 @@ class ImportGeoJSONtest(TembaTest): }] }""" - data_geojson_level_1_new_boundary = """{ + data_geojson_level_1_new_location = """{ "type": "FeatureCollection", "features": [{ "type": "Feature", @@ -183,7 +183,7 @@ def test_ok_filename_admin(self): self.assertEqual( captured_output.getvalue(), - "=== parsing R188933admin0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen boundaries (R1000)\nOther unseen boundaries removed: 0\n ** updating paths for all of Granica\n", + "=== parsing R188933admin0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen locations (R1000)\nOther unseen locations removed: 0\n ** updating paths for all of Granica\n", ) self.assertEqual(Location.objects.count(), 1) @@ -195,7 +195,7 @@ def test_ok_filename_admin_level_with_country_prefix(self): self.assertEqual( captured_output.getvalue(), - "=== parsing R188933admin0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen boundaries (R1000)\nOther unseen boundaries removed: 0\n ** updating paths for all of Granica\n", + "=== parsing R188933admin0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen locations (R1000)\nOther unseen locations removed: 0\n ** updating paths for all of Granica\n", ) self.assertEqual(Location.objects.count(), 1) @@ -207,7 +207,7 @@ def test_ok_filename_admin_level(self): self.assertEqual( captured_output.getvalue(), - "=== parsing admin_level_0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen boundaries (R1000)\nOther unseen boundaries removed: 0\n ** updating paths for all of Granica\n", + "=== parsing admin_level_0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen locations (R1000)\nOther unseen locations removed: 0\n ** updating paths for all of Granica\n", ) self.assertEqual(Location.objects.count(), 1) @@ -240,7 +240,7 @@ def test_feature_multipolygon_geometry(self): self.assertEqual( captured_output.getvalue(), - "=== parsing admin_level_0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen boundaries (R1000)\nOther unseen boundaries removed: 0\n ** updating paths for all of Granica\n", + "=== parsing admin_level_0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen locations (R1000)\nOther unseen locations removed: 0\n ** updating paths for all of Granica\n", ) self.assertOSMIDs({"R1000"}) @@ -267,7 +267,7 @@ def test_feature_with_parent(self): self.assertEqual( captured_output.getvalue(), - "=== parsing admin_level_0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen boundaries (R1000)\n=== parsing admin_level_1_simplified.json\n ** adding Međa 2 (R2000)\n ** removing unseen boundaries (R2000)\n=== parsing admin_level_2_simplified.json\n ** adding Međa 55 (R55000)\n ** removing unseen boundaries (R55000)\nOther unseen boundaries removed: 0\n ** updating paths for all of Granica\n", + "=== parsing admin_level_0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen locations (R1000)\n=== parsing admin_level_1_simplified.json\n ** adding Međa 2 (R2000)\n ** removing unseen locations (R2000)\n=== parsing admin_level_2_simplified.json\n ** adding Međa 55 (R55000)\n ** removing unseen locations (R55000)\nOther unseen locations removed: 0\n ** updating paths for all of Granica\n", ) self.assertOSMIDs({"R1000", "R2000", "R55000"}) @@ -299,12 +299,12 @@ def test_update_features_with_parent(self): self.assertEqual( captured_output.getvalue(), - "=== parsing admin_level_0_simplified.json\n ** updating Granica (R1000)\n ** removing unseen boundaries (R1000)\n=== parsing admin_level_1_simplified.json\n ** updating Međa 2 (R2000)\n ** removing unseen boundaries (R2000)\nOther unseen boundaries removed: 0\n ** updating paths for all of Granica\n", + "=== parsing admin_level_0_simplified.json\n ** updating Granica (R1000)\n ** removing unseen locations (R1000)\n=== parsing admin_level_1_simplified.json\n ** updating Međa 2 (R2000)\n ** removing unseen locations (R2000)\nOther unseen locations removed: 0\n ** updating paths for all of Granica\n", ) self.assertOSMIDs({"R1000", "R2000"}) - def test_remove_unseen_boundaries(self): + def test_remove_unseen_locations(self): # insert features in the database geojson_data = [self.data_geojson_level_0, self.data_geojson_level_1] @@ -320,8 +320,8 @@ def test_remove_unseen_boundaries(self): LocationAlias.create(self.org, self.admin, Location.objects.get(osm_id="R2000"), "My Alias") - # update data, and add a new boundary - geojson_data = [self.data_geojson_level_0, self.data_geojson_level_1_new_boundary] + # update data, and add a new location + geojson_data = [self.data_geojson_level_0, self.data_geojson_level_1_new_location] with patch("builtins.open") as mock_file: mock_file.return_value.__enter__ = lambda filename: filename @@ -333,13 +333,13 @@ def test_remove_unseen_boundaries(self): self.assertEqual( captured_output.getvalue(), - "=== parsing admin_level_0_simplified.json\n ** updating Granica (R1000)\n ** removing unseen boundaries (R1000)\n=== parsing admin_level_1_simplified.json\n ** adding Međa 3 (R3000)\n ** removing unseen boundaries (R3000)\n ** Unseen boundaries removed: 1\nOther unseen boundaries removed: 0\n ** updating paths for all of Granica\n", + "=== parsing admin_level_0_simplified.json\n ** updating Granica (R1000)\n ** removing unseen locations (R1000)\n=== parsing admin_level_1_simplified.json\n ** adding Međa 3 (R3000)\n ** removing unseen locations (R3000)\n ** Unseen locations removed: 1\nOther unseen locations removed: 0\n ** updating paths for all of Granica\n", ) self.assertOSMIDs({"R1000", "R3000"}) - def test_remove_other_unseen_boundaries(self): - # other unseen boundaries are boundaries which have not been updated in any way for a country + def test_remove_other_unseen_locations(self): + # other unseen locations are locations which have not been updated in any way for a country # insert features in the database geojson_data = [self.data_geojson_level_0, self.data_geojson_level_1] @@ -354,7 +354,7 @@ def test_remove_other_unseen_boundaries(self): self.assertOSMIDs({"R1000", "R2000"}) - # update data, and add a new boundary + # update data, and add a new location geojson_data = [self.data_geojson_level_0] with patch("builtins.open") as mock_file: @@ -367,7 +367,7 @@ def test_remove_other_unseen_boundaries(self): self.assertEqual( captured_output.getvalue(), - "=== parsing admin_level_0_simplified.json\n ** updating Granica (R1000)\n ** removing unseen boundaries (R1000)\nOther unseen boundaries removed: 1\n ** updating paths for all of Granica\n", + "=== parsing admin_level_0_simplified.json\n ** updating Granica (R1000)\n ** removing unseen locations (R1000)\nOther unseen locations removed: 1\n ** updating paths for all of Granica\n", ) self.assertOSMIDs({"R1000"}) @@ -385,7 +385,7 @@ def test_zipfiles_parsing(self): self.assertEqual( captured_output.getvalue(), - "=== parsing admin_level_0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen boundaries (R1000)\nOther unseen boundaries removed: 0\n ** updating paths for all of Granica\n", + "=== parsing admin_level_0_simplified.json\n ** adding Granica (R1000)\n ** removing unseen locations (R1000)\nOther unseen locations removed: 0\n ** updating paths for all of Granica\n", ) self.assertOSMIDs({"R1000"}) diff --git a/temba/locations/tests/test_location.py b/temba/locations/tests/test_location.py index fa05e50427e..41d584ed939 100644 --- a/temba/locations/tests/test_location.py +++ b/temba/locations/tests/test_location.py @@ -194,7 +194,7 @@ def test_boundaries(self): self.assertEqual("kig\nkigs", children[1]["aliases"]) self.assertEqual( "", children[0]["aliases"] - ) # kigs alias should have been moved from the eastern province boundary + ) # kigs alias should have been moved from the eastern province location # fetch our aliases response = self.client.get(reverse("locations.location_boundaries", args=[self.state1.osm_id])) @@ -287,7 +287,7 @@ def test_boundaries(self): self.assertEqual(400, response.status_code) self.assertEqual(response_json.get("status"), "error") - # Get geometry of admin boundary without sub-levels, should return one feature + # Get geometry of admin location without sub-levels, should return one feature response = self.client.get(reverse("locations.location_geometry", args=[self.ward3.osm_id])) self.assertEqual(200, response.status_code) response_json = response.json() @@ -305,23 +305,23 @@ def test_boundaries(self): def test_location_create(self): # create a simple location - boundary = Location.create(osm_id="-1", name="Null Island", level=0) - self.assertEqual(boundary.path, "Null Island") - self.assertIsNone(boundary.simplified_geometry) + location = Location.create(osm_id="-1", name="Null Island", level=0) + self.assertEqual(location.path, "Null Island") + self.assertIsNone(location.simplified_geometry) - # create a simple boundary with parent - child_boundary = Location.create(osm_id="-2", name="Palm Tree", level=1, parent=boundary) - self.assertEqual(child_boundary.path, "Null Island > Palm Tree") - self.assertIsNone(child_boundary.simplified_geometry) + # create a simple location with parent + child_location = Location.create(osm_id="-2", name="Palm Tree", level=1, parent=location) + self.assertEqual(child_location.path, "Null Island > Palm Tree") + self.assertIsNone(child_location.simplified_geometry) wkb_geometry = {"type": "MultiPolygon", "coordinates": [[[[71.83225, 39.95415], [71.82655, 39.9563]]]]} - # create a simple boundary with parent and geometry - geom_boundary = Location.create( - osm_id="-3", name="Plum Tree", level=1, parent=boundary, simplified_geometry=wkb_geometry + # create a simple location with parent and geometry + geom_location = Location.create( + osm_id="-3", name="Plum Tree", level=1, parent=location, simplified_geometry=wkb_geometry ) - self.assertEqual(geom_boundary.path, "Null Island > Plum Tree") - self.assertIsNotNone(geom_boundary.simplified_geometry) + self.assertEqual(geom_location.path, "Null Island > Plum Tree") + self.assertIsNotNone(geom_location.simplified_geometry) # path should not be defined when calling Location.create self.assertRaises(TypeError, Location.create, osm_id="-1", name="Null Island", level=0, path="some path") diff --git a/temba/locations/urls.py b/temba/locations/urls.py index b5fc995d174..9d097a1be4b 100644 --- a/temba/locations/urls.py +++ b/temba/locations/urls.py @@ -1,3 +1,3 @@ -from .views import BoundaryCRUDL +from .views import LocationCRUDL -urlpatterns = BoundaryCRUDL().as_urlpatterns() +urlpatterns = LocationCRUDL().as_urlpatterns() diff --git a/temba/locations/views.py b/temba/locations/views.py index 09eebbfa6c5..335cd2b7b7c 100644 --- a/temba/locations/views.py +++ b/temba/locations/views.py @@ -13,7 +13,7 @@ from temba.utils.views.mixins import ContextMenuMixin, SpaMixin -class BoundaryCRUDL(SmartCRUDL): +class LocationCRUDL(SmartCRUDL): actions = ("alias", "geometry", "boundaries") model = Location @@ -76,22 +76,22 @@ def post(self, request, *args, **kwargs): org = request.org try: - boundary_update = json.loads(json_string) + location_update = json.loads(json_string) except Exception as e: return JsonResponse(dict(status="error", description="Error parsing JSON: %s" % str(e)), status=400) - boundary = Location.objects.filter(osm_id=boundary_update["osm_id"]).first() - aliases = boundary_update.get("aliases", "") - if boundary: + location = Location.objects.filter(osm_id=location_update["osm_id"]).first() + aliases = location_update.get("aliases", "") + if location: unique_new_aliases = [a.strip() for a in set(aliases.split("\n")) if a] - boundary.update_aliases(org, self.request.user, unique_new_aliases) + location.update_aliases(org, self.request.user, unique_new_aliases) - return JsonResponse(boundary_update, safe=False) + return JsonResponse(location_update, safe=False) def get(self, request, *args, **kwargs): org = request.org - boundary = self.get_object() + location = self.get_object() page_size = 25 @@ -100,7 +100,7 @@ def get(self, request, *args, **kwargs): if query: page = int(request.GET.get("page", 0)) matches = set( - Location.objects.filter(path__startswith=f"{boundary.name} {Location.PATH_SEPARATOR}").filter( + Location.objects.filter(path__startswith=f"{location.name} {Location.PATH_SEPARATOR}").filter( name__icontains=query ) ) @@ -117,16 +117,16 @@ def get(self, request, *args, **kwargs): # otherwise grab each item in the path path = [] - while boundary: + while location: children = list( - Location.objects.filter(parent__osm_id=boundary.osm_id) + Location.objects.filter(parent__osm_id=location.osm_id) .order_by("name") .prefetch_related( Prefetch("aliases", queryset=LocationAlias.objects.filter(org=org).order_by("name")) ) ) - item = boundary.as_json(org) + item = location.as_json(org) children_json = [] for child in children: child_json = child.as_json(org) @@ -136,7 +136,7 @@ def get(self, request, *args, **kwargs): item["children"] = children_json item["has_children"] = len(children_json) > 0 path.append(item) - boundary = boundary.parent + location = location.parent path.reverse() return JsonResponse(path, safe=False) diff --git a/temba/orgs/models.py b/temba/orgs/models.py index 4677aed08a9..30d5d258260 100644 --- a/temba/orgs/models.py +++ b/temba/orgs/models.py @@ -1407,6 +1407,7 @@ def delete(self) -> dict: delete_in_batches(self.api_tokens.all(), pk="key") delete_in_batches(self.schedules.all()) delete_in_batches(self.boundaryalias_set.all()) + delete_in_batches(self.locationalias_set.all()) delete_in_batches(self.templates.all()) # needs to come after deletion of other things as those insert new negative counts diff --git a/temba/orgs/tests/test_org.py b/temba/orgs/tests/test_org.py index c9c8ce9465c..365a387a82c 100644 --- a/temba/orgs/tests/test_org.py +++ b/temba/orgs/tests/test_org.py @@ -189,7 +189,7 @@ def test_country_view(self): self.assertNotContains(response, "Rwanda") def test_default_country(self): - # if location boundary is set and name is valid country, that has priority + # if location is set and name is valid country, that has priority self.org.location = Location.create(osm_id="171496", name="Ecuador", level=0) self.org.timezone = "Africa/Nairobi" self.org.save(update_fields=("country", "timezone")) diff --git a/temba/tests/mailroom.py b/temba/tests/mailroom.py index fd60f81e221..75e36f0cf9c 100644 --- a/temba/tests/mailroom.py +++ b/temba/tests/mailroom.py @@ -810,24 +810,24 @@ def parse_location(org, location_string, level, parent=None): if not org.location_id or not isinstance(location_string, str): return [] - boundary = None + location = None # try it as a path first if it looks possible if level == Location.LEVEL_COUNTRY or Location.PATH_SEPARATOR in location_string: - boundary = parse_location_path(org, location_string) - if boundary: - boundary = [boundary] + location = parse_location_path(org, location_string) + if location: + location = [location] # try to look up it by full name - if not boundary: - boundary = find_boundary_by_name(org, location_string, level, parent) + if not location: + location = find_location_by_name(org, location_string, level, parent) # try removing punctuation and try that - if not boundary: + if not location: bare_name = re.sub(r"\W+", " ", location_string, flags=re.UNICODE).strip() - boundary = find_boundary_by_name(org, bare_name, level, parent) + location = find_location_by_name(org, bare_name, level, parent) - return boundary + return location def parse_location_path(org, location_string): @@ -841,7 +841,7 @@ def parse_location_path(org, location_string): ) -def find_boundary_by_name(org, name, level, parent): +def find_location_by_name(org, name, level, parent): # first check if we have a direct name match if parent: location = parent.children.filter(name__iexact=name, level=level)