ubuntu-bots team mailing list archive
-
ubuntu-bots team
-
Mailing list archive
-
Message #00242
[Merge] lp:~lderan/ubuntu-bots/meeetingology-output into lp:~ubuntu-bots/ubuntu-bots/meetingology
Thomas Molloy has proposed merging lp:~lderan/ubuntu-bots/meeetingology-output into lp:~ubuntu-bots/ubuntu-bots/meetingology.
Requested reviews:
Ubuntu IRC Bots (ubuntu-bots)
For more details, see:
https://code.launchpad.net/~lderan/ubuntu-bots/meeetingology-output/+merge/195883
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.
--
https://code.launchpad.net/~lderan/ubuntu-bots/meeetingology-output/+merge/195883
Your team Ubuntu IRC Bots is requested to review the proposed merge of lp:~lderan/ubuntu-bots/meeetingology-output into lp:~ubuntu-bots/ubuntu-bots/meetingology.
=== modified file 'items.py'
--- items.py 2013-04-21 19:25:19 +0000
+++ items.py 2013-11-20 00:00:42 +0000
@@ -235,6 +235,9 @@
moin_template = """ * '''%(line)s''' (%(time)s)"""
class Help(GenericItem):
itemtype = 'HELP'
+class Done(GenericItem):
+ itemtype = 'DONE'
+ moin_template = """''ACTION:'' %(line)s"""
class Accepted(GenericItem):
itemtype = 'ACCEPTED'
starthtml = '<font color="green">'
=== modified file 'meeting.py'
--- meeting.py 2013-07-24 18:38:57 +0000
+++ meeting.py 2013-11-20 00:00:42 +0000
@@ -89,6 +89,8 @@
endMeetingMessage = ("Meeting ended %(endtime)s %(timeZone)s. "
"\n"
"Minutes: %(urlBasename)s.moin.txt")
+ endMeetingNotification = ("Meeting in %(channel)s has just ended")
+ endMeetingNotificationList = ["lderan"]
#TODO: endMeetingMessage should get filenames from the writers
@@ -285,6 +287,9 @@
message = self.config.startMeetingMessage%repl
for messageline in message.split('\n'):
self.reply(messageline)
+ self.do_private_commands(self.owner)
+ for chair in self.chairs:
+ self.do_private_commands(chair)
self.do_commands()
if line.strip():
self.do_meetingtopic(nick=nick, line=line, time_=time_, **kwargs)
@@ -294,7 +299,7 @@
if not self.isChair(nick): return
#close any open votes
if not self.activeVote=="":
- self.do_endvote(nick=nick,line=line,**kwargs)
+ self.do_endvote(nick, line, **kwargs)
if self.oldtopic:
self.topic(self.oldtopic)
self.endtime = time_
@@ -304,7 +309,9 @@
for messageline in message.split('\n'):
self.reply(messageline)
self._meetingIsOver = True
-
+ for nickToPM in self.config.endMeetingNotificationList:
+ self.privateReply(nickToPM, self.config.endMeetingNotification%repl)
+
def do_topic(self, nick, line, **kwargs):
"""Set a new topic in the channel."""
@@ -370,6 +377,7 @@
self.reply("Warning: Nick not in channel: %s"%chair)
self.addnick(chair, lines=0)
self.chairs.setdefault(chair, True)
+ self.do_private_commands(chair)
chairs = dict(self.chairs) # make a copy
chairs.setdefault(self.owner, True)
self.reply("Current chairs: %s"%(" ".join(sorted(chairs.keys()))))
@@ -422,6 +430,10 @@
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)")
self.activeVote=line.strip()
self.currentVote={}
+ self.publicVoters[self.activeVote] = []
+ #Need the line number for linking to the html output
+ self.currentVoteStartLine = 0
+ self.currentVoteStartLine = len(self.lines)
#need to set up a structure to hold vote results
#people can vote by saying +1 0 or -1
#if voters have been specified then only they can vote
@@ -453,7 +465,7 @@
for v in self.currentVote:
if re.match("-1",self.currentVote[v]):
vagainst+=1
- elif re.match("0|\+0",self.currentVote[v]):
+ elif re.match("0|\+0|-0",self.currentVote[v]):
vabstain+=1
elif re.match("\+1",self.currentVote[v]):
vfor+=1
@@ -474,7 +486,8 @@
else:
self.reply("Motion carried")
voteResult = "Carried"
- self.votes[self.activeVote]=[vfor,vabstain,vagainst]#store the results
+ #store the results
+ self.votes[self.activeVote]=[vfor, vabstain, vagainst, self.currentVoteStartLine]
"""Add informational item to the minutes."""
voteResultLog = "''Vote:'' "+self.activeVote+" ("+voteResult+")"
@@ -484,6 +497,7 @@
self.activeVote=""#allow another vote to be called
self.currentVote={}
+ self.currentVoteStartLine = 0
@@ -510,7 +524,11 @@
voters = dict(self.voters) # make a copy
#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?
self.reply("Current voters: %s"%(" ".join(sorted(voters.keys()))))
-
+ def do_private_commands(self, nick, **kwargs):
+ commands = [ "#"+x[3:] for x in dir(self) if x[:3]=="do_" ]
+ commands.sort()
+ message = "Available commands: "+(" ".join(commands))
+ self.privateReply(nick, message)
# Commands for Anyone:
def do_action(self, **kwargs):
"""Add action item to the minutes.
@@ -551,9 +569,14 @@
m = items.Link(**kwargs)
self.additem(m)
def do_commands(self, **kwargs):
- commands = [ "#"+x[3:] for x in dir(self) if x[:3]=="do_" ]
+ commands = ["action", "info", "idea", "nick", "link", "commands"]
commands.sort()
self.reply("Available commands: "+(" ".join(commands)))
+ def do_done(self, nick, **kwargs):
+ """Add aggreement to the minutes - chairs only."""
+ if not self.isChair(nick): return
+ m = items.Done(**kwargs)
+ self.additem(m)
class Meeting(MeetingCommands, object):
@@ -561,7 +584,8 @@
_restrictlogs = False
def __init__(self, channel, owner, oldtopic=None,
filename=None, writeRawLog=False,
- setTopic=None, sendReply=None, getRegistryValue=None,
+ setTopic=None, sendReply=None, sendPrivateReply=None,
+ getRegistryValue=None,
safeMode=False, channelNicks=None,
extraConfig={}, network='nonetwork'):
self.config = Config(self, writeRawLog=writeRawLog, safeMode=safeMode,
@@ -570,6 +594,8 @@
self._registryValue = getRegistryValue
if sendReply is not None:
self._sendReply = sendReply
+ if sendPrivateReply is not None:
+ self._sendPrivateReply = sendPrivateReply
if setTopic is not None:
self._setTopic = setTopic
self.owner = owner
@@ -585,8 +611,9 @@
self.attendees = {}
self.chairs = {}
self.voters = {}
- self.votes={}
- self.votesrequired=0
+ self.publicVoters = {}
+ self.votes = {}
+ self.votesrequired = 0
self.activeVote = ""
self._writeRawLog = writeRawLog
self._meetingTopic = None
@@ -604,6 +631,10 @@
self._sendReply(self.config.enc(x))
else:
print "REPLY:", self.config.enc(x)
+ def privateReply(self, nick, x):
+ """Send a reply to nick"""
+ if hasattr(self, '_sendPrivateReply') and not self._lurk:
+ self._sendPrivateReply(self.config.enc(nick), self.config.enc(x))
def topic(self, x):
"""Set the topic in the IRC channel."""
if hasattr(self, '_setTopic') and not self._lurk:
@@ -656,7 +687,7 @@
self.do_link(nick=nick, line=line,
linenum=linenum, time_=time_)
self.save(realtime_update=True)
- if re.match("\+1|0|\+0|-1",line):
+ if re.match("\+1|0|\+0|-0|-1",line):
self.doCastVote(nick,line,time_)
def doCastVote(self, nick, line, time_=None, private=False):
"""if a vote is underway and the nick is a registered voter
@@ -669,6 +700,7 @@
if self.activeVote:
self.currentVote[nick]=line
if private is False:
+ self.publicVoters[self.activeVote].append(nick)
self.reply(line + " received from " + nick)
#if the vote was in a private message - how do we do that??
=== modified file 'plugin.py'
--- plugin.py 2013-07-24 18:38:57 +0000
+++ plugin.py 2013-11-20 00:00:42 +0000
@@ -100,12 +100,15 @@
irc.sendMsg(ircmsgs.topic(channel, x))
def _sendReply(x):
irc.sendMsg(ircmsgs.privmsg(channel, x))
+ def _sendPrivateReply(nick, x):
+ irc.sendMsg(ircmsgs.privmsg(nick, x))
def _channelNicks():
return irc.state.channels[channel].users
M = meeting.Meeting(channel=channel, owner=nick,
oldtopic=irc.state.channels[channel].topic,
writeRawLog=True,
setTopic = _setTopic, sendReply = _sendReply,
+ sendPrivateReply = _sendPrivateReply,
getRegistryValue = self.registryValue,
safeMode=True, channelNicks=_channelNicks,
network=network,
=== modified file 'writers.py'
--- writers.py 2013-07-13 10:36:00 +0000
+++ writers.py 2013-11-20 00:00:42 +0000
@@ -1214,17 +1214,21 @@
# Votes
Votes = [ ]
Votes.append(self.heading('Vote results'))
- for m in M.votes:
- #differentiate denied votes somehow, strikethrough perhaps?
- Votes.append(" * "+m)
+ # reversed to show the oldest first
+ for m in reversed(M.votes):
+ # differentiate denied votes somehow, strikethrough perhaps?
+ Votes.append(" * [[%(fullLogsFullURL)s#"+str(M.votes[m][3])+" "+m+"]]")
motion = "Deadlock"
if(M.votes[m][0] > M.votes[m][1]):
motion = "Motion carried"
elif(M.votes[m][0] < M.votes[m][2]):
motion = "Motion denied"
-
+
Votes.append(" * " + motion + " (For/Against/Abstained "+str(M.votes[m][0])+"/"+str(M.votes[m][2])+"/"+str(M.votes[m][1]) + ")")
- Votes = "\n".join(Votes)
+ if len(M.publicVoters[m]) > 0:
+ publicVoters = ', '.join(set(M.publicVoters[m]))
+ Votes.append(" * Voters " + publicVoters)
+ Votes = "\n".join(Votes)
return Votes
def actionItems(self):
@@ -1281,6 +1285,23 @@
return None
else:
return ActionItemsPerson
+
+ def doneItems(self):
+ M = self.M
+ # Action Items
+ DoneItems = [ ]
+ numActionItems = 0
+ DoneItems.append(self.heading('Done items'))
+ for m in M.minutes:
+ # The hack below is needed because of pickling problems
+ if m.itemtype != "DONE": continue
+ #already escaped
+ DoneItems.append(" * %s"%moin(m.line))
+ numActionItems += 1
+ if numActionItems == 0:
+ DoneItems.append(" * (none)")
+ DoneItems = "\n".join(DoneItems)
+ return DoneItems
def peoplePresent(self):
M = self.M
@@ -1315,9 +1336,10 @@
body = [ ]
body.append(self.body_start%repl)
body.append(self.meetingItems())
- body.append(self.votes())
+ body.append(self.votes()%repl)
body.append(self.actionItems())
body.append(self.actionItemsPerson())
+ body.append(self.doneItems())
body.append(self.peoplePresent())
body.append(self.fullLog())
body.append(textwrap.dedent("""\
Follow ups