Add vote delegation on server side
Add user_has_voted_for_delegations. Add tests Prevent self delegation Make delegated_user visible
This commit is contained in:
parent
ed2c298928
commit
3ac8569712
@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 2.2.16 on 2020-09-10 11:02
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import openslides.utils.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("assignments", "0014_remove_deprecated_slides"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assignmentvote",
|
||||||
|
name="delegated_user",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
null=True,
|
||||||
|
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
|
||||||
|
related_name="assignmentvote_delegated_votes",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -289,7 +289,7 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
poll.db_amount_global_no = Decimal(0)
|
poll.db_amount_global_no = Decimal(0)
|
||||||
poll.save()
|
poll.save()
|
||||||
|
|
||||||
def handle_analog_vote(self, data, poll, user):
|
def handle_analog_vote(self, data, poll):
|
||||||
for field in ["votesvalid", "votesinvalid", "votescast"]:
|
for field in ["votesvalid", "votesinvalid", "votescast"]:
|
||||||
setattr(poll, field, data[field])
|
setattr(poll, field, data[field])
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
|
|
||||||
poll.save()
|
poll.save()
|
||||||
|
|
||||||
def validate_vote_data(self, data, poll, user):
|
def validate_vote_data(self, data, poll):
|
||||||
"""
|
"""
|
||||||
Request data:
|
Request data:
|
||||||
analog:
|
analog:
|
||||||
@ -478,10 +478,12 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
|
|
||||||
options_data = data
|
options_data = data
|
||||||
|
|
||||||
def create_votes_type_votes(self, data, poll, vote_weight, vote_user):
|
def create_votes_type_votes(self, data, poll, vote_weight, vote_user, request_user):
|
||||||
"""
|
"""
|
||||||
Helper function for handle_(named|pseudoanonymous)_vote
|
Helper function for handle_(named|pseudoanonymous)_vote
|
||||||
Assumes data is already validated
|
Assumes data is already validated
|
||||||
|
vote_user is the user whose vote is given
|
||||||
|
request_user is the user who gives the vote, may be a delegate
|
||||||
"""
|
"""
|
||||||
options = poll.get_options()
|
options = poll.get_options()
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
@ -495,30 +497,46 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
if config["users_activate_vote_weight"]:
|
if config["users_activate_vote_weight"]:
|
||||||
weight *= vote_weight
|
weight *= vote_weight
|
||||||
vote = AssignmentVote.objects.create(
|
vote = AssignmentVote.objects.create(
|
||||||
option=option, user=vote_user, weight=weight, value="Y"
|
option=option,
|
||||||
|
user=vote_user,
|
||||||
|
delegated_user=request_user,
|
||||||
|
weight=weight,
|
||||||
|
value="Y",
|
||||||
)
|
)
|
||||||
inform_changed_data(vote, no_delete_on_restriction=True)
|
inform_changed_data(vote, no_delete_on_restriction=True)
|
||||||
else: # global_no or global_abstain
|
else: # global_no or global_abstain
|
||||||
option = options[0]
|
option = options[0]
|
||||||
weight = vote_weight if config["users_activate_vote_weight"] else Decimal(1)
|
weight = vote_weight if config["users_activate_vote_weight"] else Decimal(1)
|
||||||
vote = AssignmentVote.objects.create(
|
vote = AssignmentVote.objects.create(
|
||||||
option=option, user=vote_user, weight=weight, value=data
|
option=option,
|
||||||
|
user=vote_user,
|
||||||
|
delegated_user=request_user,
|
||||||
|
weight=weight,
|
||||||
|
value=data,
|
||||||
)
|
)
|
||||||
inform_changed_data(vote, no_delete_on_restriction=True)
|
inform_changed_data(vote, no_delete_on_restriction=True)
|
||||||
inform_changed_data(option)
|
inform_changed_data(option)
|
||||||
inform_changed_data(poll)
|
inform_changed_data(poll)
|
||||||
|
|
||||||
def create_votes_types_yn_yna(self, data, poll, vote_weight, vote_user):
|
def create_votes_types_yn_yna(
|
||||||
|
self, data, poll, vote_weight, vote_user, request_user
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Helper function for handle_(named|pseudoanonymous)_vote
|
Helper function for handle_(named|pseudoanonymous)_vote
|
||||||
Assumes data is already validated
|
Assumes data is already validated
|
||||||
|
vote_user is the user whose vote is given
|
||||||
|
request_user is the user who gives the vote, may be a delegate
|
||||||
"""
|
"""
|
||||||
options = poll.get_options()
|
options = poll.get_options()
|
||||||
weight = vote_weight if config["users_activate_vote_weight"] else Decimal(1)
|
weight = vote_weight if config["users_activate_vote_weight"] else Decimal(1)
|
||||||
for option_id, result in data.items():
|
for option_id, result in data.items():
|
||||||
option = options.get(pk=option_id)
|
option = options.get(pk=option_id)
|
||||||
vote = AssignmentVote.objects.create(
|
vote = AssignmentVote.objects.create(
|
||||||
option=option, user=vote_user, value=result, weight=weight
|
option=option,
|
||||||
|
user=vote_user,
|
||||||
|
delegated_user=request_user,
|
||||||
|
value=result,
|
||||||
|
weight=weight,
|
||||||
)
|
)
|
||||||
inform_changed_data(vote, no_delete_on_restriction=True)
|
inform_changed_data(vote, no_delete_on_restriction=True)
|
||||||
inform_changed_data(option, no_delete_on_restriction=True)
|
inform_changed_data(option, no_delete_on_restriction=True)
|
||||||
@ -527,24 +545,28 @@ class AssignmentPollViewSet(BasePollViewSet):
|
|||||||
VotedModel = AssignmentPoll.voted.through
|
VotedModel = AssignmentPoll.voted.through
|
||||||
VotedModel.objects.create(assignmentpoll=poll, user=user)
|
VotedModel.objects.create(assignmentpoll=poll, user=user)
|
||||||
|
|
||||||
def handle_named_vote(self, data, poll, user):
|
def handle_named_vote(self, data, poll, vote_user, request_user):
|
||||||
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
||||||
self.create_votes_type_votes(data, poll, user.vote_weight, user)
|
self.create_votes_type_votes(
|
||||||
|
data, poll, vote_user.vote_weight, vote_user, request_user
|
||||||
|
)
|
||||||
elif poll.pollmethod in (
|
elif poll.pollmethod in (
|
||||||
AssignmentPoll.POLLMETHOD_YN,
|
AssignmentPoll.POLLMETHOD_YN,
|
||||||
AssignmentPoll.POLLMETHOD_YNA,
|
AssignmentPoll.POLLMETHOD_YNA,
|
||||||
):
|
):
|
||||||
self.create_votes_types_yn_yna(data, poll, user.vote_weight, user)
|
self.create_votes_types_yn_yna(
|
||||||
|
data, poll, vote_user.vote_weight, vote_user, request_user
|
||||||
|
)
|
||||||
|
|
||||||
def handle_pseudoanonymous_vote(self, data, poll, user):
|
def handle_pseudoanonymous_vote(self, data, poll, user):
|
||||||
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
|
||||||
self.create_votes_type_votes(data, poll, user.vote_weight, None)
|
self.create_votes_type_votes(data, poll, user.vote_weight, None, None)
|
||||||
|
|
||||||
elif poll.pollmethod in (
|
elif poll.pollmethod in (
|
||||||
AssignmentPoll.POLLMETHOD_YN,
|
AssignmentPoll.POLLMETHOD_YN,
|
||||||
AssignmentPoll.POLLMETHOD_YNA,
|
AssignmentPoll.POLLMETHOD_YNA,
|
||||||
):
|
):
|
||||||
self.create_votes_types_yn_yna(data, poll, user.vote_weight, None)
|
self.create_votes_types_yn_yna(data, poll, user.vote_weight, None, None)
|
||||||
|
|
||||||
def convert_option_data(self, poll, data):
|
def convert_option_data(self, poll, data):
|
||||||
poll_options = poll.get_options()
|
poll_options = poll.get_options()
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 2.2.16 on 2020-09-10 11:02
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import openslides.utils.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("motions", "0036_rename_verbose_poll_types"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="motionvote",
|
||||||
|
name="delegated_user",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
null=True,
|
||||||
|
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
|
||||||
|
related_name="motionvote_delegated_votes",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -1190,7 +1190,7 @@ class MotionPollViewSet(BasePollViewSet):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def handle_analog_vote(self, data, poll, user):
|
def handle_analog_vote(self, data, poll):
|
||||||
option = poll.options.get()
|
option = poll.options.get()
|
||||||
vote, _ = MotionVote.objects.get_or_create(option=option, value="Y")
|
vote, _ = MotionVote.objects.get_or_create(option=option, value="Y")
|
||||||
vote.weight = data["Y"]
|
vote.weight = data["Y"]
|
||||||
@ -1209,7 +1209,7 @@ class MotionPollViewSet(BasePollViewSet):
|
|||||||
|
|
||||||
poll.save()
|
poll.save()
|
||||||
|
|
||||||
def validate_vote_data(self, data, poll, user):
|
def validate_vote_data(self, data, poll):
|
||||||
"""
|
"""
|
||||||
Request data for analog:
|
Request data for analog:
|
||||||
{ "Y": <amount>, "N": <amount>, ["A": <amount>],
|
{ "Y": <amount>, "N": <amount>, ["A": <amount>],
|
||||||
@ -1240,15 +1240,25 @@ class MotionPollViewSet(BasePollViewSet):
|
|||||||
VotedModel = MotionPoll.voted.through
|
VotedModel = MotionPoll.voted.through
|
||||||
VotedModel.objects.create(motionpoll=poll, user=user)
|
VotedModel.objects.create(motionpoll=poll, user=user)
|
||||||
|
|
||||||
def handle_named_vote(self, data, poll, user):
|
def handle_named_vote(self, data, poll, vote_user, request_user):
|
||||||
self.handle_named_and_pseudoanonymous_vote(data, user, user, poll)
|
self.handle_named_and_pseudoanonymous_vote(
|
||||||
|
data,
|
||||||
|
poll,
|
||||||
|
weight_user=vote_user,
|
||||||
|
vote_user=vote_user,
|
||||||
|
request_user=request_user,
|
||||||
|
)
|
||||||
|
|
||||||
def handle_pseudoanonymous_vote(self, data, poll, user):
|
def handle_pseudoanonymous_vote(self, data, poll, user):
|
||||||
self.handle_named_and_pseudoanonymous_vote(data, user, None, poll)
|
self.handle_named_and_pseudoanonymous_vote(data, poll, user, None, None)
|
||||||
|
|
||||||
def handle_named_and_pseudoanonymous_vote(self, data, weight_user, vote_user, poll):
|
def handle_named_and_pseudoanonymous_vote(
|
||||||
|
self, data, poll, weight_user, vote_user, request_user
|
||||||
|
):
|
||||||
option = poll.options.get()
|
option = poll.options.get()
|
||||||
vote = MotionVote.objects.create(user=vote_user, option=option)
|
vote = MotionVote.objects.create(
|
||||||
|
user=vote_user, delegated_user=request_user, option=option
|
||||||
|
)
|
||||||
vote.value = data
|
vote.value = data
|
||||||
vote.weight = (
|
vote.weight = (
|
||||||
weight_user.vote_weight
|
weight_user.vote_weight
|
||||||
|
@ -2,8 +2,13 @@ import json
|
|||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from ..poll.views import BasePoll
|
from ..poll.views import BasePoll
|
||||||
|
from ..utils import logging
|
||||||
from ..utils.access_permissions import BaseAccessPermissions
|
from ..utils.access_permissions import BaseAccessPermissions
|
||||||
from ..utils.auth import async_has_perm
|
from ..utils.auth import async_has_perm, user_collection_string
|
||||||
|
from ..utils.cache import element_cache
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class BaseVoteAccessPermissions(BaseAccessPermissions):
|
class BaseVoteAccessPermissions(BaseAccessPermissions):
|
||||||
@ -26,6 +31,7 @@ class BaseVoteAccessPermissions(BaseAccessPermissions):
|
|||||||
for vote in full_data
|
for vote in full_data
|
||||||
if vote["pollstate"] == BasePoll.STATE_PUBLISHED
|
if vote["pollstate"] == BasePoll.STATE_PUBLISHED
|
||||||
or vote["user_id"] == user_id
|
or vote["user_id"] == user_id
|
||||||
|
or vote["delegated_user_id"] == user_id
|
||||||
]
|
]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -71,8 +77,24 @@ class BasePollAccessPermissions(BaseAccessPermissions):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# add has_voted for all users to check whether op has voted
|
# add has_voted for all users to check whether op has voted
|
||||||
|
# also fill user_has_voted_for_delegations with all users for which he has
|
||||||
|
# already voted
|
||||||
|
user_data = await element_cache.get_element_data(
|
||||||
|
user_collection_string, user_id
|
||||||
|
)
|
||||||
|
if user_data is None:
|
||||||
|
logger.error(f"Could not find userdata for {user_id}")
|
||||||
|
vote_delegated_from_ids = set()
|
||||||
|
else:
|
||||||
|
vote_delegated_from_ids = set(user_data["vote_delegated_from_users_id"])
|
||||||
|
|
||||||
for poll in full_data:
|
for poll in full_data:
|
||||||
poll["user_has_voted"] = user_id in poll["voted_id"]
|
poll["user_has_voted"] = user_id in poll["voted_id"]
|
||||||
|
voted_ids = set(poll["voted_id"])
|
||||||
|
voted_for_delegations = list(
|
||||||
|
vote_delegated_from_ids.intersection(voted_ids)
|
||||||
|
)
|
||||||
|
poll["user_has_voted_for_delegations"] = voted_for_delegations
|
||||||
|
|
||||||
if await async_has_perm(user_id, self.manage_permission):
|
if await async_has_perm(user_id, self.manage_permission):
|
||||||
data = full_data
|
data = full_data
|
||||||
|
@ -29,6 +29,14 @@ class BaseVote(models.Model):
|
|||||||
blank=True,
|
blank=True,
|
||||||
on_delete=SET_NULL_AND_AUTOUPDATE,
|
on_delete=SET_NULL_AND_AUTOUPDATE,
|
||||||
)
|
)
|
||||||
|
delegated_user = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
default=None,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
on_delete=SET_NULL_AND_AUTOUPDATE,
|
||||||
|
related_name="%(class)s_delegated_votes",
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
@ -12,7 +12,15 @@ from ..utils.rest_api import (
|
|||||||
from .models import BasePoll
|
from .models import BasePoll
|
||||||
|
|
||||||
|
|
||||||
BASE_VOTE_FIELDS = ("id", "weight", "value", "user", "option", "pollstate")
|
BASE_VOTE_FIELDS = (
|
||||||
|
"id",
|
||||||
|
"weight",
|
||||||
|
"value",
|
||||||
|
"user",
|
||||||
|
"delegated_user",
|
||||||
|
"option",
|
||||||
|
"pollstate",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseVoteSerializer(ModelSerializer):
|
class BaseVoteSerializer(ModelSerializer):
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.utils import IntegrityError
|
from django.db.utils import IntegrityError
|
||||||
@ -104,8 +105,8 @@ class BasePollViewSet(ModelViewSet):
|
|||||||
# convert user ids to option ids
|
# convert user ids to option ids
|
||||||
self.convert_option_data(poll, vote_data)
|
self.convert_option_data(poll, vote_data)
|
||||||
|
|
||||||
self.validate_vote_data(vote_data, poll, request.user)
|
self.validate_vote_data(vote_data, poll)
|
||||||
self.handle_analog_vote(vote_data, poll, request.user)
|
self.handle_analog_vote(vote_data, poll)
|
||||||
|
|
||||||
if request.data.get("publish_immediately"):
|
if request.data.get("publish_immediately"):
|
||||||
poll.state = BasePoll.STATE_PUBLISHED
|
poll.state = BasePoll.STATE_PUBLISHED
|
||||||
@ -198,25 +199,41 @@ class BasePollViewSet(ModelViewSet):
|
|||||||
if isinstance(request.user, AnonymousUser):
|
if isinstance(request.user, AnonymousUser):
|
||||||
self.permission_denied(request)
|
self.permission_denied(request)
|
||||||
|
|
||||||
# check permissions based on poll type and handle requests
|
# data format is:
|
||||||
self.assert_can_vote(poll, request)
|
# { data: <vote_data>, [user_id: int] }
|
||||||
|
# if user_id is given, the operator votes for this user instead of himself
|
||||||
|
# user_id is ignored for analog polls
|
||||||
data = request.data
|
data = request.data
|
||||||
self.validate_vote_data(data, poll, request.user)
|
if "data" not in data:
|
||||||
|
raise ValidationError({"detail": "No data provided."})
|
||||||
|
vote_data = data["data"]
|
||||||
|
if "user_id" in data and poll.type != BasePoll.TYPE_ANALOG:
|
||||||
|
try:
|
||||||
|
vote_user = get_user_model().objects.get(pk=data["user_id"])
|
||||||
|
except get_user_model().DoesNotExist:
|
||||||
|
raise ValidationError({"detail": "The given user does not exist."})
|
||||||
|
else:
|
||||||
|
vote_user = request.user
|
||||||
|
|
||||||
|
# check permissions based on poll type and user
|
||||||
|
self.assert_can_vote(poll, request, vote_user)
|
||||||
|
|
||||||
|
# validate the vote data
|
||||||
|
self.validate_vote_data(vote_data, poll)
|
||||||
|
|
||||||
if poll.type == BasePoll.TYPE_ANALOG:
|
if poll.type == BasePoll.TYPE_ANALOG:
|
||||||
self.handle_analog_vote(data, poll, request.user)
|
self.handle_analog_vote(vote_data, poll)
|
||||||
if request.data.get("publish_immediately") == "1":
|
if vote_data.get("publish_immediately") == "1":
|
||||||
poll.state = BasePoll.STATE_PUBLISHED
|
poll.state = BasePoll.STATE_PUBLISHED
|
||||||
else:
|
else:
|
||||||
poll.state = BasePoll.STATE_FINISHED
|
poll.state = BasePoll.STATE_FINISHED
|
||||||
poll.save()
|
poll.save()
|
||||||
|
|
||||||
elif poll.type == BasePoll.TYPE_NAMED:
|
elif poll.type == BasePoll.TYPE_NAMED:
|
||||||
self.handle_named_vote(data, poll, request.user)
|
self.handle_named_vote(vote_data, poll, vote_user, request.user)
|
||||||
|
|
||||||
elif poll.type == BasePoll.TYPE_PSEUDOANONYMOUS:
|
elif poll.type == BasePoll.TYPE_PSEUDOANONYMOUS:
|
||||||
self.handle_pseudoanonymous_vote(data, poll, request.user)
|
self.handle_pseudoanonymous_vote(vote_data, poll, vote_user)
|
||||||
|
|
||||||
inform_changed_data(poll)
|
inform_changed_data(poll)
|
||||||
|
|
||||||
@ -231,13 +248,16 @@ class BasePollViewSet(ModelViewSet):
|
|||||||
inform_changed_data(poll.get_votes(), final_data=True)
|
inform_changed_data(poll.get_votes(), final_data=True)
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
def assert_can_vote(self, poll, request):
|
def assert_can_vote(self, poll, request, vote_user):
|
||||||
"""
|
"""
|
||||||
Raises a permission denied, if the user is not allowed to vote (or has already voted).
|
Raises a permission denied, if the user is not allowed to vote (or has already voted).
|
||||||
Adds the user to the voted array, so this needs to be reverted on error!
|
Adds the user to the voted array, so this needs to be reverted if a later error happens!
|
||||||
Analog: has to have manage permissions
|
Analog: has to have manage permissions
|
||||||
Named & Pseudoanonymous: has to be in a poll group and present
|
Named & Pseudoanonymous: has to be in a poll group and present
|
||||||
"""
|
"""
|
||||||
|
if request.user != vote_user and request.user != vote_user.vote_delegated_to:
|
||||||
|
self.permission_denied(request)
|
||||||
|
|
||||||
if poll.type == BasePoll.TYPE_ANALOG:
|
if poll.type == BasePoll.TYPE_ANALOG:
|
||||||
if not self.has_manage_permissions():
|
if not self.has_manage_permissions():
|
||||||
self.permission_denied(request)
|
self.permission_denied(request)
|
||||||
@ -246,14 +266,14 @@ class BasePollViewSet(ModelViewSet):
|
|||||||
raise ValidationError("You can only vote on a started poll.")
|
raise ValidationError("You can only vote on a started poll.")
|
||||||
|
|
||||||
if not request.user.is_present or not in_some_groups(
|
if not request.user.is_present or not in_some_groups(
|
||||||
request.user.id,
|
vote_user.id,
|
||||||
list(poll.groups.values_list("pk", flat=True)),
|
list(poll.groups.values_list("pk", flat=True)),
|
||||||
exact=True,
|
exact=True,
|
||||||
):
|
):
|
||||||
self.permission_denied(request)
|
self.permission_denied(request)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.add_user_to_voted_array(request.user, poll)
|
self.add_user_to_voted_array(vote_user, poll)
|
||||||
inform_changed_data(poll)
|
inform_changed_data(poll)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
raise ValidationError({"detail": "You have already voted"})
|
raise ValidationError({"detail": "You have already voted"})
|
||||||
@ -292,20 +312,20 @@ class BasePollViewSet(ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def validate_vote_data(self, data, poll, user):
|
def validate_vote_data(self, data, poll):
|
||||||
"""
|
"""
|
||||||
To be implemented by subclass. Validates the data according to poll type and method and fields by validated versions.
|
To be implemented by subclass. Validates the data according to poll type and method and fields by validated versions.
|
||||||
Raises ValidationError on failure
|
Raises ValidationError on failure
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def handle_analog_vote(self, data, poll, user):
|
def handle_analog_vote(self, data, poll):
|
||||||
"""
|
"""
|
||||||
To be implemented by subclass. Handles the analog vote. Assumes data is validated
|
To be implemented by subclass. Handles the analog vote. Assumes data is validated
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def handle_named_vote(self, data, poll, user):
|
def handle_named_vote(self, data, poll, vote_user, request_user):
|
||||||
"""
|
"""
|
||||||
To be implemented by subclass. Handles the named vote. Assumes data is validated.
|
To be implemented by subclass. Handles the named vote. Assumes data is validated.
|
||||||
Needs to manage the voted-array per option.
|
Needs to manage the voted-array per option.
|
||||||
|
@ -58,6 +58,8 @@ class UserAccessPermissions(BaseAccessPermissions):
|
|||||||
own_data_fields = set(little_data_fields)
|
own_data_fields = set(little_data_fields)
|
||||||
own_data_fields.add("email")
|
own_data_fields.add("email")
|
||||||
own_data_fields.add("gender")
|
own_data_fields.add("gender")
|
||||||
|
own_data_fields.add("vote_delegated_to_id")
|
||||||
|
own_data_fields.add("vote_delegated_from_users_id")
|
||||||
|
|
||||||
# Check user permissions.
|
# Check user permissions.
|
||||||
if await async_has_perm(user_id, "users.can_see_name"):
|
if await async_has_perm(user_id, "users.can_see_name"):
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 2.2.16 on 2020-09-03 11:13
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import openslides.utils.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("users", "0014_user_rename_permission"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="user",
|
||||||
|
name="vote_delegated_to",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
|
||||||
|
related_name="vote_delegated_from_users",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -22,7 +22,11 @@ from openslides.utils.manager import BaseManager
|
|||||||
|
|
||||||
from ..core.config import config
|
from ..core.config import config
|
||||||
from ..utils.auth import GROUP_ADMIN_PK
|
from ..utils.auth import GROUP_ADMIN_PK
|
||||||
from ..utils.models import CASCADE_AND_AUTOUPDATE, RESTModelMixin
|
from ..utils.models import (
|
||||||
|
CASCADE_AND_AUTOUPDATE,
|
||||||
|
SET_NULL_AND_AUTOUPDATE,
|
||||||
|
RESTModelMixin,
|
||||||
|
)
|
||||||
from .access_permissions import (
|
from .access_permissions import (
|
||||||
GroupAccessPermissions,
|
GroupAccessPermissions,
|
||||||
PersonalNoteAccessPermissions,
|
PersonalNoteAccessPermissions,
|
||||||
@ -54,7 +58,8 @@ class UserManager(BaseUserManager):
|
|||||||
queryset=Permission.objects.select_related("content_type"),
|
queryset=Permission.objects.select_related("content_type"),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
"vote_delegated_from_users",
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_user(self, username, password, skip_autoupdate=False, **kwargs):
|
def create_user(self, username, password, skip_autoupdate=False, **kwargs):
|
||||||
@ -164,6 +169,14 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser):
|
|||||||
default=Decimal("1"), max_digits=15, decimal_places=6, null=False, blank=True
|
default=Decimal("1"), max_digits=15, decimal_places=6, null=False, blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
vote_delegated_to = models.ForeignKey(
|
||||||
|
"self",
|
||||||
|
on_delete=SET_NULL_AND_AUTOUPDATE,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name="vote_delegated_from_users",
|
||||||
|
)
|
||||||
|
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -7,6 +7,7 @@ from ..utils.rest_api import (
|
|||||||
JSONField,
|
JSONField,
|
||||||
ModelSerializer,
|
ModelSerializer,
|
||||||
RelatedField,
|
RelatedField,
|
||||||
|
SerializerMethodField,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
from ..utils.validate import validate_html_strict
|
from ..utils.validate import validate_html_strict
|
||||||
@ -36,6 +37,8 @@ USERCANSEEEXTRASERIALIZER_FIELDS = USERCANSEESERIALIZER_FIELDS + (
|
|||||||
"comment",
|
"comment",
|
||||||
"is_active",
|
"is_active",
|
||||||
"auth_type",
|
"auth_type",
|
||||||
|
"vote_delegated_to_id",
|
||||||
|
"vote_delegated_from_users_id",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -57,11 +60,14 @@ class UserSerializer(ModelSerializer):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
vote_delegated_from_users_id = SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = USERCANSEEEXTRASERIALIZER_FIELDS + (
|
fields = USERCANSEEEXTRASERIALIZER_FIELDS + (
|
||||||
"default_password",
|
"default_password",
|
||||||
"session_auth_hash",
|
"session_auth_hash",
|
||||||
|
"vote_delegated_to",
|
||||||
)
|
)
|
||||||
read_only_fields = ("last_email_send", "auth_type")
|
read_only_fields = ("last_email_send", "auth_type")
|
||||||
|
|
||||||
@ -119,6 +125,13 @@ class UserSerializer(ModelSerializer):
|
|||||||
inform_changed_data(user)
|
inform_changed_data(user)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
def get_vote_delegated_from_users_id(self, user):
|
||||||
|
# check needed to prevent errors on import since we only give an OrderedDict there
|
||||||
|
if hasattr(user, "vote_delegated_from_users"):
|
||||||
|
return [delegator.id for delegator in user.vote_delegated_from_users.all()]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class PermissionRelatedField(RelatedField):
|
class PermissionRelatedField(RelatedField):
|
||||||
"""
|
"""
|
||||||
|
@ -174,9 +174,77 @@ class UserViewSet(ModelViewSet):
|
|||||||
):
|
):
|
||||||
request.data["username"] = user.username
|
request.data["username"] = user.username
|
||||||
|
|
||||||
|
# check that no chains are created with vote delegation
|
||||||
|
delegate_id = request.data.get("vote_delegated_to_id")
|
||||||
|
if delegate_id:
|
||||||
|
try:
|
||||||
|
delegate = User.objects.get(id=delegate_id)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
raise ValidationError(
|
||||||
|
{
|
||||||
|
"detail": f"Vote delegation: The user with id {delegate_id} does not exist"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_no_self_delegation(user, [delegate_id])
|
||||||
|
self.assert_vote_not_delegated(delegate)
|
||||||
|
self.assert_has_no_delegated_votes(user)
|
||||||
|
|
||||||
|
inform_changed_data(delegate)
|
||||||
|
if user.vote_delegated_to:
|
||||||
|
inform_changed_data(user.vote_delegated_to)
|
||||||
|
|
||||||
|
# handle delegated_from field seperately since its a SerializerMethodField
|
||||||
|
new_delegation_ids = request.data.get("vote_delegated_from_users_id")
|
||||||
|
if "vote_delegated_from_users_id" in request.data:
|
||||||
|
del request.data["vote_delegated_from_users_id"]
|
||||||
|
|
||||||
response = super().update(request, *args, **kwargs)
|
response = super().update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
# after rest of the request succeeded, handle delegation changes
|
||||||
|
if new_delegation_ids:
|
||||||
|
self.assert_no_self_delegation(user, new_delegation_ids)
|
||||||
|
self.assert_vote_not_delegated(user)
|
||||||
|
|
||||||
|
for id in new_delegation_ids:
|
||||||
|
delegation_user = User.objects.get(id=id)
|
||||||
|
self.assert_has_no_delegated_votes(delegation_user)
|
||||||
|
delegation_user.vote_delegated_to = user
|
||||||
|
delegation_user.save()
|
||||||
|
|
||||||
|
delegations_to_remove = user.vote_delegated_from_users.exclude(
|
||||||
|
id__in=(new_delegation_ids or [])
|
||||||
|
)
|
||||||
|
for old_delegation_user in delegations_to_remove:
|
||||||
|
old_delegation_user.vote_delegated_to = None
|
||||||
|
old_delegation_user.save()
|
||||||
|
|
||||||
|
# if only delegated_from was changed, we need an autoupdate for the operator
|
||||||
|
if new_delegation_ids or delegations_to_remove:
|
||||||
|
inform_changed_data(user)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def assert_vote_not_delegated(self, user):
|
||||||
|
if user.vote_delegated_to:
|
||||||
|
raise ValidationError(
|
||||||
|
{
|
||||||
|
"detail": "You cannot delegate a vote to a user who has already delegated his vote."
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def assert_has_no_delegated_votes(self, user):
|
||||||
|
if user.vote_delegated_from_users and len(user.vote_delegated_from_users.all()):
|
||||||
|
raise ValidationError(
|
||||||
|
{
|
||||||
|
"detail": "You cannot delegate a vote of a user who is already a delegate of another user."
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def assert_no_self_delegation(self, user, delegate_ids):
|
||||||
|
if user.id in delegate_ids:
|
||||||
|
raise ValidationError({"detail": "You cannot delegate a vote to yourself."})
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Customized view endpoint to delete an user.
|
Customized view endpoint to delete an user.
|
||||||
@ -391,6 +459,7 @@ class UserViewSet(ModelViewSet):
|
|||||||
data = serializer.prepare_password(serializer.data)
|
data = serializer.prepare_password(serializer.data)
|
||||||
groups = data["groups_id"]
|
groups = data["groups_id"]
|
||||||
del data["groups_id"]
|
del data["groups_id"]
|
||||||
|
del data["vote_delegated_from_users_id"]
|
||||||
|
|
||||||
db_user = User(**data)
|
db_user = User(**data)
|
||||||
try:
|
try:
|
||||||
|
@ -770,6 +770,7 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"options": {
|
"options": {
|
||||||
"1": {"Y": "1", "N": "2.35", "A": "-1"},
|
"1": {"Y": "1", "N": "2.35", "A": "-1"},
|
||||||
"2": {"Y": "30", "N": "-2", "A": "8.93"},
|
"2": {"Y": "30", "N": "-2", "A": "8.93"},
|
||||||
@ -778,6 +779,7 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
"votesinvalid": "-2",
|
"votesinvalid": "-2",
|
||||||
"votescast": "-2",
|
"votescast": "-2",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
self.assertEqual(AssignmentVote.objects.count(), 6)
|
self.assertEqual(AssignmentVote.objects.count(), 6)
|
||||||
@ -800,11 +802,13 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"options": {"1": {"Y": "1", "N": "1", "A": "1"}},
|
"options": {"1": {"Y": "1", "N": "1", "A": "1"}},
|
||||||
"votesvalid": "-1.5",
|
"votesvalid": "-1.5",
|
||||||
"votesinvalid": "-2",
|
"votesinvalid": "-2",
|
||||||
"votescast": "-2",
|
"votescast": "-2",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
@ -814,11 +818,13 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"options": {
|
"options": {
|
||||||
"1": {"Y": "1", "N": "2.35", "A": "-1"},
|
"1": {"Y": "1", "N": "2.35", "A": "-1"},
|
||||||
"2": {"Y": "1", "N": "2.35", "A": "-1"},
|
"2": {"Y": "1", "N": "2.35", "A": "-1"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
@ -828,7 +834,7 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"options": {"1": {"Y": "1", "N": "2.35", "A": "-1"}}},
|
{"data": {"options": {"1": {"Y": "1", "N": "2.35", "A": "-1"}}}},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
@ -839,10 +845,12 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"options": {
|
"options": {
|
||||||
"1": {"Y": "1", "N": "2.35", "A": "-1"},
|
"1": {"Y": "1", "N": "2.35", "A": "-1"},
|
||||||
"3": {"Y": "1", "N": "2.35", "A": "-1"},
|
"3": {"Y": "1", "N": "2.35", "A": "-1"},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -851,25 +859,31 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
def test_no_permissions(self):
|
def test_no_permissions(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
def test_wrong_state(self):
|
def test_wrong_state(self):
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
def test_missing_data(self):
|
def test_missing_data(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
def test_wrong_data_format(self):
|
def test_wrong_data_format(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), [1, 2, 5]
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": [1, 2, 5]}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
@ -878,7 +892,7 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"options": [1, "string"]},
|
{"data": {"options": [1, "string"]}},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
@ -887,7 +901,7 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"options": {"string": "some_other_string"}},
|
{"data": {"options": {"string": "some_other_string"}}},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
@ -896,7 +910,7 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"options": {"1": [None]}},
|
{"data": {"options": {"1": [None]}}},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
@ -907,7 +921,7 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
data = {"options": {"1": {"Y": "1", "N": "3", "A": "-1"}}}
|
data = {"options": {"1": {"Y": "1", "N": "3", "A": "-1"}}}
|
||||||
del data["options"]["1"][value]
|
del data["options"]["1"][value]
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), data
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": data}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
@ -917,10 +931,12 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.client.post(
|
self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"options": {"1": {"Y": 5, "N": 0, "A": 1}},
|
"options": {"1": {"Y": 5, "N": 0, "A": 1}},
|
||||||
"votesvalid": "-2",
|
"votesvalid": "-2",
|
||||||
"votesinvalid": "1",
|
"votesinvalid": "1",
|
||||||
"votescast": "-1",
|
"votescast": "-1",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.poll.state = 3
|
self.poll.state = 3
|
||||||
@ -928,10 +944,12 @@ class VoteAssignmentPollAnalogYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"options": {"1": {"Y": 2, "N": 2, "A": 2}},
|
"options": {"1": {"Y": 2, "N": 2, "A": 2}},
|
||||||
"votesvalid": "4.64",
|
"votesvalid": "4.64",
|
||||||
"votesinvalid": "-2",
|
"votesinvalid": "-2",
|
||||||
"votescast": "3",
|
"votescast": "3",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -973,7 +991,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y", "2": "N", "3": "A"},
|
{"data": {"1": "Y", "2": "N", "3": "A"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -1007,7 +1025,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y", "2": "N", "3": "A"},
|
{"data": {"1": "Y", "2": "N", "3": "A"}},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
self.assertEqual(AssignmentVote.objects.count(), 3)
|
self.assertEqual(AssignmentVote.objects.count(), 3)
|
||||||
@ -1039,12 +1057,12 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "N"},
|
{"data": {"1": "N"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1064,7 +1082,8 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
option2 = self.poll2.options.get()
|
option2 = self.poll2.options.get()
|
||||||
# Do request to poll with option2 (which is wrong...)
|
# Do request to poll with option2 (which is wrong...)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {str(option2.id): "Y"}
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {str(option2.id): "Y"}},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(AssignmentVote.objects.count(), 0)
|
self.assertEqual(AssignmentVote.objects.count(), 0)
|
||||||
@ -1081,7 +1100,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y", "2": "N"},
|
{"data": {"1": "Y", "2": "N"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1092,7 +1111,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -1103,7 +1122,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y", "3": "N"},
|
{"data": {"1": "Y", "3": "N"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1114,7 +1133,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
@ -1125,7 +1144,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
gclient = self.create_guest_client()
|
gclient = self.create_guest_client()
|
||||||
response = gclient.post(
|
response = gclient.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
@ -1137,20 +1156,24 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.admin.save()
|
self.admin.save()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_wrong_state(self):
|
def test_wrong_state(self):
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
def test_missing_data(self):
|
def test_missing_data(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(
|
self.assertHttpStatusVerbose(
|
||||||
response, status.HTTP_200_OK
|
response, status.HTTP_200_OK
|
||||||
) # new "feature" because of partial requests: empty requests work!
|
) # new "feature" because of partial requests: empty requests work!
|
||||||
@ -1160,7 +1183,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
[1, 2, 5],
|
{"data": [1, 2, 5]},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1170,7 +1193,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "string"},
|
{"data": {"1": "string"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1180,7 +1203,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"id": "Y"},
|
{"data": {"id": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1190,7 +1213,7 @@ class VoteAssignmentPollNamedYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": [None]},
|
{"data": {"1": [None]}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1229,7 +1252,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 1, "2": 0},
|
{"data": {"1": 1, "2": 0}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -1254,12 +1277,12 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 1, "2": 0},
|
{"data": {"1": 1, "2": 0}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 0, "2": 1},
|
{"data": {"1": 0, "2": 1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1278,7 +1301,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.poll.save()
|
self.poll.save()
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), "N"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "N"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = AssignmentPoll.objects.get()
|
poll = AssignmentPoll.objects.get()
|
||||||
@ -1294,7 +1317,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.poll.save()
|
self.poll.save()
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), "N"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "N"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
@ -1305,7 +1328,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.poll.save()
|
self.poll.save()
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), "A"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = AssignmentPoll.objects.get()
|
poll = AssignmentPoll.objects.get()
|
||||||
@ -1321,7 +1344,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.poll.save()
|
self.poll.save()
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), "A"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
@ -1331,7 +1354,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": -1},
|
{"data": {"1": -1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1342,7 +1365,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 2, "2": 1},
|
{"data": {"1": 2, "2": 1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -1361,7 +1384,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 2, "2": 2},
|
{"data": {"1": 2, "2": 2}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1372,7 +1395,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 1, "2": 1, "3": 1},
|
{"data": {"1": 1, "2": 1, "3": 1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1381,7 +1404,9 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
def test_wrong_options(self):
|
def test_wrong_options(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"2": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"2": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
@ -1390,7 +1415,9 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
@ -1399,7 +1426,9 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
gclient = self.create_guest_client()
|
gclient = self.create_guest_client()
|
||||||
response = gclient.post(
|
response = gclient.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
@ -1409,21 +1438,27 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.admin.is_present = False
|
self.admin.is_present = False
|
||||||
self.admin.save()
|
self.admin.save()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_wrong_state(self):
|
def test_wrong_state(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
def test_missing_data(self):
|
def test_missing_data(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
@ -1431,7 +1466,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
[1, 2, 5],
|
{"data": [1, 2, 5]},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1441,7 +1476,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "string"},
|
{"data": {"1": "string"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1451,7 +1486,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"id": 1},
|
{"data": {"id": 1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1461,7 +1496,7 @@ class VoteAssignmentPollNamedVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": [None]},
|
{"data": {"1": [None]}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1495,7 +1530,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y", "2": "N", "3": "A"},
|
{"data": {"1": "Y", "2": "N", "3": "A"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -1522,12 +1557,12 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "N"},
|
{"data": {"1": "N"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1541,7 +1576,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y", "2": "N"},
|
{"data": {"1": "Y", "2": "N"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1552,7 +1587,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -1563,7 +1598,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y", "3": "N"},
|
{"data": {"1": "Y", "3": "N"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1574,7 +1609,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
@ -1585,7 +1620,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
gclient = self.create_guest_client()
|
gclient = self.create_guest_client()
|
||||||
response = gclient.post(
|
response = gclient.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
@ -1597,20 +1632,24 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.admin.save()
|
self.admin.save()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "Y"},
|
{"data": {"1": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_wrong_state(self):
|
def test_wrong_state(self):
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
def test_missing_data(self):
|
def test_missing_data(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
@ -1618,7 +1657,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
[1, 2, 5],
|
{"data": [1, 2, 5]},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1628,7 +1667,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "string"},
|
{"data": {"1": "string"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1638,7 +1677,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"id": "Y"},
|
{"data": {"id": "Y"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1648,7 +1687,7 @@ class VoteAssignmentPollPseudoanonymousYNA(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": [None]},
|
{"data": {"1": [None]}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1687,7 +1726,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 1, "2": 0},
|
{"data": {"1": 1, "2": 0}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -1714,12 +1753,12 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 1, "2": 0},
|
{"data": {"1": 1, "2": 0}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 0, "2": 1},
|
{"data": {"1": 0, "2": 1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1737,7 +1776,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": -1},
|
{"data": {"1": -1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1748,7 +1787,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 2, "2": 1},
|
{"data": {"1": 2, "2": 1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
@ -1769,7 +1808,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 2, "2": 2},
|
{"data": {"1": 2, "2": 2}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1780,7 +1819,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": 1, "2": 1, "3": 1},
|
{"data": {"1": 1, "2": 1, "3": 1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1789,7 +1828,9 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
def test_wrong_options(self):
|
def test_wrong_options(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"2": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"2": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
@ -1798,7 +1839,9 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
@ -1807,7 +1850,9 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
gclient = self.create_guest_client()
|
gclient = self.create_guest_client()
|
||||||
response = gclient.post(
|
response = gclient.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
@ -1817,21 +1862,27 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.admin.is_present = False
|
self.admin.is_present = False
|
||||||
self.admin.save()
|
self.admin.save()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
self.assertFalse(AssignmentPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_wrong_state(self):
|
def test_wrong_state(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": 1}, format="json"
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"1": 1}},
|
||||||
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
def test_missing_data(self):
|
def test_missing_data(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(reverse("assignmentpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
self.assertFalse(AssignmentVote.objects.exists())
|
self.assertFalse(AssignmentVote.objects.exists())
|
||||||
|
|
||||||
@ -1839,7 +1890,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
[1, 2, 5],
|
{"data": {"data": [1, 2, 5]}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1849,7 +1900,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": "string"},
|
{"data": {"1": "string"}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1859,7 +1910,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"id": 1},
|
{"data": {"id": 1}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1869,7 +1920,7 @@ class VoteAssignmentPollPseudoanonymousVotes(VoteAssignmentPollBaseTestClass):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
reverse("assignmentpoll-vote", args=[self.poll.pk]),
|
||||||
{"1": [None]},
|
{"data": {"1": [None]}},
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
@ -1923,7 +1974,7 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
|
|
||||||
def test_vote(self):
|
def test_vote(self):
|
||||||
response = self.user_client.post(
|
response = self.user_client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": "A"}
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {"1": "A"}}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = AssignmentPoll.objects.get()
|
poll = AssignmentPoll.objects.get()
|
||||||
@ -1956,6 +2007,7 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
"votesinvalid": "0.000000",
|
"votesinvalid": "0.000000",
|
||||||
"votesvalid": "1.000000",
|
"votesvalid": "1.000000",
|
||||||
"user_has_voted": False,
|
"user_has_voted": False,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
"voted_id": [self.user.id],
|
"voted_id": [self.user.id],
|
||||||
},
|
},
|
||||||
"assignments/assignment-option:1": {
|
"assignments/assignment-option:1": {
|
||||||
@ -1973,6 +2025,7 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
"option_id": 1,
|
"option_id": 1,
|
||||||
"pollstate": AssignmentPoll.STATE_STARTED,
|
"pollstate": AssignmentPoll.STATE_STARTED,
|
||||||
"user_id": self.user.id,
|
"user_id": self.user.id,
|
||||||
|
"delegated_user_id": self.user.id,
|
||||||
"value": "A",
|
"value": "A",
|
||||||
"weight": "1.000000",
|
"weight": "1.000000",
|
||||||
},
|
},
|
||||||
@ -1989,6 +2042,7 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
"option_id": 1,
|
"option_id": 1,
|
||||||
"pollstate": AssignmentPoll.STATE_STARTED,
|
"pollstate": AssignmentPoll.STATE_STARTED,
|
||||||
"user_id": self.user.id,
|
"user_id": self.user.id,
|
||||||
|
"delegated_user_id": self.user.id,
|
||||||
"value": "A",
|
"value": "A",
|
||||||
"weight": "1.000000",
|
"weight": "1.000000",
|
||||||
},
|
},
|
||||||
@ -2018,6 +2072,7 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"votes_amount": 1,
|
"votes_amount": 1,
|
||||||
"user_has_voted": user == self.user,
|
"user_has_voted": user == self.user,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2073,6 +2128,7 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
"votesinvalid": "0.000000",
|
"votesinvalid": "0.000000",
|
||||||
"votesvalid": "1.000000",
|
"votesvalid": "1.000000",
|
||||||
"user_has_voted": user == self.user,
|
"user_has_voted": user == self.user,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
"voted_id": [self.user.id],
|
"voted_id": [self.user.id],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -2084,6 +2140,7 @@ class VoteAssignmentPollNamedAutoupdates(VoteAssignmentPollAutoupdatesBaseClass)
|
|||||||
"weight": "1.000000",
|
"weight": "1.000000",
|
||||||
"value": "A",
|
"value": "A",
|
||||||
"user_id": 3,
|
"user_id": 3,
|
||||||
|
"delegated_user_id": None,
|
||||||
"option_id": 1,
|
"option_id": 1,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -2108,9 +2165,9 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
):
|
):
|
||||||
poll_type = AssignmentPoll.TYPE_PSEUDOANONYMOUS
|
poll_type = AssignmentPoll.TYPE_PSEUDOANONYMOUS
|
||||||
|
|
||||||
def test_vote(self):
|
def test_votex(self):
|
||||||
response = self.user_client.post(
|
response = self.user_client.post(
|
||||||
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"1": "A"}
|
reverse("assignmentpoll-vote", args=[self.poll.pk]), {"data": {"1": "A"}}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = AssignmentPoll.objects.get()
|
poll = AssignmentPoll.objects.get()
|
||||||
@ -2136,6 +2193,7 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
"description": self.description,
|
"description": self.description,
|
||||||
"type": AssignmentPoll.TYPE_PSEUDOANONYMOUS,
|
"type": AssignmentPoll.TYPE_PSEUDOANONYMOUS,
|
||||||
"user_has_voted": False,
|
"user_has_voted": False,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
"voted_id": [self.user.id],
|
"voted_id": [self.user.id],
|
||||||
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_CAST,
|
"onehundred_percent_base": AssignmentPoll.PERCENT_BASE_CAST,
|
||||||
"majority_method": AssignmentPoll.MAJORITY_TWO_THIRDS,
|
"majority_method": AssignmentPoll.MAJORITY_TWO_THIRDS,
|
||||||
@ -2159,6 +2217,7 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
"option_id": 1,
|
"option_id": 1,
|
||||||
"pollstate": AssignmentPoll.STATE_STARTED,
|
"pollstate": AssignmentPoll.STATE_STARTED,
|
||||||
"user_id": None,
|
"user_id": None,
|
||||||
|
"delegated_user_id": None,
|
||||||
"value": "A",
|
"value": "A",
|
||||||
"weight": "1.000000",
|
"weight": "1.000000",
|
||||||
},
|
},
|
||||||
@ -2190,6 +2249,7 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"votes_amount": 1,
|
"votes_amount": 1,
|
||||||
"user_has_voted": user == self.user,
|
"user_has_voted": user == self.user,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2245,6 +2305,7 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
"votesinvalid": "0.000000",
|
"votesinvalid": "0.000000",
|
||||||
"votesvalid": "1.000000",
|
"votesvalid": "1.000000",
|
||||||
"user_has_voted": user == self.user,
|
"user_has_voted": user == self.user,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
"voted_id": [self.user.id],
|
"voted_id": [self.user.id],
|
||||||
},
|
},
|
||||||
"assignments/assignment-vote:1": {
|
"assignments/assignment-vote:1": {
|
||||||
@ -2253,6 +2314,7 @@ class VoteAssignmentPollPseudoanonymousAutoupdates(
|
|||||||
"weight": "1.000000",
|
"weight": "1.000000",
|
||||||
"value": "A",
|
"value": "A",
|
||||||
"user_id": None,
|
"user_id": None,
|
||||||
|
"delegated_user_id": None,
|
||||||
"option_id": 1,
|
"option_id": 1,
|
||||||
},
|
},
|
||||||
"assignments/assignment-option:1": {
|
"assignments/assignment-option:1": {
|
||||||
|
@ -167,6 +167,7 @@ class CreateMotionPoll(TestCase):
|
|||||||
"id": 1,
|
"id": 1,
|
||||||
"voted_id": [],
|
"voted_id": [],
|
||||||
"user_has_voted": False,
|
"user_has_voted": False,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(autoupdate[1], [])
|
self.assertEqual(autoupdate[1], [])
|
||||||
@ -610,6 +611,7 @@ class VoteMotionPollAnalog(TestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]),
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"Y": "1",
|
"Y": "1",
|
||||||
"N": "2.35",
|
"N": "2.35",
|
||||||
"A": "-1",
|
"A": "-1",
|
||||||
@ -617,6 +619,7 @@ class VoteMotionPollAnalog(TestCase):
|
|||||||
"votesinvalid": "-2",
|
"votesinvalid": "-2",
|
||||||
"votescast": "-2",
|
"votescast": "-2",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
@ -634,14 +637,23 @@ class VoteMotionPollAnalog(TestCase):
|
|||||||
def test_vote_no_permissions(self):
|
def test_vote_no_permissions(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_vote_no_data(self):
|
||||||
|
self.start_poll()
|
||||||
|
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]), {})
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_vote_missing_data(self):
|
def test_vote_missing_data(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), {"Y": "4", "N": "22.6"}
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": {"Y": "4", "N": "22.6"}},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
@ -649,7 +661,7 @@ class VoteMotionPollAnalog(TestCase):
|
|||||||
def test_vote_wrong_data_format(self):
|
def test_vote_wrong_data_format(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), [1, 2, 5]
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": [1, 2, 5]}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
@ -658,7 +670,7 @@ class VoteMotionPollAnalog(TestCase):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]),
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
{"Y": "some string", "N": "-2", "A": "3"},
|
{"data": {"Y": "some string", "N": "-2", "A": "3"}},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
@ -668,6 +680,7 @@ class VoteMotionPollAnalog(TestCase):
|
|||||||
self.client.post(
|
self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]),
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"Y": "3",
|
"Y": "3",
|
||||||
"N": "1",
|
"N": "1",
|
||||||
"A": "5",
|
"A": "5",
|
||||||
@ -675,12 +688,14 @@ class VoteMotionPollAnalog(TestCase):
|
|||||||
"votesinvalid": "1",
|
"votesinvalid": "1",
|
||||||
"votescast": "-1",
|
"votescast": "-1",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
self.poll.state = 3
|
self.poll.state = 3
|
||||||
self.poll.save()
|
self.poll.save()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]),
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
{
|
{
|
||||||
|
"data": {
|
||||||
"Y": "1",
|
"Y": "1",
|
||||||
"N": "2.35",
|
"N": "2.35",
|
||||||
"A": "-1",
|
"A": "-1",
|
||||||
@ -688,6 +703,7 @@ class VoteMotionPollAnalog(TestCase):
|
|||||||
"votesinvalid": "-2",
|
"votesinvalid": "-2",
|
||||||
"votescast": "3",
|
"votescast": "3",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
@ -749,7 +765,7 @@ class VoteMotionPollNamed(TestCase):
|
|||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "N"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "N"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
@ -773,7 +789,7 @@ class VoteMotionPollNamed(TestCase):
|
|||||||
self.admin.vote_weight = weight = Decimal("3.5")
|
self.admin.vote_weight = weight = Decimal("3.5")
|
||||||
self.admin.save()
|
self.admin.save()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "A"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
@ -799,11 +815,11 @@ class VoteMotionPollNamed(TestCase):
|
|||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "N"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "N"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "A"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
@ -824,38 +840,35 @@ class VoteMotionPollNamed(TestCase):
|
|||||||
config["general_system_enable_anonymous"] = True
|
config["general_system_enable_anonymous"] = True
|
||||||
guest_client = APIClient()
|
guest_client = APIClient()
|
||||||
response = guest_client.post(
|
response = guest_client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "Y"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "Y"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
# TODO: Move to unit tests
|
|
||||||
def test_not_set_vote_values(self):
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
self.poll.votesvalid = Decimal("1")
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
self.poll.votesinvalid = Decimal("1")
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
self.poll.votescast = Decimal("1")
|
|
||||||
|
|
||||||
def test_vote_wrong_state(self):
|
def test_vote_wrong_state(self):
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_vote_wrong_group(self):
|
def test_vote_wrong_group(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_vote_not_present(self):
|
def test_vote_not_present(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
@ -863,7 +876,9 @@ class VoteMotionPollNamed(TestCase):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
@ -872,11 +887,118 @@ class VoteMotionPollNamed(TestCase):
|
|||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), [1, 2, 5]
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": [1, 2, 5]}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def setup_vote_delegation(self, with_delegation=True):
|
||||||
|
""" user -> admin """
|
||||||
|
self.start_poll()
|
||||||
|
self.make_admin_delegate()
|
||||||
|
self.make_admin_present()
|
||||||
|
user, _ = self.create_user()
|
||||||
|
user.groups.add(GROUP_DELEGATE_PK)
|
||||||
|
if with_delegation:
|
||||||
|
user.vote_delegated_to = self.admin
|
||||||
|
user.save()
|
||||||
|
inform_changed_data(self.admin) # put the admin into the cache to update
|
||||||
|
# its vote_delegated_to_id field
|
||||||
|
self.user = user
|
||||||
|
|
||||||
|
def test_vote_delegation(self):
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": "N", "user_id": self.user.pk}, # user not present
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = MotionPoll.objects.get()
|
||||||
|
self.assertEqual(poll.votesvalid, Decimal("1"))
|
||||||
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votescast, Decimal("1"))
|
||||||
|
self.assertEqual(poll.get_votes().count(), 1)
|
||||||
|
option = poll.options.get()
|
||||||
|
self.assertEqual(option.yes, Decimal("0"))
|
||||||
|
self.assertEqual(option.no, Decimal("1"))
|
||||||
|
self.assertEqual(option.abstain, Decimal("0"))
|
||||||
|
vote = option.votes.get()
|
||||||
|
self.assertEqual(vote.user, self.user)
|
||||||
|
self.assertEqual(vote.delegated_user, self.admin)
|
||||||
|
|
||||||
|
autoupdate = self.get_last_autoupdate(user=self.admin)
|
||||||
|
self.assertIn("motions/motion-poll:1", autoupdate[0])
|
||||||
|
self.assertEqual(
|
||||||
|
autoupdate[0]["motions/motion-poll:1"]["user_has_voted_for_delegations"],
|
||||||
|
[self.user.pk],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_vote_delegation_and_self_vote(self):
|
||||||
|
self.test_vote_delegation()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "Y"}
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = MotionPoll.objects.get()
|
||||||
|
self.assertEqual(poll.votesvalid, Decimal("2"))
|
||||||
|
self.assertEqual(poll.votesinvalid, Decimal("0"))
|
||||||
|
self.assertEqual(poll.votescast, Decimal("2"))
|
||||||
|
self.assertEqual(poll.get_votes().count(), 2)
|
||||||
|
option = poll.options.get()
|
||||||
|
self.assertEqual(option.yes, Decimal("1"))
|
||||||
|
self.assertEqual(option.no, Decimal("1"))
|
||||||
|
self.assertEqual(option.abstain, Decimal("0"))
|
||||||
|
vote = option.votes.get(user_id=self.admin.pk)
|
||||||
|
self.assertEqual(vote.user, self.admin)
|
||||||
|
self.assertEqual(vote.delegated_user, self.admin)
|
||||||
|
|
||||||
|
def test_vote_delegation_forbidden(self):
|
||||||
|
self.setup_vote_delegation(False)
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": "N", "user_id": self.user.pk},
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_vote_delegation_not_present(self):
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
self.admin.is_present = False
|
||||||
|
self.admin.save()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": "N", "user_id": self.user.pk},
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
def test_vote_delegation_delegatee_not_in_group(self):
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
self.admin.groups.remove(GROUP_DELEGATE_PK)
|
||||||
|
self.admin.save()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": "N", "user_id": self.user.pk},
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = MotionPoll.objects.get()
|
||||||
|
self.assertEqual(poll.get_votes().count(), 1)
|
||||||
|
vote = poll.get_votes()[0]
|
||||||
|
self.assertEqual(vote.value, "N")
|
||||||
|
self.assertEqual(vote.user, self.user)
|
||||||
|
self.assertEqual(vote.delegated_user, self.admin)
|
||||||
|
|
||||||
|
def test_vote_delegation_delegator_not_in_group(self):
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
self.user.groups.remove(GROUP_DELEGATE_PK)
|
||||||
|
self.user.save()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]),
|
||||||
|
{"data": "N", "user_id": self.user.pk},
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
|
|
||||||
class VoteMotionPollNamedAutoupdates(TestCase):
|
class VoteMotionPollNamedAutoupdates(TestCase):
|
||||||
"""3 important users:
|
"""3 important users:
|
||||||
@ -916,7 +1038,7 @@ class VoteMotionPollNamedAutoupdates(TestCase):
|
|||||||
|
|
||||||
def test_vote(self):
|
def test_vote(self):
|
||||||
response = self.user_client.post(
|
response = self.user_client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "A"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
@ -942,6 +1064,7 @@ class VoteMotionPollNamedAutoupdates(TestCase):
|
|||||||
"options_id": [1],
|
"options_id": [1],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"user_has_voted": False,
|
"user_has_voted": False,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
"voted_id": [self.user.id],
|
"voted_id": [self.user.id],
|
||||||
},
|
},
|
||||||
"motions/motion-vote:1": {
|
"motions/motion-vote:1": {
|
||||||
@ -950,6 +1073,7 @@ class VoteMotionPollNamedAutoupdates(TestCase):
|
|||||||
"weight": "1.000000",
|
"weight": "1.000000",
|
||||||
"value": "A",
|
"value": "A",
|
||||||
"user_id": self.user.id,
|
"user_id": self.user.id,
|
||||||
|
"delegated_user_id": self.user.id,
|
||||||
"option_id": 1,
|
"option_id": 1,
|
||||||
},
|
},
|
||||||
"motions/motion-option:1": {
|
"motions/motion-option:1": {
|
||||||
@ -975,6 +1099,7 @@ class VoteMotionPollNamedAutoupdates(TestCase):
|
|||||||
"weight": "1.000000",
|
"weight": "1.000000",
|
||||||
"value": "A",
|
"value": "A",
|
||||||
"user_id": self.user.id,
|
"user_id": self.user.id,
|
||||||
|
"delegated_user_id": self.user.id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -1001,6 +1126,7 @@ class VoteMotionPollNamedAutoupdates(TestCase):
|
|||||||
"options_id": [1],
|
"options_id": [1],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"user_has_voted": user == self.user,
|
"user_has_voted": user == self.user,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -1055,7 +1181,7 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase):
|
|||||||
|
|
||||||
def test_vote(self):
|
def test_vote(self):
|
||||||
response = self.user_client.post(
|
response = self.user_client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "A"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
@ -1081,6 +1207,7 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase):
|
|||||||
"options_id": [1],
|
"options_id": [1],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"user_has_voted": False,
|
"user_has_voted": False,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
"voted_id": [self.user.id],
|
"voted_id": [self.user.id],
|
||||||
},
|
},
|
||||||
"motions/motion-vote:1": {
|
"motions/motion-vote:1": {
|
||||||
@ -1090,6 +1217,7 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase):
|
|||||||
"weight": "1.000000",
|
"weight": "1.000000",
|
||||||
"value": "A",
|
"value": "A",
|
||||||
"user_id": None,
|
"user_id": None,
|
||||||
|
"delegated_user_id": None,
|
||||||
},
|
},
|
||||||
"motions/motion-option:1": {
|
"motions/motion-option:1": {
|
||||||
"abstain": "1.000000",
|
"abstain": "1.000000",
|
||||||
@ -1122,6 +1250,7 @@ class VoteMotionPollPseudoanonymousAutoupdates(TestCase):
|
|||||||
"options_id": [1],
|
"options_id": [1],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"user_has_voted": user == self.user,
|
"user_has_voted": user == self.user,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1177,7 +1306,7 @@ class VoteMotionPollPseudoanonymous(TestCase):
|
|||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "N"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "N"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
poll = MotionPoll.objects.get()
|
poll = MotionPoll.objects.get()
|
||||||
@ -1199,11 +1328,11 @@ class VoteMotionPollPseudoanonymous(TestCase):
|
|||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "N"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "N"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "A"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "A"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
option = MotionPoll.objects.get().options.get()
|
option = MotionPoll.objects.get().options.get()
|
||||||
@ -1219,7 +1348,7 @@ class VoteMotionPollPseudoanonymous(TestCase):
|
|||||||
config["general_system_enable_anonymous"] = True
|
config["general_system_enable_anonymous"] = True
|
||||||
guest_client = APIClient()
|
guest_client = APIClient()
|
||||||
response = guest_client.post(
|
response = guest_client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), "Y"
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": "Y"}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
@ -1227,21 +1356,27 @@ class VoteMotionPollPseudoanonymous(TestCase):
|
|||||||
def test_vote_wrong_state(self):
|
def test_vote_wrong_state(self):
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_vote_wrong_group(self):
|
def test_vote_wrong_group(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
def test_vote_not_present(self):
|
def test_vote_not_present(self):
|
||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
self.assertHttpStatusVerbose(response, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
@ -1249,7 +1384,9 @@ class VoteMotionPollPseudoanonymous(TestCase):
|
|||||||
self.start_poll()
|
self.start_poll()
|
||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(reverse("motionpoll-vote", args=[self.poll.pk]))
|
response = self.client.post(
|
||||||
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": {}}
|
||||||
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
|
|
||||||
@ -1258,7 +1395,7 @@ class VoteMotionPollPseudoanonymous(TestCase):
|
|||||||
self.make_admin_delegate()
|
self.make_admin_delegate()
|
||||||
self.make_admin_present()
|
self.make_admin_present()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("motionpoll-vote", args=[self.poll.pk]), [1, 2, 5]
|
reverse("motionpoll-vote", args=[self.poll.pk]), {"data": [1, 2, 5]}
|
||||||
)
|
)
|
||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
self.assertFalse(MotionPoll.objects.get().get_votes().exists())
|
||||||
@ -1344,6 +1481,7 @@ class PublishMotionPoll(TestCase):
|
|||||||
"options_id": [1],
|
"options_id": [1],
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"user_has_voted": False,
|
"user_has_voted": False,
|
||||||
|
"user_has_voted_for_delegations": [],
|
||||||
"voted_id": [],
|
"voted_id": [],
|
||||||
},
|
},
|
||||||
"motions/motion-vote:1": {
|
"motions/motion-vote:1": {
|
||||||
@ -1353,6 +1491,7 @@ class PublishMotionPoll(TestCase):
|
|||||||
"weight": "2.000000",
|
"weight": "2.000000",
|
||||||
"value": "N",
|
"value": "N",
|
||||||
"user_id": None,
|
"user_id": None,
|
||||||
|
"delegated_user_id": None,
|
||||||
},
|
},
|
||||||
"motions/motion-option:1": {
|
"motions/motion-option:1": {
|
||||||
"abstain": "0.000000",
|
"abstain": "0.000000",
|
||||||
@ -1495,3 +1634,45 @@ class ResetMotionPoll(TestCase):
|
|||||||
for user in (self.admin, self.user1, self.user2):
|
for user in (self.admin, self.user1, self.user2):
|
||||||
self.assertDeletedAutoupdate(self.vote1, user=user)
|
self.assertDeletedAutoupdate(self.vote1, user=user)
|
||||||
self.assertDeletedAutoupdate(self.vote2, user=user)
|
self.assertDeletedAutoupdate(self.vote2, user=user)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMotionPollWithVoteDelegationAutoupdate(TestCase):
|
||||||
|
def advancedSetUp(self):
|
||||||
|
""" Set up user -> other_user delegation. """
|
||||||
|
self.motion = Motion(
|
||||||
|
title="test_title_dL91JqhMTiQuQLSDRItZ",
|
||||||
|
text="test_text_R7nURdXKVEfEnnJBXJYa",
|
||||||
|
)
|
||||||
|
self.motion.save()
|
||||||
|
|
||||||
|
self.delegate_group = get_group_model().objects.get(pk=GROUP_DELEGATE_PK)
|
||||||
|
|
||||||
|
self.other_user, _ = self.create_user()
|
||||||
|
self.user, user_password = self.create_user()
|
||||||
|
self.user.groups.add(self.delegate_group)
|
||||||
|
self.user.is_present = True
|
||||||
|
self.user.vote_delegated_to = self.other_user
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
self.user_client = APIClient()
|
||||||
|
self.user_client.login(username=self.user.username, password=user_password)
|
||||||
|
|
||||||
|
self.poll = MotionPoll.objects.create(
|
||||||
|
motion=self.motion,
|
||||||
|
title="test_title_Q3EuRaALSCCPJuQ2tMqj",
|
||||||
|
pollmethod="YNA",
|
||||||
|
type=BasePoll.TYPE_NAMED,
|
||||||
|
onehundred_percent_base="YN",
|
||||||
|
majority_method="simple",
|
||||||
|
)
|
||||||
|
self.poll.create_options()
|
||||||
|
self.poll.groups.add(self.delegate_group)
|
||||||
|
self.poll.save()
|
||||||
|
|
||||||
|
def test_start_poll(self):
|
||||||
|
response = self.client.post(reverse("motionpoll-start", args=[self.poll.pk]))
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
# other_user has to receive an autoupdate because he was delegated
|
||||||
|
autoupdate = self.get_last_autoupdate(user=self.other_user)
|
||||||
|
assert "motions/motion-poll:1" in autoupdate[0]
|
||||||
|
@ -24,12 +24,13 @@ def test_user_db_queries():
|
|||||||
"""
|
"""
|
||||||
Tests that only the following db queries are done:
|
Tests that only the following db queries are done:
|
||||||
* 2 requests to get the list of all users and
|
* 2 requests to get the list of all users and
|
||||||
* 1 requests to get the list of all groups.
|
* 1 request to get all vote delegations
|
||||||
|
* 1 request to get the list of all groups.
|
||||||
"""
|
"""
|
||||||
for index in range(10):
|
for index in range(10):
|
||||||
User.objects.create(username=f"user{index}")
|
User.objects.create(username=f"user{index}")
|
||||||
|
|
||||||
assert count_queries(User.get_elements)() == 3
|
assert count_queries(User.get_elements)() == 4
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db(transaction=False)
|
@pytest.mark.django_db(transaction=False)
|
||||||
@ -232,6 +233,188 @@ class UserUpdate(TestCase):
|
|||||||
# The user is not allowed to change some other fields (like last_name).
|
# The user is not allowed to change some other fields (like last_name).
|
||||||
self.assertNotEqual(user.last_name, "New name fae1Bu1Eyeis9eRox4xu")
|
self.assertNotEqual(user.last_name, "New name fae1Bu1Eyeis9eRox4xu")
|
||||||
|
|
||||||
|
def test_update_vote_delegation(self):
|
||||||
|
user = User.objects.create_user(
|
||||||
|
username="non-admin Yd4ejrJXZi4Wn16ugHgY",
|
||||||
|
password="non-admin AQ4Dw2tN9byKpGD4f1gs",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[user.pk]),
|
||||||
|
{"vote_delegated_to_id": self.admin.pk},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
user = User.objects.get(pk=user.pk)
|
||||||
|
self.assertEqual(user.vote_delegated_to_id, self.admin.pk)
|
||||||
|
admin = User.objects.get(username="admin")
|
||||||
|
self.assertEqual(
|
||||||
|
list(admin.vote_delegated_from_users.values_list("id", flat=True)),
|
||||||
|
[user.pk],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_update_vote_delegation_non_admin(self):
|
||||||
|
user = User.objects.create_user(
|
||||||
|
username="non-admin WpBQRSsCg6qNWNtN6bLP",
|
||||||
|
password="non-admin IzsDBt1uoqc2wo5BSUF1",
|
||||||
|
)
|
||||||
|
client = APIClient()
|
||||||
|
client.login(
|
||||||
|
username="non-admin WpBQRSsCg6qNWNtN6bLP",
|
||||||
|
password="non-admin IzsDBt1uoqc2wo5BSUF1",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.patch(
|
||||||
|
reverse("user-detail", args=[user.pk]),
|
||||||
|
{"vote_delegated_to_id": self.admin.pk},
|
||||||
|
)
|
||||||
|
|
||||||
|
# self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
user = User.objects.get(pk=user.pk)
|
||||||
|
self.assertIsNone(user.vote_delegated_to_id)
|
||||||
|
|
||||||
|
def test_update_vote_delegated_to_self(self):
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.admin.pk]),
|
||||||
|
{"vote_delegated_to_id": self.admin.pk},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
admin = User.objects.get(pk=self.admin.pk)
|
||||||
|
self.assertIsNone(admin.vote_delegated_to_id)
|
||||||
|
|
||||||
|
def test_update_vote_delegation_invalid_id(self):
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.admin.pk]),
|
||||||
|
{"vote_delegated_to_id": 42},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
admin = User.objects.get(pk=self.admin.pk)
|
||||||
|
self.assertIsNone(admin.vote_delegated_to_id)
|
||||||
|
|
||||||
|
def test_update_vote_delegated_from_self(self):
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.admin.pk]),
|
||||||
|
{"vote_delegated_from_users_id": [self.admin.pk]},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
admin = User.objects.get(pk=self.admin.pk)
|
||||||
|
self.assertIsNone(admin.vote_delegated_to_id)
|
||||||
|
|
||||||
|
def setup_vote_delegation(self):
|
||||||
|
""" login and setup user -> user2 delegation """
|
||||||
|
self.user, _ = self.create_user()
|
||||||
|
self.user2, _ = self.create_user()
|
||||||
|
self.user.vote_delegated_to = self.user2
|
||||||
|
self.user.save()
|
||||||
|
self.assertEqual(self.user.vote_delegated_to_id, self.user2.pk)
|
||||||
|
|
||||||
|
def test_update_reset_vote_delegated_to(self):
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.user.pk]),
|
||||||
|
{"vote_delegated_to_id": None},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
user = User.objects.get(pk=self.user.pk)
|
||||||
|
self.assertEqual(user.vote_delegated_to_id, None)
|
||||||
|
|
||||||
|
def test_update_reset_vote_delegated_from(self):
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.user2.pk]),
|
||||||
|
{"vote_delegated_from_users_id": None},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
user = User.objects.get(pk=self.user.pk)
|
||||||
|
self.assertEqual(user.vote_delegated_to_id, None)
|
||||||
|
|
||||||
|
def test_update_nested_vote_delegation_1(self):
|
||||||
|
""" user -> user2 -> admin """
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.user2.pk]),
|
||||||
|
{"vote_delegated_to_id": self.admin.pk},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
user2 = User.objects.get(pk=self.user2.pk)
|
||||||
|
self.assertIsNone(user2.vote_delegated_to_id)
|
||||||
|
|
||||||
|
def test_update_nested_vote_delegation_2(self):
|
||||||
|
""" admin -> user -> user2 """
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.admin.pk]),
|
||||||
|
{"vote_delegated_to_id": self.user.pk},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
admin = User.objects.get(pk=self.admin.pk)
|
||||||
|
self.assertIsNone(admin.vote_delegated_to_id)
|
||||||
|
|
||||||
|
def test_update_vote_delegation_autoupdate(self):
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.user.pk]),
|
||||||
|
{"vote_delegated_to_id": self.admin.pk},
|
||||||
|
)
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
autoupdate = self.get_last_autoupdate(user=self.admin)
|
||||||
|
user_au = autoupdate[0].get(f"users/user:{self.user.pk}")
|
||||||
|
self.assertIsNotNone(user_au)
|
||||||
|
self.assertEqual(user_au["vote_delegated_to_id"], self.admin.pk)
|
||||||
|
user2_au = autoupdate[0].get(f"users/user:{self.user2.pk}")
|
||||||
|
self.assertIsNotNone(user2_au)
|
||||||
|
self.assertEqual(user2_au["vote_delegated_from_users_id"], [])
|
||||||
|
admin_au = autoupdate[0].get(f"users/user:{self.admin.pk}")
|
||||||
|
self.assertIsNotNone(admin_au)
|
||||||
|
self.assertEqual(admin_au["vote_delegated_from_users_id"], [self.user.pk])
|
||||||
|
self.assertEqual(autoupdate[1], [])
|
||||||
|
|
||||||
|
def test_update_vote_delegated_from(self):
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.user2.pk]),
|
||||||
|
{"vote_delegated_from_users_id": [self.admin.pk]},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
admin = User.objects.get(pk=self.admin.pk)
|
||||||
|
self.assertEqual(admin.vote_delegated_to_id, self.user2.id)
|
||||||
|
user = User.objects.get(pk=self.user.pk)
|
||||||
|
self.assertIsNone(user.vote_delegated_to_id)
|
||||||
|
|
||||||
|
def test_update_vote_delegated_from_nested_1(self):
|
||||||
|
""" admin -> user -> user2 """
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.user.pk]),
|
||||||
|
{"vote_delegated_from_users_id": [self.admin.pk]},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
admin = User.objects.get(pk=self.admin.pk)
|
||||||
|
self.assertIsNone(admin.vote_delegated_to_id)
|
||||||
|
|
||||||
|
def test_update_vote_delegated_from_nested_2(self):
|
||||||
|
""" user -> user2 -> admin """
|
||||||
|
self.setup_vote_delegation()
|
||||||
|
response = self.client.patch(
|
||||||
|
reverse("user-detail", args=[self.admin.pk]),
|
||||||
|
{"vote_delegated_from_users_id": [self.user2.pk]},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
user2 = User.objects.get(pk=self.user2.pk)
|
||||||
|
self.assertIsNone(user2.vote_delegated_to_id)
|
||||||
|
|
||||||
|
|
||||||
class UserDelete(TestCase):
|
class UserDelete(TestCase):
|
||||||
"""
|
"""
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
from decimal import Decimal
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from openslides.motions.models import Motion, MotionChangeRecommendation
|
from openslides.motions.models import Motion, MotionChangeRecommendation, MotionPoll
|
||||||
|
|
||||||
|
|
||||||
# TODO: test for MotionPoll.set_options()
|
# TODO: test for MotionPoll.set_options()
|
||||||
@ -50,3 +51,25 @@ class MotionChangeRecommendationTest(TestCase):
|
|||||||
other_recommendations
|
other_recommendations
|
||||||
)
|
)
|
||||||
self.assertFalse(collides)
|
self.assertFalse(collides)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionPollAnalogFieldsTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.motion = Motion(
|
||||||
|
title="test_title_OoK9IeChe2Jeib9Deeji",
|
||||||
|
text="test_text_eichui1oobiSeit9aifo",
|
||||||
|
)
|
||||||
|
self.poll = MotionPoll(
|
||||||
|
motion=self.motion,
|
||||||
|
title="test_title_tho8PhiePh8upaex6phi",
|
||||||
|
pollmethod="YNA",
|
||||||
|
type=MotionPoll.TYPE_NAMED,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_not_set_vote_values(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.poll.votesvalid = Decimal("1")
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.poll.votesinvalid = Decimal("1")
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.poll.votescast = Decimal("1")
|
||||||
|
Loading…
Reference in New Issue
Block a user