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 |