summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliot Smith <elliot.smith@intel.com>2016-01-15 13:01:03 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-01-15 16:30:01 +0000
commit059a274aa96ced872156806936f887969980dda6 (patch)
tree8494b689ce4ad1226fbf79b67b2e9189bf8640d5
parent4103e0cb748888d83a5a892b5a022d633846bce8 (diff)
downloadpoky-059a274aa96ced872156806936f887969980dda6.tar.gz
bitbake: toastergui: fix error and warning counts for builds
The error and warning counts displayed for builds were counts of the errors and warnings objects associated with a build. Because these values were being derived on the fly, it was not possible to sort by them. Previously, the 3rd party django-aggregate-if library was used to add aggregate fields to Build objects and should then have been used to populate the "all builds" and "project builds" tables. However, at some point the templates had changed so that the error and warning counts were coming from the properties on the Build model and not from these aggregates. This meant that it was not possible to sort by these fields. Django 1.8 supports conditional aggregates in annotation fields on querysets. This means we can remove django-aggregate-if, use the new Django 1.8 feature to derive errors_no and warnings_no fields as annotations, then use those annotation fields in the templates. This makes the "builds" tables sortable again. [YOCTO #8738] (Bitbake rev: 9be7c5c18b325f6ed40bc431ac408db242007eb1) Signed-off-by: Elliot Smith <elliot.smith@intel.com> Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--bitbake/lib/toaster/contrib/django-aggregate-if-master/.gitignore10
-rw-r--r--bitbake/lib/toaster/contrib/django-aggregate-if-master/.travis.yml50
-rw-r--r--bitbake/lib/toaster/contrib/django-aggregate-if-master/LICENSE21
-rw-r--r--bitbake/lib/toaster/contrib/django-aggregate-if-master/README.rst156
-rw-r--r--bitbake/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py164
-rwxr-xr-xbitbake/lib/toaster/contrib/django-aggregate-if-master/runtests.py48
-rw-r--r--bitbake/lib/toaster/contrib/django-aggregate-if-master/setup.py33
-rw-r--r--bitbake/lib/toaster/contrib/django-aggregate-if-master/tox.ini198
-rw-r--r--bitbake/lib/toaster/toastergui/tables.py40
-rw-r--r--bitbake/lib/toaster/toastermain/settings.py9
10 files changed, 27 insertions, 702 deletions
diff --git a/bitbake/lib/toaster/contrib/django-aggregate-if-master/.gitignore b/bitbake/lib/toaster/contrib/django-aggregate-if-master/.gitignore
deleted file mode 100644
index c45652d29b..0000000000
--- a/bitbake/lib/toaster/contrib/django-aggregate-if-master/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
1*.pyc
2*.swp
3*.swo
4*.kpf
5*.egg-info/
6.idea
7.tox
8tmp/
9dist/
10.DS_Store
diff --git a/bitbake/lib/toaster/contrib/django-aggregate-if-master/.travis.yml b/bitbake/lib/toaster/contrib/django-aggregate-if-master/.travis.yml
deleted file mode 100644
index a920f3945c..0000000000
--- a/bitbake/lib/toaster/contrib/django-aggregate-if-master/.travis.yml
+++ /dev/null
@@ -1,50 +0,0 @@
1language: python
2python:
3 - "2.7"
4 - "3.4"
5services:
6 - mysql
7 - postgresql
8env:
9 - DJANGO=1.4 DB=sqlite
10 - DJANGO=1.4 DB=mysql
11 - DJANGO=1.4 DB=postgres
12 - DJANGO=1.5 DB=sqlite
13 - DJANGO=1.5 DB=mysql
14 - DJANGO=1.5 DB=postgres
15 - DJANGO=1.6 DB=sqlite
16 - DJANGO=1.6 DB=mysql
17 - DJANGO=1.6 DB=postgres
18 - DJANGO=1.7 DB=sqlite
19 - DJANGO=1.7 DB=mysql
20 - DJANGO=1.7 DB=postgres
21
22matrix:
23 exclude:
24 - python: "3.4"
25 env: DJANGO=1.4 DB=sqlite
26 - python: "3.4"
27 env: DJANGO=1.4 DB=mysql
28 - python: "3.4"
29 env: DJANGO=1.4 DB=postgres
30 - python: "3.4"
31 env: DJANGO=1.5 DB=sqlite
32 - python: "3.4"
33 env: DJANGO=1.5 DB=mysql
34 - python: "3.4"
35 env: DJANGO=1.5 DB=postgres
36 - python: "3.4"
37 env: DJANGO=1.6 DB=mysql
38 - python: "3.4"
39 env: DJANGO=1.7 DB=mysql
40
41before_script:
42 - mysql -e 'create database aggregation;'
43 - psql -c 'create database aggregation;' -U postgres
44install:
45 - pip install six
46 - if [ "$DB" == "mysql" ]; then pip install mysql-python; fi
47 - if [ "$DB" == "postgres" ]; then pip install psycopg2; fi
48 - pip install -q Django==$DJANGO --use-mirrors
49script:
50 - ./runtests.py --settings=tests.test_$DB
diff --git a/bitbake/lib/toaster/contrib/django-aggregate-if-master/LICENSE b/bitbake/lib/toaster/contrib/django-aggregate-if-master/LICENSE
deleted file mode 100644
index 6b7c3b174e..0000000000
--- a/bitbake/lib/toaster/contrib/django-aggregate-if-master/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
1The MIT License
2
3Copyright (c) 2012 Henrique Bastos
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE.
diff --git a/bitbake/lib/toaster/contrib/django-aggregate-if-master/README.rst b/bitbake/lib/toaster/contrib/django-aggregate-if-master/README.rst
deleted file mode 100644
index 739d4daccf..0000000000
--- a/bitbake/lib/toaster/contrib/django-aggregate-if-master/README.rst
+++ /dev/null
@@ -1,156 +0,0 @@
1Django Aggregate If: Condition aggregates for Django
2====================================================
3
4.. image:: https://travis-ci.org/henriquebastos/django-aggregate-if.png?branch=master
5 :target: https://travis-ci.org/henriquebastos/django-aggregate-if
6 :alt: Test Status
7
8.. image:: https://landscape.io/github/henriquebastos/django-aggregate-if/master/landscape.png
9 :target: https://landscape.io/github/henriquebastos/django-aggregate-if/master
10 :alt: Code Helth
11
12.. image:: https://pypip.in/v/django-aggregate-if/badge.png
13 :target: https://crate.io/packages/django-aggregate-if/
14 :alt: Latest PyPI version
15
16.. image:: https://pypip.in/d/django-aggregate-if/badge.png
17 :target: https://crate.io/packages/django-aggregate-if/
18 :alt: Number of PyPI downloads
19
20*Aggregate-if* adds conditional aggregates to Django.
21
22Conditional aggregates can help you reduce the ammount of queries to obtain
23aggregated information, like statistics for example.
24
25Imagine you have a model ``Offer`` like this one:
26
27.. code-block:: python
28
29 class Offer(models.Model):
30 sponsor = models.ForeignKey(User)
31 price = models.DecimalField(max_digits=9, decimal_places=2)
32 status = models.CharField(max_length=30)
33 expire_at = models.DateField(null=True, blank=True)
34 created_at = models.DateTimeField(auto_now_add=True)
35 updated_at = models.DateTimeField(auto_now=True)
36
37 OPEN = "OPEN"
38 REVOKED = "REVOKED"
39 PAID = "PAID"
40
41Let's say you want to know:
42
43#. How many offers exists in total;
44#. How many of them are OPEN, REVOKED or PAID;
45#. How much money was offered in total;
46#. How much money is in OPEN, REVOKED and PAID offers;
47
48To get these informations, you could query:
49
50.. code-block:: python
51
52 from django.db.models import Count, Sum
53
54 Offer.objects.count()
55 Offer.objects.filter(status=Offer.OPEN).aggregate(Count('pk'))
56 Offer.objects.filter(status=Offer.REVOKED).aggregate(Count('pk'))
57 Offer.objects.filter(status=Offer.PAID).aggregate(Count('pk'))
58 Offer.objects.aggregate(Sum('price'))
59 Offer.objects.filter(status=Offer.OPEN).aggregate(Sum('price'))
60 Offer.objects.filter(status=Offer.REVOKED).aggregate(Sum('price'))
61 Offer.objects.filter(status=Offer.PAID).aggregate(Sum('price'))
62
63In this case, **8 queries** were needed to retrieve the desired information.
64
65With conditional aggregates you can get it all with only **1 query**:
66
67.. code-block:: python
68
69 from django.db.models import Q
70 from aggregate_if import Count, Sum
71
72 Offer.objects.aggregate(
73 pk__count=Count('pk'),
74 pk__open__count=Count('pk', only=Q(status=Offer.OPEN)),
75 pk__revoked__count=Count('pk', only=Q(status=Offer.REVOKED)),
76 pk__paid__count=Count('pk', only=Q(status=Offer.PAID)),
77 pk__sum=Sum('price'),
78 pk__open__sum=Sum('price', only=Q(status=Offer.OPEN)),
79 pk__revoked__sum=Sum('price'), only=Q(status=Offer.REVOKED)),
80 pk__paid__sum=Sum('price'), only=Q(status=Offer.PAID))
81 )
82
83Installation
84------------
85
86*Aggregate-if* works with Django 1.4, 1.5, 1.6 and 1.7.
87
88To install it, simply:
89
90.. code-block:: bash
91
92 $ pip install django-aggregate-if
93
94Inspiration
95-----------
96
97There is a 5 years old `ticket 11305`_ that will (*hopefully*) implement this feature into
98Django 1.8.
99
100Using Django 1.6, I still wanted to avoid creating custom queries for very simple
101conditional aggregations. So I've cherry picked those ideas and others from the
102internet and built this library.
103
104This library uses the same API and tests proposed on `ticket 11305`_, so when the
105new feature is available you can easily replace ``django-aggregate-if``.
106
107Limitations
108-----------
109
110Conditions involving joins with aliases are not supported yet. If you want to
111help adding this feature, you're welcome to check the `first issue`_.
112
113Contributors
114------------
115
116* `Henrique Bastos <http://github.com/henriquebastos>`_
117* `Iuri de Silvio <https://github.com/iurisilvio>`_
118* `Hampus Stjernhav <https://github.com/champ>`_
119* `Bradley Martsberger <https://github.com/martsberger>`_
120* `Markus Bertheau <https://github.com/mbertheau>`_
121* `end0 <https://github.com/end0>`_
122* `Scott Sexton <https://github.com/scottsexton>`_
123* `Mauler <https://github.com/mauler>`_
124* `trbs <https://github.com/trbs>`_
125
126Changelog
127---------
128
1290.5
130 - Support for Django 1.7
131
1320.4
133 - Use tox to run tests.
134 - Add support for Django 1.6.
135 - Add support for Python3.
136 - The ``only`` parameter now freely supports joins independent of the main query.
137 - Adds support for alias relabeling permitting excludes and updates with aggregates filtered on remote foreign key relations.
138
1390.3.1
140 - Fix quotation escaping.
141 - Fix boolean casts on Postgres.
142
1430.2
144 - Fix postgres issue with LIKE conditions.
145
1460.1
147 - Initial release.
148
149
150License
151=======
152
153The MIT License.
154
155.. _ticket 11305: https://code.djangoproject.com/ticket/11305
156.. _first issue: https://github.com/henriquebastos/django-aggregate-if/issues/1
diff --git a/bitbake/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py b/bitbake/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
deleted file mode 100644
index d5f3427170..0000000000
--- a/bitbake/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
+++ /dev/null
@@ -1,164 +0,0 @@
1# coding: utf-8
2'''
3Implements conditional aggregates.
4
5This code was based on the work of others found on the internet:
6
71. http://web.archive.org/web/20101115170804/http://www.voteruniverse.com/Members/jlantz/blog/conditional-aggregates-in-django
82. https://code.djangoproject.com/ticket/11305
93. https://groups.google.com/forum/?fromgroups=#!topic/django-users/cjzloTUwmS0
104. https://groups.google.com/forum/?fromgroups=#!topic/django-users/vVprMpsAnPo
11'''
12from __future__ import unicode_literals
13from django.utils import six
14import django
15from django.db.models.aggregates import Aggregate as DjangoAggregate
16from django.db.models.sql.aggregates import Aggregate as DjangoSqlAggregate
17
18
19VERSION = django.VERSION[:2]
20
21
22class SqlAggregate(DjangoSqlAggregate):
23 conditional_template = '%(function)s(CASE WHEN %(condition)s THEN %(field)s ELSE null END)'
24
25 def __init__(self, col, source=None, is_summary=False, condition=None, **extra):
26 super(SqlAggregate, self).__init__(col, source, is_summary, **extra)
27 self.condition = condition
28
29 def relabel_aliases(self, change_map):
30 if VERSION < (1, 7):
31 super(SqlAggregate, self).relabel_aliases(change_map)
32 if self.has_condition:
33 condition_change_map = dict((k, v) for k, v in \
34 change_map.items() if k in self.condition.query.alias_map
35 )
36 self.condition.query.change_aliases(condition_change_map)
37
38 def relabeled_clone(self, change_map):
39 self.relabel_aliases(change_map)
40 return super(SqlAggregate, self).relabeled_clone(change_map)
41
42 def as_sql(self, qn, connection):
43 if self.has_condition:
44 self.sql_template = self.conditional_template
45 self.extra['condition'] = self._condition_as_sql(qn, connection)
46
47 return super(SqlAggregate, self).as_sql(qn, connection)
48
49 @property
50 def has_condition(self):
51 # Warning: bool(QuerySet) will hit the database
52 return self.condition is not None
53
54 def _condition_as_sql(self, qn, connection):
55 '''
56 Return sql for condition.
57 '''
58 def escape(value):
59 if isinstance(value, bool):
60 value = str(int(value))
61 if isinstance(value, six.string_types):
62 # Escape params used with LIKE
63 if '%' in value:
64 value = value.replace('%', '%%')
65 # Escape single quotes
66 if "'" in value:
67 value = value.replace("'", "''")
68 # Add single quote to text values
69 value = "'" + value + "'"
70 return value
71
72 sql, param = self.condition.query.where.as_sql(qn, connection)
73 param = map(escape, param)
74
75 return sql % tuple(param)
76
77
78class SqlSum(SqlAggregate):
79 sql_function = 'SUM'
80
81
82class SqlCount(SqlAggregate):
83 is_ordinal = True
84 sql_function = 'COUNT'
85 sql_template = '%(function)s(%(distinct)s%(field)s)'
86 conditional_template = '%(function)s(%(distinct)sCASE WHEN %(condition)s THEN %(field)s ELSE null END)'
87
88 def __init__(self, col, distinct=False, **extra):
89 super(SqlCount, self).__init__(col, distinct=distinct and 'DISTINCT ' or '', **extra)
90
91
92class SqlAvg(SqlAggregate):
93 is_computed = True
94 sql_function = 'AVG'
95
96
97class SqlMax(SqlAggregate):
98 sql_function = 'MAX'
99
100
101class SqlMin(SqlAggregate):
102 sql_function = 'MIN'
103
104
105class Aggregate(DjangoAggregate):
106 def __init__(self, lookup, only=None, **extra):
107 super(Aggregate, self).__init__(lookup, **extra)
108 self.only = only
109 self.condition = None
110
111 def _get_fields_from_Q(self, q):
112 fields = []
113 for child in q.children:
114 if hasattr(child, 'children'):
115 fields.extend(self._get_fields_from_Q(child))
116 else:
117 fields.append(child)
118 return fields
119
120 def add_to_query(self, query, alias, col, source, is_summary):
121 if self.only:
122 self.condition = query.model._default_manager.filter(self.only)
123 for child in self._get_fields_from_Q(self.only):
124 field_list = child[0].split('__')
125 # Pop off the last field if it's a query term ('gte', 'contains', 'isnull', etc.)
126 if field_list[-1] in query.query_terms:
127 field_list.pop()
128 # setup_joins have different returns in Django 1.5 and 1.6, but the order of what we need remains.
129 result = query.setup_joins(field_list, query.model._meta, query.get_initial_alias(), None)
130 join_list = result[3]
131
132 fname = 'promote_alias_chain' if VERSION < (1, 5) else 'promote_joins'
133 args = (join_list, True) if VERSION < (1, 7) else (join_list,)
134
135 promote = getattr(query, fname)
136 promote(*args)
137
138 aggregate = self.sql_klass(col, source=source, is_summary=is_summary, condition=self.condition, **self.extra)
139 query.aggregates[alias] = aggregate
140
141
142class Sum(Aggregate):
143 name = 'Sum'
144 sql_klass = SqlSum
145
146
147class Count(Aggregate):
148 name = 'Count'
149 sql_klass = SqlCount
150
151
152class Avg(Aggregate):
153 name = 'Avg'
154 sql_klass = SqlAvg
155
156
157class Max(Aggregate):
158 name = 'Max'
159 sql_klass = SqlMax
160
161
162class Min(Aggregate):
163 name = 'Min'
164 sql_klass = SqlMin
diff --git a/bitbake/lib/toaster/contrib/django-aggregate-if-master/runtests.py b/bitbake/lib/toaster/contrib/django-aggregate-if-master/runtests.py
deleted file mode 100755
index 2e55864e3a..0000000000
--- a/bitbake/lib/toaster/contrib/django-aggregate-if-master/runtests.py
+++ /dev/null
@@ -1,48 +0,0 @@
1#!/usr/bin/env python
2
3import os
4import sys
5from optparse import OptionParser
6
7
8def parse_args():
9 parser = OptionParser()
10 parser.add_option('-s', '--settings', help='Define settings.')
11 parser.add_option('-t', '--unittest', help='Define which test to run. Default all.')
12 options, args = parser.parse_args()
13
14 if not options.settings:
15 parser.print_help()
16 sys.exit(1)
17
18 if not options.unittest:
19 options.unittest = ['aggregation']
20
21 return options
22
23
24def get_runner(settings_module):
25 '''
26 Asks Django for the TestRunner defined in settings or the default one.
27 '''
28 os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
29
30 import django
31 from django.test.utils import get_runner
32 from django.conf import settings
33
34 if hasattr(django, 'setup'):
35 django.setup()
36
37 return get_runner(settings)
38
39
40def runtests():
41 options = parse_args()
42 TestRunner = get_runner(options.settings)
43 runner = TestRunner(verbosity=1, interactive=True, failfast=False)
44 sys.exit(runner.run_tests([]))
45
46
47if __name__ == '__main__':
48 runtests()
diff --git a/bitbake/lib/toaster/contrib/django-aggregate-if-master/setup.py b/bitbake/lib/toaster/contrib/django-aggregate-if-master/setup.py
deleted file mode 100644
index aed3db14d1..0000000000
--- a/bitbake/lib/toaster/contrib/django-aggregate-if-master/setup.py
+++ /dev/null
@@ -1,33 +0,0 @@
1# coding: utf-8
2from setuptools import setup
3import os
4
5
6setup(name='django-aggregate-if',
7 version='0.5',
8 description='Conditional aggregates for Django, just like the famous SumIf in Excel.',
9 long_description=open(os.path.join(os.path.dirname(__file__), "README.rst")).read(),
10 author="Henrique Bastos", author_email="henrique@bastos.net",
11 license="MIT",
12 py_modules=['aggregate_if'],
13 install_requires=[
14 'six>=1.6.1',
15 ],
16 zip_safe=False,
17 platforms='any',
18 include_package_data=True,
19 classifiers=[
20 'Development Status :: 5 - Production/Stable',
21 'Framework :: Django',
22 'Intended Audience :: Developers',
23 'License :: OSI Approved :: MIT License',
24 'Natural Language :: English',
25 'Operating System :: OS Independent',
26 'Programming Language :: Python',
27 'Programming Language :: Python :: 2.7',
28 'Programming Language :: Python :: 3',
29 'Topic :: Database',
30 'Topic :: Software Development :: Libraries',
31 ],
32 url='http://github.com/henriquebastos/django-aggregate-if/',
33)
diff --git a/bitbake/lib/toaster/contrib/django-aggregate-if-master/tox.ini b/bitbake/lib/toaster/contrib/django-aggregate-if-master/tox.ini
deleted file mode 100644
index 78beb148aa..0000000000
--- a/bitbake/lib/toaster/contrib/django-aggregate-if-master/tox.ini
+++ /dev/null
@@ -1,198 +0,0 @@
1[tox]
2envlist =
3 py27-django1.4-sqlite,
4 py27-django1.4-postgres,
5 py27-django1.4-mysql,
6
7 py27-django1.5-sqlite,
8 py27-django1.5-postgres,
9 py27-django1.5-mysql,
10
11 py27-django1.6-sqlite,
12 py27-django1.6-postgres,
13 py27-django1.6-mysql,
14
15 py27-django1.7-sqlite,
16 py27-django1.7-postgres,
17 py27-django1.7-mysql,
18
19 py34-django1.6-sqlite,
20 py34-django1.6-postgres,
21 #py34-django1.6-mysql
22
23 py34-django1.7-sqlite,
24 py34-django1.7-postgres,
25 #py34-django1.7-mysql
26
27[testenv]
28whitelist_externals=
29 mysql
30 psql
31
32# Python 2.7
33# Django 1.4
34[testenv:py27-django1.4-sqlite]
35basepython = python2.7
36deps =
37 django==1.4
38commands = python runtests.py --settings tests.test_sqlite
39
40[testenv:py27-django1.4-postgres]
41basepython = python2.7
42deps =
43 django==1.4
44 psycopg2
45commands =
46 psql -c 'create database aggregation;' postgres
47 python runtests.py --settings tests.test_postgres
48 psql -c 'drop database aggregation;' postgres
49
50[testenv:py27-django1.4-mysql]
51basepython = python2.7
52deps =
53 django==1.4
54 mysql-python
55commands =
56 mysql -e 'create database aggregation;'
57 python runtests.py --settings tests.test_mysql
58 mysql -e 'drop database aggregation;'
59
60# Django 1.5
61[testenv:py27-django1.5-sqlite]
62basepython = python2.7
63deps =
64 django==1.5
65commands = python runtests.py --settings tests.test_sqlite
66
67[testenv:py27-django1.5-postgres]
68basepython = python2.7
69deps =
70 django==1.5
71 psycopg2
72commands =
73 psql -c 'create database aggregation;' postgres
74 python runtests.py --settings tests.test_postgres
75 psql -c 'drop database aggregation;' postgres
76
77[testenv:py27-django1.5-mysql]
78basepython = python2.7
79deps =
80 django==1.5
81 mysql-python
82commands =
83 mysql -e 'create database aggregation;'
84 python runtests.py --settings tests.test_mysql
85 mysql -e 'drop database aggregation;'
86
87# Django 1.6
88[testenv:py27-django1.6-sqlite]
89basepython = python2.7
90deps =
91 django==1.6
92commands = python runtests.py --settings tests.test_sqlite
93
94[testenv:py27-django1.6-postgres]
95basepython = python2.7
96deps =
97 django==1.6
98 psycopg2
99commands =
100 psql -c 'create database aggregation;' postgres
101 python runtests.py --settings tests.test_postgres
102 psql -c 'drop database aggregation;' postgres
103
104[testenv:py27-django1.6-mysql]
105basepython = python2.7
106deps =
107 django==1.6
108 mysql-python
109commands =
110 mysql -e 'create database aggregation;'
111 python runtests.py --settings tests.test_mysql
112 mysql -e 'drop database aggregation;'
113
114
115# Python 2.7 and Django 1.7
116[testenv:py27-django1.7-sqlite]
117basepython = python2.7
118deps =
119 django==1.7
120commands = python runtests.py --settings tests.test_sqlite
121
122[testenv:py27-django1.7-postgres]
123basepython = python2.7
124deps =
125 django==1.7
126 psycopg2
127commands =
128 psql -c 'create database aggregation;' postgres
129 python runtests.py --settings tests.test_postgres
130 psql -c 'drop database aggregation;' postgres
131
132[testenv:py27-django1.7-mysql]
133basepython = python2.7
134deps =
135 django==1.7
136 mysql-python
137commands =
138 mysql -e 'create database aggregation;'
139 python runtests.py --settings tests.test_mysql
140 mysql -e 'drop database aggregation;'
141
142
143# Python 3.4
144# Django 1.6
145[testenv:py34-django1.6-sqlite]
146basepython = python3.4
147deps =
148 django==1.6
149commands = python runtests.py --settings tests.test_sqlite
150
151[testenv:py34-django1.6-postgres]
152basepython = python3.4
153deps =
154 django==1.6
155 psycopg2
156commands =
157 psql -c 'create database aggregation;' postgres
158 python runtests.py --settings tests.test_postgres
159 psql -c 'drop database aggregation;' postgres
160
161[testenv:py34-django1.6-mysql]
162basepython = python3.4
163deps =
164 django==1.6
165 mysql-python3
166commands =
167 mysql -e 'create database aggregation;'
168 python runtests.py --settings tests.test_mysql
169 mysql -e 'drop database aggregation;'
170
171
172# Python 3.4
173# Django 1.7
174[testenv:py34-django1.7-sqlite]
175basepython = python3.4
176deps =
177 django==1.7
178commands = python runtests.py --settings tests.test_sqlite
179
180[testenv:py34-django1.7-postgres]
181basepython = python3.4
182deps =
183 django==1.7
184 psycopg2
185commands =
186 psql -c 'create database aggregation;' postgres
187 python runtests.py --settings tests.test_postgres
188 psql -c 'drop database aggregation;' postgres
189
190[testenv:py34-django1.7-mysql]
191basepython = python3.4
192deps =
193 django==1.7
194 mysql-python3
195commands =
196 mysql -e 'create database aggregation;'
197 python runtests.py --settings tests.test_mysql
198 mysql -e 'drop database aggregation;'
diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py
index 14077e10ae..227973114c 100644
--- a/bitbake/lib/toaster/toastergui/tables.py
+++ b/bitbake/lib/toaster/toastergui/tables.py
@@ -23,7 +23,7 @@ from toastergui.widgets import ToasterTable
23from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project 23from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project
24from orm.models import CustomImageRecipe, Package, Build, LogMessage, Task 24from orm.models import CustomImageRecipe, Package, Build, LogMessage, Task
25from orm.models import ProjectTarget 25from orm.models import ProjectTarget
26from django.db.models import Q, Max, Count 26from django.db.models import Q, Max, Count, When, Case, Value, IntegerField
27from django.conf.urls import url 27from django.conf.urls import url
28from django.core.urlresolvers import reverse, resolve 28from django.core.urlresolvers import reverse, resolve
29from django.http import HttpResponse 29from django.http import HttpResponse
@@ -927,6 +927,13 @@ class BuildsTable(ToasterTable):
927 return context 927 return context
928 928
929 def setup_queryset(self, *args, **kwargs): 929 def setup_queryset(self, *args, **kwargs):
930 """
931 The queryset is annotated so that it can be sorted by number of
932 errors and number of warnings; but note that the criteria for
933 finding the log messages to populate these fields should match those
934 used in the Build model (orm/models.py) to populate the errors and
935 warnings properties
936 """
930 queryset = self.get_builds() 937 queryset = self.get_builds()
931 938
932 # don't include in progress builds 939 # don't include in progress builds
@@ -935,20 +942,27 @@ class BuildsTable(ToasterTable):
935 # sort 942 # sort
936 queryset = queryset.order_by(self.default_orderby) 943 queryset = queryset.order_by(self.default_orderby)
937 944
938 # annotate with number of ERROR and EXCEPTION log messages 945 # annotate with number of ERROR, EXCEPTION and CRITICAL log messages
946 criteria = (Q(logmessage__level=LogMessage.ERROR) |
947 Q(logmessage__level=LogMessage.EXCEPTION) |
948 Q(logmessage__level=LogMessage.CRITICAL))
949
939 queryset = queryset.annotate( 950 queryset = queryset.annotate(
940 errors_no = Count( 951 errors_no=Count(
941 'logmessage', 952 Case(
942 only = Q(logmessage__level=LogMessage.ERROR) | 953 When(criteria, then=Value(1)),
943 Q(logmessage__level=LogMessage.EXCEPTION) 954 output_field=IntegerField()
955 )
944 ) 956 )
945 ) 957 )
946 958
947 # annotate with number of WARNING log messages 959 # annotate with number of WARNING log messages
948 queryset = queryset.annotate( 960 queryset = queryset.annotate(
949 warnings_no = Count( 961 warnings_no=Count(
950 'logmessage', 962 Case(
951 only = Q(logmessage__level=LogMessage.WARNING) 963 When(logmessage__level=LogMessage.WARNING, then=Value(1)),
964 output_field=IntegerField()
965 )
952 ) 966 )
953 ) 967 )
954 968
@@ -1020,17 +1034,17 @@ class BuildsTable(ToasterTable):
1020 ''' 1034 '''
1021 1035
1022 errors_template = ''' 1036 errors_template = '''
1023 {% if data.errors.count %} 1037 {% if data.errors_no %}
1024 <a class="errors.count error" href="{% url "builddashboard" data.id %}#errors"> 1038 <a class="errors.count error" href="{% url "builddashboard" data.id %}#errors">
1025 {{data.errors.count}} error{{data.errors.count|pluralize}} 1039 {{data.errors_no}} error{{data.errors_no|pluralize}}
1026 </a> 1040 </a>
1027 {% endif %} 1041 {% endif %}
1028 ''' 1042 '''
1029 1043
1030 warnings_template = ''' 1044 warnings_template = '''
1031 {% if data.warnings.count %} 1045 {% if data.warnings_no %}
1032 <a class="warnings.count warning" href="{% url "builddashboard" data.id %}#warnings"> 1046 <a class="warnings.count warning" href="{% url "builddashboard" data.id %}#warnings">
1033 {{data.warnings.count}} warning{{data.warnings.count|pluralize}} 1047 {{data.warnings_no}} warning{{data.warnings_no|pluralize}}
1034 </a> 1048 </a>
1035 {% endif %} 1049 {% endif %}
1036 ''' 1050 '''
diff --git a/bitbake/lib/toaster/toastermain/settings.py b/bitbake/lib/toaster/toastermain/settings.py
index 74103f3063..c4f3d6bfff 100644
--- a/bitbake/lib/toaster/toastermain/settings.py
+++ b/bitbake/lib/toaster/toastermain/settings.py
@@ -399,12 +399,3 @@ class InvalidString(str):
399 "Undefined variable or unknown value for: \"%s\"" % other) 399 "Undefined variable or unknown value for: \"%s\"" % other)
400 400
401TEMPLATE_STRING_IF_INVALID = InvalidString("%s") 401TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
402
403import sys
404sys.path.append(
405 os.path.join(
406 os.path.join(
407 os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
408 "contrib"),
409 "django-aggregate-if-master")
410 )