Chromium Code Reviews| 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) |