jenkins-bot has submitted this change and it was merged.
Change subject: Better IRC project list
......................................................................
Better IRC project list
T1231: If no projects were matched, and all projects
are 'hidden', show them anyway.
T86448: projects are sorted alphabetically in two groups,
matched and non-matched
T86759: matched projects are shown, even if they are in
a 'hidden' project type. They are always shown
before non-matched projects
T88011: number of projects is limited to four
Plus:
- Show (no projects) if there are no tags attached,
Bug: T1231
Bug: T86448
Bug: T86759
Bug: T88011
Change-Id: Ib0b95a8639388778a2fce8baadbfb20d676e4759
---
M channelfilter.py
M messagebuilder.py
M redis2irc.py
M redis2stdout.py
M test_channels_yaml.py
5 files changed, 119 insertions(+), 38 deletions(-)
Approvals:
Legoktm: Looks good to me, approved
jenkins-bot: Verified
diff --git a/channelfilter.py b/channelfilter.py
index 6fac33f..1fa6477 100644
--- a/channelfilter.py
+++ b/channelfilter.py
@@ -5,7 +5,7 @@
import time
import yaml
import re
-
+import collections
import logging
logger = logging.getLogger('wikibugs2.channelfilter')
@@ -64,16 +64,18 @@
"""
:param project: Get all channels to spam for given projects
:type project: iterable
+ :returns: dict[channel: matched projects]
"""
- channels = set()
+ channels = collections.defaultdict(list)
for channel in self.config['channels']:
for project in projects:
if self.config['channels'][channel].match(project):
- channels.add(channel)
+ channels[channel].append(project)
break
if not channels:
- channels.add(self.default_channel)
- if '/dev/null' in channels:
- channels.remove('/dev/null')
- channels.add(self.firehose_channel)
+ channels[self.default_channel] = []
+
+ channels.pop('/dev/null', None)
+ channels[self.firehose_channel] = []
+
return channels
diff --git a/messagebuilder.py b/messagebuilder.py
index 4de168d..6276850 100644
--- a/messagebuilder.py
+++ b/messagebuilder.py
@@ -3,10 +3,15 @@
class IRCMessageBuilder(object):
MAX_MESSAGE_LENGTH = 80 * 4
+ MAX_NUM_PROJECTS = 4
- COLORS = {'white': 0, 'black': 1, 'blue': 2, 'green': 3, 'red': 4,
'brown': 5,
- 'purple': 6, 'orange': 7, 'yellow': 8, 'lime': 9, 'teal': 10,
- 'cyan': 11, 'royal': 12, 'pink': 13, 'grey': 14, 'silver': 15}
+ COLORS = {
+ 'white': 0, 'black': 1, 'blue': 2, 'green': 3, 'red': 4, 'brown': 5,
+ 'purple': 6, 'orange': 7, 'yellow': 8, 'lime': 9, 'teal': 10,
+ 'cyan': 11, 'royal': 12, 'pink': 13, 'grey': 14, 'silver': 15,
+
+ 'indigo': 6, 'violet': 13, # match phabricator colors
+ }
PRIORITY = {
'100': 'Unbreak!',
@@ -27,15 +32,27 @@
OUTPUT_PROJECT_TYPES = ['briefcase', 'users', 'umbrella']
- def colorify(self, text, foreground=None, background=None):
- outtext = "\x03"
+ TEXT_STYLE = {
+ 'bold': '\x02',
+ 'underline': '\x1f',
+ 'reversed': '\x16',
+ }
+
+ def ircformat(self, text, foreground=None, background=None, style=None):
+ outtext = ""
+ if style:
+ outtext += self.TEXT_STYLE[style]
+ if foreground or background:
+ outtext += "\x03"
if foreground:
outtext += str(self.COLORS[foreground])
if background:
outtext += "," + str(self.COLORS[background])
outtext += text
- outtext += "\x03"
-
+ if foreground or background:
+ outtext += "\x03"
+ if style:
+ outtext += self.TEXT_STYLE[style]
return outtext
def _human_status(self, name):
@@ -53,45 +70,103 @@
"""
return text.replace('\n', ' ').replace('\r', ' ')
- def build_message(self, useful_info):
- text = ''
- if useful_info['projects']:
- # This could be either a dict (if we are able to scrape all the
info)
- # Or a list, if it could not scrape all the info. Handle both
cases.
- if isinstance(useful_info['projects'], dict):
- visible_projects = [
- p for p, info in useful_info['projects'].items()
- if info['tagtype'] in self.OUTPUT_PROJECT_TYPES and not
info['disabled']]
+ def build_project_text(self, all_projects, matched_projects):
+ """
+ Build project text to be shown.
+ Requirement:
+ (1) Show matched projects first, and bold
+ (2) Only show other projects if they are in
self.OUTPUT_PROJECT_TYPES
+ and not disabled
+ (3) Colors match phabricator colors
+ (4) If the list is empty (e.g. only tags and firehose channel),
show
+ all projects, irrespective of type.
+ (4a) If there are no projects at all, show "(no projects)" in
bright red
+ (5) Never show more than self.MAX_NUM_PROJECTS, even if they are
matched
+
+ :param all_projects: dict[project name, info] (scraped) or
+ list[project name] (failed scraping)
+ :param matched_projects: list[project name]
+ :return: list with formatted projects
+ """
+
+ # (3) format all projects
+ # and map to a standardized format in the process
+ projects = {}
+ for project in all_projects:
+ try:
+ info = all_projects[project]
+ except KeyError:
+ info = {
+ 'shade': 'green',
+ 'tagtype': 'briefcase',
+ 'disabled': False,
+ 'uri': ''
+ }
+ info['matched'] = project in matched_projects
+ style = 'bold' if info['matched'] else None
+ info['irc_text'] = self.ircformat(project, info['shade'],
style=style)
+ projects[project] = info
+
+ # (1)
+ matched_parts = [projects[project]['irc_text'] for project in
sorted(matched_projects)]
+
+ # (2)
+ other_projects = [proj for proj in all_projects if proj not in
matched_projects]
+ other_parts = []
+ hidden_parts = []
+ for project in sorted(other_projects):
+ info = projects[project]
+ if info['tagtype'] in self.OUTPUT_PROJECT_TYPES and not
info['disabled']:
+ other_parts.append(info['irc_text'])
else:
- visible_projects = useful_info['projects']
- text += self.colorify(', '.join(visible_projects), 'green')
- text += ': '
+ hidden_parts.append(info['irc_text'])
+
+ # (4)
+ show_parts = matched_parts + other_parts
+ if len(show_parts) == 0:
+ show_parts = hidden_parts
+ hidden_parts = []
+ if len(show_parts) == 0:
+ show_parts = self.ircformat('(no projects)', 'red', 'bold')
+
+ # (5)
+ hidden_parts.extend(show_parts[self.MAX_NUM_PROJECTS:])
+ show_parts = show_parts[:self.MAX_NUM_PROJECTS]
+
+ if len(hidden_parts) == 1:
+ show_parts.append("and 1 other")
+ elif len(hidden_parts) > 0:
+ show_parts.append("and %i others" % len(hidden_parts))
+ return ", ".join(show_parts)
+
+ def build_message(self, useful_info):
+ text = self.build_project_text(useful_info['projects'],
useful_info['matched_projects']) + ': '
text += useful_info['title']
text += ' - ' + useful_info['url']
- text += " (" + self.colorify(useful_info['user'], "teal") + ") "
+ text += " (" + self.ircformat(useful_info['user'], "teal") + ") "
is_new = 'new' in useful_info
if is_new:
- text += self.colorify('NEW', 'green') + ' '
+ text += self.ircformat('NEW', 'green') + ' '
elif 'status' in useful_info:
status = useful_info['status']
- text += self.colorify(self._human_status(status['old']), 'brown')
+ text += self.ircformat(self._human_status(status['old']), 'brown')
text += '>'
- text += self.colorify(self._human_status(status['new']), 'green')
+ ' '
+ text += self.ircformat(self._human_status(status['new']), 'green')
+ ' '
if 'priority' in useful_info:
prio = useful_info['priority']
text += 'p:'
if prio['old']:
- text += self.colorify(self._human_prio(prio['old']), 'brown')
+ text += self.ircformat(self._human_prio(prio['old']), 'brown')
text += '>'
- text += self.colorify(self._human_prio(prio['new']), 'green')
+ text += self.ircformat(self._human_prio(prio['new']), 'green')
text += ' '
if 'assignee' in useful_info:
ass = useful_info['assignee']
text += 'a:'
if ass['old']:
- text += self.colorify(ass['old'], 'brown')
+ text += self.ircformat(ass['old'], 'brown')
text += '>'
- text += self.colorify(str(ass['new']), 'green')
+ text += self.ircformat(str(ass['new']), 'green')
text += ' '
if 'comment' in useful_info:
diff --git a/redis2irc.py b/redis2irc.py
index 46be480..8b0d2c8 100644
--- a/redis2irc.py
+++ b/redis2irc.py
@@ -78,14 +78,16 @@
if useful_info['user'] == 'gerritbot':
# Ignore "Patch to review" stuff
return
- text = bot.builder.build_message(useful_info)
updated = bot.chanfilter.update()
if updated:
bot.privmsg('#wikimedia-labs', '!log tools.wikibugs Updated
channels.yaml to: %s' % updated)
logger.info('Updated channels.yaml to: %s' % updated)
channels = bot.chanfilter.channels_for(useful_info['projects'])
- for chan in channels:
+ for chan, matched_projects in channels.items():
+ useful_info['channel'] = chan
+ useful_info['matched_projects'] = matched_projects
+ text = bot.builder.build_message(useful_info)
bot.privmsg(chan, text)
diff --git a/redis2stdout.py b/redis2stdout.py
index 30823d0..58ba414 100644
--- a/redis2stdout.py
+++ b/redis2stdout.py
@@ -46,6 +46,8 @@
useful_info = self.rqueue.get()
print(useful_info)
if useful_info:
+ useful_info['matched_projects'] =
list(useful_info['projects'])[:]
+ useful_info['channel'] = '#wikimedia-labs'
text = self.builder.build_message(useful_info)
updated = self.channelfilter.update()
if updated:
diff --git a/test_channels_yaml.py b/test_channels_yaml.py
index 04474d7..f1f745a 100755
--- a/test_channels_yaml.py
+++ b/test_channels_yaml.py
@@ -13,11 +13,11 @@
assertEquals(
{'#mediawiki-feed', '#wikimedia-releng'},
- chanfilter.channels_for(['Continuous-Integration']))
+ set(chanfilter.channels_for(['Continuous-Integration'])))
assertEquals(
{'#mediawiki-feed', '#wikimedia-devtools', '#wikimedia-dev'},
- chanfilter.channels_for(['Phabricator']))
+ set(chanfilter.channels_for(['Phabricator'])))
json.load(open("config.json.example"))
--
To view, visit https://gerrit.wikimedia.org/r/190733
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ib0b95a8639388778a2fce8baadbfb20d676e4759
Gerrit-PatchSet: 2
Gerrit-Project: labs/tools/wikibugs2
Gerrit-Branch: master
Gerrit-Owner: Merlijn van Deen <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits