start to rewrite the poll-api

This commit is contained in:
Oskar Hahn 2012-02-14 16:31:21 +01:00
parent a54ff100d1
commit 0957de83a9
11 changed files with 245 additions and 270 deletions

View File

@ -137,29 +137,7 @@
</td> </td>
{% if perms.agenda.can_manage_agenda %} {% if perms.agenda.can_manage_agenda %}
<td> <td>
{% ifequal item.type 'ItemApplication' %} Text
<a href="{% url application_view item.cast.application.id %}">{% trans "Application" %} {{ item.cast.application.number }}</a>
{% endifequal %}
{% ifequal item.type 'ItemPoll' %}
{% if item.cast.poll.application %}
<a href="{% url poll_view item.cast.poll.id %}">{% trans "Poll of Application" %}</a>
{% endif %}
{% if item.cast.poll.assignment %}
<a href="{% url poll_view item.cast.poll.id %}">{% trans "Poll of Election" %}</a>
{% endif %}
{% endifequal %}
{% ifequal item.type 'ItemAssignment' %}
<a href="{% url assignment_view item.cast.assignment.id %}">{% trans "Election" %}</a>
{% endifequal %}
{% ifequal item.type 'ItemText' %}
Text
{% endifequal %}
{% if item.hidden %}
({% trans 'hidden' %})
{% endif %}
</td> </td>
{% endif %} {% endif %}
<td><span style="width: 1px;white-space: nowrap;"> <td><span style="width: 1px;white-space: nowrap;">

View File

@ -23,6 +23,8 @@ from projector.models import Slide
from participant.models import Profile from participant.models import Profile
from system.api import config_get from system.api import config_get
from utils.utils import _propper_unicode from utils.utils import _propper_unicode
from poll import ChoicePoll
from poll.models import BaseOption, BasePoll
class Application(models.Model, Slide): class Application(models.Model, Slide):
@ -410,24 +412,28 @@ class Application(models.Model, Slide):
""" """
Generates a poll object for the application Generates a poll object for the application
""" """
from poll.models import Poll poll = ApplicationPoll()
poll = Poll(optiondecision=True, \
application=self)
poll.save() poll.save()
poll.add_option(self) poll.set_options([{'application': self}])
self.writelog(_("Poll created"), user) self.writelog(_("Poll created"), user)
return poll return poll
@property
def polls(self):
#todo: return an query_set
return [option.poll for option in self.applicationoption_set.all()]
@property @property
def results(self): def results(self):
""" """
Return a list of voting results Return a list of voting results
""" """
# TODO: This will propably not work
results = [] results = []
for poll in self.poll_set.all(): for poll in self.polls:
for option in poll.options: for option in poll.get_options():
if poll.votesinvalid != None and poll.votescast != None: #if poll.votesinvalid != None and poll.votescast != None:
results.append([option.yes, option.no, option.undesided, poll.votesinvalidf, poll.votescastf]) results.append([option.yes, option.no, option.undesided, poll.votesinvalidf, poll.votescastf])
return results return results
def slide(self): def slide(self):
@ -484,3 +490,14 @@ class AVersion(models.Model):
return self._aid return self._aid
register_slidemodel(Application) register_slidemodel(Application)
class ApplicationOption(BaseOption):
application = models.ForeignKey(Application)
def __unicode__(self):
return unicode(self.application)
class ApplicationPoll(BasePoll):
option_class = ApplicationOption

View File

