Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update locations 3 #5778

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions temba/api/internal/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from rest_framework import serializers

from temba.locations.models import AdminBoundary
from temba.locations.models import Location
from temba.templates.models import Template, TemplateTranslation
from temba.tickets.models import Shortcut

Expand All @@ -14,7 +14,7 @@ def to_representation(self, instance):

class LocationReadSerializer(serializers.ModelSerializer):
class Meta:
model = AdminBoundary
model = Location
fields = ("osm_id", "name", "path")


Expand Down
2 changes: 1 addition & 1 deletion temba/api/internal/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def test_locations(self):
self.assertPostNotAllowed(endpoint_url)
self.assertDeleteNotAllowed(endpoint_url)

# no country, no results
# no location on org, no results
self.assertGet(endpoint_url + "?level=state", [self.agent], results=[])

self.setUpLocations()
Expand Down
18 changes: 8 additions & 10 deletions temba/api/internal/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.db.models import Prefetch, Q

from temba.channels.models import Channel
from temba.locations.models import AdminBoundary
from temba.locations.models import Location
from temba.notifications.models import Notification
from temba.templates.models import Template, TemplateTranslation
from temba.tickets.models import Shortcut
Expand Down Expand Up @@ -36,16 +36,16 @@ class LocationsEndpoint(ListAPIMixin, BaseEndpoint):
"""

LEVELS = {
"state": AdminBoundary.LEVEL_STATE,
"district": AdminBoundary.LEVEL_DISTRICT,
"ward": AdminBoundary.LEVEL_WARD,
"state": Location.LEVEL_STATE,
"district": Location.LEVEL_DISTRICT,
"ward": Location.LEVEL_WARD,
}

class Pagination(CursorPagination):
ordering = ("name", "id")
offset_cutoff = 100000

model = AdminBoundary
model = Location
serializer_class = serializers.LocationReadSerializer
pagination_class = Pagination

Expand All @@ -54,12 +54,10 @@ def derive_queryset(self):
level = self.LEVELS.get(self.request.query_params.get("level"))
query = self.request.query_params.get("query")

if not org.country or not level:
return AdminBoundary.objects.none()
if not org.location or not level:
return Location.objects.none()

qs = AdminBoundary.objects.filter(
path__startswith=f"{org.country.name} {AdminBoundary.PATH_SEPARATOR}", level=level
)
qs = Location.objects.filter(path__startswith=f"{org.location.name} {Location.PATH_SEPARATOR}", level=level)

if query:
qs = qs.filter(Q(path__icontains=query))
Expand Down
10 changes: 5 additions & 5 deletions temba/api/v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from temba.contacts.models import URN, Contact, ContactField, ContactGroup, ContactNote, ContactURN
from temba.flows.models import Flow, FlowRun, FlowStart
from temba.globals.models import Global
from temba.locations.models import AdminBoundary
from temba.locations.models import Location
from temba.mailroom import modifiers
from temba.msgs.models import Broadcast, Label, Media, Msg, OptIn
from temba.orgs.models import Org, OrgRole, User
Expand Down Expand Up @@ -123,7 +123,7 @@ def run_validation(self, data=serializers.empty):
# ============================================================


class AdminBoundaryReadSerializer(ReadSerializer):
class LocationReadSerializer(ReadSerializer):
parent = serializers.SerializerMethodField()
aliases = serializers.SerializerMethodField()
geometry = serializers.SerializerMethodField()
Expand All @@ -135,13 +135,13 @@ def get_aliases(self, obj):
return [alias.name for alias in obj.aliases.all()]

def get_geometry(self, obj):
if self.context["include_geometry"] and obj.simplified_geometry:
return json.loads(obj.simplified_geometry.geojson)
if self.context["include_geometry"] and obj.geometry:
return obj.geometry
else:
return None

class Meta:
model = AdminBoundary
model = Location
fields = ("osm_id", "name", "parent", "level", "aliases", "geometry")


Expand Down
18 changes: 10 additions & 8 deletions temba/api/v2/tests/test_boundaries.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.contrib.gis.geos import GEOSGeometry
from django.urls import reverse

from temba.locations.models import BoundaryAlias
from temba.locations.models import LocationAlias
from temba.tests import matchers

from . import APITest
Expand All @@ -17,11 +16,14 @@ def test_endpoint(self):

self.setUpLocations()

BoundaryAlias.create(self.org, self.admin, self.state1, "Kigali")
BoundaryAlias.create(self.org, self.admin, self.state2, "East Prov")
BoundaryAlias.create(self.org2, self.admin2, self.state1, "Other Org") # shouldn't be returned
LocationAlias.create(self.org, self.admin, self.state1, "Kigali")
LocationAlias.create(self.org, self.admin, self.state2, "East Prov")
LocationAlias.create(self.org2, self.admin2, self.state1, "Other Org") # shouldn't be returned

self.state1.simplified_geometry = GEOSGeometry("MULTIPOLYGON(((1 1, 1 -1, -1 -1, -1 1, 1 1)))")
self.state1.geometry = {
"type": "MultiPolygon",
"coordinates": [[[[1, 1], [1, -1], [-1, -1], [-1, 1], [1, 1]]]],
}
self.state1.save()

# test without geometry
Expand Down Expand Up @@ -136,7 +138,7 @@ def test_endpoint(self):
)

# if org doesn't have a country, just return no results
self.org.country = None
self.org.save(update_fields=("country",))
self.org.location = None
self.org.save(update_fields=("location",))

self.assertGet(endpoint_url, [self.admin], results=[])
24 changes: 12 additions & 12 deletions temba/api/v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from temba.contacts.models import Contact, ContactField, ContactGroup, ContactNote, ContactURN
from temba.flows.models import Flow, FlowRun, FlowStart, FlowStartCount
from temba.globals.models import Global
from temba.locations.models import AdminBoundary, BoundaryAlias
from temba.locations.models import Location, LocationAlias
from temba.msgs.models import Broadcast, BroadcastMsgCount, Label, LabelCount, Media, Msg, MsgFolder, OptIn
from temba.orgs.models import OrgMembership, User
from temba.orgs.views.mixins import OrgPermsMixin
Expand All @@ -43,7 +43,6 @@
)
from ..views import BaseAPIView, BulkWriteAPIMixin, DeleteAPIMixin, ListAPIMixin, WriteAPIMixin
from .serializers import (
AdminBoundaryReadSerializer,
ArchiveReadSerializer,
BroadcastReadSerializer,
BroadcastWriteSerializer,
Expand All @@ -69,6 +68,7 @@
GlobalWriteSerializer,
LabelReadSerializer,
LabelWriteSerializer,
LocationReadSerializer,
MediaReadSerializer,
MediaWriteSerializer,
MsgBulkActionSerializer,
Expand Down Expand Up @@ -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.**
Expand Down Expand Up @@ -453,19 +453,19 @@ class BoundariesEndpoint(ListAPIMixin, BaseEndpoint):
class Pagination(CursorPagination):
ordering = ("osm_id",)

model = AdminBoundary
serializer_class = AdminBoundaryReadSerializer
model = Location
serializer_class = LocationReadSerializer
pagination_class = Pagination

def derive_queryset(self):
org = self.request.org
if not org.country:
return AdminBoundary.objects.none()
if not org.location:
return Location.objects.none()

queryset = org.country.get_descendants(include_self=True)
queryset = org.location.get_descendants(include_self=True)

queryset = queryset.prefetch_related(
Prefetch("aliases", queryset=BoundaryAlias.objects.filter(org=org).order_by("name"))
Prefetch("aliases", queryset=LocationAlias.objects.filter(org=org).order_by("name"))
)

return queryset.defer(None).select_related("parent")
Expand Down
2 changes: 1 addition & 1 deletion temba/channels/android/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class AndroidTest(TembaTest):
def test_register_unsupported_android(self):
# remove our explicit country so it needs to be derived from channels
self.org.country = None
self.org.location = None
self.org.save()

Channel.objects.all().delete()
Expand Down
4 changes: 2 additions & 2 deletions temba/channels/types/android/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class AndroidTypeTest(TembaTest, CRUDLTestMixin):
@mock_mailroom
def test_claim(self, mr_mocks):
# remove our explicit country so it needs to be derived from channels
self.org.country = None
self.org.location = None
self.org.timezone = "UTC"
self.org.save(update_fields=("country", "timezone"))
self.org.save(update_fields=("location", "timezone"))

Channel.objects.all().delete()

Expand Down
6 changes: 3 additions & 3 deletions temba/contacts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

from temba import mailroom
from temba.channels.models import Channel
from temba.locations.models import AdminBoundary
from temba.locations.models import Location
from temba.mailroom import ContactSpec, modifiers, queue_populate_dynamic_group
from temba.orgs.models import DependencyMixin, Export, ExportType, Org, OrgRole, User
from temba.utils import format_number, on_transaction_commit
Expand Down Expand Up @@ -843,7 +843,7 @@ def get_field_serialized(self, field) -> str:

def get_field_value(self, field: ContactField):
"""
Given the passed in contact field object, returns the value (as a string, decimal, datetime, AdminBoundary)
Given the passed in contact field object, returns the value (as a string, decimal, datetime, Location)
for this contact or None.
"""

Expand All @@ -861,7 +861,7 @@ def get_field_value(self, field: ContactField):
elif field.value_type == ContactField.TYPE_NUMBER:
return Decimal(string_value)
elif field.value_type in [ContactField.TYPE_STATE, ContactField.TYPE_DISTRICT, ContactField.TYPE_WARD]:
return AdminBoundary.get_by_path(self.org, string_value)
return Location.get_by_path(self.org, string_value)

def get_field_display(self, field: ContactField) -> str:
"""
Expand Down
16 changes: 8 additions & 8 deletions temba/contacts/tests/test_contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from temba.channels.models import ChannelEvent
from temba.contacts.models import URN, Contact, ContactField, ContactGroup, ContactURN
from temba.flows.models import Flow
from temba.locations.models import AdminBoundary
from temba.locations.models import Location
from temba.mailroom import modifiers
from temba.msgs.models import Msg, SystemLabel
from temba.orgs.models import Org
Expand Down Expand Up @@ -932,10 +932,10 @@ def test_set_location_fields(self):
not_state_field = self.create_field("not_state", "Not State", value_type=ContactField.TYPE_TEXT)

