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

Reply via email to