Merge pull request #1537 from normanjaeckel/RESTPoll

Added create, updated and destroy view for assignment polls.
This commit is contained in:
Oskar Hahn 2015-06-15 16:34:11 +02:00
commit a604f36634
5 changed files with 118 additions and 18 deletions

View File

@ -17,7 +17,7 @@ class AssignmentAppConfig(AppConfig):
from openslides.utils.signals import template_manipulation
from .signals import setup_assignment_config
from .template import add_assignment_stylesheets
from .views import AssignmentViewSet
from .views import AssignmentViewSet, AssignmentPollViewSet
# Connect signals.
config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config')
@ -33,3 +33,4 @@ class AssignmentAppConfig(AppConfig):
# Register viewsets.
router.register('assignments/assignment', AssignmentViewSet)
router.register('assignments/assignmentpoll', AssignmentPollViewSet)

View File

@ -247,7 +247,7 @@ class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
def create_poll(self):
"""
Creates an new poll for the assignment and adds all candidates to all
Creates a new poll for the assignment and adds all candidates to all
lists of speakers of related agenda items.
"""
candidates = self.candidates.all()

View File

@ -1,4 +1,13 @@
from openslides.utils.rest_api import ListSerializer, ModelSerializer
from django.db import transaction
from django.utils.translation import ugettext as _
from openslides.utils.rest_api import (
DictField,
IntegerField,
ListField,
ListSerializer,
ModelSerializer,
ValidationError)
from .models import (
models,
@ -64,6 +73,10 @@ class AssignmentAllPollSerializer(ModelSerializer):
Serializes all polls.
"""
assignmentoption_set = AssignmentOptionSerializer(many=True, read_only=True)
votes = ListField(
child=DictField(
child=IntegerField(min_value=-2)),
write_only=True)
class Meta:
model = AssignmentPoll
@ -75,7 +88,46 @@ class AssignmentAllPollSerializer(ModelSerializer):
'assignmentoption_set',
'votesvalid',
'votesinvalid',
'votescast',)
'votescast',
'votes',)
read_only_fields = ('yesnoabstain',)
@transaction.atomic
def update(self, instance, validated_data):
"""
Customized update method for polls. To update votes use the write
only field 'votes'.
Example data for a 'yesnoabstain' poll with two candidates:
"votes": [{"Yes": 10, "No": 4, "Abstain": -2},
{"Yes": -1, "No": 0, "Abstain": -2}]
"""
# Update votes.
votes = validated_data.get('votes')
if votes:
options = list(instance.get_options())
if len(votes) != len(options):
raise ValidationError({
'detail': _('You have to submit data for %d candidates.') % len(options)})
for index, option in enumerate(options):
if len(votes[index]) != len(instance.get_vote_values()):
raise ValidationError({
'detail': _('You have to submit data for %d vote values.') % len(instance.get_vote_values())})
for vote_value, vote_weight in votes[index].items():
if vote_value not in instance.get_vote_values():
raise ValidationError({
'detail': _('Vote value %s is invalid.') % vote_value})
instance.set_vote_objects_with_values(option, votes[index])
# Update remaining writeable fields.
instance.description = validated_data.get('description', instance.description)
instance.published = validated_data.get('published', instance.published)
instance.votesvalid = validated_data.get('votesvalid', instance.votesvalid)
instance.votesinvalid = validated_data.get('votesinvalid', instance.votesinvalid)
instance.votescast = validated_data.get('votescast', instance.votescast)
instance.save()
return instance
class AssignmentShortPollSerializer(AssignmentAllPollSerializer):

View File

@ -1,5 +1,6 @@
from cgi import escape
from django.db import transaction
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
from reportlab.lib import colors
@ -10,34 +11,46 @@ from reportlab.platypus import (PageBreak, Paragraph, SimpleDocTemplate, Spacer,
from openslides.config.api import config
from openslides.users.models import Group, User # TODO: remove this
from openslides.utils.pdf import stylesheet
from openslides.utils.rest_api import ModelViewSet, Response, ValidationError, detail_route
from openslides.utils.rest_api import (
DestroyModelMixin,
GenericViewSet,
ModelViewSet,
Response,
UpdateModelMixin,
ValidationError,
detail_route)
from openslides.utils.views import PDFView
from .models import Assignment, AssignmentPoll
from .serializers import AssignmentFullSerializer, AssignmentShortSerializer
from .serializers import (
AssignmentAllPollSerializer,
AssignmentFullSerializer,
AssignmentShortSerializer
)
class AssignmentViewSet(ModelViewSet):
"""
API endpoint to list, retrieve, create, update and destroy assignments and
to manage candidatures.
API endpoint to list, retrieve, create, update and destroy assignments
and to manage candidatures.
"""
queryset = Assignment.objects.all()
def check_permissions(self, request):
"""
Calls self.permission_denied() if the requesting user has not the
permission to see assignments and in case of create, update or destroy
requests the permission to manage assignments.
permission to see assignments and in case of create, update,
partial_update or destroy requests the permission to manage
assignments.
"""
if (not request.user.has_perm('assignments.can_see') or
(self.action in ('create', 'update', 'destroy') and not
request.user.has_perm('assignments.can_manage'))):
(self.action in ('create', 'update', 'partial_update', 'destroy') and
not request.user.has_perm('assignments.can_manage'))):
self.permission_denied(request)
def get_serializer_class(self):
"""
Returns different serializer classes with respect to users permissions.
Returns different serializer classes according to users permissions.
"""
if self.request.user.has_perm('assignments.can_manage'):
serializer_class = AssignmentFullSerializer
@ -48,8 +61,8 @@ class AssignmentViewSet(ModelViewSet):
@detail_route(methods=['post', 'delete'])
def candidature_self(self, request, pk=None):
"""
View to nominate self as candidate (POST) or withdraw own candidature
(DELETE).
View to nominate self as candidate (POST) or withdraw own
candidature (DELETE).
"""
if not request.user.has_perm('assignments.can_nominate_self'):
self.permission_denied(request)
@ -157,8 +170,8 @@ class AssignmentViewSet(ModelViewSet):
@detail_route(methods=['post', 'delete'])
def mark_elected(self, request, pk=None):
"""
View to mark other users as elected (POST) undo this (DELETE). The
client has to send {'user': <id>}.
View to mark other users as elected (POST) or undo this (DELETE).
The client has to send {'user': <id>}.
"""
if not request.user.has_perm('assignments.can_manage'):
self.permission_denied(request)
@ -178,6 +191,37 @@ class AssignmentViewSet(ModelViewSet):
message = _('User %s was successfully unelected.') % user
return Response({'detail': message})
@detail_route(methods=['post'])
def create_poll(self, request, pk=None):
"""
View to create a poll. It is a POST request without any data.
"""
if not request.user.has_perm('assignments.can_manage'):
self.permission_denied(request)
assignment = self.get_object()
if not assignment.candidates.exists():
raise ValidationError({'detail': _('Can not create poll because there are no candidates.')})
with transaction.atomic():
assignment.create_poll()
return Response({'detail': _(' Poll created successfully.')})
class AssignmentPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet):
"""
API endpoint to update and destroy assignment polls.
"""
queryset = AssignmentPoll.objects.all()
serializer_class = AssignmentAllPollSerializer
def check_permissions(self, request):
"""
Calls self.permission_denied() if the requesting user has not the
permission to see assignments and to manage assignments.
"""
if (not request.user.has_perm('assignments.can_see') or
not request.user.has_perm('assignments.can_manage')):
self.permission_denied(request)
class AssignmentPDF(PDFView):
required_permission = 'assignments.can_see'

View File

@ -6,17 +6,20 @@ from django.core.urlresolvers import reverse
from rest_framework.decorators import detail_route # noqa
from rest_framework.serializers import ( # noqa
CharField,
DictField,
Field,
IntegerField,
ListField,
ListSerializer,
ModelSerializer,
PrimaryKeyRelatedField,
RelatedField,
SerializerMethodField,
ValidationError)
from rest_framework.mixins import DestroyModelMixin, UpdateModelMixin # noqa
from rest_framework.response import Response # noqa
from rest_framework.routers import DefaultRouter
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet # noqa
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet, ViewSet # noqa
from rest_framework.decorators import list_route # noqa
from .exceptions import OpenSlidesError