~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
Polls
=====

In Launchpad, we have teams as a way to group free software
developers/contributors usually based on the free software
product/project/distribution they're involved in. This is the case with teams
like the 'Gnome Team' and the 'Ubuntu Team'. These teams often have leaders
whose ellection depends on the vote of all members, and this is one of the
reasons why we teams can have polls attached to them.

  >>> import pytz
  >>> from datetime import datetime, timedelta
  >>> from zope.component import getUtility
  >>> from lp.services.database.sqlbase import flush_database_updates
  >>> from lp.testing import login
  >>> from lp.registry.interfaces.person import IPersonSet
  >>> from lp.registry.interfaces.poll import (
  ...     IPollSubset,
  ...     PollAlgorithm,
  ...     PollSecrecy,
  ...     )

  >>> team = getUtility(IPersonSet).getByName('ubuntu-team')
  >>> member = getUtility(IPersonSet).getByName('stevea')
  >>> member2 = getUtility(IPersonSet).getByName('jdub')
  >>> member3 = getUtility(IPersonSet).getByName('kamion')
  >>> member4 = getUtility(IPersonSet).getByName('name16')
  >>> member5 = getUtility(IPersonSet).getByName('limi')
  >>> nonmember = getUtility(IPersonSet).getByName('justdave')
  >>> now = datetime.now(pytz.timezone('UTC'))
  >>> onesec = timedelta(seconds=1)

We need to login with one of the administrators of the team named 
'ubuntu-team' to be able to create/edit polls.
  >>> login('colin.watson@ubuntulinux.com')

First we get an object implementing IPollSubset, which is the set of polls for
a given team (in our case, the 'Ubuntu Team')
  >>> pollsubset = IPollSubset(team)

Now we create a new poll on this team.
  >>> opendate = datetime(2005, 01, 01, tzinfo=pytz.timezone('UTC'))
  >>> closedate = opendate + timedelta(weeks=2)
  >>> title = "2005 Leader's Elections"
  >>> proposition = "Who's going to be the next leader?"
  >>> type = PollAlgorithm.SIMPLE
  >>> secrecy = PollSecrecy.SECRET
  >>> allowspoilt = True
  >>> poll = pollsubset.new("leader-election", title, proposition, opendate,
  ...                       closedate, secrecy, allowspoilt, type)

Now we test the if the poll is open or closed in some specific dates.
  >>> poll.isOpen(when=opendate)
  True
  >>> poll.isOpen(when=opendate - onesec)
  False
  >>> poll.isOpen(when=closedate)
  True
  >>> poll.isOpen(when=closedate + onesec)
  False

To know what polls are open/closed/not-yet-opened in a team, you can use the
methods of PollSubset.
Here we'll query using three different dates:

Query for open polls in the exact second the poll is opening.
  >>> [p.name for p in pollsubset.getOpenPolls(when=opendate)]
  [u'leader-election', u'never-closes', u'never-closes2', u'never-closes3',
   u'never-closes4']

Query for closed polls, one second after the poll closes.
  >>> [p.name for p in pollsubset.getClosedPolls(when=closedate + onesec)]
  [u'director-2004', u'leader-2004', u'leader-election']

Query for not-yet-opened polls, one second before the poll opens.
  >>> [p.name for p in pollsubset.getNotYetOpenedPolls(when=opendate - onesec)]
  [u'leader-election', u'not-yet-opened']

All polls must have a set of options for people to choose, and they'll always
start with zero options. We're responsible for adding new ones.
  >>> poll.getAllOptions().count()
  0

Let's add some options to this poll, so people can start voting. :)
  >>> will = poll.newOption('wgraham', 'Will Graham')
  >>> jack = poll.newOption('jcrawford', 'Jack Crawford')
  >>> francis = poll.newOption('fd', 'Francis Dolarhyde')
  >>> [o.title for o in poll.getActiveOptions()]
  [u'Francis Dolarhyde', u'Jack Crawford', u'Will Graham']

Now, what happens if the poll is already open and, let's say, Francis Dolarhyde
is convicted and thus becomes ineligible? We'll have to mark that option as
inactive, so people can't vote on it.
  >>> francis.active = False
  >>> flush_database_updates()
  >>> [o.title for o in poll.getActiveOptions()]
  [u'Jack Crawford', u'Will Graham']

If the poll is not yet opened, it's possible to simply remove a given option.
  >>> poll.removeOption(will, when=opendate - onesec)
  >>> [o.title for o in poll.getAllOptions()]
  [u'Francis Dolarhyde', u'Jack Crawford']

Any member of the team this poll refers to is eligible to vote, if the poll is
still open.

  >>> vote1 = poll.storeSimpleVote(member, jack, when=opendate)
  >>> vote2 = poll.storeSimpleVote(member2, None, when=opendate)


Now we create a Condorcet poll on this team and add some options to it, so
people can start voting.

  >>> title = "2005 Director's Elections"
  >>> proposition = "Who's going to be the next director?"
  >>> type = PollAlgorithm.CONDORCET
  >>> secrecy = PollSecrecy.SECRET
  >>> allowspoilt = True
  >>> poll2 = pollsubset.new("director-election", title, proposition, opendate,
  ...                        closedate, secrecy, allowspoilt, type)
  >>> a = poll2.newOption('A', 'Option A')
  >>> b = poll2.newOption('B', 'Option B')
  >>> c = poll2.newOption('C', 'Option C')
  >>> d = poll2.newOption('D', 'Option D')

  >>> options = {b: 1, d: 2, c: 3}
  >>> votes = poll2.storeCondorcetVote(member, options, when=opendate)
  >>> options = {d: 1, b: 2}
  >>> votes = poll2.storeCondorcetVote(member2, options, when=opendate)
  >>> options = {a: 1, c: 2, b: 3}
  >>> votes = poll2.storeCondorcetVote(member3, options, when=opendate)
  >>> options = {a: 1}
  >>> votes = poll2.storeCondorcetVote(member4, options, when=opendate)
  >>> for row in poll2.getPairwiseMatrix():
  ...     print row
  [None, 2L, 2L, 2L]
  [2L, None, 2L, 2L]
  [1L, 1L, None, 1L]
  [2L, 1L, 2L, None]