team-3/src/c3nav/mapdata/models/update.py

119 lines
3.9 KiB
Python
Raw Normal View History

2017-11-10 16:32:58 +01:00
import pickle
from contextlib import contextmanager
2017-07-05 22:04:22 +02:00
from django.conf import settings
from django.core.cache import cache
from django.db import models, transaction
2017-07-05 23:38:47 +02:00
from django.utils.http import int_to_base36
from django.utils.timezone import make_naive
2017-07-05 22:04:22 +02:00
from django.utils.translation import ugettext_lazy as _
from c3nav.mapdata.tasks import process_map_updates
2017-07-05 22:04:22 +02:00
class MapUpdate(models.Model):
"""
A map update. created whenever mapdata is changed.
"""
datetime = models.DateTimeField(auto_now_add=True, db_index=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.PROTECT)
type = models.CharField(max_length=32)
2017-11-10 16:32:58 +01:00
processed = models.BooleanField(default=False)
changed_geometries = models.BinaryField(null=True)
2017-07-05 22:04:22 +02:00
class Meta:
verbose_name = _('Map update')
verbose_name_plural = _('Map updates')
default_related_name = 'mapupdates'
get_latest_by = 'datetime'
2017-11-10 16:32:58 +01:00
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.was_processed = self.processed
@classmethod
def last_update(cls):
last_update = cache.get('mapdata:last_update', None)
if last_update is not None:
return last_update
with cls.lock():
2017-07-05 23:38:47 +02:00
last_update = cls.objects.latest()
2017-11-10 16:32:58 +01:00
result = last_update.to_tuple
cache.set('mapdata:last_update', result, 900)
return result
2017-11-10 16:32:58 +01:00
@classmethod
def last_processed_update(cls):
last_processed_update = cache.get('mapdata:last_processed_update', None)
if last_processed_update is not None:
return last_processed_update
with cls.lock():
last_processed_update = cls.objects.filter(processed=True).latest()
result = last_processed_update.to_tuple
cache.set('mapdata:last_processed_update', result, 900)
return result
@property
def to_tuple(self):
return self.pk, int(make_naive(self.datetime).timestamp())
2017-07-05 23:38:47 +02:00
2017-10-23 22:49:45 +02:00
@property
def cache_key(self):
2017-10-24 18:12:46 +02:00
return self.build_cache_key(self.pk, int(make_naive(self.datetime).timestamp()))
2017-10-23 22:49:45 +02:00
2017-07-05 23:38:47 +02:00
@classmethod
2017-10-23 22:49:45 +02:00
def current_cache_key(cls):
2017-10-24 18:12:46 +02:00
return cls.build_cache_key(*cls.last_update())
2017-11-10 16:32:58 +01:00
@classmethod
def current_processed_cache_key(cls):
return cls.build_cache_key(*cls.last_processed_update())
2017-10-24 18:12:46 +02:00
@staticmethod
def build_cache_key(pk, timestamp):
return int_to_base36(pk)+'_'+int_to_base36(timestamp)
@classmethod
@contextmanager
def lock(cls):
with transaction.atomic():
yield cls.objects.select_for_update().earliest()
2017-11-10 16:32:58 +01:00
@classmethod
def process_updates(cls):
with cls.lock():
new_updates = tuple(cls.objects.filter(processed=False))
if not new_updates:
return ()
2017-11-10 17:58:39 +01:00
last_processed_update = cls.objects.filter(processed=True).latest().to_tuple
for new_update in new_updates:
pickle.loads(new_update.changed_geometries).save(last_processed_update, new_update.to_tuple)
new_update.processed = True
new_update.save()
2017-11-10 16:32:58 +01:00
from c3nav.mapdata.models import AltitudeArea
AltitudeArea.recalculate()
2017-10-24 00:11:02 +02:00
2017-11-10 16:32:58 +01:00
from c3nav.mapdata.render.data import LevelRenderData
LevelRenderData.rebuild()
2017-10-24 00:11:02 +02:00
2017-11-10 16:32:58 +01:00
cache.set('mapdata:last_processed_update', new_updates[-1].to_tuple, 900)
return new_updates
def save(self, **kwargs):
new = self.pk is None
if not new and (self.was_processed or not self.processed):
2017-11-10 16:32:58 +01:00
raise TypeError
2017-10-24 00:11:02 +02:00
from c3nav.mapdata.cache import changed_geometries
2017-11-10 16:32:58 +01:00
self.changed_geometries = pickle.dumps(changed_geometries)
2017-10-24 00:11:02 +02:00
2017-11-10 16:32:58 +01:00
super().save(**kwargs)
cache.set('mapdata:last_update', self.to_tuple, 900)
if new and settings.HAS_CELERY:
2017-11-10 18:30:40 +01:00
process_map_updates.delay()