12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
1 |
# Copyright 2009 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
3 |
||
4983.1.2
by Curtis Hovey
Added pylint exceptions to database classes. |
4 |
# pylint: disable-msg=E0611,W0212
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
5 |
|
6 |
__metaclass__ = type |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
7 |
__all__ = [ |
8 |
'Poll', |
|
9 |
'PollOption', |
|
10 |
'PollOptionSet', |
|
11 |
'PollSet', |
|
12 |
'VoteCast', |
|
13 |
'Vote', |
|
14 |
'VoteSet', |
|
15 |
'VoteCastSet', |
|
16 |
]
|
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
17 |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
18 |
from datetime import datetime |
19 |
import random |
|
20 |
||
1716.1.90
by Christian Reis
Delintifying some of the database classes |
21 |
import pytz |
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
22 |
from sqlobject import ( |
23 |
AND, |
|
24 |
BoolCol, |
|
25 |
ForeignKey, |
|
26 |
IntCol, |
|
27 |
OR, |
|
28 |
SQLObjectNotFound, |
|
29 |
StringCol, |
|
30 |
)
|
|
31 |
from storm.store import Store |
|
32 |
from zope.component import getUtility |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
33 |
from zope.interface import implements |
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
34 |
|
35 |
from lp.registry.interfaces.person import validate_public_person |
|
7675.110.3
by Curtis Hovey
Ran the migration script to move registry code to lp.registry. |
36 |
from lp.registry.interfaces.poll import ( |
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
37 |
IPoll, |
38 |
IPollOption, |
|
39 |
IPollOptionSet, |
|
40 |
IPollSet, |
|
41 |
IVote, |
|
42 |
IVoteCast, |
|
43 |
IVoteCastSet, |
|
44 |
IVoteSet, |
|
45 |
OptionIsNotFromSimplePoll, |
|
46 |
PollAlgorithm, |
|
47 |
PollSecrecy, |
|
48 |
PollStatus, |
|
49 |
)
|
|
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
50 |
from lp.services.database.datetimecol import UtcDateTimeCol |
51 |
from lp.services.database.enumcol import EnumCol |
|
52 |
from lp.services.database.sqlbase import ( |
|
53 |
SQLBase, |
|
54 |
sqlvalues, |
|
55 |
)
|
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
56 |
|
57 |
||
58 |
class Poll(SQLBase): |
|
59 |
"""See IPoll."""
|
|
60 |
||
61 |
implements(IPoll) |
|
62 |
_table = 'Poll' |
|
2701
by Canonical.com Patch Queue Manager
[trivial] Do not access Foo._defaultOrder from other classes; use a public name instead |
63 |
sortingColumns = ['title', 'id'] |
64 |
_defaultOrder = sortingColumns |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
65 |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
66 |
team = ForeignKey( |
67 |
dbName='team', foreignKey='Person', |
|
68 |
storm_validator=validate_public_person, notNull=True) |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
69 |
|
70 |
name = StringCol(dbName='name', notNull=True) |
|
71 |
||
72 |
title = StringCol(dbName='title', notNull=True, unique=True) |
|
73 |
||
74 |
dateopens = UtcDateTimeCol(dbName='dateopens', notNull=True) |
|
75 |
||
76 |
datecloses = UtcDateTimeCol(dbName='datecloses', notNull=True) |
|
77 |
||
78 |
proposition = StringCol(dbName='proposition', notNull=True) |
|
79 |
||
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
80 |
type = EnumCol(dbName='type', enum=PollAlgorithm, |
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
81 |
default=PollAlgorithm.SIMPLE) |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
82 |
|
83 |
allowspoilt = BoolCol(dbName='allowspoilt', default=True, notNull=True) |
|
84 |
||
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
85 |
secrecy = EnumCol(dbName='secrecy', enum=PollSecrecy, |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
86 |
default=PollSecrecy.SECRET) |
87 |
||
2736.1.11
by Mark Shuttleworth
poll system fixes |
88 |
def newOption(self, name, title, active=True): |
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
89 |
"""See IPoll."""
|
2736.1.11
by Mark Shuttleworth
poll system fixes |
90 |
return getUtility(IPollOptionSet).new(self, name, title, active) |
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
91 |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
92 |
def isOpen(self, when=None): |
93 |
"""See IPoll."""
|
|
94 |
if when is None: |
|
95 |
when = datetime.now(pytz.timezone('UTC')) |
|
96 |
return (self.datecloses >= when and self.dateopens <= when) |
|
97 |
||
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
98 |
@property
|
99 |
def closesIn(self): |
|
100 |
"""See IPoll."""
|
|
101 |
return self.datecloses - datetime.now(pytz.timezone('UTC')) |
|
102 |
||
103 |
@property
|
|
104 |
def opensIn(self): |
|
105 |
"""See IPoll."""
|
|
106 |
return self.dateopens - datetime.now(pytz.timezone('UTC')) |
|
107 |
||
2219
by Canonical.com Patch Queue Manager
Some fixes as per Steve review. Do not use Subsets to traverse; instead do the traversal by consuming items from the traversal stack. r=SteveA |
108 |
def isClosed(self, when=None): |
109 |
"""See IPoll."""
|
|
110 |
if when is None: |
|
111 |
when = datetime.now(pytz.timezone('UTC')) |
|
112 |
return self.datecloses <= when |
|
113 |
||
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
114 |
def isNotYetOpened(self, when=None): |
115 |
"""See IPoll."""
|
|
116 |
if when is None: |
|
117 |
when = datetime.now(pytz.timezone('UTC')) |
|
118 |
return self.dateopens > when |
|
119 |
||
2219
by Canonical.com Patch Queue Manager
Some fixes as per Steve review. Do not use Subsets to traverse; instead do the traversal by consuming items from the traversal stack. r=SteveA |
120 |
def getAllOptions(self): |
121 |
"""See IPoll."""
|
|
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
122 |
return getUtility(IPollOptionSet).selectByPoll(self) |
123 |
||
124 |
def getActiveOptions(self): |
|
125 |
"""See IPoll."""
|
|
126 |
return getUtility(IPollOptionSet).selectByPoll(self, only_active=True) |
|
127 |
||
128 |
def getVotesByPerson(self, person): |
|
129 |
"""See IPoll."""
|
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
130 |
return Vote.selectBy(person=person, poll=self) |
2219
by Canonical.com Patch Queue Manager
Some fixes as per Steve review. Do not use Subsets to traverse; instead do the traversal by consuming items from the traversal stack. r=SteveA |
131 |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
132 |
def personVoted(self, person): |
133 |
"""See IPoll."""
|
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
134 |
results = VoteCast.selectBy(person=person, poll=self) |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
135 |
return bool(results.count()) |
136 |
||
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
137 |
def removeOption(self, option, when=None): |
138 |
"""See IPoll."""
|
|
139 |
assert self.isNotYetOpened(when=when) |
|
140 |
if option.poll != self: |
|
141 |
raise ValueError( |
|
142 |
"Can't remove an option that doesn't belong to this poll") |
|
143 |
option.destroySelf() |
|
144 |
||
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
145 |
def getOptionByName(self, name): |
146 |
"""See IPoll."""
|
|
147 |
return PollOption.selectOneBy(poll=self, name=name) |
|
148 |
||
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
149 |
def _assertEverythingOkAndGetVoter(self, person, when=None): |
150 |
"""Use assertions to Make sure all pre-conditions for a person to vote
|
|
151 |
are met.
|
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
152 |
|
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
153 |
Return the person if this is not a secret poll or None if it's a
|
154 |
secret one.
|
|
155 |
"""
|
|
156 |
assert self.isOpen(when=when), "This poll is not open" |
|
157 |
assert not self.personVoted(person), "Can't vote twice in the same poll" |
|
158 |
assert person.inTeam(self.team), ( |
|
159 |
"Person %r is not a member of this poll's team." % person) |
|
160 |
||
161 |
# We only associate the option with the person if the poll is not a
|
|
162 |
# SECRET one.
|
|
163 |
if self.secrecy == PollSecrecy.SECRET: |
|
164 |
voter = None |
|
165 |
else: |
|
166 |
voter = person |
|
167 |
return voter |
|
168 |
||
169 |
def storeCondorcetVote(self, person, options, when=None): |
|
170 |
"""See IPoll."""
|
|
171 |
voter = self._assertEverythingOkAndGetVoter(person, when=when) |
|
172 |
assert self.type == PollAlgorithm.CONDORCET |
|
173 |
voteset = getUtility(IVoteSet) |
|
174 |
||
175 |
token = voteset.newToken() |
|
176 |
votes = [] |
|
177 |
activeoptions = self.getActiveOptions() |
|
178 |
for option, preference in options.items(): |
|
179 |
assert option.poll == self, ( |
|
180 |
"The option %r doesn't belong to this poll" % option) |
|
181 |
assert option.active, "Option %r is not active" % option |
|
182 |
votes.append(voteset.new(self, option, preference, token, voter)) |
|
183 |
||
184 |
# Store a vote with preference = None for each active option of this
|
|
185 |
# poll that wasn't in the options argument.
|
|
186 |
for option in activeoptions: |
|
187 |
if option not in options: |
|
188 |
votes.append(voteset.new(self, option, None, token, voter)) |
|
189 |
||
190 |
getUtility(IVoteCastSet).new(self, person) |
|
191 |
return votes |
|
192 |
||
193 |
def storeSimpleVote(self, person, option, when=None): |
|
194 |
"""See IPoll."""
|
|
195 |
voter = self._assertEverythingOkAndGetVoter(person, when=when) |
|
196 |
assert self.type == PollAlgorithm.SIMPLE |
|
197 |
voteset = getUtility(IVoteSet) |
|
198 |
||
199 |
if option is None and not self.allowspoilt: |
|
200 |
raise ValueError("This poll doesn't allow spoilt votes.") |
|
201 |
elif option is not None: |
|
202 |
assert option.poll == self, ( |
|
203 |
"The option %r doesn't belong to this poll" % option) |
|
204 |
assert option.active, "Option %r is not active" % option |
|
205 |
token = voteset.newToken() |
|
206 |
# This is a simple-style poll, so you can vote only on a single option
|
|
207 |
# and this option's preference must be 1
|
|
208 |
preference = 1 |
|
209 |
vote = voteset.new(self, option, preference, token, voter) |
|
210 |
getUtility(IVoteCastSet).new(self, person) |
|
211 |
return vote |
|
212 |
||
213 |
def getTotalVotes(self): |
|
214 |
"""See IPoll."""
|
|
215 |
assert self.isClosed() |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
216 |
return Vote.selectBy(poll=self).count() |
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
217 |
|
218 |
def getWinners(self): |
|
219 |
"""See IPoll."""
|
|
220 |
assert self.isClosed() |
|
4664.1.1
by Curtis Hovey
Normalized comments for bug 3732. |
221 |
# XXX: GuilhermeSalgado 2005-08-24:
|
222 |
# For now, this method works only for SIMPLE-style polls. This is
|
|
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
223 |
# not a problem as CONDORCET-style polls are disabled.
|
224 |
assert self.type == PollAlgorithm.SIMPLE |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
225 |
query = """ |
226 |
SELECT option
|
|
227 |
FROM Vote
|
|
228 |
WHERE poll = %d AND option IS NOT NULL |
|
229 |
GROUP BY option
|
|
230 |
HAVING COUNT(*) = (
|
|
231 |
SELECT COUNT(*)
|
|
232 |
FROM Vote
|
|
233 |
WHERE poll = %d |
|
234 |
GROUP BY option
|
|
235 |
ORDER BY COUNT(*) DESC LIMIT 1
|
|
236 |
)
|
|
237 |
""" % (self.id, self.id) |
|
238 |
results = Store.of(self).execute(query).get_all() |
|
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
239 |
if not results: |
240 |
return None |
|
241 |
return [PollOption.get(id) for (id,) in results] |
|
242 |
||
243 |
def getPairwiseMatrix(self): |
|
244 |
"""See IPoll."""
|
|
245 |
assert self.type == PollAlgorithm.CONDORCET |
|
246 |
options = list(self.getAllOptions()) |
|
247 |
pairwise_matrix = [] |
|
248 |
for option1 in options: |
|
249 |
pairwise_row = [] |
|
250 |
for option2 in options: |
|
251 |
points_query = """ |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
252 |
SELECT COUNT(*) FROM Vote as v1, Vote as v2 WHERE
|
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
253 |
v1.token = v2.token AND
|
254 |
v1.option = %s AND v2.option = %s AND |
|
255 |
(
|
|
256 |
(
|
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
257 |
v1.preference IS NOT NULL AND
|
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
258 |
v2.preference IS NOT NULL AND
|
259 |
v1.preference < v2.preference
|
|
260 |
)
|
|
261 |
OR
|
|
262 |
(
|
|
263 |
v1.preference IS NOT NULL AND
|
|
264 |
v2.preference IS NULL
|
|
265 |
)
|
|
266 |
)
|
|
267 |
""" % sqlvalues(option1.id, option2.id) |
|
268 |
if option1 == option2: |
|
269 |
pairwise_row.append(None) |
|
270 |
else: |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
271 |
points = Store.of(self).execute(points_query).get_one()[0] |
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
272 |
pairwise_row.append(points) |
273 |
pairwise_matrix.append(pairwise_row) |
|
274 |
return pairwise_matrix |
|
275 |
||
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
276 |
|
277 |
class PollSet: |
|
278 |
"""See IPollSet."""
|
|
279 |
||
280 |
implements(IPollSet) |
|
281 |
||
282 |
def new(self, team, name, title, proposition, dateopens, datecloses, |
|
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
283 |
secrecy, allowspoilt, poll_type=PollAlgorithm.SIMPLE): |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
284 |
"""See IPollSet."""
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
285 |
return Poll(team=team, name=name, title=title, |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
286 |
proposition=proposition, dateopens=dateopens, |
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
287 |
datecloses=datecloses, secrecy=secrecy, |
288 |
allowspoilt=allowspoilt, type=poll_type) |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
289 |
|
2219
by Canonical.com Patch Queue Manager
Some fixes as per Steve review. Do not use Subsets to traverse; instead do the traversal by consuming items from the traversal stack. r=SteveA |
290 |
def selectByTeam(self, team, status=PollStatus.ALL, orderBy=None, when=None): |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
291 |
"""See IPollSet."""
|
292 |
if when is None: |
|
293 |
when = datetime.now(pytz.timezone('UTC')) |
|
294 |
||
295 |
if orderBy is None: |
|
2701
by Canonical.com Patch Queue Manager
[trivial] Do not access Foo._defaultOrder from other classes; use a public name instead |
296 |
orderBy = Poll.sortingColumns |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
297 |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
298 |
|
2219
by Canonical.com Patch Queue Manager
Some fixes as per Steve review. Do not use Subsets to traverse; instead do the traversal by consuming items from the traversal stack. r=SteveA |
299 |
status = set(status) |
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
300 |
status_clauses = [] |
301 |
if PollStatus.OPEN in status: |
|
302 |
status_clauses.append(AND(Poll.q.dateopens <= when, |
|
303 |
Poll.q.datecloses > when)) |
|
304 |
if PollStatus.CLOSED in status: |
|
305 |
status_clauses.append(Poll.q.datecloses <= when) |
|
306 |
if PollStatus.NOT_YET_OPENED in status: |
|
307 |
status_clauses.append(Poll.q.dateopens > when) |
|
308 |
||
309 |
assert len(status_clauses) > 0, "No poll statuses were selected" |
|
310 |
||
311 |
results = Poll.select(AND(Poll.q.teamID == team.id, |
|
312 |
OR(*status_clauses))) |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
313 |
|
314 |
return results.orderBy(orderBy) |
|
315 |
||
316 |
def getByTeamAndName(self, team, name, default=None): |
|
317 |
"""See IPollSet."""
|
|
1716.1.90
by Christian Reis
Delintifying some of the database classes |
318 |
query = AND(Poll.q.teamID == team.id, Poll.q.name == name) |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
319 |
try: |
320 |
return Poll.selectOne(query) |
|
321 |
except SQLObjectNotFound: |
|
322 |
return default |
|
323 |
||
324 |
||
325 |
class PollOption(SQLBase): |
|
326 |
"""See IPollOption."""
|
|
327 |
||
328 |
implements(IPollOption) |
|
329 |
_table = 'PollOption' |
|
2736.1.11
by Mark Shuttleworth
poll system fixes |
330 |
_defaultOrder = ['title', 'id'] |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
331 |
|
332 |
poll = ForeignKey(dbName='poll', foreignKey='Poll', notNull=True) |
|
333 |
||
2736.1.11
by Mark Shuttleworth
poll system fixes |
334 |
name = StringCol(notNull=True) |
335 |
||
336 |
title = StringCol(notNull=True) |
|
337 |
||
338 |
active = BoolCol(notNull=True, default=False) |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
339 |
|
340 |
||
341 |
class PollOptionSet: |
|
342 |
"""See IPollOptionSet."""
|
|
343 |
||
344 |
implements(IPollOptionSet) |
|
345 |
||
2736.1.11
by Mark Shuttleworth
poll system fixes |
346 |
def new(self, poll, name, title, active=True): |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
347 |
"""See IPollOptionSet."""
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
348 |
return PollOption(poll=poll, name=name, title=title, active=active) |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
349 |
|
350 |
def selectByPoll(self, poll, only_active=False): |
|
351 |
"""See IPollOptionSet."""
|
|
1716.1.90
by Christian Reis
Delintifying some of the database classes |
352 |
query = PollOption.q.pollID == poll.id |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
353 |
if only_active: |
1716.1.90
by Christian Reis
Delintifying some of the database classes |
354 |
query = AND(query, PollOption.q.active == True) |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
355 |
return PollOption.select(query) |
356 |
||
1716.1.90
by Christian Reis
Delintifying some of the database classes |
357 |
def getByPollAndId(self, poll, option_id, default=None): |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
358 |
"""See IPollOptionSet."""
|
1716.1.90
by Christian Reis
Delintifying some of the database classes |
359 |
query = AND(PollOption.q.pollID == poll.id, |
360 |
PollOption.q.id == option_id) |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
361 |
try: |
362 |
return PollOption.selectOne(query) |
|
363 |
except SQLObjectNotFound: |
|
364 |
return default |
|
365 |
||
366 |
||
367 |
class VoteCast(SQLBase): |
|
368 |
"""See IVoteCast."""
|
|
369 |
||
370 |
implements(IVoteCast) |
|
371 |
_table = 'VoteCast' |
|
2643
by Canonical.com Patch Queue Manager
[trivial] Fix https://launchpad.net/products/launchpad/+bug/2717 (The Vote class misses a default ordering column) |
372 |
_defaultOrder = 'id' |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
373 |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
374 |
person = ForeignKey( |
375 |
dbName='person', foreignKey='Person', |
|
376 |
storm_validator=validate_public_person, notNull=True) |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
377 |
|
378 |
poll = ForeignKey(dbName='poll', foreignKey='Poll', notNull=True) |
|
379 |
||
380 |
||
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
381 |
class VoteCastSet: |
382 |
"""See IVoteCastSet."""
|
|
383 |
||
384 |
implements(IVoteCastSet) |
|
385 |
||
386 |
def new(self, poll, person): |
|
387 |
"""See IVoteCastSet."""
|
|
388 |
return VoteCast(poll=poll, person=person) |
|
389 |
||
390 |
||
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
391 |
class Vote(SQLBase): |
392 |
"""See IVote."""
|
|
393 |
||
394 |
implements(IVote) |
|
395 |
_table = 'Vote' |
|
2643
by Canonical.com Patch Queue Manager
[trivial] Fix https://launchpad.net/products/launchpad/+bug/2717 (The Vote class misses a default ordering column) |
396 |
_defaultOrder = ['preference', 'id'] |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
397 |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
398 |
person = ForeignKey( |
399 |
dbName='person', foreignKey='Person', |
|
400 |
storm_validator=validate_public_person) |
|
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
401 |
|
402 |
poll = ForeignKey(dbName='poll', foreignKey='Poll', notNull=True) |
|
403 |
||
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
404 |
option = ForeignKey(dbName='option', foreignKey='PollOption') |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
405 |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
406 |
preference = IntCol(dbName='preference') |
2162
by Canonical.com Patch Queue Manager
cleanup and portlet love [r=stevea] |
407 |
|
408 |
token = StringCol(dbName='token', notNull=True, unique=True) |
|
409 |
||
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
410 |
|
411 |
class VoteSet: |
|
412 |
"""See IVoteSet."""
|
|
413 |
||
414 |
implements(IVoteSet) |
|
415 |
||
416 |
def newToken(self): |
|
417 |
"""See IVoteSet."""
|
|
418 |
chars = '23456789bcdfghjkmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ' |
|
419 |
length = 10 |
|
420 |
token = ''.join([random.choice(chars) for c in range(length)]) |
|
421 |
while self.getByToken(token): |
|
422 |
token = ''.join([random.choice(chars) for c in range(length)]) |
|
423 |
return token |
|
424 |
||
425 |
def new(self, poll, option, preference, token, person): |
|
426 |
"""See IVoteSet."""
|
|
427 |
return Vote(poll=poll, option=option, preference=preference, |
|
428 |
token=token, person=person) |
|
429 |
||
430 |
def getByToken(self, token): |
|
431 |
"""See IVoteSet."""
|
|
432 |
return Vote.selectBy(token=token) |
|
433 |
||
434 |
def getVotesByOption(self, option): |
|
435 |
"""See IVoteSet."""
|
|
436 |
if option.poll.type != PollAlgorithm.SIMPLE: |
|
437 |
raise OptionIsNotFromSimplePoll( |
|
438 |
'%r is not an option of a simple-style poll.' % option) |
|
12156.3.1
by Curtis Hovey
Restored polls so that they can be used by a handful of important teams once a year. |
439 |
return Vote.selectBy(option=option).count() |
2491
by Canonical.com Patch Queue Manager
Simple and Condorcet polls (although the latter is disabled for now) for teams. r=BjornT |
440 |