From 73aa646f3d4a18abf5c5a3ef7161aaa3b978c41f Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 15 Dec 2012 22:16:37 +0100 Subject: [PATCH] Redmine: Add announces support. --- Redmine/config.py | 10 ++++++++++ Redmine/plugin.py | 46 +++++++++++++++++++++++++++++++++++++++++++++- Redmine/test.py | 41 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/Redmine/config.py b/Redmine/config.py index 5f18e67..bebb4fa 100644 --- a/Redmine/config.py +++ b/Redmine/config.py @@ -68,5 +68,15 @@ conf.registerChannelValue(Redmine.format, 'issue', '\x02%(author__name)s\x02 (%(author__id)i) on \x02%(created_on)s\x02, ' 'last updated on \x02%(updated_on)s', _("""Format of issues displayed by @issue."""))) +conf.registerGroup(Redmine.format, 'announces') +conf.registerChannelValue(Redmine.format.announces, 'issue', + registry.String('Updated issue: \x02#%(id)i (%(status__name)s)\x02: ' + '\x02%(subject)s\x02 in \x02%(project__name)s\x02 (%(project__id)i).', + _("""Format of issues displayed by @issue."""))) + +conf.registerGroup(Redmine, 'announce') +conf.registerChannelValue(Redmine.announce, 'sites', + registry.SpaceSeparatedListOfStrings([], + _("""List of sites announced on this channel."""))) # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/Redmine/plugin.py b/Redmine/plugin.py index a4e3547..c9357f4 100644 --- a/Redmine/plugin.py +++ b/Redmine/plugin.py @@ -28,12 +28,15 @@ ### +import sys import json +import time import supybot.conf as conf import supybot.utils as utils from supybot.commands import * import supybot.plugins as plugins +import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks from supybot.i18n import PluginInternationalization, internationalizeDocstring @@ -149,6 +152,47 @@ class Redmine(callbacks.Plugin): This should describe *how* to use this plugin.""" threaded = True + _last_fetch = {} # {site: (time, data)} + def __call__(self, irc, msg): + super(Redmine, self).__call__(irc, msg) + with self.registryValue('sites', value=False).editable() as sites: + assert isinstance(sites, dict), repr(sites) + for site_name, site in sites.items(): + if 'interval' not in site: + site['interval'] = 60 + if site_name in self._last_fetch: + last_time, last_data = self._last_fetch[site_name] + if last_time>time.time()-site['interval']: + continue + data = fetch(site, 'issues', sort='updated_on:desc') + self._last_fetch[site_name] = (time.time(), data) + if 'last_time' not in locals(): + continue + try: + last_update = last_data['issues'][0]['updated_on'] + except IndexError: + # There was no issue submitted before + last_update = '' + + announces = [] + for issue in data['issues']: + if issue['updated_on'] <= last_update: + break + announces.append(issue) + for channel in irc.state.channels: + if site_name in self.registryValue('announce.sites', + channel): + format_ = self.registryValue('format.announces.issue', + channel) + for issue in announces: + s = format_ % flatten_subdicts(issue) + if sys.version_info[0] < 3: + s = s.encode('utf8', errors='replace') + msg = ircmsgs.privmsg(channel, s) + irc.sendMsg(msg) + + + class site(callbacks.Commands): conf = conf.supybot.plugins.Redmine @internationalizeDocstring @@ -158,7 +202,7 @@ class Redmine(callbacks.Plugin): Add a site to the list of known redmine sites.""" if not url.endswith('/'): url += '/' - if name == '': + if not name: irc.error(_('Invalid site name.'), Raise=True) if name in self.conf.sites(): irc.error(_('This site name is already registered.'), Raise=True) diff --git a/Redmine/test.py b/Redmine/test.py index 32b621d..5db95f3 100644 --- a/Redmine/test.py +++ b/Redmine/test.py @@ -28,6 +28,9 @@ ### +import time + +import supybot.conf as conf from supybot.test import * def init(f): @@ -39,8 +42,8 @@ def init(f): self.assertNotError('site remove lqdn') return newf -class RedmineTestCase(PluginTestCase): - plugins = ('Redmine',) +class RedmineTestCase(ChannelPluginTestCase): + plugins = ('Redmine', 'Config', 'Utilities') def testSite(self): self.assertRegexp('site list', 'No registered') @@ -66,6 +69,40 @@ class RedmineTestCase(PluginTestCase): self.assertNotError('issue lqdn 130') self.assertResponse('issue lqdn 999999', 'Error: Issue not found.') + @init + def testAnnounce(self): + pl = self.irc.getCallback('Redmine') + + self.assertNotError('ping') + self.assertNotError('config plugins.Redmine.announce.sites lqdn') + with conf.supybot.plugins.Redmine.sites.editable() as sites: + sites['lqdn']['interval'] = 1 + + # Make sure it does not update everytime a message is received + self.assertNotError('ping') + self.assertIs(self.irc.takeMsg(), None) + last_fetch = pl._last_fetch.copy() + self.assertNotError('ping') + self.assertEqual(last_fetch, pl._last_fetch) + self.assertIs(self.irc.takeMsg(), None) + + # Make sure it updates after the interval is finished, but there is + # nothing new + time.sleep(1.1) + self.assertNotError('ping') + self.assertNotEqual(last_fetch, pl._last_fetch) + self.assertIs(self.irc.takeMsg(), None) + + # Let's cheat a little and "olderize" the latest issue. + pl._last_fetch['lqdn'][1]['issues'][0]['updated_on'] = \ + '2012/12/09 20:37:27 +0100' + time.sleep(1.1) + self.assertNotError('ping') + self.assertNotEqual(last_fetch, pl._last_fetch) + m = self.irc.takeMsg() + self.assertIsNot(m, None) + + if not network: del RedmineTestCase