Skip to content

Commit 98e03ff

Browse files
committed
Refactoring reports creation for clarity
- Added some tests for Report generation
1 parent 6a385b2 commit 98e03ff

2 files changed

Lines changed: 365 additions & 111 deletions

File tree

codespeed/models.py

Lines changed: 133 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import absolute_import, unicode_literals
33

4+
import logging
45
import os
56
import json
67

@@ -12,6 +13,8 @@
1213

1314
from .commits.github import GITHUB_URL_RE
1415

16+
logger = logging.getLogger(__name__)
17+
1518

1619
@python_2_unicode_compatible
1720
class Project(models.Model):
@@ -73,6 +76,31 @@ def save(self, *args, **kwargs):
7376
super(Project, self).save(*args, **kwargs)
7477

7578

79+
class HistoricalValue(object):
80+
def __init__(self, name=None, val=0, color='none'):
81+
self.name = name
82+
self.val = val
83+
self.color = color
84+
85+
def update_if_less_important_than(self, val, color, name):
86+
if self.is_less_important_than(val, color):
87+
# Do update biggest total change
88+
self.val = val
89+
self.color = color
90+
self.name = name
91+
92+
def is_less_important_than(self, val, color):
93+
if color == "red" and self.color != "red":
94+
return True
95+
elif color == "red" and abs(val) > abs(self.val):
96+
return True
97+
elif (color == "green" and self.color != "red" and
98+
abs(val) > abs(self.val)):
99+
return True
100+
else:
101+
return False
102+
103+
76104
@python_2_unicode_compatible
77105
class Branch(models.Model):
78106
name = models.CharField(max_length=20)
@@ -225,11 +253,44 @@ class Meta:
225253

226254
def save(self, *args, **kwargs):
227255
tablelist = self.get_changes_table(force_save=True)
228-
max_change, max_change_ben, max_change_color = 0, None, "none"
229-
max_trend, max_trend_ben, max_trend_color = 0, None, "none"
230-
average_change, average_change_units, average_change_color = 0, None, "none"
231-
average_trend, average_trend_units, average_trend_color = 0, None, "none"
256+
self.reinitialize()
257+
changes = self.aggregate_significant_changes(tablelist)
258+
self.update_to_highest_priority_change(changes)
259+
260+
super(Report, self).save(*args, **kwargs)
261+
262+
def update_to_highest_priority_change(self, changes):
263+
average_change = changes['average_change']
264+
max_change = changes['max_change']
265+
average_trend = changes['average_trend']
266+
max_trend = changes['max_trend']
267+
268+
# Average change
269+
if average_change.color != "none":
270+
self.update_summary("Average {} {}", average_change)
271+
self.colorcode = average_change.color
272+
273+
# Single benchmark change
274+
elif max_change.color != "none":
275+
self.update_summary("{} {}", max_change)
276+
self.colorcode = max_change.color
277+
278+
# Average trend
279+
elif average_trend.color != "none":
280+
self.update_summary("Average {} trend {}", average_trend)
281+
self.update_by_trend_color(average_trend.color)
282+
283+
# Single benchmark trend
284+
elif max_trend.color != "none":
285+
self.update_summary("{} trend {}", max_trend)
286+
# use lighter colors for trend results:
287+
self.update_by_trend_color(max_trend.color)
288+
289+
def reinitialize(self):
290+
self.summary = ""
291+
self.colorcode = "none"
232292

293+
def aggregate_significant_changes(self, tablelist):
233294
# Get default threshold values
234295
change_threshold = 3.0
235296
trend_threshold = 5.0
@@ -239,95 +300,54 @@ def save(self, *args, **kwargs):
239300
if hasattr(settings, 'TREND_THRESHOLD') and settings.TREND_THRESHOLD:
240301
trend_threshold = settings.TREND_THRESHOLD
241302