@ -6,60 +6,45 @@
{% if perms.application.can_manage_application %} {% if perms.application.can_manage_application %}
{% block submenu %} {% block submenu %}
{{ block.super }} {{ block.super }}
<br> <br>
<h3>{%trans "Application No." %} {{ poll.application.number }}</h3> <h3>{%trans "Application No." %} {{ poll.application.number }}</h3>
<ul> <ul>
<li><a href="{% url print_application_poll poll.id %}"><img src="/static/images/icons/application-pdf.png"> {%trans 'Print vote' %}</a></li> <li><a href="{% url print_application_poll poll.id %}"><img src="/static/images/icons/application-pdf.png"> {%trans 'Print vote' %}</a></li>
</ul> </ul>
{% endblock %} {% endblock %}
{% endif %} {% endif %}
{% block content %} {% block content %}
<h1>{%trans "Application No." %} {{ poll.application.number }} - {%trans "Vote" %}</h1> <h1>{%trans "Application No." %} {{ application.number }} - {%trans "Vote" %}</h1>
<h3>{{ poll.application.title }}</h3> <h3>{{ application.title }}</h3>
<p>{%trans "Results of" %} {{ ballot }}. {%trans "Vote" %}</p> <p>{%trans "Results of" %} {{ ballot }}. {%trans "Vote" %}</p>
<i>-1 := {% trans 'majority' %}, -2 := {% trans 'undocumented' %}</i> <i>-1 := {% trans 'majority' %}, -2 := {% trans 'undocumented' %}</i>
<form action="" method="post">{% csrf_token %} <form action="" method="post">{% csrf_token %}
<table class="table" style="width: auto;"> <table class="table" style="width: auto;">
<tr> <tr>
<th>{%trans "Option" %}</th> <th>{% trans "Option" %}</th>
<th>{%trans "Votes" %}</th> <th>{% trans "Votes" %}</th>
</tr> </tr>
<tr> {% for value in forms.0 %}
<td>{%trans "Yes" %}</td> <tr>
<td>{{ options.0.form.yes.errors }}{{ options.0.form.yes }}</td> <td>{{ value.label }}</td>
</tr> <td>{{ value.errors }}{{ value }}</td>
<tr class="odd"> </tr>
<td>{%trans "No" %}</label></td> {% endfor %}
<td>{{ options.0.form.no.errors }}{{ options.0.form.no }}</td> </table>
</tr> <p>
<tr> <button class="button" type="submit">
<td>{%trans "Abstentions" %}</td> <span class="icon ok">{%trans 'Save' %}</span>
<td>{{ options.0.form.undesided.errors }}{{ options.0.form.undesided }}</td> </button>
</tr> <button class="button" type="submit" name="apply">
<tr class="odd"> <span class="icon apply">{%trans 'Apply' %}</span>
<td>{%trans "Invalid votes" %}</td> </button>
<td>{{ form.invalid.errors }}{{ form.invalid }}</td> <a href='{% url application_view application.id %}'>
</tr> <button class="button" type="button" onclick="window.location='{% url application_view application.id %}'">
<tr class="total"> <span class="icon cancel">{%trans 'Cancel' %}</span>
<td style="white-space: nowrap;"><b>{%trans "Votes cast" %}</b></td> </button>
<td>{{ form.votescast.errors }}{{ form.votescast }}</td> </a>
</tr>
</table>
<p>
<button class="button" type="submit">
<span class="icon ok">{%trans 'Save' %}</span>
</button>
<button class="button" type="submit" name="apply">
<span class="icon apply">{%trans 'Apply' %}</span>
</button>
<a href='{% url application_view poll.application.id %}'>
<button class="button" type="button" onclick="window.location='{% url application_view poll.application.id %}'">
<span class="icon cancel">{%trans 'Cancel' %}</span>
</button>
</a>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -47,7 +47,7 @@
{% endfor %} {% endfor %}
<h4>{% trans "Vote results" %}:</h4> <h4>{% trans "Vote results" %}:</h4>
{% with application.poll_set.all as polls %} {% with application.polls as polls %}
{% if polls|length == 0 %} {% if polls|length == 0 %}
{% if perms.application.can_manage_application %} {% if perms.application.can_manage_application %}
{% if "genpoll" in actions %} {% if "genpoll" in actions %}

View File

