741 lines
31 KiB
Python
Executable File
741 lines
31 KiB
Python
Executable File
#!/usr/bin/env 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 Server"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
import sys
|
|
|
|
glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install
|
|
sys.path.insert(0, glasnostPythonDir)
|
|
|
|
import glasnost
|
|
|
|
import glasnost.common.context as context
|
|
from glasnost.common.ElectionsCommon import *
|
|
import glasnost.common.faults as faults
|
|
import glasnost.common.tools_new as commonTools
|
|
import glasnost.common.xhtmlgenerator as X
|
|
|
|
from glasnost.server.ObjectsServer import register, ObjectServerMixin, \
|
|
AdminServerMixin, ObjectsServer
|
|
from glasnost.server.tools import *
|
|
|
|
from glasnost.proxy.AtomsProxy import Atom
|
|
from glasnost.proxy.BrevesProxy import BreveElectionCandidateAdded, \
|
|
BreveElectionRunning, BreveElectionClosed
|
|
from glasnost.proxy.CacheProxy import invalidateValue
|
|
from glasnost.proxy.DispatcherProxy import getApplicationId
|
|
from glasnost.proxy.GroupsProxy import getSetContainedIds
|
|
|
|
|
|
applicationName = 'ElectionsServer'
|
|
applicationRole = 'elections'
|
|
dispatcher = None
|
|
|
|
|
|
class AdminElections(AdminServerMixin, AdminElectionsCommon):
|
|
pass
|
|
register(AdminElections)
|
|
|
|
|
|
class ElectionMixin(ObjectServerMixin):
|
|
def checkAddIsPossible(self):
|
|
ObjectServerMixin.checkAddIsPossible(self)
|
|
if self.winnersGroupId:
|
|
group = getProxyForServerRole('groups').getObject(
|
|
self.winnersGroupId)
|
|
if len(group.getContainedIds()) > 0:
|
|
raise faults.WinnersGroupNotEmpty()
|
|
|
|
def importVoteFromXmlRpc(self, voteImport):
|
|
return commonTools.importThing(voteImport)
|
|
|
|
|
|
class AbstractElection(ElectionMixin, AbstractElectionCommon):
|
|
pass
|
|
register(AbstractElection)
|
|
|
|
|
|
class ElectionAverage(ElectionMixin, ElectionAverageCommon):
|
|
def compute(self):
|
|
virtualServerId = self.getServer().computeVirtualServerId(self.id)
|
|
self.candidateIds = getSetContainedIds(self.candidatesSet)
|
|
voterIds = self.getVoterIds(virtualServerId)
|
|
|
|
if not self.canCompute():
|
|
if self.ratings is not None:
|
|
del self.ratings
|
|
if self.winnerIds is not None:
|
|
del self.winnerIds
|
|
return
|
|
|
|
# Compute the ratings.
|
|
ratings = {}
|
|
for candidateId in self.candidateIds:
|
|
ratings[candidateId] = 0
|
|
count = 0
|
|
votes = getProxyForServerRole('ballots').getElectionVotes(
|
|
self.voteTokens, voterIds)
|
|
if self.weightingsGradeId is not None:
|
|
weightingsGrade = getProxyForServerRole('grades').getObject(
|
|
self.weightingsGradeId)
|
|
weightingsById = weightingsGrade.repairMarks(
|
|
weightingsGrade.marks, voterIds)
|
|
weightings = getProxyForServerRole('ballots'
|
|
).getElectionWeightings(self.voteTokens, weightingsById)
|
|
else:
|
|
weightings = None
|
|
for vote in votes:
|
|
if not vote.isAbstention() and not vote.isBlank(self.candidateIds):
|
|
if weightings is not None:
|
|
weighting = weightings[vote.voterToken]
|
|
else:
|
|
weighting = 1
|
|
count = count + weighting
|
|
marks = vote.repairMarks(vote.marks, self.candidateIds)
|
|
for candidateId, mark in marks.items():
|
|
ratings[candidateId] += weighting * mark
|
|
if count > 0:
|
|
count = float(count)
|
|
for candidateId in self.candidateIds:
|
|
ratings[candidateId] = ratings[candidateId] / count
|
|
self.ratings = ratings
|
|
|
|
# Compute the winners.
|
|
if self.winnersCount > 0:
|
|
winnerIds = self.candidateIds[:]
|
|
def winnersSorter(x, y, ratings = ratings):
|
|
return cmp(ratings[y], ratings[x])
|
|
winnerIds.sort(winnersSorter)
|
|
winnersCount = self.winnersCount
|
|
if len(winnerIds) > 0 and winnersCount < len(winnerIds):
|
|
lastRating = ratings[winnerIds[winnersCount - 1]]
|
|
for winnerId in winnerIds[winnersCount:]:
|
|
if ratings[winnerId] < lastRating:
|
|
break
|
|
winnersCount = winnersCount + 1
|
|
self.winnerIds = winnerIds[:winnersCount]
|
|
elif self.winnerIds is not None:
|
|
del self.winnerIds
|
|
|
|
# Update the winners' group, if it exists.
|
|
if self.winnerIds and self.winnersGroupId is not None:
|
|
group = getProxyForServerRole('groups').getObject(
|
|
self.winnersGroupId)
|
|
group.membersSet = self.winnerIds
|
|
group.nonMembersSet = []
|
|
getProxyForServerRole('groups').modifyObject(group)
|
|
|
|
# Update the winners' grade, if it exists.
|
|
if self.winnersGradeId is not None:
|
|
grade = getProxyForServerRole('grades').getObject(
|
|
self.winnersGradeId)
|
|
grade.membersSet = self.candidateIds
|
|
grade.marks = self.ratings
|
|
getProxyForServerRole('grades').modifyObject(grade)
|
|
register(ElectionAverage)
|
|
|
|
|
|
def _addToPotentialWinners(candidateId, newWinnerIds, winnerIds, ratings):
|
|
if candidateId in newWinnerIds:
|
|
return
|
|
if candidateId in winnerIds:
|
|
return
|
|
newWinnerIds.append(candidateId)
|
|
rating = ratings[candidateId]
|
|
for tieId in rating['ties']:
|
|
_addToPotentialWinners(tieId, newWinnerIds, winnerIds, ratings)
|
|
for lossId in rating['losses']:
|
|
_addToPotentialWinners(lossId, newWinnerIds, winnerIds, ratings)
|
|
|
|
|
|
class ElectionCondorcet(ElectionMixin, ElectionCondorcetCommon):
|
|
def compute(self):
|
|
virtualServerId = self.getServer().computeVirtualServerId(self.id)
|
|
self.candidateIds = getSetContainedIds(self.candidatesSet)
|
|
voterIds = self.getVoterIds(virtualServerId)
|
|
|
|
if not self.canCompute():
|
|
if self.pairwiseMatrix is not None:
|
|
del self.pairwiseMatrix
|
|
if self.ratings is not None:
|
|
del self.ratings
|
|
if self.winnerIds is not None:
|
|
del self.winnerIds
|
|
return
|
|
|
|
# Compute the pairwise matrix.
|
|
pairwiseMatrix = {}
|
|
for candidateId in self.candidateIds:
|
|
pairwiseMatrix[candidateId] = row = {}
|
|
for candidateId2 in self.candidateIds:
|
|
row[candidateId2] = 0
|
|
votes = getProxyForServerRole('ballots').getElectionVotes(
|
|
self.voteTokens, voterIds)
|
|
if self.weightingsGradeId is not None:
|
|
weightingsGrade = getProxyForServerRole('grades').getObject(
|
|
self.weightingsGradeId)
|
|
weightingsById = weightingsGrade.repairMarks(
|
|
weightingsGrade.marks, voterIds)
|
|
weightings = getProxyForServerRole('ballots'
|
|
).getElectionWeightings(self.voteTokens, weightingsById)
|
|
else:
|
|
weightings = None
|
|
for vote in votes:
|
|
if not vote.isAbstention() and not vote.isBlank(self.candidateIds):
|
|
if weightings is not None:
|
|
weighting = weightings[vote.voterToken]
|
|
else:
|
|
weighting = 1
|
|
marks = vote.repairMarks(vote.marks, self.candidateIds)
|
|
for candidateId, mark in marks.items():
|
|
for candidateId2, mark2 in marks.items():
|
|
if candidateId == candidateId2:
|
|
continue
|
|
if mark == mark2:
|
|
pairwiseMatrix[candidateId][candidateId2] += \
|
|
weighting / 2.0
|
|
elif mark < mark2:
|
|
pairwiseMatrix[candidateId][candidateId2] += \
|
|
weighting
|
|
self.pairwiseMatrix = pairwiseMatrix
|
|
|
|
# Compute the ratings.
|
|
ratings = {}
|
|
for candidateId in self.candidateIds:
|
|
rating = {
|
|
'wins': [],
|
|
'losses': [],
|
|
'ties': [],
|
|
'worstDefeat': 0,
|
|
}
|
|
for candidateId2 in self.candidateIds:
|
|
if candidateId == candidateId2:
|
|
continue
|
|
delta = pairwiseMatrix[candidateId][candidateId2] \
|
|
- pairwiseMatrix[candidateId2][candidateId]
|
|
if delta > 0:
|
|
rating['wins'].append(candidateId2)
|
|
elif delta < 0:
|
|
rating['losses'].append(candidateId2)
|
|
if delta < rating['worstDefeat']:
|
|
rating['worstDefeat'] = -delta
|
|
else:
|
|
rating['ties'].append(candidateId2)
|
|
ratings[candidateId] = rating
|
|
self.ratings = ratings
|
|
|
|
# Compute the winners.
|
|
if self.winnersCount >= len(self.candidateIds):
|
|
self.winnerIds = self.candidateIds[:]
|
|
elif self.winnersCount > 0:
|
|
winnerIds = []
|
|
remaining = self.candidateIds[:]
|
|
for k in range(self.winnersCount):
|
|
if len(winnerIds) >= self.winnersCount:
|
|
break
|
|
# If there is a candidate with no loss and no tie, then it is a
|
|
# winner.
|
|
for remainingId in remaining:
|
|
remainingRating = ratings[remainingId]
|
|
isWinner = 1
|
|
for lossId in remainingRating['losses']:
|
|
if lossId not in winnerIds:
|
|
isWinner = 0
|
|
break
|
|
if not isWinner:
|
|
continue
|
|
for tieId in remainingRating['ties']:
|
|
if tieId not in winnerIds:
|
|
isWinner = 0
|
|
break
|
|
if not isWinner:
|
|
continue
|
|
winnerIds.append(remainingId)
|
|
remaining.remove(remainingId)
|
|
break
|
|
else:
|
|
# If there is a group of candidates who lose only between
|
|
# them and if this group is smaller or equal to the
|
|
# remaining winners to find, add this group to the winners.
|
|
smallestGroup = remaining[:]
|
|
for remainingId in remaining:
|
|
newWinnerIds = []
|
|
_addToPotentialWinners(remainingId, newWinnerIds,
|
|
winnerIds, ratings)
|
|
if len(newWinnerIds) < len(smallestGroup):
|
|
smallestGroup = newWinnerIds[:]
|
|
if len(winnerIds) + len(newWinnerIds) \
|
|
<= self.winnersCount:
|
|
for winnerId in newWinnerIds:
|
|
winnerIds.append(winnerId)
|
|
remaining.remove(winnerId)
|
|
break
|
|
else:
|
|
# No candidate respects the Condorcet criterion.
|
|
# Choose the ones with the narrowest worst defeat.
|
|
narrowestWorstDefeat = None
|
|
newWinnerIds = None
|
|
for remainingId in smallestGroup:
|
|
remainingRating = ratings[remainingId]
|
|
if narrowestWorstDefeat is None \
|
|
or remainingRating['worstDefeat'] \
|
|
< narrowestWorstDefeat:
|
|
narrowestWorstDefeat = remainingRating[
|
|
'worstDefeat']
|
|
newWinnerIds = [remainingId]
|
|
elif remainingRating['worstDefeat'] \
|
|
== narrowestWorstDefeat:
|
|
newWinnerIds.append(remainingId)
|
|
## if len(winnerIds) + len(newWinnerIds) \
|
|
## <= self.winnersCount:
|
|
for winnerId in newWinnerIds:
|
|
winnerIds.append(winnerId)
|
|
remaining.remove(winnerId)
|
|
self.winnerIds = winnerIds
|
|
elif self.winnerIds is not None:
|
|
del self.winnerIds
|
|
|
|
# Update the winners' group, if it exists.
|
|
if self.winnerIds and self.winnersGroupId is not None:
|
|
group = getProxyForServerRole('groups').getObject(
|
|
self.winnersGroupId)
|
|
group.membersSet = self.winnerIds
|
|
group.nonMembersSet = []
|
|
getProxyForServerRole('groups').modifyObject(group)
|
|
register(ElectionCondorcet)
|
|
|
|
|
|
class ElectionsServer(ElectionsCommonMixin, ObjectsServer):
|
|
def abstainForVote(self, objectId, previousVoteToken):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if object.state != 'running':
|
|
raise faults.UserAccessDenied()
|
|
if object.voteTokens is None:
|
|
object.voteTokens = []
|
|
if previousVoteToken \
|
|
and object.voteTokens is not None \
|
|
and previousVoteToken in object.voteTokens:
|
|
del object.voteTokens[object.voteTokens.index(
|
|
previousVoteToken)]
|
|
if len(object.voteTokens) == 0:
|
|
del object.voteTokens
|
|
virtualServer.markObjectAsDirty(object)
|
|
|
|
def addObjectXmlRpc(self, objectImport):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = commonTools.importThing(objectImport)
|
|
# User must be an admin or (a writer and a writer of this object))
|
|
if not self.isAdmin() and not (
|
|
self.useAdminWritersSet
|
|
and getProxyForServerRole('identities').setContainsUser(
|
|
self.getAdminCore().writersSet) and (
|
|
getProxyForServerRole('identities').setContainsUser(
|
|
object.writersSet))) \
|
|
or object.state != 'draft':
|
|
if not object.canBeCreatedByClient():
|
|
raise faults.UserAccessDenied()
|
|
object.checkAddIsPossible()
|
|
object.setAutomaticalSlots()
|
|
virtualServer.objects[object.id] = object
|
|
object.saveNonCore()
|
|
object.releaseNonCore()
|
|
virtualServer.markObjectAsDirty(object)
|
|
virtualServer.markCoreAsDirty()
|
|
return object.id
|
|
|
|
def canDeleteObject(self, objectId):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
if not virtualServer.canLoadObjectCore(objectId):
|
|
return 0
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
isAdmin = self.isAdmin()
|
|
if not isAdmin and not getProxyForServerRole('identities'
|
|
).setContainsUser(object.writersSet):
|
|
return 0
|
|
if not isAdmin and object.state != 'draft':
|
|
return 0
|
|
return 1
|
|
|
|
def canModifyObject(self, objectId):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
if not virtualServer.canLoadObjectCore(objectId):
|
|
return 0
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if not object.canBeModified():
|
|
return 0
|
|
isAdmin = self.isAdmin()
|
|
if not isAdmin and not getProxyForServerRole('identities'
|
|
).setContainsUser(object.writersSet):
|
|
return 0
|
|
if not isAdmin and object.state != 'draft':
|
|
return 0
|
|
return 1
|
|
|
|
def canVote(self, objectId):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
if not virtualServer.canLoadObjectCore(objectId):
|
|
return 0
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if object.state != 'running':
|
|
return 0
|
|
return 1
|
|
|
|
def computeObject(self, objectId):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
virtualServer.objects[objectId].compute()
|
|
invalidateValue(objectId)
|
|
|
|
def getBallotKind(self, objectId):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
return object.ballotKind
|
|
|
|
def getLastObjectIds(
|
|
self, objectsCount, possibleAuthorsSet, possibleReadersSet,
|
|
possibleWritersSet, possibleStates):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
isAdmin = self.isAdmin()
|
|
try:
|
|
possibleAuthorIds = getSetContainedIds(
|
|
possibleAuthorsSet, ['people'], raiseWhenUncountable = 1)
|
|
except faults.UncountableGroup:
|
|
possibleAuthorIds = 'everybody'
|
|
try:
|
|
possibleReaderIds = getSetContainedIds(
|
|
possibleReadersSet, ['identities'],
|
|
raiseWhenUncountable = 1)
|
|
except faults.UncountableGroup:
|
|
possibleReaderIds = 'everybody'
|
|
try:
|
|
possibleWriterIds = getSetContainedIds(
|
|
possibleWritersSet, ['identities'],
|
|
raiseWhenUncountable = 1)
|
|
except faults.UncountableGroup:
|
|
possibleWriterIds = 'everybody'
|
|
objectIds = virtualServer.objects.keys()
|
|
def modificationTimeSorter(xId, yId,
|
|
virtualServer = virtualServer):
|
|
return cmp(virtualServer.loadObjectCore(yId).modificationTime,
|
|
virtualServer.loadObjectCore(xId).modificationTime)
|
|
objectIds.sort(modificationTimeSorter)
|
|
result = []
|
|
for objectId in objectIds:
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if possibleStates and not object.state in possibleStates:
|
|
continue
|
|
if not isAdmin and not getProxyForServerRole(
|
|
'identities').setContainsUser(object.readersSet):
|
|
continue
|
|
if not self.getLastObjectIds_filter(
|
|
possibleAuthorIds, 1, object.authorsSet):
|
|
continue
|
|
if not self.getLastObjectIds_filter(
|
|
possibleReaderIds, 1, object.readersSet):
|
|
continue
|
|
if not self.getLastObjectIds_filter(
|
|
possibleWriterIds, 1, object.writersSet):
|
|
continue
|
|
result.append(objectId)
|
|
if objectsCount != -1 and len(result) >= objectsCount:
|
|
break
|
|
return result
|
|
|
|
def getLastObjectIdsWithVoter(self, objectsCount, voterId, possibleStates):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
objectIds = virtualServer.objects.keys()
|
|
def modificationTimeSorter(xId, yId,
|
|
virtualServer = virtualServer):
|
|
return cmp(virtualServer.loadObjectCore(yId).modificationTime,
|
|
virtualServer.loadObjectCore(xId).modificationTime)
|
|
objectIds.sort(modificationTimeSorter)
|
|
result = []
|
|
for objectId in objectIds:
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if not self.isAdmin() \
|
|
and not getProxyForServerRole('identities'
|
|
).setContainsUser(object.readersSet):
|
|
continue
|
|
if possibleStates and not object.state in possibleStates:
|
|
continue
|
|
if not voterId in object.getVoterIds(virtualServerId):
|
|
continue
|
|
result.append(objectId)
|
|
if len(result) >= objectsCount:
|
|
break
|
|
return result
|
|
|
|
def getVoteXmlRpc(self, objectId, voteToken):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if object.votes is None or not object.votes.has_key(voteToken):
|
|
raise faults.UnknownVoteToken(voteToken)
|
|
vote = object.votes[voteToken]
|
|
result = vote.exportToXmlRpc()
|
|
return result
|
|
|
|
def modifyObjectXmlRpc(self,objectImport):
|
|
objectChanges = commonTools.importThing(objectImport)
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = virtualServer.loadObjectCore(objectChanges.id)
|
|
object.acquireNonCore()
|
|
try:
|
|
if not object.canBeModified():
|
|
raise faults.ReadOnlyObject()
|
|
previousState = object.state
|
|
newState = objectChanges.state
|
|
if not self.canModifyObject(object.id) or not (
|
|
self.isAdmin()
|
|
or not objectChanges.hasSlotName('writersSet')
|
|
or getProxyForServerRole('identities'
|
|
).setContainsUser(
|
|
objectChanges.getSlot('writersSet').getValue())):
|
|
if not object.canBeModifiedByClient():
|
|
raise faults.UserAccessDenied()
|
|
if not self.isAdmin() and previousState != newState \
|
|
and (previousState != 'draft'
|
|
or newState != 'proposed'):
|
|
raise faults.UserAccessDenied()
|
|
object.checkModifyIsPossible(objectChanges)
|
|
object.modify(objectChanges)
|
|
object.compute()
|
|
object.saveNonCore()
|
|
finally:
|
|
object.releaseNonCore()
|
|
virtualServer.markObjectAsDirty(object)
|
|
brevesProxy = getProxyForServerRole('breves')
|
|
if brevesProxy:
|
|
if newState == 'running' and previousState != newState:
|
|
breve = BreveElectionRunning()
|
|
breve.language = object.getLanguage()
|
|
breve.electionId = object.id
|
|
breve.electionTitle = object.title
|
|
breve.electionSubject = object.subject
|
|
breve.readersSet = object.readersSet
|
|
if breve.readersSet is not None:
|
|
breve.readersSet = breve.readersSet[:]
|
|
breve.writersSet = object.writersSet
|
|
if breve.writersSet is not None:
|
|
breve.writersSet = breve.writersSet[:]
|
|
try:
|
|
brevesProxy.addObject(breve, serverId = virtualServerId)
|
|
except faults.UnknownServerId:
|
|
pass
|
|
elif newState == 'closed' and previousState != newState:
|
|
breve = BreveElectionClosed()
|
|
breve.language = object.getLanguage()
|
|
breve.electionId = object.id
|
|
breve.electionTitle = object.title
|
|
breve.electionSubject = object.subject
|
|
breve.readersSet = object.readersSet
|
|
if breve.readersSet is not None:
|
|
breve.readersSet = breve.readersSet[:]
|
|
breve.writersSet = object.writersSet
|
|
if breve.writersSet is not None:
|
|
breve.writersSet = breve.writersSet[:]
|
|
try:
|
|
brevesProxy.addObject(breve, serverId = virtualServerId)
|
|
except faults.UnknownServerId:
|
|
pass
|
|
invalidateValue(object.id)
|
|
return object.version
|
|
|
|
def pesterAbstentionnistsXmlRpc(self, objectId, httpHostName, httpPort,
|
|
httpScriptDirectoryPath):
|
|
httpHostName = iso8859_15(httpHostName)
|
|
httpPort = iso8859_15(httpPort)
|
|
httpScriptDirectoryPath = iso8859_15(httpScriptDirectoryPath)
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
if not virtualServer.canLoadObjectCore(objectId):
|
|
return 0
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if object.state != 'running':
|
|
return 0
|
|
|
|
|
|
if httpHostName:
|
|
context.setVar('httpHostName', httpHostName)
|
|
if httpPort:
|
|
context.setVar('httpPort', httpPort)
|
|
if httpScriptDirectoryPath:
|
|
context.setVar(
|
|
'httpScriptDirectoryPath', httpScriptDirectoryPath)
|
|
electionUrl = X.idUrl(objectId).getAsAbsoluteUrl()
|
|
|
|
fromAddress = virtualServer.adminEmailAddress
|
|
abstentionnistsCount = 0
|
|
for voterId in object.getVoterIds(virtualServerId):
|
|
voter = getProxyForServerRole('identities').getObject(voterId)
|
|
if voter.voteTokens is not None \
|
|
and voter.voteTokens.has_key(objectId):
|
|
continue
|
|
abstentionnistsCount += 1
|
|
toAddress = voter.email
|
|
message = """\
|
|
REMINDER
|
|
|
|
The election "%(label)s" is running.
|
|
You can vote at the URL:
|
|
%(url)s
|
|
|
|
Please, do it now!
|
|
""" % {
|
|
'label': object.getLabel(),
|
|
'url': electionUrl,
|
|
}
|
|
sendMail(
|
|
mailFrom = fromAddress,
|
|
mailTo = toAddress,
|
|
mailSubject = "[Glasnost] Election in Progress",
|
|
mailMessage = message,
|
|
mailPerson = voter,
|
|
)
|
|
return abstentionnistsCount
|
|
|
|
def registerPublicMethods(self):
|
|
ObjectsServer.registerPublicMethods(self)
|
|
self.registerPublicMethod('abstainForVote')
|
|
self.registerPublicMethod('canVote')
|
|
self.registerPublicMethod('computeObject')
|
|
self.registerPublicMethod('getBallotKind')
|
|
self.registerPublicMethod('getLastObjectIdsWithVoter')
|
|
self.registerPublicMethod('getVote', self.getVoteXmlRpc)
|
|
self.registerPublicMethod('pesterAbstentionnists',
|
|
self.pesterAbstentionnistsXmlRpc)
|
|
self.registerPublicMethod('vote')
|
|
|
|
def repairVirtualServer(self, virtualServer, version):
|
|
changed = 0
|
|
if version < 2006:
|
|
for object in virtualServer.objects.values():
|
|
if hasattr(object, 'isSecret'):
|
|
changed = 1
|
|
object.ballotKind = object.isSecret
|
|
del object.isSecret
|
|
if version < 3005:
|
|
for object in virtualServer.objects.values():
|
|
if object.readersSet is None and object.writersSet is None:
|
|
changed = 1
|
|
object.writersSet = object.authorsSet
|
|
if object.writersSet is not None:
|
|
object.writersSet = object.writersSet[:]
|
|
if object.state in ['refused', 'trashed']:
|
|
del object.state # => object.state = 'draft'
|
|
if object.state == 'draft':
|
|
object.readersSet = object.authorsSet
|
|
if object.readersSet is not None:
|
|
object.readersSet = object.readersSet[:]
|
|
if version < 4000:
|
|
changed = virtualServer.admin.repair(4000) or changed
|
|
for id, object in virtualServer.objects.items():
|
|
newId = repairId(id)
|
|
if newId:
|
|
changed = 1
|
|
del virtualServer.objects[id]
|
|
virtualServer.objects[newId] = object
|
|
changed = object.repair(4000) or changed
|
|
if not object.__dict__.has_key('language'):
|
|
changed = 1
|
|
object.language = 'fr'
|
|
if version < 5004:
|
|
changed = virtualServer.admin.repair(5004) or changed
|
|
for id, object in virtualServer.objects.items():
|
|
changed = object.repair(5004) or changed
|
|
if version <= 1013000:
|
|
admin = virtualServer.admin
|
|
if admin.id is None:
|
|
changed = 1
|
|
admin.id = '%s/__admin__' % virtualServer.virtualServerId
|
|
if version <= 1018000:
|
|
for object in virtualServer.objects.values():
|
|
if object.voteTokens is None:
|
|
continue
|
|
for i in range(len(object.voteTokens)):
|
|
voteToken = object.voteTokens[i]
|
|
if not voteToken.startswith('glasnost://'):
|
|
changed = 1
|
|
object.voteTokens[i] = '%s/votes/%s' % (
|
|
commonTools.extractDispatcherId(
|
|
virtualServer.virtualServerId),
|
|
voteToken)
|
|
if changed:
|
|
virtualServer.markAllAsDirtyFIXME()
|
|
|
|
def vote(self, objectId, voteToken, previousVoteToken):
|
|
virtualServerId = context.getVar('applicationId')
|
|
virtualServer = self.getVirtualServer(virtualServerId)
|
|
object = virtualServer.loadObjectCore(objectId)
|
|
if object.state != 'running':
|
|
raise faults.UserAccessDenied()
|
|
if object.voteTokens is None:
|
|
object.voteTokens = []
|
|
if previousVoteToken \
|
|
and previousVoteToken in object.voteTokens:
|
|
del object.voteTokens[object.voteTokens.index(
|
|
previousVoteToken)]
|
|
object.voteTokens.append(voteToken)
|
|
virtualServer.markObjectAsDirty(object)
|
|
|
|
|
|
electionsServer = ElectionsServer()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
electionsServer.launch(applicationName, applicationRole)
|