Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(532)

Unified Diff: status.py

Issue 84943003: chromium-status: automatically linkify usernames/status messages (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/chromium-status
Patch Set: feedback from maruel Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | stylesheets/style.css » ('j') | templates/main.html » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: status.py
diff --git a/status.py b/status.py
index fc37e40ea896dcbff03fe3c5f8f14ea01ca3670c..317dacb2850e26fb556110073bad2f3592a97e6a 100644
--- a/status.py
+++ b/status.py
@@ -22,6 +22,102 @@ ALLOWED_ORIGINS = [
]
+class Link(object):
Vadim Sh. 2013/12/03 20:06:36 I'd convert it to collections.namedtuple. Shorter,
Vadim Sh. 2013/12/03 20:06:36 Also, 'Link' is a misleading name. This object can
vapier 2013/12/04 07:54:33 we discussed collections in the previous patch. a
+ """Simple object to hold text that might be linked"""
+
+ def __init__(self, text, target=None, is_email=False):
+ self.text = text
+ self.target = target
+ self.is_email = is_email
+
+ def __repr__(self):
+ return 'Link({%s->%s})' % (self.text, self.target)
+
+
+class LinkableText(object):
+ """Turns arbitrary text into a set of links"""
+
+ GERRIT_URLS = {
+ 'chrome': 'https://chrome-internal-review.googlesource.com',
+ 'chromium': 'https://chromium-review.googlesource.com',
+ }
+
+ WATERFALL_URLS = {
+ 'chromeos': 'http://chromegw/i/chromeos',
+ 'chromiumos': 'http://build.chromium.org/p/chromiumos',
+ }
+
+ # Automatically linkify convert known strings for the user.
Vadim Sh. 2013/12/03 20:06:36 nit: 'linkify convert'. Keep only one?)
vapier 2013/12/04 07:54:33 done
+ _CONVERTS = [
Vadim Sh. 2013/12/03 20:06:36 Is is possible to get rid of 're.compile' repetiti
vapier 2013/12/04 07:54:33 it is a bit cleaner, and it would provide self doc
+ # Convert CrOS bug links. Support the forms:
+ # http://crbug.com/1234
+ # http://crosbug.com/1234
+ # crbug/1234
+ # crosbug/p/1234
+ (re.compile(
+ # 1 2 3 4 5 6 7
+ r'\b((http://)?((crbug|crosbug)(\.com)?(/(p/)?[0-9]+)))\b',
+ flags=re.I),
+ r'http://\4.com\6', r'\1', False),
+
+ # Convert e-mail addresses.
+ (re.compile(r'(([-+.a-z0-9_!#$%&*/=?^_`{|}~]+)@[-a-z0-9.]+\.[a-z0-9]+)\b',
+ flags=re.I),
+ r'\1', r'\2', True),
Vadim Sh. 2013/12/03 20:06:36 Why not put 'mailto:' into target (i.e. r'mailto:\
vapier 2013/12/04 07:54:33 "mailto:" is part of the rendering side (i.e. HTML
+
+ # Convert SHA1's to gerrit links. Assume all external since
+ # there is no sane way to detect it's an internal CL.
+ (re.compile(r'\b([0-9a-f]{40})\b', flags=re.I),
+ r'%s/#q,\1,n,z' % GERRIT_URLS['chromium'], r'\1', False),
+
+ # Convert public gerrit CL numbers.
+ (re.compile(r'\b(CL:([0-9]+))\b', flags=re.I),
+ r'%s/\2' % GERRIT_URLS['chromium'], r'\1', False),
+ # Convert internal gerrit CL numbers.
+ (re.compile(r'\b(CL:\*([0-9]+))\b', flags=re.I),
+ r'%s/\2' % GERRIT_URLS['chrome'], r'\1', False),
+
+ # Do this for everyone since "cbuildbot" is unique to CrOS.
+ # Otherwise, we'd do this in the bootstrap func below.
+ # Match the string:
+ # Automatic: "cbuildbot" on "x86-generic ASAN" from.
+ (re.compile(r'("cbuildbot" on "([^"]+ canary)")',
+ flags=re.I),
+ r'%s/builders/\2' % WATERFALL_URLS['chromeos'], r'\1', False),
+ (re.compile(r'("cbuildbot" on "([^"]+)")',
+ flags=re.I),
+ r'%s/builders/\2' % WATERFALL_URLS['chromiumos'], r'\1', False),
+ ]
+
+ @classmethod
+ def bootstrap(cls, _app_name):
+ """Add conversions specific to |app_name| instance"""
+ pass
+
+ @classmethod
+ def parse(cls, text):
+ """Creates a list of Link objects based on |text|"""
+ if not text:
+ return []
+ for prog, target, pretty_text, is_email in cls._CONVERTS:
+ m = prog.search(text)
+ if m:
+ link = Link(m.expand(pretty_text),
+ target=m.expand(target),
+ is_email=is_email)
+ left_links = cls.parse(text[:m.start()].rstrip())
+ right_links = cls.parse(text[m.end():].lstrip())
+ return left_links + [link] + right_links
Vadim Sh. 2013/12/03 20:06:36 This looks nice and elegant :)
+ return [Link(text)]
+
+ def __init__(self, text):
+ self.raw_text = text
+ self.links = self.parse(text.strip())
+
+ def __str__(self):
+ return self.raw_text
+
+
class Status(db.Model):
"""Description for the status table."""
# The username who added this status.
@@ -31,6 +127,14 @@ class Status(db.Model):
# The message. It can contain html code.
message = db.StringProperty(required=True)
+ def __init__(self, *args, **kwargs):
+ # Normalize newlines otherwise the DB store barfs.
+ kwargs['message'] = kwargs.get('message', '').replace('\n', '')
+
+ super(Status, self).__init__(*args, **kwargs)
+ self.username_links = LinkableText(self.username)
Vadim Sh. 2013/12/03 20:06:36 __init__ in db.Model subclass is not very conventi
vapier 2013/12/04 07:54:33 for the message issue, i think this would fix it:
+ self.message_links = LinkableText(self.message)
+
@property
def general_state(self):
"""Returns a string representing the state that the status message
@@ -296,3 +400,4 @@ def bootstrap():
# Guarantee that at least one instance exists.
if db.GqlQuery('SELECT __key__ FROM Status').get() is None:
Status(username='none', message='welcome to status').put()
+ LinkableText.bootstrap(BasePage.APP_NAME)
« no previous file with comments | « no previous file | stylesheets/style.css » ('j') | templates/main.html » ('J')

Powered by Google App Engine
This is Rietveld 408576698