summaryrefslogtreecommitdiffstats
path: root/bitbake
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake')
-rwxr-xr-xbitbake/lib/toaster/toastergui/static/js/filtersnippet.js95
-rw-r--r--bitbake/lib/toaster/toastergui/templates/build.html17
-rw-r--r--bitbake/lib/toaster/toastergui/templates/filtersnippet.html34
-rw-r--r--bitbake/lib/toaster/toastergui/templates/managed_builds.html19
-rw-r--r--bitbake/lib/toaster/toastergui/templatetags/projecttags.py4
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py132
6 files changed, 277 insertions, 24 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/filtersnippet.js b/bitbake/lib/toaster/toastergui/static/js/filtersnippet.js
new file mode 100755
index 0000000000..27b057e64e
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/static/js/filtersnippet.js
@@ -0,0 +1,95 @@
1"use strict"
2
3// The disable removes the 'datepicker' attribute and
4// settings, so you have to re-initialize it each time
5// the date range is selected and enabled
6// DOM is used instead of jQuery to find the elements
7// in all contexts
8function date_enable (key, action) {
9
10 var elemFrom=document.getElementById("date_from_"+key);
11 var elemTo=document.getElementById("date_to_"+key);
12
13 if ('enable' == action) {
14 elemFrom.removeAttribute("disabled");
15 elemTo.removeAttribute("disabled");
16
17 $(elemFrom).datepicker();
18 $(elemTo).datepicker();
19
20 $(elemFrom).datepicker( "option", "dateFormat", "dd/mm/yy" );
21 $(elemTo).datepicker( "option", "dateFormat", "dd/mm/yy" );
22
23 $(elemFrom).datepicker( "setDate", elemFrom.getAttribute( "data-setDate") );
24 $(elemTo).datepicker( "setDate", elemTo.getAttribute( "data-setDate") );
25 $(elemFrom).datepicker( "option", "minDate", elemFrom.getAttribute( "data-minDate"));
26 $(elemTo).datepicker( "option", "minDate", elemTo.getAttribute( "data-minDate"));
27 $(elemFrom).datepicker( "option", "maxDate", elemFrom.getAttribute( "data-maxDate"));
28 $(elemTo).datepicker( "option", "maxDate", elemTo.getAttribute( "data-maxDate"));
29 } else {
30 elemFrom.setAttribute("disabled","disabled");
31 elemTo.setAttribute("disabled","disabled");
32 }
33}
34
35// Initialize the date picker elements with their default state variables, and
36// register the radio button and form actions
37function date_init (key, from_date, to_date, min_date, max_date, initial_enable) {
38
39 var elemFrom=document.getElementById("date_from_"+key);
40 var elemTo=document.getElementById("date_to_"+key);
41
42 // Were there any daterange filters instantiated? (e.g. no builds found)
43 if (null == elemFrom) {
44 return;
45 }
46
47 // init the datepicker context data
48 elemFrom.setAttribute( "data-setDate", from_date );
49 elemTo.setAttribute( "data-setDate", to_date );
50 elemFrom.setAttribute( "data-minDate", min_date);
51 elemTo.setAttribute( "data-minDate", min_date);
52 elemFrom.setAttribute( "data-maxDate", max_date);
53 elemTo.setAttribute( "data-maxDate", max_date);
54
55 // does the date set start enabled?
56 if (key == initial_enable) {
57 date_enable (key, "enable");
58 } else {
59 date_enable (key, "disable");
60 }
61
62 // catch the radio button selects for enable/disable
63 $('input:radio[name="filter"]').change(function(){
64 if ($(this).val() == 'daterange') {
65 key=$(this).attr("data-key");
66 date_enable (key, 'enable');
67 } else {
68 key=$(this).attr("data-key");
69 date_enable (key, 'disable');
70 }
71 });
72
73 // catch any new 'from' date as minDate for 'to' date
74 $("#date_from_"+key).change(function(){
75 from_date = $("#date_from_"+key).val();
76 $("#date_to_"+key).datepicker( "option", "minDate", from_date );
77 });
78
79 // catch the submit (just once)
80 $("form").unbind('submit');
81 $("form").submit(function(e) {
82 // format a composite daterange filter value so that it can be parsed and post-processed in the view
83 var key=e.originalEvent.explicitOriginalTarget.getAttribute("data-key")
84 if (typeof key != "undefined") {
85 if ($("#date_from_"+key).length) {
86 var filter=key+"__gte!"+key+"__lt:"+$("#date_from_"+key).val()+"!"+$("#date_to_"+key).val()+"_daterange";
87 $("#last_date_from_"+key).val($("#date_from_"+key).val());
88 $("#last_date_to_"+key).val($("#date_to_"+key).val());
89 $("#filter_value_"+key).val(filter);
90 }
91 }
92 return true;
93 });
94
95};
diff --git a/bitbake/lib/toaster/toastergui/templates/build.html b/bitbake/lib/toaster/toastergui/templates/build.html
index 684ec65884..f7ad2f4892 100644
--- a/bitbake/lib/toaster/toastergui/templates/build.html
+++ b/bitbake/lib/toaster/toastergui/templates/build.html
@@ -4,7 +4,24 @@
4{% load projecttags %} 4{% load projecttags %}
5{% load humanize %} 5{% load humanize %}
6 6
7{% block extraheadcontent %}
8<link rel="stylesheet" href="/static/css/jquery-ui.min.css" type='text/css'>
9<link rel="stylesheet" href="/static/css/jquery-ui.structure.min.css" type='text/css'>
10<link rel="stylesheet" href="/static/css/jquery-ui.theme.min.css" type='text/css'>
11<script src="/static/js/jquery-ui.min.js"></script>
12<script src="/static/js/filtersnippet.js"></script>
13{% endblock %}
14
7{% block pagecontent %} 15{% block pagecontent %}
16
17<script>
18 // intiialize the date range controls
19 $(document).ready(function () {
20 date_init('started_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_started_on}}','{{dateMax_started_on}}','{{daterange_selected}}');
21 date_init('completed_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_completed_on}}','{{dateMax_completed_on}}','{{daterange_selected}}');
22 });
23</script>
24
8<div class="row-fluid"> 25<div class="row-fluid">
9 26
10 {% include "mrb_section.html" %} 27 {% include "mrb_section.html" %}
diff --git a/bitbake/lib/toaster/toastergui/templates/filtersnippet.html b/bitbake/lib/toaster/toastergui/templates/filtersnippet.html
index fe70e7143e..f624d88bad 100644
--- a/bitbake/lib/toaster/toastergui/templates/filtersnippet.html
+++ b/bitbake/lib/toaster/toastergui/templates/filtersnippet.html
@@ -1,5 +1,6 @@
1{% load projecttags %} 1{% load projecttags %}
2<!-- '{{f.class}}' filter --> 2<!-- '{{f.class}}' filter -->
3{% with f.class as key %}
3<form id="filter_{{f.class}}" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true"> 4<form id="filter_{{f.class}}" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true">
4 <input type="hidden" name="search" value="{{request.GET.search}}"/> 5 <input type="hidden" name="search" value="{{request.GET.search}}"/>
5 <div class="modal-header"> 6 <div class="modal-header">
@@ -13,22 +14,37 @@
13 <div class="modal-body"> 14 <div class="modal-body">
14 <p>{{f.label}}</p> 15 <p>{{f.label}}</p>
15 <label class="radio"> 16 <label class="radio">
16 <input type="radio" name="filter" {%if request.GET.filter%}{{f.options|check_filter_status:request.GET.filter}} {%else%} checked {%endif%} value=""> All {%if filter_search_display%}{{filter_search_display|title}}{%else%}{{objectname|title}}{%endif%} ({{total_count}}) 17 <input type="radio" name="filter" {%if request.GET.filter%}{{f.options|check_filter_status:request.GET.filter}} {%else%} checked {%endif%} value="" data-key="{{key}}"> All {%if filter_search_display%}{{filter_search_display|title}}{%else%}{{objectname|title}}{%endif%}
17 </label> 18 </label>
18 {% for option in f.options %} 19 {% for option in f.options %}
19 {% if option.2 %} 20 {% if option.1 == 'daterange' %}
20 <label class="radio"> 21 <div class="form-inline">
21 <input type="radio" name="filter" {%if request.GET.filter == option.1 %}checked{%endif%} value="{{option.1}}"> {{option.0}} (<span id="{{option.1}}_count">{{option.2}}</span>) 22 <label class="radio">
23 <input type="radio" name="filter" id="filter_value_{{key}}" {%if key == daterange_selected %}checked{%endif%} value="{{option.1}}" data-key="{{key}}"> {{option.0}}
22 {% else %} 24 {% else %}
23 <label class="radio muted"> 25 {% if 1 %}
24 <input type="radio" name="filter" disabled {%if request.GET.filter == option.1 %}checked{%endif%} value="{{option.1}}"> {{option.0}} (<span id="{{option.1}}_count">{{option.2}}</span>) 26 <label class="radio">
27 <input type="radio" name="filter" {%if request.GET.filter == option.1 %}checked{%endif%} value="{{option.1}}" data-key="{{key}}"> {{option.0}}
28 {% comment "do not disable radio selections by count for now" %}{% else %}
29 <label class="radio muted">
30 <input type="radio" name="filter" disabled {%if request.GET.filter == option.1 %}checked{%endif%} value="{{option.1}}" data-key="{{key}}"> {{option.0}}
31 {% endcomment %}{% endif %}
25 {% endif %} 32 {% endif %}
26 {% if option.3 %}<i class="icon-question-sign get-help" data-placement="right" title="{{option.3}}"></i>{% endif %} 33 {% if option.3 %}<i class="icon-question-sign get-help" data-placement="right" title="{{option.3}}"></i>{% endif %}
27 </label> 34 </label>
35 {% if option.1 == 'daterange' %}
36 <input type="text" id="date_from_{{key}}" name="date_from_{{key}}" disabled class="input-small" /><label class="help-inline">to</label>
37 <input type="text" id="date_to_{{key}}" name="date_to_{{key}}" disabled class="input-small" />
38 <label class="help-inline get-help" >(dd/mm/yyyy)</label>
39 </div>
40 {% endif %}
28 {% endfor %} 41 {% endfor %}
42 <!-- daterange persistence -->
43 <input type="hidden" id="last_date_from_{{key}}" name="last_date_from" value="{{last_date_from}}"/>
44 <input type="hidden" id="last_date_to_{{key}}" name="last_date_to" value="{{last_date_to}}"/>
29 </div> 45 </div>
30 <div class="modal-footer"> 46 <div class="modal-footer">
31 <button type="submit" class="btn btn-primary">Apply</button> 47 <button type="submit" class="btn btn-primary" data-key="{{key}}">Apply</button>
32 {% if request.GET.filter %} 48 {% if request.GET.filter %}
33 {% if request.GET.filter|string_remove_regex:':.*' != f.options.0.1|string_remove_regex:':.*' %} 49 {% if request.GET.filter|string_remove_regex:':.*' != f.options.0.1|string_remove_regex:':.*' %}
34 <span class="help-inline pull-left">You can only apply one filter to the table. This filter will override the current filter.</span> 50 <span class="help-inline pull-left">You can only apply one filter to the table. This filter will override the current filter.</span>
@@ -36,4 +52,4 @@
36 {% endif %} 52 {% endif %}
37 </div> 53 </div>
38</form> 54</form>
39 55{% endwith %}
diff --git a/bitbake/lib/toaster/toastergui/templates/managed_builds.html b/bitbake/lib/toaster/toastergui/templates/managed_builds.html
index e23b832bae..63ae5408f5 100644
--- a/bitbake/lib/toaster/toastergui/templates/managed_builds.html
+++ b/bitbake/lib/toaster/toastergui/templates/managed_builds.html
@@ -4,7 +4,24 @@
4{% load projecttags %} 4{% load projecttags %}
5{% load humanize %} 5{% load humanize %}
6 6
7{% block extraheadcontent %}
8<link rel="stylesheet" href="/static/css/jquery-ui.min.css" type='text/css'>
9<link rel="stylesheet" href="/static/css/jquery-ui.structure.min.css" type='text/css'>
10<link rel="stylesheet" href="/static/css/jquery-ui.theme.min.css" type='text/css'>
11<script src="/static/js/jquery-ui.min.js"></script>
12<script src="/static/js/filtersnippet.js"></script>
13{% endblock %}
14
7{% block pagecontent %} 15{% block pagecontent %}
16
17<script>
18 // initialize the date range controls
19 $(document).ready(function () {
20 date_init('created','{{last_date_from}}','{{last_date_to}}','{{dateMin_created}}','{{dateMax_created}}','{{daterange_selected}}');
21 date_init('updated','{{last_date_from}}','{{last_date_to}}','{{dateMin_updated}}','{{dateMax_updated}}','{{daterange_selected}}');
22 });
23</script>
24
8<div class="row-fluid"> 25<div class="row-fluid">
9 26
10 {% include "managed_mrb_section.html" %} 27 {% include "managed_mrb_section.html" %}
@@ -47,7 +64,7 @@
47 {% for buildrequest in objects %}{% if buildrequest.build %} {% with build=buildrequest.build %} {# if we have a build, just display it #} 64 {% for buildrequest in objects %}{% if buildrequest.build %} {% with build=buildrequest.build %} {# if we have a build, just display it #}
48 <tr class="data"> 65 <tr class="data">
49 <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a> 66 <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a>
50 {% if build.project %} 67 {% if build.project %}
51 &nbsp; <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}"> 68 &nbsp; <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}">
52 <i class="icon-download-alt" title="" data-original-title="Download build log"></i> 69 <i class="icon-download-alt" title="" data-original-title="Download build log"></i>
53 </a> 70 </a>
diff --git a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
index 0ccf73a619..54700e3842 100644
--- a/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
+++ b/bitbake/lib/toaster/toastergui/templatetags/projecttags.py
@@ -125,6 +125,8 @@ def filtered_icon(options, filter):
125 for option in options: 125 for option in options:
126 if filter == option[1]: 126 if filter == option[1]:
127 return "btn-primary" 127 return "btn-primary"
128 if ('daterange' == option[1]) and filter.startswith(option[4]):
129 return "btn-primary"
128 return "" 130 return ""
129 131
130@register.filter 132@register.filter
@@ -134,6 +136,8 @@ def filtered_tooltip(options, filter):
134 for option in options: 136 for option in options:
135 if filter == option[1]: 137 if filter == option[1]:
136 return "Showing only %s"%option[0] 138 return "Showing only %s"%option[0]
139 if ('daterange' == option[1]) and filter.startswith(option[4]):
140 return "Showing only %s"%option[0]
137 return "" 141 return ""
138 142
139@register.filter 143@register.filter
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 4ebcf6d54f..d999959446 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -35,7 +35,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
35from django.http import HttpResponseBadRequest, HttpResponseNotFound 35from django.http import HttpResponseBadRequest, HttpResponseNotFound
36from django.utils import timezone 36from django.utils import timezone
37from django.utils.html import escape 37from django.utils.html import escape
38from datetime import timedelta 38from datetime import timedelta, datetime, date
39from django.utils import formats 39from django.utils import formats
40from toastergui.templatetags.projecttags import json as jsonfilter 40from toastergui.templatetags.projecttags import json as jsonfilter
41import json 41import json
@@ -279,6 +279,65 @@ def _save_parameters_cookies(response, pagesize, orderby, request):
279 response.set_cookie(key='orderby', value=html_parser.unescape(orderby), path=request.path) 279 response.set_cookie(key='orderby', value=html_parser.unescape(orderby), path=request.path)
280 return response 280 return response
281 281
282# date range: normalize GUI's dd/mm/yyyy to date object
283def _normalize_input_date(date_str,default):
284 date_str=re.sub('/', '-', date_str)
285 # accept dd/mm/yyyy to d/m/yy
286 try:
287 date_in = datetime.strptime(date_str, "%d-%m-%Y")
288 except ValueError:
289 # courtesy try with two digit year
290 try:
291 date_in = datetime.strptime(date_str, "%d-%m-%y")
292 except ValueError:
293 return default
294 date_in = date_in.replace(tzinfo=default.tzinfo)
295 return date_in
296
297# convert and normalize any received date range filter, for example:
298# "completed_on__gte!completed_on__lt:01/03/2015!02/03/2015_daterange" to
299# "completed_on__gte!completed_on__lt:2015-03-01!2015-03-02"
300def _modify_date_range_filter(filter_string):
301 # was the date range radio button selected?
302 if 0 > filter_string.find('_daterange'):
303 return filter_string,''
304 # normalize GUI dates to database format
305 filter_string = filter_string.replace('_daterange','').replace(':','!');
306 filter_list = filter_string.split('!');
307 if 4 != len(filter_list):
308 return filter_string
309 today = timezone.localtime(timezone.now())
310 date_id = filter_list[1]
311 date_from = _normalize_input_date(filter_list[2],today)
312 date_to = _normalize_input_date(filter_list[3],today)
313 # swap dates if manually set dates are out of order
314 if date_to < date_from:
315 date_to,date_from = date_from,date_to
316 # convert to strings, make 'date_to' inclusive by moving to begining of next day
317 date_from_str = date_from.strftime("%Y-%m-%d")
318 date_to_str = (date_to+timedelta(days=1)).strftime("%Y-%m-%d")
319 filter_string=filter_list[0]+'!'+filter_list[1]+':'+date_from_str+'!'+date_to_str
320 daterange_selected = re.sub('__.*','', date_id)
321 return filter_string,daterange_selected
322
323def _add_daterange_context(queryset_all, request, daterange_list):
324 # calculate the exact begining of local today and yesterday
325 today_begin = timezone.localtime(timezone.now())
326 today_begin = date(today_begin.year,today_begin.month,today_begin.day)
327 yesterday_begin = today_begin-timedelta(days=1)
328 # add daterange persistent
329 context_date = {}
330 context_date['last_date_from'] = request.GET.get('last_date_from',timezone.localtime(timezone.now()).strftime("%d/%m/%Y"))
331 context_date['last_date_to' ] = request.GET.get('last_date_to' ,context_date['last_date_from'])
332 # calculate the date ranges, avoid second sort for 'created'
333 # fetch the respective max range from the database
334 context_date['daterange_filter']=''
335 for key in daterange_list:
336 queryset_key = queryset_all.order_by(key)
337 context_date['dateMin_'+key]=timezone.localtime(getattr(queryset_key.first(),key)).strftime("%d/%m/%Y")
338 context_date['dateMax_'+key]=timezone.localtime(getattr(queryset_key.last(),key)).strftime("%d/%m/%Y")
339 return context_date,today_begin,yesterday_begin
340
282 341
283## 342##
284# build dashboard for a single build, coming in as argument 343# build dashboard for a single build, coming in as argument
@@ -1868,6 +1927,9 @@ if toastermain.settings.MANAGED:
1868 # boilerplate code that takes a request for an object type and returns a queryset 1927 # boilerplate code that takes a request for an object type and returns a queryset
1869 # for that object type. copypasta for all needed table searches 1928 # for that object type. copypasta for all needed table searches
1870 (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest) 1929 (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest)
1930 # post-process any date range filters
1931 filter_string,daterange_selected = _modify_date_range_filter(filter_string)
1932
1871 # we don't display in-progress or deleted builds 1933 # we don't display in-progress or deleted builds
1872 queryset_all = buildrequests.exclude(state = BuildRequest.REQ_DELETED) 1934 queryset_all = buildrequests.exclude(state = BuildRequest.REQ_DELETED)
1873 queryset_all = queryset_all.select_related("build", "build__project").annotate(Count('brerror')) 1935 queryset_all = queryset_all.select_related("build", "build__project").annotate(Count('brerror'))
@@ -1918,6 +1980,7 @@ if toastermain.settings.MANAGED:
1918 'fstypes' : fstypes_map, 1980 'fstypes' : fstypes_map,
1919 'search_term' : search_term, 1981 'search_term' : search_term,
1920 'total_count' : queryset_with_search.count(), 1982 'total_count' : queryset_with_search.count(),
1983 'daterange_selected' : daterange_selected,
1921 # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns 1984 # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns
1922 'tablecols' : [ 1985 'tablecols' : [
1923 {'name': 'Outcome', # column with a single filter 1986 {'name': 'Outcome', # column with a single filter
@@ -1956,6 +2019,10 @@ if toastermain.settings.MANAGED:
1956 } 2019 }
1957 ) 2020 )
1958 2021
2022 # calculate the exact begining of local today and yesterday
2023 context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'created','updated'})
2024 context.update(context_date)
2025
1959 context['tablecols'].append( 2026 context['tablecols'].append(
1960 {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column 2027 {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column
1961 'qhelp': "The date and time you started the build", 2028 'qhelp': "The date and time you started the build",
@@ -1964,9 +2031,16 @@ if toastermain.settings.MANAGED:
1964 'filter' : {'class' : 'created', 2031 'filter' : {'class' : 'created',
1965 'label': 'Show:', 2032 'label': 'Show:',
1966 'options' : [ 2033 'options' : [
1967 ("Today's builds" , 'created__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_all.filter(created__gte=timezone.now()).count()), 2034 ("Today's builds" , 'created__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(created__gte=today_begin).count()),
1968 ("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()), 2035 ("Yesterday's builds",
1969 ("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()), 2036 'created__gte!created__lt:'
2037 +yesterday_begin.strftime("%Y-%m-%d")+'!'
2038 +today_begin.strftime("%Y-%m-%d"),
2039 queryset_all.filter(
2040 created__gte=yesterday_begin,
2041 created__lt=today_begin
2042 ).count()),
2043 ("Build date range", 'daterange', 1, '', 'created'),
1970 ] 2044 ]
1971 } 2045 }
1972 } 2046 }
@@ -1980,9 +2054,16 @@ if toastermain.settings.MANAGED:
1980 'filter' : {'class' : 'updated', 2054 'filter' : {'class' : 'updated',
1981 'label': 'Show:', 2055 'label': 'Show:',
1982 'options' : [ 2056 'options' : [
1983 ("Today's builds", 'updated__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=timezone.now()).count()), 2057 ("Today's builds" , 'updated__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=today_begin).count()),
1984 ("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()), 2058 ("Yesterday's builds",
1985 ("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()), 2059 'updated__gte!updated__lt:'
2060 +yesterday_begin.strftime("%Y-%m-%d")+'!'
2061 +today_begin.strftime("%Y-%m-%d"),
2062 queryset_all.filter(
2063 updated__gte=yesterday_begin,
2064 updated__lt=today_begin
2065 ).count()),
2066 ("Build date range", 'daterange', 1, '', 'updated'),
1986 ] 2067 ]
1987 } 2068 }
1988 } 2069 }
@@ -3280,6 +3361,8 @@ else:
3280 # boilerplate code that takes a request for an object type and returns a queryset 3361 # boilerplate code that takes a request for an object type and returns a queryset
3281 # for that object type. copypasta for all needed table searches 3362 # for that object type. copypasta for all needed table searches
3282 (filter_string, search_term, ordering_string) = _search_tuple(request, Build) 3363 (filter_string, search_term, ordering_string) = _search_tuple(request, Build)
3364 # post-process any date range filters
3365 filter_string,daterange_selected = _modify_date_range_filter(filter_string)
3283 queryset_all = Build.objects.exclude(outcome = Build.IN_PROGRESS) 3366 queryset_all = Build.objects.exclude(outcome = Build.IN_PROGRESS)
3284 queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on') 3367 queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on')
3285 queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on') 3368 queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on')
@@ -3290,6 +3373,9 @@ else:
3290 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) 3373 # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
3291 build_mru = Build.objects.order_by("-started_on")[:3] 3374 build_mru = Build.objects.order_by("-started_on")[:3]
3292 3375
3376 # calculate the exact begining of local today and yesterday, append context
3377 context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'started_on','completed_on'})
3378
3293 # set up list of fstypes for each build 3379 # set up list of fstypes for each build
3294 fstypes_map = {}; 3380 fstypes_map = {};
3295 for build in build_info: 3381 for build in build_info:
@@ -3320,6 +3406,7 @@ else:
3320 'fstypes' : fstypes_map, 3406 'fstypes' : fstypes_map,
3321 'search_term' : search_term, 3407 'search_term' : search_term,
3322 'total_count' : queryset_with_search.count(), 3408 'total_count' : queryset_with_search.count(),
3409 'daterange_selected' : daterange_selected,
3323 # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns 3410 # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns
3324 'tablecols' : [ 3411 'tablecols' : [
3325 {'name': 'Outcome', # column with a single filter 3412 {'name': 'Outcome', # column with a single filter
@@ -3356,12 +3443,19 @@ else:
3356 'filter' : {'class' : 'started_on', 3443 'filter' : {'class' : 'started_on',
3357 'label': 'Show:', 3444 'label': 'Show:',
3358 'options' : [ 3445 'options' : [
3359 ("Today's builds" , 'started_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=timezone.now()).count()), 3446 ("Today's builds" , 'started_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(started_on__gte=today_begin).count()),
3360 ("Yesterday's builds", 'started_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(hours=24))).count()), 3447 ("Yesterday's builds",
3361 ("This week's builds", 'started_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(days=7))).count()), 3448 'started_on__gte!started_on__lt:'
3449 +yesterday_begin.strftime("%Y-%m-%d")+'!'
3450 +today_begin.strftime("%Y-%m-%d"),
3451 queryset_all.filter(
3452 started_on__gte=yesterday_begin,
3453 started_on__lt=today_begin
3454 ).count()),
3455 ("Build date range", 'daterange', 1, '', 'started_on'),
3362 ] 3456 ]
3363 } 3457 }
3364 }, 3458 },
3365 {'name': 'Completed on', 3459 {'name': 'Completed on',
3366 'qhelp': "The date and time the build finished", 3460 'qhelp': "The date and time the build finished",
3367 'orderfield': _get_toggle_order(request, "completed_on", True), 3461 'orderfield': _get_toggle_order(request, "completed_on", True),
@@ -3370,9 +3464,16 @@ else:
3370 'filter' : {'class' : 'completed_on', 3464 'filter' : {'class' : 'completed_on',
3371 'label': 'Show:', 3465 'label': 'Show:',
3372 'options' : [ 3466 'options' : [
3373 ("Today's builds", 'completed_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=timezone.now()).count()), 3467 ("Today's builds" , 'completed_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(completed_on__gte=today_begin).count()),
3374 ("Yesterday's builds", 'completed_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(hours=24))).count()), 3468 ("Yesterday's builds",
3375 ("This week's builds", 'completed_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(days=7))).count()), 3469 'completed_on__gte!completed_on__lt:'
3470 +yesterday_begin.strftime("%Y-%m-%d")+'!'
3471 +today_begin.strftime("%Y-%m-%d"),
3472 queryset_all.filter(
3473 completed_on__gte=yesterday_begin,
3474 completed_on__lt=today_begin
3475 ).count()),
3476 ("Build date range", 'daterange', 1, '', 'completed_on'),
3376 ] 3477 ]
3377 } 3478 }
3378 }, 3479 },
@@ -3433,6 +3534,9 @@ else:
3433 ] 3534 ]
3434 } 3535 }
3435 3536
3537 # merge daterange values
3538 context.update(context_date)
3539
3436 response = render(request, template, context) 3540 response = render(request, template, context)
3437 _save_parameters_cookies(response, pagesize, orderby, request) 3541 _save_parameters_cookies(response, pagesize, orderby, request)
3438 return response 3542 return response