Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # coding=utf-8 | 1 # coding=utf-8 |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Status management pages.""" | 6 """Status management pages.""" |
| 7 | 7 |
| 8 import datetime | 8 import datetime |
| 9 import json | 9 import json |
| 10 import re | 10 import re |
| 11 | 11 |
| 12 from google.appengine.api import memcache | 12 from google.appengine.api import memcache |
| 13 from google.appengine.ext import db | 13 from google.appengine.ext import db |
| 14 | 14 |
| 15 from base_page import BasePage | 15 from base_page import BasePage |
| 16 import utils | 16 import utils |
| 17 | 17 |
| 18 | 18 |
| 19 ALLOWED_ORIGINS = [ | 19 ALLOWED_ORIGINS = [ |
| 20 'https://gerrit-int.chromium.org', | 20 'https://gerrit-int.chromium.org', |
| 21 'https://gerrit.chromium.org', | 21 'https://gerrit.chromium.org', |
| 22 ] | 22 ] |
| 23 | 23 |
| 24 | 24 |
| 25 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
| |
| 26 """Simple object to hold text that might be linked""" | |
| 27 | |
| 28 def __init__(self, text, target=None, is_email=False): | |
| 29 self.text = text | |
| 30 self.target = target | |
| 31 self.is_email = is_email | |
| 32 | |
| 33 def __repr__(self): | |
| 34 return 'Link({%s->%s})' % (self.text, self.target) | |
| 35 | |
| 36 | |
| 37 class LinkableText(object): | |
| 38 """Turns arbitrary text into a set of links""" | |
| 39 | |
| 40 GERRIT_URLS = { | |
| 41 'chrome': 'https://chrome-internal-review.googlesource.com', | |
| 42 'chromium': 'https://chromium-review.googlesource.com', | |
| 43 } | |
| 44 | |
| 45 WATERFALL_URLS = { | |
| 46 'chromeos': 'http://chromegw/i/chromeos', | |
| 47 'chromiumos': 'http://build.chromium.org/p/chromiumos', | |
| 48 } | |
| 49 | |
| 50 # 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
| |
| 51 _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
| |
| 52 # Convert CrOS bug links. Support the forms: | |
| 53 # http://crbug.com/1234 | |
| 54 # http://crosbug.com/1234 | |
| 55 # crbug/1234 | |
| 56 # crosbug/p/1234 | |
| 57 (re.compile( | |
| 58 # 1 2 3 4 5 6 7 | |
| 59 r'\b((http://)?((crbug|crosbug)(\.com)?(/(p/)?[0-9]+)))\b', | |
| 60 flags=re.I), | |
| 61 r'http://\4.com\6', r'\1', False), | |
| 62 | |
| 63 # Convert e-mail addresses. | |
| 64 (re.compile(r'(([-+.a-z0-9_!#$%&*/=?^_`{|}~]+)@[-a-z0-9.]+\.[a-z0-9]+)\b', | |
| 65 flags=re.I), | |
| 66 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
| |
| 67 | |
| 68 # Convert SHA1's to gerrit links. Assume all external since | |
| 69 # there is no sane way to detect it's an internal CL. | |
| 70 (re.compile(r'\b([0-9a-f]{40})\b', flags=re.I), | |
| 71 r'%s/#q,\1,n,z' % GERRIT_URLS['chromium'], r'\1', False), | |
| 72 | |
| 73 # Convert public gerrit CL numbers. | |
| 74 (re.compile(r'\b(CL:([0-9]+))\b', flags=re.I), | |
| 75 r'%s/\2' % GERRIT_URLS['chromium'], r'\1', False), | |
| 76 # Convert internal gerrit CL numbers. | |
| 77 (re.compile(r'\b(CL:\*([0-9]+))\b', flags=re.I), | |
| 78 r'%s/\2' % GERRIT_URLS['chrome'], r'\1', False), | |
| 79 | |
| 80 # Do this for everyone since "cbuildbot" is unique to CrOS. | |
| 81 # Otherwise, we'd do this in the bootstrap func below. | |
| 82 # Match the string: | |
| 83 # Automatic: "cbuildbot" on "x86-generic ASAN" from. | |
| 84 (re.compile(r'("cbuildbot" on "([^"]+ canary)")', | |
| 85 flags=re.I), | |
| 86 r'%s/builders/\2' % WATERFALL_URLS['chromeos'], r'\1', False), | |
| 87 (re.compile(r'("cbuildbot" on "([^"]+)")', | |
| 88 flags=re.I), | |
| 89 r'%s/builders/\2' % WATERFALL_URLS['chromiumos'], r'\1', False), | |
| 90 ] | |
| 91 | |
| 92 @classmethod | |
| 93 def bootstrap(cls, _app_name): | |
| 94 """Add conversions specific to |app_name| instance""" | |
| 95 pass | |
| 96 | |
| 97 @classmethod | |
| 98 def parse(cls, text): | |
| 99 """Creates a list of Link objects based on |text|""" | |
| 100 if not text: | |
| 101 return [] | |
| 102 for prog, target, pretty_text, is_email in cls._CONVERTS: | |
| 103 m = prog.search(text) | |
| 104 if m: | |
| 105 link = Link(m.expand(pretty_text), | |
| 106 target=m.expand(target), | |
| 107 is_email=is_email) | |
| 108 left_links = cls.parse(text[:m.start()].rstrip()) | |
| 109 right_links = cls.parse(text[m.end():].lstrip()) | |
| 110 return left_links + [link] + right_links | |
|
Vadim Sh.
2013/12/03 20:06:36
This looks nice and elegant :)
| |
| 111 return [Link(text)] | |
| 112 | |
| 113 def __init__(self, text): | |
| 114 self.raw_text = text | |
| 115 self.links = self.parse(text.strip()) | |
| 116 | |
| 117 def __str__(self): | |
| 118 return self.raw_text | |
| 119 | |
| 120 | |
| 25 class Status(db.Model): | 121 class Status(db.Model): |
| 26 """Description for the status table.""" | 122 """Description for the status table.""" |
| 27 # The username who added this status. | 123 # The username who added this status. |
| 28 username = db.StringProperty(required=True) | 124 username = db.StringProperty(required=True) |
| 29 # The date when the status got added. | 125 # The date when the status got added. |
| 30 date = db.DateTimeProperty(auto_now_add=True) | 126 date = db.DateTimeProperty(auto_now_add=True) |
| 31 # The message. It can contain html code. | 127 # The message. It can contain html code. |
| 32 message = db.StringProperty(required=True) | 128 message = db.StringProperty(required=True) |
| 33 | 129 |
| 130 def __init__(self, *args, **kwargs): | |
| 131 # Normalize newlines otherwise the DB store barfs. | |
| 132 kwargs['message'] = kwargs.get('message', '').replace('\n', '') | |
| 133 | |
| 134 super(Status, self).__init__(*args, **kwargs) | |
| 135 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:
| |
| 136 self.message_links = LinkableText(self.message) | |
| 137 | |
| 34 @property | 138 @property |
| 35 def general_state(self): | 139 def general_state(self): |
| 36 """Returns a string representing the state that the status message | 140 """Returns a string representing the state that the status message |
| 37 describes. | 141 describes. |
| 38 """ | 142 """ |
| 39 message = self.message | 143 message = self.message |
| 40 closed = re.search('close', message, re.IGNORECASE) | 144 closed = re.search('close', message, re.IGNORECASE) |
| 41 if closed and re.search('maint', message, re.IGNORECASE): | 145 if closed and re.search('maint', message, re.IGNORECASE): |
| 42 return 'maintenance' | 146 return 'maintenance' |
| 43 if re.search('throt', message, re.IGNORECASE): | 147 if re.search('throt', message, re.IGNORECASE): |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 289 return self._handle(error_message, last_message) | 393 return self._handle(error_message, last_message) |
| 290 else: | 394 else: |
| 291 put_status(Status(message=new_message, username=self.user.email())) | 395 put_status(Status(message=new_message, username=self.user.email())) |
| 292 self.redirect("/") | 396 self.redirect("/") |
| 293 | 397 |
| 294 | 398 |
| 295 def bootstrap(): | 399 def bootstrap(): |
| 296 # Guarantee that at least one instance exists. | 400 # Guarantee that at least one instance exists. |
| 297 if db.GqlQuery('SELECT __key__ FROM Status').get() is None: | 401 if db.GqlQuery('SELECT __key__ FROM Status').get() is None: |
| 298 Status(username='none', message='welcome to status').put() | 402 Status(username='none', message='welcome to status').put() |
| 403 LinkableText.bootstrap(BasePage.APP_NAME) | |
| OLD | NEW |