implement SiteUpdates: make the user reload on critical code changes

This commit is contained in:
Laura Klünder 2017-12-24 01:55:30 +01:00
parent 8503b2554c
commit cdfef25034
7 changed files with 84 additions and 11 deletions

View file

@ -418,7 +418,9 @@ class UpdatesViewSet(GenericViewSet):
except ValueError:
cache.set('api_updates_fetch_requests', 0, None)
from c3nav.site.models import SiteUpdate
response = Response({
'last_site_update': SiteUpdate.last_update(),
'last_map_update': MapUpdate.current_processed_cache_key(),
'user': get_user_data(request),
})

View file

@ -163,7 +163,7 @@ class MapUpdate(models.Model):
new_update.save()
transaction.on_commit(
lambda: cache.set('mapdata:last_processed_update', new_updates[-1].to_tuple, 300)
lambda: cache.set('mapdata:last_processed_update', new_updates[-1].to_tuple, None)
)
return new_updates
@ -188,7 +188,7 @@ class MapUpdate(models.Model):
if new:
transaction.on_commit(
lambda: cache.set('mapdata:last_update', self.to_tuple, 300)
lambda: cache.set('mapdata:last_update', self.to_tuple, None)
)
if settings.HAS_CELERY:
transaction.on_commit(

View file

@ -8,7 +8,7 @@ class Command(BaseCommand):
result = input('Type YES to create a new site update: ')
if result == 'YES':
from c3nav.mapdata.models import SiteUpdate
from c3nav.site.models import SiteUpdate
SiteUpdate.objects.create()
print('New site update created.')
else:

View file

@ -1,6 +1,8 @@
from contextlib import contextmanager
from django.conf import settings
from django.core.cache import cache
from django.db import models
from django.db import models, transaction
from django.db.models import Q
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
@ -50,3 +52,42 @@ class SiteUpdate(models.Model):
A site update that asks the user to reload the page.
"""
created = models.DateTimeField(auto_now_add=True, verbose_name=_('create'))
class Meta:
verbose_name = _('Site update')
verbose_name_plural = _('Site updates')
default_related_name = 'siteupdates'
get_latest_by = 'created'
@classmethod
@contextmanager
def lock(cls):
with transaction.atomic():
try:
yield cls.objects.select_for_update().get(pk=cls.objects.earliest().pk)
except cls.DoesNotExist:
yield
@classmethod
def last_update(cls):
last_update = cache.get('site:last_site_update', None)
if last_update is not None:
return last_update
with cls.lock():
try:
last_update = cls.objects.latest()
except cls.DoesNotExist:
last_update = None
else:
last_update = last_update.pk
cache.set('site:last_site_update', last_update, None)
return last_update
def save(self, **kwargs):
new = self.pk is None
with transaction.atomic():
super().save(**kwargs)
if new:
transaction.on_commit(
lambda: cache.set('site:last_site_update', self.pk, None)
)

View file

@ -68,6 +68,9 @@ c3nav = {
state = JSON.parse($main.attr('data-state'));
c3nav.embed = $main.is('[data-embed]');
c3nav.last_site_update = JSON.parse($main.attr('data-last-site-update'));
c3nav.new_site_update = false;
history.replaceState(state, window.location.path);
c3nav.load_state(state, true);
c3nav.update_map_locations();
@ -549,10 +552,13 @@ c3nav = {
// console.log('state pushed');
history.pushState(state, '', url);
}
c3nav._maybe_load_site_update(state);
},
_onpopstate: function (e) {
// console.log('state popped');
c3nav.load_state(e.state);
c3nav._maybe_load_site_update(e.state);
},
load_state: function (state, nofly) {
if (state.modal) {
@ -886,22 +892,24 @@ c3nav = {
}
},
open_modal: function (content) {
modal_noclose: false,
open_modal: function (content, no_close) {
c3nav.modal_noclose = no_close;
var $modal = $('#modal');
c3nav._set_modal_content(content);
c3nav._set_modal_content(content, no_close);
if (!$modal.is('.show')) {
c3nav._push_state({modal: true, sidebar: true});
$modal.addClass('show');
}
},
_set_modal_content: function(content) {
_set_modal_content: function(content, no_close) {
$('#modal').toggleClass('loading', !content)
.find('#modal-content')
.html('<button class="button-clear material-icons" id="close-modal">clear</button>')
.html((!no_close) ? '<button class="button-clear material-icons" id="close-modal">clear</button>' :'')
.append(content || '');
},
_modal_click: function(e) {
if (e.target.id === 'modal' || e.target.id === 'close-modal') {
if (!c3nav.modal_noclose && (e.target.id === 'modal' || e.target.id === 'close-modal')) {
history.back();
}
},
@ -1168,8 +1176,26 @@ c3nav = {
},
_fetch_updates_callback: function (data) {
c3nav.schedule_fetch_updates();
if (c3nav.last_site_update !== data.last_site_update) {
c3nav.new_site_update = true;
c3nav.last_site_update = data.last_site_update;
c3nav._maybe_load_site_update(c3nav.state);
}
c3nav._set_user_data(data.user);
},
_maybe_load_site_update: function(state) {
if (c3nav.new_site_update && !state.modal && (!state.routing || !state.origin || !state.destination)) {
c3nav._load_site_update();
}
},
_load_site_update: function() {
$('#modal-content').css({
width: 'auto',
minHeight: 0
});
c3nav.open_modal($('.reload-msg').html(), true);
window.location.reload();
},
_set_user_data: function (data) {
var $user = $('header #user');
$user.find('span').text(data.title);

View file

@ -4,7 +4,7 @@
{% load i18n %}
{% block content %}
<main class="map" data-state="{{ state }}"{% if embed %} data-embed{% endif %}>
<main class="map" data-state="{{ state }}"{% if embed %} data-embed{% endif %} data-last-site-update="{{ last_site_update }}">
<section id="attributions">
{% if not embed %}
{% get_current_language as CURRENT_LANGUAGE %}
@ -37,6 +37,9 @@
<button class="mobileclient-shortcut">{% trans 'create shortcut' %}</button>
</p>
</section>
<section class="reload-msg">
<img src="{% static 'img/loader.gif' %}">
</section>
<section id="sidebar">
<section id="search" class="loading">
<div class="location locationinput empty" id="origin-input">

View file

@ -28,7 +28,7 @@ from c3nav.mapdata.models.locations import LocationRedirect, SpecificLocation
from c3nav.mapdata.utils.locations import get_location_by_slug_for_request, levels_by_short_label_for_request
from c3nav.mapdata.utils.user import get_user_data
from c3nav.mapdata.views import set_tile_access_cookie
from c3nav.site.models import Announcement
from c3nav.site.models import Announcement, SiteUpdate
def check_location(location: Optional[str], request) -> Optional[SpecificLocation]:
@ -120,6 +120,7 @@ def map_index(request, mode=None, slug=None, slug2=None, details=None, options=N
'tile_cache_server': settings.TILE_CACHE_SERVER,
'initial_level': settings.INITIAL_LEVEL,
'initial_bounds': json.dumps(settings.INITIAL_BOUNDS, separators=(',', ':')),
'last_site_update': json.dumps(SiteUpdate.last_update()),
'embed': bool(embed),
}