diff options
Diffstat (limited to 'bitbake/lib/toaster/toastergui/views.py')
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 437 |
1 files changed, 103 insertions, 334 deletions
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index 060d680b85..0324d17065 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -55,11 +55,23 @@ def landing(request): | |||
55 | 55 | ||
56 | return render(request, 'landing.html', context) | 56 | return render(request, 'landing.html', context) |
57 | 57 | ||
58 | # returns a list for most recent builds; for use in the Project page, xhr_ updates, and other places, as needed | 58 | |
59 | |||
60 | # returns a list for most recent builds; | ||
61 | def _get_latest_builds(prj=None): | ||
62 | queryset = Build.objects.all() | ||
63 | |||
64 | if prj is not None: | ||
65 | queryset = queryset.filter(project = prj) | ||
66 | |||
67 | return itertools.chain(queryset.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3], queryset.filter(outcome=Build.IN_PROGRESS).order_by("-pk")) | ||
68 | |||
69 | |||
70 | # a JSON-able dict of recent builds; for use in the Project page, xhr_ updates, and other places, as needed | ||
59 | def _project_recent_build_list(prj): | 71 | def _project_recent_build_list(prj): |
60 | data = [] | 72 | data = [] |
61 | # take the most recent 3 completed builds, plus any builds in progress | 73 | # take the most recent 3 completed builds, plus any builds in progress |
62 | for x in itertools.chain(prj.build_set.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3], prj.build_set.filter(outcome=Build.IN_PROGRESS).order_by("-pk")): | 74 | for x in _get_latest_builds(prj): |
63 | d = { | 75 | d = { |
64 | "id": x.pk, | 76 | "id": x.pk, |
65 | "targets" : map(lambda y: {"target": y.target, "task": None }, x.target_set.all()), # TODO: create the task entry in the Target table | 77 | "targets" : map(lambda y: {"target": y.target, "task": None }, x.target_set.all()), # TODO: create the task entry in the Target table |
@@ -1866,10 +1878,10 @@ if True: | |||
1866 | # be able to display something. 'count' and 'page' are mandatory for all views | 1878 | # be able to display something. 'count' and 'page' are mandatory for all views |
1867 | # that use paginators. | 1879 | # that use paginators. |
1868 | 1880 | ||
1869 | buildrequests = BuildRequest.objects.exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) | 1881 | queryset = Build.objects.filter(outcome__lte = Build.IN_PROGRESS) |
1870 | 1882 | ||
1871 | try: | 1883 | try: |
1872 | context, pagesize, orderby = _build_list_helper(request, buildrequests, True) | 1884 | context, pagesize, orderby = _build_list_helper(request, queryset) |
1873 | except InvalidRequestException as e: | 1885 | except InvalidRequestException as e: |
1874 | raise RedirectException( builds, request.GET, e.response) | 1886 | raise RedirectException( builds, request.GET, e.response) |
1875 | 1887 | ||
@@ -1878,66 +1890,37 @@ if True: | |||
1878 | 1890 | ||
1879 | 1891 | ||
1880 | # helper function, to be used on "all builds" and "project builds" pages | 1892 | # helper function, to be used on "all builds" and "project builds" pages |
1881 | def _build_list_helper(request, buildrequests, insert_projects): | 1893 | def _build_list_helper(request, queryset_all): |
1882 | # ATTN: we use here the ordering parameters for interactive mode; the translation for BuildRequest fields will happen below | 1894 | |
1883 | default_orderby = 'completed_on:-' | 1895 | default_orderby = 'completed_on:-' |
1884 | (pagesize, orderby) = _get_parameters_values(request, 10, default_orderby) | 1896 | (pagesize, orderby) = _get_parameters_values(request, 10, default_orderby) |
1885 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } | 1897 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } |
1886 | retval = _verify_parameters( request.GET, mandatory_parameters ) | 1898 | retval = _verify_parameters( request.GET, mandatory_parameters ) |
1887 | if retval: | 1899 | if retval: |
1888 | raise InvalidRequestException(mandatory_parameters) | 1900 | raise RedirectException( 'all-builds', request.GET, mandatory_parameters) |
1889 | |||
1890 | orig_orderby = orderby | ||
1891 | # translate interactive mode ordering to managed mode ordering | ||
1892 | ordering_params = orderby.split(":") | ||
1893 | if ordering_params[0] == "completed_on": | ||
1894 | ordering_params[0] = "updated" | ||
1895 | if ordering_params[0] == "started_on": | ||
1896 | ordering_params[0] = "created" | ||
1897 | if ordering_params[0] == "errors_no": | ||
1898 | ordering_params[0] = "build__errors_no" | ||
1899 | if ordering_params[0] == "warnings_no": | ||
1900 | ordering_params[0] = "build__warnings_no" | ||
1901 | if ordering_params[0] == "machine": | ||
1902 | ordering_params[0] = "build__machine" | ||
1903 | if ordering_params[0] == "target__target": | ||
1904 | ordering_params[0] = "brtarget__target" | ||
1905 | if ordering_params[0] == "timespent": | ||
1906 | ordering_params[0] = "id" | ||
1907 | orderby = default_orderby | ||
1908 | |||
1909 | request.GET = request.GET.copy() # get a mutable copy of the GET QueryDict | ||
1910 | request.GET['orderby'] = ":".join(ordering_params) | ||
1911 | 1901 | ||
1912 | # boilerplate code that takes a request for an object type and returns a queryset | 1902 | # boilerplate code that takes a request for an object type and returns a queryset |
1913 | # for that object type. copypasta for all needed table searches | 1903 | # for that object type. copypasta for all needed table searches |
1914 | (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest) | 1904 | (filter_string, search_term, ordering_string) = _search_tuple(request, Build) |
1915 | # post-process any date range filters | 1905 | # post-process any date range filters |
1916 | filter_string,daterange_selected = _modify_date_range_filter(filter_string) | 1906 | filter_string,daterange_selected = _modify_date_range_filter(filter_string) |
1917 | 1907 | queryset_all = queryset_all.select_related("project") | |
1918 | # we don't display in-progress or deleted builds | 1908 | queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on') |
1919 | queryset_all = buildrequests.exclude(state = BuildRequest.REQ_DELETED) | 1909 | queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on') |
1920 | queryset_all = queryset_all.select_related("build", "build__project").annotate(Count('brerror')) | ||
1921 | queryset_with_search = _get_queryset(BuildRequest, queryset_all, filter_string, search_term, ordering_string, '-updated') | ||
1922 | |||
1923 | 1910 | ||
1924 | # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display | 1911 | # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display |
1925 | build_info = _build_page_range(Paginator(queryset_with_search, pagesize), request.GET.get('page', 1)) | 1912 | build_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1)) |
1926 | 1913 | ||
1927 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) | 1914 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) |
1928 | # most recent build is like projects' most recent builds, but across all projects | 1915 | build_mru = Build.objects.order_by("-started_on")[:3] |
1929 | build_mru = _managed_get_latest_builds() | 1916 | |
1917 | # calculate the exact begining of local today and yesterday, append context | ||
1918 | context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'started_on','completed_on'}) | ||
1930 | 1919 | ||
1920 | # set up list of fstypes for each build | ||
1931 | fstypes_map = {}; | 1921 | fstypes_map = {}; |
1932 | for build_request in build_info: | 1922 | for build in build_info: |
1933 | # set display variables for build request | 1923 | targets = Target.objects.filter( build_id = build.id ) |
1934 | build_request.machine = build_request.brvariable_set.get(name="MACHINE").value | ||
1935 | build_request.timespent = build_request.updated - build_request.created | ||
1936 | |||
1937 | # set up list of fstypes for each build | ||
1938 | if build_request.build is None: | ||
1939 | continue | ||
1940 | targets = Target.objects.filter( build_id = build_request.build.id ) | ||
1941 | comma = ""; | 1924 | comma = ""; |
1942 | extensions = ""; | 1925 | extensions = ""; |
1943 | for t in targets: | 1926 | for t in targets: |
@@ -1951,8 +1934,7 @@ if True: | |||
1951 | if None == re.search(s,extensions): | 1934 | if None == re.search(s,extensions): |
1952 | extensions += comma + s | 1935 | extensions += comma + s |
1953 | comma = ", " | 1936 | comma = ", " |
1954 | fstypes_map[build_request.build.id]=extensions | 1937 | fstypes_map[build.id]=extensions |
1955 | |||
1956 | 1938 | ||
1957 | # send the data to the template | 1939 | # send the data to the template |
1958 | context = { | 1940 | context = { |
@@ -1961,7 +1943,7 @@ if True: | |||
1961 | # TODO: common objects for all table views, adapt as needed | 1943 | # TODO: common objects for all table views, adapt as needed |
1962 | 'objects' : build_info, | 1944 | 'objects' : build_info, |
1963 | 'objectname' : "builds", | 1945 | 'objectname' : "builds", |
1964 | 'default_orderby' : 'updated:-', | 1946 | 'default_orderby' : default_orderby, |
1965 | 'fstypes' : fstypes_map, | 1947 | 'fstypes' : fstypes_map, |
1966 | 'search_term' : search_term, | 1948 | 'search_term' : search_term, |
1967 | 'total_count' : queryset_with_search.count(), | 1949 | 'total_count' : queryset_with_search.count(), |
@@ -1971,151 +1953,137 @@ if True: | |||
1971 | {'name': 'Outcome', # column with a single filter | 1953 | {'name': 'Outcome', # column with a single filter |
1972 | 'qhelp' : "The outcome tells you if a build successfully completed or failed", # the help button content | 1954 | 'qhelp' : "The outcome tells you if a build successfully completed or failed", # the help button content |
1973 | 'dclass' : "span2", # indication about column width; comes from the design | 1955 | 'dclass' : "span2", # indication about column width; comes from the design |
1974 | 'orderfield': _get_toggle_order(request, "state"), # adds ordering by the field value; default ascending unless clicked from ascending into descending | 1956 | 'orderfield': _get_toggle_order(request, "outcome"), # adds ordering by the field value; default ascending unless clicked from ascending into descending |
1975 | 'ordericon':_get_toggle_order_icon(request, "state"), | 1957 | 'ordericon':_get_toggle_order_icon(request, "outcome"), |
1976 | # filter field will set a filter on that column with the specs in the filter description | 1958 | # filter field will set a filter on that column with the specs in the filter description |
1977 | # the class field in the filter has no relation with clclass; the control different aspects of the UI | 1959 | # the class field in the filter has no relation with clclass; the control different aspects of the UI |
1978 | # still, it is recommended for the values to be identical for easy tracking in the generated HTML | 1960 | # still, it is recommended for the values to be identical for easy tracking in the generated HTML |
1979 | 'filter' : {'class' : 'outcome', | 1961 | 'filter' : {'class' : 'outcome', |
1980 | 'label': 'Show:', | 1962 | 'label': 'Show:', |
1981 | 'options' : [ | 1963 | 'options' : [ |
1982 | ('Successful builds', 'build__outcome:' + str(Build.SUCCEEDED), queryset_all.filter(build__outcome = Build.SUCCEEDED).count()), # this is the field search expression | 1964 | ('Successful builds', 'outcome:' + str(Build.SUCCEEDED), queryset_with_search.filter(outcome=str(Build.SUCCEEDED)).count()), # this is the field search expression |
1983 | ('Failed builds', 'build__outcome:NOT'+ str(Build.SUCCEEDED), queryset_all.exclude(build__outcome = Build.SUCCEEDED).count()), | 1965 | ('Failed builds', 'outcome:'+ str(Build.FAILED), queryset_with_search.filter(outcome=str(Build.FAILED)).count()), |
1984 | ] | 1966 | ] |
1985 | } | 1967 | } |
1986 | }, | 1968 | }, |
1987 | {'name': 'Recipe', # default column, disabled box, with just the name in the list | 1969 | {'name': 'Recipe', # default column, disabled box, with just the name in the list |
1988 | 'qhelp': "What you built (i.e. one or more recipes or image recipes)", | 1970 | 'qhelp': "What you built (i.e. one or more recipes or image recipes)", |
1989 | 'orderfield': _get_toggle_order(request, "brtarget__target"), | 1971 | 'orderfield': _get_toggle_order(request, "target__target"), |
1990 | 'ordericon':_get_toggle_order_icon(request, "brtarget__target"), | 1972 | 'ordericon':_get_toggle_order_icon(request, "target__target"), |
1991 | }, | 1973 | }, |
1992 | {'name': 'Machine', | 1974 | {'name': 'Machine', |
1993 | 'qhelp': "The machine is the hardware for which you are building a recipe or image recipe", | 1975 | 'qhelp': "The machine is the hardware for which you are building a recipe or image recipe", |
1994 | 'orderfield': _get_toggle_order(request, "build__machine"), | 1976 | 'orderfield': _get_toggle_order(request, "machine"), |
1995 | 'ordericon':_get_toggle_order_icon(request, "build__machine"), | 1977 | 'ordericon':_get_toggle_order_icon(request, "machine"), |
1996 | 'dclass': 'span3' | 1978 | 'dclass': 'span3' |
1997 | }, # a slightly wider column | 1979 | }, # a slightly wider column |
1998 | ] | ||
1999 | } | ||
2000 | |||
2001 | if (insert_projects): | ||
2002 | context['tablecols'].append( | ||
2003 | {'name': 'Project', 'clclass': 'project_column', | ||
2004 | } | ||
2005 | ) | ||
2006 | |||
2007 | # calculate the exact begining of local today and yesterday | ||
2008 | context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'created','updated'}) | ||
2009 | context.update(context_date) | ||
2010 | |||
2011 | context['tablecols'].append( | ||
2012 | {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column | 1980 | {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column |
2013 | 'qhelp': "The date and time you started the build", | 1981 | 'qhelp': "The date and time you started the build", |
2014 | 'orderfield': _get_toggle_order(request, "created", True), | 1982 | 'orderfield': _get_toggle_order(request, "started_on", True), |
2015 | 'ordericon':_get_toggle_order_icon(request, "created"), | 1983 | 'ordericon':_get_toggle_order_icon(request, "started_on"), |
2016 | 'filter' : {'class' : 'created', | 1984 | 'orderkey' : "started_on", |
1985 | 'filter' : {'class' : 'started_on', | ||
2017 | 'label': 'Show:', | 1986 | 'label': 'Show:', |
2018 | 'options' : [ | 1987 | 'options' : [ |
2019 | ("Today's builds" , 'created__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(created__gte=today_begin).count()), | 1988 | ("Today's builds" , 'started_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(started_on__gte=today_begin).count()), |
2020 | ("Yesterday's builds", | 1989 | ("Yesterday's builds", |
2021 | 'created__gte!created__lt:' | 1990 | 'started_on__gte!started_on__lt:' |
2022 | +yesterday_begin.strftime("%Y-%m-%d")+'!' | 1991 | +yesterday_begin.strftime("%Y-%m-%d")+'!' |
2023 | +today_begin.strftime("%Y-%m-%d"), | 1992 | +today_begin.strftime("%Y-%m-%d"), |
2024 | queryset_all.filter( | 1993 | queryset_all.filter( |
2025 | created__gte=yesterday_begin, | 1994 | started_on__gte=yesterday_begin, |
2026 | created__lt=today_begin | 1995 | started_on__lt=today_begin |
2027 | ).count()), | 1996 | ).count()), |
2028 | ("Build date range", 'daterange', 1, '', 'created'), | 1997 | ("Build date range", 'daterange', 1, '', 'started_on'), |
2029 | ] | 1998 | ] |
2030 | } | 1999 | } |
2031 | } | 2000 | }, |
2032 | ) | ||
2033 | context['tablecols'].append( | ||
2034 | {'name': 'Completed on', | 2001 | {'name': 'Completed on', |
2035 | 'qhelp': "The date and time the build finished", | 2002 | 'qhelp': "The date and time the build finished", |
2036 | 'orderfield': _get_toggle_order(request, "updated", True), | 2003 | 'orderfield': _get_toggle_order(request, "completed_on", True), |
2037 | 'ordericon':_get_toggle_order_icon(request, "updated"), | 2004 | 'ordericon':_get_toggle_order_icon(request, "completed_on"), |
2038 | 'orderkey' : 'updated', | 2005 | 'orderkey' : 'completed_on', |
2039 | 'filter' : {'class' : 'updated', | 2006 | 'filter' : {'class' : 'completed_on', |
2040 | 'label': 'Show:', | 2007 | 'label': 'Show:', |
2041 | 'options' : [ | 2008 | 'options' : [ |
2042 | ("Today's builds" , 'updated__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=today_begin).count()), | 2009 | ("Today's builds" , 'completed_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(completed_on__gte=today_begin).count()), |
2043 | ("Yesterday's builds", | 2010 | ("Yesterday's builds", |
2044 | 'updated__gte!updated__lt:' | 2011 | 'completed_on__gte!completed_on__lt:' |
2045 | +yesterday_begin.strftime("%Y-%m-%d")+'!' | 2012 | +yesterday_begin.strftime("%Y-%m-%d")+'!' |
2046 | +today_begin.strftime("%Y-%m-%d"), | 2013 | +today_begin.strftime("%Y-%m-%d"), |
2047 | queryset_all.filter( | 2014 | queryset_all.filter( |
2048 | updated__gte=yesterday_begin, | 2015 | completed_on__gte=yesterday_begin, |
2049 | updated__lt=today_begin | 2016 | completed_on__lt=today_begin |
2050 | ).count()), | 2017 | ).count()), |
2051 | ("Build date range", 'daterange', 1, '', 'updated'), | 2018 | ("Build date range", 'daterange', 1, '', 'completed_on'), |
2052 | ] | 2019 | ] |
2053 | } | 2020 | } |
2054 | } | 2021 | }, |
2055 | ) | ||
2056 | context['tablecols'].append( | ||
2057 | {'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox | 2022 | {'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox |
2058 | 'qhelp': "How many tasks failed during the build", | 2023 | 'qhelp': "How many tasks failed during the build", |
2059 | 'filter' : {'class' : 'failed_tasks', | 2024 | 'filter' : {'class' : 'failed_tasks', |
2060 | 'label': 'Show:', | 2025 | 'label': 'Show:', |
2061 | 'options' : [ | 2026 | 'options' : [ |
2062 | ('Builds with failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED, | 2027 | ('Builds with failed tasks', 'task_build__outcome:4', queryset_with_search.filter(task_build__outcome=4).count()), |
2063 | queryset_all.filter(build__task_build__outcome=Task.OUTCOME_FAILED).count()), | 2028 | ('Builds without failed tasks', 'task_build__outcome:NOT4', queryset_with_search.filter(~Q(task_build__outcome=4)).count()), |
2064 | ('Builds without failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED, | ||
2065 | queryset_all.filter(~Q(build__task_build__outcome=Task.OUTCOME_FAILED)).count()), | ||
2066 | ] | 2029 | ] |
2067 | } | 2030 | } |
2068 | } | 2031 | }, |
2069 | ) | ||
2070 | context['tablecols'].append( | ||
2071 | {'name': 'Errors', 'clclass': 'errors_no', | 2032 | {'name': 'Errors', 'clclass': 'errors_no', |
2072 | 'qhelp': "How many errors were encountered during the build (if any)", | 2033 | 'qhelp': "How many errors were encountered during the build (if any)", |
2073 | 'orderfield': _get_toggle_order(request, "build__errors_no", True), | 2034 | 'orderfield': _get_toggle_order(request, "errors_no", True), |
2074 | 'ordericon':_get_toggle_order_icon(request, "build__errors_no"), | 2035 | 'ordericon':_get_toggle_order_icon(request, "errors_no"), |
2075 | 'orderkey' : 'errors_no', | 2036 | 'orderkey' : 'errors_no', |
2076 | 'filter' : {'class' : 'errors_no', | 2037 | 'filter' : {'class' : 'errors_no', |
2077 | 'label': 'Show:', | 2038 | 'label': 'Show:', |
2078 | 'options' : [ | 2039 | 'options' : [ |
2079 | ('Builds with errors', 'build|build__errors_no__gt:None|0', | 2040 | ('Builds with errors', 'errors_no__gte:1', queryset_with_search.filter(errors_no__gte=1).count()), |
2080 | queryset_all.filter(Q(build=None) | Q(build__errors_no__gt=0)).count()), | 2041 | ('Builds without errors', 'errors_no:0', queryset_with_search.filter(errors_no=0).count()), |
2081 | ('Builds without errors', 'build__errors_no:0', | ||
2082 | queryset_all.filter(build__errors_no=0).count()), | ||
2083 | ] | 2042 | ] |
2084 | } | 2043 | } |
2085 | } | 2044 | }, |
2086 | ) | ||
2087 | context['tablecols'].append( | ||
2088 | {'name': 'Warnings', 'clclass': 'warnings_no', | 2045 | {'name': 'Warnings', 'clclass': 'warnings_no', |
2089 | 'qhelp': "How many warnings were encountered during the build (if any)", | 2046 | 'qhelp': "How many warnings were encountered during the build (if any)", |
2090 | 'orderfield': _get_toggle_order(request, "build__warnings_no", True), | 2047 | 'orderfield': _get_toggle_order(request, "warnings_no", True), |
2091 | 'ordericon':_get_toggle_order_icon(request, "build__warnings_no"), | 2048 | 'ordericon':_get_toggle_order_icon(request, "warnings_no"), |
2092 | 'orderkey' : 'build__warnings_no', | 2049 | 'orderkey' : 'warnings_no', |
2093 | 'filter' : {'class' : 'build__warnings_no', | 2050 | 'filter' : {'class' : 'warnings_no', |
2094 | 'label': 'Show:', | 2051 | 'label': 'Show:', |
2095 | 'options' : [ | 2052 | 'options' : [ |
2096 | ('Builds with warnings','build__warnings_no__gte:1', queryset_all.filter(build__warnings_no__gte=1).count()), | 2053 | ('Builds with warnings','warnings_no__gte:1', queryset_with_search.filter(warnings_no__gte=1).count()), |
2097 | ('Builds without warnings','build__warnings_no:0', queryset_all.filter(build__warnings_no=0).count()), | 2054 | ('Builds without warnings','warnings_no:0', queryset_with_search.filter(warnings_no=0).count()), |
2098 | ] | 2055 | ] |
2099 | } | 2056 | } |
2100 | } | 2057 | }, |
2101 | ) | 2058 | {'name': 'Log', |
2102 | context['tablecols'].append( | 2059 | 'dclass': "span4", |
2060 | 'qhelp': "Path to the build main log file", | ||
2061 | 'clclass': 'log', 'hidden': 1, | ||
2062 | 'orderfield': _get_toggle_order(request, "cooker_log_path"), | ||
2063 | 'ordericon':_get_toggle_order_icon(request, "cooker_log_path"), | ||
2064 | 'orderkey' : 'cooker_log_path', | ||
2065 | }, | ||
2103 | {'name': 'Time', 'clclass': 'time', 'hidden' : 1, | 2066 | {'name': 'Time', 'clclass': 'time', 'hidden' : 1, |
2104 | 'qhelp': "How long it took the build to finish", | 2067 | 'qhelp': "How long it took the build to finish", |
2105 | # 'orderfield': _get_toggle_order(request, "timespent", True), | 2068 | 'orderfield': _get_toggle_order(request, "timespent", True), |
2106 | # 'ordericon':_get_toggle_order_icon(request, "timespent"), | 2069 | 'ordericon':_get_toggle_order_icon(request, "timespent"), |
2107 | 'orderkey' : 'timespent', | 2070 | 'orderkey' : 'timespent', |
2108 | } | 2071 | }, |
2109 | ) | ||
2110 | context['tablecols'].append( | ||
2111 | {'name': 'Image files', 'clclass': 'output', | 2072 | {'name': 'Image files', 'clclass': 'output', |
2112 | 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory", | 2073 | 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory", |
2113 | # TODO: compute image fstypes from Target_Image_File | 2074 | # TODO: compute image fstypes from Target_Image_File |
2075 | }, | ||
2076 | {'name': 'Project', 'clcalss': 'project_column', | ||
2114 | } | 2077 | } |
2115 | ) | 2078 | ] |
2079 | } | ||
2116 | 2080 | ||
2081 | # merge daterange values | ||
2082 | context.update(context_date) | ||
2117 | return context, pagesize, orderby | 2083 | return context, pagesize, orderby |
2118 | 2084 | ||
2085 | |||
2086 | |||
2119 | # new project | 2087 | # new project |
2120 | def newproject(request): | 2088 | def newproject(request): |
2121 | template = "newproject.html" | 2089 | template = "newproject.html" |
@@ -2236,7 +2204,7 @@ if True: | |||
2236 | "lvs_nos" : Layer_Version.objects.all().count(), | 2204 | "lvs_nos" : Layer_Version.objects.all().count(), |
2237 | "completedbuilds": Build.objects.filter(project_id = pid).filter(outcome__lte = Build.IN_PROGRESS), | 2205 | "completedbuilds": Build.objects.filter(project_id = pid).filter(outcome__lte = Build.IN_PROGRESS), |
2238 | "prj" : {"name": prj.name, }, | 2206 | "prj" : {"name": prj.name, }, |
2239 | #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED), | 2207 | "buildrequests" : prj.build_set.filter(outcome=Build.IN_PROGRESS), |
2240 | "builds" : _project_recent_build_list(prj), | 2208 | "builds" : _project_recent_build_list(prj), |
2241 | "layers" : map(lambda x: { | 2209 | "layers" : map(lambda x: { |
2242 | "id": x.layercommit.pk, | 2210 | "id": x.layercommit.pk, |
@@ -2607,9 +2575,10 @@ if True: | |||
2607 | 2575 | ||
2608 | @_template_renderer('projectbuilds.html') | 2576 | @_template_renderer('projectbuilds.html') |
2609 | def projectbuilds(request, pid): | 2577 | def projectbuilds(request, pid): |
2610 | # process any build request | ||
2611 | prj = Project.objects.get(id = pid) | 2578 | prj = Project.objects.get(id = pid) |
2579 | |||
2612 | if request.method == "POST": | 2580 | if request.method == "POST": |
2581 | # process any build request | ||
2613 | 2582 | ||
2614 | if 'buildCancel' in request.POST: | 2583 | if 'buildCancel' in request.POST: |
2615 | for i in request.POST['buildCancel'].strip().split(" "): | 2584 | for i in request.POST['buildCancel'].strip().split(" "): |
@@ -2641,10 +2610,10 @@ if True: | |||
2641 | br = prj.schedule_build() | 2610 | br = prj.schedule_build() |
2642 | 2611 | ||
2643 | 2612 | ||
2644 | buildrequests = BuildRequest.objects.filter(project = prj).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) | 2613 | queryset = Build.objects.filter(outcome__lte = Build.IN_PROGRESS) |
2645 | 2614 | ||
2646 | try: | 2615 | try: |
2647 | context, pagesize, orderby = _build_list_helper(request, buildrequests, False) | 2616 | context, pagesize, orderby = _build_list_helper(request, queryset) |
2648 | except InvalidRequestException as e: | 2617 | except InvalidRequestException as e: |
2649 | raise RedirectException('projectbuilds', request.GET, e.response, pid = pid) | 2618 | raise RedirectException('projectbuilds', request.GET, e.response, pid = pid) |
2650 | 2619 | ||
@@ -2759,12 +2728,6 @@ if True: | |||
2759 | } | 2728 | } |
2760 | return render(request, "unavailable_artifact.html", context) | 2729 | return render(request, "unavailable_artifact.html", context) |
2761 | 2730 | ||
2762 | # This returns the mru object that is needed for the | ||
2763 | # managed_mrb_section.html template | ||
2764 | def _managed_get_latest_builds(): | ||
2765 | build_mru = BuildRequest.objects.all() | ||
2766 | build_mru = list(build_mru.filter(Q(state__lt=BuildRequest.REQ_COMPLETED) or Q(state=BuildRequest.REQ_DELETED)).order_by("-pk")) + list(build_mru.filter(state__in=[BuildRequest.REQ_COMPLETED, BuildRequest.REQ_FAILED]).order_by("-pk")[:3]) | ||
2767 | return build_mru | ||
2768 | 2731 | ||
2769 | 2732 | ||
2770 | 2733 | ||
@@ -2796,7 +2759,7 @@ if True: | |||
2796 | p.projectTargetsUrl = reverse('projectavailabletargets', args=(p.id,)) | 2759 | p.projectTargetsUrl = reverse('projectavailabletargets', args=(p.id,)) |
2797 | 2760 | ||
2798 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) | 2761 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) |
2799 | build_mru = _managed_get_latest_builds() | 2762 | build_mru = _get_latest_builds() |
2800 | 2763 | ||
2801 | # translate the project's build target strings | 2764 | # translate the project's build target strings |
2802 | fstypes_map = {}; | 2765 | fstypes_map = {}; |
@@ -2878,202 +2841,8 @@ if True: | |||
2878 | return context | 2841 | return context |
2879 | 2842 | ||
2880 | @_template_renderer("buildrequestdetails.html") | 2843 | @_template_renderer("buildrequestdetails.html") |
2881 | def buildrequestdetails(request, pid, brid): | 2844 | def buildrequestdetails(request, pid, bid): |
2882 | context = { | 2845 | context = { |
2883 | 'buildrequest' : BuildRequest.objects.get(pk = brid, project_id = pid) | 2846 | 'buildrequest' : Build.objects.get(pk = bid, project_id = pid).buildrequest |
2884 | } | 2847 | } |
2885 | return context | 2848 | return context |
2886 | |||
2887 | |||
2888 | @_template_renderer('builds.html') | ||
2889 | def builds_old(request): | ||
2890 | # define here what parameters the view needs in the GET portion in order to | ||
2891 | # be able to display something. 'count' and 'page' are mandatory for all views | ||
2892 | # that use paginators. | ||
2893 | (pagesize, orderby) = _get_parameters_values(request, 10, 'completed_on:-') | ||
2894 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } | ||
2895 | retval = _verify_parameters( request.GET, mandatory_parameters ) | ||
2896 | if retval: | ||
2897 | raise RedirectException( 'all-builds', request.GET, mandatory_parameters) | ||
2898 | |||
2899 | # boilerplate code that takes a request for an object type and returns a queryset | ||
2900 | # for that object type. copypasta for all needed table searches | ||
2901 | (filter_string, search_term, ordering_string) = _search_tuple(request, Build) | ||
2902 | # post-process any date range filters | ||
2903 | filter_string,daterange_selected = _modify_date_range_filter(filter_string) | ||
2904 | queryset_all = Build.objects.exclude(outcome = Build.IN_PROGRESS) | ||
2905 | queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on') | ||
2906 | queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on') | ||
2907 | |||
2908 | # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display | ||
2909 | build_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1)) | ||
2910 | |||
2911 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) | ||
2912 | build_mru = Build.objects.order_by("-started_on")[:3] | ||
2913 | |||
2914 | # calculate the exact begining of local today and yesterday, append context | ||
2915 | context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'started_on','completed_on'}) | ||
2916 | |||
2917 | # set up list of fstypes for each build | ||
2918 | fstypes_map = {}; | ||
2919 | for build in build_info: | ||
2920 | targets = Target.objects.filter( build_id = build.id ) | ||
2921 | comma = ""; | ||
2922 | extensions = ""; | ||
2923 | for t in targets: | ||
2924 | if ( not t.is_image ): | ||
2925 | continue | ||
2926 | tif = Target_Image_File.objects.filter( target_id = t.id ) | ||
2927 | for i in tif: | ||
2928 | s=re.sub('.*tar.bz2', 'tar.bz2', i.file_name) | ||
2929 | if s == i.file_name: | ||
2930 | s=re.sub('.*\.', '', i.file_name) | ||
2931 | if None == re.search(s,extensions): | ||
2932 | extensions += comma + s | ||
2933 | comma = ", " | ||
2934 | fstypes_map[build.id]=extensions | ||
2935 | |||
2936 | # send the data to the template | ||
2937 | context = { | ||
2938 | # specific info for | ||
2939 | 'mru' : build_mru, | ||
2940 | # TODO: common objects for all table views, adapt as needed | ||
2941 | 'objects' : build_info, | ||
2942 | 'objectname' : "builds", | ||
2943 | 'default_orderby' : 'completed_on:-', | ||
2944 | 'fstypes' : fstypes_map, | ||
2945 | 'search_term' : search_term, | ||
2946 | 'total_count' : queryset_with_search.count(), | ||
2947 | 'daterange_selected' : daterange_selected, | ||
2948 | # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns | ||
2949 | 'tablecols' : [ | ||
2950 | {'name': 'Outcome', # column with a single filter | ||
2951 | 'qhelp' : "The outcome tells you if a build successfully completed or failed", # the help button content | ||
2952 | 'dclass' : "span2", # indication about column width; comes from the design | ||
2953 | 'orderfield': _get_toggle_order(request, "outcome"), # adds ordering by the field value; default ascending unless clicked from ascending into descending | ||
2954 | 'ordericon':_get_toggle_order_icon(request, "outcome"), | ||
2955 | # filter field will set a filter on that column with the specs in the filter description | ||
2956 | # the class field in the filter has no relation with clclass; the control different aspects of the UI | ||
2957 | # still, it is recommended for the values to be identical for easy tracking in the generated HTML | ||
2958 | 'filter' : {'class' : 'outcome', | ||
2959 | 'label': 'Show:', | ||
2960 | 'options' : [ | ||
2961 | ('Successful builds', 'outcome:' + str(Build.SUCCEEDED), queryset_with_search.filter(outcome=str(Build.SUCCEEDED)).count()), # this is the field search expression | ||
2962 | ('Failed builds', 'outcome:'+ str(Build.FAILED), queryset_with_search.filter(outcome=str(Build.FAILED)).count()), | ||
2963 | ] | ||
2964 | } | ||
2965 | }, | ||
2966 | {'name': 'Recipe', # default column, disabled box, with just the name in the list | ||
2967 | 'qhelp': "What you built (i.e. one or more recipes or image recipes)", | ||
2968 | 'orderfield': _get_toggle_order(request, "target__target"), | ||
2969 | 'ordericon':_get_toggle_order_icon(request, "target__target"), | ||
2970 | }, | ||
2971 | {'name': 'Machine', | ||
2972 | 'qhelp': "The machine is the hardware for which you are building a recipe or image recipe", | ||
2973 | 'orderfield': _get_toggle_order(request, "machine"), | ||
2974 | 'ordericon':_get_toggle_order_icon(request, "machine"), | ||
2975 | 'dclass': 'span3' | ||
2976 | }, # a slightly wider column | ||
2977 | {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column | ||
2978 | 'qhelp': "The date and time you started the build", | ||
2979 | 'orderfield': _get_toggle_order(request, "started_on", True), | ||
2980 | 'ordericon':_get_toggle_order_icon(request, "started_on"), | ||
2981 | 'filter' : {'class' : 'started_on', | ||
2982 | 'label': 'Show:', | ||
2983 | 'options' : [ | ||
2984 | ("Today's builds" , 'started_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(started_on__gte=today_begin).count()), | ||
2985 | ("Yesterday's builds", | ||
2986 | 'started_on__gte!started_on__lt:' | ||
2987 | +yesterday_begin.strftime("%Y-%m-%d")+'!' | ||
2988 | +today_begin.strftime("%Y-%m-%d"), | ||
2989 | queryset_all.filter( | ||
2990 | started_on__gte=yesterday_begin, | ||
2991 | started_on__lt=today_begin | ||
2992 | ).count()), | ||
2993 | ("Build date range", 'daterange', 1, '', 'started_on'), | ||
2994 | ] | ||
2995 | } | ||
2996 | }, | ||
2997 | {'name': 'Completed on', | ||
2998 | 'qhelp': "The date and time the build finished", | ||
2999 | 'orderfield': _get_toggle_order(request, "completed_on", True), | ||
3000 | 'ordericon':_get_toggle_order_icon(request, "completed_on"), | ||
3001 | 'orderkey' : 'completed_on', | ||
3002 | 'filter' : {'class' : 'completed_on', | ||
3003 | 'label': 'Show:', | ||
3004 | 'options' : [ | ||
3005 | ("Today's builds" , 'completed_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(completed_on__gte=today_begin).count()), | ||
3006 | ("Yesterday's builds", | ||
3007 | 'completed_on__gte!completed_on__lt:' | ||
3008 | +yesterday_begin.strftime("%Y-%m-%d")+'!' | ||
3009 | +today_begin.strftime("%Y-%m-%d"), | ||
3010 | queryset_all.filter( | ||
3011 | completed_on__gte=yesterday_begin, | ||
3012 | completed_on__lt=today_begin | ||
3013 | ).count()), | ||
3014 | ("Build date range", 'daterange', 1, '', 'completed_on'), | ||
3015 | ] | ||
3016 | } | ||
3017 | }, | ||
3018 | {'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox | ||
3019 | 'qhelp': "How many tasks failed during the build", | ||
3020 | 'filter' : {'class' : 'failed_tasks', | ||
3021 | 'label': 'Show:', | ||
3022 | 'options' : [ | ||
3023 | ('Builds with failed tasks', 'task_build__outcome:4', queryset_with_search.filter(task_build__outcome=4).count()), | ||
3024 | ('Builds without failed tasks', 'task_build__outcome:NOT4', queryset_with_search.filter(~Q(task_build__outcome=4)).count()), | ||
3025 | ] | ||
3026 | } | ||
3027 | }, | ||
3028 | {'name': 'Errors', 'clclass': 'errors_no', | ||
3029 | 'qhelp': "How many errors were encountered during the build (if any)", | ||
3030 | 'orderfield': _get_toggle_order(request, "errors_no", True), | ||
3031 | 'ordericon':_get_toggle_order_icon(request, "errors_no"), | ||
3032 | 'orderkey' : 'errors_no', | ||
3033 | 'filter' : {'class' : 'errors_no', | ||
3034 | 'label': 'Show:', | ||
3035 | 'options' : [ | ||
3036 | ('Builds with errors', 'errors_no__gte:1', queryset_with_search.filter(errors_no__gte=1).count()), | ||
3037 | ('Builds without errors', 'errors_no:0', queryset_with_search.filter(errors_no=0).count()), | ||
3038 | ] | ||
3039 | } | ||
3040 | }, | ||
3041 | {'name': 'Warnings', 'clclass': 'warnings_no', | ||
3042 | 'qhelp': "How many warnings were encountered during the build (if any)", | ||
3043 | 'orderfield': _get_toggle_order(request, "warnings_no", True), | ||
3044 | 'ordericon':_get_toggle_order_icon(request, "warnings_no"), | ||
3045 | 'orderkey' : 'warnings_no', | ||
3046 | 'filter' : {'class' : 'warnings_no', | ||
3047 | 'label': 'Show:', | ||
3048 | 'options' : [ | ||
3049 | ('Builds with warnings','warnings_no__gte:1', queryset_with_search.filter(warnings_no__gte=1).count()), | ||
3050 | ('Builds without warnings','warnings_no:0', queryset_with_search.filter(warnings_no=0).count()), | ||
3051 | ] | ||
3052 | } | ||
3053 | }, | ||
3054 | {'name': 'Log', | ||
3055 | 'dclass': "span4", | ||
3056 | 'qhelp': "Path to the build main log file", | ||
3057 | 'clclass': 'log', 'hidden': 1, | ||
3058 | 'orderfield': _get_toggle_order(request, "cooker_log_path"), | ||
3059 | 'ordericon':_get_toggle_order_icon(request, "cooker_log_path"), | ||
3060 | 'orderkey' : 'cooker_log_path', | ||
3061 | }, | ||
3062 | {'name': 'Time', 'clclass': 'time', 'hidden' : 1, | ||
3063 | 'qhelp': "How long it took the build to finish", | ||
3064 | 'orderfield': _get_toggle_order(request, "timespent", True), | ||
3065 | 'ordericon':_get_toggle_order_icon(request, "timespent"), | ||
3066 | 'orderkey' : 'timespent', | ||
3067 | }, | ||
3068 | {'name': 'Image files', 'clclass': 'output', | ||
3069 | 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory", | ||
3070 | # TODO: compute image fstypes from Target_Image_File | ||
3071 | }, | ||
3072 | ] | ||
3073 | } | ||
3074 | |||
3075 | # merge daterange values | ||
3076 | context.update(context_date) | ||
3077 | _set_parameters_values(pagesize, orderby, request) | ||
3078 | |||
3079 | return context | ||