team-3/src/c3nav/routing/models.py

190 lines
6 KiB
Python
Raw Normal View History

2017-12-12 23:09:15 +01:00
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['mode'] = forms.ChoiceField(
label=_('Routing mode'),
choices=(('fastest', _('fastest')), ('shortest', _('shortest'))),
initial='fastest'
)
2017-12-12 23:09:15 +01:00
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)
2017-12-24 21:13:40 +01:00
if session_options is not None:
session_options = cls(request=request)
session_options.update(session_options, ignore_errors=True)
2017-12-12 23:09:15 +01:00
user_options = None
if request.user.is_authenticated:
user_options = cls.get_for_user(request.user)
2017-12-26 22:52:24 +01:00
2017-12-24 21:20:53 +01:00
if user_options is not None:
2017-12-26 22:52:24 +01:00
user_options.request = request
2017-12-24 21:20:53 +01:00
user_options.clean_data()
2017-12-26 22:52:24 +01:00
elif session_options:
user_options = session_options
user_options.user = request.user
user_options.save()
request.session.pop('session_options')
2017-12-12 23:09:15 +01:00
2017-12-24 21:13:40 +01:00
return user_options or session_options or cls(request=request)
2017-12-12 23:09:15 +01:00
2017-12-24 21:13:40 +01:00
def clean_data(self):
2017-12-12 23:09:15 +01:00
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
2017-12-24 21:13:40 +01:00
def __init__(self, *args, request=None, **kwargs):
super().__init__(*args, **kwargs)
self.clean_data()
2017-12-12 23:09:15 +01:00
self.request = request
def __getitem__(self, key):
try:
return self.data[key]
except AttributeError:
return self.get_fields()[key].initial
2017-12-16 19:33:13 +01:00
def update(self, value_dict, ignore_errors=False, ignore_unknown=False):
2017-12-12 23:09:15 +01:00
if not value_dict:
return
2017-12-16 21:22:43 +01:00
if isinstance(value_dict, RouteOptions):
value_dict = value_dict.data
2017-12-12 23:09:15 +01:00
fields = self.get_fields()
for key, value in value_dict.items():
field = fields.get(key)
if not field:
2017-12-16 19:33:13 +01:00
if ignore_errors or ignore_unknown:
2017-12-12 23:09:15 +01:00
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})
@property
def walk_factor(self):
return {'slow': 0.8, 'default': 1, 'fast': 1.2}[self['walk_speed']]
2017-12-16 22:14:00 +01:00
def get(self, key, default):
try:
return self[key]
except AttributeError:
return default
2017-12-16 19:33:13 +01:00
def serialize(self):
return [
{
'name': name,
'type': field.widget.input_type,
'label': field.label,
'choices': [
{
'name': choice_name,
'title': choice_title,
}
for choice_name, choice_title in field.choices
],
'value': self[name],
}
for name, field in self.get_fields().items()
]
2017-12-12 23:09:15 +01:00
def save(self, *args, **kwargs):
if self.request is None or self.request.user.is_authenticated:
self.user = self.request.user
2017-12-16 21:22:43 +01:00
return super().save(*args, **kwargs)
2017-12-12 23:09:15 +01:00
2017-12-24 21:13:40 +01:00
self.request.session['route_options'] = self.data
2017-12-17 01:34:24 +01:00
def items(self):
yield from self.data.items()