Adding ability for a non-chair to end a meeting after time
This is particularly useful if the chair forgot to end the meeting or has been net split out and the nick is no longer useable. Change-Id: Id5c0c11ec94898f3a79ac253a9d804baec79a85d
This commit is contained in:
parent
b06edb2517
commit
5f8bdbeef1
|
@ -92,11 +92,13 @@ class MeetBot(callbacks.Plugin):
|
|||
# Start meeting if we are requested
|
||||
if payload[:13] == '#startmeeting':
|
||||
if M is not None:
|
||||
irc.error("Can't start another meeting, one is in progress.")
|
||||
irc.error("Can't start another meeting, one is in progress."
|
||||
" Use #endmeeting first.")
|
||||
return
|
||||
name = payload[13:].strip()
|
||||
if not name:
|
||||
irc.error("A meeting name is required, e.g., '#startmeeting Marketing Committee'")
|
||||
irc.error("A meeting name is required, e.g., "
|
||||
"'#startmeeting Marketing Committee'")
|
||||
return
|
||||
# This callback is used to send data to the channel:
|
||||
def _setTopic(x):
|
||||
|
|
|
@ -88,11 +88,12 @@ class Config(object):
|
|||
# you have to use doubled percent signs. Also, it gets split by
|
||||
# '\n' and each part between newlines get said in a separate IRC
|
||||
# message.
|
||||
startMeetingMessage = ("Meeting started %(starttime)s %(timeZone)s. "
|
||||
"The chair is %(chair)s. Information about MeetBot at "
|
||||
"%(MeetBotInfoURL)s.\n"
|
||||
"Useful Commands: #action #agreed #help #info #idea #link "
|
||||
"#topic #startvote.")
|
||||
startMeetingMessage = ("Meeting started %(starttime)s %(timeZone)s "
|
||||
"and is due to finish in %(length)d minutes. "
|
||||
"The chair is %(chair)s. Information about MeetBot at "
|
||||
"%(MeetBotInfoURL)s.\n"
|
||||
"Useful Commands: #action #agreed #help #info #idea #link "
|
||||
"#topic #startvote.")
|
||||
endMeetingMessage = ("Meeting ended %(endtime)s %(timeZone)s. "
|
||||
"Information about MeetBot at %(MeetBotInfoURL)s . "
|
||||
"(v %(__version__)s)\n"
|
||||
|
@ -309,6 +310,7 @@ class MeetingCommands(object):
|
|||
def do_startmeeting(self, nick, time_, line, **kwargs):
|
||||
"""Begin a meeting."""
|
||||
self.starttime = time_
|
||||
self.expectedend = time.mktime(time_) + self.length * 60
|
||||
repl = self.replacements()
|
||||
message = self.config.startMeetingMessage%repl
|
||||
for messageline in message.split('\n'):
|
||||
|
@ -318,7 +320,8 @@ class MeetingCommands(object):
|
|||
self.do_meetingname(nick=nick, line=line, time_=time_, **kwargs)
|
||||
def do_endmeeting(self, nick, time_, **kwargs):
|
||||
"""End the meeting."""
|
||||
if not self.isChair(nick): return
|
||||
# Chairs can end the meeting early - anyone can end it after the meeting length
|
||||
if (not self.isChair(nick)) and (self.expectedend > time.mktime(time_)): return
|
||||
if self.oldtopic:
|
||||
self.topic(self.oldtopic)
|
||||
self.endtime = time_
|
||||
|
@ -541,7 +544,7 @@ class Meeting(MeetingCommands, object):
|
|||
filename=None, writeRawLog=False,
|
||||
setTopic=None, sendReply=None, getRegistryValue=None,
|
||||
safeMode=False, channelNicks=None,
|
||||
extraConfig={}, network='nonetwork'):
|
||||
extraConfig={}, network='nonetwork', length=60):
|
||||
if getRegistryValue is not None:
|
||||
self._registryValue = getRegistryValue
|
||||
if sendReply is not None:
|
||||
|
@ -551,6 +554,7 @@ class Meeting(MeetingCommands, object):
|
|||
self.owner = owner
|
||||
self.channel = channel
|
||||
self.network = network
|
||||
self.length = length
|
||||
self.currenttopic = ""
|
||||
self.config = Config(self, writeRawLog=writeRawLog, safeMode=safeMode,
|
||||
extraConfig=extraConfig)
|
||||
|
@ -603,7 +607,7 @@ class Meeting(MeetingCommands, object):
|
|||
return (nick == self.owner or nick in self.chairs)
|
||||
def save(self, **kwargs):
|
||||
return self.config.save(**kwargs)
|
||||
# Primary enttry point for new lines in the log:
|
||||
# Primary entry point for new lines in the log:
|
||||
def addline(self, nick, line, time_=None):
|
||||
"""This is the way to add lines to the Meeting object.
|
||||
"""
|
||||
|
@ -666,6 +670,7 @@ class Meeting(MeetingCommands, object):
|
|||
repl['starttime'] = time.asctime(self.starttime)
|
||||
if getattr(self, "endtime", None) is not None:
|
||||
repl['endtime'] = time.asctime(self.endtime)
|
||||
repl['length'] = self.length
|
||||
repl['__version__'] = __version__
|
||||
repl['chair'] = self.owner
|
||||
repl['urlBasename'] = self.config.filename(url=True)
|
||||
|
@ -677,9 +682,11 @@ class Meeting(MeetingCommands, object):
|
|||
|
||||
|
||||
def parse_time(time_):
|
||||
try: return time.strptime(time_, "%H:%M:%S")
|
||||
# Need a date > 1970 to convert to a timestamp for comparisons.
|
||||
# Without a year here, Python assumes 1900.
|
||||
try: return time.strptime("01/01/2000 "+time_, "%m/%d/%Y %H:%M:%S")
|
||||
except ValueError: pass
|
||||
try: return time.strptime(time_, "%H:%M")
|
||||
try: return time.strptime("01/01/2000 "+time_, "%m/%d/%Y %H:%M")
|
||||
except ValueError: pass
|
||||
logline_re = re.compile(r'\[?([0-9: ]*)\]? *<[@+]?([^>]+)> *(.*)')
|
||||
loglineAction_re = re.compile(r'\[?([0-9: ]*)\]? *\* *([^ ]+) *(.*)')
|
||||
|
@ -688,12 +695,17 @@ loglineAction_re = re.compile(r'\[?([0-9: ]*)\]? *\* *([^ ]+) *(.*)')
|
|||
def process_meeting(contents, channel, filename,
|
||||
extraConfig = {},
|
||||
dontSave=False,
|
||||
safeMode=True):
|
||||
M = Meeting(channel=channel, owner=None,
|
||||
filename=filename, writeRawLog=False, safeMode=safeMode,
|
||||
extraConfig=extraConfig)
|
||||
if dontSave:
|
||||
M.config.dontSave = True
|
||||
safeMode=True,
|
||||
existingMeeting=None):
|
||||
# Allow tests part way through the meeting
|
||||
if existingMeeting:
|
||||
M = existingMeeting
|
||||
else:
|
||||
M = Meeting(channel=channel, owner=None,
|
||||
filename=filename, writeRawLog=False, safeMode=safeMode,
|
||||
extraConfig=extraConfig)
|
||||
if dontSave:
|
||||
M.config.dontSave = True
|
||||
# process all lines
|
||||
for line in contents.split('\n'):
|
||||
# match regular spoken lines:
|
||||
|
|
|
@ -15,16 +15,17 @@ import ircmeeting.writers as writers
|
|||
running_tests = True
|
||||
|
||||
def process_meeting(contents, extraConfig={}, dontSave=True,
|
||||
filename='/dev/null'):
|
||||
filename='/dev/null', existingMeeting=None):
|
||||
"""Take a test script, return Meeting object of that meeting.
|
||||
|
||||
To access the results (a dict keyed by extensions), use M.save(),
|
||||
with M being the return of this function.
|
||||
"""
|
||||
return meeting.process_meeting(contents=contents,
|
||||
channel="#none", filename=filename,
|
||||
dontSave=dontSave, safeMode=False,
|
||||
extraConfig=extraConfig)
|
||||
channel="#none", filename=filename,
|
||||
dontSave=dontSave, safeMode=False,
|
||||
extraConfig=extraConfig,
|
||||
existingMeeting=existingMeeting)
|
||||
|
||||
class MeetBotTest(unittest.TestCase):
|
||||
|
||||
|
@ -248,6 +249,20 @@ class MeetBotTest(unittest.TestCase):
|
|||
assert re.search(r'href.*mailto://a@mail.com.*suffix',
|
||||
results), "URL missing 5"
|
||||
|
||||
def test_lateEnd(self):
|
||||
"""Test that anyone can end a meeting late
|
||||
"""
|
||||
script = """
|
||||
20:13:50 <x> #startmeeting
|
||||
20:43:57 <y> #endmeeting
|
||||
"""
|
||||
M = process_meeting(script)
|
||||
assert M._meetingIsOver == False, "Early call from non-chair should fail"
|
||||
process_meeting("22:13:52 <z> #endmeeting", existingMeeting=M)
|
||||
assert M._meetingIsOver, "Late call from non-chair should succeed"
|
||||
results = M.save()['.txt']
|
||||
assert 'Meeting ended at 22:13:52' in results
|
||||
|
||||
def t_css(self):
|
||||
"""Runs all CSS-related tests.
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue