#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
###############################################################################
# #
# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# This file is part of the beat.web module of the BEAT platform. #
# #
# Commercial License Usage #
# Licensees holding valid commercial BEAT licenses may use this file in #
# accordance with the terms contained in a written agreement between you #
# and Idiap. For further information contact tto@idiap.ch #
# #
# Alternatively, this file may be used under the terms of the GNU Affero #
# Public License version 3 as published by the Free Software and appearing #
# in the file LICENSE.AGPL included in the packaging of this file. #
# The BEAT platform is distributed in the hope that it will be useful, but #
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
# or FITNESS FOR A PARTICULAR PURPOSE. #
# #
# You should have received a copy of the GNU Affero Public License along #
# with the BEAT platform. If not, see http://www.gnu.org/licenses/. #
# #
###############################################################################
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework import generics
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.reverse import reverse
from .responses import BadRequestResponse, ForbiddenResponse
from .models import Contribution, Versionable
from .permissions import IsAuthor
from .exceptions import ShareError, BaseCreationError
from .serializers import SharingSerializer, ContributionSerializer, CheckNameSerializer, DiffSerializer
from .mixins import CommonContextMixin, SerializerFieldsMixin, IsAuthorOrReadOnlyMixin
from . import is_true
[docs]class CheckContributionNameView(CommonContextMixin, generics.CreateAPIView):
serializer_class = CheckNameSerializer
permission_classes = [permissions.IsAuthenticated]
[docs] def get_serializer_context(self):
context = super(CheckContributionNameView, self).get_serializer_context()
context['model'] = self.model
return context
[docs] def post(self, request):
response = super(CheckContributionNameView, self).post(request)
response.status_code = status.HTTP_200_OK
return response
[docs]class ShareView(CommonContextMixin, generics.CreateAPIView):
permission_classes = [permissions.IsAuthenticated, IsAuthor]
serializer_class = SharingSerializer
[docs] def get_queryset(self):
author_name = self.kwargs.get('author_name')
object_name = self.kwargs.get('object_name')
version = self.kwargs.get('version')
return get_object_or_404(self.model,
author__username__iexact=author_name,
name__iexact=object_name,
version=version)
[docs] def do_share(self, obj, data):
users = data.get('users', None)
teams = data.get('teams', None)
obj.share(users=users, teams=teams)
[docs] def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
return BadRequestResponse(serializer.errors)
data = serializer.data
object_db = self.get_queryset()
try:
self.do_share(object_db, data)
except ShareError as e:
return BadRequestResponse(e.errors)
return Response(object_db.sharing_preferences())
[docs]class ListContributionView(CommonContextMixin, SerializerFieldsMixin, generics.ListAPIView):
model = Contribution
serializer_class = ContributionSerializer
permission_classes = [permissions.AllowAny]
[docs] def get_queryset(self):
return self.model.objects.for_user(self.request.user, True)
[docs] def get(self, request, *args, **kwargs):
fields_to_return = self.get_serializer_fields(request)
limit_to_latest_versions = is_true(request.query_params.get('latest_versions', False))
all_contributions = self.get_queryset().select_related()
if hasattr(self.model, 'author'):
all_contributions = all_contributions.order_by('author__username', 'name', '-version')
else:
all_contributions = all_contributions.order_by('name', '-version')
if limit_to_latest_versions:
all_contributions = self.model.filter_latest_versions(all_contributions)
# Sort the data formats and sends the response
all_contributions.sort(lambda x, y: cmp(x.fullname(), y.fullname()))
serializer = self.get_serializer(all_contributions, many=True, fields=fields_to_return)
return Response(serializer.data)
[docs]class ListCreateBaseView(CommonContextMixin, SerializerFieldsMixin, generics.ListCreateAPIView):
[docs] def get_serializer(self, *args, **kwargs):
if self.request.method == 'POST':
self.serializer_class = self.writing_serializer_class
return super(ListCreateBaseView, self).get_serializer(*args, **kwargs)
[docs] def get(self, request, *args, **kwargs):
fields_to_return = self.get_serializer_fields(request)
limit_to_latest_versions = is_true(request.query_params.get('latest_versions', False))
objects = self.get_queryset().select_related()
if limit_to_latest_versions:
objects = self.model.filter_latest_versions(objects)
serializer = self.get_serializer(objects, many=True, fields=fields_to_return)
return Response(serializer.data)
[docs] def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
try:
if hasattr(self.model, 'author'):
db_object = serializer.save(author=request.user)
else:
db_object = serializer.save()
except BaseCreationError as e:
return BadRequestResponse(e.errors)
else:
return BadRequestResponse(serializer.errors)
html_view_args = [db_object.name, db_object.version]
if hasattr(db_object, "author"):
html_view_args.insert(0, db_object.author.username)
result = {
'name': db_object.name,
'full_name': db_object.fullname(),
'url': reverse('{}:all'.format(self.namespace)) + db_object.fullname() + '/',
'object_view': reverse('{}:view'.format(self.namespace.split('_')[1]), args=html_view_args),
}
response = Response(result, status=201)
response['Location'] = result['url']
return response
[docs]class ListCreateContributionView(IsAuthorOrReadOnlyMixin, ListCreateBaseView):
[docs] def get_queryset(self):
user = self.request.user
author_name = self.kwargs.get('author_name')
return self.model.objects.from_author_and_public(user, author_name)
[docs]class DiffView(generics.RetrieveAPIView):
model = Versionable
serializer_class = DiffSerializer
[docs] def get(self, request, author1, name1, version1, author2, name2, version2):
# Retrieve the objects
try:
object1 = self.model.objects.get(author__username__iexact=author1,
name__iexact=name1,
version=int(version1))
except:
return Response('%s/%s/%s' % (author1, name1, version1), status=404)
try:
object2 = self.model.objects.get(author__username__iexact=author2,
name__iexact=name2,
version=int(version2))
except:
return Response('%s/%s/%s' % (author2, name2, version2), status=404)
# Check that the user can access them
accessibility = object1.accessibility_for(request.user)
if not accessibility[0]:
return ForbiddenResponse(object1.fullname())
accessibility = object2.accessibility_for(request.user)
if not accessibility[0]:
return ForbiddenResponse(object2.fullname())
# Compute the diff
serializer = self.get_serializer({'object1': object1,
'object2': object2})
return Response(serializer.data)
[docs]class RetrieveUpdateDestroyContributionView(CommonContextMixin, SerializerFieldsMixin, IsAuthorOrReadOnlyMixin, generics.RetrieveUpdateDestroyAPIView):
model = Contribution
[docs] def get_queryset(self):
version = self.kwargs.get('version', None)
author_name = self.kwargs.get('author_name')
object_name = self.kwargs.get('object_name')
user = self.request.user
if version is not None:
queryset = self.model.objects.for_user(user, True).filter(author__username__iexact=author_name,
name__iexact=object_name,
version__gte=version)\
.order_by('version')
else:
queryset = self.model.objects.for_user(user, True).filter(author__username__iexact=author_name,
name__iexact=object_name).order_by('-version')
return queryset
[docs] def get(self, request, *args, **kwargs):
db_objects = self.get_queryset()
if db_objects.count() == 0:
return Response(status=404)
db_object = db_objects[0]
version = int(self.kwargs.get('version', -1))
if version != -1 and db_object.version != version:
return Response(status=404)
# Process the query string
allow_sharing = hasattr(db_object, 'author') and (request.user == db_object.author)
fields_to_return = self.get_serializer_fields(request, allow_sharing=allow_sharing)
serializer = self.get_serializer(db_object, fields=fields_to_return)
return Response(serializer.data)
[docs] def delete(self, request, *args, **kwargs):
author_name = self.kwargs.get('author_name')
object_name = self.kwargs.get('object_name')
version = self.kwargs.get('version', None)
# Retrieve the object
if version is None:
return BadRequestResponse('A version number must be provided')
db_object = get_object_or_404(self.model,
author__username__iexact=author_name,
name__iexact=object_name,
version=version)
# Check that the object can be deleted
if not(db_object.deletable()):
return ForbiddenResponse("The {} can't be deleted anymore (needed by an attestation, an algorithm or another data format)".format(db_object.model_name()))
# Deletion of the object
db_object.delete()
return Response(status=204)