This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
glasnost/shared/web/ElectionsWeb.py

892 lines
35 KiB
Python

# -*- coding: iso-8859-15 -*-
# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
# Romain Chantereau <rchantereau@entrouvert.com>
# Nicolas Clapiès <nclapies@easter-eggs.org>
# Pierre-Antoine Dejace <padejace@entrouvert.be>
# Thierry Dulieu <tdulieu@easter-eggs.com>
# Florent Monnier <monnier@codelutin.com>
# Cédric Musso <cmusso@easter-eggs.org>
# Frédéric Péters <fpeters@entrouvert.be>
# Benjamin Poussin <poussin@codelutin.com>
# Emmanuel Raviart <eraviart@entrouvert.com>
# Sébastien Régnier <regnier@codelutin.com>
# Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
# Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
# Régnier, Emmanuel Saracco, Théridion & Vecam
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
__doc__ = """Glasnost Elections Web Proxy"""
__version__ = '$Revision$'[11:-2]
import glasnost.common.context as context
import glasnost.common.faults as faults
import glasnost.common.tools_new as commonTools
from glasnost.proxy.ElectionsProxy import *
from ObjectsWeb import register, AdminMixin, ObjectWebMixin, ObjectsWebMixin
from tools import *
import VotesWeb # Do not remove!
try:
import Graphs
except ImportError:
Graphs = None
class AdminElections(AdminMixin, AdminElections):
pass
register(AdminElections)
class ElectionMixin(ObjectWebMixin):
ballotKind_kind_defaultValue = 'voterChoice'
ballotKind_kind_widget_fieldLabel = N_('Kind of Ballots')
ballotKind_kind_widget_labels = {
'public': N_('Public Ballots'),
'secret': N_('Secret Ballots'),
'voterChoice': N_('Voters\' Choice'),
}
ballotKind_kind_widgetName = 'Select'
candidateIds_kind_stateInEditMode = 'hidden'
candidateIds_kind_stateInViewMode = 'hidden'
candidatesSet_kind_defaultValue = [None, None, None]
candidatesSet_kind_itemKind_value_widgetName = 'SelectId'
candidatesSet_kind_widget_fieldLabel = N_('Candidates')
candidatesSet_kind_widgetName = 'Multi'
endTime_kind_widget_fieldLabel = N_('End Time')
isAlwaysRunning_kind_defaultValue = 0
isAlwaysRunning_kind_widget_fieldLabel = N_('Election Duration')
isAlwaysRunning_kind_widget_labels = {
'0': N_('One-Time'),
'1': N_('Never Ending'),
}
isAlwaysRunning_kind_widgetName = 'InputCheckBox'
method = None
method_kind_defaultValue = 'condorcet'
#method_kind_hasToMakeFieldFromValue = 0
method_kind_isRequired = 1
method_kind_values = [
'average',
'condorcet',
]
method_kind_widget_apply = 1
method_kind_widget_fieldLabel = N_('Electoral Method')
method_kind_widget_labels = {
'average': N_('Best Average'),
'condorcet': N_('Condorcet Method'),
}
method_kindName = 'Choice'
state_kind_defaultValue = 'draft'
state_kind_widget_apply = 1
state_kind_widget_fieldLabel = N_('State')
state_kind_widget_labels = {
'closed': N_('Ended'),
'draft': N_('Being Written'),
'proposed': N_('Submitted for Evaluation'),
'running': N_('Running'),
}
state_kind_widgetName = 'Select'
subject_kind_widget_fieldLabel = N_('Subject')
subject_kind_widget_cols = 75
subject_kind_widget_colSpan = 2
subject_kind_widget_rows = 4
subject_kind_widgetName = 'TextArea'
title_kind_widget_fieldLabel = N_('Title')
title_kind_widget_size = 40
title_kind_widgetName = 'InputText'
voteKind_kind_defaultValue = 'VoteRanking'
voteKind_kind_widget_fieldLabel = N_('Kind of Vote')
voteKind_kind_widget_labels = {
'VoteApproval': N_('Approval'),
'VoteDistribution': N_('Percentage'),
'VoteRanking': N_('Ranking'),
'VoteRating': N_('Rating'),
}
voteKind_kind_widgetName = 'Select'
votersSet_kind_itemKind_value_widgetName = 'SelectId'
votersSet_kind_widget_fieldLabel = N_('Voters')
votersSet_kind_widgetName = 'Multi'
voteTokens_kind_stateInEditMode = 'hidden'
voteTokens_kind_stateInViewMode = 'hidden'
weightingsGradeId_kind_widget_fieldLabel = N_('Voters Weightings')
weightingsGradeId_kind_widgetName = 'SelectId'
winnerIds_kind_itemKind_value_widgetName = 'SelectId'
winnerIds_kind_widget_fieldLabel = N_('Winners')
winnerIds_kind_widgetName = 'Multi'
winnersCount_kind_defaultValue = 1
winnersCount_kind_min = 0
winnersCount_kind_textMaxLength = 3
winnersCount_kind_widget_fieldLabel = N_('Winners Count')
winnersCount_kind_widget_size = 3
winnersCount_kind_widgetName = 'InputText'
winnersGroupId_kind_widget_fieldLabel = N_('Winners Group')
winnersGroupId_kind_widgetName = 'SelectId'
def countVotes(self):
ballotsWeb = getWebForServerRole('ballots')
registeredVotersCount = 0
abstentionsCount = 0
blanksCount = 0
voterIds = self.getVoterIds()
registeredVotersCount = len(voterIds)
votes = ballotsWeb.getElectionVotes(self.voteTokens, voterIds)
nonAbstentionsCount = 0
if len(votes) > 0:
candidateIds = self.getSortedCandidateIds()
for vote in votes:
nonAbstentionsCount = nonAbstentionsCount + 1
if vote.isBlank(candidateIds):
blanksCount = blanksCount + 1
abstentionsCount = registeredVotersCount - nonAbstentionsCount
return registeredVotersCount, abstentionsCount, blanksCount
def getEditLayoutReadOnlySlotNames(self, fields, parentSlot = None):
readOnlySlotNames = ObjectWebMixin.getEditLayoutReadOnlySlotNames(
self, fields, parentSlot = parentSlot)
readOnlySlotNames = readOnlySlotNames[:]
if not self.id:
slotName = 'state'
if not slotName in readOnlySlotNames:
readOnlySlotNames.append(slotName)
if self.state in ['closed', 'running']:
for slotName in [
'ballotKind', 'candidatesSet', 'method', 'subject', 'title',
'voteKind', 'votersSet', 'weightingsGradeId']:
if not slotName in readOnlySlotNames:
readOnlySlotNames.append(slotName)
return readOnlySlotNames
def getEditLayoutSlotNames(self, fields, parentSlot = None):
slotNames = ObjectWebMixin.getEditLayoutSlotNames(
self, fields, parentSlot = parentSlot)
slotNames = slotNames[:]
for slotName in ['winnerIds']:
if slotName in slotNames:
slotNames.remove(slotName)
return slotNames
def getResultsLayout(
self, registeredVotersCount, abstentionsCount, blanksCount):
layout = X.array(
X.h3()(_('Analyze')),
X.table(_class = 'election-results')(
X.caption(_('Analyse of the election')),
X.tr(
X.th(_class = 'field-label')(_('Registered Voters:')),
X.td(_class = 'field-value')(registeredVotersCount),
),
X.tr(
X.th(_class = 'field-label')(_('Abstentions:')),
X.td(_class = 'field-value')(abstentionsCount),
),
X.tr(
X.th(_class = 'field-label')(_('Voters:')),
X.td(_class = 'field-value')(
registeredVotersCount - abstentionsCount),
),
X.tr(
X.th(_class = 'field-label')(_('Blank Ballots:')),
X.td(_class = 'field-value')(blanksCount),
),
X.tr(
X.th(_class = 'field-label')(_('Expressed Votes:')),
X.td(_class = 'field-value')(
registeredVotersCount - abstentionsCount - blanksCount),
),
))
return layout
def getViewLayoutSlotNames(self, fields, parentSlot = None):
slotNames = ObjectWebMixin.getViewLayoutSlotNames(
self, fields, parentSlot = parentSlot)
slotNames = slotNames[:]
userToken = context.getVar('userToken', default = '')
if not userToken or context.getVar('useCompactLayout', default = 0):
for slotName in ('authorsSet', 'creationTime', 'modificationTime',
'readersSet', 'writersSet'):
if slotName in slotNames:
slotNames.remove(slotName)
if self.weightingsGradeId is None:
slotName = 'weightingsGradeId'
if slotName in slotNames:
slotNames.remove(slotName)
if self.winnersCount == 0:
slotName = 'winnersCount'
if slotName in slotNames:
slotNames.remove(slotName)
if self.winnersGroupId is None:
slotName = 'winnersGroupId'
if slotName in slotNames:
slotNames.remove(slotName)
if self.winnersCount == 0 or not self.canCompute():
slotName = 'winnerIds'
if slotName in slotNames:
slotNames.remove(slotName)
return slotNames
def getVotesLayout(self):
ballotsWeb = getWebForServerRole('ballots')
gradesWeb = getWebForServerRole('grades')
layout = X.array()
userVoteToken = None
userId = context.getVar('userId')
user = context.getVar('user')
if user is not None:
if user.voteTokens is not None \
and user.voteTokens.has_key(self.id):
userVoteToken = user.voteTokens[self.id]
voterIds = self.getVoterIds()
votes = ballotsWeb.getElectionVotes(self.voteTokens, voterIds)
if len(votes) > 0:
layout += X.h3()(_('Votes'))
table = X.table(_class = 'election-votes')
table += X.caption(_('Votes for the election'))
layout += table
table += X.colgroup()
thead = X.thead()
tr = X.tr()
thead += tr
tr += X.th()
if self.weightingsGradeId is not None:
weightingsGrade = gradesWeb.getObject(
self.weightingsGradeId)
weightings = weightingsGrade.repairMarks(
weightingsGrade.marks, voterIds)
else:
weightings = None
if weightings is not None:
table += X.colgroup()
tr += X.td()( _('Weightings') )
candidateIds = self.getSortedCandidateIds()
table += X.colgroup(span = len(candidateIds))
table += thead
tbody = X.tbody()
table += tbody
for candidateId in candidateIds:
tr += X.th(scope = 'col')(X.objectHypertextLabel(candidateId))
for vote in votes:
tr = X.tr()
tbody += tr
if vote.comment:
comment = X.array(
X.br(),
X.a(href = X.idUrl(vote.id))(_('comment')),
)
else:
comment = None
if self.ballotKind == 'secret' \
or self.ballotKind == 'voterChoice' \
and vote.ballotKind == 'secret':
tdAttributes = { 'scope': 'row' }
if vote.token == userVoteToken:
realToken = vote.token
token = X.a(href = X.roleUrl('votes').add(
'electionId', self.id).add(
'personId', userId))(vote.token)
tdAttributes['class'] = 'owned-vote'
else:
realToken = token = vote.token
shortToken = realToken
if shortToken.startswith(context.getVar('dispatcherId')):
shortToken = shortToken[len(context.getVar(
'dispatcherId'))+7:]
tr += X.th(**tdAttributes)(
X.array(_('Secret vote'),
X.br(),
_('(token: %s)') % shortToken, comment))
if weightings is not None:
if vote.token == userVoteToken:
voterId = ballotsWeb.getVoteVoterId(
vote.id)
tr += X.td()(
'%.2f' % (weightings[voterId] * 100),
X.nbsp,
'%',
)
else:
tr += X.td()( _('secret'))
else:
voterId = ballotsWeb.getVoteVoterId(vote.id)
tdAttributes = { 'scope': 'row'}
if vote.token == userVoteToken:
tdAttributes['class'] = 'owned-vote'
tr += X.th( **tdAttributes)(
X.objectHypertextLabel(voterId), comment)
if weightings is not None:
if vote.token == userVoteToken:
tr += X.td()(
'%.2f' % (weightings[voterId] * 100),
X.nbsp,
'%',
)
else:
tr += X.td()(_('secret'))
if vote.isBlank(candidateIds):
tr += X.td(colspan = len(candidateIds))(_('blank vote'))
else:
tr += vote.getMarksRowLayout(candidateIds)
return layout
def newVote(self):
vote = commonTools.newThing('object', 'votes.%s' % self.voteKind)
if self.ballotKind != 'voter-choice':
vote.ballotKind = self.ballotKind
return vote
class AbstractElection(ElectionMixin, AbstractElection):
pass
register(AbstractElection)
class ElectionAverage(ElectionMixin, ElectionAverage):
ratings_kind_stateInEditMode = 'hidden'
ratings_kind_stateInViewMode = 'hidden'
voteKind_kind_defaultValue = 'VoteRating'
winnersGradeId_kind_widget_fieldLabel = N_('Winners Grading')
winnersGradeId_kind_widgetName = 'SelectId'
def getAnalyzeLayout(self):
if self.winnerIds:
if len(self.winnerIds) == 1:
analyze = _(
'<p>The candidate "<i>%(candidate)s</i>" gets the '\
'best average rating.</p> <p>The candidate '\
'"<i>%(candidate)s</i>" wins the election.</p>') % {
'candidate': X.objectHypertextLabel(self.winnerIds[0]),
}
else:
candidates = '"<i>%s</i>"' % X.objectHypertextLabel(
self.winnerIds[0])
for winnerId in self.winnerIds[1:-1]:
candidates = candidates + ', "<i>%s</i>"' \
% X.objectHypertextLabel(winnerId)
candidates = candidates + ' &amp; "<i>%s</i>"' \
% X.objectHypertextLabel(self.winnerIds[-1])
analyze = _(
'<p>The candidates <i>%(candidates)s</i> get the '\
'best average ratings.</p> <p>The candidates '\
'%(candidates)s win the election.</p>') % {
'candidates': candidates,
}
else:
analyze = _(
"""<p>No candidate gets an average rating above the other ones.</p>
<p>The election has no winner.</p>""")
return X.asIs(analyze)
def getDetailsLayout(
self, registeredVotersCount, abstentionsCount, blanksCount):
if not self.ratings:
return None
for k in self.ratings.keys():
if self.ratings[k] is None:
self.ratings[k] = 0
layout = X.array()
layout += X.h3()(_('Results'))
table = X.table(_class = 'election-details')
table += X.caption(_('Details of the election'))
layout += table
table += X.tr(
X.th(scope = 'col')(_('Candidates')),
X.th(scope = 'col')(_('Means')),
)
minRating = min(self.ratings.values())
maxRating = max(self.ratings.values())
if minRating < maxRating:
averageRating = (maxRating + minRating) / 2.0
coef = 255 / float(averageRating - minRating)
candidateIds = self.getSortedCandidateIds()
for candidateId in candidateIds:
rating = self.ratings[candidateId]
if rating is None:
rating = 0
tdAttributes = {}
if minRating < maxRating:
if rating <= averageRating:
red = 255
green = coef * (rating - minRating)
else:
red = coef * (maxRating - rating)
green = 255
tdAttributes['style'] = \
'background-color: #%(red)02x%(green)02x00' % {
'green': green,
'red': red,
}
table += X.tr(
X.th(scope = 'row')(
X.objectHypertextLabel(candidateId)),
X.td(**tdAttributes)(
'%.2f' % (rating * 100),
X.nbsp,
'%',
),
)
return layout
def getGraphPie(self):
if not Graphs:
raise Exception('Graphing module not available')
candidateIds = self.getSortedCandidateIds()
slices = []
for candidateId in candidateIds:
slices.append(Graphs.Slice(
getObjectLabelTranslated(
candidateId, context.getVar('readLanguages')),
self.ratings[candidateId]))
return Graphs.getPie(slices)
def getSortedCandidateIds(self):
candidateIds = self.candidateIds[:]
if self.ratings is not None and len(self.ratings) > 0:
candidateIds.sort(lambda id1, id2, ratings = self.ratings:
cmp(ratings[id2], ratings[id1]))
return candidateIds
def getViewLayoutSlotNames(self, fields, parentSlot = None):
slotNames = ElectionMixin.getViewLayoutSlotNames(
self, fields, parentSlot = parentSlot)
slotNames = slotNames[:]
if self.winnersGradeId is None:
slotName = 'winnersGradeId'
if slotName in slotNames:
slotNames.remove(slotName)
return slotNames
register(ElectionAverage)
class ElectionCondorcet(ElectionMixin, ElectionCondorcet):
pairwiseMatrix_kind_stateInEditMode = 'hidden'
pairwiseMatrix_kind_stateInViewMode = 'hidden'
ratings_kind_stateInEditMode = 'hidden'
ratings_kind_stateInViewMode = 'hidden'
voteKind_kind_defaultValue = 'VoteRanking'
def getAnalyzeLayout(self):
if self.winnerIds:
if len(self.winnerIds) == 1:
analyze = _(
'<p>The candidate "<i>%(candidate)s</i>" beats each '\
'of the other candidates.</p><p>The candidate "<i> '\
'%(candidate)s</i>" wins the election.</p>') % {
'candidate': X.objectHypertextLabel(self.winnerIds[0]),
}
else:
candidates = '"<i>%s</i>"' % X.objectHypertextLabel(
self.winnerIds[0])
for winnerId in self.winnerIds[1:-1]:
candidates = candidates + ', "<i>%s</i>"' \
% X.objectHypertextLabel(winnerId)
candidates = candidates + ' &amp; "<i>%s</i>"' \
% X.objectHypertextLabel(self.winnerIds[-1])
analyze = _(
'<p>The candidates <i>%(candidates)s</i> beat each '\
'of the other candidates.</p><p>The candidates '\
'%(candidates)s win the election.</p>') % {
'candidates': candidates,
}
else:
analyze = _("""\
<p>No candidate beats every other candidates.</p><p>The election has no winner.</p>\
""")
return X.asIs(analyze)
def getDetailsLayout(
self, registeredVotersCount, abstentionsCount, blanksCount):
candidateIds = self.getSortedCandidateIds()
layout = X.array()
# Write the pairwise matrix.
if self.pairwiseMatrix is not None and len(self.pairwiseMatrix) > 0:
layout += X.h3()(
_('Candidates One-on-One Comparison'))
table = X.table(_class = 'election-pairwise-matrix')
table += X.caption(
_('Table with One-on-One comparisons of the candidates'))
layout += table
table += X.colgroup()
tr = X.tr(X.th())
thead = X.thead()
thead += tr
for candidateId in candidateIds:
tr += X.th(scope = 'col')(
X.objectHypertextLabel(candidateId))
table += X.colgroup(span = len(candidateIds))
table += thead
tbody = X.tbody()
table += tbody
for candidateId in candidateIds:
pairwiseRow = self.pairwiseMatrix[candidateId]
tr = X.tr(X.th(scope = 'row')(
X.objectHypertextLabel(candidateId)))
tbody += tr
for candidateId2 in candidateIds:
tdAttributes = {}
if candidateId2 != candidateId:
value = pairwiseRow[candidateId2]
peer = self.pairwiseMatrix[candidateId2][candidateId]
cell = '%.2f' % value
if value > peer:
bgcolor = '#00ff00'
elif value == peer:
bgcolor = '#ffff00'
else:
bgcolor = '#ff0000'
tdAttributes['style'] = 'background-color: %s' \
% bgcolor
else:
cell = X.nbsp
tr += X.td(**tdAttributes)(cell)
# Write the ratings.
if self.ratings is not None and len(self.ratings) > 0:
layout += X.h3()(_('Results'))
table = X.table(_class = 'election-ratings')
table += X.caption(_('Ratings for the election'))
layout += table
table += X.colgroup()
table += X.colgroup(span = 3)
table += X.thead(X.tr(
X.th()(_('Candidates')),
X.th(scope = 'col')(_('Wins')),
X.th(scope = 'col')(_('Losses')),
X.th(scope = 'col')(_('Ties')),
))
tbody = X.tbody()
table += tbody
scores = {}
for candidateId, rating in self.ratings.items():
scores[candidateId] = len(rating['wins']) \
+ len(rating['ties']) / 2.0
minScore = 0
maxScore = len(candidateIds) - 1
if minScore < maxScore:
averageScore = (maxScore + minScore) / 2.0
coef = 255 / float(averageScore - minScore)
for candidateId in candidateIds:
rating = self.ratings[candidateId]
tdAttributes = {}
if minScore < maxScore:
score = scores[candidateId]
if score <= averageScore:
red = 255
green = coef * (score - minScore)
else:
red = coef * (maxScore - score)
green = 255
tdAttributes['style'] = \
'background-color: #%(red)02x%(green)02x00' % {
'green': green,
'red': red,
}
tbody += X.tr(
X.th(scope = 'row')(
X.objectHypertextLabel(candidateId)),
X.td(**tdAttributes)(
len(rating['wins'])),
X.td(**tdAttributes)(
len(rating['losses'])),
X.td(**tdAttributes)(
len(rating['ties'])),
)
return layout
def getSortedCandidateIds(self):
candidateIds = self.candidateIds[:]
if self.ratings is not None and len(self.ratings) > 0:
scores = {}
for candidateId, rating in self.ratings.items():
scores[candidateId] = len(rating['wins']) \
+ len(rating['ties']) / 2.0
candidateIds.sort(lambda id1, id2, scores = scores:
cmp(scores[id2], scores[id1]))
return candidateIds
register(ElectionCondorcet)
class ElectionsWeb(ObjectsWebMixin, ElectionsProxy):
def clone(self, id):
if not id or not self.hasObject(id):
return pageNotFound()
if not self.canCloneObject(id):
return accessForbidden()
object = self.getObject(id)
object.id = None
object.version = 0
object.state = 'draft' # The difference is here.
return self.editObject(object)
clone.isPublicForWeb = 1
def confirmPesterAbstentionnists(self, id):
if not self.hasObject(id):
return pageNotFound()
if not self.canGetObject(id):
return accessForbidden()
election = self.getObject(id)
layout = X.array()
layout += X.div(_class = 'alert')(
_('Are you sure you want to pester by email '\
'the abstentionnists of election "%s"?') % \
election.getLabelTranslated(context.getVar('readLanguages')))
layout += X.br()
layout += X.buttonStandalone(
'pester-abstentionnists', X.idUrl(id, 'pesterAbstentionnists'))
return writePageLayout(layout,
_('Confirm Pestering of Abstentionnists by Email'),
canCache = 0)
confirmPesterAbstentionnists.isPublicForWeb = 1
def getObject_handleResult(self, lazyObject):
object = ElectionsProxy.getObject_handleResult(self,
lazyObject)
if self.__class__ is ElectionAverage:
object.getSlot('method').setValue('average')
else:
object.getSlot('method').setValue('condorcet')
return object
def getViewBelowButtonsBarLayout(self, object, fields):
layout = X.div(_class = 'election-results')
layout += ObjectsWebMixin.getViewBelowButtonsBarLayout(
self, object, fields)
if object.canCompute():
layout += X.hr()
layout += X.h2()(_('Counting of the Votes'))
layout += object.getVotesLayout()
registeredVotersCount, abstentionsCount, blanksCount \
= object.countVotes()
layout += object.getDetailsLayout(
registeredVotersCount, abstentionsCount, blanksCount)
layout += object.getResultsLayout(
registeredVotersCount, abstentionsCount, blanksCount)
if object.winnersCount > 0:
layout += object.getAnalyzeLayout()
elif object.state == 'running':
layout += X.hr()
layout += X.h2()(_('Election in progress'))
registeredVotersCount, abstentionsCount, blanksCount \
= object.countVotes()
layout += object.getResultsLayout(
registeredVotersCount, abstentionsCount, blanksCount)
return layout
def getViewOtherActionButtonsBarLayout(self, object, fields):
isAdmin = self.isAdmin()
layout = X.array()
layout += ObjectsWebMixin.getViewOtherActionButtonsBarLayout(
self, object, fields)
if object.state == 'running' \
and getProxyForServerRole('identities').setContainsUser(
object.votersSet):
layout += X.buttonStandalone(
'vote',
X.roleUrl('votes', 'edit').add('electionId', object.id))
identitiesProxy = getProxyForServerRole('identities')
if object.state == 'running' and \
(isAdmin or \
identitiesProxy.setContainsUser(object.writersSet)):
layout += X.buttonStandalone(
'pester-abstentionnists',
X.idUrl(object.id, 'confirmPesterAbstentionnists'))
return layout
def getVotesProxy(self):
return getWebForServerRole('votes')
def graphPie(self, id):
if not self.hasObject(id):
return pageNotFound()
if not self.canGetObject(id):
return accessForbidden()
election = self.getObject(id)
try:
png = election.getGraphPie()
except: # TODO: tighter check
if context.getVar('debug'):
raise
return pageNotFound()
req = context.getVar('req')
req.headers_out['Content-Length'] = str(len(png))
req.content_type = 'image/png'
setHttpCookie()
req.send_http_header()
if req.method == 'HEAD':
return OK
if req.caching:
req.openCachePage()
req.write(png)
if req.caching:
req.closeCachePage()
return OK
graphPie.isPublicForWeb = 1
def newObject(self, keywords = None):
if keywords and keywords.has_key('method') and \
keywords['method'] == 'average':
object = ElectionAverage()
else:
object = ElectionCondorcet()
return object
def pesterAbstentionnists(self, id):
if not self.hasObject(id):
return pageNotFound()
if not self.canGetObject(id):
return accessForbidden()
election = self.getObject(id)
try:
ElectionsProxy.pesterAbstentionnists(self, id)
except faults.Fault:
return failure(
_('The pestering of abstentionnists by email has failed!'),
X.idUrl(id))
return success(
_('The pestering of abstentionnists by email has succeeded!'),
X.idUrl(id))
pesterAbstentionnists.isPublicForWeb = 1
def submitAddObject(self, object):
try:
return ObjectsWebMixin.submitAddObject(self, object)
except faults.WinnersGroupNotEmpty, f:
context.setVar('error', 1)
object.setError('self.winnersGroupId', f)
return self.editObject(object)
def view(self, id):
if not self.hasObject(id):
return pageNotFound()
if not self.canGetObject(id):
return accessForbidden()
if context.getVar('userToken', default = ''):
self.computeObject(id)
return ObjectsWebMixin.view(self, id)
view.isPublicForWeb = 1
def viewAll(self):
context.push(_level = 'viewAll',
defaultDispatcherId = context.getVar('dispatcherId'))
try:
if not self.canGetObjects():
return accessForbidden()
isAdmin = self.isAdmin()
userId = context.getVar('userId')
if userId:
userSet = [userId]
else:
userSet = None
layout = X.array()
requiredSlotNames = [
'endTime', 'isAlwaysRunning', 'state', 'title']
displayedSlotNames = ['state', 'isAlwaysRunning', 'endTime']
if userSet:
lastElections = self.getLastObjects(
10, None, None, userSet, ['draft'],
requiredSlotNames)
layout += self.getObjectsSectionLayout(
lastElections,
_("""Your elections being written"""),
displayedSlotNames)
if isAdmin:
lastElections = self.getLastObjects(
10, None, None, None, ['proposed'],
requiredSlotNames)
layout += self.getObjectsSectionLayout(
lastElections,
_("""The elections being submitted for evaluation"""),
displayedSlotNames)
if userSet:
lastElections = self.getLastObjects(
10, None, None, userSet, ['proposed'],
requiredSlotNames)
layout += self.getObjectsSectionLayout(
lastElections,
_("""Your elections waiting for validation"""),
displayedSlotNames)
lastElections = self.getLastObjects(
50, None, None, None, ['running'],
requiredSlotNames)
layout += self.getObjectsSectionLayout(
lastElections,
_("""The elections in progress"""),
displayedSlotNames)
if userSet:
lastElections = self.getLastObjects(
10, None, None, userSet, ['closed'],
requiredSlotNames)
layout += self.getObjectsSectionLayout(
lastElections,
_("""Your last closed elections"""),
displayedSlotNames)
lastElections = self.getLastObjects(
10, None, None, None, ['closed'],
requiredSlotNames)
layout += self.getObjectsSectionLayout(
lastElections,
_("""The last closed elections"""),
displayedSlotNames)
layout += self.getViewAllButtonsBarLayout()
finally:
context.pull(_level = 'viewAll')
return writePageLayout(layout, _('Elections'))
viewAll.isPublicForWeb = 1