Rework of assignment
fixes: #1429, #1345 A database recreation is needed
This commit is contained in:
parent
85dfbb185f
commit
c4c9322321
@ -1,7 +1,7 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
@ -95,9 +95,8 @@ class Item(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, MPTTModel):
|
||||
"""
|
||||
Field for generic relation to a related object. Id of the object.
|
||||
"""
|
||||
# TODO: rename it to object_pk
|
||||
|
||||
content_object = generic.GenericForeignKey()
|
||||
content_object = GenericForeignKey()
|
||||
"""
|
||||
Field for generic relation to a related object. General field to the related object.
|
||||
"""
|
||||
|
@ -8,12 +8,12 @@ from .models import Assignment
|
||||
|
||||
|
||||
class AssignmentForm(CssClassMixin, forms.ModelForm):
|
||||
posts = forms.IntegerField(
|
||||
open_posts = forms.IntegerField(
|
||||
min_value=1, initial=1, label=ugettext_lazy("Number of available posts"))
|
||||
|
||||
class Meta:
|
||||
model = Assignment
|
||||
exclude = ('status', 'elected')
|
||||
fields = ('title', 'description', 'open_posts', 'poll_description_default')
|
||||
|
||||
|
||||
class AssignmentRunForm(CssClassMixin, forms.Form):
|
||||
|
@ -8,7 +8,7 @@ class AssignmentMainMenuEntry(MainMenuEntry):
|
||||
Main menu entry for the assignment app.
|
||||
"""
|
||||
verbose_name = ugettext_lazy('Elections')
|
||||
required_permission = 'assignment.can_see_assignment'
|
||||
required_permission = 'assignment.can_see_assignments'
|
||||
default_weight = 40
|
||||
pattern_name = 'assignment_list'
|
||||
icon_css_class = 'icon-assignment'
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.datastructures import SortedDict
|
||||
@ -15,24 +15,36 @@ from openslides.projector.models import SlideMixin
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.models import AbsoluteUrlMixin
|
||||
from openslides.utils.rest_api import RESTModelMixin
|
||||
from openslides.utils.utils import html_strong
|
||||
from openslides.users.models import User
|
||||
|
||||
|
||||
class AssignmentCandidate(RESTModelMixin, models.Model):
|
||||
class AssignmentRelatedUser(RESTModelMixin, models.Model):
|
||||
"""
|
||||
Many2Many table between an assignment and the candidates.
|
||||
Many to Many table between an assignment and user.
|
||||
"""
|
||||
assignment = models.ForeignKey("Assignment")
|
||||
person = models.ForeignKey(User, db_index=True)
|
||||
elected = models.BooleanField(default=False)
|
||||
blocked = models.BooleanField(default=False)
|
||||
STATUS_CANDIDATE = 1
|
||||
STATUS_ELECTED = 2
|
||||
STATUS_BLOCKED = 3
|
||||
STATUSES = (
|
||||
(STATUS_CANDIDATE, ugettext_lazy('candidate')),
|
||||
(STATUS_ELECTED, ugettext_lazy('elected')),
|
||||
(STATUS_BLOCKED, ugettext_lazy('blocked')),
|
||||
)
|
||||
|
||||
assignment = models.ForeignKey(
|
||||
'Assignment',
|
||||
db_index=True,
|
||||
related_name='assignment_related_users')
|
||||
user = models.ForeignKey(User, db_index=True)
|
||||
status = models.IntegerField(
|
||||
choices=STATUSES,
|
||||
default=STATUS_CANDIDATE)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("assignment", "person")
|
||||
unique_together = ('assignment', 'user')
|
||||
|
||||
def __str__(self):
|
||||
return str(self.person)
|
||||
return "%s <-> %s" % (self.assignment, self.user)
|
||||
|
||||
def get_root_rest_element(self):
|
||||
"""
|
||||
@ -44,35 +56,87 @@ class AssignmentCandidate(RESTModelMixin, models.Model):
|
||||
class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
slide_callback_name = 'assignment'
|
||||
|
||||
STATUS = (
|
||||
('sea', ugettext_lazy('Searching for candidates')),
|
||||
('vot', ugettext_lazy('Voting')),
|
||||
('fin', ugettext_lazy('Finished')),
|
||||
PHASE_SEARCH = 1
|
||||
PHASE_VOTING = 2
|
||||
PHASE_FINISHED = 3
|
||||
|
||||
PHASES = (
|
||||
(PHASE_SEARCH, ugettext_lazy('Searching for candidates')),
|
||||
(PHASE_VOTING, ugettext_lazy('Voting')),
|
||||
(PHASE_FINISHED, ugettext_lazy('Finished')),
|
||||
)
|
||||
|
||||
name = models.CharField(max_length=100, verbose_name=ugettext_lazy("Name"))
|
||||
description = models.TextField(null=True, blank=True, verbose_name=ugettext_lazy("Description"))
|
||||
posts = models.PositiveSmallIntegerField(verbose_name=ugettext_lazy("Number of available posts"))
|
||||
title = models.CharField(
|
||||
max_length=100,
|
||||
verbose_name=ugettext_lazy("Title"))
|
||||
"""
|
||||
Title of the assignment.
|
||||
"""
|
||||
|
||||
description = models.TextField(
|
||||
blank=True,
|
||||
verbose_name=ugettext_lazy("Description"))
|
||||
"""
|
||||
Text to describe the assignment.
|
||||
"""
|
||||
|
||||
open_posts = models.PositiveSmallIntegerField(
|
||||
verbose_name=ugettext_lazy("Number of members to be elected"))
|
||||
"""
|
||||
The number of members to be elected.
|
||||
"""
|
||||
|
||||
poll_description_default = models.CharField(
|
||||
max_length=79, null=True, blank=True,
|
||||
max_length=79,
|
||||
blank=True,
|
||||
verbose_name=ugettext_lazy("Default comment on the ballot paper"))
|
||||
status = models.CharField(max_length=3, choices=STATUS, default='sea')
|
||||
"""
|
||||
Default text for the poll description.
|
||||
"""
|
||||
|
||||
phase = models.IntegerField(
|
||||
choices=PHASES,
|
||||
default=PHASE_SEARCH)
|
||||
"""
|
||||
Phase in which the assignment is.
|
||||
"""
|
||||
|
||||
related_users = models.ManyToManyField(
|
||||
User,
|
||||
through='AssignmentRelatedUser')
|
||||
"""
|
||||
Users that a candidates, elected or blocked as candidate.
|
||||
|
||||
See AssignmentRelatedUser for more infos.
|
||||
"""
|
||||
|
||||
tags = models.ManyToManyField(Tag, blank=True)
|
||||
"""
|
||||
Tags for the assignment.
|
||||
"""
|
||||
|
||||
items = GenericRelation(Item)
|
||||
"""
|
||||
Agenda items for this assignment.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('can_see_assignment', ugettext_noop('Can see elections')), # TODO: Add plural s to the codestring
|
||||
('can_nominate_other', ugettext_noop('Can nominate another person')),
|
||||
('can_see_assignments', ugettext_noop('Can see elections')),
|
||||
('can_nominate_other', ugettext_noop('Can nominate another participant')),
|
||||
('can_nominate_self', ugettext_noop('Can nominate oneself')),
|
||||
('can_manage_assignment', ugettext_noop('Can manage elections')), # TODO: Add plural s also to the codestring
|
||||
('can_manage_assignments', ugettext_noop('Can manage elections')),
|
||||
)
|
||||
ordering = ('name',)
|
||||
ordering = ('title', )
|
||||
verbose_name = ugettext_noop('Election')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self, link='detail'):
|
||||
"""
|
||||
Returns absolute url to the assignment instance.
|
||||
"""
|
||||
if link == 'detail':
|
||||
url = reverse('assignment_detail', args=[str(self.pk)])
|
||||
elif link == 'update':
|
||||
@ -80,125 +144,115 @@ class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
elif link == 'delete':
|
||||
url = reverse('assignment_delete', args=[str(self.pk)])
|
||||
else:
|
||||
url = super(Assignment, self).get_absolute_url(link)
|
||||
url = super().get_absolute_url(link)
|
||||
return url
|
||||
|
||||
def get_slide_context(self, **context):
|
||||
context.update({
|
||||
'polls': self.poll_set.filter(published=True),
|
||||
'vote_results': self.vote_results(only_published=True)})
|
||||
return super(Assignment, self).get_slide_context(**context)
|
||||
|
||||
def set_status(self, status):
|
||||
status_dict = dict(self.STATUS)
|
||||
if status not in status_dict:
|
||||
raise ValueError(_('%s is not a valid status.') % html_strong(status))
|
||||
if self.status == status:
|
||||
raise ValueError(
|
||||
_('The election status is already %s.') % html_strong(status_dict[status]))
|
||||
self.status = status
|
||||
self.save()
|
||||
|
||||
def run(self, candidate, person=None):
|
||||
"""
|
||||
run for a vote
|
||||
candidate: The user who will be a candidate
|
||||
person: The user who chooses the candidate
|
||||
Retuns the context to generate the assignment slide.
|
||||
"""
|
||||
# TODO: don't make any permission checks here.
|
||||
# Use other Exceptions
|
||||
if self.is_candidate(candidate):
|
||||
raise NameError(_('<b>%s</b> is already a candidate.') % candidate)
|
||||
if not person.has_perm("assignment.can_manage_assignment") and self.status != 'sea':
|
||||
raise NameError(_('The candidate list is already closed.'))
|
||||
candidature = self.assignment_candidates.filter(person=candidate)
|
||||
if candidature and candidate != person and \
|
||||
not person.has_perm("assignment.can_manage_assignment"):
|
||||
# if the candidature is blocked and anotherone tries to run the
|
||||
# candidate
|
||||
raise NameError(
|
||||
_('%s does not want to be a candidate.') % candidate)
|
||||
elif candidature:
|
||||
candidature[0].blocked = False
|
||||
candidature[0].save()
|
||||
else:
|
||||
AssignmentCandidate(assignment=self, person=candidate).save()
|
||||
|
||||
def delrun(self, candidate, blocked=True):
|
||||
"""
|
||||
stop running for a vote
|
||||
"""
|
||||
try:
|
||||
candidature = self.assignment_candidates.get(person=candidate)
|
||||
except AssignmentCandidate.DoesNotExist:
|
||||
raise Exception(_('%s is no candidate') % candidate)
|
||||
|
||||
if not candidature.blocked:
|
||||
if blocked:
|
||||
candidature.blocked = True
|
||||
candidature.save()
|
||||
else:
|
||||
candidature.delete()
|
||||
else:
|
||||
candidature.delete()
|
||||
|
||||
def is_candidate(self, person):
|
||||
"""
|
||||
return True, if person is a candidate.
|
||||
"""
|
||||
try:
|
||||
return self.assignment_candidates.filter(person=person).exclude(blocked=True).exists()
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def is_blocked(self, person):
|
||||
"""
|
||||
return True, if the person is blockt for candidature.
|
||||
"""
|
||||
return self.assignment_candidates.filter(person=person).filter(blocked=True).exists()
|
||||
|
||||
@property
|
||||
def assignment_candidates(self):
|
||||
return AssignmentCandidate.objects.filter(assignment=self)
|
||||
return super().get_slide_context(
|
||||
polls=self.polls.filter(published=True),
|
||||
vote_results=self.vote_results(only_published=True),
|
||||
**context)
|
||||
|
||||
@property
|
||||
def candidates(self):
|
||||
return self.get_participants(only_candidate=True)
|
||||
"""
|
||||
Queryset that represents the candidates for the assignment.
|
||||
"""
|
||||
return self.related_users.filter(
|
||||
assignmentrelateduser__status=AssignmentRelatedUser.STATUS_CANDIDATE)
|
||||
|
||||
@property
|
||||
def elected(self):
|
||||
return self.get_participants(only_elected=True)
|
||||
"""
|
||||
Queryset that represents all elected users for the assignment.
|
||||
"""
|
||||
return self.related_users.filter(
|
||||
assignmentrelateduser__status=AssignmentRelatedUser.STATUS_ELECTED)
|
||||
|
||||
def get_participants(self, only_elected=False, only_candidate=False):
|
||||
candidates = self.assignment_candidates.exclude(blocked=True)
|
||||
@property
|
||||
def blocked(self):
|
||||
"""
|
||||
Queryset that represents all blocked users for the assignment.
|
||||
"""
|
||||
return self.related_users.filter(
|
||||
assignmentrelateduser__status=AssignmentRelatedUser.STATUS_BLOCKED)
|
||||
|
||||
assert not (only_elected and only_candidate)
|
||||
def is_candidate(self, user):
|
||||
"""
|
||||
Returns True if user is a candidate.
|
||||
|
||||
if only_elected:
|
||||
candidates = candidates.filter(elected=True)
|
||||
Costs one database query.
|
||||
"""
|
||||
return self.candidates.filter(pk=user.pk).exists()
|
||||
|
||||
if only_candidate:
|
||||
candidates = candidates.filter(elected=False)
|
||||
def is_elected(self, user):
|
||||
"""
|
||||
Returns True if the user is elected for this assignment.
|
||||
|
||||
# TODO: rewrite this with a queryset
|
||||
participants = []
|
||||
for candidate in candidates.all():
|
||||
participants.append(candidate.person)
|
||||
return participants
|
||||
Costs one database query.
|
||||
"""
|
||||
return self.elected.filter(pk=user.pk).exists()
|
||||
|
||||
def set_elected(self, person, value=True):
|
||||
candidate = self.assignment_candidates.get(person=person)
|
||||
candidate.elected = value
|
||||
candidate.save()
|
||||
def is_blocked(self, user):
|
||||
"""
|
||||
Returns True if the user is blockt for candidature.
|
||||
|
||||
def is_elected(self, person):
|
||||
return person in self.elected
|
||||
Costs one database query.
|
||||
"""
|
||||
return self.blocked.filter(pk=user.pk).exists()
|
||||
|
||||
def gen_poll(self):
|
||||
def set_candidate(self, user):
|
||||
"""
|
||||
Adds the user as candidate.
|
||||
"""
|
||||
related_user, __ = self.assignment_related_users.update_or_create(
|
||||
user=user,
|
||||
defaults={'status': AssignmentRelatedUser.STATUS_CANDIDATE})
|
||||
|
||||
def set_elected(self, user):
|
||||
"""
|
||||
Makes user an elected user for this assignment.
|
||||
"""
|
||||
related_user, __ = self.assignment_related_users.update_or_create(
|
||||
user=user,
|
||||
defaults={'status': AssignmentRelatedUser.STATUS_ELECTED})
|
||||
|
||||
def set_blocked(self, user):
|
||||
"""
|
||||
Block user from this assignment, so he can not get an candidate.
|
||||
"""
|
||||
related_user, __ = self.assignment_related_users.update_or_create(
|
||||
user=user,
|
||||
defaults={'status': AssignmentRelatedUser.STATUS_BLOCKED})
|
||||
|
||||
def delete_related_user(self, user):
|
||||
"""
|
||||
Delete the connection from the assignment to the user.
|
||||
"""
|
||||
self.assignment_related_users.filter(user=user).delete()
|
||||
|
||||
def set_phase(self, phase):
|
||||
"""
|
||||
Sets the phase attribute of the assignment.
|
||||
|
||||
Raises a ValueError if the phase is not valide.
|
||||
"""
|
||||
if phase not in dict(self.PHASES):
|
||||
raise ValueError("Invalid phase %s" % phase)
|
||||
|
||||
self.phase = phase
|
||||
|
||||
def create_poll(self):
|
||||
"""
|
||||
Creates an new poll for the assignment and adds all candidates to all
|
||||
lists of speakers of related agenda items.
|
||||
"""
|
||||
candidates = self.candidates.all()
|
||||
|
||||
# Find out the method of the election
|
||||
if config['assignment_poll_vote_values'] == 'votes':
|
||||
yesnoabstain = False
|
||||
elif config['assignment_poll_vote_values'] == 'yesnoabstain':
|
||||
@ -206,19 +260,20 @@ class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
else:
|
||||
# config['assignment_poll_vote_values'] == 'auto'
|
||||
# candidates <= available posts -> yes/no/abstain
|
||||
if len(self.candidates) <= (self.posts - len(self.elected)):
|
||||
if len(candidates) <= (self.open_posts - self.elected.count()):
|
||||
yesnoabstain = True
|
||||
else:
|
||||
yesnoabstain = False
|
||||
|
||||
poll = AssignmentPoll.objects.create(
|
||||
assignment=self,
|
||||
# Create the poll with the candidates.
|
||||
poll = self.polls.create(
|
||||
description=self.poll_description_default,
|
||||
yesnoabstain=yesnoabstain)
|
||||
poll.set_options([{'candidate': person} for person in self.candidates])
|
||||
poll.set_options({'candidate': user} for user in candidates)
|
||||
|
||||
items = Item.objects.filter(content_type=ContentType.objects.get_for_model(Assignment), object_id=self.pk)
|
||||
for item in items:
|
||||
# Add all candidates to all agenda items for this assignment
|
||||
# TODO: Try to do this in a bulk create
|
||||
for item in self.items.all():
|
||||
for candidate in self.candidates:
|
||||
try:
|
||||
Speaker.objects.add(candidate, item)
|
||||
@ -231,14 +286,15 @@ class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
|
||||
def vote_results(self, only_published):
|
||||
"""
|
||||
returns a table represented as a list with all candidates from all
|
||||
Returns a table represented as a list with all candidates from all
|
||||
related polls and their vote results.
|
||||
"""
|
||||
vote_results_dict = SortedDict()
|
||||
# All polls related to this assigment
|
||||
polls = self.poll_set.all()
|
||||
|
||||
polls = self.polls.all()
|
||||
if only_published:
|
||||
polls = polls.filter(published=True)
|
||||
|
||||
# All PollOption-Objects related to this assignment
|
||||
options = []
|
||||
for poll in polls:
|
||||
@ -263,7 +319,7 @@ class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||
return vote_results_dict
|
||||
|
||||
def get_agenda_title(self):
|
||||
return self.name
|
||||
return str(self)
|
||||
|
||||
def get_agenda_title_supplement(self):
|
||||
return '(%s)' % _('Assignment')
|
||||
@ -296,15 +352,14 @@ class AssignmentOption(RESTModelMixin, BaseOption):
|
||||
|
||||
class AssignmentPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin,
|
||||
PublishPollMixin, AbsoluteUrlMixin, BasePoll):
|
||||
|
||||
slide_callback_name = 'assignmentpoll'
|
||||
"""Name of the callback for the slide system."""
|
||||
|
||||
option_class = AssignmentOption
|
||||
assignment = models.ForeignKey(Assignment, related_name='poll_set')
|
||||
|
||||
assignment = models.ForeignKey(Assignment, related_name='polls')
|
||||
yesnoabstain = models.BooleanField(default=False)
|
||||
description = models.CharField(
|
||||
max_length=79, null=True, blank=True,
|
||||
max_length=79,
|
||||
blank=True,
|
||||
verbose_name=ugettext_lazy("Comment on the ballot paper"))
|
||||
|
||||
def __str__(self):
|
||||
@ -321,7 +376,7 @@ class AssignmentPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin,
|
||||
elif link == 'delete':
|
||||
url = reverse('assignmentpoll_delete', args=[str(self.pk)])
|
||||
else:
|
||||
url = super(AssignmentPoll, self).get_absolute_url(link)
|
||||
url = super().get_absolute_url(link)
|
||||
return url
|
||||
|
||||
def get_assignment(self):
|
||||
@ -334,17 +389,17 @@ class AssignmentPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin,
|
||||
return [ugettext_noop('Votes')]
|
||||
|
||||
def get_ballot(self):
|
||||
return self.assignment.poll_set.filter(id__lte=self.id).count()
|
||||
return self.assignment.polls.filter(id__lte=self.pk).count()
|
||||
|
||||
def get_percent_base_choice(self):
|
||||
return config['assignment_poll_100_percent_base']
|
||||
|
||||
def append_pollform_fields(self, fields):
|
||||
fields.append('description')
|
||||
super(AssignmentPoll, self).append_pollform_fields(fields)
|
||||
super().append_pollform_fields(fields)
|
||||
|
||||
def get_slide_context(self, **context):
|
||||
return super(AssignmentPoll, self).get_slide_context(poll=self)
|
||||
return super().get_slide_context(poll=self)
|
||||
|
||||
def get_root_rest_element(self):
|
||||
"""
|
||||
|
@ -2,7 +2,7 @@ from django.utils.translation import ugettext_lazy
|
||||
|
||||
from openslides.utils.personal_info import PersonalInfo
|
||||
|
||||
from .models import Assignment
|
||||
from .models import Assignment, AssignmentRelatedUser
|
||||
|
||||
|
||||
class AssignmentPersonalInfo(PersonalInfo):
|
||||
@ -13,6 +13,5 @@ class AssignmentPersonalInfo(PersonalInfo):
|
||||
default_weight = 40
|
||||
|
||||
def get_queryset(self):
|
||||
return Assignment.objects.filter(
|
||||
assignmentcandidate__person=self.request.user,
|
||||
assignmentcandidate__blocked=False)
|
||||
return (Assignment.objects.filter(assignment_related_users__user=self.request.user)
|
||||
.exclude(assignment_related_users__status=AssignmentRelatedUser.STATUS_BLOCKED))
|
||||
|
@ -3,23 +3,22 @@ from openslides.utils.rest_api import serializers
|
||||
from .models import (
|
||||
models,
|
||||
Assignment,
|
||||
AssignmentCandidate,
|
||||
AssignmentRelatedUser,
|
||||
AssignmentOption,
|
||||
AssignmentPoll,
|
||||
AssignmentVote)
|
||||
|
||||
|
||||
class AssignmentCandidateSerializer(serializers.ModelSerializer):
|
||||
class AssignmentRelatedUserSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Serializer for assignment.models.AssignmentCandidate objects.
|
||||
Serializer for assignment.models.AssignmentRelatedUser objects.
|
||||
"""
|
||||
class Meta:
|
||||
model = AssignmentCandidate
|
||||
model = AssignmentRelatedUser
|
||||
fields = (
|
||||
'id',
|
||||
'person',
|
||||
'elected',
|
||||
'blocked',)
|
||||
'user',
|
||||
'status')
|
||||
|
||||
|
||||
class AssignmentVoteSerializer(serializers.ModelSerializer):
|
||||
@ -103,8 +102,8 @@ class AssignmentFullSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Serializer for assignment.models.Assignment objects. With all polls.
|
||||
"""
|
||||
assignmentcandidate_set = AssignmentCandidateSerializer(many=True, read_only=True)
|
||||
poll_set = AssignmentAllPollSerializer(many=True, read_only=True)
|
||||
related_users = AssignmentRelatedUserSerializer(many=True, read_only=True)
|
||||
polls = AssignmentAllPollSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Assignment
|
||||
@ -114,9 +113,9 @@ class AssignmentFullSerializer(serializers.ModelSerializer):
|
||||
'description',
|
||||
'posts',
|
||||
'status',
|
||||
'assignmentcandidate_set',
|
||||
'related_users',
|
||||
'poll_description_default',
|
||||
'poll_set',
|
||||
'polls',
|
||||
'tags',)
|
||||
|
||||
|
||||
@ -124,7 +123,8 @@ class AssignmentShortSerializer(AssignmentFullSerializer):
|
||||
"""
|
||||
Serializer for assignment.models.Assignment objects. Without unpublished poll.
|
||||
"""
|
||||
poll_set = AssignmentShortPollSerializer(many=True, read_only=True)
|
||||
related_users = AssignmentRelatedUserSerializer(many=True, read_only=True)
|
||||
polls = AssignmentShortPollSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Assignment
|
||||
@ -134,7 +134,7 @@ class AssignmentShortSerializer(AssignmentFullSerializer):
|
||||
'description',
|
||||
'posts',
|
||||
'status',
|
||||
'assignmentcandidate_set',
|
||||
'related_users',
|
||||
'poll_description_default',
|
||||
'poll_set',
|
||||
'polls',
|
||||
'tags',)
|
||||
|
@ -35,13 +35,13 @@
|
||||
<span class="glyphicon glyphicon-facetime-video" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.assignment.can_manage_assignment or perms.agenda.can_manage_agenda %}
|
||||
{% if perms.assignment.can_manage_assignments or perms.agenda.can_manage_agenda %}
|
||||
<div class="btn-group">
|
||||
<a data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">
|
||||
<span class="optional-small">{% trans 'More actions' %}</span> <span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<!-- edit -->
|
||||
<li>
|
||||
<a href="{{ assignment|absolute_url:'update' }}">
|
||||
@ -88,31 +88,27 @@
|
||||
{% endif %}
|
||||
<br>
|
||||
<!-- Candidates -->
|
||||
{% if assignment.status != "fin" %}
|
||||
{% if assignment.phase != assignment.PHASE_FINISHED %}
|
||||
<h4>{% trans "Candidates" %}</h4>
|
||||
<ol>
|
||||
{% for person in assignment.get_participants %}
|
||||
{% for person in assignment.candidates %}
|
||||
<li>
|
||||
<a href="{{ person|absolute_url }}">{{ person }}</a>
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if assignment.status == "sea" or assignment.status == "vot" %}
|
||||
<a href="{% url 'assignment_delother' assignment.id person.pk %}"
|
||||
class="btn btn-default btn-xs"
|
||||
rel="tooltip" data-original-title="{% trans 'Remove candidate' %}">
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<a href="{% url 'assignment_del_candidate_other' assignment.id person.pk %}"
|
||||
class="btn btn-default btn-xs"
|
||||
rel="tooltip" data-original-title="{% trans 'Remove candidate' %}">
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if person in assignment.elected %}
|
||||
| <b>{% trans "elected" %}</b>
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if assignment.status == "sea" or assignment.status == "vot" %}
|
||||
<a href="{% url 'assignment_user_not_elected' assignment.id person.pk %}"
|
||||
class="btn btn-default btn-xs"
|
||||
rel="tooltip" data-original-title="{% trans 'Mark candidate as not elected' %}">
|
||||
<span class="glyphicon glyphicon-ban-circle" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<a href="{% url 'assignment_user_not_elected' assignment.id person.pk %}"
|
||||
class="btn btn-default btn-xs"
|
||||
rel="tooltip" data-original-title="{% trans 'Mark candidate as not elected' %}">
|
||||
<span class="glyphicon glyphicon-ban-circle" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
@ -122,18 +118,18 @@
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% if assignment.status == "sea" or perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||
{% if assignment.phase == assignment.PHASE_SEARCH or perms.assignment.can_manage_assignments and assignment.phase == assignment.PHASE_VOTING %}
|
||||
{% if perms.assignment.can_nominate_self or perms.assignment.can_nominate_other %}
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% if perms.assignment.can_nominate_self %}
|
||||
<p>
|
||||
{% if user_is_candidate %}
|
||||
<a href='{% url 'assignment_delrun' assignment.id %}' class="btn btn-default btn-sm">
|
||||
<a href='{% url 'assignment_del_candidate' assignment.id %}' class="btn btn-default btn-sm">
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
{% trans 'Withdraw self candidature' %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href='{% url 'assignment_run' assignment.id %}' class="btn btn-default btn-sm">
|
||||
<a href='{% url 'assignment_candidate' assignment.id %}' class="btn btn-default btn-sm">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
{% trans 'Self candidature' %}
|
||||
</a>
|
||||
@ -161,13 +157,13 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if perms.assignment.can_manage_assignment and blocked_candidates and assignment.status != "fin" %}
|
||||
{% if perms.assignment.can_manage_assignments and blocked_candidates and assignment.phase != assignment.PHASE_FINISHED %}
|
||||
<h4>{% trans "Blocked Candidates" %}</h4>
|
||||
<ul>
|
||||
{% for person in blocked_candidates %}
|
||||
<li>
|
||||
<a href="{{ person|absolute_url }}">{{ person }}</a>
|
||||
<a class="btn btn-mini" href="{% url 'assignment_delother' assignment.id person.pk %}"
|
||||
<a class="btn btn-mini" href="{% url 'assignment_del_candidate_other' assignment.id person.pk %}"
|
||||
rel="tooltip" data-original-title="{% trans 'Remove candidate' %}">
|
||||
<i class="icon-ban-circle"></i>
|
||||
</a>
|
||||
@ -180,7 +176,7 @@
|
||||
|
||||
|
||||
<!-- Election result -->
|
||||
{% if assignment.status != "sea" or polls.exists %}
|
||||
{% if assignment.phase != assignment.PHASE_SEARCH or polls.exists %}
|
||||
<h4>{% trans "Election result" %}</h4>
|
||||
{% if polls.exists %}
|
||||
<table id="election-result-table" class="table table-striped table-bordered">
|
||||
@ -188,11 +184,11 @@
|
||||
<th>{% trans "Candidates" %}</th>
|
||||
{% for poll in polls %}
|
||||
<th style="white-space: nowrap;" class="col-sm-1">
|
||||
{% if perms.assignment.can_manage_assignment %}<p class="text-center">{% endif %}
|
||||
{% if perms.assignment.can_manage_assignments %}<p class="text-center">{% endif %}
|
||||
{{ poll.get_ballot|ordinal|safe }} {% trans 'ballot' %}
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<a class="publish_link btn btn-sm btn-danger {% if poll.published %}btn-primary{% endif %}"
|
||||
href="{% url 'assignmentpoll_publish_status' poll.id %}"
|
||||
href="{% url 'assignmentpoll_publish_poll' poll.id %}"
|
||||
rel="tooltip" data-original-title="{% trans 'Publish result' %}">
|
||||
{% if poll.published %}
|
||||
<i class="icon-checked-new_white"></i>
|
||||
@ -217,7 +213,7 @@
|
||||
{% endif %}
|
||||
</th>
|
||||
{% endfor %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignments and assignment.phase == assignment.PHASE_VOTING %}
|
||||
<th class="col-sm-1 nobr">
|
||||
<a href="{% url 'assignmentpoll_create' assignment.pk %}" class="btn btn-default btn-sm">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
@ -230,7 +226,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
{% if candidate in assignment.elected %}
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<a class="election_link elected tooltip-bottom" href="{% url 'assignment_user_not_elected' assignment.id candidate.pk %}"
|
||||
data-original-title="{% trans 'Mark candidate as elected' %}"></a>
|
||||
{% else %}
|
||||
@ -239,7 +235,7 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<a class="election_link tooltip-bottom" href="{% url 'assignment_user_elected' assignment.id candidate.pk %}"
|
||||
data-original-title="{% trans 'Mark candidate as elected' %}"></a>
|
||||
{% endif %}
|
||||
@ -261,7 +257,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignments and assignment.phase == assignment.PHASE_VOTING %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
@ -269,7 +265,7 @@
|
||||
<tr>
|
||||
<td>{% trans 'Valid votes' %}</td>
|
||||
{% for poll in polls %}
|
||||
{% if poll.published or perms.assignment.can_manage_assignment %}
|
||||
{% if poll.published or perms.assignment.can_manage_assignments %}
|
||||
<td style="white-space:nowrap;">
|
||||
{% if poll.has_votes %}
|
||||
<img src="{% static 'img/voting-yes-grey.png' %}" class="tooltip-left" data-original-title="{% trans 'Valid votes' %}">
|
||||
@ -278,14 +274,14 @@
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignments and assignment.phase == assignment.PHASE_VOTING %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Invalid votes' %}</td>
|
||||
{% for poll in polls %}
|
||||
{% if poll.published or perms.assignment.can_manage_assignment %}
|
||||
{% if poll.published or perms.assignment.can_manage_assignments %}
|
||||
<td style="white-space:nowrap;">
|
||||
{% if poll.has_votes %}
|
||||
<img src="{% static 'img/voting-invalid.png' %}" class="tooltip-left" data-original-title="{% trans 'Invalid votes' %}">
|
||||
@ -294,14 +290,14 @@
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignments and assignment.phase == assignment.PHASE_VOTING %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
<tr class="info total">
|
||||
<td>{% trans 'Votes cast' %}</td>
|
||||
{% for poll in polls %}
|
||||
{% if poll.published or perms.assignment.can_manage_assignment %}
|
||||
{% if poll.published or perms.assignment.can_manage_assignments %}
|
||||
<td style="white-space:nowrap;">
|
||||
{% if poll.has_votes %}
|
||||
<img src="{% static 'img/voting-total.png' %}" class="tooltip-left" data-original-title="{% trans 'Votes cast' %}">
|
||||
@ -310,14 +306,14 @@
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignments and assignment.phase == assignment.PHASE_VOTING %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</table>
|
||||
{% else %}
|
||||
<i>{% trans "No ballots available." %}</i>
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
|
||||
{% if assignment.candidates and perms.assignment.can_manage_assignments and assignment.phase == assignment.PHASE_VOTING %}
|
||||
<p>
|
||||
<a href='{% url 'assignmentpoll_create' assignment.id %}' class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
@ -332,25 +328,25 @@
|
||||
<div class="col-sm-3">
|
||||
<div class="well">
|
||||
<!-- Text -->
|
||||
<h5>{% trans "Status" %}:</h5>
|
||||
{% trans assignment.get_status_display %}
|
||||
<h5>{% trans "Phases" %}:</h5>
|
||||
{% trans assignment.get_phase_display %}
|
||||
<!-- Posts -->
|
||||
<h5>{% trans "Number of available posts" %}:</h5>
|
||||
{{ assignment.posts }}
|
||||
</div> <!--/well-->
|
||||
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<div class="well">
|
||||
<h4>{% trans "Change status" %}:</h4>
|
||||
<h4>{% trans "Change phase" %}:</h4>
|
||||
<div class="btn-group btn-group-vertical" data-toggle="buttons-radio">
|
||||
<a href="{% url 'assignment_set_status' assignment.id 'sea' %}"
|
||||
class="btn btn-default btn-sm {% if 'sea' in assignment.status %}active{% endif %}">
|
||||
<a href="{% url 'assignment_set_phase' assignment.id assignment.PHASE_SEARCH %}"
|
||||
class="btn btn-default btn-sm {% if assignment.phase == assignment.PHASE_SEARCH %}active{% endif %}">
|
||||
{% trans 'Searching for candidates' %}</a>
|
||||
<a href="{% url 'assignment_set_status' assignment.id 'vot' %}"
|
||||
class="btn btn-default btn-sm {% if 'vot' in assignment.status %}active{% endif %}">
|
||||
<a href="{% url 'assignment_set_phase' assignment.id assignment.PHASE_VOTING %}"
|
||||
class="btn btn-default btn-sm {% if assignment.phase == assignment.PHASE_VOTING %}active{% endif %}">
|
||||
{% trans 'Voting' %}</a>
|
||||
<a href="{% url 'assignment_set_status' assignment.id 'fin' %}"
|
||||
class="btn btn-default btn-sm {% if 'fin' in assignment.status %}active{% endif %}">
|
||||
<a href="{% url 'assignment_set_phase' assignment.id assignment.PHASE_FINISHED %}"
|
||||
class="btn btn-default btn-sm {% if assignment.phase == assignment.PHASE_FINISHED %}active{% endif %}">
|
||||
{% trans 'Finished' %}</a>
|
||||
</div>
|
||||
</div> <!--/well-->
|
||||
|
@ -9,7 +9,7 @@
|
||||
{% block content %}
|
||||
<h1>{% trans "Elections" %}
|
||||
<small class="pull-right">
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<a href="{% url 'assignment_create' %}" class="btn btn-primary btn-sm"
|
||||
rel="tooltip" data-original-title="{% trans 'New election' %}">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
@ -23,7 +23,7 @@
|
||||
<span class="optional-small"> {% trans 'Tags' %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.assignment.can_see_assignment %}
|
||||
{% if perms.assignment.can_see_assignments %}
|
||||
<a href="{% url 'assignment_list_pdf' %}" class="btn btn-default btn-sm"
|
||||
rel="tooltip" data-original-title="{% trans 'Print all elections as PDF' %}" target="_blank">
|
||||
<span class="glyphicon glyphicon-print" aria-hidden="true"></span>
|
||||
@ -38,8 +38,8 @@
|
||||
<tr>
|
||||
<th>{% trans "Election" %}</th>
|
||||
<th class="optional">{% trans "Candidates" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
{% if perms.assignment.can_manage_assignment or perms.core.can_manage_projector %}
|
||||
<th>{% trans "Phase" %}</th>
|
||||
{% if perms.assignment.can_manage_assignments or perms.core.can_manage_projector %}
|
||||
<th class="mini_width">{% trans "Actions" %}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
@ -54,16 +54,16 @@
|
||||
<td class="optional">
|
||||
<!-- posts -->
|
||||
{% trans "Posts" context "Number of searched candidates for an election" %}:
|
||||
<span class="badge badge-info">{{ object.posts }}</span>
|
||||
<span class="badge badge-info">{{ object.open_posts }}</span>
|
||||
<!-- candidates -->
|
||||
{% if object.status != 'fin' %}
|
||||
| {% trans "Candidates" %}: <span class="badge badge-warning">{{ object.get_participants|length }}</span>
|
||||
{% if object.phase != object.PHASE_FINISHED %}
|
||||
| {% trans "Candidates" %}: <span class="badge badge-warning">{{ object.candidates.count }}</span>
|
||||
{% endif %}
|
||||
<!-- elected candidates -->
|
||||
| {% trans "Elected" %}: <span class="badge badge-success">{{ object.elected|length }}</span>
|
||||
| {% trans "Elected" %}: <span class="badge badge-success">{{ object.elected.count }}</span>
|
||||
</td>
|
||||
<td><span class="label label-info">{{ object.get_status_display }}</status></td>
|
||||
{% if perms.assignment.can_manage_assignment or perms.core.can_manage_projector %}
|
||||
<td><span class="label label-info">{{ object.get_phase_display }}</span></td>
|
||||
{% if perms.assignment.can_manage_assignments or perms.core.can_manage_projector %}
|
||||
<td>
|
||||
<span style="width: 1px; white-space: nowrap;">
|
||||
{% if perms.core.can_manage_projector %}
|
||||
@ -73,7 +73,7 @@
|
||||
<span class="glyphicon glyphicon-facetime-video" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<a href="{{ object|absolute_url:'update' }}"
|
||||
rel="tooltip" data-original-title="{% trans 'Edit' %}"
|
||||
class="btn btn-default btn-sm">
|
||||
|
@ -11,7 +11,7 @@
|
||||
rel="tooltip" data-original-title="{% trans 'Show' %}">
|
||||
<i class="icon-facetime-video {% if assignment.is_active_slide %}icon-white{% endif %}"></i>
|
||||
</a>
|
||||
{% if perms.assignment.can_manage_assignment %}
|
||||
{% if perms.assignment.can_manage_assignments %}
|
||||
<a href="{{ assignment|absolute_url:'update' }}"
|
||||
rel="tooltip" data-original-title="{% trans 'Edit' %}" class="btn btn-mini right">
|
||||
<i class="icon-pencil"></i>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% load i18n %}
|
||||
{% load highlight %}
|
||||
|
||||
{% if perms.assignment.can_see_assignment %}
|
||||
{% if perms.assignment.can_see_assignments %}
|
||||
<li>
|
||||
<a href="{{ result.object.get_absolute_url }}">{{ result.object }}</a><br>
|
||||
<span class="app">{% trans "Election" %}</a></span><br>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{{ object.name }}
|
||||
{{ object.title }}
|
||||
{{ object.description }}
|
||||
{{ object.candidates }}
|
||||
{{ object.tags.all }}
|
||||
|
@ -24,21 +24,21 @@ urlpatterns = patterns(
|
||||
views.AssignmentDeleteView.as_view(),
|
||||
name='assignment_delete'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/setstatus/(?P<status>[a-z]{3})/$',
|
||||
views.AssignmentSetStatusView.as_view(),
|
||||
name='assignment_set_status'),
|
||||
url(r'^(?P<pk>\d+)/set_phase/(?P<phase>\d+)/$',
|
||||
views.AssignmentSetPhaseView.as_view(),
|
||||
name='assignment_set_phase'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/run/$',
|
||||
views.AssignmentRunView.as_view(),
|
||||
name='assignment_run'),
|
||||
url(r'^(?P<pk>\d+)/candidate/$',
|
||||
views.AssignmentCandidateView.as_view(),
|
||||
name='assignment_candidate'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/delrun/$',
|
||||
views.AssignmentRunDeleteView.as_view(),
|
||||
name='assignment_delrun'),
|
||||
url(r'^(?P<pk>\d+)/delete_candidate/$',
|
||||
views.AssignmentDeleteCandidateshipView.as_view(),
|
||||
name='assignment_del_candidate'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/delother/(?P<user_id>[^/]+)/$',
|
||||
views.AssignmentRunOtherDeleteView.as_view(),
|
||||
name='assignment_delother'),
|
||||
url(r'^(?P<pk>\d+)/delother/(?P<user_pk>[^/]+)/$',
|
||||
views.AssignmentDeleteCandidateshipOtherView.as_view(),
|
||||
name='assignment_del_candidate_other'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/agenda/$',
|
||||
views.CreateRelatedAgendaItemView.as_view(),
|
||||
@ -52,7 +52,7 @@ urlpatterns = patterns(
|
||||
views.AssignmentPDF.as_view(),
|
||||
name='assignment_pdf'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/gen_poll/$',
|
||||
url(r'^(?P<pk>\d+)/create_poll/$',
|
||||
views.PollCreateView.as_view(),
|
||||
name='assignmentpoll_create'),
|
||||
|
||||
@ -64,22 +64,26 @@ urlpatterns = patterns(
|
||||
views.AssignmentPollDeleteView.as_view(),
|
||||
name='assignmentpoll_delete'),
|
||||
|
||||
url(r'^poll/(?P<poll_id>\d+)/print/$',
|
||||
url(r'^poll/(?P<poll_pk>\d+)/print/$',
|
||||
views.AssignmentPollPDF.as_view(),
|
||||
name='assignmentpoll_pdf'),
|
||||
|
||||
# TODO: use seperate urls to publish and unpublish the poll
|
||||
# see assignment_user_elected
|
||||
url(r'^poll/(?P<pk>\d+)/pub/$',
|
||||
views.SetPublishStatusView.as_view(),
|
||||
name='assignmentpoll_publish_status'),
|
||||
views.SetPublishPollView.as_view(),
|
||||
{'publish': True},
|
||||
name='assignmentpoll_publish_poll'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/elected/(?P<user_id>[^/]+)/$',
|
||||
url(r'^poll/(?P<pk>\d+)/unpub/$',
|
||||
views.SetPublishPollView.as_view(),
|
||||
{'publish': False},
|
||||
name='assignmentpoll_unpublish_poll'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/elected/(?P<user_pk>[^/]+)/$',
|
||||
views.SetElectedView.as_view(),
|
||||
{'elected': True},
|
||||
name='assignment_user_elected'),
|
||||
|
||||
url(r'^(?P<pk>\d+)/notelected/(?P<user_id>[^/]+)/$',
|
||||
url(r'^(?P<pk>\d+)/notelected/(?P<user_pk>[^/]+)/$',
|
||||
views.SetElectedView.as_view(),
|
||||
{'elected': False},
|
||||
name='assignment_user_not_elected')
|
||||
|
@ -2,7 +2,6 @@ from cgi import escape
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ungettext
|
||||
from reportlab.lib import colors
|
||||
@ -18,9 +17,9 @@ from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.rest_api import viewsets
|
||||
from openslides.utils.utils import html_strong
|
||||
from openslides.utils.views import (CreateView, DeleteView, DetailView,
|
||||
ListView, PDFView, PermissionMixin,
|
||||
ListView, PDFView,
|
||||
QuestionView, RedirectView,
|
||||
SingleObjectMixin, UpdateView, View)
|
||||
SingleObjectMixin, UpdateView)
|
||||
|
||||
from .forms import AssignmentForm, AssignmentRunForm
|
||||
from .models import Assignment, AssignmentPoll
|
||||
@ -28,150 +27,183 @@ from .serializers import AssignmentFullSerializer, AssignmentShortSerializer
|
||||
|
||||
|
||||
class AssignmentListView(ListView):
|
||||
"""ListView for all Assignments"""
|
||||
required_permission = 'assignment.can_see_assignment'
|
||||
"""
|
||||
Lists all assignments.
|
||||
"""
|
||||
required_permission = 'assignment.can_see_assignments'
|
||||
model = Assignment
|
||||
|
||||
|
||||
class AssignmentDetail(DetailView):
|
||||
required_permission = 'assignment.can_see_assignment'
|
||||
"""
|
||||
Shows one assignment.
|
||||
"""
|
||||
# TODO: use another view as 'run form' when updating this to angular
|
||||
required_permission = 'assignment.can_see_assignments'
|
||||
model = Assignment
|
||||
form_class = AssignmentRunForm
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super(AssignmentDetail, self).get_context_data(*args, **kwargs)
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
assignment = self.get_object()
|
||||
if self.request.method == 'POST':
|
||||
context['form'] = self.form_class(self.request.POST)
|
||||
else:
|
||||
context['form'] = self.form_class()
|
||||
polls = self.get_object().poll_set.all()
|
||||
if not self.request.user.has_perm('assignment.can_manage_assignment'):
|
||||
polls = self.get_object().poll_set.filter(published=True)
|
||||
vote_results = self.get_object().vote_results(only_published=True)
|
||||
else:
|
||||
polls = self.get_object().poll_set.all()
|
||||
vote_results = self.get_object().vote_results(only_published=False)
|
||||
|
||||
blocked_candidates = [
|
||||
candidate.person for candidate in
|
||||
self.get_object().assignment_candidates.filter(blocked=True)]
|
||||
polls = assignment.polls.all()
|
||||
if not self.request.user.has_perm('assignment.can_manage_assignments'):
|
||||
polls = polls.filter(published=True)
|
||||
vote_results = assignment.vote_results(only_published=True)
|
||||
else:
|
||||
polls = self.get_object().polls.all()
|
||||
vote_results = assignment.vote_results(only_published=False)
|
||||
|
||||
context['polls'] = polls
|
||||
context['vote_results'] = vote_results
|
||||
context['blocked_candidates'] = blocked_candidates
|
||||
context['user_is_candidate'] = self.get_object().is_candidate(self.request.user)
|
||||
context['blocked_candidates'] = assignment.blocked
|
||||
context['user_is_candidate'] = assignment.is_candidate(self.request.user)
|
||||
return context
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if self.request.user.has_perm('assignment.can_nominate_other'):
|
||||
assignment = self.get_object()
|
||||
form = self.form_class(self.request.POST)
|
||||
if form.is_valid():
|
||||
user = form.cleaned_data['candidate']
|
||||
try:
|
||||
self.get_object().run(user, self.request.user)
|
||||
except NameError as e:
|
||||
messages.error(self.request, e)
|
||||
if (assignment.phase == assignment.PHASE_SEARCH or
|
||||
self.request.user.has_perm('assignment.can_manage_assignments')):
|
||||
if (assignment.is_blocked(user) and
|
||||
not self.request.user.has_perm('assignment.can_manage_assignments')):
|
||||
messages.error(
|
||||
self.request,
|
||||
_("User %s does not want to be an candidate") % user)
|
||||
elif assignment.is_elected(user):
|
||||
messages.error(
|
||||
self.request,
|
||||
_("User %s is already elected") % html_strong(user))
|
||||
elif assignment.is_candidate(user):
|
||||
messages.error(
|
||||
self.request,
|
||||
_("User %s is already an candidate") % html_strong(user))
|
||||
else:
|
||||
assignment.set_candidate(user)
|
||||
messages.success(
|
||||
self.request,
|
||||
_("User %s was nominated successfully.") % html_strong(user))
|
||||
else:
|
||||
messages.success(self.request, _(
|
||||
"Candidate %s was nominated successfully.")
|
||||
% html_strong(user))
|
||||
messages.error(
|
||||
self.request,
|
||||
_("You can not add candidates to this assignment"))
|
||||
return super(AssignmentDetail, self).get(*args, **kwargs)
|
||||
|
||||
|
||||
class AssignmentCreateView(CreateView):
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = Assignment
|
||||
form_class = AssignmentForm
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
|
||||
|
||||
class AssignmentUpdateView(UpdateView):
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = Assignment
|
||||
form_class = AssignmentForm
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
|
||||
|
||||
class AssignmentDeleteView(DeleteView):
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = Assignment
|
||||
success_url_name = 'assignment_list'
|
||||
|
||||
|
||||
class AssignmentSetStatusView(SingleObjectMixin, RedirectView):
|
||||
class AssignmentSetPhaseView(SingleObjectMixin, RedirectView):
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = Assignment
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
url_name = 'assignment_detail'
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
status = kwargs.get('status')
|
||||
if status is not None:
|
||||
try:
|
||||
self.get_object().set_status(status)
|
||||
except ValueError as e:
|
||||
messages.error(self.request, e)
|
||||
else:
|
||||
messages.success(
|
||||
self.request,
|
||||
_('Election status was set to: %s.') %
|
||||
html_strong(self.get_object().get_status_display())
|
||||
)
|
||||
|
||||
|
||||
class AssignmentRunView(SingleObjectMixin, PermissionMixin, View):
|
||||
model = Assignment
|
||||
required_permission = 'assignment.can_nominate_self'
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
phase = int(kwargs.get('phase'))
|
||||
assignment = self.get_object()
|
||||
try:
|
||||
assignment.run(self.request.user, self.request.user)
|
||||
except NameError as e:
|
||||
assignment.set_phase(phase)
|
||||
except ValueError as e:
|
||||
messages.error(self.request, e)
|
||||
else:
|
||||
assignment.save()
|
||||
messages.success(
|
||||
self.request, _('You have set your candidature successfully.'))
|
||||
return redirect(reverse('assignment_detail', args=[assignment.pk]))
|
||||
self.request,
|
||||
_('Election status was set to: %s.') %
|
||||
html_strong(assignment.get_phase_display()))
|
||||
|
||||
|
||||
class AssignmentRunDeleteView(SingleObjectMixin, RedirectView):
|
||||
class AssignmentCandidateView(SingleObjectMixin, RedirectView):
|
||||
required_permission = 'assignment.can_nominate_self'
|
||||
model = Assignment
|
||||
url_name = 'assignment_detail'
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
if self.get_object().status == 'sea' or self.request.user.has_perm(
|
||||
"assignment.can_manage_assignment"):
|
||||
try:
|
||||
self.get_object().delrun(self.request.user, blocked=True)
|
||||
except Exception as e:
|
||||
# TODO: only catch relevant exception
|
||||
messages.error(self.request, e)
|
||||
assignment = self.get_object()
|
||||
if (assignment.phase == assignment.PHASE_SEARCH or
|
||||
self.request.user.has_perm('assignment.can_manage_assignments')):
|
||||
user = self.request.user
|
||||
if assignment.is_elected(user):
|
||||
messages.error(
|
||||
self.request,
|
||||
_("You are already elected"))
|
||||
elif assignment.is_candidate(user):
|
||||
messages.error(
|
||||
self.request,
|
||||
_("You are already an candidate"))
|
||||
else:
|
||||
messages.success(self.request, _(
|
||||
'You have withdrawn your candidature successfully. '
|
||||
'You can not be nominated by other participants anymore.'))
|
||||
assignment.set_candidate(user)
|
||||
messages.success(
|
||||
self.request,
|
||||
_("You were nominated successfully."))
|
||||
else:
|
||||
messages.error(
|
||||
self.request,
|
||||
_("You can not candidate to this assignment"))
|
||||
|
||||
|
||||
class AssignmentDeleteCandidateshipView(SingleObjectMixin, RedirectView):
|
||||
required_permission = None # Any user can withdraw his candidature
|
||||
model = Assignment
|
||||
url_name = 'assignment_detail'
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
assignment = self.get_object()
|
||||
if (assignment.phase == assignment.PHASE_SEARCH or
|
||||
self.request.user.has_perm('assignment.can_manage_assignments')):
|
||||
user = self.request.user
|
||||
assignment.set_blocked(user)
|
||||
messages.success(self.request, _(
|
||||
'You have withdrawn your candidature successfully. '
|
||||
'You can not be nominated by other participants anymore.'))
|
||||
else:
|
||||
messages.error(self.request, _('The candidate list is already closed.'))
|
||||
|
||||
|
||||
class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
|
||||
class AssignmentDeleteCandidateshipOtherView(SingleObjectMixin, QuestionView):
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = Assignment
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
|
||||
def get_question_message(self):
|
||||
self._get_person_information()
|
||||
if not self.is_blocked:
|
||||
question = _("Do you really want to withdraw %s from the election?") % html_strong(self.person)
|
||||
self.user = User.objects.get(pk=self.kwargs.get('user_pk'))
|
||||
assignment = self.get_object()
|
||||
if assignment.is_blocked:
|
||||
question = _("Do you really want to unblock %s for the election?") % html_strong(self.user)
|
||||
else:
|
||||
question = _("Do you really want to unblock %s for the election?") % html_strong(self.person)
|
||||
question = _("Do you really want to withdraw %s from the election?") % html_strong(self.user)
|
||||
return question
|
||||
|
||||
def on_clicked_yes(self):
|
||||
self._get_person_information()
|
||||
try:
|
||||
self.get_object().delrun(self.person, blocked=False)
|
||||
except Exception as e:
|
||||
# TODO: only catch relevant exception
|
||||
self.error = e
|
||||
else:
|
||||
self.user = User.objects.get(pk=self.kwargs.get('user_pk'))
|
||||
assignment = self.get_object()
|
||||
if not assignment.is_elected(self.user):
|
||||
assignment.delete_related_user(self.user)
|
||||
self.error = False
|
||||
else:
|
||||
self.error = _("User %s is already elected") % html_strong(self.user)
|
||||
|
||||
def create_final_message(self):
|
||||
if self.error:
|
||||
@ -180,14 +212,7 @@ class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
|
||||
messages.success(self.request, self.get_final_message())
|
||||
|
||||
def get_final_message(self):
|
||||
message = _("Candidate %s was withdrawn successfully.") % html_strong(self.person)
|
||||
if self.is_blocked:
|
||||
message = _("%s was unblocked successfully.") % html_strong(self.person)
|
||||
return message
|
||||
|
||||
def _get_person_information(self):
|
||||
self.person = User.objects.get(pk=self.kwargs.get('user_id'))
|
||||
self.is_blocked = self.get_object().is_blocked(self.person)
|
||||
return _("Candidate %s was withdrawn successfully.") % html_strong(self.user)
|
||||
|
||||
|
||||
class AssignmentViewSet(viewsets.ModelViewSet):
|
||||
@ -203,16 +228,16 @@ class AssignmentViewSet(viewsets.ModelViewSet):
|
||||
permission to see assignments and in case of create, update or destroy
|
||||
requests the permission to manage assignments.
|
||||
"""
|
||||
if (not request.user.has_perm('assignment.can_see_assignment') or
|
||||
if (not request.user.has_perm('assignment.can_see_assignments') or
|
||||
(self.action in ('create', 'update', 'destroy') and not
|
||||
request.user.has_perm('assignment.can_manage_assignment'))):
|
||||
request.user.has_perm('assignment.can_manage_assignments'))):
|
||||
self.permission_denied(request)
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""
|
||||
Returns different serializer classes with respect to users permissions.
|
||||
"""
|
||||
if self.request.user.has_perm('assignment.can_manage_assignment'):
|
||||
if self.request.user.has_perm('assignment.can_manage_assignments'):
|
||||
serializer_class = AssignmentFullSerializer
|
||||
else:
|
||||
serializer_class = AssignmentShortSerializer
|
||||
@ -220,16 +245,17 @@ class AssignmentViewSet(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class PollCreateView(SingleObjectMixin, RedirectView):
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = Assignment
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
url_name = 'assignment_detail'
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
self.get_object().gen_poll()
|
||||
self.get_object().create_poll()
|
||||
messages.success(self.request, _("New ballot was successfully created."))
|
||||
|
||||
|
||||
class PollUpdateView(PollFormView):
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
poll_class = AssignmentPoll
|
||||
template_name = 'assignment/assignmentpoll_form.html'
|
||||
|
||||
@ -238,49 +264,46 @@ class PollUpdateView(PollFormView):
|
||||
self.assignment = self.poll.get_assignment()
|
||||
context['assignment'] = self.assignment
|
||||
context['poll'] = self.poll
|
||||
context['polls'] = self.assignment.poll_set.all()
|
||||
context['polls'] = self.assignment.polls.all()
|
||||
context['ballotnumber'] = self.poll.get_ballot()
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return_url = ''
|
||||
if 'apply' not in self.request.POST:
|
||||
return_url = reverse('assignment_detail', args=[self.poll.assignment.id])
|
||||
else:
|
||||
return_url = ''
|
||||
return return_url
|
||||
|
||||
|
||||
class SetPublishStatusView(SingleObjectMixin, RedirectView):
|
||||
class SetPublishPollView(SingleObjectMixin, RedirectView):
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = AssignmentPoll
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
url_name = 'assignment_detail'
|
||||
allow_ajax = True
|
||||
publish = False
|
||||
|
||||
def get_ajax_context(self, **kwargs):
|
||||
return {'published': self.object.published}
|
||||
def get_ajax_context(self, **context):
|
||||
return super().get_ajax_context(
|
||||
published=self.object.published,
|
||||
**context)
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
try:
|
||||
poll = self.get_object()
|
||||
except self.model.DoesNotExist:
|
||||
messages.error(self.request, _('Ballot ID %d does not exist.') %
|
||||
int(kwargs['poll_id']))
|
||||
else:
|
||||
if poll.published:
|
||||
poll.set_published(False)
|
||||
else:
|
||||
poll.set_published(True)
|
||||
poll = self.get_object()
|
||||
poll.set_published(kwargs['publish'])
|
||||
|
||||
|
||||
class SetElectedView(SingleObjectMixin, RedirectView):
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = Assignment
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
url_name = 'assignment_detail'
|
||||
allow_ajax = True
|
||||
|
||||
def pre_redirect(self, *args, **kwargs):
|
||||
self.person = User.objects.get(pk=kwargs['user_id'])
|
||||
self.elected = kwargs['elected']
|
||||
self.get_object().set_elected(self.person, self.elected)
|
||||
# TODO: un-elect users if self.elected is False
|
||||
self.get_object().set_elected(self.person)
|
||||
|
||||
def get_ajax_context(self, **kwargs):
|
||||
if self.elected:
|
||||
@ -298,16 +321,16 @@ class AssignmentPollDeleteView(DeleteView):
|
||||
"""
|
||||
Delete an assignment poll object.
|
||||
"""
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
model = AssignmentPoll
|
||||
|
||||
def pre_redirect(self, request, *args, **kwargs):
|
||||
self.set_assignment()
|
||||
super(AssignmentPollDeleteView, self).pre_redirect(request, *args, **kwargs)
|
||||
super().pre_redirect(request, *args, **kwargs)
|
||||
|
||||
def pre_post_redirect(self, request, *args, **kwargs):
|
||||
self.set_assignment()
|
||||
super(AssignmentPollDeleteView, self).pre_post_redirect(request, *args, **kwargs)
|
||||
super().pre_post_redirect(request, *args, **kwargs)
|
||||
|
||||
def set_assignment(self):
|
||||
self.assignment = self.get_object().assignment
|
||||
@ -320,26 +343,26 @@ class AssignmentPollDeleteView(DeleteView):
|
||||
|
||||
|
||||
class AssignmentPDF(PDFView):
|
||||
required_permission = 'assignment.can_see_assignment'
|
||||
required_permission = 'assignment.can_see_assignments'
|
||||
top_space = 0
|
||||
|
||||
def get_filename(self):
|
||||
try:
|
||||
assignment_id = self.kwargs['pk']
|
||||
assignment = Assignment.objects.get(id=assignment_id)
|
||||
assignment = Assignment.objects.get(pk=self.kwargs['pk'])
|
||||
filename = u'%s-%s' % (
|
||||
_("Assignment"),
|
||||
assignment.name.replace(' ', '_'))
|
||||
assignment.title.replace(' ', '_'))
|
||||
except:
|
||||
filename = _("Elections")
|
||||
return filename
|
||||
|
||||
def append_to_pdf(self, story):
|
||||
try:
|
||||
assignment_id = self.kwargs['pk']
|
||||
assignment_pk = self.kwargs['pk']
|
||||
except KeyError:
|
||||
assignment_id = None
|
||||
if assignment_id is None: # print all assignments
|
||||
assignment_pk = None
|
||||
|
||||
if assignment_pk is None: # print all assignments
|
||||
title = escape(config["assignment_pdf_title"])
|
||||
story.append(Paragraph(title, stylesheet['Heading1']))
|
||||
preamble = escape(config["assignment_pdf_preamble"])
|
||||
@ -356,31 +379,31 @@ class AssignmentPDF(PDFView):
|
||||
# List of assignments
|
||||
for assignment in assignments:
|
||||
story.append(Paragraph(
|
||||
escape(assignment.name), stylesheet['Heading3']))
|
||||
escape(assignment.title), stylesheet['Heading3']))
|
||||
# Assignment details (each assignment on single page)
|
||||
for assignment in assignments:
|
||||
story.append(PageBreak())
|
||||
# append the assignment to the story-object
|
||||
self.get_assignment(assignment, story)
|
||||
else: # print selected assignment
|
||||
assignment = Assignment.objects.get(id=assignment_id)
|
||||
assignment = Assignment.objects.get(pk=assignment_pk)
|
||||
# append the assignment to the story-object
|
||||
self.get_assignment(assignment, story)
|
||||
|
||||
def get_assignment(self, assignment, story):
|
||||
# title
|
||||
story.append(Paragraph(
|
||||
_("Election: %s") % escape(assignment.name), stylesheet['Heading1']))
|
||||
_("Election: %s") % escape(assignment.title), stylesheet['Heading1']))
|
||||
story.append(Spacer(0, 0.5 * cm))
|
||||
|
||||
# Filling table rows...
|
||||
data = []
|
||||
polls = assignment.poll_set.filter(published=True)
|
||||
polls = assignment.polls.filter(published=True)
|
||||
# 1. posts
|
||||
data.append([
|
||||
Paragraph("%s:" %
|
||||
_("Number of available posts"), stylesheet['Bold']),
|
||||
Paragraph(str(assignment.posts), stylesheet['Paragraph'])])
|
||||
_("Number of members to be elected"), stylesheet['Bold']),
|
||||
Paragraph(str(assignment.open_posts), stylesheet['Paragraph'])])
|
||||
|
||||
# 2a. if no polls available print candidates
|
||||
if not polls:
|
||||
@ -393,7 +416,7 @@ class AssignmentPDF(PDFView):
|
||||
[],
|
||||
Paragraph("<seq id='counter'/>. %s" % candidate,
|
||||
stylesheet['Signaturefield'])])
|
||||
if assignment.status == "sea":
|
||||
if assignment.phase == assignment.PHASE_SEARCH:
|
||||
for x in range(0, 7):
|
||||
data.append([
|
||||
[],
|
||||
@ -517,16 +540,16 @@ class CreateRelatedAgendaItemView(_CreateRelatedAgendaItemView):
|
||||
|
||||
|
||||
class AssignmentPollPDF(PDFView):
|
||||
required_permission = 'assignment.can_manage_assignment'
|
||||
required_permission = 'assignment.can_manage_assignments'
|
||||
top_space = 0
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.poll = AssignmentPoll.objects.get(id=self.kwargs['poll_id'])
|
||||
self.poll = AssignmentPoll.objects.get(pk=self.kwargs['poll_pk'])
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_filename(self):
|
||||
filename = u'%s-%s_%s' % (
|
||||
_("Election"), self.poll.assignment.name.replace(' ', '_'),
|
||||
_("Election"), self.poll.assignment.title.replace(' ', '_'),
|
||||
self.poll.get_ballot())
|
||||
return filename
|
||||
|
||||
@ -543,7 +566,7 @@ class AssignmentPollPDF(PDFView):
|
||||
cell = []
|
||||
cell.append(Spacer(0, 0.8 * cm))
|
||||
cell.append(Paragraph(
|
||||
_("Election") + ": " + self.poll.assignment.name,
|
||||
_("Election") + ": " + self.poll.assignment.title,
|
||||
stylesheet['Ballot_title']))
|
||||
cell.append(Paragraph(
|
||||
self.poll.description or '',
|
||||
@ -555,7 +578,7 @@ class AssignmentPollPDF(PDFView):
|
||||
"%d candidate", "%d candidates", len(options)) % len(options)
|
||||
available_posts_string = ungettext(
|
||||
"%d available post", "%d available posts",
|
||||
self.poll.assignment.posts) % self.poll.assignment.posts
|
||||
self.poll.assignment.open_posts) % self.poll.assignment.open_posts
|
||||
cell.append(Paragraph(
|
||||
"%s, %s, %s" % (ballot_string, candidate_string, available_posts_string),
|
||||
stylesheet['Ballot_description']))
|
||||
|
@ -76,7 +76,7 @@ def create_builtin_groups_and_admin():
|
||||
perm_15 = Permission.objects.get(content_type=ct_motion, codename='can_see_motion')
|
||||
|
||||
ct_assignment = ContentType.objects.get(app_label='assignment', model='assignment')
|
||||
perm_16 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignment')
|
||||
perm_16 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignments')
|
||||
|
||||
ct_users = ContentType.objects.get(app_label='users', model='user')
|
||||
perm_users_can_see_name = Permission.objects.get(content_type=ct_users, codename='can_see_name')
|
||||
@ -114,7 +114,7 @@ def create_builtin_groups_and_admin():
|
||||
# Staff (pk 4)
|
||||
perm_41 = Permission.objects.get(content_type=ct_agenda, codename='can_manage_agenda')
|
||||
perm_42 = Permission.objects.get(content_type=ct_motion, codename='can_manage_motion')
|
||||
perm_43 = Permission.objects.get(content_type=ct_assignment, codename='can_manage_assignment')
|
||||
perm_43 = Permission.objects.get(content_type=ct_assignment, codename='can_manage_assignments')
|
||||
perm_44 = Permission.objects.get(content_type=ct_users, codename='can_manage')
|
||||
perm_45 = Permission.objects.get(content_type=ct_core, codename='can_manage_projector')
|
||||
perm_46 = Permission.objects.get(content_type=ct_core, codename='can_use_chat')
|
||||
|
@ -91,8 +91,8 @@ class PersonalInfoWidget(TestCase):
|
||||
def test_candidate_list(self):
|
||||
assignment = self.import_assignment()
|
||||
if assignment:
|
||||
assignment_1 = assignment.models.Assignment.objects.create(name='Hausmeister ooKoh7roApoo3phe', posts=1)
|
||||
assignment_1.run(candidate=self.user, person=self.user)
|
||||
assignment_1 = assignment.models.Assignment.objects.create(title='Hausmeister ooKoh7roApoo3phe', open_posts=1)
|
||||
assignment_1.set_candidate(self.user)
|
||||
response = self.client.get('/dashboard/')
|
||||
self.assertContains(response, 'I am candidate for the following elections:', status_code=200)
|
||||
self.assertContains(response, 'Hausmeister ooKoh7roApoo3phe', status_code=200)
|
||||
|
@ -1,39 +0,0 @@
|
||||
from django.test.client import Client
|
||||
|
||||
from openslides.agenda.models import Item, Speaker
|
||||
from openslides.assignment.models import Assignment
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
class AssignmentModelTest(TestCase):
|
||||
def setUp(self):
|
||||
# Admin
|
||||
self.admin = User.objects.get(pk=1)
|
||||
self.admin_client = Client()
|
||||
self.admin_client.login(username='admin', password='admin')
|
||||
|
||||
def test_delete_with_related_item(self):
|
||||
assignment = Assignment.objects.create(name='assignment_name_fgdhensbch34zfu1284ds', posts=1)
|
||||
response = self.admin_client.get('/assignment/1/agenda/')
|
||||
self.assertRedirects(response, '/agenda/')
|
||||
self.assertEqual(Item.objects.get(pk=1).get_title(), 'assignment_name_fgdhensbch34zfu1284ds')
|
||||
assignment.delete()
|
||||
self.assertTrue(Item.objects.filter(pk=1).exists())
|
||||
|
||||
def test_begin_speach(self):
|
||||
assignment = Assignment.objects.create(name='test_assignment_gjbnchs4620sdfhjfsksj1', posts=1)
|
||||
item = Item.objects.create(content_object=assignment)
|
||||
person_1 = User.objects.create(username='user_1_bnhdjgd8747djcbjd8fg')
|
||||
person_2 = User.objects.create(username='user_2_qmlkohid6qvx5q0fbmh9')
|
||||
person_3 = User.objects.create(username='user_3_nbjf74jf9bjag219ou96')
|
||||
assignment.run(person_1, person_1)
|
||||
assignment.run(person_2, person_2)
|
||||
assignment.run(person_3, person_3)
|
||||
Speaker.objects.add(person_1, item)
|
||||
self.assertEqual(item.speaker_set.count(), 1)
|
||||
|
||||
assignment.gen_poll()
|
||||
self.assertTrue(item.speaker_set.filter(user=person_1).exists())
|
||||
self.assertTrue(item.speaker_set.filter(user=person_2).exists())
|
||||
self.assertTrue(item.speaker_set.filter(user=person_3).exists())
|
@ -16,11 +16,11 @@ class AssignmentPDFTest(TestCase):
|
||||
self.admin_client.login(username='admin', password='admin')
|
||||
|
||||
def test_render_pdf(self):
|
||||
Assignment.objects.create(name='assignment_name_ith8qua1Eiferoqu5ju2', description="test", posts=1)
|
||||
Assignment.objects.create(title='assignment_name_ith8qua1Eiferoqu5ju2', description="test", open_posts=1)
|
||||
response = self.admin_client.get('/assignment/print/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_render_many_posts(self):
|
||||
Assignment.objects.create(name='assignment_name_cohZ9shaipee3Phaing4', description="test", posts=20)
|
||||
Assignment.objects.create(title='assignment_name_cohZ9shaipee3Phaing4', description="test", open_posts=20)
|
||||
response = self.admin_client.get('/assignment/print/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -34,7 +34,7 @@ class AssignmentViewTestCase(TestCase):
|
||||
self.registered_client = Client()
|
||||
self.registered_client.login(username='registered', password='registered')
|
||||
|
||||
self.assignment1 = Assignment.objects.create(name='test', posts=2)
|
||||
self.assignment1 = Assignment.objects.create(title='test', open_posts=2)
|
||||
|
||||
def check_url(self, url, test_client, response_cose):
|
||||
response = test_client.get(url)
|
||||
@ -45,7 +45,7 @@ class AssignmentViewTestCase(TestCase):
|
||||
class TestAssignmentPollDelete(AssignmentViewTestCase):
|
||||
def setUp(self):
|
||||
super(TestAssignmentPollDelete, self).setUp()
|
||||
self.assignment1.gen_poll()
|
||||
self.assignment1.create_poll()
|
||||
|
||||
def test_get(self):
|
||||
response = self.check_url('/assignment/poll/1/del/', self.admin_client, 302)
|
||||
@ -65,7 +65,7 @@ class TestAssignmentDetailView(AssignmentViewTestCase):
|
||||
self.assertContains(response, 'No candidates available.')
|
||||
self.assertNotContains(response, 'Blocked Candidates')
|
||||
|
||||
response = self.delegate_client.get('/assignment/1/run/')
|
||||
response = self.delegate_client.get('/assignment/1/candidate/')
|
||||
self.assertTrue(self.assignment1.is_candidate(self.delegate))
|
||||
self.assertFalse(self.assignment1.is_blocked(self.delegate))
|
||||
|
||||
@ -73,7 +73,7 @@ class TestAssignmentDetailView(AssignmentViewTestCase):
|
||||
self.assertNotContains(response, 'No candidates available.')
|
||||
self.assertNotContains(response, 'Blocked Candidates')
|
||||
|
||||
response = self.delegate_client.get('/assignment/1/delrun/')
|
||||
response = self.delegate_client.get('/assignment/1/delete_candidate/')
|
||||
self.assertFalse(self.assignment1.is_candidate(self.delegate))
|
||||
self.assertTrue(self.assignment1.is_blocked(self.delegate))
|
||||
|
||||
@ -89,19 +89,19 @@ class TestAssignmentPollCreateView(TestCase):
|
||||
def test_assignment_add_candidate(self):
|
||||
admin = User.objects.get(pk=1)
|
||||
self.assignment = Assignment.objects.create(
|
||||
name='test_assignment_oiL2heerookiegeirai0',
|
||||
posts=1)
|
||||
self.assignment.run(admin, admin)
|
||||
title='test_assignment_oiL2heerookiegeirai0',
|
||||
open_posts=1)
|
||||
self.assignment.set_candidate(admin)
|
||||
self.assertEqual(len(Assignment.objects.get(pk=self.assignment.pk).candidates), 1)
|
||||
|
||||
def test_assignment_poll_creation(self):
|
||||
self.test_assignment_add_candidate()
|
||||
self.assignment.set_status('vot')
|
||||
self.assignment.set_phase(self.assignment.PHASE_VOTING)
|
||||
admin_client = Client()
|
||||
admin_client.login(username='admin', password='admin')
|
||||
self.assertFalse(AssignmentPoll.objects.exists())
|
||||
self.assertEqual(config['assignment_poll_vote_values'], 'auto')
|
||||
response = admin_client.get('/assignment/1/gen_poll/')
|
||||
response = admin_client.get('/assignment/1/create_poll/')
|
||||
self.assertRedirects(response, '/assignment/1/')
|
||||
poll = AssignmentPoll.objects.get()
|
||||
self.assertEqual(poll.assignment, self.assignment)
|
||||
@ -117,10 +117,10 @@ class TestAssignmentPollPdfView(TestCase):
|
||||
def test_assignment_create_poll_pdf(self):
|
||||
# Create a assignment with a poll
|
||||
admin = User.objects.get(pk=1)
|
||||
assignment = Assignment.objects.create(name='assignment1', posts=1)
|
||||
assignment.run(admin, admin)
|
||||
assignment.set_status('vot')
|
||||
assignment.gen_poll()
|
||||
assignment = Assignment.objects.create(title='assignment1', open_posts=1)
|
||||
assignment.set_candidate(admin)
|
||||
assignment.set_phase(assignment.PHASE_VOTING)
|
||||
assignment.create_poll()
|
||||
client = Client()
|
||||
client.login(username='admin', password='admin')
|
||||
|
||||
@ -140,7 +140,7 @@ class TestPollUpdateView(TestCase):
|
||||
"""
|
||||
Tests that a 404 is returned, when a non existing poll is requested.
|
||||
"""
|
||||
Assignment.objects.create(name='test assignment', posts=1)
|
||||
Assignment.objects.create(title='test assignment', open_posts=1)
|
||||
url = '/assignment/poll/1/edit/'
|
||||
|
||||
response = self.admin_client.get(url)
|
||||
|
Loading…
Reference in New Issue
Block a user