From 2231de1dec99460197feef1116c9b698d7d1c2e0 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Tue, 5 May 2015 20:12:21 +0200 Subject: [PATCH] MT#7247 use param_release not tag for filter release - migrate tag field from 32 to 64 chars max - add make migrate rule to deploy changes on db - add panel project-view Checking just one project per review - panel release-view upgraded to deal with uuids no fixed project names - update API to support dynamic discover of uuids and jobs by release Change-Id: I0e64b3b341744ec326ee4e123901a1fdcf220baa --- Makefile | 5 + panel/templates/panel/index.html | 2 +- panel/templates/panel/project.html | 303 ++++++++++++++++++ panel/templates/panel/release.html | 303 +++++++++++++----- panel/tests.py | 18 -- panel/urls.py | 6 +- panel/views.py | 5 + repoapi/fixtures/test_model_queries.json | 86 +++++ repoapi/migrations/0002_auto_20150507_0746.py | 19 ++ repoapi/models.py | 27 +- repoapi/serializers.py | 5 +- repoapi/settings/dev.py | 5 +- repoapi/settings/prod.py | 5 +- repoapi/test/test_model_queries.py | 45 +++ repoapi/urls.py | 10 + repoapi/views.py | 36 ++- 16 files changed, 760 insertions(+), 120 deletions(-) create mode 100644 panel/templates/panel/project.html delete mode 100644 panel/tests.py create mode 100644 repoapi/fixtures/test_model_queries.json create mode 100644 repoapi/migrations/0002_auto_20150507_0746.py create mode 100644 repoapi/test/test_model_queries.py diff --git a/Makefile b/Makefile index fe18cb2..47ec672 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,11 @@ deploy: venv_prod ./manage.py collectstatic --noinput --settings="repoapi.settings.prod" chown www-data:www-data -R ./static_media/ +migrate: venv_prod + source $(VAR_DIR)/venv_prod/bin/activate && \ + ./manage.py migrate --settings="repoapi.settings.prod" + chown www-data:www-data $(VAR_DIR)/db.sqlite3 + ################################### run_dev: venv_dev diff --git a/panel/templates/panel/index.html b/panel/templates/panel/index.html index 1f77acf..1db42b0 100644 --- a/panel/templates/panel/index.html +++ b/panel/templates/panel/index.html @@ -1,6 +1,6 @@

Releases

\ No newline at end of file diff --git a/panel/templates/panel/project.html b/panel/templates/panel/project.html new file mode 100644 index 0000000..c564830 --- /dev/null +++ b/panel/templates/panel/project.html @@ -0,0 +1,303 @@ +{% extends "panel/base.html" %} +{% block title %}{{ project }}{% endblock %} +{% block content %} +
+

+ + Release {{ release }} + +

+
+
+

{{ project }}

+
+
+ +
+ +
+
+{% endblock %} + +{% block extrajs %} + +{% endblock %} diff --git a/panel/templates/panel/release.html b/panel/templates/panel/release.html index 3a0a827..a53c685 100644 --- a/panel/templates/panel/release.html +++ b/panel/templates/panel/release.html @@ -2,42 +2,37 @@ {% block title %}{{ release }}{% endblock %} {% block content %}
-

Release {{ release }}

