blob: 1c64f2030cb8b7f51b0186b8c4a3860fa1fe3c65 [file] [log] [blame]
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""URL endpoints to show bisect stats."""
import datetime
import json
from dashboard import layered_cache
from dashboard import request_handler
from dashboard import utils
_BISECT_STATS_CACHE_KEY = 'bisect_stats'
_NUM_POINTS_TO_DISPLAY = 52
_BISECT_STAT_SERIES_NAME = ['win', 'linux', 'mac', 'android']
class BisectStatsHandler(request_handler.RequestHandler):
"""URL endpoint to get stats about bisect runs."""
def get(self):
"""Renders the UI with charts."""
bisect_stats = layered_cache.GetExternal(_BISECT_STATS_CACHE_KEY)
if not bisect_stats:
bisect_stats = {
'failed': [],
'completed': []
}
series_data = {
'failed': bisect_stats['failed'],
'completed': bisect_stats['completed']
}
total_series_data = {
'failed': self._GetTotalBisectRunSeries(bisect_stats['failed']),
'completed': self._GetTotalBisectRunSeries(bisect_stats['completed'])
}
self.RenderHtml('bisect_stats.html', {
'series_data': json.dumps(series_data),
'total_series_data': json.dumps(total_series_data),
})
def _GetTotalBisectRunSeries(self, series_map):
"""Sums up failed and completed bisect run series.
Args:
series_map: Dictionary of series names to list of data series.
Returns:
A list of data series.
"""
cropped_series_list = []
for key in series_map:
series = series_map[key]
cropped_series_list.append(series[len(series) - _NUM_POINTS_TO_DISPLAY:])
# Sum up series.
series_map = {}
for series in cropped_series_list:
for x_value, y_value in series:
if x_value not in series_map:
series_map[x_value] = y_value
else:
series_map[x_value] += y_value
result_list = []
for key in sorted(series_map):
result_list.append([key, series_map[key]])
return result_list
def UpdateBisectStats(bot_name, status):
"""Updates bisect run stat by bot name and status.
Bisect stats stored in a layered_cache entity have the form below. Each
tick is one week and count is the sum of failed or completed bisect runs.
{
'failed': {
bot_name: [[week_timestamp, count], [week_timestamp, count]],
},
'completed': {
bot_name: [[week_timestamp, count], [week_timestamp, count]],
}
}
Args:
bot_name: Name of the bisect bot.
status: Bisect status. Either 'failed' or 'completed'.
"""
# TODO(chrisphan): Add stats for staled bisect.
if status not in ['failed', 'completed']:
return
series_name = _GetSeriesNameFromBotName(bot_name)
week_timestamp = _GetLastMondayTimestamp()
bisect_stats = layered_cache.GetExternal(_BISECT_STATS_CACHE_KEY)
if not bisect_stats:
bisect_stats = {
'failed': {},
'completed': {},
}
series_map = bisect_stats[status]
if series_name not in series_map:
series_map[series_name] = [[week_timestamp, 1]]
else:
series = series_map[series_name]
if week_timestamp == series[-1][0]:
series[-1][1] += 1
else:
series.append([week_timestamp, 1])
layered_cache.SetExternal(_BISECT_STATS_CACHE_KEY, bisect_stats)
def _GetLastMondayTimestamp():
"""Gets timestamp of 00:00 last Monday in milliseconds as an integer."""
today = datetime.date.today()
monday = today - datetime.timedelta(days=today.weekday())
return utils.TimestampMilliseconds(monday)
def _GetSeriesNameFromBotName(bot_name):
for series_name in _BISECT_STAT_SERIES_NAME:
if series_name in bot_name:
return series_name
return 'other'