# -*- 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'

    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
        identitiesProxy = getProxyForServerRole('identities')
        voterIds = [voterId
                    for voterId in self.getVoterIds()
                    if identitiesProxy.hasObject(voterId)]
        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)
        identitiesProxy = getProxyForServerRole('identities')
        if object.state == 'running' \
                and identitiesProxy.setContainsUser(object.votersSet):
            layout += X.buttonStandalone(
                    'vote',
                    X.roleUrl('votes', 'edit').add('electionId', object.id))
        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

