diff options
author | Alexandru DAMIAN <alexandru.damian@intel.com> | 2015-01-23 17:36:14 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-01-29 22:11:35 +0000 |
commit | 4f7182775cfa39c589e2e4693b1769127d7dd4d4 (patch) | |
tree | f62dfeba0e4a021fcbcb64c05e873b9da3458921 /bitbake/lib/toaster/toastergui/views.py | |
parent | fefef50e5474da740f926ef635676c4d5f24b9b7 (diff) | |
download | poky-4f7182775cfa39c589e2e4693b1769127d7dd4d4.tar.gz |
bitbake: toastergui: update project build listing
We update the build listings in the project mode to enable
proper display and selection of build requests that do not have
an actual build object because the bitbake process did not start.
We add a page to display error details for build requests that
did not start a build.
Fixing errors and misspelling in build sections.
Sorting by "timespent" is disabled for build-listing pages.
[YOCTO #7165]
[YOCTO #7156]
[YOCTO #7196]
[YOCTO #7188]
(Bitbake rev: ee13bf45cecd6a0132d724b3206a6f4515669496)
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui/views.py')
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 103 |
1 files changed, 66 insertions, 37 deletions
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index e718ced570..6ccbf5452d 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -22,7 +22,7 @@ | |||
22 | import operator,re | 22 | import operator,re |
23 | import HTMLParser | 23 | import HTMLParser |
24 | 24 | ||
25 | from django.db.models import Q, Sum | 25 | from django.db.models import Q, Sum, Count |
26 | from django.db import IntegrityError | 26 | from django.db import IntegrityError |
27 | from django.shortcuts import render, redirect | 27 | from django.shortcuts import render, redirect |
28 | from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable | 28 | from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable |
@@ -117,7 +117,8 @@ def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs): | |||
117 | return redirect(url + "?%s" % urllib.urlencode(params), *args, **kwargs) | 117 | return redirect(url + "?%s" % urllib.urlencode(params), *args, **kwargs) |
118 | 118 | ||
119 | FIELD_SEPARATOR = ":" | 119 | FIELD_SEPARATOR = ":" |
120 | VALUE_SEPARATOR = "!" | 120 | AND_VALUE_SEPARATOR = "!" |
121 | OR_VALUE_SEPARATOR = "|" | ||
121 | DESCENDING = "-" | 122 | DESCENDING = "-" |
122 | 123 | ||
123 | def __get_q_for_val(name, value): | 124 | def __get_q_for_val(name, value): |
@@ -126,20 +127,31 @@ def __get_q_for_val(name, value): | |||
126 | if "AND" in value: | 127 | if "AND" in value: |
127 | return reduce(operator.and_, map(lambda x: __get_q_for_val(name, x), [ x for x in value.split("AND") ])) | 128 | return reduce(operator.and_, map(lambda x: __get_q_for_val(name, x), [ x for x in value.split("AND") ])) |
128 | if value.startswith("NOT"): | 129 | if value.startswith("NOT"): |
129 | kwargs = { name : value.strip("NOT") } | 130 | value = value[3:] |
131 | if value == 'None': | ||
132 | value = None | ||
133 | kwargs = { name : value } | ||
130 | return ~Q(**kwargs) | 134 | return ~Q(**kwargs) |
131 | else: | 135 | else: |
136 | if value == 'None': | ||
137 | value = None | ||
132 | kwargs = { name : value } | 138 | kwargs = { name : value } |
133 | return Q(**kwargs) | 139 | return Q(**kwargs) |
134 | 140 | ||
135 | def _get_filtering_query(filter_string): | 141 | def _get_filtering_query(filter_string): |
136 | 142 | ||
137 | search_terms = filter_string.split(FIELD_SEPARATOR) | 143 | search_terms = filter_string.split(FIELD_SEPARATOR) |
138 | keys = search_terms[0].split(VALUE_SEPARATOR) | 144 | and_keys = search_terms[0].split(AND_VALUE_SEPARATOR) |
139 | values = search_terms[1].split(VALUE_SEPARATOR) | 145 | and_values = search_terms[1].split(AND_VALUE_SEPARATOR) |
146 | |||
147 | and_query = [] | ||
148 | for kv in zip(and_keys, and_values): | ||
149 | or_keys = kv[0].split(OR_VALUE_SEPARATOR) | ||
150 | or_values = kv[1].split(OR_VALUE_SEPARATOR) | ||
151 | querydict = dict(zip(or_keys, or_values)) | ||
152 | and_query.append(reduce(operator.or_, map(lambda x: __get_q_for_val(x, querydict[x]), [k for k in querydict]))) | ||
140 | 153 | ||
141 | querydict = dict(zip(keys, values)) | 154 | return reduce(operator.and_, [k for k in and_query]) |
142 | return reduce(operator.and_, map(lambda x: __get_q_for_val(x, querydict[x]), [k for k in querydict])) | ||
143 | 155 | ||
144 | def _get_toggle_order(request, orderkey, reverse = False): | 156 | def _get_toggle_order(request, orderkey, reverse = False): |
145 | if reverse: | 157 | if reverse: |
@@ -169,13 +181,13 @@ def _validate_input(input, model): | |||
169 | return None, invalid | 181 | return None, invalid |
170 | 182 | ||
171 | # Check we have an equal number of terms both sides of the colon | 183 | # Check we have an equal number of terms both sides of the colon |
172 | if len(input_list[0].split(VALUE_SEPARATOR)) != len(input_list[1].split(VALUE_SEPARATOR)): | 184 | if len(input_list[0].split(AND_VALUE_SEPARATOR)) != len(input_list[1].split(AND_VALUE_SEPARATOR)): |
173 | invalid = "Not all arg names got values" | 185 | invalid = "Not all arg names got values" |
174 | return None, invalid + str(input_list) | 186 | return None, invalid + str(input_list) |
175 | 187 | ||
176 | # Check we are looking for a valid field | 188 | # Check we are looking for a valid field |
177 | valid_fields = model._meta.get_all_field_names() | 189 | valid_fields = model._meta.get_all_field_names() |
178 | for field in input_list[0].split(VALUE_SEPARATOR): | 190 | for field in input_list[0].split(AND_VALUE_SEPARATOR): |
179 | if not reduce(lambda x, y: x or y, map(lambda x: field.startswith(x), [ x for x in valid_fields ])): | 191 | if not reduce(lambda x, y: x or y, map(lambda x: field.startswith(x), [ x for x in valid_fields ])): |
180 | return None, (field, [ x for x in valid_fields ]) | 192 | return None, (field, [ x for x in valid_fields ]) |
181 | 193 | ||
@@ -216,6 +228,7 @@ def _search_tuple(request, model): | |||
216 | def _get_queryset(model, queryset, filter_string, search_term, ordering_string, ordering_secondary=''): | 228 | def _get_queryset(model, queryset, filter_string, search_term, ordering_string, ordering_secondary=''): |
217 | if filter_string: | 229 | if filter_string: |
218 | filter_query = _get_filtering_query(filter_string) | 230 | filter_query = _get_filtering_query(filter_string) |
231 | # raise Exception(filter_query) | ||
219 | queryset = queryset.filter(filter_query) | 232 | queryset = queryset.filter(filter_query) |
220 | else: | 233 | else: |
221 | queryset = queryset.all() | 234 | queryset = queryset.all() |
@@ -1780,12 +1793,13 @@ if toastermain.settings.MANAGED: | |||
1780 | # for that object type. copypasta for all needed table searches | 1793 | # for that object type. copypasta for all needed table searches |
1781 | (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest) | 1794 | (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest) |
1782 | # we don't display in-progress or deleted builds | 1795 | # we don't display in-progress or deleted builds |
1783 | queryset_all = buildrequests | 1796 | queryset_all = buildrequests.exclude(state = BuildRequest.REQ_DELETED) |
1784 | queryset_with_search = _get_queryset(BuildRequest, queryset_all, None, search_term, ordering_string, '-updated') | 1797 | queryset_all = queryset_all.annotate(Count('brerror')) |
1785 | queryset = _get_queryset(BuildRequest, queryset_all, filter_string, search_term, ordering_string, '-updated') | 1798 | queryset_with_search = _get_queryset(BuildRequest, queryset_all, filter_string, search_term, ordering_string, '-updated') |
1799 | |||
1786 | 1800 | ||
1787 | # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display | 1801 | # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display |
1788 | build_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1)) | 1802 | build_info = _build_page_range(Paginator(queryset_with_search, pagesize), request.GET.get('page', 1)) |
1789 | 1803 | ||
1790 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) | 1804 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) |
1791 | # most recent build is like projects' most recent builds, but across all projects | 1805 | # most recent build is like projects' most recent builds, but across all projects |
@@ -1842,8 +1856,8 @@ if toastermain.settings.MANAGED: | |||
1842 | 'filter' : {'class' : 'outcome', | 1856 | 'filter' : {'class' : 'outcome', |
1843 | 'label': 'Show:', | 1857 | 'label': 'Show:', |
1844 | 'options' : [ | 1858 | 'options' : [ |
1845 | ('Successful builds', 'state:' + str(BuildRequest.REQ_COMPLETED), queryset_with_search.filter(state=str(BuildRequest.REQ_COMPLETED)).count()), # this is the field search expression | 1859 | ('Successful builds', 'state:' + str(BuildRequest.REQ_COMPLETED), queryset_all.filter(state=str(BuildRequest.REQ_COMPLETED)).count()), # this is the field search expression |
1846 | ('Failed builds', 'state:'+ str(BuildRequest.REQ_FAILED), queryset_with_search.filter(state=str(BuildRequest.REQ_FAILED)).count()), | 1860 | ('Failed builds', 'state:'+ str(BuildRequest.REQ_FAILED), queryset_all.filter(state=str(BuildRequest.REQ_FAILED)).count()), |
1847 | ] | 1861 | ] |
1848 | } | 1862 | } |
1849 | }, | 1863 | }, |
@@ -1865,9 +1879,9 @@ if toastermain.settings.MANAGED: | |||
1865 | 'filter' : {'class' : 'created', | 1879 | 'filter' : {'class' : 'created', |
1866 | 'label': 'Show:', | 1880 | 'label': 'Show:', |
1867 | 'options' : [ | 1881 | 'options' : [ |
1868 | ("Today's builds" , 'created__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(created__gte=timezone.now()).count()), | 1882 | ("Today's builds" , 'created__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_all.filter(created__gte=timezone.now()).count()), |
1869 | ("Yesterday's builds", 'created__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(created__gte=(timezone.now()-timedelta(hours=24))).count()), | 1883 | ("Yesterday's builds", 'created__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_all.filter(created__gte=(timezone.now()-timedelta(hours=24))).count()), |
1870 | ("This week's builds", 'created__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(created__gte=(timezone.now()-timedelta(days=7))).count()), | 1884 | ("This week's builds", 'created__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_all.filter(created__gte=(timezone.now()-timedelta(days=7))).count()), |
1871 | ] | 1885 | ] |
1872 | } | 1886 | } |
1873 | }, | 1887 | }, |
@@ -1879,9 +1893,9 @@ if toastermain.settings.MANAGED: | |||
1879 | 'filter' : {'class' : 'updated', | 1893 | 'filter' : {'class' : 'updated', |
1880 | 'label': 'Show:', | 1894 | 'label': 'Show:', |
1881 | 'options' : [ | 1895 | 'options' : [ |
1882 | ("Today's builds", 'updated__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(updated__gte=timezone.now()).count()), | 1896 | ("Today's builds", 'updated__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=timezone.now()).count()), |
1883 | ("Yesterday's builds", 'updated__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(updated__gte=(timezone.now()-timedelta(hours=24))).count()), | 1897 | ("Yesterday's builds", 'updated__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=(timezone.now()-timedelta(hours=24))).count()), |
1884 | ("This week's builds", 'updated__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(updated__gte=(timezone.now()-timedelta(days=7))).count()), | 1898 | ("This week's builds", 'updated__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=(timezone.now()-timedelta(days=7))).count()), |
1885 | ] | 1899 | ] |
1886 | } | 1900 | } |
1887 | }, | 1901 | }, |
@@ -1890,8 +1904,10 @@ if toastermain.settings.MANAGED: | |||
1890 | 'filter' : {'class' : 'failed_tasks', | 1904 | 'filter' : {'class' : 'failed_tasks', |
1891 | 'label': 'Show:', | 1905 | 'label': 'Show:', |
1892 | 'options' : [ | 1906 | 'options' : [ |
1893 | ('Build with failed tasks', 'build__task_build__outcome:4', queryset_with_search.filter(build__task_build__outcome=4).count()), | 1907 | ('Builds with failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED, |
1894 | ('Build without failed tasks', 'build__task_build__outcome:NOT4', queryset_with_search.filter(~Q(build__task_build__outcome=4)).count()), | 1908 | queryset_all.filter(build__task_build__outcome=Task.OUTCOME_FAILED).count()), |
1909 | ('Builds without failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED, | ||
1910 | queryset_all.filter(~Q(build__task_build__outcome=Task.OUTCOME_FAILED)).count()), | ||
1895 | ] | 1911 | ] |
1896 | } | 1912 | } |
1897 | }, | 1913 | }, |
@@ -1903,8 +1919,10 @@ if toastermain.settings.MANAGED: | |||
1903 | 'filter' : {'class' : 'errors_no', | 1919 | 'filter' : {'class' : 'errors_no', |
1904 | 'label': 'Show:', | 1920 | 'label': 'Show:', |
1905 | 'options' : [ | 1921 | 'options' : [ |
1906 | ('Build with errors', 'build__errors_no__gte:1', queryset_with_search.filter(build__errors_no__gte=1).count()), | 1922 | ('Builds with errors', 'build|build__errors_no__gt:None|0', |
1907 | ('Build without errors', 'build__errors_no:0', queryset_with_search.filter(build__errors_no=0).count()), | 1923 | queryset_all.filter(Q(build=None) | Q(build__errors_no__gt=0)).count()), |
1924 | ('Builds without errors', 'build__errors_no:0', | ||
1925 | queryset_all.filter(build__errors_no=0).count()), | ||
1908 | ] | 1926 | ] |
1909 | } | 1927 | } |
1910 | }, | 1928 | }, |
@@ -1916,8 +1934,8 @@ if toastermain.settings.MANAGED: | |||
1916 | 'filter' : {'class' : 'build__warnings_no', | 1934 | 'filter' : {'class' : 'build__warnings_no', |
1917 | 'label': 'Show:', | 1935 | 'label': 'Show:', |
1918 | 'options' : [ | 1936 | 'options' : [ |
1919 | ('Build with warnings','build__warnings_no__gte:1', queryset_with_search.filter(build__warnings_no__gte=1).count()), | 1937 | ('Builds with warnings','build__warnings_no__gte:1', queryset_all.filter(build__warnings_no__gte=1).count()), |
1920 | ('Build without warnings','build__warnings_no:0', queryset_with_search.filter(build__warnings_no=0).count()), | 1938 | ('Builds without warnings','build__warnings_no:0', queryset_all.filter(build__warnings_no=0).count()), |
1921 | ] | 1939 | ] |
1922 | } | 1940 | } |
1923 | }, | 1941 | }, |
@@ -2016,7 +2034,7 @@ if toastermain.settings.MANAGED: | |||
2016 | 2034 | ||
2017 | context = { | 2035 | context = { |
2018 | "project" : prj, | 2036 | "project" : prj, |
2019 | "completedbuilds": Build.objects.filter(project = prj).exclude(outcome = Build.IN_PROGRESS), | 2037 | "completedbuilds": BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED), |
2020 | "prj" : {"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}}, | 2038 | "prj" : {"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}}, |
2021 | #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED), | 2039 | #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED), |
2022 | "builds" : _project_recent_build_list(prj), | 2040 | "builds" : _project_recent_build_list(prj), |
@@ -2061,7 +2079,7 @@ if toastermain.settings.MANAGED: | |||
2061 | try: | 2079 | try: |
2062 | if request.method != "POST": | 2080 | if request.method != "POST": |
2063 | raise BadParameterException("invalid method") | 2081 | raise BadParameterException("invalid method") |
2064 | request.session['project_id'] = pid | 2082 | request.session['project_id'] = pid |
2065 | prj = Project.objects.get(id = pid) | 2083 | prj = Project.objects.get(id = pid) |
2066 | 2084 | ||
2067 | 2085 | ||
@@ -2167,11 +2185,11 @@ if toastermain.settings.MANAGED: | |||
2167 | try: | 2185 | try: |
2168 | prj = None | 2186 | prj = None |
2169 | if request.GET.has_key('project_id'): | 2187 | if request.GET.has_key('project_id'): |
2170 | prj = Project.objects.get(pk = request.GET['project_id']) | 2188 | prj = Project.objects.get(pk = request.GET['project_id']) |
2171 | elif 'project_id' in request.session: | 2189 | elif 'project_id' in request.session: |
2172 | prj = Project.objects.get(pk = request.session['project_id']) | 2190 | prj = Project.objects.get(pk = request.session['project_id']) |
2173 | else: | 2191 | else: |
2174 | raise Exception("No valid project selected") | 2192 | raise Exception("No valid project selected") |
2175 | 2193 | ||
2176 | 2194 | ||
2177 | def _lv_to_dict(x): | 2195 | def _lv_to_dict(x): |
@@ -2819,10 +2837,10 @@ if toastermain.settings.MANAGED: | |||
2819 | 2837 | ||
2820 | vars_blacklist = { | 2838 | vars_blacklist = { |
2821 | 'DL_DR','PARALLEL_MAKE','BB_NUMBER_THREADS','SSTATE_DIR', | 2839 | 'DL_DR','PARALLEL_MAKE','BB_NUMBER_THREADS','SSTATE_DIR', |
2822 | 'BB_DISKMON_DIRS','BB_NUMBER_THREADS','CVS_PROXY_HOST','CVS_PROXY_PORT', | 2840 | 'BB_DISKMON_DIRS','BB_NUMBER_THREADS','CVS_PROXY_HOST','CVS_PROXY_PORT', |
2823 | 'DL_DIR','PARALLEL_MAKE','SSTATE_DIR','SSTATE_DIR','SSTATE_MIRRORS','TMPDIR', | 2841 | 'DL_DIR','PARALLEL_MAKE','SSTATE_DIR','SSTATE_DIR','SSTATE_MIRRORS','TMPDIR', |
2824 | 'all_proxy','ftp_proxy','http_proxy ','https_proxy' | 2842 | 'all_proxy','ftp_proxy','http_proxy ','https_proxy' |
2825 | } | 2843 | } |
2826 | 2844 | ||
2827 | vars_fstypes = { | 2845 | vars_fstypes = { |
2828 | 'btrfs','cpio','cpio.gz','cpio.lz4','cpio.lzma','cpio.xz','cramfs', | 2846 | 'btrfs','cpio','cpio.gz','cpio.lz4','cpio.lzma','cpio.xz','cramfs', |
@@ -2874,7 +2892,7 @@ if toastermain.settings.MANAGED: | |||
2874 | 2892 | ||
2875 | def projectbuilds(request, pid): | 2893 | def projectbuilds(request, pid): |
2876 | template = 'projectbuilds.html' | 2894 | template = 'projectbuilds.html' |
2877 | buildrequests = BuildRequest.objects.exclude(project_id = pid, state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) | 2895 | buildrequests = BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) |
2878 | 2896 | ||
2879 | try: | 2897 | try: |
2880 | context, pagesize, orderby = _build_list_helper(request, buildrequests) | 2898 | context, pagesize, orderby = _build_list_helper(request, buildrequests) |
@@ -3012,6 +3030,14 @@ if toastermain.settings.MANAGED: | |||
3012 | } | 3030 | } |
3013 | return render(request, template, context) | 3031 | return render(request, template, context) |
3014 | 3032 | ||
3033 | def buildrequestdetails(request, pid, brid): | ||
3034 | template = "buildrequestdetails.html" | ||
3035 | context = { | ||
3036 | 'buildrequest' : BuildRequest.objects.get(pk = brid, project_id = pid) | ||
3037 | } | ||
3038 | return render(request, template, context) | ||
3039 | |||
3040 | |||
3015 | else: | 3041 | else: |
3016 | # these are pages that are NOT available in interactive mode | 3042 | # these are pages that are NOT available in interactive mode |
3017 | def managedcontextprocessor(request): | 3043 | def managedcontextprocessor(request): |
@@ -3256,3 +3282,6 @@ else: | |||
3256 | 3282 | ||
3257 | def xhr_updatelayer(request): | 3283 | def xhr_updatelayer(request): |
3258 | raise Exception("page not available in interactive mode") | 3284 | raise Exception("page not available in interactive mode") |
3285 | |||
3286 | def buildrequestdetails(request, pid, brid): | ||
3287 | raise Exception("page not available in interactive mode") | ||