Commit a5774969 authored by Miquel Torres's avatar Miquel Torres Committed by GitHub
Browse files

Merge pull request #224 from tobami/default-banch-in-db

Default banch in db
parents 69f74055 3743ece0
...@@ -185,10 +185,6 @@ several parameters (the file includes comments with full examples). ...@@ -185,10 +185,6 @@ several parameters (the file includes comments with full examples).
* `grid`: will always show as default the grid of plots * `grid`: will always show as default the grid of plots
* `show_none`: will show a text message (better default when there are lots of benchmarks) * `show_none`: will show a text message (better default when there are lots of benchmarks)
* `mybench`: will select benchmark named "mybench" * `mybench`: will select benchmark named "mybench"
* `DEF_BRANCH`: Defines the default branch to be used when calculating timeline and changes data for presentation. Example values:
* `default`: the default value, and usually mercurial's default branch
* `master`: usually git's default branch
* `trunk`: usually SVN's default branch
### Comparison View ### Comparison View
* `CHART_TYPE`: Chooses the default chart type (normal bars, stacked bars or * `CHART_TYPE`: Chooses the default chart type (normal bars, stacked bars or
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django import forms
from django.contrib import admin
from codespeed.models import (Project, Revision, Executable, Benchmark, Branch, from codespeed.models import (Project, Revision, Executable, Benchmark, Branch,
Result, Environment, Report) Result, Environment, Report)
from django.contrib import admin
class ProjectForm(forms.ModelForm):
default_branch = forms.CharField(max_length=32, required=False)
def clean(self):
if not self.cleaned_data.get('default_branch'):
repo_type = self.cleaned_data['repo_type']
if repo_type in [Project.GIT, Project.GITHUB]:
self.cleaned_data['default_branch'] = "master"
elif repo_type == Project.MERCURIAL:
self.cleaned_data['default_branch'] = "default"
elif repo_type == Project.SUBVERSION:
self.cleaned_data['default_branch'] = "trunk"
else:
self.add_error('default_branch', 'This field is required.')
class Meta:
model = Project
fields = '__all__'
@admin.register(Project) @admin.register(Project)
class ProjectAdmin(admin.ModelAdmin): class ProjectAdmin(admin.ModelAdmin):
list_display = ('name', 'repo_type', 'repo_path', 'track') list_display = ('name', 'repo_type', 'repo_path', 'track')
form = ProjectForm
@admin.register(Branch) @admin.register(Branch)
class BranchAdmin(admin.ModelAdmin): class BranchAdmin(admin.ModelAdmin):
......
...@@ -35,15 +35,14 @@ class ResultFeed(Feed): ...@@ -35,15 +35,14 @@ class ResultFeed(Feed):
class LatestEntries(ResultFeed): class LatestEntries(ResultFeed):
description = "Last benchmark runs" description = "Last Results"
def result_filter(self): def result_filter(self):
return Q(revision__branch__name=settings.DEF_BRANCH) return Q()
class LatestSignificantEntries(ResultFeed): class LatestSignificantEntries(ResultFeed):
description = "Last benchmark runs with significant changes" description = "Last results with significant changes"
def result_filter(self): def result_filter(self):
return Q(revision__branch__name=settings.DEF_BRANCH, return Q(colorcode__in=('red', 'green'))
colorcode__in=('red', 'green'))
...@@ -3,26 +3,28 @@ ...@@ -3,26 +3,28 @@
"pk": 1, "pk": 1,
"model": "codespeed.project", "model": "codespeed.project",
"fields": { "fields": {
"repo_type": "N", "repo_type": "G",
"name": "MyProject", "name": "MyProject",
"commit_browsing_url": "", "commit_browsing_url": "",
"repo_user": "", "repo_user": "",
"track": true, "track": true,
"repo_pass": "", "repo_pass": "",
"repo_path": "" "repo_path": "",
"default_branch": "master"
} }
}, },
{ {
"pk": 2, "pk": 2,
"model": "codespeed.project", "model": "codespeed.project",
"fields": { "fields": {
"repo_type": "N", "repo_type": "M",
"name": "Other", "name": "Other",
"commit_browsing_url": "", "commit_browsing_url": "",
"repo_user": "", "repo_user": "",
"track": true, "track": true,
"repo_pass": "", "repo_pass": "",
"repo_path": "" "repo_path": "",
"default_branch": "default"
} }
}, },
{ {
...@@ -35,7 +37,8 @@ ...@@ -35,7 +37,8 @@
"repo_user": "", "repo_user": "",
"track": false, "track": false,
"repo_pass": "", "repo_pass": "",
"repo_path": "" "repo_path": "",
"default_branch": "master"
} }
}, },
{ {
...@@ -59,7 +62,7 @@ ...@@ -59,7 +62,7 @@
"model": "codespeed.branch", "model": "codespeed.branch",
"fields": { "fields": {
"project": 2, "project": 2,
"name": "master" "name": "default"
} }
}, },
{ {
......
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2017-08-04 03:45
from __future__ import unicode_literals
from django.db import migrations, models
def get_default_branch_name():
from django.conf import settings
try:
return settings.DEF_BRANCH
except AttributeError:
return "master"
class Migration(migrations.Migration):
dependencies = [
('codespeed', '0002_median'),
]
operations = [
migrations.AddField(
model_name='project',
name='default_branch',
field=models.CharField(default=get_default_branch_name, max_length=32),
preserve_default=False,
),
migrations.AlterField(
model_name='branch',
name='name',
field=models.CharField(max_length=32),
),
]
...@@ -42,6 +42,7 @@ class Project(models.Model): ...@@ -42,6 +42,7 @@ class Project(models.Model):
commit_browsing_url = models.CharField("Commit browsing URL", commit_browsing_url = models.CharField("Commit browsing URL",
blank=True, max_length=200) blank=True, max_length=200)
track = models.BooleanField("Track changes", default=True) track = models.BooleanField("Track changes", default=True)
default_branch = models.CharField(max_length=32)
def __str__(self): def __str__(self):
return self.name return self.name
...@@ -103,7 +104,7 @@ class HistoricalValue(object): ...@@ -103,7 +104,7 @@ class HistoricalValue(object):
@python_2_unicode_compatible @python_2_unicode_compatible
class Branch(models.Model): class Branch(models.Model):
name = models.CharField(max_length=20) name = models.CharField(max_length=32)
project = models.ForeignKey(Project, related_name="branches") project = models.ForeignKey(Project, related_name="branches")
def __str__(self): def __str__(self):
...@@ -138,7 +139,7 @@ class Revision(models.Model): ...@@ -138,7 +139,7 @@ class Revision(models.Model):
else: else:
date = self.date.strftime("%b %d, %H:%M") date = self.date.strftime("%b %d, %H:%M")
string = " - ".join(filter(None, (date, self.commitid, self.tag))) string = " - ".join(filter(None, (date, self.commitid, self.tag)))
if self.branch.name != settings.DEF_BRANCH: if self.branch.name != self.branch.project.default_branch:
string += " - " + self.branch.name string += " - " + self.branch.name
return string return string
......
...@@ -134,6 +134,9 @@ def save_result(data): ...@@ -134,6 +134,9 @@ def save_result(data):
def create_report_if_enough_data(rev, exe, e): def create_report_if_enough_data(rev, exe, e):
"""Triggers Report creation when there are enough results""" """Triggers Report creation when there are enough results"""
if exe.project.track is not True:
return False
last_revs = Revision.objects.filter( last_revs = Revision.objects.filter(
branch=rev.branch branch=rev.branch
).order_by('-date')[:2] ).order_by('-date')[:2]
......
...@@ -6,9 +6,6 @@ WEBSITE_NAME = "MySpeedSite" # This name will be used in the reports RSS feed ...@@ -6,9 +6,6 @@ WEBSITE_NAME = "MySpeedSite" # This name will be used in the reports RSS feed
DEF_ENVIRONMENT = None # Name of the environment which should be selected as default DEF_ENVIRONMENT = None # Name of the environment which should be selected as default
DEF_BRANCH = "master" # Defines the default branch to be used.
# In git projects, this branch is usually called "master"
DEF_BASELINE = None # Which executable + revision should be default as a baseline DEF_BASELINE = None # Which executable + revision should be default as a baseline
# Given as the name of the executable and commitid of the revision # Given as the name of the executable and commitid of the revision
# Example: defaultbaseline = {'executable': 'myexe', 'revision': '21'} # Example: defaultbaseline = {'executable': 'myexe', 'revision': '21'}
......
...@@ -3,7 +3,6 @@ from datetime import datetime, timedelta ...@@ -3,7 +3,6 @@ from datetime import datetime, timedelta
import copy import copy
import json import json
from django.conf import settings
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -342,19 +341,24 @@ class TestTimeline(TestCase): ...@@ -342,19 +341,24 @@ class TestTimeline(TestCase):
"base": "2+4", "base": "2+4",
"ben": "float", "ben": "float",
"env": "1", "env": "1",
"revs": 2 "revs": "2"
} }
response = self.client.get(path, data) response = self.client.get(path, data)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
responsedata = json.loads(response.content.decode()) responsedata = json.loads(response.content.decode())
self.assertEquals( self.assertEquals(
responsedata['error'], "None", "there should be no errors") responsedata['error'], "None", "there should be no errors")
self.assertEquals( self.assertEquals(
len(responsedata['timelines']), 1, "there should be 1 benchmark") len(responsedata['timelines']), 1, "there should be 1 benchmark")
self.assertEquals( self.assertEquals(
len(responsedata['timelines'][0]['branches']['master']), len(responsedata['timelines'][0]['branches']),
2, 2,
"there should be 2 timelines") "there should be 2 branches")
self.assertEquals(
len(responsedata['timelines'][0]['branches']['default']),
1,
"there should be 1 timeline for master")
self.assertEquals( self.assertEquals(
len(responsedata['timelines'][0]['branches']['master']['1']), len(responsedata['timelines'][0]['branches']['master']['1']),
2, 2,
...@@ -371,7 +375,7 @@ class TestReports(TestCase): ...@@ -371,7 +375,7 @@ class TestReports(TestCase):
Environment.objects.create(name='Dual Core', cpu='Core 2 Duo 8200') Environment.objects.create(name='Dual Core', cpu='Core 2 Duo 8200')
self.data = { self.data = {
'commitid': 'abcd1', 'commitid': 'abcd1',
'branch': settings.DEF_BRANCH, 'branch': 'master',
'project': 'MyProject', 'project': 'MyProject',
'executable': 'myexe O3 64bits', 'executable': 'myexe O3 64bits',
'benchmark': 'float', 'benchmark': 'float',
...@@ -399,3 +403,13 @@ class TestReports(TestCase): ...@@ -399,3 +403,13 @@ class TestReports(TestCase):
response = self.client.post(reverse('codespeed.views.reports'), {}) response = self.client.post(reverse('codespeed.views.reports'), {})
self.assertEqual(response.status_code, 405) self.assertEqual(response.status_code, 405)
class TestFeeds(TestCase):
def test_latest_result_feed(self):
response = self.client.get(reverse('latest-results'))
self.assertEqual(response.status_code, 200)
content = response.content.decode()
self.assertIn('<atom:link ', content)
...@@ -9,9 +9,9 @@ urlpatterns = patterns('', ...@@ -9,9 +9,9 @@ urlpatterns = patterns('',
url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'), url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'), url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
# RSS for reports # RSS for reports
url(r'^feeds/latest/$', LatestEntries(), name='latest_feeds'), url(r'^feeds/latest/$', LatestEntries(), name='latest-results'),
url(r'^feeds/latest_significant/$', LatestSignificantEntries(), url(r'^feeds/latest_significant/$', LatestSignificantEntries(),
name='latest_significant_feeds'), name='latest-significant-results'),
) )
urlpatterns += patterns('codespeed.views', urlpatterns += patterns('codespeed.views',
......
...@@ -5,17 +5,18 @@ import json ...@@ -5,17 +5,18 @@ import json
import logging import logging
import django import django
from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.http import HttpResponse, Http404, HttpResponseBadRequest,\ from django.http import HttpResponse, Http404, HttpResponseBadRequest,\
HttpResponseNotFound HttpResponseNotFound
from django.db.models import F
from django.shortcuts import get_object_or_404, render_to_response from django.shortcuts import get_object_or_404, render_to_response
from django.views.decorators.http import require_GET, require_POST from django.views.decorators.http import require_GET, require_POST
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.template import RequestContext from django.template import RequestContext
from django.conf import settings
from .auth import basic_auth_required
from .auth import basic_auth_required
from .models import (Environment, Report, Project, Revision, Result, from .models import (Environment, Report, Project, Revision, Result,
Executable, Benchmark, Branch) Executable, Benchmark, Branch)
from .views_data import (get_default_environment, getbaselineexecutables, from .views_data import (get_default_environment, getbaselineexecutables,
...@@ -231,8 +232,18 @@ def gettimelinedata(request): ...@@ -231,8 +232,18 @@ def gettimelinedata(request):
timeline_list = {'error': 'None', 'timelines': []} timeline_list = {'error': 'None', 'timelines': []}
executables = data.get('exe', "").split(",") executable_ids = data.get('exe', '').split(',')
if not filter(None, executables):
executables = []
for i in executable_ids:
if not i:
continue
try:
executables.append(Executable.objects.get(id=int(i)))
except Executable.DoesNotExist:
pass
if not executables:
timeline_list['error'] = "No executables selected" timeline_list['error'] = "No executables selected"
return HttpResponse(json.dumps(timeline_list)) return HttpResponse(json.dumps(timeline_list))
environment = None environment = None
...@@ -270,16 +281,14 @@ def gettimelinedata(request): ...@@ -270,16 +281,14 @@ def gettimelinedata(request):
'branches': {}, 'branches': {},
'baseline': "None", 'baseline': "None",
} }
# Temporary
trunks = []
if Branch.objects.filter(name=settings.DEF_BRANCH):
trunks.append(settings.DEF_BRANCH)
# For now, we'll only work with trunk branches
append = False append = False
for branch in trunks: for branch in Branch.objects.filter(
append = False project__track=True, name=F('project__default_branch')):
timeline['branches'][branch] = {} # For now, we'll only work with default branches
for executable in executables: for executable in executables:
if executable.project != branch.project:
continue
resultquery = Result.objects.filter( resultquery = Result.objects.filter(
benchmark=bench benchmark=bench
).filter( ).filter(
...@@ -287,12 +296,13 @@ def gettimelinedata(request): ...@@ -287,12 +296,13 @@ def gettimelinedata(request):
).filter( ).filter(
executable=executable executable=executable
).filter( ).filter(
revision__branch__name=branch revision__branch=branch
).select_related( ).select_related(
"revision" "revision"
).order_by('-revision__date')[:number_of_revs] ).order_by('-revision__date')[:number_of_revs]
if not len(resultquery): if not len(resultquery):
continue continue
timeline['branches'].setdefault(branch.name, {})
results = [] results = []
for res in resultquery: for res in resultquery:
...@@ -313,7 +323,7 @@ def gettimelinedata(request): ...@@ -313,7 +323,7 @@ def gettimelinedata(request):
[ [
res.revision.date.strftime('%Y/%m/%d %H:%M:%S %z'), res.revision.date.strftime('%Y/%m/%d %H:%M:%S %z'),
res.value, val_max, q3, q1, val_min, res.value, val_max, q3, q1, val_min,
res.revision.get_short_commitid(), res.revision.tag, branch res.revision.get_short_commitid(), res.revision.tag, branch.name
] ]
) )
else: else:
...@@ -324,34 +334,37 @@ def gettimelinedata(request): ...@@ -324,34 +334,37 @@ def gettimelinedata(request):
[ [
res.revision.date.strftime('%Y/%m/%d %H:%M:%S %z'), res.revision.date.strftime('%Y/%m/%d %H:%M:%S %z'),
res.value, std_dev, res.value, std_dev,
res.revision.get_short_commitid(), res.revision.tag, branch res.revision.get_short_commitid(), res.revision.tag, branch.name
] ]
) )
timeline['branches'][branch][executable] = results timeline['branches'][branch.name][executable.id] = results
append = True append = True
if baselinerev is not None and append:
try: if baselinerev is not None and append:
baselinevalue = Result.objects.get( try:
executable=baselineexe, baselinevalue = Result.objects.get(
benchmark=bench, executable=baselineexe,
revision=baselinerev, benchmark=bench,
environment=environment revision=baselinerev,
).value environment=environment
except Result.DoesNotExist: ).value
timeline['baseline'] = "None" except Result.DoesNotExist:
else: timeline['baseline'] = "None"
# determine start and end revision (x axis) else:
# from longest data series # determine start and end revision (x axis)
results = [] # from longest data series
results = []
for branch in timeline['branches']:
for exe in timeline['branches'][branch]: for exe in timeline['branches'][branch]:
if len(timeline['branches'][branch][exe]) > len(results): if len(timeline['branches'][branch][exe]) > len(results):
results = timeline['branches'][branch][exe] results = timeline['branches'][branch][exe]
end = results[0][0] end = results[0][0]
start = results[len(results) - 1][0] start = results[len(results) - 1][0]
timeline['baseline'] = [ timeline['baseline'] = [
[str(start), baselinevalue], [str(start), baselinevalue],
[str(end), baselinevalue] [str(end), baselinevalue]
] ]
if append: if append:
timeline_list['timelines'].append(timeline) timeline_list['timelines'].append(timeline)
...@@ -401,8 +414,8 @@ def timeline(request): ...@@ -401,8 +414,8 @@ def timeline(request):
branch_list.sort() branch_list.sort()
defaultbranch = "" defaultbranch = ""
if settings.DEF_BRANCH in branch_list: if defaultproject.default_branch in branch_list:
defaultbranch = settings.DEF_BRANCH defaultbranch = defaultproject.default_branch
if data.get('bran') in branch_list: if data.get('bran') in branch_list:
defaultbranch = data.get('bran') defaultbranch = data.get('bran')
...@@ -576,7 +589,7 @@ def changes(request): ...@@ -576,7 +589,7 @@ def changes(request):
for proj in Project.objects.filter(track=True): for proj in Project.objects.filter(track=True):
executables[proj] = Executable.objects.filter(project=proj) executables[proj] = Executable.objects.filter(project=proj)
projectlist.append(proj) projectlist.append(proj)
branch = Branch.objects.filter(name=settings.DEF_BRANCH, project=proj) branch = Branch.objects.filter(name=proj.default_branch, project=proj)
revisionlists[proj.name] = list(Revision.objects.filter( revisionlists[proj.name] = list(Revision.objects.filter(
branch=branch branch=branch
).order_by('-date')[:revlimit]) ).order_by('-date')[:revlimit])
...@@ -631,15 +644,11 @@ def reports(request): ...@@ -631,15 +644,11 @@ def reports(request):
context = {} context = {}
context['reports'] = \ context['reports'] = \