general: remove "vote" extension and its support code (#37573)
This commit is contained in:
parent
f9590d9d4b
commit
1b7a956ad6
|
@ -1,277 +0,0 @@
|
|||
# w.c.s. - web application for online forms
|
||||
# Copyright (C) 2005-2010 Entr'ouvert
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301 USA
|
||||
|
||||
import random
|
||||
from quixote.html import htmltext, TemplateIO
|
||||
from wcs.qommon.form import CompositeWidget, IntWidget, WidgetList, StringWidget, \
|
||||
CheckboxWidget, SingleSelectWidget
|
||||
from wcs.fields import WidgetField, register_field_class
|
||||
from pyvotecore import schulze_method, irv, ranked_pairs, schulze_pr, \
|
||||
schulze_stv, schulze_npr
|
||||
|
||||
class VoteWidget(CompositeWidget):
|
||||
readonly = False
|
||||
|
||||
def __init__(self, name, value = None, elements = None, **kwargs):
|
||||
CompositeWidget.__init__(self, name, value, **kwargs)
|
||||
self.element_names = {}
|
||||
|
||||
if kwargs.has_key('title'):
|
||||
del kwargs['title']
|
||||
if kwargs.has_key('readonly'):
|
||||
if kwargs['readonly']:
|
||||
self.readonly = True
|
||||
del kwargs['readonly']
|
||||
if kwargs.has_key('required'):
|
||||
if kwargs['required']:
|
||||
self.required = True
|
||||
del kwargs['required']
|
||||
|
||||
self.randomize_items = False
|
||||
if kwargs.has_key('randomize_items'):
|
||||
if kwargs['randomize_items']:
|
||||
self.randomize_items = True
|
||||
del kwargs['randomize_items']
|
||||
|
||||
for v in elements:
|
||||
if type(v) is tuple:
|
||||
title = v[1]
|
||||
key = v[0]
|
||||
if type(key) is int:
|
||||
name = 'element%d' % v[0]
|
||||
elif type(key) in (str, htmltext):
|
||||
name = str('element%s' % v[0])
|
||||
key = str(key)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
else:
|
||||
title = v
|
||||
key = v
|
||||
name = 'element%d' % len(self.element_names.keys())
|
||||
|
||||
if value:
|
||||
position = value.get(key)
|
||||
else:
|
||||
position = None
|
||||
self.add(IntWidget, name, title = title, value = position, size = 5, **kwargs)
|
||||
self.element_names[name] = key
|
||||
|
||||
if self.randomize_items:
|
||||
random.shuffle(self.widgets)
|
||||
|
||||
if self.readonly:
|
||||
def cmp_w(x, y):
|
||||
if x.value is None and y.value is None:
|
||||
return 0
|
||||
if x.value is None:
|
||||
return 1
|
||||
if y.value is None:
|
||||
return -1
|
||||
return cmp(x.value, y.value)
|
||||
self.widgets.sort(cmp_w)
|
||||
|
||||
|
||||
def _parse(self, request):
|
||||
values = {}
|
||||
for name in self.element_names:
|
||||
value = self.get(name)
|
||||
values[self.element_names[name]] = value
|
||||
if type(value) is not int:
|
||||
self.get_widget(name).set_error(IntWidget.TYPE_ERROR)
|
||||
self.value = values or None
|
||||
|
||||
def parse(self, request=None):
|
||||
value = CompositeWidget.parse(self, request=request)
|
||||
for widget in self.widgets:
|
||||
if widget.has_error():
|
||||
self.set_error(_('Some fields were not filled properly.'))
|
||||
break
|
||||
return value
|
||||
|
||||
def render_content(self):
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<ul>')
|
||||
for widget in self.get_widgets():
|
||||
if widget.has_error():
|
||||
r += htmltext('<li class="error"><label>')
|
||||
else:
|
||||
r += htmltext('<li><label>')
|
||||
if self.readonly:
|
||||
widget.attrs['disabled'] = 'disabled'
|
||||
if widget.value:
|
||||
r += htmltext('<input type="hidden" name="%s" value="%s" >') % (
|
||||
widget.name, widget.value)
|
||||
widget.name = widget.name + 'xx'
|
||||
r += widget.render_content()
|
||||
r += htmltext('</label>')
|
||||
r += widget.title
|
||||
r += htmltext('</li>')
|
||||
r += htmltext('</ul>')
|
||||
return r.getvalue()
|
||||
|
||||
METHODS = {
|
||||
'condorcet-schulze': N_('Condorcet-Shulze'),
|
||||
'irv': N_('Instant Run-off'),
|
||||
'ranked_pairs': N_('Ranked pairs'),
|
||||
'schulze-pr': N_('Schulze Proportional Ranking'),
|
||||
'schulze-stv': N_('Schulze STV'),
|
||||
'schulze-npr': N_('Schulze NPR'),
|
||||
}
|
||||
|
||||
class VoteField(WidgetField):
|
||||
key = 'vote-field'
|
||||
description = N_('Ranked choice vote')
|
||||
|
||||
items = None
|
||||
randomize_items = True
|
||||
widget_class = VoteWidget
|
||||
required_winners = None
|
||||
winner_threshold = None
|
||||
tallying_method = 'condorcet-schulze'
|
||||
|
||||
def perform_more_widget_changes(self, form, kwargs, edit = True):
|
||||
kwargs['elements'] = self.items or []
|
||||
kwargs['randomize_items'] = self.randomize_items
|
||||
|
||||
def fill_admin_form(self, form):
|
||||
WidgetField.fill_admin_form(self, form)
|
||||
form.add(WidgetList, 'items', title = _('Items'), element_type = StringWidget,
|
||||
value = self.items, required = True,
|
||||
element_kwargs = {'render_br': False, 'size': 50},
|
||||
add_element_label = _('Add item'))
|
||||
form.add(CheckboxWidget, 'randomize_items', title = _('Randomize Items'),
|
||||
value = self.randomize_items)
|
||||
form.add(SingleSelectWidget, 'tallying_method', title=_('Tallying method'),
|
||||
value=self.tallying_method,
|
||||
options=METHODS.items())
|
||||
form.add(IntWidget, 'required_winners', title=_('Required winners'),
|
||||
value=self.required_winners),
|
||||
form.add(IntWidget, 'winner_threshold', title=_('Winner threshold'),
|
||||
value=self.winner_threshold),
|
||||
|
||||
def get_admin_attributes(self):
|
||||
return WidgetField.get_admin_attributes(self) + ['items',
|
||||
'randomize_items', 'tallying_method', 'required_winners',
|
||||
'winner_threshold']
|
||||
|
||||
def get_view_value(self, value):
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<ul>')
|
||||
items = value.items()
|
||||
items.sort(lambda x,y: cmp(x[1], y[1]))
|
||||
for it in items:
|
||||
if it[1]:
|
||||
r += htmltext('<li>%s: %s</li>') % (it[1], it[0])
|
||||
r += htmltext('</ul>')
|
||||
return r.getvalue()
|
||||
|
||||
def stats(self, values):
|
||||
'''
|
||||
Compute vote result using the pyvotecore library.
|
||||
'''
|
||||
r = TemplateIO(html = True)
|
||||
|
||||
votes = [x.data.get(self.id) for x in values]
|
||||
# build ballots
|
||||
kwargs = { 'winner_threshold': self.winner_threshold,
|
||||
'required_winners': self.required_winners }
|
||||
if self.tallying_method == 'condorcet-schulze':
|
||||
votes = [{ 'count': 1, 'ballot': x } for x in votes if x]
|
||||
method = schulze_method.SchulzeMethod
|
||||
elif self.tallying_method == 'irv':
|
||||
votes = [ x.items() for x in votes ]
|
||||
votes = [ sorted(x, cmp=lambda x,y: cmp(x[1],y[1])) for x in votes ]
|
||||
votes = [ {'count':1, 'ballot': [ a for a,b in x ] } for x in votes ]
|
||||
|
||||
method = irv.IRV
|
||||
elif self.tallying_method == 'ranked_pairs':
|
||||
votes = [{ 'count': 1, 'ballot': x } for x in votes if x]
|
||||
method = ranked_pairs.RankedPairs
|
||||
elif self.tallying_method == 'schulze-pr':
|
||||
votes = [{ 'count': 1, 'ballot': x } for x in votes if x]
|
||||
method = schulze_pr.SchulzePR
|
||||
elif self.tallying_method == 'schulze-stv':
|
||||
votes = [{ 'count': 1, 'ballot': x } for x in votes if x]
|
||||
method = schulze_stv.SchulzeSTV
|
||||
elif self.tallying_method == 'schulze-npr':
|
||||
votes = [{ 'count': 1, 'ballot': x } for x in votes if x]
|
||||
method = schulze_npr.SchulzeNPR
|
||||
else:
|
||||
raise ValueError, 'unknown method', self.tallying_method
|
||||
# Restrain arguments
|
||||
accepted_args = method.__init__.im_func.func_code.co_varnames
|
||||
for key in kwargs.keys():
|
||||
if key not in accepted_args or kwargs.get(key) is None:
|
||||
del kwargs[key]
|
||||
# Run vote
|
||||
result = method(votes, **kwargs).as_dict()
|
||||
|
||||
r += htmltext('<h4>%s</h4>') % _('Method')
|
||||
r += htmltext('<p>%s</p>') % _(METHODS.get(self.tallying_method))
|
||||
if 'candidates' in result:
|
||||
r += htmltext('<h4>%s</h4>') % _('Candidates')
|
||||
r += htmltext('<p>%s</p>') % (', '.join(result['candidates']))
|
||||
if 'quota' in result:
|
||||
r += htmltext('<h4>%s</h4>') % _('Quota')
|
||||
r += htmltext('<p>%s</p>') % result.get('quota')
|
||||
if 'rounds' in result:
|
||||
for i, _round in enumerate(result['rounds']):
|
||||
r += htmltext('<h4>%s</h4><div class="round">') % (_('Round %s') % (i+1))
|
||||
if 'loser' in _round:
|
||||
r += htmltext('<h5>%s</h5>') % _('Loser')
|
||||
r += htmltext('<p>%s</p>') % _round['loser']
|
||||
if 'tallies' in _round:
|
||||
r += htmltext('<h5>%s</h5><ul>') % _('Tallies')
|
||||
for a, b in _round['tallies'].iteritems():
|
||||
r += htmltext('<li>%s: %s</li>') % (a,b)
|
||||
r += htmltext('</ul>')
|
||||
if 'tied_losers' in _round:
|
||||
r += htmltext('<h5>%s</h5>') % _('Tied losers')
|
||||
r += htmltext('<p>%s</p>') % ', '.join(list(_round['tied_losers']))
|
||||
if 'winner' in _round:
|
||||
r += htmltext('<h5>%s</h5>') % _('Winner')
|
||||
r += htmltext('<p>%s</p>') % _round['winner']
|
||||
r += htmltext('</div>')
|
||||
if 'pairs' in result:
|
||||
r += htmltext('<h4>%s</h4><dl>') % _('Pairs')
|
||||
for a, b in result['pairs'].iteritems():
|
||||
r += htmltext('<dt>%s</dt>') % ', '.join(a)
|
||||
r += htmltext('<dd>%s</dd>') % b
|
||||
r += htmltext('</dl>')
|
||||
if 'strong_pairs' in result:
|
||||
r += htmltext('<h4>%s</h4><dl>') % _('Strong pairs')
|
||||
for a, b in result['strong_pairs'].iteritems():
|
||||
r += htmltext('<dt>%s</dt>') % ', '.join(a)
|
||||
r += htmltext('<dd>%s</dd>') % b
|
||||
r += htmltext('</dl>')
|
||||
|
||||
if 'winner' in result:
|
||||
r += htmltext('<h4>%s</h4>') % _('Winner')
|
||||
r += htmltext('<p>%s</p>') % result['winner']
|
||||
if 'winners' in result:
|
||||
r += htmltext('<h4>%s</h4>') % _('Winners')
|
||||
r += htmltext('<p>%s</p>') % ', '.join(result['winners'])
|
||||
if 'order' in result:
|
||||
r += htmltext('<h4>%s</h4>') % _('Order')
|
||||
r += htmltext('<p>%s</p>') % ', '.join(result['order'])
|
||||
# FIXME: show actions
|
||||
# import pprint
|
||||
# r += htmltext('<pre>%s</pre>') % pprint.pformat(result)
|
||||
return r.getvalue()
|
||||
|
||||
register_field_class(VoteField)
|
|
@ -1,109 +0,0 @@
|
|||
# w.c.s. - web application for online forms
|
||||
# Copyright (C) 2005-2010 Entr'ouvert
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301 USA
|
||||
|
||||
from quixote import get_publisher
|
||||
|
||||
from wcs.fields import WidgetField, register_field_class
|
||||
from wcs.qommon.form import *
|
||||
|
||||
class VoteAnonymityWidget(CheckboxWidget):
|
||||
vote_anonymity = 'anonymous'
|
||||
|
||||
def __init__(self, name, value = None, elements = None, **kwargs):
|
||||
if kwargs and kwargs.get('vote_anonymity'):
|
||||
self.vote_anonymity = kwargs.get('vote_anonymity')
|
||||
if kwargs and 'required' in kwargs:
|
||||
del kwargs['required']
|
||||
CheckboxWidget.__init__(self, name, value=value, elements=elements, **kwargs)
|
||||
|
||||
def render(self):
|
||||
if self.vote_anonymity != 'choice':
|
||||
# XXX: include info for user ?
|
||||
return ''
|
||||
return CheckboxWidget.render(self)
|
||||
|
||||
def render_title(self, title):
|
||||
return CheckboxWidget.render_title(self, _('Anonymous Voting'))
|
||||
|
||||
def render_content(self):
|
||||
value = True
|
||||
if self.value:
|
||||
if self.value[0] == 'anonymous':
|
||||
value = True
|
||||
else:
|
||||
value = False
|
||||
return htmltag('input', xml_end=True,
|
||||
type='checkbox',
|
||||
name=self.name,
|
||||
value='yes',
|
||||
checked=value and 'checked' or None,
|
||||
**self.attrs)
|
||||
|
||||
def _parse(self, request):
|
||||
if self.vote_anonymity == 'anonymous':
|
||||
self.value = ('anonymous', None)
|
||||
elif self.vote_anonymity == 'public':
|
||||
self.value = ('public', request.user.id)
|
||||
else:
|
||||
if type(request.form.get(self.name)) is tuple:
|
||||
self.value = request.form.get(self.name)
|
||||
else:
|
||||
CheckboxWidget._parse(self, request)
|
||||
if self.value is True:
|
||||
self.value = ('anonymous', None)
|
||||
else:
|
||||
self.value = ('public', request.user.id)
|
||||
|
||||
|
||||
class VoteAnonymityField(WidgetField):
|
||||
key = 'vote-anonymity'
|
||||
description = N_('Vote Anonymity')
|
||||
|
||||
widget_class = VoteAnonymityWidget
|
||||
vote_anonymity = 'anonymous'
|
||||
# possible values:
|
||||
# 'anonymous': anonymous vote
|
||||
# 'public': public vote
|
||||
# 'choice': anonymous or public, choice of voter
|
||||
|
||||
def fill_admin_form(self, form):
|
||||
form.add(StringWidget, 'label', title = _('Label'), value = self.label,
|
||||
required = True, size = 50)
|
||||
form.add(SingleSelectWidget, 'vote_anonymity', title = _('Vote Anonymity'),
|
||||
value = self.vote_anonymity,
|
||||
options = [('anonymous', _('Anonymous Vote')),
|
||||
('public', _('Public Vote')),
|
||||
('choice', _('Choice of Voter'))])
|
||||
|
||||
def get_admin_attributes(self):
|
||||
return ['label', 'vote_anonymity']
|
||||
|
||||
def perform_more_widget_changes(self, form, kwargs, edit = True):
|
||||
kwargs['vote_anonymity'] = self.vote_anonymity
|
||||
|
||||
def get_view_value(self, value):
|
||||
public = False
|
||||
if self.vote_anonymity == 'choice':
|
||||
if value and value[0] == 'public':
|
||||
public = True
|
||||
if public:
|
||||
return get_publisher().user_class.get(value[1]).get_display_name()
|
||||
else:
|
||||
return _('Anonymous') # XXX: include token?
|
||||
|
||||
register_field_class(VoteAnonymityField)
|
|
@ -1,281 +0,0 @@
|
|||
# w.c.s. - web application for online forms
|
||||
# Copyright (C) 2005-2010 Entr'ouvert
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301 USA
|
||||
|
||||
import random
|
||||
from quixote.html import htmltext, TemplateIO
|
||||
from wcs.qommon.form import *
|
||||
from wcs.fields import WidgetField, register_field_class
|
||||
|
||||
class RankedItemsWidget(CompositeWidget):
|
||||
readonly = False
|
||||
|
||||
def __init__(self, name, value = None, elements = None, **kwargs):
|
||||
CompositeWidget.__init__(self, name, value, **kwargs)
|
||||
self.element_names = {}
|
||||
|
||||
if kwargs.has_key('title'):
|
||||
del kwargs['title']
|
||||
if kwargs.has_key('readonly'):
|
||||
if kwargs['readonly']:
|
||||
self.readonly = True
|
||||
del kwargs['readonly']
|
||||
if kwargs.has_key('required'):
|
||||
if kwargs['required']:
|
||||
self.required = True
|
||||
del kwargs['required']
|
||||
|
||||
self.randomize_items = False
|
||||
if kwargs.has_key('randomize_items'):
|
||||
if kwargs['randomize_items']:
|
||||
self.randomize_items = True
|
||||
del kwargs['randomize_items']
|
||||
|
||||
for v in elements:
|
||||
if type(v) is tuple:
|
||||
title = v[1]
|
||||
key = v[0]
|
||||
if type(key) is int:
|
||||
name = 'element%d' % v[0]
|
||||
elif type(key) in (str, htmltext):
|
||||
name = str('element%s' % v[0])
|
||||
key = str(key)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
else:
|
||||
title = v
|
||||
key = v
|
||||
name = 'element%d' % len(self.element_names.keys())
|
||||
|
||||
if value:
|
||||
position = value.get(key)
|
||||
else:
|
||||
position = None
|
||||
self.add(IntWidget, name, title = title, value = position, size = 5, **kwargs)
|
||||
self.element_names[name] = key
|
||||
|
||||
if self.randomize_items:
|
||||
random.shuffle(self.widgets)
|
||||
|
||||
if self.readonly:
|
||||
def cmp_w(x, y):
|
||||
if x.value is None and y.value is None:
|
||||
return 0
|
||||
if x.value is None:
|
||||
return 1
|
||||
if y.value is None:
|
||||
return -1
|
||||
return cmp(x.value, y.value)
|
||||
self.widgets.sort(cmp_w)
|
||||
|
||||
|
||||
def _parse(self, request):
|
||||
values = {}
|
||||
for name in self.element_names:
|
||||
value = self.get(name)
|
||||
values[self.element_names[name]] = value
|
||||
if type(value) is not int:
|
||||
self.get_widget(name).set_error(IntWidget.TYPE_ERROR)
|
||||
self.value = values or None
|
||||
|
||||
def parse(self, request=None):
|
||||
value = CompositeWidget.parse(self, request=request)
|
||||
for widget in self.widgets:
|
||||
if widget.has_error():
|
||||
self.set_error(_('Some fields were not filled properly.'))
|
||||
break
|
||||
return value
|
||||
|
||||
def render_content(self):
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<ul>')
|
||||
for widget in self.get_widgets():
|
||||
if widget.has_error():
|
||||
r += htmltext('<li class="error"><label>')
|
||||
else:
|
||||
r += htmltext('<li><label>')
|
||||
if self.readonly:
|
||||
widget.attrs['disabled'] = 'disabled'
|
||||
if widget.value:
|
||||
r += htmltext('<input type="hidden" name="%s" value="%s" >') % (
|
||||
widget.name, widget.value)
|
||||
widget.name = widget.name + 'xx'
|
||||
r += widget.render_content()
|
||||
r += htmltext('</label>')
|
||||
r += widget.title
|
||||
r += htmltext('</li>')
|
||||
r += htmltext('</ul>')
|
||||
return r.getvalue()
|
||||
|
||||
|
||||
class RankedItemsField(WidgetField):
|
||||
key = 'ranked-items'
|
||||
description = N_('Ranked Items')
|
||||
|
||||
items = None
|
||||
randomize_items = True
|
||||
widget_class = RankedItemsWidget
|
||||
|
||||
def perform_more_widget_changes(self, form, kwargs, edit = True):
|
||||
kwargs['elements'] = self.items or []
|
||||
kwargs['randomize_items'] = self.randomize_items
|
||||
|
||||
def fill_admin_form(self, form):
|
||||
WidgetField.fill_admin_form(self, form)
|
||||
form.add(WidgetList, 'items', title = _('Items'), element_type = StringWidget,
|
||||
value = self.items, required = True,
|
||||
element_kwargs = {'render_br': False, 'size': 50},
|
||||
add_element_label = _('Add item'))
|
||||
form.add(CheckboxWidget, 'randomize_items', title = _('Randomize Items'),
|
||||
value = self.randomize_items)
|
||||
|
||||
def get_admin_attributes(self):
|
||||
return WidgetField.get_admin_attributes(self) + ['items', 'randomize_items']
|
||||
|
||||
def get_view_value(self, value):
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<ul>')
|
||||
items = value.items()
|
||||
items.sort(lambda x,y: cmp(x[1], y[1]))
|
||||
for it in items:
|
||||
if it[1]:
|
||||
r += htmltext('<li>%s: %s</li>') % (it[1], it[0])
|
||||
r += htmltext('</ul>')
|
||||
return r.getvalue()
|
||||
|
||||
def stats(self, values):
|
||||
r = TemplateIO(html = True)
|
||||
|
||||
# hardcoded to condorcet for the moment
|
||||
candidates = self.items
|
||||
# compute matrix
|
||||
matrix = {}
|
||||
for c1 in candidates:
|
||||
matrix[c1] = {}
|
||||
for c2 in candidates:
|
||||
matrix[c1][c2] = 0
|
||||
votes = [x.data.get(self.id) for x in values]
|
||||
votes = [x for x in votes if x]
|
||||
for vote in votes:
|
||||
for c1 in candidates:
|
||||
for c2 in candidates:
|
||||
if c1 == c2:
|
||||
continue
|
||||
vote_a = vote.get(c1)
|
||||
vote_b = vote.get(c2)
|
||||
if vote_a is None:
|
||||
vote_a = 99999 # XXX MAX_INT
|
||||
if vote_b is None:
|
||||
vote_b = 99999 # idem
|
||||
if int(vote_a) == int(vote_b):
|
||||
matrix[c1][c2] += 0.5
|
||||
elif int(vote_a) < int(vote_b):
|
||||
matrix[c1][c2] += 1
|
||||
import pprint
|
||||
pprint.pprint(matrix)
|
||||
# compute ratings
|
||||
ratings = {}
|
||||
for c1 in candidates:
|
||||
ratings[c1] = {'win': [], 'loss': [], 'tie': [], 'worst': 0}
|
||||
for c2 in candidates:
|
||||
if c1 == c2:
|
||||
continue
|
||||
delta = matrix[c1][c2] - matrix[c2][c1]
|
||||
if delta > 0:
|
||||
ratings[c1]['win'].append(c2)
|
||||
elif delta < 0:
|
||||
ratings[c1]['loss'].append(c2)
|
||||
if delta < ratings[c1]['worst']:
|
||||
ratings[c1]['worst'] = -delta
|
||||
else:
|
||||
ratings[c1]['tie'].append(c2)
|
||||
|
||||
pprint.pprint(ratings)
|
||||
|
||||
# compute winner
|
||||
winners = []
|
||||
remaining = candidates[:]
|
||||
for c1 in remaining:
|
||||
rating = ratings[c1]
|
||||
winner = True
|
||||
for loss in rating['loss']:
|
||||
if loss not in winners:
|
||||
winner = False
|
||||
break
|
||||
if not winner:
|
||||
continue
|
||||
for tie in rating['tie']:
|
||||
if tie not in winners:
|
||||
winner = False
|
||||
break
|
||||
if not winner:
|
||||
continue
|
||||
winners.append(c1)
|
||||
remaining.remove(c1)
|
||||
break
|
||||
else:
|
||||
narrowest = None
|
||||
winners = []
|
||||
for c2 in remaining:
|
||||
rating = ratings[c2]
|
||||
if narrowest is None or rating['worst'] < narrowest:
|
||||
narrowest = rating['worst']
|
||||
winners = [c2]
|
||||
elif rating['worst'] == narrowest:
|
||||
winners.append(c2)
|
||||
|
||||
candidates.sort(lambda x,y: cmp(len(ratings[x]['win']), len(ratings[y]['win'])))
|
||||
|
||||
r += htmltext('<table>'
|
||||
'<thead>'
|
||||
'<tr>'
|
||||
'<td></td>')
|
||||
for c in candidates:
|
||||
r += htmltext('<th>%s</th>' % c)
|
||||
r += htmltext('</tr>'
|
||||
'</thead>'
|
||||
'<tbody>')
|
||||
for c1 in candidates:
|
||||
r += htmltext('<tr>'
|
||||
'<th>%s</th>' % c1)
|
||||
for c2 in candidates:
|
||||
if c2 == c1:
|
||||
r += htmltext('<td></td>')
|
||||
else:
|
||||
if matrix[c1][c2] > matrix[c2][c1]:
|
||||
color = '#0f0'
|
||||
elif matrix[c1][c2] == matrix[c2][c1]:
|
||||
color = '#ff0'
|
||||
else:
|
||||
color = '#f00'
|
||||
r += htmltext('<td style="background: %s">' % color)
|
||||
r += '%.1f' % matrix[c1][c2]
|
||||
r += htmltext('</td>')
|
||||
r += htmltext('</tr>')
|
||||
r += htmltext('</tbody>'
|
||||
'</table>'
|
||||
'<p>')
|
||||
r += _('Winner:')
|
||||
r += ' '
|
||||
r += ' '.join(winners)
|
||||
r += htmltext('</p>')
|
||||
return r.getvalue()
|
||||
|
||||
register_field_class(RankedItemsField)
|
||||
|
||||
|
||||
|
|
@ -1184,17 +1184,7 @@ class FormDef(StorableObject):
|
|||
def get_detailed_email_form(self, formdata, url):
|
||||
details = []
|
||||
|
||||
display_username = True
|
||||
# this is custom code so it is possible to mark forms as anonyms, this
|
||||
# is done through the VoteAnonymity field, this is very specific but
|
||||
# isn't generalised yet into an useful extension mechanism, as it's not
|
||||
# clear at the moment what could be useful.
|
||||
for f in self.fields:
|
||||
if f.key == 'vote-anonymity':
|
||||
display_username = False
|
||||
break
|
||||
|
||||
if display_username and formdata.user_id and formdata.user:
|
||||
if formdata.user_id and formdata.user:
|
||||
details.append(_('User name:'))
|
||||
details.append(' %s' % formdata.user.name)
|
||||
details.append('')
|
||||
|
|
|
@ -350,15 +350,6 @@ class FormStatusPage(Directory, FormTemplateMixin):
|
|||
except KeyError:
|
||||
user = None
|
||||
|
||||
# this is custom code so it is possible to mark forms as anonyms, this
|
||||
# is done through the VoteAnonymity field, this is very specific but
|
||||
# isn't generalised yet into an useful extension mechanism, as it's not
|
||||
# clear at the moment what could be useful.
|
||||
for f in self.formdef.fields:
|
||||
if f.key == 'vote-anonymity':
|
||||
user = None
|
||||
break
|
||||
|
||||
r = TemplateIO(html=True)
|
||||
klasses = 'foldable'
|
||||
if self.should_fold_summary(mine, request_user):
|
||||
|
|
Loading…
Reference in New Issue