team-3/src/c3nav/mapdata/views.py

144 lines
4.9 KiB
Python
Raw Normal View History

import hashlib
2017-10-20 16:47:53 +02:00
import os
2017-10-29 11:32:44 +01:00
from itertools import chain
2017-10-20 16:47:53 +02:00
from django.conf import settings
2017-10-24 18:12:46 +02:00
from django.core.cache import cache
2017-10-24 13:59:42 +02:00
from django.core.exceptions import PermissionDenied
from django.core.signing import b64_encode
2017-10-20 16:21:25 +02:00
from django.http import Http404, HttpResponse, HttpResponseNotModified
2017-10-24 13:59:42 +02:00
from django.shortcuts import get_object_or_404
2017-10-24 21:40:10 +02:00
from django.views.decorators.http import etag
2017-10-10 14:39:11 +02:00
from shapely.geometry import box
2017-10-24 13:59:42 +02:00
from c3nav.mapdata.cache import MapHistory
2017-10-24 21:03:56 +02:00
from c3nav.mapdata.middleware import no_language
2017-10-20 16:21:25 +02:00
from c3nav.mapdata.models import Level, MapUpdate, Source
2017-11-06 11:18:45 +01:00
from c3nav.mapdata.render import MapRenderer, get_render_level_ids, get_tile_access_cookie, set_tile_access_cookie
from c3nav.mapdata.render.engines import ImageRenderEngine
2017-10-10 14:39:11 +02:00
2017-10-24 21:03:56 +02:00
@no_language()
2017-11-09 17:42:19 +01:00
def tile(request, level, zoom, x, y):
2017-10-10 14:39:11 +02:00
zoom = int(zoom)
if not (0 <= zoom <= 10):
raise Http404
2017-10-24 18:12:46 +02:00
# calculate bounds
2017-10-10 14:39:11 +02:00
x, y = int(x), int(y)
size = 256/2**zoom
minx = size * x
miny = size * (-y-1)
2017-10-10 14:39:11 +02:00
maxx = minx + size
maxy = miny + size
# add one pixel so tiles can overlap to avoid rendering bugs in chrome or webkit
maxx += size / 256
miny -= size / 256
2017-10-24 18:12:46 +02:00
# error 404 if tiles is out of bounds
bounds = Source.max_bounds()
2017-10-29 11:32:44 +01:00
if not box(*chain(*bounds)).intersects(box(minx, miny, maxx, maxy)):
2017-10-10 14:39:11 +02:00
raise Http404
2017-10-24 18:12:46 +02:00
# is this a valid level?
cache_key = MapUpdate.current_cache_key()
level = int(level)
if level not in get_render_level_ids(cache_key):
raise Http404
2017-10-24 23:41:53 +02:00
# decode access permissions
access_permissions = get_tile_access_cookie(request)
2017-10-24 18:12:46 +02:00
# init renderer
2017-11-06 11:18:45 +01:00
renderer = MapRenderer(level, minx, miny, maxx, maxy, scale=2 ** zoom, access_permissions=access_permissions)
2017-10-24 18:12:46 +02:00
tile_cache_key = renderer.cache_key
update_cache_key = renderer.update_cache_key
2017-10-20 16:21:25 +02:00
2017-10-24 18:12:46 +02:00
# check browser cache
2017-10-24 21:39:45 +02:00
etag = '"'+b64_encode(hashlib.sha256(
('%d-%d-%d-%d:%s:%s' % (level, zoom, x, y, tile_cache_key, settings.SECRET_TILE_KEY)).encode()
2017-10-24 23:24:32 +02:00
).digest()).decode()+'"'
2017-10-20 16:21:25 +02:00
if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
if if_none_match == etag:
return HttpResponseNotModified()
2017-10-24 18:12:46 +02:00
data = None
tile_dirname, last_update_filename, tile_filename, tile_cache_update_cache_key = '', '', '', ''
2017-10-20 16:47:53 +02:00
2017-10-24 18:12:46 +02:00
# get tile cache last update
if settings.CACHE_TILES:
tile_dirname = os.path.sep.join((settings.TILES_ROOT, str(level), str(zoom), str(x), str(y)))
last_update_filename = os.path.join(tile_dirname, 'last_update')
2017-11-09 17:42:19 +01:00
tile_filename = os.path.join(tile_dirname, renderer.access_cache_key+'.png')
2017-10-24 18:12:46 +02:00
# get tile cache last update
tile_cache_update_cache_key = 'mapdata:tile-cache-update:%d-%d-%d-%d' % (level, zoom, x, y)
tile_cache_update = cache.get(tile_cache_update_cache_key, None)
if tile_cache_update is None:
try:
with open(last_update_filename) as f:
tile_cache_update = f.read()
except FileNotFoundError:
pass
if tile_cache_update != update_cache_key:
os.system('rm -rf '+os.path.join(tile_dirname, '*'))
else:
try:
with open(tile_filename, 'rb') as f:
data = f.read()
except FileNotFoundError:
pass
2017-10-20 16:47:53 +02:00
2017-10-24 18:12:46 +02:00
if data is None:
2017-11-09 17:42:19 +01:00
image = renderer.render(ImageRenderEngine)
data = image.render()
2017-10-20 16:47:53 +02:00
if settings.CACHE_TILES:
2017-10-24 18:12:46 +02:00
os.makedirs(tile_dirname, exist_ok=True)
2017-11-09 17:42:19 +01:00
with open(tile_filename, 'wb') as f:
2017-10-20 16:47:53 +02:00
f.write(data)
2017-10-24 18:12:46 +02:00
with open(last_update_filename, 'w') as f:
f.write(update_cache_key)
cache.get(tile_cache_update_cache_key, update_cache_key, 60)
2017-10-10 14:39:11 +02:00
2017-11-09 17:42:19 +01:00
response = HttpResponse(data, 'image/png')
2017-10-20 16:21:25 +02:00
response['ETag'] = etag
response['Cache-Control'] = 'no-cache'
2017-10-24 23:41:53 +02:00
response['Vary'] = 'Cookie'
response['X-Access-Restrictions'] = ', '.join(str(s) for s in renderer.unlocked_access_restrictions) or '0'
2017-10-20 16:21:25 +02:00
2017-10-10 14:39:11 +02:00
return response
2017-10-24 13:59:42 +02:00
2017-10-24 23:49:02 +02:00
@no_language()
def tile_access(request):
response = HttpResponse(content_type='text/plain')
set_tile_access_cookie(request, response)
response['Cache-Control'] = 'no-cache'
return response
2017-10-24 23:48:53 +02:00
@etag(lambda *args, **kwargs: MapUpdate.current_cache_key())
2017-10-24 21:06:42 +02:00
@no_language()
2017-10-24 13:59:42 +02:00
def history(request, level, mode, format):
if not request.user.is_superuser:
raise PermissionDenied
level = get_object_or_404(Level, pk=level)
2017-10-24 15:23:41 +02:00
if mode == 'render' and level.on_top_of_id is not None:
2017-10-24 13:59:42 +02:00
raise Http404
2017-10-24 14:46:35 +02:00
history = MapHistory.open_level(level.pk, mode)
2017-10-24 13:59:42 +02:00
if format == 'png':
response = HttpResponse(content_type='image/png')
history.to_image().save(response, format='PNG')
elif format == 'data':
response = HttpResponse(content_type='application/octet-stream')
history.write(response)
else:
raise ValueError
2017-10-24 21:40:10 +02:00
response['Cache-Control'] = 'no-cache'
2017-10-24 13:59:42 +02:00
return response