Merge ~cjwatson/launchpad:stormify-poll into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: aef52646666fa9c9cc3d2cc40f0570fe98cff414
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:stormify-poll
Merge into: launchpad:master
Diff against target: 733 lines (+201/-143)
15 files modified
lib/lp/registry/adapters.py (+7/-7)
lib/lp/registry/browser/person.py (+3/-3)
lib/lp/registry/browser/tests/poll-views.txt (+1/-1)
lib/lp/registry/browser/tests/poll-views_0.txt (+2/-1)
lib/lp/registry/browser/tests/test_breadcrumbs.py (+3/-2)
lib/lp/registry/browser/tests/test_poll.py (+2/-0)
lib/lp/registry/doc/person-merge.txt (+2/-2)
lib/lp/registry/doc/poll-preconditions.txt (+2/-2)
lib/lp/registry/doc/poll.txt (+14/-14)
lib/lp/registry/interfaces/poll.py (+6/-6)
lib/lp/registry/model/poll.py (+152/-100)
lib/lp/registry/stories/team-polls/create-poll-options.txt (+2/-2)
lib/lp/registry/stories/team-polls/edit-poll.txt (+2/-2)
lib/lp/registry/templates/poll-index.pt (+1/-1)
lib/lp/registry/tests/test_poll.py (+2/-0)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+386813@code.launchpad.net

Commit message

Convert Poll and friends to Storm

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) wrote :

looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/registry/adapters.py b/lib/lp/registry/adapters.py
2index ac453fe..e5016c4 100644
3--- a/lib/lp/registry/adapters.py
4+++ b/lib/lp/registry/adapters.py
5@@ -89,29 +89,29 @@ class PollSubset:
6 """See IPollSubset."""
7 assert self.team is not None, (
8 'team cannot be None to call this method.')
9- return getUtility(IPollSet).selectByTeam(self.team)
10+ return getUtility(IPollSet).findByTeam(self.team)
11
12 def getOpenPolls(self, when=None):
13 """See IPollSubset."""
14 assert self.team is not None, (
15 'team cannot be None to call this method.')
16- return getUtility(IPollSet).selectByTeam(
17- self.team, [PollStatus.OPEN], orderBy='datecloses', when=when)
18+ return getUtility(IPollSet).findByTeam(
19+ self.team, [PollStatus.OPEN], order_by='datecloses', when=when)
20
21 def getClosedPolls(self, when=None):
22 """See IPollSubset."""
23 assert self.team is not None, (
24 'team cannot be None to call this method.')
25- return getUtility(IPollSet).selectByTeam(
26- self.team, [PollStatus.CLOSED], orderBy='datecloses', when=when)
27+ return getUtility(IPollSet).findByTeam(
28+ self.team, [PollStatus.CLOSED], order_by='datecloses', when=when)
29
30 def getNotYetOpenedPolls(self, when=None):
31 """See IPollSubset."""
32 assert self.team is not None, (
33 'team cannot be None to call this method.')
34- return getUtility(IPollSet).selectByTeam(
35+ return getUtility(IPollSet).findByTeam(
36 self.team, [PollStatus.NOT_YET_OPENED],
37- orderBy='dateopens', when=when)
38+ order_by='dateopens', when=when)
39
40
41 def productseries_to_product(productseries):
42diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py
43index e46cce4..8ece6c2 100644
44--- a/lib/lp/registry/browser/person.py
45+++ b/lib/lp/registry/browser/person.py
46@@ -1676,17 +1676,17 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
47 @cachedproperty
48 def openpolls(self):
49 assert self.context.is_team
50- return IPollSubset(self.context).getOpenPolls()
51+ return list(IPollSubset(self.context).getOpenPolls())
52
53 @cachedproperty
54 def closedpolls(self):
55 assert self.context.is_team
56- return IPollSubset(self.context).getClosedPolls()
57+ return list(IPollSubset(self.context).getClosedPolls())
58
59 @cachedproperty
60 def notyetopenedpolls(self):
61 assert self.context.is_team
62- return IPollSubset(self.context).getNotYetOpenedPolls()
63+ return list(IPollSubset(self.context).getNotYetOpenedPolls())
64
65 @cachedproperty
66 def contributions(self):
67diff --git a/lib/lp/registry/browser/tests/poll-views.txt b/lib/lp/registry/browser/tests/poll-views.txt
68index 2b67312..b9e1e65 100644
69--- a/lib/lp/registry/browser/tests/poll-views.txt
70+++ b/lib/lp/registry/browser/tests/poll-views.txt
71@@ -60,7 +60,7 @@ has not opened.
72 >>> close_date = open_date + timedelta(weeks=1)
73 >>> poll_subset = IPollSubset(team)
74 >>> poll = poll_subset.new(
75- ... 'name', 'title', 'proposition', open_date, close_date,
76+ ... u'name', u'title', u'proposition', open_date, close_date,
77 ... PollSecrecy.OPEN, False)
78
79 >>> ignored = login_person(user)
80diff --git a/lib/lp/registry/browser/tests/poll-views_0.txt b/lib/lp/registry/browser/tests/poll-views_0.txt
81index b55c376..a89220f 100644
82--- a/lib/lp/registry/browser/tests/poll-views_0.txt
83+++ b/lib/lp/registry/browser/tests/poll-views_0.txt
84@@ -53,7 +53,8 @@ Now we successfully create a poll which starts 12h from now.
85
86 == Displaying results of condorcet polls ==
87
88- >>> poll = getUtility(IPollSet).getByTeamAndName(ubuntu_team, 'director-2004')
89+ >>> poll = getUtility(IPollSet).getByTeamAndName(
90+ ... ubuntu_team, u'director-2004')
91 >>> poll.type.title
92 'Condorcet Voting'
93
94diff --git a/lib/lp/registry/browser/tests/test_breadcrumbs.py b/lib/lp/registry/browser/tests/test_breadcrumbs.py
95index 020dfe5..6c30093 100644
96--- a/lib/lp/registry/browser/tests/test_breadcrumbs.py
97+++ b/lib/lp/registry/browser/tests/test_breadcrumbs.py
98@@ -1,12 +1,15 @@
99 # Copyright 2009-2011 Canonical Ltd. This software is licensed under the
100 # GNU Affero General Public License version 3 (see the file LICENSE).
101
102+from __future__ import absolute_import, print_function, unicode_literals
103+
104 __metaclass__ = type
105
106 from zope.component import getUtility
107
108 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
109 from lp.registry.browser.tests.test_pillar_sharing import SharingBaseTestCase
110+from lp.registry.interfaces.nameblacklist import INameBlacklistSet
111 from lp.services.webapp.publisher import canonical_url
112 from lp.testing import login_person
113 from lp.testing.breadcrumbs import BaseBreadcrumbTestCase
114@@ -153,8 +156,6 @@ class TestPollBreadcrumb(BaseBreadcrumbTestCase):
115 last_crumb = crumbs[-1]
116 self.assertEqual(self.poll.title, last_crumb.text)
117
118-from lp.registry.interfaces.nameblacklist import INameBlacklistSet
119-
120
121 class TestNameblacklistBreadcrumb(BaseBreadcrumbTestCase):
122 """Test breadcrumbs for +nameblacklist."""
123diff --git a/lib/lp/registry/browser/tests/test_poll.py b/lib/lp/registry/browser/tests/test_poll.py
124index 8dc202a..825bb81 100644
125--- a/lib/lp/registry/browser/tests/test_poll.py
126+++ b/lib/lp/registry/browser/tests/test_poll.py
127@@ -3,6 +3,8 @@
128
129 """Tests for IPoll views."""
130
131+from __future__ import absolute_import, print_function, unicode_literals
132+
133 __metaclass__ = type
134
135 import os
136diff --git a/lib/lp/registry/doc/person-merge.txt b/lib/lp/registry/doc/person-merge.txt
137index ffb12b9..40b7030 100644
138--- a/lib/lp/registry/doc/person-merge.txt
139+++ b/lib/lp/registry/doc/person-merge.txt
140@@ -384,7 +384,7 @@ hand, are carried over just like when merging people.
141 >>> today = datetime.now(pytz.timezone('UTC'))
142 >>> tomorrow = today + timedelta(days=1)
143 >>> poll = IPollSubset(test_team).new(
144- ... 'test-poll', 'Title', 'Proposition', today, tomorrow,
145+ ... u'test-poll', u'Title', u'Proposition', today, tomorrow,
146 ... PollSecrecy.OPEN, allowspoilt=True)
147
148 # test_team has a superteam, one active member and a poll.
149@@ -399,7 +399,7 @@ hand, are carried over just like when merging people.
150 [u'name12']
151
152 >>> list(IPollSubset(test_team).getAll())
153- [<Poll at ...]
154+ [<lp.registry.model.poll.Poll object at ...]
155
156 # Landscape-developers has no super teams, two members and no polls.
157
158diff --git a/lib/lp/registry/doc/poll-preconditions.txt b/lib/lp/registry/doc/poll-preconditions.txt
159index a4b5374..f1fcc1c 100644
160--- a/lib/lp/registry/doc/poll-preconditions.txt
161+++ b/lib/lp/registry/doc/poll-preconditions.txt
162@@ -18,9 +18,9 @@ should be threated as so.
163
164 >>> pollset = getUtility(IPollSet)
165 >>> director_election = pollset.getByTeamAndName(ubuntu_team,
166- ... 'director-2004')
167+ ... u'director-2004')
168 >>> director_options = director_election.getActiveOptions()
169- >>> leader_election = pollset.getByTeamAndName(ubuntu_team, 'leader-2004')
170+ >>> leader_election = pollset.getByTeamAndName(ubuntu_team, u'leader-2004')
171 >>> leader_options = leader_election.getActiveOptions()
172 >>> opendate = leader_election.dateopens
173 >>> onesec = timedelta(seconds=1)
174diff --git a/lib/lp/registry/doc/poll.txt b/lib/lp/registry/doc/poll.txt
175index 269b9ec..6ef79a1 100644
176--- a/lib/lp/registry/doc/poll.txt
177+++ b/lib/lp/registry/doc/poll.txt
178@@ -41,12 +41,12 @@ a given team (in our case, the 'Ubuntu Team')
179 Now we create a new poll on this team.
180 >>> opendate = datetime(2005, 1, 1, tzinfo=pytz.timezone('UTC'))
181 >>> closedate = opendate + timedelta(weeks=2)
182- >>> title = "2005 Leader's Elections"
183- >>> proposition = "Who's going to be the next leader?"
184+ >>> title = u"2005 Leader's Elections"
185+ >>> proposition = u"Who's going to be the next leader?"
186 >>> type = PollAlgorithm.SIMPLE
187 >>> secrecy = PollSecrecy.SECRET
188 >>> allowspoilt = True
189- >>> poll = pollsubset.new("leader-election", title, proposition, opendate,
190+ >>> poll = pollsubset.new(u"leader-election", title, proposition, opendate,
191 ... closedate, secrecy, allowspoilt, type)
192
193 Now we test the if the poll is open or closed in some specific dates.
194@@ -82,9 +82,9 @@ start with zero options. We're responsible for adding new ones.
195 0
196
197 Let's add some options to this poll, so people can start voting. :)
198- >>> will = poll.newOption('wgraham', 'Will Graham')
199- >>> jack = poll.newOption('jcrawford', 'Jack Crawford')
200- >>> francis = poll.newOption('fd', 'Francis Dolarhyde')
201+ >>> will = poll.newOption(u'wgraham', u'Will Graham')
202+ >>> jack = poll.newOption(u'jcrawford', u'Jack Crawford')
203+ >>> francis = poll.newOption(u'fd', u'Francis Dolarhyde')
204 >>> [o.title for o in poll.getActiveOptions()]
205 [u'Francis Dolarhyde', u'Jack Crawford', u'Will Graham']
206
207@@ -111,17 +111,17 @@ still open.
208 Now we create a Condorcet poll on this team and add some options to it, so
209 people can start voting.
210
211- >>> title = "2005 Director's Elections"
212- >>> proposition = "Who's going to be the next director?"
213+ >>> title = u"2005 Director's Elections"
214+ >>> proposition = u"Who's going to be the next director?"
215 >>> type = PollAlgorithm.CONDORCET
216 >>> secrecy = PollSecrecy.SECRET
217 >>> allowspoilt = True
218- >>> poll2 = pollsubset.new("director-election", title, proposition, opendate,
219- ... closedate, secrecy, allowspoilt, type)
220- >>> a = poll2.newOption('A', 'Option A')
221- >>> b = poll2.newOption('B', 'Option B')
222- >>> c = poll2.newOption('C', 'Option C')
223- >>> d = poll2.newOption('D', 'Option D')
224+ >>> poll2 = pollsubset.new(u"director-election", title, proposition,
225+ ... opendate, closedate, secrecy, allowspoilt, type)
226+ >>> a = poll2.newOption(u'A', u'Option A')
227+ >>> b = poll2.newOption(u'B', u'Option B')
228+ >>> c = poll2.newOption(u'C', u'Option C')
229+ >>> d = poll2.newOption(u'D', u'Option D')
230
231 >>> options = {b: 1, d: 2, c: 3}
232 >>> votes = poll2.storeCondorcetVote(member, options, when=opendate)
233diff --git a/lib/lp/registry/interfaces/poll.py b/lib/lp/registry/interfaces/poll.py
234index a6dd40d..8fab288 100644
235--- a/lib/lp/registry/interfaces/poll.py
236+++ b/lib/lp/registry/interfaces/poll.py
237@@ -296,16 +296,16 @@ class IPollSet(Interface):
238 secrecy, allowspoilt, poll_type=PollAlgorithm.SIMPLE):
239 """Create a new Poll for the given team."""
240
241- def selectByTeam(team, status=PollStatus.ALL, orderBy=None, when=None):
242+ def findByTeam(team, status=PollStatus.ALL, order_by=None, when=None):
243 """Return all Polls for the given team, filtered by status.
244
245 :status: is a sequence containing as many values as you want from
246 PollStatus.
247
248- :orderBy: can be either a string with the column name you want to sort
249- or a list of column names as strings.
250- If no orderBy is specified the results will be ordered using the
251- default ordering specified in Poll._defaultOrder.
252+ :order_by: can be either a string with the column name you want to
253+ sort or a list of column names as strings.
254+ If no order_by is specified the results will be ordered using the
255+ default ordering specified in Poll.sortingColumns.
256
257 The optional :when argument is used only by our tests, to test if the
258 poll is/was/will-be open at a specific date.
259@@ -407,7 +407,7 @@ class IPollOptionSet(Interface):
260 def new(poll, name, title, active=True):
261 """Create a new PollOption."""
262
263- def selectByPoll(poll, only_active=False):
264+ def findByPoll(poll, only_active=False):
265 """Return all PollOptions of the given poll.
266
267 If :only_active is True, then return only the active polls.
268diff --git a/lib/lp/registry/model/poll.py b/lib/lp/registry/model/poll.py
269index b75852a..b34786b 100644
270--- a/lib/lp/registry/model/poll.py
271+++ b/lib/lp/registry/model/poll.py
272@@ -1,6 +1,8 @@
273 # Copyright 2009 Canonical Ltd. This software is licensed under the
274 # GNU Affero General Public License version 3 (see the file LICENSE).
275
276+from __future__ import absolute_import, print_function, unicode_literals
277+
278 __metaclass__ = type
279 __all__ = [
280 'Poll',
281@@ -16,16 +18,16 @@ __all__ = [
282 from datetime import datetime
283
284 import pytz
285-from sqlobject import (
286- AND,
287- BoolCol,
288- ForeignKey,
289- IntCol,
290- OR,
291- SQLObjectNotFound,
292- StringCol,
293+from storm.locals import (
294+ And,
295+ Bool,
296+ DateTime,
297+ Int,
298+ Or,
299+ Reference,
300+ Store,
301+ Unicode,
302 )
303-from storm.store import Store
304 from zope.component import getUtility
305 from zope.interface import implementer
306
307@@ -44,43 +46,57 @@ from lp.registry.interfaces.poll import (
308 PollSecrecy,
309 PollStatus,
310 )
311-from lp.services.database.datetimecol import UtcDateTimeCol
312-from lp.services.database.enumcol import EnumCol
313-from lp.services.database.sqlbase import (
314- SQLBase,
315- sqlvalues,
316- )
317+from lp.services.database.enumcol import DBEnum
318+from lp.services.database.interfaces import IStore
319+from lp.services.database.sqlbase import sqlvalues
320+from lp.services.database.stormbase import StormBase
321 from lp.services.tokens import create_token
322
323
324 @implementer(IPoll)
325-class Poll(SQLBase):
326+class Poll(StormBase):
327 """See IPoll."""
328- _table = 'Poll'
329+ __storm_table__ = 'Poll'
330 sortingColumns = ['title', 'id']
331- _defaultOrder = sortingColumns
332+ __storm_order__ = sortingColumns
333+
334+ id = Int(primary=True)
335+
336+ team_id = Int(
337+ name='team', validator=validate_public_person, allow_none=False)
338+ team = Reference(team_id, 'Person.id')
339
340- team = ForeignKey(
341- dbName='team', foreignKey='Person',
342- storm_validator=validate_public_person, notNull=True)
343+ name = Unicode(name='name', allow_none=False)
344
345- name = StringCol(dbName='name', notNull=True)
346+ title = Unicode(name='title', allow_none=False)
347
348- title = StringCol(dbName='title', notNull=True, unique=True)
349+ dateopens = DateTime(tzinfo=pytz.UTC, name='dateopens', allow_none=False)
350
351- dateopens = UtcDateTimeCol(dbName='dateopens', notNull=True)
352+ datecloses = DateTime(tzinfo=pytz.UTC, name='datecloses', allow_none=False)
353
354- datecloses = UtcDateTimeCol(dbName='datecloses', notNull=True)
355+ proposition = Unicode(name='proposition', allow_none=False)
356
357- proposition = StringCol(dbName='proposition', notNull=True)
358+ type = DBEnum(
359+ name='type', enum=PollAlgorithm, default=PollAlgorithm.SIMPLE)
360
361- type = EnumCol(dbName='type', enum=PollAlgorithm,
362- default=PollAlgorithm.SIMPLE)
363+ allowspoilt = Bool(name='allowspoilt', default=True, allow_none=False)
364
365- allowspoilt = BoolCol(dbName='allowspoilt', default=True, notNull=True)
366+ secrecy = DBEnum(
367+ name='secrecy', enum=PollSecrecy, default=PollSecrecy.SECRET)
368
369- secrecy = EnumCol(dbName='secrecy', enum=PollSecrecy,
370- default=PollSecrecy.SECRET)
371+ def __init__(self, team, name, title, proposition, dateopens, datecloses,
372+ secrecy=PollSecrecy.SECRET, allowspoilt=True,
373+ type=PollAlgorithm.SIMPLE):
374+ super(Poll, self).__init__()
375+ self.team = team
376+ self.name = name
377+ self.title = title
378+ self.proposition = proposition
379+ self.dateopens = dateopens
380+ self.datecloses = datecloses
381+ self.secrecy = secrecy
382+ self.allowspoilt = allowspoilt
383+ self.type = type
384
385 def newOption(self, name, title, active=True):
386 """See IPoll."""
387@@ -116,20 +132,20 @@ class Poll(SQLBase):
388
389 def getAllOptions(self):
390 """See IPoll."""
391- return getUtility(IPollOptionSet).selectByPoll(self)
392+ return getUtility(IPollOptionSet).findByPoll(self)
393
394 def getActiveOptions(self):
395 """See IPoll."""
396- return getUtility(IPollOptionSet).selectByPoll(self, only_active=True)
397+ return getUtility(IPollOptionSet).findByPoll(self, only_active=True)
398
399 def getVotesByPerson(self, person):
400 """See IPoll."""
401- return Vote.selectBy(person=person, poll=self)
402+ return IStore(Vote).find(Vote, person=person, poll=self)
403
404 def personVoted(self, person):
405 """See IPoll."""
406- results = VoteCast.selectBy(person=person, poll=self)
407- return bool(results.count())
408+ results = IStore(VoteCast).find(VoteCast, person=person, poll=self)
409+ return not results.is_empty()
410
411 def removeOption(self, option, when=None):
412 """See IPoll."""
413@@ -141,7 +157,7 @@ class Poll(SQLBase):
414
415 def getOptionByName(self, name):
416 """See IPoll."""
417- return PollOption.selectOneBy(poll=self, name=name)
418+ return IStore(PollOption).find(PollOption, poll=self, name=name).one()
419
420 def _assertEverythingOkAndGetVoter(self, person, when=None):
421 """Use assertions to Make sure all pre-conditions for a person to vote
422@@ -210,7 +226,7 @@ class Poll(SQLBase):
423 def getTotalVotes(self):
424 """See IPoll."""
425 assert self.isClosed()
426- return Vote.selectBy(poll=self).count()
427+ return IStore(Vote).find(Vote, poll=self).count()
428
429 def getWinners(self):
430 """See IPoll."""
431@@ -235,7 +251,7 @@ class Poll(SQLBase):
432 results = Store.of(self).execute(query).get_all()
433 if not results:
434 return None
435- return [PollOption.get(id) for (id,) in results]
436+ return [IStore(PollOption).get(PollOption, id) for (id,) in results]
437
438 def getPairwiseMatrix(self):
439 """See IPoll."""
440@@ -278,59 +294,72 @@ class PollSet:
441 def new(self, team, name, title, proposition, dateopens, datecloses,
442 secrecy, allowspoilt, poll_type=PollAlgorithm.SIMPLE):
443 """See IPollSet."""
444- return Poll(team=team, name=name, title=title,
445- proposition=proposition, dateopens=dateopens,
446- datecloses=datecloses, secrecy=secrecy,
447- allowspoilt=allowspoilt, type=poll_type)
448-
449- def selectByTeam(self, team, status=PollStatus.ALL, orderBy=None,
450- when=None):
451+ poll = Poll(
452+ team=team, name=name, title=title,
453+ proposition=proposition, dateopens=dateopens,
454+ datecloses=datecloses, secrecy=secrecy,
455+ allowspoilt=allowspoilt, type=poll_type)
456+ IStore(Poll).add(poll)
457+ return poll
458+
459+ def findByTeam(self, team, status=PollStatus.ALL, order_by=None,
460+ when=None):
461 """See IPollSet."""
462 if when is None:
463 when = datetime.now(pytz.timezone('UTC'))
464
465- if orderBy is None:
466- orderBy = Poll.sortingColumns
467+ if order_by is None:
468+ order_by = Poll.sortingColumns
469
470 status = set(status)
471 status_clauses = []
472 if PollStatus.OPEN in status:
473- status_clauses.append(AND(Poll.q.dateopens <= when,
474- Poll.q.datecloses > when))
475+ status_clauses.append(
476+ And(Poll.dateopens <= when, Poll.datecloses > when))
477 if PollStatus.CLOSED in status:
478- status_clauses.append(Poll.q.datecloses <= when)
479+ status_clauses.append(Poll.datecloses <= when)
480 if PollStatus.NOT_YET_OPENED in status:
481- status_clauses.append(Poll.q.dateopens > when)
482+ status_clauses.append(Poll.dateopens > when)
483
484 assert len(status_clauses) > 0, "No poll statuses were selected"
485
486- results = Poll.select(AND(Poll.q.teamID == team.id,
487- OR(*status_clauses)))
488+ results = IStore(Poll).find(
489+ Poll, Poll.team == team, Or(*status_clauses))
490
491- return results.orderBy(orderBy)
492+ return results.order_by(order_by)
493
494 def getByTeamAndName(self, team, name, default=None):
495 """See IPollSet."""
496- query = AND(Poll.q.teamID == team.id, Poll.q.name == name)
497- try:
498- return Poll.selectOne(query)
499- except SQLObjectNotFound:
500- return default
501+ poll = IStore(Poll).find(Poll, team=team, name=name).one()
502+ return poll if poll is not None else default
503
504
505 @implementer(IPollOption)
506-class PollOption(SQLBase):
507+class PollOption(StormBase):
508 """See IPollOption."""
509- _table = 'PollOption'
510- _defaultOrder = ['title', 'id']
511+ __storm_table__ = 'PollOption'
512+ __storm_order__ = ['title', 'id']
513+
514+ id = Int(primary=True)
515+
516+ poll_id = Int(name='poll', allow_none=False)
517+ poll = Reference(poll_id, 'Poll.id')
518
519- poll = ForeignKey(dbName='poll', foreignKey='Poll', notNull=True)
520+ name = Unicode(allow_none=False)
521
522- name = StringCol(notNull=True)
523+ title = Unicode(allow_none=False)
524
525- title = StringCol(notNull=True)
526+ active = Bool(allow_none=False, default=False)
527
528- active = BoolCol(notNull=True, default=False)
529+ def __init__(self, poll, name, title, active=False):
530+ super(PollOption, self).__init__()
531+ self.poll = poll
532+ self.name = name
533+ self.title = title
534+ self.active = active
535+
536+ def destroySelf(self):
537+ IStore(PollOption).remove(self)
538
539
540 @implementer(IPollOptionSet)
541@@ -339,36 +368,43 @@ class PollOptionSet:
542
543 def new(self, poll, name, title, active=True):
544 """See IPollOptionSet."""
545- return PollOption(poll=poll, name=name, title=title, active=active)
546+ option = PollOption(poll=poll, name=name, title=title, active=active)
547+ IStore(PollOption).add(option)
548+ return option
549
550- def selectByPoll(self, poll, only_active=False):
551+ def findByPoll(self, poll, only_active=False):
552 """See IPollOptionSet."""
553- query = PollOption.q.pollID == poll.id
554+ clauses = [PollOption.poll == poll]
555 if only_active:
556- query = AND(query, PollOption.q.active == True)
557- return PollOption.select(query)
558+ clauses.append(PollOption.active)
559+ return IStore(PollOption).find(PollOption, *clauses)
560
561 def getByPollAndId(self, poll, option_id, default=None):
562 """See IPollOptionSet."""
563- query = AND(PollOption.q.pollID == poll.id,
564- PollOption.q.id == option_id)
565- try:
566- return PollOption.selectOne(query)
567- except SQLObjectNotFound:
568- return default
569+ option = IStore(PollOption).find(
570+ PollOption, poll=poll, id=option_id).one()
571+ return option if option is not None else default
572
573
574 @implementer(IVoteCast)
575-class VoteCast(SQLBase):
576+class VoteCast(StormBase):
577 """See IVoteCast."""
578- _table = 'VoteCast'
579- _defaultOrder = 'id'
580+ __storm_table__ = 'VoteCast'
581+ __storm_order__ = 'id'
582+
583+ id = Int(primary=True)
584+
585+ person_id = Int(
586+ name='person', validator=validate_public_person, allow_none=False)
587+ person = Reference(person_id, 'Person.id')
588
589- person = ForeignKey(
590- dbName='person', foreignKey='Person',
591- storm_validator=validate_public_person, notNull=True)
592+ poll_id = Int(name='poll', allow_none=False)
593+ poll = Reference(poll_id, 'Poll.id')
594
595- poll = ForeignKey(dbName='poll', foreignKey='Poll', notNull=True)
596+ def __init__(self, person, poll):
597+ super(VoteCast, self).__init__()
598+ self.person = person
599+ self.poll = poll
600
601
602 @implementer(IVoteCastSet)
603@@ -377,26 +413,39 @@ class VoteCastSet:
604
605 def new(self, poll, person):
606 """See IVoteCastSet."""
607- return VoteCast(poll=poll, person=person)
608+ vote_cast = VoteCast(poll=poll, person=person)
609+ IStore(VoteCast).add(vote_cast)
610+ return vote_cast
611
612
613 @implementer(IVote)
614-class Vote(SQLBase):
615+class Vote(StormBase):
616 """See IVote."""
617- _table = 'Vote'
618- _defaultOrder = ['preference', 'id']
619+ __storm_table__ = 'Vote'
620+ __storm_order__ = ['preference', 'id']
621
622- person = ForeignKey(
623- dbName='person', foreignKey='Person',
624- storm_validator=validate_public_person)
625+ id = Int(primary=True)
626
627- poll = ForeignKey(dbName='poll', foreignKey='Poll', notNull=True)
628+ person_id = Int(name='person', validator=validate_public_person)
629+ person = Reference(person_id, 'Person.id')
630
631- option = ForeignKey(dbName='option', foreignKey='PollOption')
632+ poll_id = Int(name='poll', allow_none=False)
633+ poll = Reference(poll_id, 'Poll.id')
634
635- preference = IntCol(dbName='preference')
636+ option_id = Int(name='option')
637+ option = Reference(option_id, 'PollOption.id')
638
639- token = StringCol(dbName='token', notNull=True, unique=True)
640+ preference = Int(name='preference')
641+
642+ token = Unicode(name='token', allow_none=False)
643+
644+ def __init__(self, poll, token, person=None, option=None, preference=None):
645+ super(Vote, self).__init__()
646+ self.poll = poll
647+ self.token = token
648+ self.person = person
649+ self.option = option
650+ self.preference = preference
651
652
653 @implementer(IVoteSet)
654@@ -405,16 +454,19 @@ class VoteSet:
655
656 def new(self, poll, option, preference, token, person):
657 """See IVoteSet."""
658- return Vote(poll=poll, option=option, preference=preference,
659- token=token, person=person)
660+ vote = Vote(
661+ poll=poll, option=option, preference=preference, token=token,
662+ person=person)
663+ IStore(Vote).add(vote)
664+ return vote
665
666 def getByToken(self, token):
667 """See IVoteSet."""
668- return Vote.selectBy(token=token)
669+ return IStore(Vote).find(Vote, token=token)
670
671 def getVotesByOption(self, option):
672 """See IVoteSet."""
673 if option.poll.type != PollAlgorithm.SIMPLE:
674 raise OptionIsNotFromSimplePoll(
675 '%r is not an option of a simple-style poll.' % option)
676- return Vote.selectBy(option=option).count()
677+ return IStore(Vote).find(Vote, option=option).count()
678diff --git a/lib/lp/registry/stories/team-polls/create-poll-options.txt b/lib/lp/registry/stories/team-polls/create-poll-options.txt
679index 2a9711a..2af52e0 100644
680--- a/lib/lp/registry/stories/team-polls/create-poll-options.txt
681+++ b/lib/lp/registry/stories/team-polls/create-poll-options.txt
682@@ -9,8 +9,8 @@ First we create a new poll to use throughout this test.
683 >>> from zope.component import getUtility
684 >>> from lp.registry.interfaces.person import IPersonSet
685 >>> factory.makePoll(getUtility(IPersonSet).getByName('ubuntu-team'),
686- ... 'dpl-2080', 'dpl-2080', 'dpl-2080')
687- <Poll...
688+ ... u'dpl-2080', u'dpl-2080', u'dpl-2080')
689+ <lp.registry.model.poll.Poll...
690 >>> logout()
691
692 Our poll is not yet open, so new options can be added to it.
693diff --git a/lib/lp/registry/stories/team-polls/edit-poll.txt b/lib/lp/registry/stories/team-polls/edit-poll.txt
694index 2590f56..7265ff3 100644
695--- a/lib/lp/registry/stories/team-polls/edit-poll.txt
696+++ b/lib/lp/registry/stories/team-polls/edit-poll.txt
697@@ -9,8 +9,8 @@ First we create a new poll to use throughout this test.
698 >>> from zope.component import getUtility
699 >>> from lp.registry.interfaces.person import IPersonSet
700 >>> factory.makePoll(getUtility(IPersonSet).getByName('ubuntu-team'),
701- ... 'dpl-2080', 'dpl-2080', 'dpl-2080')
702- <Poll...
703+ ... u'dpl-2080', u'dpl-2080', u'dpl-2080')
704+ <lp.registry.model.poll.Poll...
705 >>> logout()
706
707 Now we'll try to change its name to something that is already in use.
708diff --git a/lib/lp/registry/templates/poll-index.pt b/lib/lp/registry/templates/poll-index.pt
709index de586e1..69a60f5 100644
710--- a/lib/lp/registry/templates/poll-index.pt
711+++ b/lib/lp/registry/templates/poll-index.pt
712@@ -22,7 +22,7 @@
713 />
714 <br />
715
716- <p tal:condition="not: context/getActiveOptions">
717+ <p tal:condition="python: context.getActiveOptions().is_empty()">
718 This poll does not yet have any voting options. Please <a
719 href="+newoption">add an option</a>. Note, you need more than one option
720 for a real poll, of course :-)
721diff --git a/lib/lp/registry/tests/test_poll.py b/lib/lp/registry/tests/test_poll.py
722index ad5386c..e2297fc 100644
723--- a/lib/lp/registry/tests/test_poll.py
724+++ b/lib/lp/registry/tests/test_poll.py
725@@ -1,6 +1,8 @@
726 # Copyright 2009 Canonical Ltd. This software is licensed under the
727 # GNU Affero General Public License version 3 (see the file LICENSE).
728
729+from __future__ import absolute_import, print_function, unicode_literals
730+
731 from datetime import (
732 datetime,
733 timedelta,

Subscribers

People subscribed via source and target branches

to status/vote changes: