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 28 matching lines...) Expand all Loading... | |
| 39 | 39 |
| 40 or could look like: | 40 or could look like: |
| 41 | 41 |
| 42 (step_name, revision) | 42 (step_name, revision) |
| 43 """ | 43 """ |
| 44 potential_culprit_tuple_list = [] | 44 potential_culprit_tuple_list = [] |
| 45 | 45 |
| 46 # Iterates through the failures, tests, and suspected_cls, appending potential | 46 # Iterates through the failures, tests, and suspected_cls, appending potential |
| 47 # (step_name, test_name, revision) and (step_name, revision) culprit tuples to | 47 # (step_name, test_name, revision) and (step_name, revision) culprit tuples to |
| 48 # the list. | 48 # the list. |
| 49 | |
| 49 for failure in analysis.result['failures']: | 50 for failure in analysis.result['failures']: |
| 50 if failure.get('tests'): | 51 if failure.get('tests'): |
| 51 for test in failure['tests']: | 52 for test in failure['tests']: |
| 52 for suspected_cl in test.get('suspected_cls', []): | 53 for suspected_cl in test.get('suspected_cls', []): |
| 53 potential_culprit_tuple_list.append(( | 54 potential_culprit_tuple_list.append(( |
| 54 failure['step_name'], | 55 failure['step_name'], |
| 55 test['test_name'], | 56 test['test_name'], |
| 56 suspected_cl['revision'])) | 57 suspected_cl['revision'])) |
| 57 else: | 58 else: |
| 58 for suspected_cl in failure['suspected_cls']: | 59 for suspected_cl in failure['suspected_cls']: |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 81 | 82 |
| 82 # Both analyses must have non-empty potential culprit lists. | 83 # Both analyses must have non-empty potential culprit lists. |
| 83 if not potential_culprit_tuple_list_1 or not potential_culprit_tuple_list_2: | 84 if not potential_culprit_tuple_list_1 or not potential_culprit_tuple_list_2: |
| 84 return False | 85 return False |
| 85 | 86 |
| 86 # Both analyses must have matching potential culprit lists. | 87 # Both analyses must have matching potential culprit lists. |
| 87 return (sorted(potential_culprit_tuple_list_1) == | 88 return (sorted(potential_culprit_tuple_list_1) == |
| 88 sorted(potential_culprit_tuple_list_2)) | 89 sorted(potential_culprit_tuple_list_2)) |
| 89 | 90 |
| 90 | 91 |
| 91 def _AppendTriageHistoryRecord(analysis, is_correct, user_name): | 92 def _AppendTriageHistoryRecord( |
| 93 analysis, is_correct, user_name, duplicate=False): | |
| 92 """Appends a triage history record to the given analysis. | 94 """Appends a triage history record to the given analysis. |
| 93 | 95 |
| 94 Args: | 96 Args: |
| 95 analysis: The analysis to which to append the history record. | 97 analysis: The analysis to which to append the history record. |
| 96 correct: True if the history record should indicate a correct judgement, | 98 correct: True if the history record should indicate a correct judgement, |
|
chanli
2016/06/24 18:24:20
Did you rebase your patch? I remembered you have c
| |
| 97 otherwise False. | 99 otherwise False. |
| 98 user_name: The user_name of the person to include in the triage record. | 100 user_name: The user_name of the person to include in the triage record. |
|
chanli
2016/06/24 18:24:20
Add description of 'duplicate' here
josiahk
2016/06/27 19:33:25
Done.
| |
| 99 """ | 101 """ |
| 100 if is_correct: | 102 if is_correct: |
| 101 if analysis.suspected_cls: | 103 if analysis.suspected_cls: |
| 102 analysis.result_status = result_status.FOUND_CORRECT | 104 if duplicate: |
| 105 analysis.result_status = result_status.FOUND_CORRECT_DUPLICATE | |
| 106 else: | |
| 107 analysis.result_status = result_status.FOUND_CORRECT | |
| 103 analysis.culprit_cls = analysis.suspected_cls | 108 analysis.culprit_cls = analysis.suspected_cls |
| 104 else: | 109 else: |
| 105 analysis.result_status = result_status.NOT_FOUND_CORRECT | 110 analysis.result_status = result_status.NOT_FOUND_CORRECT |
| 106 analysis.culprit_cls = None | 111 analysis.culprit_cls = None |
| 107 else: | 112 else: |
| 108 analysis.culprit_cls = None | 113 analysis.culprit_cls = None |
| 109 if analysis.suspected_cls: | 114 if analysis.suspected_cls: |
| 110 analysis.result_status = result_status.FOUND_INCORRECT | 115 if duplicate: |
| 116 analysis.result_status = result_status.FOUND_INCORRECT_DUPLICATE | |
| 117 else: | |
| 118 analysis.result_status = result_status.FOUND_INCORRECT | |
| 111 else: | 119 else: |
| 112 analysis.result_status = result_status.NOT_FOUND_INCORRECT | 120 analysis.result_status = result_status.NOT_FOUND_INCORRECT |
| 113 | 121 |
| 114 triage_record = { | 122 triage_record = { |
| 115 'triage_timestamp': calendar.timegm(datetime.utcnow().timetuple()), | 123 'triage_timestamp': calendar.timegm(datetime.utcnow().timetuple()), |
| 116 'user_name': user_name, | 124 'user_name': user_name, |
| 117 'result_status': analysis.result_status, | 125 'result_status': analysis.result_status, |
| 118 'version': analysis.version, | 126 'version': analysis.version, |
| 119 } | 127 } |
| 120 if not analysis.triage_history: | 128 if not analysis.triage_history: |
| 121 analysis.triage_history = [] | 129 analysis.triage_history = [] |
| 122 analysis.triage_history.append(triage_record) | 130 analysis.triage_history.append(triage_record) |
| 123 | 131 |
| 124 analysis.put() | 132 analysis.put() |
| 125 | 133 |
| 126 | 134 |
| 127 @ndb.transactional | 135 @ndb.transactional |
| 128 def _UpdateAnalysisResultStatus( | 136 def _UpdateAnalysisResultStatus( |
| 129 master_name, builder_name, build_number, is_correct, user_name=None): | 137 master_name, builder_name, build_number, is_correct, user_name=None): |
| 130 analysis = WfAnalysis.Get(master_name, builder_name, build_number) | 138 analysis = WfAnalysis.Get(master_name, builder_name, build_number) |
| 131 if not analysis or not analysis.completed: | 139 if not analysis or not analysis.completed: |
| 132 return False, None | 140 return False, None |
| 133 | 141 |
| 142 analysis.triage_reference_analysis_master_name = None | |
| 143 analysis.triage_reference_analysis_builder_name = None | |
| 144 analysis.triage_reference_analysis_build_number = None | |
|
chanli
2016/06/24 18:24:20
These lines are not needed.
| |
| 145 | |
| 134 _AppendTriageHistoryRecord(analysis, is_correct, user_name) | 146 _AppendTriageHistoryRecord(analysis, is_correct, user_name) |
| 135 | 147 |
| 136 return True, analysis | 148 return True, analysis |
| 137 | 149 |
| 138 | 150 |
| 139 def _GetDuplicateAnalyses(original_analysis): | 151 def _GetDuplicateAnalyses(original_analysis): |
| 140 start_time = (original_analysis.build_start_time - | 152 start_time = (original_analysis.build_start_time - |
| 141 timedelta(hours=MATCHING_ANALYSIS_HOURS_AGO_START)) | 153 timedelta(hours=MATCHING_ANALYSIS_HOURS_AGO_START)) |
| 142 end_time = (original_analysis.build_start_time + | 154 end_time = (original_analysis.build_start_time + |
| 143 timedelta(hours=MATCHING_ANALYSIS_HOURS_AGO_END)) | 155 timedelta(hours=MATCHING_ANALYSIS_HOURS_AGO_END)) |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 159 return [analysis for analysis in analysis_results if | 171 return [analysis for analysis in analysis_results if |
| 160 _DoAnalysesMatch(original_analysis, analysis) and | 172 _DoAnalysesMatch(original_analysis, analysis) and |
| 161 original_analysis.key is not analysis.key and | 173 original_analysis.key is not analysis.key and |
| 162 analysis.completed] | 174 analysis.completed] |
| 163 | 175 |
| 164 | 176 |
| 165 def _TriageDuplicateResults(original_analysis, is_correct, user_name=None): | 177 def _TriageDuplicateResults(original_analysis, is_correct, user_name=None): |
| 166 matching_analyses = _GetDuplicateAnalyses(original_analysis) | 178 matching_analyses = _GetDuplicateAnalyses(original_analysis) |
| 167 | 179 |
| 168 for analysis in matching_analyses: | 180 for analysis in matching_analyses: |
| 169 _AppendTriageHistoryRecord(analysis, is_correct, user_name) | 181 analysis.triage_reference_analysis_master_name = ( |
| 182 original_analysis.master_name) | |
| 183 analysis.triage_reference_analysis_builder_name = ( | |
| 184 original_analysis.builder_name) | |
| 185 analysis.triage_reference_analysis_build_number = ( | |
| 186 original_analysis.build_number) | |
| 187 _AppendTriageHistoryRecord(analysis, is_correct, user_name, duplicate=True) | |
| 188 | |
| 189 return len(matching_analyses) | |
| 170 | 190 |
| 171 | 191 |
| 172 class TriageAnalysis(BaseHandler): | 192 class TriageAnalysis(BaseHandler): |
| 173 PERMISSION_LEVEL = Permission.CORP_USER | 193 PERMISSION_LEVEL = Permission.CORP_USER |
| 174 | 194 |
| 175 def HandleGet(self): # pragma: no cover | 195 def HandleGet(self): # pragma: no cover |
| 176 return self.HandlePost() | 196 return self.HandlePost() |
| 177 | 197 |
| 178 def HandlePost(self): | 198 def HandlePost(self): |
| 179 """Sets the manual triage result for the analysis. | 199 """Sets the manual triage result for the analysis. |
| 180 | 200 |
| 181 Mark the analysis result as correct/wrong/etc. | 201 Mark the analysis result as correct/wrong/etc. |
| 182 TODO: make it possible to set the real culprit CLs. | 202 TODO: make it possible to set the real culprit CLs. |
| 183 """ | 203 """ |
| 184 url = self.request.get('url').strip() | 204 url = self.request.get('url').strip() |
| 185 build_info = buildbot.ParseBuildUrl(url) | 205 build_info = buildbot.ParseBuildUrl(url) |
| 186 if not build_info: | 206 if not build_info: |
| 187 return {'data': {'success': False}} | 207 return {'data': {'success': False}} |
| 188 master_name, builder_name, build_number = build_info | 208 master_name, builder_name, build_number = build_info |
| 189 | 209 |
| 190 is_correct = self.request.get('correct').lower() == 'true' | 210 is_correct = self.request.get('correct').lower() == 'true' |
| 191 # As the permission level is CORP_USER, we could assume the current user | 211 # As the permission level is CORP_USER, we could assume the current user |
| 192 # already logged in. | 212 # already logged in. |
| 193 user_name = users.get_current_user().email().split('@')[0] | 213 user_name = users.get_current_user().email().split('@')[0] |
| 194 success, original_analysis = _UpdateAnalysisResultStatus( | 214 success, original_analysis = _UpdateAnalysisResultStatus( |
| 195 master_name, builder_name, build_number, is_correct, user_name) | 215 master_name, builder_name, build_number, is_correct, user_name) |
| 216 num_duplicate_analyses = 0 | |
| 196 if success: | 217 if success: |
| 197 _TriageDuplicateResults(original_analysis, is_correct, user_name) | 218 num_duplicate_analyses = _TriageDuplicateResults( |
| 198 return {'data': {'success': success}} | 219 original_analysis, is_correct, user_name) |
| 220 return {'data': {'success': success, | |
| 221 'num_duplicate_analyses': num_duplicate_analyses}} | |
| OLD | NEW |