Merge ~cjwatson/launchpad:stormify-poll into launchpad:master
- Git
- lp:~cjwatson/launchpad
- stormify-poll
- Merge into 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) |
Related bugs: |
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
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/registry/adapters.py b/lib/lp/registry/adapters.py |
2 | index 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): |
42 | diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py |
43 | index 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): |
67 | diff --git a/lib/lp/registry/browser/tests/poll-views.txt b/lib/lp/registry/browser/tests/poll-views.txt |
68 | index 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) |
80 | diff --git a/lib/lp/registry/browser/tests/poll-views_0.txt b/lib/lp/registry/browser/tests/poll-views_0.txt |
81 | index 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 | |
94 | diff --git a/lib/lp/registry/browser/tests/test_breadcrumbs.py b/lib/lp/registry/browser/tests/test_breadcrumbs.py |
95 | index 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.""" |
123 | diff --git a/lib/lp/registry/browser/tests/test_poll.py b/lib/lp/registry/browser/tests/test_poll.py |
124 | index 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 |
136 | diff --git a/lib/lp/registry/doc/person-merge.txt b/lib/lp/registry/doc/person-merge.txt |
137 | index 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 | |
158 | diff --git a/lib/lp/registry/doc/poll-preconditions.txt b/lib/lp/registry/doc/poll-preconditions.txt |
159 | index 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) |
174 | diff --git a/lib/lp/registry/doc/poll.txt b/lib/lp/registry/doc/poll.txt |
175 | index 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) |
233 | diff --git a/lib/lp/registry/interfaces/poll.py b/lib/lp/registry/interfaces/poll.py |
234 | index 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. |
268 | diff --git a/lib/lp/registry/model/poll.py b/lib/lp/registry/model/poll.py |
269 | index 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() |
678 | diff --git a/lib/lp/registry/stories/team-polls/create-poll-options.txt b/lib/lp/registry/stories/team-polls/create-poll-options.txt |
679 | index 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. |
693 | diff --git a/lib/lp/registry/stories/team-polls/edit-poll.txt b/lib/lp/registry/stories/team-polls/edit-poll.txt |
694 | index 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. |
708 | diff --git a/lib/lp/registry/templates/poll-index.pt b/lib/lp/registry/templates/poll-index.pt |
709 | index 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 :-) |
721 | diff --git a/lib/lp/registry/tests/test_poll.py b/lib/lp/registry/tests/test_poll.py |
722 | index 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, |
looks good