Merge lp:~lderan/ubuntu-bots/meeetingology-output into lp:~ubuntu-bots/ubuntu-bots/meetingology

Proposed by Thomas Molloy
Status: Merged
Merged at revision: 15
Proposed branch: lp:~lderan/ubuntu-bots/meeetingology-output
Merge into: lp:~ubuntu-bots/ubuntu-bots/meetingology
Diff against target: 277 lines (+76/-16)
4 files modified
items.py (+3/-0)
meeting.py (+42/-10)
plugin.py (+3/-0)
writers.py (+28/-6)
To merge this branch: bzr merge lp:~lderan/ubuntu-bots/meeetingology-output
Reviewer Review Type Date Requested Status
Alan Bell Approve
Review via email: mp+195883@code.launchpad.net

Description of the change

Commands that are not publicly useable are privately sent to the chairs, and when a new chair gets added they are sent the list as well. This does result in a list that will have to be maintained if new public commands are added.

DONE meeting item added.

Meeting notifications when they end sent to preset irc nicks.

Votes now link to the HTML log url to the point where the voting begins in the MoinMoin output.

Votes are now in chronological order in the MoinMoin output.

To post a comment you must log in.
Revision history for this message
Alan Bell (alanbell) wrote :

good stuff

review: Approve
15. By Alan Bell

accepting merge

Revision history for this message
Alan Bell (alanbell) wrote :

ooh hang on, not sure that the hard coded lderan notification nick is a good idea

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'items.py'
2--- items.py 2013-04-21 19:25:19 +0000
3+++ items.py 2013-11-20 00:00:42 +0000
4@@ -235,6 +235,9 @@
5 moin_template = """ * '''%(line)s''' (%(time)s)"""
6 class Help(GenericItem):
7 itemtype = 'HELP'
8+class Done(GenericItem):
9+ itemtype = 'DONE'
10+ moin_template = """''ACTION:'' %(line)s"""
11 class Accepted(GenericItem):
12 itemtype = 'ACCEPTED'
13 starthtml = '<font color="green">'
14
15=== modified file 'meeting.py'
16--- meeting.py 2013-07-24 18:38:57 +0000
17+++ meeting.py 2013-11-20 00:00:42 +0000
18@@ -89,6 +89,8 @@
19 endMeetingMessage = ("Meeting ended %(endtime)s %(timeZone)s. "
20 "\n"
21 "Minutes: %(urlBasename)s.moin.txt")
22+ endMeetingNotification = ("Meeting in %(channel)s has just ended")
23+ endMeetingNotificationList = ["lderan"]
24
25 #TODO: endMeetingMessage should get filenames from the writers
26
27@@ -285,6 +287,9 @@
28 message = self.config.startMeetingMessage%repl
29 for messageline in message.split('\n'):
30 self.reply(messageline)
31+ self.do_private_commands(self.owner)
32+ for chair in self.chairs:
33+ self.do_private_commands(chair)
34 self.do_commands()
35 if line.strip():
36 self.do_meetingtopic(nick=nick, line=line, time_=time_, **kwargs)
37@@ -294,7 +299,7 @@
38 if not self.isChair(nick): return
39 #close any open votes
40 if not self.activeVote=="":
41- self.do_endvote(nick=nick,line=line,**kwargs)
42+ self.do_endvote(nick, line, **kwargs)
43 if self.oldtopic:
44 self.topic(self.oldtopic)
45 self.endtime = time_
46@@ -304,7 +309,9 @@
47 for messageline in message.split('\n'):
48 self.reply(messageline)
49 self._meetingIsOver = True
50-
51+ for nickToPM in self.config.endMeetingNotificationList:
52+ self.privateReply(nickToPM, self.config.endMeetingNotification%repl)
53+
54
55 def do_topic(self, nick, line, **kwargs):
56 """Set a new topic in the channel."""
57@@ -370,6 +377,7 @@
58 self.reply("Warning: Nick not in channel: %s"%chair)
59 self.addnick(chair, lines=0)
60 self.chairs.setdefault(chair, True)
61+ self.do_private_commands(chair)
62 chairs = dict(self.chairs) # make a copy
63 chairs.setdefault(self.owner, True)
64 self.reply("Current chairs: %s"%(" ".join(sorted(chairs.keys()))))
65@@ -422,6 +430,10 @@
66 self.reply("Public votes can be registered by saying +1, +0 or -1 in channel, (for private voting, private message me with 'vote +1/-1/+0 #channelname)")
67 self.activeVote=line.strip()
68 self.currentVote={}
69+ self.publicVoters[self.activeVote] = []
70+ #Need the line number for linking to the html output
71+ self.currentVoteStartLine = 0
72+ self.currentVoteStartLine = len(self.lines)
73 #need to set up a structure to hold vote results
74 #people can vote by saying +1 0 or -1
75 #if voters have been specified then only they can vote
76@@ -453,7 +465,7 @@
77 for v in self.currentVote:
78 if re.match("-1",self.currentVote[v]):
79 vagainst+=1
80- elif re.match("0|\+0",self.currentVote[v]):
81+ elif re.match("0|\+0|-0",self.currentVote[v]):
82 vabstain+=1
83 elif re.match("\+1",self.currentVote[v]):
84 vfor+=1
85@@ -474,7 +486,8 @@
86 else:
87 self.reply("Motion carried")
88 voteResult = "Carried"
89- self.votes[self.activeVote]=[vfor,vabstain,vagainst]#store the results
90+ #store the results
91+ self.votes[self.activeVote]=[vfor, vabstain, vagainst, self.currentVoteStartLine]
92
93 """Add informational item to the minutes."""
94 voteResultLog = "''Vote:'' "+self.activeVote+" ("+voteResult+")"
95@@ -484,6 +497,7 @@
96
97 self.activeVote=""#allow another vote to be called
98 self.currentVote={}
99+ self.currentVoteStartLine = 0
100
101
102
103@@ -510,7 +524,11 @@
104 voters = dict(self.voters) # make a copy
105 #voters.setdefault(self.owner, True)#not sure about this if resetting voters to everyone - in fact why auto add the person calling #voters at all?
106 self.reply("Current voters: %s"%(" ".join(sorted(voters.keys()))))
107-
108+ def do_private_commands(self, nick, **kwargs):
109+ commands = [ "#"+x[3:] for x in dir(self) if x[:3]=="do_" ]
110+ commands.sort()
111+ message = "Available commands: "+(" ".join(commands))
112+ self.privateReply(nick, message)
113 # Commands for Anyone:
114 def do_action(self, **kwargs):
115 """Add action item to the minutes.
116@@ -551,9 +569,14 @@
117 m = items.Link(**kwargs)
118 self.additem(m)
119 def do_commands(self, **kwargs):
120- commands = [ "#"+x[3:] for x in dir(self) if x[:3]=="do_" ]
121+ commands = ["action", "info", "idea", "nick", "link", "commands"]
122 commands.sort()
123 self.reply("Available commands: "+(" ".join(commands)))
124+ def do_done(self, nick, **kwargs):
125+ """Add aggreement to the minutes - chairs only."""
126+ if not self.isChair(nick): return
127+ m = items.Done(**kwargs)
128+ self.additem(m)
129
130
131 class Meeting(MeetingCommands, object):
132@@ -561,7 +584,8 @@
133 _restrictlogs = False
134 def __init__(self, channel, owner, oldtopic=None,
135 filename=None, writeRawLog=False,
136- setTopic=None, sendReply=None, getRegistryValue=None,
137+ setTopic=None, sendReply=None, sendPrivateReply=None,
138+ getRegistryValue=None,
139 safeMode=False, channelNicks=None,
140 extraConfig={}, network='nonetwork'):
141 self.config = Config(self, writeRawLog=writeRawLog, safeMode=safeMode,
142@@ -570,6 +594,8 @@
143 self._registryValue = getRegistryValue
144 if sendReply is not None:
145 self._sendReply = sendReply
146+ if sendPrivateReply is not None:
147+ self._sendPrivateReply = sendPrivateReply
148 if setTopic is not None:
149 self._setTopic = setTopic
150 self.owner = owner
151@@ -585,8 +611,9 @@
152 self.attendees = {}
153 self.chairs = {}
154 self.voters = {}
155- self.votes={}
156- self.votesrequired=0
157+ self.publicVoters = {}
158+ self.votes = {}
159+ self.votesrequired = 0
160 self.activeVote = ""
161 self._writeRawLog = writeRawLog
162 self._meetingTopic = None
163@@ -604,6 +631,10 @@
164 self._sendReply(self.config.enc(x))
165 else:
166 print "REPLY:", self.config.enc(x)
167+ def privateReply(self, nick, x):
168+ """Send a reply to nick"""
169+ if hasattr(self, '_sendPrivateReply') and not self._lurk:
170+ self._sendPrivateReply(self.config.enc(nick), self.config.enc(x))
171 def topic(self, x):
172 """Set the topic in the IRC channel."""
173 if hasattr(self, '_setTopic') and not self._lurk:
174@@ -656,7 +687,7 @@
175 self.do_link(nick=nick, line=line,
176 linenum=linenum, time_=time_)
177 self.save(realtime_update=True)
178- if re.match("\+1|0|\+0|-1",line):
179+ if re.match("\+1|0|\+0|-0|-1",line):
180 self.doCastVote(nick,line,time_)
181 def doCastVote(self, nick, line, time_=None, private=False):
182 """if a vote is underway and the nick is a registered voter
183@@ -669,6 +700,7 @@
184 if self.activeVote:
185 self.currentVote[nick]=line
186 if private is False:
187+ self.publicVoters[self.activeVote].append(nick)
188 self.reply(line + " received from " + nick)
189
190 #if the vote was in a private message - how do we do that??
191
192=== modified file 'plugin.py'
193--- plugin.py 2013-07-24 18:38:57 +0000
194+++ plugin.py 2013-11-20 00:00:42 +0000
195@@ -100,12 +100,15 @@
196 irc.sendMsg(ircmsgs.topic(channel, x))
197 def _sendReply(x):
198 irc.sendMsg(ircmsgs.privmsg(channel, x))
199+ def _sendPrivateReply(nick, x):
200+ irc.sendMsg(ircmsgs.privmsg(nick, x))
201 def _channelNicks():
202 return irc.state.channels[channel].users
203 M = meeting.Meeting(channel=channel, owner=nick,
204 oldtopic=irc.state.channels[channel].topic,
205 writeRawLog=True,
206 setTopic = _setTopic, sendReply = _sendReply,
207+ sendPrivateReply = _sendPrivateReply,
208 getRegistryValue = self.registryValue,
209 safeMode=True, channelNicks=_channelNicks,
210 network=network,
211
212=== modified file 'writers.py'
213--- writers.py 2013-07-13 10:36:00 +0000
214+++ writers.py 2013-11-20 00:00:42 +0000
215@@ -1214,17 +1214,21 @@
216 # Votes
217 Votes = [ ]
218 Votes.append(self.heading('Vote results'))
219- for m in M.votes:
220- #differentiate denied votes somehow, strikethrough perhaps?
221- Votes.append(" * "+m)
222+ # reversed to show the oldest first
223+ for m in reversed(M.votes):
224+ # differentiate denied votes somehow, strikethrough perhaps?
225+ Votes.append(" * [[%(fullLogsFullURL)s#"+str(M.votes[m][3])+" "+m+"]]")
226 motion = "Deadlock"
227 if(M.votes[m][0] > M.votes[m][1]):
228 motion = "Motion carried"
229 elif(M.votes[m][0] < M.votes[m][2]):
230 motion = "Motion denied"
231-
232+
233 Votes.append(" * " + motion + " (For/Against/Abstained "+str(M.votes[m][0])+"/"+str(M.votes[m][2])+"/"+str(M.votes[m][1]) + ")")
234- Votes = "\n".join(Votes)
235+ if len(M.publicVoters[m]) > 0:
236+ publicVoters = ', '.join(set(M.publicVoters[m]))
237+ Votes.append(" * Voters " + publicVoters)
238+ Votes = "\n".join(Votes)
239 return Votes
240
241 def actionItems(self):
242@@ -1281,6 +1285,23 @@
243 return None
244 else:
245 return ActionItemsPerson
246+
247+ def doneItems(self):
248+ M = self.M
249+ # Action Items
250+ DoneItems = [ ]
251+ numActionItems = 0
252+ DoneItems.append(self.heading('Done items'))
253+ for m in M.minutes:
254+ # The hack below is needed because of pickling problems
255+ if m.itemtype != "DONE": continue
256+ #already escaped
257+ DoneItems.append(" * %s"%moin(m.line))
258+ numActionItems += 1
259+ if numActionItems == 0:
260+ DoneItems.append(" * (none)")
261+ DoneItems = "\n".join(DoneItems)
262+ return DoneItems
263
264 def peoplePresent(self):
265 M = self.M
266@@ -1315,9 +1336,10 @@
267 body = [ ]
268 body.append(self.body_start%repl)
269 body.append(self.meetingItems())
270- body.append(self.votes())
271+ body.append(self.votes()%repl)
272 body.append(self.actionItems())
273 body.append(self.actionItemsPerson())
274+ body.append(self.doneItems())
275 body.append(self.peoplePresent())
276 body.append(self.fullLog())
277 body.append(textwrap.dedent("""\

Subscribers

People subscribed via source and target branches