2017-12-16 19:33:13 +01:00
|
|
|
from django.core.exceptions import ValidationError
|
2019-12-24 03:56:35 +01:00
|
|
|
from django.urls import reverse
|
2017-12-11 01:23:20 +01:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2018-11-23 18:12:03 +01:00
|
|
|
from rest_framework.decorators import action
|
2017-11-27 16:40:15 +01:00
|
|
|
from rest_framework.response import Response
|
|
|
|
from rest_framework.viewsets import ViewSet
|
|
|
|
|
2018-12-25 18:43:24 +01:00
|
|
|
from c3nav.mapdata.api import api_stats_clean_location_value
|
2019-12-28 17:28:54 +01:00
|
|
|
from c3nav.mapdata.forms import PositionAPIUpdateForm
|
2017-11-28 00:41:34 +01:00
|
|
|
from c3nav.mapdata.models.access import AccessPermission
|
2019-12-27 23:27:50 +01:00
|
|
|
from c3nav.mapdata.models.locations import Position
|
2018-12-25 18:43:24 +01:00
|
|
|
from c3nav.mapdata.utils.cache.stats import increment_cache_key
|
2017-12-26 00:16:43 +01:00
|
|
|
from c3nav.mapdata.utils.locations import visible_locations_for_request
|
2017-12-11 01:23:20 +01:00
|
|
|
from c3nav.routing.exceptions import LocationUnreachable, NoRouteFound, NotYetRoutable
|
2017-11-27 16:40:15 +01:00
|
|
|
from c3nav.routing.forms import RouteForm
|
2017-12-25 23:56:12 +01:00
|
|
|
from c3nav.routing.locator import Locator
|
2017-12-12 23:09:15 +01:00
|
|
|
from c3nav.routing.models import RouteOptions
|
2017-11-27 16:40:15 +01:00
|
|
|
from c3nav.routing.router import Router
|
|
|
|
|
|
|
|
|
|
|
|
class RoutingViewSet(ViewSet):
|
2017-12-16 19:33:13 +01:00
|
|
|
"""
|
|
|
|
/route/ Get routes.
|
|
|
|
/options/ Get or set route options.
|
2017-12-24 04:37:05 +01:00
|
|
|
/locate/ Wifi locate.
|
2019-12-28 18:29:26 +01:00
|
|
|
|
|
|
|
How to use the /locate/ endpoint:
|
|
|
|
POST visible wifi stations as JSON data like this:
|
|
|
|
{
|
|
|
|
"stations": [
|
|
|
|
{
|
|
|
|
"bssid": "11:22:33:44:55:66",
|
|
|
|
"ssid": "36C3",
|
|
|
|
"level": -55,
|
|
|
|
"frequency": 5500
|
|
|
|
},
|
|
|
|
...
|
|
|
|
]
|
|
|
|
}
|
2017-12-16 19:33:13 +01:00
|
|
|
"""
|
2018-11-23 18:12:03 +01:00
|
|
|
@action(detail=False, methods=['get', 'post'])
|
2017-11-27 16:40:15 +01:00
|
|
|
def route(self, request, *args, **kwargs):
|
|
|
|
params = request.POST if request.method == 'POST' else request.GET
|
|
|
|
form = RouteForm(params, request=request)
|
|
|
|
|
|
|
|
if not form.is_valid():
|
|
|
|
return Response({
|
|
|
|
'errors': form.errors,
|
2017-12-16 19:33:13 +01:00
|
|
|
}, status=400)
|
|
|
|
|
|
|
|
options = RouteOptions.get_for_request(request)
|
|
|
|
try:
|
|
|
|
options.update(params, ignore_unknown=True)
|
|
|
|
except ValidationError as e:
|
|
|
|
return Response({
|
|
|
|
'errors': (str(e), ),
|
|
|
|
}, status=400)
|
2017-11-27 16:40:15 +01:00
|
|
|
|
2017-12-11 01:23:20 +01:00
|
|
|
try:
|
|
|
|
route = Router.load().get_route(origin=form.cleaned_data['origin'],
|
|
|
|
destination=form.cleaned_data['destination'],
|
2017-12-16 22:14:00 +01:00
|
|
|
permissions=AccessPermission.get_for_request(request),
|
|
|
|
options=options)
|
2017-12-11 01:23:20 +01:00
|
|
|
except NotYetRoutable:
|
|
|
|
return Response({
|
|
|
|
'error': _('Not yet routable, try again shortly.'),
|
|
|
|
})
|
|
|
|
except LocationUnreachable:
|
|
|
|
return Response({
|
|
|
|
'error': _('Unreachable location.'),
|
|
|
|
})
|
|
|
|
except NoRouteFound:
|
|
|
|
return Response({
|
|
|
|
'error': _('No route found.'),
|
|
|
|
})
|
2017-11-27 16:40:15 +01:00
|
|
|
|
2018-12-25 19:31:24 +01:00
|
|
|
origin_values = api_stats_clean_location_value(form.cleaned_data['origin'].pk)
|
|
|
|
destination_values = api_stats_clean_location_value(form.cleaned_data['destination'].pk)
|
|
|
|
increment_cache_key('apistats__route')
|
|
|
|
for origin_value in origin_values:
|
|
|
|
for destination_value in destination_values:
|
|
|
|
increment_cache_key('apistats__route_tuple_%s_%s' % (origin_value, destination_value))
|
|
|
|
for value in origin_values:
|
|
|
|
increment_cache_key('apistats__route_origin_%s' % value)
|
|
|
|
for value in destination_values:
|
|
|
|
increment_cache_key('apistats__route_destination_%s' % value)
|
2018-12-25 18:43:24 +01:00
|
|
|
|
2017-11-28 12:32:46 +01:00
|
|
|
return Response({
|
|
|
|
'request': {
|
2019-12-27 23:27:50 +01:00
|
|
|
'origin': self.get_request_pk(form.cleaned_data['origin']),
|
|
|
|
'destination': self.get_request_pk(form.cleaned_data['destination']),
|
2017-11-28 12:32:46 +01:00
|
|
|
},
|
2017-12-16 19:33:13 +01:00
|
|
|
'options': options.serialize(),
|
2019-12-25 01:34:12 +01:00
|
|
|
'report_issue_url': reverse('site.report_create', kwargs={
|
2019-12-24 03:56:35 +01:00
|
|
|
'origin': request.POST['origin'],
|
|
|
|
'destination': request.POST['destination'],
|
|
|
|
'options': options.serialize_string()
|
|
|
|
}),
|
2017-11-28 12:32:46 +01:00
|
|
|
'result': route.serialize(locations=visible_locations_for_request(request)),
|
|
|
|
})
|
2017-12-12 23:09:15 +01:00
|
|
|
|
2019-12-27 23:27:50 +01:00
|
|
|
def get_request_pk(self, location):
|
|
|
|
return location.slug if isinstance(location, Position) else location.pk
|
|
|
|
|
2018-11-23 18:12:03 +01:00
|
|
|
@action(detail=False, methods=['get', 'post'])
|
2017-12-12 23:09:15 +01:00
|
|
|
def options(self, request, *args, **kwargs):
|
2017-12-16 21:22:43 +01:00
|
|
|
options = RouteOptions.get_for_request(request)
|
2017-12-12 23:09:15 +01:00
|
|
|
|
2017-12-16 21:22:43 +01:00
|
|
|
if request.method == 'POST':
|
|
|
|
try:
|
|
|
|
options.update(request.POST, ignore_unknown=True)
|
|
|
|
except ValidationError as e:
|
|
|
|
return Response({
|
|
|
|
'errors': (str(e),),
|
|
|
|
}, status=400)
|
|
|
|
options.save()
|
2017-12-12 23:09:15 +01:00
|
|
|
|
2017-12-16 19:33:13 +01:00
|
|
|
return Response(options.serialize())
|
2017-12-24 04:37:05 +01:00
|
|
|
|
2018-11-23 18:12:03 +01:00
|
|
|
@action(detail=False, methods=('POST', ))
|
2017-12-24 04:37:05 +01:00
|
|
|
def locate(self, request, *args, **kwargs):
|
2019-12-28 17:47:20 +01:00
|
|
|
if isinstance(request.data, list):
|
|
|
|
stations_data = request.data
|
|
|
|
data = {}
|
|
|
|
else:
|
|
|
|
data = request.data
|
|
|
|
stations_data = data['stations']
|
|
|
|
|
2017-12-25 23:56:12 +01:00
|
|
|
try:
|
2019-12-28 17:47:20 +01:00
|
|
|
location = Locator.load().locate(stations_data, permissions=AccessPermission.get_for_request(request))
|
2018-12-25 20:12:24 +01:00
|
|
|
if location is not None:
|
|
|
|
increment_cache_key('apistats__locate__%s' % location.pk)
|
2017-12-25 23:56:12 +01:00
|
|
|
except ValidationError:
|
|
|
|
return Response({
|
|
|
|
'errors': (_('Invalid scan data.'),),
|
|
|
|
}, status=400)
|
|
|
|
|
2019-12-28 17:47:20 +01:00
|
|
|
if 'set_position' in data:
|
|
|
|
set_position = data['set_position']
|
2019-12-28 17:28:54 +01:00
|
|
|
if not set_position.startswith('p:'):
|
|
|
|
return Response({
|
2019-12-28 19:47:42 +01:00
|
|
|
'errors': (_('Invalid set_position.'),),
|
2019-12-28 17:28:54 +01:00
|
|
|
}, status=400)
|
|
|
|
|
|
|
|
try:
|
|
|
|
position = Position.objects.get(secret=set_position[2:])
|
|
|
|
except Position.DoesNotExist:
|
|
|
|
return Response({
|
2019-12-28 19:47:42 +01:00
|
|
|
'errors': (_('Invalid set_position.'),),
|
2019-12-28 17:28:54 +01:00
|
|
|
}, status=400)
|
|
|
|
|
2019-12-28 17:47:20 +01:00
|
|
|
form_data = {
|
|
|
|
**data,
|
2019-12-28 17:28:54 +01:00
|
|
|
'coordinates_id': None if location is None else location.pk,
|
|
|
|
}
|
|
|
|
|
2019-12-28 17:47:20 +01:00
|
|
|
form = PositionAPIUpdateForm(instance=position, data=form_data, request=request)
|
2019-12-28 17:28:54 +01:00
|
|
|
|
|
|
|
if not form.is_valid():
|
|
|
|
return Response({
|
|
|
|
'errors': form.errors,
|
|
|
|
}, status=400)
|
|
|
|
|
|
|
|
form.save()
|
|
|
|
|
2017-12-26 01:51:37 +01:00
|
|
|
return Response({'location': None if location is None else location.serialize(simple_geometry=True)})
|