@ -11,6 +11,10 @@
""" """
from django.conf.urls.defaults import * from django.conf.urls.defaults import *
from django.utils.translation import ugettext as _
from application.views import ViewPoll
urlpatterns = patterns('application.views', urlpatterns = patterns('application.views',
url(r'^application/$', 'overview', \ url(r'^application/$', 'overview', \
@ -77,8 +81,10 @@ urlpatterns = patterns('application.views',
url(r'^application/poll/(?P<poll_id>\d+)/print$', 'print_application_poll', \ url(r'^application/poll/(?P<poll_id>\d+)/print$', 'print_application_poll', \
name='print_application_poll'), name='print_application_poll'),
url(r'^application/poll/(?P<poll_id>\d+)$', 'view_poll', \ url(r'^application/poll/(?P<poll_id>\d+)$',
name='application_poll_view'), ViewPoll.as_view(),
name='application_poll_view'
),
url(r'^application/poll/(?P<poll_id>\d+)/del$', 'delete_poll', \ url(r'^application/poll/(?P<poll_id>\d+)/del$', 'delete_poll', \
name='application_poll_delete'), name='application_poll_delete'),

View File

@ -23,14 +23,15 @@ from django.utils.translation import ugettext as _
from django.utils.translation import ungettext from django.utils.translation import ungettext
from django.db import transaction from django.db import transaction
from openslides.agenda.models import Item from agenda.models import Item
from openslides.application.models import Application, AVersion from application.models import Application, AVersion, ApplicationPoll
from openslides.application.forms import ApplicationForm, \ from application.forms import ApplicationForm, \
ApplicationManagerForm, \ ApplicationManagerForm, \
ApplicationImportForm ApplicationImportForm
from openslides.participant.models import Profile from openslides.participant.models import Profile
from openslides.poll.models import Poll
from openslides.poll.forms import OptionResultForm, PollForm from poll.models import PollFormView
from openslides.utils.utils import template, permission_required, \ from openslides.utils.utils import template, permission_required, \
render_to_forbitten, del_confirm_form, gen_confirm_form render_to_forbitten, del_confirm_form, gen_confirm_form
from openslides.utils.pdf import print_application, print_application_poll from openslides.utils.pdf import print_application, print_application_poll
@ -92,7 +93,7 @@ def view(request, application_id, newest=False):
'actions': actions, 'actions': actions,
'min_supporters': int(config_get('application_min_supporters')), 'min_supporters': int(config_get('application_min_supporters')),
'version': version, 'version': version,
'results': application.results #'results': application.results
} }
@ -347,7 +348,7 @@ def gen_poll(request, application_id):
poll = Application.objects.get(pk=application_id).gen_poll(user=request.user) poll = Application.objects.get(pk=application_id).gen_poll(user=request.user)
messages.success(request, _("New vote was successfully created.") ) messages.success(request, _("New vote was successfully created.") )
except Application.DoesNotExist: except Application.DoesNotExist:
pass pass # TODO: do not call poll after this excaption
return redirect(reverse('application_poll_view', args=[poll.id])) return redirect(reverse('application_poll_view', args=[poll.id]))
@ -373,9 +374,9 @@ def view_poll(request, poll_id):
""" """
view a poll for this application. view a poll for this application.
""" """
poll = Poll.objects.get(pk=poll_id) poll = ApplicationPoll.objects.get(pk=poll_id)
ballot = poll.ballot #ballot = poll.ballot
options = poll.options options = poll.get_options()
if request.user.has_perm('application.can_manage_application'): if request.user.has_perm('application.can_manage_application'):
if request.method == 'POST': if request.method == 'POST':
form = PollForm(request.POST, prefix="poll") form = PollForm(request.POST, prefix="poll")
@ -408,9 +409,27 @@ def view_poll(request, poll_id):
'poll': poll, 'poll': poll,
'form': form, 'form': form,
'options': options, 'options': options,
'ballot': ballot, #'ballot': ballot,
} }
class ViewPoll(PollFormView):
poll_class = ApplicationPoll
vote_values = [_('yes'), _('no'), _('contained')]
template_name = 'application/poll_view.html'
def get_context_data(self, **kwargs):
context = super(ViewPoll, self).get_context_data()
self.application = self.poll.get_options()[0].application
context['application'] = self.application
return context
def get_success_url(self):
if not 'apply' in self.request.POST:
return reverse('application_view', args=[self.application.id])
return ''
@permission_required('application.can_manage_application') @permission_required('application.can_manage_application')
def permit_version(request, aversion_id): def permit_version(request, aversion_id):
aversion = AVersion.objects.get(pk=aversion_id) aversion = AVersion.objects.get(pk=aversion_id)

View File

@ -17,8 +17,8 @@ from django.contrib.auth.decorators import login_required
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from openslides.agenda.models import Item from openslides.agenda.models import Item
from poll.models import Poll, Option #from poll.models import Poll, Option
from poll.forms import OptionResultForm, PollForm #from poll.forms import OptionResultForm, PollForm
from assignment.models import Assignment from assignment.models import Assignment
from assignment.forms import AssignmentForm, AssignmentRunForm from assignment.forms import AssignmentForm, AssignmentRunForm
from utils.utils import template, permission_required, gen_confirm_form, del_confirm_form, ajax_request from utils.utils import template, permission_required, gen_confirm_form, del_confirm_form, ajax_request

View File

@ -52,3 +52,4 @@ class Profile(models.Model):
('can_see_participant', "Can see participant"), ('can_see_participant', "Can see participant"),
('can_manage_participant', "Can manage participant"), ('can_manage_participant', "Can manage participant"),
) )

View File

@ -0,0 +1,12 @@
from django.utils.translation import ugettext as _
from models import PollFormView, BasePoll
class DesicionPoll(PollFormView):
vote_values = [_('yes'), _('no'), _('contained')]
poll_class = BasePoll
class ChoicePoll(PollFormView):
poll_class = BasePoll

