Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 41 | 41 |
| 42 or could look like: | 42 or could look like: |
| 43 | 43 |
| 44 (step_name, revision) | 44 (step_name, revision) |
| 45 """ | 45 """ |
| 46 potential_culprit_tuple_list = [] | 46 potential_culprit_tuple_list = [] |
| 47 | 47 |
| 48 # Iterates through the failures, tests, and suspected_cls, appending potential | 48 # Iterates through the failures, tests, and suspected_cls, appending potential |
| 49 # (step_name, test_name, revision) and (step_name, revision) culprit tuples to | 49 # (step_name, test_name, revision) and (step_name, revision) culprit tuples to |
| 50 # the list. | 50 # the list. |
| 51 | |
|
lijeffrey
2016/06/27 20:38:22
nit: delete this empty line
josiahk
2016/06/28 22:59:23
Done.
| |
| 51 for failure in analysis.result['failures']: | 52 for failure in analysis.result['failures']: |
| 52 if failure.get('tests'): | 53 if failure.get('tests'): |
| 53 for test in failure['tests']: | 54 for test in failure['tests']: |
| 54 for suspected_cl in test.get('suspected_cls', []): | 55 for suspected_cl in test.get('suspected_cls', []): |
| 55 potential_culprit_tuple_list.append(( | 56 potential_culprit_tuple_list.append(( |
| 56 failure['step_name'], | 57 failure['step_name'], |
| 57 test['test_name'], | 58 test['test_name'], |
| 58 suspected_cl['revision'])) | 59 suspected_cl['revision'])) |
| 59 else: | 60 else: |
| 60 for suspected_cl in failure['suspected_cls']: | 61 for suspected_cl in failure['suspected_cls']: |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 83 | 84 |
| 84 # Both analyses must have non-empty potential culprit lists. | 85 # Both analyses must have non-empty potential culprit lists. |
| 85 if not potential_culprit_tuple_list_1 or not potential_culprit_tuple_list_2: | 86 if not potential_culprit_tuple_list_1 or not potential_culprit_tuple_list_2: |
| 86 return False | 87 return False |
| 87 | 88 |
| 88 # Both analyses must have matching potential culprit lists. | 89 # Both analyses must have matching potential culprit lists. |
| 89 return (sorted(potential_culprit_tuple_list_1) == | 90 return (sorted(potential_culprit_tuple_list_1) == |
| 90 sorted(potential_culprit_tuple_list_2)) | 91 sorted(potential_culprit_tuple_list_2)) |
| 91 | 92 |
| 92 | 93 |
| 93 def _AppendTriageHistoryRecord(analysis, is_correct, user_name): | 94 def _AppendTriageHistoryRecord( |
| 95 analysis, is_correct, user_name, is_duplicate=False): | |
| 94 """Appends a triage history record to the given analysis. | 96 """Appends a triage history record to the given analysis. |
| 95 | 97 |
| 96 Args: | 98 Args: |
| 97 analysis: The analysis to which to append the history record. | 99 analysis: The analysis to which to append the history record. |
| 98 is_correct: True if the history record should indicate a correct judgement, | 100 is_correct: True if the history record should indicate a correct judgement, |
| 99 otherwise False. | 101 otherwise False. |
| 100 user_name: The user_name of the person to include in the triage record. | 102 user_name: The user_name of the person to include in the triage record. |
| 103 is_duplicate: True if the history record should indicate that this triage is | |
| 104 a duplicate, otherwise False. | |
| 101 """ | 105 """ |
| 102 if is_correct: | 106 if is_correct: |
| 103 if analysis.suspected_cls: | 107 if analysis.suspected_cls: |
| 104 analysis.result_status = result_status.FOUND_CORRECT | 108 if is_duplicate: |
| 109 analysis.result_status = result_status.FOUND_CORRECT_DUPLICATE | |
| 110 else: | |
| 111 analysis.result_status = result_status.FOUND_CORRECT | |
| 105 analysis.culprit_cls = analysis.suspected_cls | 112 analysis.culprit_cls = analysis.suspected_cls |
| 106 else: | 113 else: |
| 107 analysis.result_status = result_status.NOT_FOUND_CORRECT | 114 analysis.result_status = result_status.NOT_FOUND_CORRECT |
| 108 analysis.culprit_cls = None | 115 analysis.culprit_cls = None |
| 109 else: | 116 else: |
| 110 analysis.culprit_cls = None | 117 analysis.culprit_cls = None |
| 111 if analysis.suspected_cls: | 118 if analysis.suspected_cls: |
| 112 analysis.result_status = result_status.FOUND_INCORRECT | 119 if is_duplicate: |
| 120 analysis.result_status = result_status.FOUND_INCORRECT_DUPLICATE | |
| 121 else: | |
| 122 analysis.result_status = result_status.FOUND_INCORRECT | |
| 113 else: | 123 else: |
| 114 analysis.result_status = result_status.NOT_FOUND_INCORRECT | 124 analysis.result_status = result_status.NOT_FOUND_INCORRECT |
| 115 | 125 |
| 116 triage_record = { | 126 triage_record = { |
| 117 'triage_timestamp': calendar.timegm(datetime.utcnow().timetuple()), | 127 'triage_timestamp': calendar.timegm(datetime.utcnow().timetuple()), |
| 118 'user_name': user_name, | 128 'user_name': user_name, |
| 119 'result_status': analysis.result_status, | 129 'result_status': analysis.result_status, |
| 120 'version': analysis.version, | 130 'version': analysis.version, |
| 121 } | 131 } |
| 122 if not analysis.triage_history: | 132 if not analysis.triage_history: |
| 123 analysis.triage_history = [] | 133 analysis.triage_history = [] |
| 124 analysis.triage_history.append(triage_record) | 134 analysis.triage_history.append(triage_record) |
| 125 | 135 |
| 126 analysis.put() | 136 analysis.put() |
| 127 | 137 |
| 128 | 138 |
| 129 @ndb.transactional | 139 @ndb.transactional |
| 130 def _UpdateAnalysisResultStatus( | 140 def _UpdateAnalysisResultStatus( |
| 131 master_name, builder_name, build_number, is_correct, user_name=None): | 141 master_name, builder_name, build_number, is_correct, user_name=None): |
| 132 analysis = WfAnalysis.Get(master_name, builder_name, build_number) | 142 analysis = WfAnalysis.Get(master_name, builder_name, build_number) |
| 133 if not analysis or not analysis.completed: | 143 if not analysis or not analysis.completed: |
| 134 return False, None | 144 return False, None |
| 135 | 145 |
| 146 analysis.triage_reference_analysis_master_name = None | |
|
lijeffrey
2016/06/27 20:38:22
so what are these for, and why are we setting them
chanli
2016/06/27 22:27:56
+1
josiahk
2016/06/28 22:59:23
When another "first-cause" build analysis is triag
lijeffrey
2016/06/29 00:48:23
you don't need to initialize them to None, if they
josiahk
2016/06/29 22:40:00
Yes, I want to potentially overwrite previously se
lijeffrey
2016/06/30 01:09:46
It appears with what you have triage duplicate res
josiahk
2016/06/30 22:34:56
_UpdateAnalysisResultStatus() operates on a single
| |
| 147 analysis.triage_reference_analysis_builder_name = None | |
| 148 analysis.triage_reference_analysis_build_number = None | |
| 149 | |
| 136 _AppendTriageHistoryRecord(analysis, is_correct, user_name) | 150 _AppendTriageHistoryRecord(analysis, is_correct, user_name) |
| 137 | 151 |
| 138 return True, analysis | 152 return True, analysis |
| 139 | 153 |
| 140 | 154 |
| 141 def _GetDuplicateAnalyses(original_analysis): | 155 def _GetDuplicateAnalyses(original_analysis): |
| 142 start_time = (original_analysis.build_start_time - | 156 start_time = (original_analysis.build_start_time - |
| 143 timedelta(hours=MATCHING_ANALYSIS_HOURS_AGO_START)) | 157 timedelta(hours=MATCHING_ANALYSIS_HOURS_AGO_START)) |
| 144 end_time = (original_analysis.build_start_time + | 158 end_time = (original_analysis.build_start_time + |
| 145 timedelta(hours=MATCHING_ANALYSIS_HOURS_AGO_END)) | 159 timedelta(hours=MATCHING_ANALYSIS_HOURS_AGO_END)) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 168 | 182 |
| 169 # Retrieve potential duplicate build analyses. | 183 # Retrieve potential duplicate build analyses. |
| 170 analysis_results = WfAnalysis.query(ndb.AND( | 184 analysis_results = WfAnalysis.query(ndb.AND( |
| 171 WfAnalysis.build_start_time >= start_time, | 185 WfAnalysis.build_start_time >= start_time, |
| 172 WfAnalysis.build_start_time <= end_time, | 186 WfAnalysis.build_start_time <= end_time, |
| 173 WfAnalysis.result_status == result_status.FOUND_UNTRIAGED | 187 WfAnalysis.result_status == result_status.FOUND_UNTRIAGED |
| 174 )).fetch() | 188 )).fetch() |
| 175 | 189 |
| 176 # Further filter potential duplicates and return them. | 190 # Further filter potential duplicates and return them. |
| 177 return [analysis for analysis in analysis_results if | 191 return [analysis for analysis in analysis_results if |
| 192 analysis.completed and | |
| 193 analysis.result and | |
| 178 _DoAnalysesMatch(original_analysis, analysis) and | 194 _DoAnalysesMatch(original_analysis, analysis) and |
| 179 original_analysis.key is not analysis.key and | 195 original_analysis.key is not analysis.key] |
| 180 analysis.completed] | |
| 181 | 196 |
| 182 | 197 |
| 183 def _TriageDuplicateResults(original_analysis, is_correct, user_name=None): | 198 def _TriageDuplicateResults(original_analysis, is_correct, user_name=None): |
| 184 matching_analyses = _GetDuplicateAnalyses(original_analysis) | 199 matching_analyses = _GetDuplicateAnalyses(original_analysis) |
| 185 | 200 |
| 186 for analysis in matching_analyses: | 201 for analysis in matching_analyses: |
| 187 _AppendTriageHistoryRecord(analysis, is_correct, user_name) | 202 analysis.triage_reference_analysis_master_name = ( |
| 203 original_analysis.master_name) | |
| 204 analysis.triage_reference_analysis_builder_name = ( | |
| 205 original_analysis.builder_name) | |
| 206 analysis.triage_reference_analysis_build_number = ( | |
| 207 original_analysis.build_number) | |
| 208 _AppendTriageHistoryRecord(analysis, is_correct, user_name, | |
| 209 is_duplicate=True) | |
| 210 | |
| 211 return len(matching_analyses) | |
|
lijeffrey
2016/06/27 20:38:22
It looks like you're returning the number of match
josiahk
2016/06/28 22:59:23
Renamed to _TriageAndCountDuplicateResults()
| |
| 188 | 212 |
| 189 | 213 |
| 190 class TriageAnalysis(BaseHandler): | 214 class TriageAnalysis(BaseHandler): |
| 191 PERMISSION_LEVEL = Permission.CORP_USER | 215 PERMISSION_LEVEL = Permission.CORP_USER |
| 192 | 216 |
| 193 def HandleGet(self): # pragma: no cover | 217 def HandleGet(self): # pragma: no cover |
| 194 return self.HandlePost() | 218 return self.HandlePost() |
| 195 | 219 |
| 196 def HandlePost(self): | 220 def HandlePost(self): |
| 197 """Sets the manual triage result for the analysis. | 221 """Sets the manual triage result for the analysis. |
| 198 | 222 |
| 199 Mark the analysis result as correct/wrong/etc. | 223 Mark the analysis result as correct/wrong/etc. |
| 200 TODO: make it possible to set the real culprit CLs. | 224 TODO: make it possible to set the real culprit CLs. |
| 201 """ | 225 """ |
| 202 url = self.request.get('url').strip() | 226 url = self.request.get('url').strip() |
| 203 build_info = buildbot.ParseBuildUrl(url) | 227 build_info = buildbot.ParseBuildUrl(url) |
| 204 if not build_info: | 228 if not build_info: |
| 205 return {'data': {'success': False}} | 229 return {'data': {'success': False}} |
| 206 master_name, builder_name, build_number = build_info | 230 master_name, builder_name, build_number = build_info |
| 207 | 231 |
| 208 is_correct = self.request.get('correct').lower() == 'true' | 232 is_correct = self.request.get('correct').lower() == 'true' |
| 209 # As the permission level is CORP_USER, we could assume the current user | 233 # As the permission level is CORP_USER, we could assume the current user |
| 210 # already logged in. | 234 # already logged in. |
| 211 user_name = users.get_current_user().email().split('@')[0] | 235 user_name = users.get_current_user().email().split('@')[0] |
| 212 success, original_analysis = _UpdateAnalysisResultStatus( | 236 success, original_analysis = _UpdateAnalysisResultStatus( |
| 213 master_name, builder_name, build_number, is_correct, user_name) | 237 master_name, builder_name, build_number, is_correct, user_name) |
| 238 num_duplicate_analyses = 0 | |
| 214 if success: | 239 if success: |
| 215 _TriageDuplicateResults(original_analysis, is_correct, user_name) | 240 num_duplicate_analyses = _TriageDuplicateResults( |
| 216 return {'data': {'success': success}} | 241 original_analysis, is_correct, user_name) |
| 242 return {'data': {'success': success, | |
| 243 'num_duplicate_analyses': num_duplicate_analyses}} | |
| OLD | NEW |