2019-10-18 14:18:49 +02:00
|
|
|
from django.contrib.auth.models import AnonymousUser
|
|
|
|
|
|
|
|
from openslides.utils.auth import in_some_groups
|
|
|
|
from openslides.utils.autoupdate import inform_changed_data
|
|
|
|
from openslides.utils.rest_api import (
|
|
|
|
DecimalField,
|
|
|
|
GenericViewSet,
|
|
|
|
ListModelMixin,
|
|
|
|
ModelViewSet,
|
|
|
|
Response,
|
|
|
|
RetrieveModelMixin,
|
|
|
|
ValidationError,
|
|
|
|
detail_route,
|
|
|
|
)
|
|
|
|
|
|
|
|
from .models import BasePoll
|
|
|
|
|
|
|
|
|
|
|
|
class BasePollViewSet(ModelViewSet):
|
|
|
|
def check_view_permissions(self):
|
|
|
|
"""
|
|
|
|
the vote view is checked seperately. For all other views manage permissions
|
|
|
|
are required.
|
|
|
|
"""
|
|
|
|
if self.action == "vote":
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return self.has_manage_permissions()
|
|
|
|
|
|
|
|
def perform_create(self, serializer):
|
|
|
|
poll = serializer.save()
|
|
|
|
poll.create_options()
|
|
|
|
|
|
|
|
def update(self, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Customized view endpoint to update a motion poll.
|
|
|
|
"""
|
|
|
|
poll = self.get_object()
|
|
|
|
|
|
|
|
if poll.state != BasePoll.STATE_CREATED:
|
|
|
|
raise ValidationError(
|
|
|
|
{"detail": "You can just edit a poll if it was not started."}
|
|
|
|
)
|
|
|
|
|
|
|
|
return super().update(*args, **kwargs)
|
|
|
|
|
|
|
|
@detail_route(methods=["POST"])
|
|
|
|
def start(self, request, pk):
|
|
|
|
poll = self.get_object()
|
|
|
|
if poll.state != BasePoll.STATE_CREATED:
|
|
|
|
raise ValidationError({"detail": "Wrong poll state"})
|
|
|
|
poll.state = BasePoll.STATE_STARTED
|
|
|
|
|
|
|
|
poll.save()
|
|
|
|
inform_changed_data(poll.get_votes())
|
|
|
|
return Response()
|
|
|
|
|
|
|
|
@detail_route(methods=["POST"])
|
|
|
|
def stop(self, request, pk):
|
|
|
|
poll = self.get_object()
|
2019-11-13 07:46:13 +01:00
|
|
|
# Analog polls could not be stopped; they are stopped when
|
|
|
|
# the results are entered.
|
|
|
|
if poll.type == BasePoll.TYPE_ANALOG:
|
|
|
|
raise ValidationError(
|
|
|
|
{"detail": "Analog polls can not be stopped. Please enter votes."}
|
|
|
|
)
|
|
|
|
|
2019-10-18 14:18:49 +02:00
|
|
|
if poll.state != BasePoll.STATE_STARTED:
|
|
|
|
raise ValidationError({"detail": "Wrong poll state"})
|
|
|
|
|
|
|
|
poll.state = BasePoll.STATE_FINISHED
|
|
|
|
poll.save()
|
|
|
|
inform_changed_data(poll.get_votes())
|
|
|
|
return Response()
|
|
|
|
|
|
|
|
@detail_route(methods=["POST"])
|
|
|
|
def publish(self, request, pk):
|
|
|
|
poll = self.get_object()
|
|
|
|
if poll.state != BasePoll.STATE_FINISHED:
|
|
|
|
raise ValidationError({"detail": "Wrong poll state"})
|
|
|
|
|
|
|
|
poll.state = BasePoll.STATE_PUBLISHED
|
|
|
|
poll.save()
|
|
|
|
inform_changed_data(poll.get_votes())
|
|
|
|
return Response()
|
|
|
|
|
|
|
|
@detail_route(methods=["POST"])
|
|
|
|
def pseudoanonymize(self, request, pk):
|
|
|
|
poll = self.get_object()
|
|
|
|
|
|
|
|
if poll.state not in (BasePoll.STATE_FINISHED, BasePoll.STATE_PUBLISHED):
|
|
|
|
raise ValidationError(
|
|
|
|
{"detail": "Pseudoanonmizing can only be done after a finished poll"}
|
|
|
|
)
|
|
|
|
if poll.type != BasePoll.TYPE_NAMED:
|
|
|
|
raise ValidationError(
|
|
|
|
{"detail": "You can just pseudoanonymize named polls"}
|
|
|
|
)
|
|
|
|
|
|
|
|
poll.pseudoanonymize()
|
|
|
|
return Response()
|
|
|
|
|
|
|
|
@detail_route(methods=["POST"])
|
|
|
|
def reset(self, request, pk):
|
|
|
|
poll = self.get_object()
|
|
|
|
|
|
|
|
if poll.state not in (BasePoll.STATE_FINISHED, BasePoll.STATE_PUBLISHED):
|
|
|
|
raise ValidationError(
|
|
|
|
{"detail": "You can only reset this poll after it is finished"}
|
|
|
|
)
|
|
|
|
|
|
|
|
poll.reset()
|
|
|
|
return Response()
|
|
|
|
|
|
|
|
@detail_route(methods=["POST"])
|
|
|
|
def vote(self, request, pk):
|
|
|
|
"""
|
|
|
|
For motion polls: Just "Y", "N" or "A" (if pollmethod is "YNA")
|
|
|
|
"""
|
|
|
|
poll = self.get_object()
|
|
|
|
if poll.state != BasePoll.STATE_STARTED:
|
|
|
|
raise ValidationError({"detail": "You cannot vote for an unstarted poll"})
|
|
|
|
|
|
|
|
if isinstance(request.user, AnonymousUser):
|
|
|
|
self.permission_denied(request)
|
|
|
|
|
|
|
|
# check permissions based on poll type and handle requests
|
|
|
|
if poll.type == BasePoll.TYPE_ANALOG:
|
|
|
|
if not self.has_manage_permissions():
|
|
|
|
self.permission_denied(request)
|
|
|
|
|
|
|
|
self.handle_analog_vote(request.data, poll, request.user)
|
|
|
|
# special: change the poll state to finished.
|
|
|
|
poll.state = BasePoll.STATE_FINISHED
|
|
|
|
poll.save()
|
|
|
|
|
|
|
|
elif poll.type == BasePoll.TYPE_NAMED:
|
|
|
|
self.assert_can_vote(poll, request)
|
|
|
|
self.handle_named_vote(request.data, poll, request.user)
|
|
|
|
poll.voted.add(request.user)
|
|
|
|
|
|
|
|
elif poll.type == BasePoll.TYPE_PSEUDOANONYMOUS:
|
|
|
|
self.assert_can_vote(poll, request)
|
|
|
|
|
|
|
|
if request.user in poll.voted.all():
|
2019-10-29 12:58:37 +01:00
|
|
|
self.permission_denied(request)
|
2019-10-18 14:18:49 +02:00
|
|
|
self.handle_pseudoanonymous_vote(request.data, poll)
|
|
|
|
poll.voted.add(request.user)
|
|
|
|
|
|
|
|
inform_changed_data(poll) # needed for the changed voted relation
|
|
|
|
return Response()
|
|
|
|
|
|
|
|
def assert_can_vote(self, poll, request):
|
|
|
|
"""
|
|
|
|
Raises a permission denied, if the user is not in a poll group
|
|
|
|
and present
|
|
|
|
"""
|
|
|
|
if not request.user.is_present or not in_some_groups(
|
|
|
|
request.user.id, poll.groups.all(), exact=True
|
|
|
|
):
|
|
|
|
self.permission_denied(request)
|
|
|
|
|
|
|
|
def parse_decimal_value(self, value, min_value=None):
|
|
|
|
""" Raises a ValidationError on incorrect values """
|
|
|
|
field = DecimalField(min_value=min_value, max_digits=15, decimal_places=6)
|
|
|
|
return field.to_internal_value(value)
|
|
|
|
|
|
|
|
|
|
|
|
class BaseVoteViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
|
|
|
pass
|