- + {% endfor %}
{% endblock %} {% block extrajs %} diff --git a/panel/tests.py b/panel/tests.py deleted file mode 100644 index 221a42f..0000000 --- a/panel/tests.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (C) 2015 The Sipwise Team - http://sipwise.com - -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. - -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. - -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . - -from django.test import TestCase - -# Create your tests here. diff --git a/panel/urls.py b/panel/urls.py index 82ed423..7bddd81 100644 --- a/panel/urls.py +++ b/panel/urls.py @@ -13,10 +13,12 @@ # You should have received a copy of the GNU General Public License along # with this program. If not, see . -from django.conf.urls import include, url +from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name='index'), - url(r'^(?P.+)/$', views.release, name='release'), + url(r'^(?P[\w\d\.-]+)/$', views.release, name='release-view'), + url(r'^(?P[\w\d\.-]+)/(?P[\w\d-]+)/$', + views.project, name='project-view'), ] diff --git a/panel/views.py b/panel/views.py index cbafcc5..a454cd4 100644 --- a/panel/views.py +++ b/panel/views.py @@ -26,3 +26,8 @@ def index(request): def release(request, release): context = {'projects': models.PROJECTS, 'release': release} return render(request, 'panel/release.html', context) + + +def project(request, release, project): + context = {'project': project, 'release': release} + return render(request, 'panel/project.html', context) diff --git a/repoapi/fixtures/test_model_queries.json b/repoapi/fixtures/test_model_queries.json new file mode 100644 index 0000000..96a1bcc --- /dev/null +++ b/repoapi/fixtures/test_model_queries.json @@ -0,0 +1,86 @@ +[ + { + "fields": { + "buildnumber": 1, + "date": "2015-05-01T12:57:54.379Z", + "gerrit_change": null, + "gerrit_eventtype": null, + "gerrit_patchset": null, + "job_url": "https://jenkins.mgm.sipwise.com/job/foo-get-code/", + "param_branch": null, + "param_distribution": "wheezy", + "param_ppa": null, + "param_release": "mr3.1-fake", + "param_tag": null, + "projectname": "fake-source", + "repo_name": null, + "result": "FAILED", + "tag": "UUID1" + }, + "model": "repoapi.jenkinsbuildinfo", + "pk": 1 + }, + { + "fields": { + "buildnumber": 1, + "date": "2015-05-04T10:41:53.788Z", + "gerrit_change": null, + "gerrit_eventtype": null, + "gerrit_patchset": null, + "job_url": "http://fake.org/gogo", + "param_branch": null, + "param_distribution": "wheezy", + "param_ppa": null, + "param_release": "mr3.1-fake", + "param_tag": null, + "projectname": "fake-get-code", + "repo_name": null, + "result": "FAILED", + "tag": "UUID0" + }, + "model": "repoapi.jenkinsbuildinfo", + "pk": 2 + }, + { + "fields": { + "buildnumber": 1, + "date": "2015-05-04T10:42:13.259Z", + "gerrit_change": null, + "gerrit_eventtype": null, + "gerrit_patchset": null, + "job_url": "http://fake.org/gogo", + "param_branch": null, + "param_distribution": "wheezy", + "param_ppa": null, + "param_release": "mr3.1-fake", + "param_tag": null, + "projectname": "fake-source-tests", + "repo_name": null, + "result": "SUCCESS", + "tag": "UUID1" + }, + "model": "repoapi.jenkinsbuildinfo", + "pk": 3 + }, + { + "fields": { + "buildnumber": 1, + "date": "2015-05-04T17:04:57.802Z", + "gerrit_change": null, + "gerrit_eventtype": null, + "gerrit_patchset": null, + "job_url": "https://jenkins.mgm.sipwise.com/job/foo-get-code/", + "param_branch": null, + "param_distribution": "wheezy", + "param_ppa": null, + "param_release": "mr3.1-fake", + "param_tag": null, + "projectname": "fake-get-code", + "repo_name": null, + "result": "SUCCESS", + "tag": "UUID1" + }, + "model": "repoapi.jenkinsbuildinfo", + "pk": 4 + } +] \ No newline at end of file diff --git a/repoapi/migrations/0002_auto_20150507_0746.py b/repoapi/migrations/0002_auto_20150507_0746.py new file mode 100644 index 0000000..1fd7446 --- /dev/null +++ b/repoapi/migrations/0002_auto_20150507_0746.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('repoapi', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='jenkinsbuildinfo', + name='tag', + field=models.CharField(max_length=64, null=True), + ), + ] diff --git a/repoapi/models.py b/repoapi/models.py index 87765fb..52fdc27 100644 --- a/repoapi/models.py +++ b/repoapi/models.py @@ -18,17 +18,32 @@ from django.db import models class JenkinsBuildInfoManager(models.Manager): - def releases(self): + def releases(self, flat=True): res = self.get_queryset().values('param_release').distinct() - return res.values_list('param_release', flat=True) + if flat: + return res.values_list('param_release', flat=True) + else: + return res.values('param_release') - def projects(self, release): - res = self.get_queryset().filter(param_release=release).distinct() - return res + def release_uuids_by_project(self, release, project, flat=True): + res = self.get_queryset().filter( + param_release=release, projectname__startswith=project).distinct() + if flat: + return res.values_list('tag', flat=True) + else: + return res.values('tag') + + def projects_by_uuid(self, release, project, uuid, flat=True): + res = self.get_queryset().filter(tag=uuid, param_release=release, + projectname__startswith=project) + if flat: + return res.order_by('-date').values_list('projectname', flat=True) + else: + return res.order_by('-date').values('projectname') class JenkinsBuildInfo(models.Model): - tag = models.CharField(max_length=32, null=True) + tag = models.CharField(max_length=64, null=True) projectname = models.CharField(max_length=100) buildnumber = models.IntegerField() date = models.DateTimeField(auto_now_add=True) diff --git a/repoapi/serializers.py b/repoapi/serializers.py index 100246d..966e584 100644 --- a/repoapi/serializers.py +++ b/repoapi/serializers.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License along # with this program. If not, see . -from django.forms import widgets from rest_framework import serializers import repoapi.models as models @@ -22,3 +21,7 @@ class JenkinsBuildInfoSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = models.JenkinsBuildInfo + + +class ReleaseListSerializer(serializers.Serializer): + param_release = serializers.CharField(max_length=50) diff --git a/repoapi/settings/dev.py b/repoapi/settings/dev.py index f7407fb..93b3439 100644 --- a/repoapi/settings/dev.py +++ b/repoapi/settings/dev.py @@ -16,7 +16,8 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os -BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +BASE_DIR = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Quick-start development settings - unsuitable for production @@ -132,7 +133,7 @@ STATICFILES_FINDERS = ( 'django_assets.finders.AssetsFinder', ) -STATIC_ROOT= os.path.join(BASE_DIR,'static_media/') +STATIC_ROOT = os.path.join(BASE_DIR, 'static_media/') TEMPLATE_DIRS = ( 'repoapi/templates', diff --git a/repoapi/settings/prod.py b/repoapi/settings/prod.py index efcb6b0..9492406 100644 --- a/repoapi/settings/prod.py +++ b/repoapi/settings/prod.py @@ -16,7 +16,8 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os -BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +BASE_DIR = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) VAR_DIR = '/var/lib/repoapi' if not os.path.exists(VAR_DIR): @@ -123,7 +124,7 @@ STATICFILES_FINDERS = ( 'django_assets.finders.AssetsFinder', ) -STATIC_ROOT= os.path.join(BASE_DIR,'static_media/') +STATIC_ROOT = os.path.join(BASE_DIR, 'static_media/') TEMPLATE_DIRS = ( 'repoapi/templates', diff --git a/repoapi/test/test_model_queries.py b/repoapi/test/test_model_queries.py new file mode 100644 index 0000000..d664057 --- /dev/null +++ b/repoapi/test/test_model_queries.py @@ -0,0 +1,45 @@ +# Copyright (C) 2015 The Sipwise Team - http://sipwise.com + +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. + +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . + +from django.test import TestCase +from repoapi.models import JenkinsBuildInfo + + +class JBIQueriesTestCase(TestCase): + fixtures = ['test_model_queries.json'] + + def test_releases(self): + releases = JenkinsBuildInfo.objects.releases() + self.assertItemsEqual(releases, ['mr3.1-fake', ]) + + def test_release_uuids_by_project(self): + projects = ['fake', ] + uuids_ok = dict() + uuids = dict() + + uuids_ok['fake'] = ['UUID1', 'UUID0'] + for p in projects: + uuids[p] = JenkinsBuildInfo.objects.release_uuids_by_project( + 'mr3.1-fake', p) + self.assertItemsEqual(uuids_ok[p], uuids[p]) + + def test_projects_by_uuid(self): + projects = JenkinsBuildInfo.objects.projects_by_uuid( + 'mr3.1-fake', 'fake', 'UUID0') + self.assertItemsEqual(['fake-get-code', ], projects) + projects = JenkinsBuildInfo.objects.projects_by_uuid( + 'mr3.1-fake', 'fake', 'UUID1') + self.assertItemsEqual( + ['fake-get-code', 'fake-source-tests', 'fake-source'], projects) diff --git a/repoapi/urls.py b/repoapi/urls.py index 56936cc..31cafcf 100644 --- a/repoapi/urls.py +++ b/repoapi/urls.py @@ -26,6 +26,16 @@ api_patterns = [ url(r'^jenkinsbuildinfo/(?P[0-9]+)/$', views.JenkinsBuildInfoDetail.as_view(), name='jenkinsbuildinfo-detail'), + url(r'^release/$', + views.ReleaseList.as_view(), + name='release-list'), + url(r'^release/(?P[\w\d\.-]+)/(?P[\w\d\.-]+)/$', + views.ProjectUUIDList.as_view(), + name='projectuuid-list'), + url(r'^release/(?P[\w\d\.-]+)' + '/(?P[\w\d\.-]+)/(?P[\w\d-]+)/$', + views.UUIDInfoList.as_view(), + name='uuidinfo-list'), ] api_patterns = format_suffix_patterns(api_patterns) diff --git a/repoapi/views.py b/repoapi/views.py index 4ed1424..b0e3d35 100644 --- a/repoapi/views.py +++ b/repoapi/views.py @@ -13,12 +13,13 @@ # You should have received a copy of the GNU General Public License along # with this program. If not, see . -from repoapi import models, serializers -from rest_framework import filters +from . import serializers +from .models import JenkinsBuildInfo as jbi from rest_framework import generics from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework.reverse import reverse +from rest_framework.views import APIView import django_filters @@ -27,23 +28,46 @@ def api_root(request, format=None): return Response({ 'jenkinsbuildinfo': reverse('jenkinsbuildinfo-list', request=request, format=format), + 'release': reverse('release-list', + request=request, format=format), }) class JenkinsBuildInfoFilter(django_filters.FilterSet): class Meta: - model = models.JenkinsBuildInfo - fields = ['tag', 'projectname', 'date'] + model = jbi + fields = ['tag', 'projectname', 'param_release', 'date'] order_by = ['-date', ] class JenkinsBuildInfoList(generics.ListCreateAPIView): - queryset = models.JenkinsBuildInfo.objects.all() + queryset = jbi.objects.all() serializer_class = serializers.JenkinsBuildInfoSerializer filter_class = JenkinsBuildInfoFilter class JenkinsBuildInfoDetail(generics.RetrieveUpdateDestroyAPIView): - queryset = models.JenkinsBuildInfo.objects.all() + queryset = jbi.objects.all() serializer_class = serializers.JenkinsBuildInfoSerializer + + +class ReleaseList(generics.ListAPIView): + queryset = jbi.objects.releases(flat=False) + serializer_class = serializers.ReleaseListSerializer + + +class ProjectUUIDList(APIView): + + def get(self, request, release, project, format=None): + res = jbi.objects.release_uuids_by_project( + release, project, flat=False) + return Response(res) + + +class UUIDInfoList(APIView): + + def get(self, request, release, project, uuid, format=None): + res = jbi.objects.projects_by_uuid( + release, project, uuid, flat=False) + return Response(res)