# add duplicate district in different states
east_province = AdminBoundary.create(osm_id="R005", name="East Province", level=1, parent=self.country)
AdminBoundary.create(osm_id="R004", name="Remera", level=2, parent=east_province)
kigali = AdminBoundary.objects.get(name="Kigali City")
AdminBoundary.create(osm_id="R003", name="Remera", level=2, parent=kigali)
east_province = Location.create(osm_id="R005", name="East Province", level=1, parent=self.country)
Location.create(osm_id="R004", name="Remera", level=2, parent=east_province)
kigali = Location.objects.get(name="Kigali City")
Location.create(osm_id="R003", name="Remera", level=2, parent=kigali)

joe = Contact.objects.get(pk=self.joe.pk)
self.set_contact_field(joe, "district", "Remera")
Expand All @@ -961,9 +961,9 @@ def test_set_location_fields(self):
def test_set_location_ward_fields(self):
self.setUpLocations()

state = AdminBoundary.create(osm_id="3710302", name="Kano", level=1, parent=self.country)
district = AdminBoundary.create(osm_id="3710307", name="Bichi", level=2, parent=state)
AdminBoundary.create(osm_id="3710377", name="Bichi", level=3, parent=district)
state = Location.create(osm_id="3710302", name="Kano", level=1, parent=self.country)
district = Location.create(osm_id="3710307", name="Bichi", level=2, parent=state)
Location.create(osm_id="3710377", name="Bichi", level=3, parent=district)

