OLD | NEW |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Testable functions for Antibody.""" | 5 """Testable functions for Antibody.""" |
6 | 6 |
7 import jinja2 | 7 import jinja2 |
8 import json | 8 import json |
9 import logging | 9 import logging |
10 import os | 10 import os |
11 import shutil | 11 import shutil |
12 import time | 12 import time |
13 | 13 |
14 import infra.tools.antibody.cloudsql_connect as csql | 14 import infra.tools.antibody.cloudsql_connect as csql |
| 15 from infra.tools.antibody import compute_stats |
15 | 16 |
16 THIS_DIR = os.path.dirname(os.path.realpath(__file__)) | 17 THIS_DIR = os.path.dirname(os.path.realpath(__file__)) |
17 ANTIBODY_UI_MAIN_NAME = 'index.html' | 18 ANTIBODY_UI_MAIN_NAME = 'index.html' |
18 TBR_BY_USER_NAME = 'tbr_by_user.html' | 19 TBR_BY_USER_NAME = 'tbr_by_user.html' |
| 20 STATS_NAME = 'stats.html' |
| 21 LEADERBOARD_NAME = 'leaderboard.html' |
19 | 22 |
20 # https://storage.googleapis.com/chromium-infra-docs/infra/html/logging.html | 23 # https://storage.googleapis.com/chromium-infra-docs/infra/html/logging.html |
21 LOGGER = logging.getLogger(__name__) | 24 LOGGER = logging.getLogger(__name__) |
22 | 25 |
23 | 26 |
24 def add_argparse_options(parser): | 27 def add_argparse_options(parser): |
25 """Define command-line arguments.""" | 28 """Define command-line arguments.""" |
26 parser.add_argument('--cache-path', '-c', help="path to the rietveld cache") | 29 parser.add_argument('--cache-path', '-c', help="path to the rietveld cache") |
27 parser.add_argument('--git-checkout-path', '-g', required=True, | 30 parser.add_argument('--git-checkout-path', '-g', required=True, |
28 help="path to the git checkout") | 31 help="path to the git checkout") |
(...skipping 15 matching lines...) Expand all Loading... |
44 "format as YYYY-MM-DD") | 47 "format as YYYY-MM-DD") |
45 | 48 |
46 | 49 |
47 def setup_antibody_db(cc, filename): # pragma: no cover | 50 def setup_antibody_db(cc, filename): # pragma: no cover |
48 csql.execute_sql_script_from_file(cc, filename) | 51 csql.execute_sql_script_from_file(cc, filename) |
49 | 52 |
50 | 53 |
51 def generate_antibody_ui(suspicious_commits_data, gitiles_prefix, ui_dirpath): | 54 def generate_antibody_ui(suspicious_commits_data, gitiles_prefix, ui_dirpath): |
52 template_loader = jinja2.FileSystemLoader(os.path.join(THIS_DIR, 'templates')) | 55 template_loader = jinja2.FileSystemLoader(os.path.join(THIS_DIR, 'templates')) |
53 template_env = jinja2.Environment(loader=template_loader) | 56 template_env = jinja2.Environment(loader=template_loader) |
54 index_template = template_env.get_template('antibody_ui_all.jinja') | 57 template_vars_all = { |
55 tbr_by_user_template = template_env.get_template('tbr_by_user.jinja') | |
56 | |
57 template_vars = { | |
58 'title' : 'Potentially Suspicious Commits', | 58 'title' : 'Potentially Suspicious Commits', |
59 'description' : 'List of commits with a TBR but no lgtm', | 59 'description' : 'List of commits with a TBR but no lgtm', |
60 'antibody_main_link' : ANTIBODY_UI_MAIN_NAME, | 60 'antibody_main_link' : ANTIBODY_UI_MAIN_NAME, |
61 'tbr_by_user_link' : TBR_BY_USER_NAME, | 61 'tbr_by_user_link' : TBR_BY_USER_NAME, |
| 62 'stats_link' : STATS_NAME, |
| 63 'leaderboard_link' : LEADERBOARD_NAME, |
62 'generation_time' : time.strftime("%a, %d %b %Y %H:%M:%S", | 64 'generation_time' : time.strftime("%a, %d %b %Y %H:%M:%S", |
63 time.gmtime()), | 65 time.gmtime()), |
64 'page_header_text' : "All Potentially Suspicious Commits", | 66 'page_header_text' : "Antibody", |
65 'to_be_reviewed' : "To be reviewed by user", | 67 'to_be_reviewed' : "TBR by user", |
66 'num_tbr_no_lgtm': len(suspicious_commits_data), | 68 'stats' : 'Stats', |
67 'num_no_review_url': 42, | 69 'leaderboard' : 'Leaderboard', |
68 'blank_TBR': 3, | |
69 'table_headers' : ['git_hash', 'rietveld_url', | |
70 'request_timestamp'], | |
71 'suspicious_commits' : suspicious_commits_data, | 70 'suspicious_commits' : suspicious_commits_data, |
72 'gitiles_prefix' : gitiles_prefix, | 71 'gitiles_prefix' : gitiles_prefix, |
73 } | 72 } |
74 with open(os.path.join(ui_dirpath, ANTIBODY_UI_MAIN_NAME), 'wb') as f: | |
75 f.write(index_template.render(template_vars)) | |
76 | |
77 with open(os.path.join(ui_dirpath, TBR_BY_USER_NAME), 'wb') as f: | |
78 f.write(tbr_by_user_template.render(template_vars)) | |
79 | |
80 try: # pragma: no cover | 73 try: # pragma: no cover |
81 if (ui_dirpath != THIS_DIR): # pragma: no cover | 74 if (ui_dirpath != THIS_DIR): # pragma: no cover |
82 shutil.rmtree(os.path.join(ui_dirpath, 'static')) | 75 shutil.rmtree(os.path.join(ui_dirpath, 'static')) |
83 except OSError, e: # pragma: no cover | 76 except OSError, e: # pragma: no cover |
84 if e.errno == 2: # [Errno 2] No such file or directory | 77 if e.errno == 2: # [Errno 2] No such file or directory |
85 pass | 78 pass |
86 else: | 79 else: |
87 raise | 80 raise |
88 if (ui_dirpath != THIS_DIR): # pragma: no cover | 81 if (ui_dirpath != THIS_DIR): # pragma: no cover |
89 shutil.copytree(os.path.join(THIS_DIR, 'static'), | 82 shutil.copytree(os.path.join(THIS_DIR, 'static'), |
90 os.path.join(ui_dirpath, 'static')) | 83 os.path.join(ui_dirpath, 'static')) |
91 | 84 |
| 85 file_generators = [generate_homepage, generate_tbr_page, generate_stats_page, |
| 86 generate_leaderboard_page] |
| 87 for item in file_generators: |
| 88 item(template_env, template_vars_all, ui_dirpath) |
| 89 |
| 90 |
| 91 def generate_homepage(template_env, template_vars_all, ui_dirpath): |
| 92 index_template = template_env.get_template('antibody_ui_all.jinja') |
| 93 with open(os.path.join(ui_dirpath, 'all_monthly_stats.json')) as f: |
| 94 data = json.load(f) |
| 95 stats_7_day = data['7_days'] |
| 96 template_vars = { |
| 97 'num_tbr_no_lgtm': stats_7_day['tbr_no_lgtm'], |
| 98 'num_no_review_url': stats_7_day['no_review_url'], |
| 99 'blank_TBR': stats_7_day['blank_tbr'], |
| 100 'table_headers' : ['Git Commit Subject', 'Review URL', |
| 101 'Request Timestamp']} |
| 102 template_vars.update(template_vars_all) |
| 103 with open(os.path.join(ui_dirpath, ANTIBODY_UI_MAIN_NAME), 'wb') as f: |
| 104 f.write(index_template.render(template_vars)) |
| 105 |
| 106 |
| 107 def generate_tbr_page(template_env, template_vars_all, ui_dirpath): |
| 108 tbr_by_user_template = template_env.get_template('tbr_by_user.jinja') |
| 109 template_vars = { |
| 110 } |
| 111 template_vars.update(template_vars_all) |
| 112 with open(os.path.join(ui_dirpath, TBR_BY_USER_NAME), 'wb') as f: |
| 113 f.write(tbr_by_user_template.render(template_vars)) |
| 114 |
| 115 |
| 116 def generate_stats_page(template_env, template_vars_all, ui_dirpath): |
| 117 stats_template = template_env.get_template('stats.jinja') |
| 118 with open(os.path.join(ui_dirpath, 'all_monthly_stats.json')) as f: |
| 119 data = json.load(f) |
| 120 template_vars = {} |
| 121 stats_all = [ |
| 122 [data['7_days'], 'stats_7_day'], |
| 123 [data['30_days'], 'stats_30_day'], |
| 124 [data['all_time'], 'stats_all_time'], |
| 125 ] |
| 126 categories_keys = [ |
| 127 ['"Suspicious":Total Commits', 'suspicious_to_total_ratio'], |
| 128 ['Total Commits', 'total_commits'], |
| 129 ['TBR without LGTM', 'tbr_no_lgtm'], |
| 130 ['Without review url', 'no_review_url'], |
| 131 ['Blank TBR', 'blank_tbr'], |
| 132 ] |
| 133 for stats, key in stats_all: |
| 134 template_vars[key] = [[x[0], stats[x[1]]] for x in categories_keys] |
| 135 template_vars.update(template_vars_all) |
| 136 with open(os.path.join(ui_dirpath, STATS_NAME), 'wb') as f: |
| 137 f.write(stats_template.render(template_vars)) |
| 138 |
| 139 |
| 140 def generate_leaderboard_page(template_env, template_vars_all, ui_dirpath): |
| 141 leaderboard_template = template_env.get_template('leaderboard.jinja') |
| 142 template_vars = { |
| 143 } |
| 144 template_vars.update(template_vars_all) |
| 145 with open(os.path.join(ui_dirpath, LEADERBOARD_NAME), 'wb') as f: |
| 146 f.write(leaderboard_template.render(template_vars)) |
| 147 |
92 | 148 |
93 def get_tbr_by_user(tbr_no_lgtm, gitiles_prefix, output_dirpath): | 149 def get_tbr_by_user(tbr_no_lgtm, gitiles_prefix, output_dirpath): |
94 # tbr_no_lgtm: review_url, request_timestamp, hash, people_email_address | 150 # tbr_no_lgtm: review_url, request_timestamp, subject, people_email_address, |
| 151 # hash |
95 tbr_blame_dict = {} | 152 tbr_blame_dict = {} |
96 for url, timestamp, git_hash, reviewer in tbr_no_lgtm: | 153 for url, timestamp, subject, reviewer, git_hash in tbr_no_lgtm: |
97 reviewer = reviewer.strip().split('@') | 154 # timestamp = timestamp.strftime("%Y-%m-%d %H:%M:%S") |
98 timestamp = timestamp.strftime("%Y-%m-%d %H:%M:%S") | 155 tbr_blame_dict.setdefault(reviewer, []).append( |
99 tbr_blame_dict.setdefault(reviewer[0], []).append( | 156 [subject, url, timestamp, git_hash]) |
100 [git_hash, url, timestamp]) | |
101 tbr_data = { | 157 tbr_data = { |
102 "by_user" : tbr_blame_dict, | 158 "by_user" : tbr_blame_dict, |
103 "gitiles_prefix" : gitiles_prefix, | 159 "gitiles_prefix" : gitiles_prefix, |
104 } | 160 } |
105 with open(os.path.join(output_dirpath, 'tbr_by_user.json'), 'wb') as f: | 161 with open(os.path.join(output_dirpath, 'tbr_by_user.json'), 'wb') as f: |
106 f.write(json.dumps(tbr_data)) | 162 f.write(json.dumps(tbr_data)) |
107 | 163 |
108 | 164 |
| 165 def generate_stats_files(cc, output_dirpath): # pragma: no cover |
| 166 compute_stats.all_time_leaderboard(cc, |
| 167 os.path.join(output_dirpath, 'all_time_leaderboard.json')) |
| 168 compute_stats.past_month_leaderboard(cc, |
| 169 os.path.join(output_dirpath, 'past_month_leaderboard.json')) |
| 170 compute_stats.all_monthly_stats(cc, |
| 171 os.path.join(output_dirpath, 'all_monthly_stats.json')) |
| 172 |
| 173 |
109 def get_gitiles_prefix(git_checkout_path): | 174 def get_gitiles_prefix(git_checkout_path): |
110 with open(os.path.join(git_checkout_path, 'codereview.settings'), 'r') as f: | 175 with open(os.path.join(git_checkout_path, 'codereview.settings'), 'r') as f: |
111 lines = f.readlines() | 176 lines = f.readlines() |
112 for line in lines: | 177 for line in lines: |
113 if line.startswith('VIEW_VC:'): | 178 if line.startswith('VIEW_VC:'): |
114 return line[len('VIEW_VC:'):].strip() | 179 return line[len('VIEW_VC:'):].strip() |
115 # TODO (ksho): implement more sophisticated solution if codereview.settings | 180 # TODO (ksho): implement more sophisticated solution if codereview.settings |
116 # does not contain VIEW_VC | 181 # does not contain VIEW_VC |
117 return None | 182 return None |
OLD | NEW |