refactor api views inter their respecting apps
This commit is contained in:
parent
b69dff708e
commit
02cafee6be
12 changed files with 112 additions and 117 deletions
|
@ -1,26 +0,0 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from shapely.geometry import mapping, shape
|
||||
|
||||
from ..mapdata.utils import sort_geojson
|
||||
|
||||
|
||||
class GeometryField(serializers.DictField):
|
||||
"""
|
||||
shapely geometry objects serialized using GeoJSON
|
||||
"""
|
||||
default_error_messages = {
|
||||
'invalid': _('Invalid GeoJSON.')
|
||||
}
|
||||
|
||||
def to_representation(self, obj):
|
||||
geojson = sort_geojson(mapping(obj))
|
||||
return super().to_representation(geojson)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
geojson = super().to_internal_value(data)
|
||||
try:
|
||||
return shape(geojson)
|
||||
except:
|
||||
raise ValidationError(_('Invalid GeoJSON.'))
|
|
@ -1,26 +0,0 @@
|
|||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.permissions import BasePermission
|
||||
|
||||
from ..mapdata.models import Source
|
||||
|
||||
|
||||
def get_unlocked_packages(request):
|
||||
return set(settings.PUBLIC_PACKAGES) | set(request.session.get('unlocked_packages', ()))
|
||||
|
||||
|
||||
def can_access_package(request, package):
|
||||
return settings.DEBUG or package.name in get_unlocked_packages(request)
|
||||
|
||||
|
||||
def filter_source_queryset(request, queryset):
|
||||
return queryset if settings.DEBUG else queryset.filter(package__name__in=get_unlocked_packages(request))
|
||||
|
||||
|
||||
class LockedMapFeatures(BasePermission):
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if isinstance(obj, Source):
|
||||
if not can_access_package(request, obj.package):
|
||||
raise PermissionDenied(_('This Source belongs to a package you don\'t have access to.'))
|
||||
return True
|
|
@ -1,63 +0,0 @@
|
|||
from django.conf import settings
|
||||
from rest_framework import serializers
|
||||
|
||||
from ..editor.hosters import get_hoster_for_package
|
||||
from ..mapdata.models import Feature, Level, Package, Source
|
||||
from .fields import GeometryField
|
||||
from .permissions import can_access_package
|
||||
|
||||
|
||||
class LevelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Level
|
||||
fields = ('name', 'altitude', 'package')
|
||||
|
||||
|
||||
class PackageSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Package
|
||||
fields = ('name', 'home_repo', 'commit_id', 'depends', 'bounds')
|
||||
|
||||
def to_representation(self, obj):
|
||||
result = super().to_representation(obj)
|
||||
result['public'] = obj.name in settings.PUBLIC_PACKAGES
|
||||
hoster = get_hoster_for_package(obj)
|
||||
if 'request' in self.context:
|
||||
result['access_granted'] = can_access_package(self.context['request'], obj)
|
||||
if hoster is not None:
|
||||
result['hoster'] = hoster.name
|
||||
return result
|
||||
|
||||
|
||||
class SourceSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Source
|
||||
fields = ('name', 'package', 'bounds')
|
||||
|
||||
|
||||
class FeatureTypeSerializer(serializers.Serializer):
|
||||
name = serializers.CharField()
|
||||
title = serializers.CharField()
|
||||
title_plural = serializers.CharField()
|
||||
geomtype = serializers.CharField()
|
||||
color = serializers.CharField()
|
||||
|
||||
|
||||
class FeatureSerializer(serializers.ModelSerializer):
|
||||
geometry = GeometryField()
|
||||
|
||||
class Meta:
|
||||
model = Feature
|
||||
fields = ('name', 'package', 'feature_type', 'geometry')
|
||||
|
||||
|
||||
class HosterSerializer(serializers.Serializer):
|
||||
name = serializers.CharField()
|
||||
base_url = serializers.CharField()
|
||||
|
||||
def to_representation(self, obj):
|
||||
result = super().to_representation(obj)
|
||||
result['packages'] = tuple(obj.get_packages().values_list('name', flat=True))
|
||||
if 'request' in self.context:
|
||||
result['signed_in'] = obj.is_access_granted(self.context['request'])
|
||||
return result
|
|
@ -1,16 +1,16 @@
|
|||
from django.conf.urls import include, url
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from .views import editor as editor_views
|
||||
from .views import mapdata as mapdata_views
|
||||
from ..editor import api as editor_api
|
||||
from ..mapdata import api as mapdata_api
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'levels', mapdata_views.LevelViewSet)
|
||||
router.register(r'packages', mapdata_views.PackageViewSet)
|
||||
router.register(r'sources', mapdata_views.SourceViewSet)
|
||||
router.register(r'featuretypes', mapdata_views.FeatureTypeViewSet, base_name='featuretype')
|
||||
router.register(r'features', editor_views.FeatureViewSet)
|
||||
router.register(r'hosters', editor_views.HosterViewSet, base_name='hoster')
|
||||
router.register(r'levels', mapdata_api.LevelViewSet)
|
||||
router.register(r'packages', mapdata_api.PackageViewSet)
|
||||
router.register(r'sources', mapdata_api.SourceViewSet)
|
||||
router.register(r'featuretypes', mapdata_api.FeatureTypeViewSet, base_name='featuretype')
|
||||
router.register(r'features', mapdata_api.FeatureViewSet)
|
||||
router.register(r'hosters', editor_api.HosterViewSet, base_name='hoster')
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import base64
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.template.response import SimpleTemplateResponse
|
||||
from django.utils.cache import patch_vary_headers
|
||||
|
||||
from ..permissions import get_unlocked_packages
|
||||
|
||||
|
||||
class CachedViewSetMixin:
|
||||
def get_cache_key(self, request):
|
||||
cache_key = ('api__' + ('OPTIONS' if request.method == 'OPTIONS' else 'GET') + '_' +
|
||||
base64.b64encode(self.get_cache_params(request).encode()).decode() + '_' +
|
||||
request.path + '?' + request.META['QUERY_STRING'])
|
||||
return cache_key
|
||||
|
||||
def get_cache_params(self, request):
|
||||
return request.META.get('HTTP_ACCEPT', '')
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
do_cache = request.method in ('GET', 'HEAD', 'OPTIONS')
|
||||
if do_cache:
|
||||
cache_key = self.get_cache_key(request)
|
||||
if cache_key in cache:
|
||||
return cache.get(cache_key)
|
||||
response = super().dispatch(request, *args, **kwargs)
|
||||
patch_vary_headers(response, ['Cookie'])
|
||||
if do_cache:
|
||||
if isinstance(response, SimpleTemplateResponse):
|
||||
response.render()
|
||||
cache.set(cache_key, response, 60)
|
||||
return response
|
||||
|
||||
@property
|
||||
def default_response_headers(self):
|
||||
headers = super().default_response_headers
|
||||
headers['Vary'] += ', Cookie'
|
||||
return headers
|
||||
|
||||
|
||||
class AccessCachedViewSetMixin(CachedViewSetMixin):
|
||||
def get_cache_params(self, request):
|
||||
return super().get_cache_params(request)+'___'+'___'.join(get_unlocked_packages(request))
|
|
@ -1,31 +0,0 @@
|
|||
from django.http import Http404
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet, ViewSet
|
||||
|
||||
from ...editor.hosters import hosters
|
||||
from ...mapdata.models import Feature
|
||||
from ..serializers import FeatureSerializer, HosterSerializer
|
||||
|
||||
|
||||
class HosterViewSet(ViewSet):
|
||||
"""
|
||||
Get Package Hosters
|
||||
"""
|
||||
def list(self, request, version=None):
|
||||
serializer = HosterSerializer(hosters.values(), many=True, context={'request': request})
|
||||
return Response(serializer.data)
|
||||
|
||||
def retrieve(self, request, pk=None, version=None):
|
||||
if pk not in hosters:
|
||||
raise Http404
|
||||
serializer = HosterSerializer(hosters[pk], context={'request': request})
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class FeatureViewSet(ModelViewSet):
|
||||
"""
|
||||
Get all Map Features including ones that are only part of the current session
|
||||
"""
|
||||
queryset = Feature.objects.all()
|
||||
serializer_class = FeatureSerializer
|
||||
lookup_value_regex = '[^/]+'
|
|
@ -1,81 +0,0 @@
|
|||
import mimetypes
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files import File
|
||||
from django.http import Http404, HttpResponse
|
||||
from rest_framework.decorators import detail_route
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
|
||||
|
||||
from ...mapdata.models import FEATURE_TYPES, Level, Package, Source
|
||||
from ..permissions import filter_source_queryset
|
||||
from ..serializers import FeatureTypeSerializer, LevelSerializer, PackageSerializer, SourceSerializer
|
||||
from .cache import AccessCachedViewSetMixin, CachedViewSetMixin
|
||||
|
||||
|
||||
class LevelViewSet(CachedViewSetMixin, ReadOnlyModelViewSet):
|
||||
"""
|
||||
Returns a list of all levels on the map.
|
||||
"""
|
||||
queryset = Level.objects.all()
|
||||
serializer_class = LevelSerializer
|
||||
lookup_value_regex = '[^/]+'
|
||||
filter_fields = ('altitude', 'package')
|
||||
ordering_fields = ('altitude', 'package')
|
||||
ordering = ('altitude',)
|
||||
search_fields = ('name',)
|
||||
|
||||
|
||||
class PackageViewSet(AccessCachedViewSetMixin, ReadOnlyModelViewSet):
|
||||
"""
|
||||
Returns a list of all packages the map consists of.
|
||||
"""
|
||||
queryset = Package.objects.all()
|
||||
serializer_class = PackageSerializer
|
||||
lookup_value_regex = '[^/]+'
|
||||
filter_fields = ('name', 'depends')
|
||||
ordering_fields = ('name',)
|
||||
ordering = ('name',)
|
||||
search_fields = ('name',)
|
||||
|
||||
|
||||
class SourceViewSet(AccessCachedViewSetMixin, ReadOnlyModelViewSet):
|
||||
"""
|
||||
Returns a list of source images (to use as a drafts).
|
||||
Call /sources/{name}/image to get the image.
|
||||
"""
|
||||
queryset = Source.objects.all()
|
||||
serializer_class = SourceSerializer
|
||||
lookup_value_regex = '[^/]+'
|
||||
filter_fields = ('package',)
|
||||
ordering_fields = ('name', 'package')
|
||||
ordering = ('name',)
|
||||
search_fields = ('name',)
|
||||
|
||||
def get_queryset(self):
|
||||
return filter_source_queryset(self.request, super().get_queryset())
|
||||
|
||||
@detail_route(methods=['get'])
|
||||
def image(self, request, pk=None, version=None):
|
||||
source = self.get_object()
|
||||
response = HttpResponse(content_type=mimetypes.guess_type(source.name)[0])
|
||||
image_path = os.path.join(settings.MAP_ROOT, source.package.directory, 'sources', source.name)
|
||||
for chunk in File(open(image_path, 'rb')).chunks():
|
||||
response.write(chunk)
|
||||
return response
|
||||
|
||||
|
||||
class FeatureTypeViewSet(ViewSet):
|
||||
"""
|
||||
Get Feature types
|
||||
"""
|
||||
def list(self, request, version=None):
|
||||
serializer = FeatureTypeSerializer(FEATURE_TYPES.values(), many=True, context={'request': request})
|
||||
return Response(serializer.data)
|
||||
|
||||
def retrieve(self, request, pk=None, version=None):
|
||||
if pk not in FEATURE_TYPES:
|
||||
raise Http404
|
||||
serializer = FeatureTypeSerializer(FEATURE_TYPES[pk], context={'request': request})
|
||||
return Response(serializer.data)
|
Loading…
Add table
Add a link
Reference in a new issue