self.create_field("state", "State", value_type=ContactField.TYPE_STATE)
self.create_field("district", "District", value_type=ContactField.TYPE_DISTRICT)
Expand Down
6 changes: 3 additions & 3 deletions temba/contacts/tests/test_contactcrudl.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from temba.contacts.models import URN, Contact, ContactExport, ContactField
from temba.flows.models import FlowSession, FlowStart
from temba.ivr.models import Call
from temba.locations.models import AdminBoundary
from temba.locations.models import Location
from temba.msgs.models import Msg
from temba.orgs.models import Export, OrgRole
from temba.schedules.models import Schedule
Expand All @@ -32,8 +32,8 @@ class ContactCRUDLTest(CRUDLTestMixin, TembaTest):
def setUp(self):
super().setUp()

self.country = AdminBoundary.create(osm_id="171496", name="Rwanda", level=0)
AdminBoundary.create(osm_id="1708283", name="Kigali", level=1, parent=self.country)
self.location = Location.create(osm_id="171496", name="Rwanda", level=0)
Location.create(osm_id="1708283", name="Kigali", level=1, parent=self.location)

self.create_field("age", "Age", value_type="N", show_in_table=True)
self.create_field("home", "Home", value_type="S", show_in_table=True, priority=10)
Expand Down
2 changes: 1 addition & 1 deletion temba/flows/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ def get_features(self, org) -> list:
features.append("classifier")
if org.get_resthooks():
features.append("resthook")
if org.country_id:
if org.location_id:
features.append("locations")

return features
Expand Down
Loading
Loading