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

Feature/retrieve mobileunits in division #263

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
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
15 changes: 15 additions & 0 deletions mobility_data/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from rest_framework.exceptions import ParseError
from rest_framework.response import Response

from services.api import resolve_divisions
from services.models import Unit

from ..models import ContentType, GroupType, MobileUnit, MobileUnitGroup
Expand Down Expand Up @@ -176,6 +177,20 @@ def list(self, request):
)
queryset = queryset.filter(Q(**bbox_geometry_filter))

if "division" in filters:
# Divisions can be specified with form:
# division=ocd-division/country:fi/kunta:turku/kaupunginosa:002_ii,
# ocd-division/country:fi/kunta:turku/kaupunginosa:001_i
d_list = filters["division"].lower().split(",")
div_list = resolve_divisions(d_list)

if div_list:
mp = div_list.pop(0).geometry.boundary
for div in div_list:
mp += div.geometry.boundary

queryset = queryset.filter(geometry__within=mp)

for filter in filters:
if filter.startswith("extra__"):
if "type_name" not in filters:
Expand Down
9 changes: 7 additions & 2 deletions mobility_data/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def mobile_units(content_types):
"test_string": "4242",
"test_bool": False,
}
geometry = Point(22.21, 60.3, srid=4326)
geometry = Point(22.22, 60.44, srid=4326)
geometry.transform(settings.DEFAULT_SRID)
mobile_unit = MobileUnit.objects.create(
id="aa6c2903-d36f-4c61-b828-19084fc7a64b",
Expand All @@ -113,6 +113,7 @@ def mobile_units(content_types):
"test_string": "hello",
"test_bool": True,
}
# Not in Turku
mobile_unit = MobileUnit.objects.create(
id="ba6c2903-d36f-4c61-b828-19084fc7a64b",
name="Test2 mobileunit",
Expand Down Expand Up @@ -186,7 +187,11 @@ def administrative_division_type():
@pytest.fixture
def administrative_division(administrative_division_type):
adm_div = AdministrativeDivision.objects.get_or_create(
id=1, name="Turku", origin_id=853, type_id=1
id=1,
name="Turku",
origin_id=853,
type_id=1,
ocd_id="ocd-division/country:fi/kunta:turku",
)
return adm_div

Expand Down
23 changes: 19 additions & 4 deletions mobility_data/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pytest
from django.conf import settings
from django.contrib.gis.geos import Point
from rest_framework.reverse import reverse


Expand Down Expand Up @@ -29,7 +28,15 @@ def test_group_type(api_client, group_type):


@pytest.mark.django_db
def test_mobile_unit(api_client, mobile_units, content_types, unit):
def test_mobile_unit(
api_client,
mobile_units,
content_types,
unit,
administrative_division,
administrative_division_geometry,
administrative_division_type,
):
url = reverse("mobility_data:mobile_units-list")
response = api_client.get(url)
assert response.status_code == 200
Expand All @@ -47,8 +54,9 @@ def test_mobile_unit(api_client, mobile_units, content_types, unit):
assert result["extra"]["test_string"] == "4242"
assert result["extra"]["test_int"] == 4242
assert result["extra"]["test_float"] == 42.42
assert result["geometry"] == Point(
235404.6706163187, 6694437.919005549, srid=settings.DEFAULT_SRID
assert (
result["geometry"]
== f"SRID={settings.DEFAULT_SRID};POINT (237087.7394446331 6709962.367627109)"
)
url = reverse(
"mobility_data:mobile_units-detail",
Expand Down Expand Up @@ -122,6 +130,13 @@ def test_mobile_unit(api_client, mobile_units, content_types, unit):
assert result["geometry"] == "POINT (24.24 62.22)"
assert result["geometry_coords"]["lon"] == 24.24
assert result["geometry_coords"]["lat"] == 62.22
# Test filtering by division
url = (
reverse("mobility_data:mobile_units-list")
+ "?division=ocd-division/country:fi/kunta:turku"
)
response = api_client.get(url)
assert len(response.json()["results"]) == 1


@pytest.mark.django_db
Expand Down
96 changes: 27 additions & 69 deletions services/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,32 @@ def root_service_nodes(services):
)


def resolve_divisions(divisions):
div_list = []
for division_path in divisions:
if division_path.startswith("ocd-division"):
muni_ocd_id = division_path
else:
ocd_id_base = r"[\w0-9~_.-]+"
match_re = r"(%s)/([\w_-]+):(%s)" % (ocd_id_base, ocd_id_base)
m = re.match(match_re, division_path, re.U)
if not m:
raise ParseError("'division' must be of form 'muni/type:id'")

arr = division_path.split("/")
muni_ocd_id = make_muni_ocd_id(arr.pop(0), "/".join(arr))
try:
div = AdministrativeDivision.objects.select_related("geometry").get(
ocd_id=muni_ocd_id
)
except AdministrativeDivision.DoesNotExist:
raise ParseError(
"administrative division with OCD ID '%s' not found" % muni_ocd_id
)
div_list.append(div)
return div_list


class JSONAPISerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super(JSONAPISerializer, self).__init__(*args, **kwargs)
Expand Down Expand Up @@ -916,29 +942,7 @@ def validate_service_node_ids(service_node_ids):
# Divisions can be specified with form:
# division=helsinki/kaupunginosa:kallio,vantaa/äänestysalue:5
d_list = filters["division"].lower().split(",")
div_list = []
for division_path in d_list:
if division_path.startswith("ocd-division"):
muni_ocd_id = division_path
else:
ocd_id_base = r"[\w0-9~_.-]+"
match_re = r"(%s)/([\w_-]+):(%s)" % (ocd_id_base, ocd_id_base)
m = re.match(match_re, division_path, re.U)
if not m:
raise ParseError("'division' must be of form 'muni/type:id'")

arr = division_path.split("/")
muni_ocd_id = make_muni_ocd_id(arr.pop(0), "/".join(arr))
try:
div = AdministrativeDivision.objects.select_related("geometry").get(
ocd_id=muni_ocd_id
)
except AdministrativeDivision.DoesNotExist:
raise ParseError(
"administrative division with OCD ID '%s' not found"
% muni_ocd_id
)
div_list.append(div)
div_list = resolve_divisions(d_list)

if div_list:
mp = div_list.pop(0).geometry.boundary
Expand Down Expand Up @@ -1085,52 +1089,6 @@ def list(self, request, **kwargs):
register_view(UnitViewSet, "unit")


class SearchSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
super(SearchSerializer, self).__init__(*args, **kwargs)
self.serializer_by_model = {}

def _strip_context(self, context, model):
if model == Unit:
key = "unit"
elif model == Service:
key = "service"
else:
key = "service_node"
for spec in ["include", "only"]:
if spec in context:
context[spec] = context[spec].get(key, [])
if "only" in context and context["only"] == []:
context.pop("only")
return context

def get_result_serializer(self, model, instance):
ser = self.serializer_by_model.get(model)
if not ser:
ser_class = serializers_by_model[model]
assert model in serializers_by_model, "Serializer for %s not found" % model
context = self._strip_context(self.context.copy(), model)
ser = ser_class(context=context, many=False)
self.serializer_by_model[model] = ser
# TODO: another way to serialize with new data without
# costly Serializer instantiation
ser.instance = instance
if hasattr(ser, "_data"):
del ser._data
return ser

def to_representation(self, search_result):
if not search_result or not search_result.model:
return None
model = search_result.model
serializer = self.get_result_serializer(model, search_result.object)
data = serializer.data
data["sort_index"] = search_result._sort_index
data["object_type"] = model._meta.model_name
data["score"] = search_result.score
return data


class AccessibilityRuleView(viewsets.ViewSetMixin, generics.ListAPIView):
serializer_class = None

Expand Down