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

Side by Side 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 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | stylesheets/style.css » ('j') | templates/main.html » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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)
OLDNEW
« 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