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

Side by Side Diff: appengine/findit/handlers/triage_analysis.py

Issue 2029873002: [Findit] Cross-platform triage (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Test and style corrections Created 4 years, 6 months 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
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 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 """This module is to handle manual triage of analysis result. 5 """This module is to handle manual triage of analysis result.
6 6
7 This handler will flag the analysis result as correct or incorrect. 7 This handler will flag the analysis result as correct or incorrect.
8 TODO: work on an automatic or semi-automatic way to triage analysis result. 8 TODO: work on an automatic or semi-automatic way to triage analysis result.
9 """ 9 """
10 10
11 import calendar 11 import calendar
12 from datetime import datetime 12 from datetime import datetime
13 from datetime import timedelta
chanli 2016/06/13 20:21:04 nit: need an empty line below.
josiahk 2016/06/21 00:56:54 Done.
14 from common.base_handler import BaseHandler
15 from common.base_handler import Permission
16 from model import result_status
17 from model.wf_analysis import WfAnalysis
18 from waterfall import buildbot
chanli 2016/06/13 20:21:04 Nit: move ln 14 - 18 under ln 22. And also leave 2
josiahk 2016/06/21 00:56:54 Done.
13 19
14 from google.appengine.api import users 20 from google.appengine.api import users
15 from google.appengine.ext import ndb 21 from google.appengine.ext import ndb
stgao 2016/06/13 23:39:49 for imports, there is an order for different group
josiahk 2016/06/21 00:56:53 Thanks!
16 22
17 from common.base_handler import BaseHandler 23
18 from common.base_handler import Permission 24 MATCHING_ANALYSIS_DAYS_AGO_START = 1
19 from model.wf_analysis import WfAnalysis 25 MATCHING_ANALYSIS_DAYS_AGO_END = 1
20 from model import result_status
21 from waterfall import buildbot
22 26
23 27
24 @ndb.transactional 28 def _GenPotentialCulpritTupleList(analysis):
25 def _UpdateAnalysisResultStatus( 29 """Generates a list of potential culprit tuples.
26 master_name, builder_name, build_number, correct, user_name=None): 30
27 analysis = WfAnalysis.Get(master_name, builder_name, build_number) 31 Args:
28 if not analysis or not analysis.completed: 32 analysis: the analysis from which to generate a potenial culript tuple list.
33
34 Returns:
35 A list of cultprit tuples that each could look like:
36
37 (step_name, test_name, revision)
38
39 or could look like:
40
41 (step_name, revision)
42 """
43 potential_culprit_tuple_list = []
44
45 # Iterates through the failures, tests, and suspected_cls, appending potential
46 # (step_name, test_name, revision) and (step_name, revision) culprit tuples to
47 # the list.
48 for failure in analysis.result['failures']:
49 if failure.get('tests'):
50 for test in failure['tests']:
51 for suspected_cl in test.get('suspected_cls', []):
52 potential_culprit_tuple_list.append((
53 failure['step_name'],
54 test['test_name'],
55 suspected_cl['revision']))
56 else:
57 for suspected_cl in failure['suspected_cls']:
58 potential_culprit_tuple_list.append((
59 failure['step_name'],
60 suspected_cl['revision']))
61
62 return potential_culprit_tuple_list
63
64
65 def _DoAnalysesMatch(analysis_1, analysis_2):
66 """Checks if two analyses match.
67
68 Args:
69 analysis_1: The first analysis to compare
70 analysis_2: The second analysis to compare
71
72 Returns:
73 True if the two analyses' sorted potential culprit lists match, otherwise
74 False.
75 """
76
77 # Get list of potential culprit tuples
78 potential_culprit_tuple_list_1 = _GenPotentialCulpritTupleList(analysis_1)
79 potential_culprit_tuple_list_2 = _GenPotentialCulpritTupleList(analysis_2)
80
81 # Both analyses must have non-empty potential culprit lists
82 if not potential_culprit_tuple_list_1 or not potential_culprit_tuple_list_2:
29 return False 83 return False
30 84
85 # Both analyses must have matching potential culprit lists
86 if (sorted(potential_culprit_tuple_list_1) !=
lijeffrey 2016/06/13 18:15:06 you can just do return sorted(potential_culprit_t
josiahk 2016/06/21 00:56:54 Done. Thanks!
87 sorted(potential_culprit_tuple_list_2)):
88 return False
89
90 return True
91
92
93 def _AppendTriageHistoryRecord(analysis, correct, user_name):
94 """Appends a triage history record to the given analysis.
95
96 Args:
97 analysis: The analysis to which to append the history record
lijeffrey 2016/06/13 18:15:06 . after sentences.
josiahk 2016/06/21 00:56:54 Done.
98 correct: True if the history record should indicate a correct judgement,
99 otherwise False.
100 user_name: The user_name of the person to include in the triage record
101 """
31 if correct: 102 if correct:
32 if analysis.suspected_cls: 103 if analysis.suspected_cls:
33 analysis.result_status = result_status.FOUND_CORRECT 104 analysis.result_status = result_status.FOUND_CORRECT
34 analysis.culprit_cls = analysis.suspected_cls 105 analysis.culprit_cls = analysis.suspected_cls
35 else: 106 else:
36 analysis.result_status = result_status.NOT_FOUND_CORRECT 107 analysis.result_status = result_status.NOT_FOUND_CORRECT
37 analysis.culprit_cls = None 108 analysis.culprit_cls = None
38 else: 109 else:
39 analysis.culprit_cls = None 110 analysis.culprit_cls = None
40 if analysis.suspected_cls: 111 if analysis.suspected_cls:
41 analysis.result_status = result_status.FOUND_INCORRECT 112 analysis.result_status = result_status.FOUND_INCORRECT
42 else: 113 else:
43 analysis.result_status = result_status.NOT_FOUND_INCORRECT 114 analysis.result_status = result_status.NOT_FOUND_INCORRECT
44 115
45 triage_record = { 116 triage_record = {
46 'triage_timestamp': calendar.timegm(datetime.utcnow().timetuple()), 117 'triage_timestamp': calendar.timegm(datetime.utcnow().timetuple()),
47 'user_name': user_name, 118 'user_name': user_name,
48 'result_status': analysis.result_status, 119 'result_status': analysis.result_status,
49 'version': analysis.version, 120 'version': analysis.version,
50 } 121 }
51 if not analysis.triage_history: 122 if not analysis.triage_history:
52 analysis.triage_history = [] 123 analysis.triage_history = []
53 analysis.triage_history.append(triage_record) 124 analysis.triage_history.append(triage_record)
54 125
55 analysis.put() 126 analysis.put()
127
128
129 @ndb.transactional
130 def _UpdateAnalysisResultStatus(
131 master_name, builder_name, build_number, correct, user_name=None):
132 analysis = WfAnalysis.Get(master_name, builder_name, build_number)
133 if not analysis or not analysis.completed:
134 return False
135
136 _AppendTriageHistoryRecord(analysis, correct, user_name)
137
138 return True, analysis
139
140 # TODO: Add @ndb.transactional?
lijeffrey 2016/06/13 18:15:06 do some research as to what ndb.transactional mean
141 def _TriageDuplicateResults(original_analysis, correct, user_name=None):
142 # QUESTION: Instead of looking for analyses with start_dates relative to
lijeffrey 2016/06/13 18:15:06 when you find out the answer take this comment out
chanli 2016/06/13 20:21:04 I think your idea makes sense, but how relative wo
josiahk 2016/06/21 00:56:54 Thanks! I have taken the comment out. I also talke
143 # today, should we be looking for analyses with start_dates relative to the
144 # date of the original analysis?
stgao 2016/06/13 23:39:49 Should it be relative to the wf_analysis.WfAnalysi
josiahk 2016/06/21 00:56:54 Yes! Thank you!
145
146 start_date = datetime.utcnow() - timedelta(MATCHING_ANALYSIS_DAYS_AGO_START)
147 start_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0)
148 end_date = datetime.utcnow() - timedelta(MATCHING_ANALYSIS_DAYS_AGO_END - 1)
chanli 2016/06/13 20:21:03 Why didn't make MATCHING_ANALYSIS_DAYS_AGO_END = 0
josiahk 2016/06/21 00:56:53 Weird code removed. Underlying logic changed.
149 end_date = end_date.replace(hour=0, minute=0, second=0, microsecond=0)
150
151 analysis_results = WfAnalysis.query(ndb.AND(
152 WfAnalysis.build_start_time >= start_date,
153 WfAnalysis.build_start_time < end_date,
154 WfAnalysis.result_status == result_status.FOUND_UNTRIAGED
155 )).fetch()
156
157 # Generate list of matching build analyses
158 matching_analyses = [a for a in analysis_results if
159 _DoAnalysesMatch(original_analysis, a) and
160 original_analysis.key is not a.key]
161
162 for a in matching_analyses:
lijeffrey 2016/06/13 18:15:06 nit: use meaningful variable names
josiahk 2016/06/21 00:56:54 Done.
163 _AppendTriageHistoryRecord(a, correct, user_name)
164
165 # QUESTION: Should this function ever return anything but True?
lijeffrey 2016/06/13 18:15:06 see lines 133-134
josiahk 2016/06/21 00:56:54 Thanks!
56 return True 166 return True
57 167
58 168
59 class TriageAnalysis(BaseHandler): 169 class TriageAnalysis(BaseHandler):
60 PERMISSION_LEVEL = Permission.CORP_USER 170 PERMISSION_LEVEL = Permission.CORP_USER
61 171
62 def HandleGet(self): # pragma: no cover 172 def HandleGet(self): # pragma: no cover
63 return self.HandlePost() 173 return self.HandlePost()
64 174
65 def HandlePost(self): 175 def HandlePost(self):
66 """Sets the manual triage result for the analysis. 176 """Sets the manual triage result for the analysis.
67 177
68 Mark the analysis result as correct/wrong/etc. 178 Mark the analysis result as correct/wrong/etc.
69 TODO: make it possible to set the real culprit CLs. 179 TODO: make it possible to set the real culprit CLs.
70 """ 180 """
71 url = self.request.get('url').strip() 181 url = self.request.get('url').strip()
72 build_info = buildbot.ParseBuildUrl(url) 182 build_info = buildbot.ParseBuildUrl(url)
73 if not build_info: 183 if not build_info:
74 return {'data': {'success': False}} 184 return {'data': {'success': False}}
75 master_name, builder_name, build_number = build_info 185 master_name, builder_name, build_number = build_info
76 186
77 correct = self.request.get('correct').lower() == 'true' 187 correct = self.request.get('correct').lower() == 'true'
78 # As the permission level is CORP_USER, we could assume the current user 188 # As the permission level is CORP_USER, we could assume the current user
79 # already logged in. 189 # already logged in.
80 user_name = users.get_current_user().email().split('@')[0] 190 user_name = users.get_current_user().email().split('@')[0]
81 success = _UpdateAnalysisResultStatus( 191 success_and_maybe_analysis = _UpdateAnalysisResultStatus(
82 master_name, builder_name, build_number, correct, user_name) 192 master_name, builder_name, build_number, correct, user_name)
193 success = not not success_and_maybe_analysis and _TriageDuplicateResults(
lijeffrey 2016/06/13 18:15:06 not not shouldn't be necessary
chanli 2016/06/13 20:21:04 Based your current change, how could you display t
josiahk 2016/06/21 00:56:54 Done.
josiahk 2016/06/21 00:56:54 The original analysis link information will hopefu
194 success_and_maybe_analysis[1], correct, user_name)
83 return {'data': {'success': success}} 195 return {'data': {'success': success}}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698