View File

@ -12,185 +12,142 @@
from django.db import models from django.db import models
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django import forms
from application.models import Application from django.views.generic import TemplateView
from assignment.models import Assignment from django.http import HttpResponseRedirect
from participant.models import Profile
class Poll(models.Model): class OptionForm(forms.Form):
optiondecision = models.BooleanField(default=True, verbose_name = _("Poll of decision (yes, no, abstention)")) def __init__(self, *args, **kwargs):
application = models.ForeignKey(Application, null=True, blank=True, verbose_name = _("Application")) extra = kwargs.pop('extra')
assignment = models.ForeignKey(Assignment, null=True, blank=True, verbose_name = _("Election")) formid = kwargs.pop('formid')
kwargs['prefix'] = "option-%s" % formid
super(OptionForm, self).__init__(*args, **kwargs)
for key, value in extra:
self.fields[key] = forms.IntegerField(
widget=forms.TextInput(attrs={'class': 'small-input'}),
label=_(key),
initial=value,
)
class BaseOption(models.Model):
poll = models.ForeignKey('BasePoll')
@property
def votes(self):
count = 0
for vote in Vote.objects.filter(option=self):
count += vote.weight
return weight
class TextOption(BaseOption):
text = models.CharField(max_length=255)
def __unicode__(self):
return self.text
class Vote(models.Model):
option = models.ForeignKey(BaseOption)
#profile = models.ForeignKey(Profile) # TODO: we need a person+ here
weight = models.IntegerField(default=1)
value = models.CharField(max_length=255, null=True)
class BasePoll(models.Model):
description = models.TextField(null=True, blank=True, verbose_name = _("Description")) description = models.TextField(null=True, blank=True, verbose_name = _("Description"))
votescast = models.IntegerField(null=True, blank=True, verbose_name = _("Votes cast")) votescast = models.IntegerField(null=True, blank=True, verbose_name = _("Votes cast"))
votesinvalid = models.IntegerField(null=True, blank=True, verbose_name = _("Votes invalid")) votesinvalid = models.IntegerField(null=True, blank=True, verbose_name = _("Votes invalid"))
published = models.BooleanField(default=False)
def add_option(self, option): option_class = TextOption
self.save() vote_values = [_('votes')]
optionc = Option()
optionc.poll = self
if isinstance(option, Application):
optionc.application = option
elif isinstance(option, Profile):
optionc.user = option
else:
optionc.text = str(option)
optionc.save()
return optionc
@property def set_options(self, options_data):
def votescastf(self): for option_data in options_data:
if self.votescast == -2: option = self.option_class(**option_data)
return _('undocumented') option.poll = self
elif self.votescast: option.save()
return self.votescast
return '0'
@property
def votesinvalidf(self):
if self.votesinvalid == -2:
return _('undocumented')
elif self.votesinvalid:
if self.votescast > 0:
percentage = round(float(self.votesinvalid) / float(self.votescast) * 100,1)
invalid = "%s (%s %%)" % (str(self.votesinvalid), str(percentage))
return invalid
return self.votesinvalid
return '0'
def has_vote(self):
for option in self.options:
if option.voteyes or option.voteno or option.voteundesided:
return True
return False
def get_options(self): def get_options(self):
return self.option_set.all() return self.get_option_class().objects.filter(poll=self)
@property def get_option_class(self):
def options(self): return self.option_class
return self.option_set.all()
@property def get_vote_values(self):
def options_values(self): return self.vote_values
return [option.value for option in self.options]
def set_published(self, published=True): def set_form_values(self, option, data):
""" for value in self.get_vote_values():
Changes the published-status of the poll. try:
""" vote = Vote.objects.filter(option=option).get(value=value)
self.published = published except Vote.DoesNotExist:
self.save() vote = Vote(option=option, value=value)
vote.weight = data[value]
@property vote.save()
def count_ballots(self):
if self.application:
return Poll.objects.filter(application=self.application).count()
if self.assignment:
return Poll.objects.filter(assignment=self.assignment).count()
return None
@property
def ballot(self):
if self.application:
counter = 0
for poll in Poll.objects.filter(application=self.application):
counter = counter + 1
if self == poll:
return counter
if self.assignment:
counter = 0
for poll in Poll.objects.filter(assignment=self.assignment):
counter = counter + 1
if self == poll:
return counter
return None
@models.permalink
def get_absolute_url(self, link='view'):
if self.application:
if link == 'view':
return ('application_poll_view', [str(self.id), 0])
if link == 'delete':
return ('application_poll_delete', [str(self.id)])
if self.assignment:
if link == 'view':
return ('assignment_poll_view', [str(self.id), 0])
if link == 'delete':
return ('assignment_poll_delete', [str(self.id)])
if link == 'view':
return ('poll_view', [str(self.id)])
if link == 'delete':
return ('poll_delete', [str(self.id)])
def __unicode__(self):
if self.application:
return self.application.title
if self.assignment:
return self.assignment.name
class Option(models.Model): def get_form_values(self, option_id):
text = models.CharField(max_length=100, null=True, blank=True, verbose_name = _("Text")) values = []
user = models.ForeignKey(Profile, null=True, blank=True, verbose_name = _("Participant")) for value in self.get_vote_values():
application = models.ForeignKey(Application, null=True, blank=True, verbose_name = _("Application")) try:
poll = models.ForeignKey(Poll, verbose_name = _("Poll")) vote = Vote.objects.filter(option=option_id).get(value=value)
voteyes = models.IntegerField(null=True, blank=True) weight = vote.weight
voteno = models.IntegerField(null=True, blank=True) except Vote.DoesNotExist:
voteundesided = models.IntegerField(null=True, blank=True) weight = None
values.append((value, weight))
return values
@property def get_vote_form(self, **kwargs):
def yes(self): return OptionForm(extra=self.get_form_values(kwargs['formid']), **kwargs)
if self.voteyes == -1:
return _('majority')
if self.voteyes == -2:
return _('undocumented')
if self.voteyes:
if self.poll.votescast > 0 and self.voteyes > 0:
percentage = round(float(self.voteyes) / float(self.poll.votescast) * 100,1)
return "%s (%s %%)" % (str(self.voteyes), str(percentage))
return self.voteyes
return '0'
@property def get_vote_forms(self, **kwargs):
def no(self): forms = []
if self.voteno == -1: for option in self.get_options():
return _('majority') form = self.get_vote_form(formid=option.id, **kwargs)
if self.voteno == -2: form.option = option
return _('undocumented') forms.append(form)
if self.voteno:
if self.poll.votescast > 0 and self.voteno > 0:
percentage = round(float(self.voteno) / float(self.poll.votescast) * 100,1)
return "%s (%s %%)" % (str(self.voteno), str(percentage))
return self.voteno
return '0'
@property return forms
def undesided(self):
if self.voteundesided == -1:
return _('majority')
if self.voteundesided == -2:
return _('undocumented')
if self.voteundesided:
if self.poll.votescast > 0 and self.voteundesided > 0:
percentage = round(float(self.voteundesided) / float(self.poll.votescast) * 100,1)
return "%s (%s %%)" % (str(self.voteundesided), str(percentage))
return self.voteundesided
return '0'
@property
def value(self):
if self.text != "" and self.text is not None:
return self.text
if self.user is not None:
return self.user
if self.application is not None:
return self.application
return None
def __unicode__(self): class PollFormView(TemplateView):
if self.value: template_name = 'poll/poll.html'
return unicode(self.value) poll_argument = 'poll_id'
return _("No options")
def set_poll(self, poll_id):
poll_id = poll_id
self.poll = self.poll_class.objects.get(pk=poll_id)
self.poll.vote_values = self.vote_values
def get_context_data(self, **kwargs):
context = super(PollFormView, self).get_context_data(**kwargs)
self.set_poll(self.kwargs['poll_id'])
context['poll'] = self.poll
if not 'forms' in context:
context['forms'] = context['poll'].get_vote_forms()
return context
def get_success_url(self):
return self.success_url
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
forms = self.poll.get_vote_forms(data=self.request.POST)
error = False
for form in forms:
if not form.is_valid():
error = True
if error:
return self.render_to_response(self.get_context_data(forms=forms))
for form in forms:
data = {}
for value in self.poll.vote_values:
data[value] = form.cleaned_data[value]
print data
self.poll.set_form_values(form.option, data)
return HttpResponseRedirect(self.get_success_url())

View File

@ -36,7 +36,7 @@ from openslides.agenda.models import Item
from openslides.agenda.api import children_list from openslides.agenda.api import children_list
from openslides.application.models import Application from openslides.application.models import Application
from openslides.assignment.models import Assignment from openslides.assignment.models import Assignment
from openslides.poll.models import Poll, Option #from openslides.poll.models import Poll, Option
from openslides.participant.models import Profile from openslides.participant.models import Profile
from openslides.system.api import config_get from openslides.system.api import config_get
from openslides.settings import SITE_ROOT from openslides.settings import SITE_ROOT