242-
# Fetch big changes for each unit type and each benchmark
243-
for units in tablelist:
244-
# Total change
245-
val = units['totals']['change']
303+
max_change = HistoricalValue()
304+
max_trend = HistoricalValue()
305+
average_change = HistoricalValue()
306+
average_trend = HistoricalValue()
307+
308+
# Fetch big changes for each quantity and each benchmark
309+
for quantity in tablelist:
310+
quantity_name = quantity['units_title'].lower()
311+
less_is_better = quantity['lessisbetter']
312+
313+
val = quantity['totals']['change']
246314
if val == "-":
247315
continue
248-
color = self.getcolorcode(val, units['lessisbetter'],
249-
change_threshold)
250-
if self.is_big_change(val, color, average_change, average_change_color):
251-
# Do update biggest total change
252-
average_change = val
253-
average_change_units = units['units_title']
254-
average_change_color = color
255-
# Total trend
256-
val = units['totals']['trend']
316+
color = self.getcolorcode(val, less_is_better, change_threshold)
317+
average_change.update_if_less_important_than(val, color,
318+
quantity_name)
319+
320+
val = quantity['totals']['trend']
257321
if val != "-":
258-
color = self.getcolorcode(val, units['lessisbetter'],
259-
trend_threshold)
260-
if self.is_big_change(val, color, average_trend, average_trend_color):
261-
# Do update biggest total trend change
262-
average_trend = val
263-
average_trend_units = units['units_title']
264-
average_trend_color = color
265-
for row in units['rows']:
322+
color = self.getcolorcode(val, less_is_better, trend_threshold)
323+
average_trend.update_if_less_important_than(val, color,
324+
quantity_name)
325+
326+
for row in quantity['rows']:
327+
benchmark_name = row['bench_name']
266328
# Single change
267329
val = row['change']
268330
if val == "-":
269331
continue
270-
color = self.getcolorcode(val, units['lessisbetter'],
332+
color = self.getcolorcode(val, less_is_better,
271333
change_threshold)
272-
if self.is_big_change(val, color, max_change, max_change_color):
273-
# Do update biggest single change
274-
max_change = val
275-
max_change_ben = row['bench_name']
276-
max_change_color = color
334+
max_change.update_if_less_important_than(val, color,
335+
benchmark_name)
277336
# Single trend
278337
val = row['trend']
279338
if val == "-":
280339
continue
281-
color = self.getcolorcode(val, units['lessisbetter'], trend_threshold)
282-
if self.is_big_change(val, color, max_trend, max_trend_color):
283-
# Do update biggest single trend change
284-
max_trend = val
285-
max_trend_ben = row['bench_name']
286-
max_trend_color = color
287-
# Reinitialize
288-
self.summary = ""
289-
self.colorcode = "none"
290-
291-
# Save summary in order of priority
292-
# (changes results before trends, averages before individual results)
293-
294-
# Average change
295-
if average_change_color != "none":
296-
self.summary = "Average %s %s" % (
297-
average_change_units.lower(),
298-
self.updown(average_change))
299-
self.colorcode = average_change_color
300-
301-
# Single benchmark change
302-
elif max_change_color != "none":
303-
self.summary = "%s %s" % (
304-
max_change_ben,
305-
self.updown(max_change))
306-
self.colorcode = max_change_color
307-
308-
# Average trend
309-
elif average_trend_color != "none":
310-
self.summary = "Average %s trend %s" % (
311-
average_trend_units.lower(),
312-
self.updown(average_trend))
313-
# use lighter colors for trend results:
314-
if average_trend_color == "red":
315-
self.colorcode = "yellow"
316-
elif average_trend_color == "green":
317-
self.colorcode = "lightgreen"
318-
319-
# Single benchmark trend
320-
elif max_trend_color != "none":
321-
self.summary = "%s trend %s" % (
322-
max_trend_ben,
323-
self.updown(max_trend))
324-
# use lighter colors for trend results:
325-
if max_trend_color == "red":
326-
self.colorcode = "yellow"
327-
elif max_trend_color == "green":
328-
self.colorcode = "lightgreen"
329-
330-
super(Report, self).save(*args, **kwargs)
340+
color = self.getcolorcode(val, less_is_better, trend_threshold)
341+
max_trend.update_if_less_important_than(val, color,
342+
benchmark_name)
343+
return {'max_change': max_change,
344+
'max_trend': max_trend,
345+
'average_change': average_change,
346+
'average_trend': average_trend}
347+
348+
def update_summary(self, format, hist_value):
349+
self.summary = format.format(hist_value.name,
350+
self.updown(hist_value.val))
331351

332352
def updown(self, val):
333353
"""Substitutes plus/minus with up/down"""
@@ -338,16 +358,12 @@ def updown(self, val):
338358
else:
339359
return "%s %.1f%%" % (direction, aval)
340360

341-
def is_big_change(self, val, color, current_val, current_color):
342-
if color == "red" and current_color != "red":
343-
return True
344-
elif color == "red" and abs(val) > abs(current_val):
345-
return True
346-
elif (color == "green" and current_color != "red" and
347-
abs(val) > abs(current_val)):
348-
return True
349-
else:
350-
return False
361+
def update_by_trend_color(self, color):
362+
# use lighter colors for trend results:
363+
if color == "red":
364+
self.colorcode = "yellow"
365+
elif color == "green":
366+
self.colorcode = "lightgreen"
351367

352368
def getcolorcode(self, val, lessisbetter, threshold):
353369
if lessisbetter:
@@ -359,6 +375,20 @@ def getcolorcode(self, val, lessisbetter, threshold):
359375
else:
360376
return "none"
361377

378+
def get_last_revisions(self, depth):
379+
lastrevisions = []
380+
try:
381+
lastrevisions = Revision.objects.filter(
382+
branch=self.revision.branch
383+
).filter(
384+
date__lte=self.revision.date
385+
).order_by('-date')[:depth + 1]
386+
# Same as self.revision unless in a different branch
387+
except Exception as e:
388+
logger.warning("Exception while getting results: %s", e,
389+
exc_info=True)
390+
return lastrevisions
391+
362392
def get_changes_table(self, trend_depth=10, force_save=False):
363393
# Determine whether required trend value is the default one
364394
default_trend = 10
@@ -370,16 +400,10 @@ def get_changes_table(self, trend_depth=10, force_save=False):
370400
return self._get_tablecache()
371401
# Otherwise generate a new changes table
372402
# Get latest revisions for this branch (which also sets the project)
373-
try:
374-
lastrevisions = Revision.objects.filter(
375-
branch=self.revision.branch
376-
).filter(
377-
date__lte=self.revision.date
378-
).order_by('-date')[:trend_depth + 1]
379-
# Same as self.revision unless in a different branch
380-
lastrevision = lastrevisions[0]
381-
except:
403+
lastrevisions = self.get_last_revisions(trend_depth)
404+
if not lastrevisions:
382405
return []
406+
383407
change_list = []
384408
pastrevisions = []
385409
if len(lastrevisions) > 1:
@@ -394,7 +418,7 @@ def get_changes_table(self, trend_depth=10, force_save=False):
394418
pastrevisions = lastrevisions[trend_depth - 2:trend_depth + 1]
395419

396420
result_list = Result.objects.filter(
397-
revision=lastrevision
421+
revision=lastrevisions[0]
398422
).filter(
399423
environment=self.environment
400424
).filter(
@@ -463,23 +487,23 @@ def get_changes_table(self, trend_depth=10, force_save=False):
463487
# Calculate trend:
464488
# percentage change relative to average of 3 previous results
465489
# Calculate past average
466-
average = 0
467-
averagecount = 0
490+
result_sum = 0
491+
num_past_results = 0
468492
if len(pastrevisions):
469493
for rev in pastrevisions:
470-
past_rev = Result.objects.filter(
494+
past_result = Result.objects.filter(
471495
revision=rev
472496
).filter(
473497
environment=self.environment
474498
).filter(
475499
executable=self.executable
476500
).filter(benchmark=bench)
477-
if past_rev.count():
478-
average += past_rev[0].value
479-
averagecount += 1
501+
if past_result.count():
502+
result_sum += past_result[0].value
503+
num_past_results += 1
480504
trend = "-"
481-
if average:
482-
average = average / averagecount
505+
if result_sum:
506+
average = result_sum / num_past_results
483507
trend = (result - average) * 100 / average
484508
totals['trend'].append(result / average)
485509

0 commit comments

Comments
 (0)