add RouteOptions

This commit is contained in:
Laura Klünder 2017-12-12 23:09:15 +01:00
parent 9fe2bc4533
commit d3dc6ae02e
4 changed files with 195 additions and 0 deletions

View file

@ -7,6 +7,7 @@ from c3nav.mapdata.models.access import AccessPermission
from c3nav.mapdata.utils.locations import visible_locations_for_request from c3nav.mapdata.utils.locations import visible_locations_for_request
from c3nav.routing.exceptions import LocationUnreachable, NoRouteFound, NotYetRoutable from c3nav.routing.exceptions import LocationUnreachable, NoRouteFound, NotYetRoutable
from c3nav.routing.forms import RouteForm from c3nav.routing.forms import RouteForm
from c3nav.routing.models import RouteOptions
from c3nav.routing.router import Router from c3nav.routing.router import Router
@ -45,3 +46,22 @@ class RoutingViewSet(ViewSet):
}, },
'result': route.serialize(locations=visible_locations_for_request(request)), 'result': route.serialize(locations=visible_locations_for_request(request)),
}) })
@list_route(methods=['get', 'post'])
def options(self, request, *args, **kwargs):
params = request.POST if request.method == 'POST' else request.GET
if request.method == 'POST' or 'save' in params:
pass
options = RouteOptions.get_for_request(request)
return Response({
'options': options.data,
'fields': {
name: {
'type': field.widget.input_type,
'label': field.label,
'choices': dict(field.choices),
}
for name, field in options.get_fields().items()},
})

View file

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-12 22:03
from __future__ import unicode_literals
import c3nav.mapdata.fields
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0008_alter_user_username_max_length'),
]
operations = [
migrations.CreateModel(
name='RouteOptions',
fields=[
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='routeoptions', serialize=False, to=settings.AUTH_USER_MODEL)),
('data', c3nav.mapdata.fields.JSONField(default={})),
],
options={
'verbose_name': 'Route options',
'verbose_name_plural': 'Route options',
'default_related_name': 'routeoptions',
},
),
]

View file

143
src/c3nav/routing/models.py Normal file
View file

@ -0,0 +1,143 @@
import threading
from collections import OrderedDict
from django import forms
from django.conf import settings
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _
from c3nav.mapdata.fields import JSONField
from c3nav.mapdata.models import MapUpdate, WayType
class RouteOptions(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
data = JSONField(default={})
class Meta:
verbose_name = _('Route options')
verbose_name_plural = _('Route options')
default_related_name = 'routeoptions'
fields_cached = None
fields_cache_key = None
fields_cache_lock = threading.Lock()
@classmethod
def build_fields(cls):
fields = OrderedDict()
fields['walk_speed'] = forms.ChoiceField(
label=_('Walk speed'),
choices=(('slow', _('slow')), ('default', _('default')), ('fast', _('fast'))),
initial='default'
)
for waytype in WayType.objects.all():
choices = []
choices.append(('allow', _('allow')))
if waytype.up_separate:
choices.append(('avoid_up', _('avoid upwards')))
choices.append(('avoid_down', _('avoid downwards')))
choices.append(('avoid', _('avoid completely')))
else:
choices.append(('avoid', _('avoid')))
fields['waytype_%d' % waytype.pk] = forms.ChoiceField(
label=waytype.title_plural,
choices=tuple(choices),
initial='allow'
)
return fields
@classmethod
def get_fields(cls):
cache_key = MapUpdate.current_cache_key()
if cls.fields_cache_key != cache_key:
with cls.fields_cache_lock:
cls.fields_cache_key = cache_key
cls.fields_cached = cls.build_fields()
return cls.fields_cached
@staticmethod
def get_cache_key(pk):
return 'routing:options:user:%d' % pk
@classmethod
def get_for_user(cls, user):
cache_key = cls.get_cache_key(user.pk)
result = cache.get(cache_key, None)
if result:
return result
try:
result = user.routeoptions
except AttributeError:
result = None
if result:
cache.set(cache_key, result, 900)
return result
@classmethod
def get_for_request(cls, request):
session_options = request.session.get('route_options', None)
user_options = None
if request.user.is_authenticated:
user_options = cls.get_for_user(request.user)
if session_options and not user_options:
user_options = session_options
user_options.user = request.user
user_options.save()
request.session.pop('session_options')
options = cls(request=request)
options.update(user_options or session_options or {}, ignore_errors=True)
return options
def __init__(self, *args, request=None, **kwargs):
super().__init__(*args, **kwargs)
new_data = OrderedDict()
for name, field in self.get_fields().items():
value = self.data.get(name)
if value is None or value not in dict(field.choices):
value = field.initial
new_data[name] = value
self.data = new_data
self.request = request
def __getitem__(self, key):
try:
return self.data[key]
except AttributeError:
return self.get_fields()[key].initial
def update(self, value_dict, ignore_errors=False):
if not value_dict:
return
fields = self.get_fields()
for key, value in value_dict.items():
field = fields.get(key)
if not field:
if ignore_errors:
continue
raise ValidationError(_('Unknown route option: %s') % key)
if value is None or value not in dict(field.choices):
if ignore_errors:
continue
raise ValidationError(_('Invalid value for route option %s.') % key)
self.data[key] = value
def __setitem__(self, key, value):
self.update({key: value})
def save(self, *args, **kwargs):
if self.request is None or self.request.user.is_authenticated:
self.user = self.request.user
super().save(*args, **kwargs)
self.request.session['route_options'] = self