diff options
author | David Reyna <David.Reyna@windriver.com> | 2015-03-21 18:01:38 -0700 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-03-24 22:54:37 +0000 |
commit | 7c86ed5fb51c6237fa40fb454e58564ef027dd51 (patch) | |
tree | c0a3479696587db7f6ec4dcf90fdecfb651c1313 | |
parent | cedebbb47192be85ff0b1e4a313c6f9cf737a9c6 (diff) | |
download | bitbake-7c86ed5fb51c6237fa40fb454e58564ef027dd51.tar.gz |
toaster: build date range selections
Enable date range selections for build start and build complete in all
builds page for both managed and interactive mode. Disable the filter
counts.
[YOCTO #6040]
[YOCTO #7249]
[YOCTO #7461]
Signed-off-by: David Reyna <David.Reyna@windriver.com>
-rwxr-xr-x | lib/toaster/toastergui/static/js/filtersnippet.js | 95 | ||||
-rw-r--r-- | lib/toaster/toastergui/templates/build.html | 17 | ||||
-rw-r--r-- | lib/toaster/toastergui/templates/filtersnippet.html | 34 | ||||
-rw-r--r-- | lib/toaster/toastergui/templates/managed_builds.html | 19 | ||||
-rw-r--r-- | lib/toaster/toastergui/templatetags/projecttags.py | 4 | ||||
-rwxr-xr-x | lib/toaster/toastergui/views.py | 132 |
6 files changed, 277 insertions, 24 deletions
diff --git a/lib/toaster/toastergui/static/js/filtersnippet.js b/lib/toaster/toastergui/static/js/filtersnippet.js new file mode 100755 index 000000000..27b057e64 --- /dev/null +++ b/lib/toaster/toastergui/static/js/filtersnippet.js @@ -0,0 +1,95 @@ +"use strict" + +// The disable removes the 'datepicker' attribute and +// settings, so you have to re-initialize it each time +// the date range is selected and enabled +// DOM is used instead of jQuery to find the elements +// in all contexts +function date_enable (key, action) { + + var elemFrom=document.getElementById("date_from_"+key); + var elemTo=document.getElementById("date_to_"+key); + + if ('enable' == action) { + elemFrom.removeAttribute("disabled"); + elemTo.removeAttribute("disabled"); + + $(elemFrom).datepicker(); + $(elemTo).datepicker(); + + $(elemFrom).datepicker( "option", "dateFormat", "dd/mm/yy" ); + $(elemTo).datepicker( "option", "dateFormat", "dd/mm/yy" ); + + $(elemFrom).datepicker( "setDate", elemFrom.getAttribute( "data-setDate") ); + $(elemTo).datepicker( "setDate", elemTo.getAttribute( "data-setDate") ); + $(elemFrom).datepicker( "option", "minDate", elemFrom.getAttribute( "data-minDate")); + $(elemTo).datepicker( "option", "minDate", elemTo.getAttribute( "data-minDate")); + $(elemFrom).datepicker( "option", "maxDate", elemFrom.getAttribute( "data-maxDate")); + $(elemTo).datepicker( "option", "maxDate", elemTo.getAttribute( "data-maxDate")); + } else { + elemFrom.setAttribute("disabled","disabled"); + elemTo.setAttribute("disabled","disabled"); + } +} + +// Initialize the date picker elements with their default state variables, and +// register the radio button and form actions +function date_init (key, from_date, to_date, min_date, max_date, initial_enable) { + + var elemFrom=document.getElementById("date_from_"+key); + var elemTo=document.getElementById("date_to_"+key); + + // Were there any daterange filters instantiated? (e.g. no builds found) + if (null == elemFrom) { + return; + } + + // init the datepicker context data + elemFrom.setAttribute( "data-setDate", from_date ); + elemTo.setAttribute( "data-setDate", to_date ); + elemFrom.setAttribute( "data-minDate", min_date); + elemTo.setAttribute( "data-minDate", min_date); + elemFrom.setAttribute( "data-maxDate", max_date); + elemTo.setAttribute( "data-maxDate", max_date); + + // does the date set start enabled? + if (key == initial_enable) { + date_enable (key, "enable"); + } else { + date_enable (key, "disable"); + } + + // catch the radio button selects for enable/disable + $('input:radio[name="filter"]').change(function(){ + if ($(this).val() == 'daterange') { + key=$(this).attr("data-key"); + date_enable (key, 'enable'); + } else { + key=$(this).attr("data-key"); + date_enable (key, 'disable'); + } + }); + + // catch any new 'from' date as minDate for 'to' date + $("#date_from_"+key).change(function(){ + from_date = $("#date_from_"+key).val(); + $("#date_to_"+key).datepicker( "option", "minDate", from_date ); + }); + + // catch the submit (just once) + $("form").unbind('submit'); + $("form").submit(function(e) { + // format a composite daterange filter value so that it can be parsed and post-processed in the view + var key=e.originalEvent.explicitOriginalTarget.getAttribute("data-key") + if (typeof key != "undefined") { + if ($("#date_from_"+key).length) { + var filter=key+"__gte!"+key+"__lt:"+$("#date_from_"+key).val()+"!"+$("#date_to_"+key).val()+"_daterange"; + $("#last_date_from_"+key).val($("#date_from_"+key).val()); + $("#last_date_to_"+key).val($("#date_to_"+key).val()); + $("#filter_value_"+key).val(filter); + } + } + return true; + }); + +}; diff --git a/lib/toaster/toastergui/templates/build.html b/lib/toaster/toastergui/templates/build.html index 684ec6588..f7ad2f489 100644 --- a/lib/toaster/toastergui/templates/build.html +++ b/lib/toaster/toastergui/templates/build.html @@ -4,7 +4,24 @@ {% load projecttags %} {% load humanize %} +{% block extraheadcontent %} +<link rel="stylesheet" href="/static/css/jquery-ui.min.css" type='text/css'> +<link rel="stylesheet" href="/static/css/jquery-ui.structure.min.css" type='text/css'> +<link rel="stylesheet" href="/static/css/jquery-ui.theme.min.css" type='text/css'> +<script src="/static/js/jquery-ui.min.js"></script> +<script src="/static/js/filtersnippet.js"></script> +{% endblock %} + {% block pagecontent %} + +<script> + // intiialize the date range controls + $(document).ready(function () { + date_init('started_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_started_on}}','{{dateMax_started_on}}','{{daterange_selected}}'); + date_init('completed_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_completed_on}}','{{dateMax_completed_on}}','{{daterange_selected}}'); + }); +</script> + <div class="row-fluid"> {% include "mrb_section.html" %} diff --git a/lib/toaster/toastergui/templates/filtersnippet.html b/lib/toaster/toastergui/templates/filtersnippet.html index fe70e7143..f624d88ba 100644 --- a/lib/toaster/toastergui/templates/filtersnippet.html +++ b/lib/toaster/toastergui/templates/filtersnippet.html @@ -1,5 +1,6 @@ {% load projecttags %} <!-- '{{f.class}}' filter --> +{% with f.class as key %} <form id="filter_{{f.class}}" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true"> <input type="hidden" name="search" value="{{request.GET.search}}"/> <div class="modal-header"> @@ -13,22 +14,37 @@ <div class="modal-body"> <p>{{f.label}}</p> <label class="radio"> - <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}}) + <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%} </label> {% for option in f.options %} - {% if option.2 %} - <label class="radio"> - <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>) + {% if option.1 == 'daterange' %} + <div class="form-inline"> + <label class="radio"> + <input type="radio" name="filter" id="filter_value_{{key}}" {%if key == daterange_selected %}checked{%endif%} value="{{option.1}}" data-key="{{key}}"> {{option.0}} {% else %} - <label class="radio muted"> - <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>) + {% if 1 %} + <label class="radio"> + <input type="radio" name="filter" {%if request.GET.filter == option.1 %}checked{%endif%} value="{{option.1}}" data-key="{{key}}"> {{option.0}} + {% comment "do not disable radio selections by count for now" %}{% else %} + <label class="radio muted"> + <input type="radio" name="filter" disabled {%if request.GET.filter == option.1 %}checked{%endif%} value="{{option.1}}" data-key="{{key}}"> {{option.0}} + {% endcomment %}{% endif %} {% endif %} {% if option.3 %}<i class="icon-question-sign get-help" data-placement="right" title="{{option.3}}"></i>{% endif %} - </label> + </label> + {% if option.1 == 'daterange' %} + <input type="text" id="date_from_{{key}}" name="date_from_{{key}}" disabled class="input-small" /><label class="help-inline">to</label> + <input type="text" id="date_to_{{key}}" name="date_to_{{key}}" disabled class="input-small" /> + <label class="help-inline get-help" >(dd/mm/yyyy)</label> + </div> + {% endif %} {% endfor %} + <!-- daterange persistence --> + <input type="hidden" id="last_date_from_{{key}}" name="last_date_from" value="{{last_date_from}}"/> + <input type="hidden" id="last_date_to_{{key}}" name="last_date_to" value="{{last_date_to}}"/> </div> <div class="modal-footer"> - <button type="submit" class="btn btn-primary">Apply</button> + <button type="submit" class="btn btn-primary" data-key="{{key}}">Apply</button> {% if request.GET.filter %} {% if request.GET.filter|string_remove_regex:':.*' != f.options.0.1|string_remove_regex:':.*' %} <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 @@ {% endif %} </div> </form> - +{% endwith %} diff --git a/lib/toaster/toastergui/templates/managed_builds.html b/lib/toaster/toastergui/templates/managed_builds.html index e23b832ba..63ae5408f 100644 --- a/lib/toaster/toastergui/templates/managed_builds.html +++ b/lib/toaster/toastergui/templates/managed_builds.html @@ -4,7 +4,24 @@ {% load projecttags %} {% load humanize %} +{% block extraheadcontent %} +<link rel="stylesheet" href="/static/css/jquery-ui.min.css" type='text/css'> +<link rel="stylesheet" href="/static/css/jquery-ui.structure.min.css" type='text/css'> +<link rel="stylesheet" href="/static/css/jquery-ui.theme.min.css" type='text/css'> +<script src="/static/js/jquery-ui.min.js"></script> +<script src="/static/js/filtersnippet.js"></script> +{% endblock %} + {% block pagecontent %} + +<script> + // initialize the date range controls + $(document).ready(function () { + date_init('created','{{last_date_from}}','{{last_date_to}}','{{dateMin_created}}','{{dateMax_created}}','{{daterange_selected}}'); + date_init('updated','{{last_date_from}}','{{last_date_to}}','{{dateMin_updated}}','{{dateMax_updated}}','{{daterange_selected}}'); + }); +</script> + <div class="row-fluid"> {% include "managed_mrb_section.html" %} @@ -47,7 +64,7 @@ {% for buildrequest in objects %}{% if buildrequest.build %} {% with build=buildrequest.build %} {# if we have a build, just display it #} <tr class="data"> <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> - {% if build.project %} + {% if build.project %} <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}"> <i class="icon-download-alt" title="" data-original-title="Download build log"></i> </a> diff --git a/lib/toaster/toastergui/templatetags/projecttags.py b/lib/toaster/toastergui/templatetags/projecttags.py index 0ccf73a61..54700e384 100644 --- a/lib/toaster/toastergui/templatetags/projecttags.py +++ b/lib/toaster/toastergui/templatetags/projecttags.py @@ -125,6 +125,8 @@ def filtered_icon(options, filter): for option in options: if filter == option[1]: return "btn-primary" + if ('daterange' == option[1]) and filter.startswith(option[4]): + return "btn-primary" return "" @register.filter @@ -134,6 +136,8 @@ def filtered_tooltip(options, filter): for option in options: if filter == option[1]: return "Showing only %s"%option[0] + if ('daterange' == option[1]) and filter.startswith(option[4]): + return "Showing only %s"%option[0] return "" @register.filter diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py index 4ebcf6d54..d99995944 100755 --- a/lib/toaster/toastergui/views.py +++ b/lib/toaster/toastergui/views.py @@ -35,7 +35,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.http import HttpResponseBadRequest, HttpResponseNotFound from django.utils import timezone from django.utils.html import escape -from datetime import timedelta +from datetime import timedelta, datetime, date from django.utils import formats from toastergui.templatetags.projecttags import json as jsonfilter import json @@ -279,6 +279,65 @@ def _save_parameters_cookies(response, pagesize, orderby, request): response.set_cookie(key='orderby', value=html_parser.unescape(orderby), path=request.path) return response +# date range: normalize GUI's dd/mm/yyyy to date object +def _normalize_input_date(date_str,default): + date_str=re.sub('/', '-', date_str) + # accept dd/mm/yyyy to d/m/yy + try: + date_in = datetime.strptime(date_str, "%d-%m-%Y") + except ValueError: + # courtesy try with two digit year + try: + date_in = datetime.strptime(date_str, "%d-%m-%y") + except ValueError: + return default + date_in = date_in.replace(tzinfo=default.tzinfo) + return date_in + +# convert and normalize any received date range filter, for example: +# "completed_on__gte!completed_on__lt:01/03/2015!02/03/2015_daterange" to +# "completed_on__gte!completed_on__lt:2015-03-01!2015-03-02" +def _modify_date_range_filter(filter_string): + # was the date range radio button selected? + if 0 > filter_string.find('_daterange'): + return filter_string,'' + # normalize GUI dates to database format + filter_string = filter_string.replace('_daterange','').replace(':','!'); + filter_list = filter_string.split('!'); + if 4 != len(filter_list): + return filter_string + today = timezone.localtime(timezone.now()) + date_id = filter_list[1] + date_from = _normalize_input_date(filter_list[2],today) + date_to = _normalize_input_date(filter_list[3],today) + # swap dates if manually set dates are out of order + if date_to < date_from: + date_to,date_from = date_from,date_to + # convert to strings, make 'date_to' inclusive by moving to begining of next day + date_from_str = date_from.strftime("%Y-%m-%d") + date_to_str = (date_to+timedelta(days=1)).strftime("%Y-%m-%d") + filter_string=filter_list[0]+'!'+filter_list[1]+':'+date_from_str+'!'+date_to_str + daterange_selected = re.sub('__.*','', date_id) + return filter_string,daterange_selected + +def _add_daterange_context(queryset_all, request, daterange_list): + # calculate the exact begining of local today and yesterday + today_begin = timezone.localtime(timezone.now()) + today_begin = date(today_begin.year,today_begin.month,today_begin.day) + yesterday_begin = today_begin-timedelta(days=1) + # add daterange persistent + context_date = {} + context_date['last_date_from'] = request.GET.get('last_date_from',timezone.localtime(timezone.now()).strftime("%d/%m/%Y")) + context_date['last_date_to' ] = request.GET.get('last_date_to' ,context_date['last_date_from']) + # calculate the date ranges, avoid second sort for 'created' + # fetch the respective max range from the database + context_date['daterange_filter']='' + for key in daterange_list: + queryset_key = queryset_all.order_by(key) + context_date['dateMin_'+key]=timezone.localtime(getattr(queryset_key.first(),key)).strftime("%d/%m/%Y") + context_date['dateMax_'+key]=timezone.localtime(getattr(queryset_key.last(),key)).strftime("%d/%m/%Y") + return context_date,today_begin,yesterday_begin + ## # build dashboard for a single build, coming in as argument @@ -1868,6 +1927,9 @@ if toastermain.settings.MANAGED: # boilerplate code that takes a request for an object type and returns a queryset # for that object type. copypasta for all needed table searches (filter_string, search_term, ordering_string) = _search_tuple(request, BuildRequest) + # post-process any date range filters + filter_string,daterange_selected = _modify_date_range_filter(filter_string) + # we don't display in-progress or deleted builds queryset_all = buildrequests.exclude(state = BuildRequest.REQ_DELETED) queryset_all = queryset_all.select_related("build", "build__project").annotate(Count('brerror')) @@ -1918,6 +1980,7 @@ if toastermain.settings.MANAGED: 'fstypes' : fstypes_map, 'search_term' : search_term, 'total_count' : queryset_with_search.count(), + 'daterange_selected' : daterange_selected, # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns 'tablecols' : [ {'name': 'Outcome', # column with a single filter @@ -1956,6 +2019,10 @@ if toastermain.settings.MANAGED: } ) + # calculate the exact begining of local today and yesterday + context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'created','updated'}) + context.update(context_date) + context['tablecols'].append( {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column 'qhelp': "The date and time you started the build", @@ -1964,9 +2031,16 @@ if toastermain.settings.MANAGED: 'filter' : {'class' : 'created', 'label': 'Show:', 'options' : [ - ("Today's builds" , 'created__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_all.filter(created__gte=timezone.now()).count()), - ("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()), - ("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()), + ("Today's builds" , 'created__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(created__gte=today_begin).count()), + ("Yesterday's builds", + 'created__gte!created__lt:' + +yesterday_begin.strftime("%Y-%m-%d")+'!' + +today_begin.strftime("%Y-%m-%d"), + queryset_all.filter( + created__gte=yesterday_begin, + created__lt=today_begin + ).count()), + ("Build date range", 'daterange', 1, '', 'created'), ] } } @@ -1980,9 +2054,16 @@ if toastermain.settings.MANAGED: 'filter' : {'class' : 'updated', 'label': 'Show:', 'options' : [ - ("Today's builds", 'updated__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=timezone.now()).count()), - ("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()), - ("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()), + ("Today's builds" , 'updated__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=today_begin).count()), + ("Yesterday's builds", + 'updated__gte!updated__lt:' + +yesterday_begin.strftime("%Y-%m-%d")+'!' + +today_begin.strftime("%Y-%m-%d"), + queryset_all.filter( + updated__gte=yesterday_begin, + updated__lt=today_begin + ).count()), + ("Build date range", 'daterange', 1, '', 'updated'), ] } } @@ -3280,6 +3361,8 @@ else: # boilerplate code that takes a request for an object type and returns a queryset # for that object type. copypasta for all needed table searches (filter_string, search_term, ordering_string) = _search_tuple(request, Build) + # post-process any date range filters + filter_string,daterange_selected = _modify_date_range_filter(filter_string) queryset_all = Build.objects.exclude(outcome = Build.IN_PROGRESS) queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on') queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on') @@ -3290,6 +3373,9 @@ else: # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) build_mru = Build.objects.order_by("-started_on")[:3] + # calculate the exact begining of local today and yesterday, append context + context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'started_on','completed_on'}) + # set up list of fstypes for each build fstypes_map = {}; for build in build_info: @@ -3320,6 +3406,7 @@ else: 'fstypes' : fstypes_map, 'search_term' : search_term, 'total_count' : queryset_with_search.count(), + 'daterange_selected' : daterange_selected, # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns 'tablecols' : [ {'name': 'Outcome', # column with a single filter @@ -3356,12 +3443,19 @@ else: 'filter' : {'class' : 'started_on', 'label': 'Show:', 'options' : [ - ("Today's builds" , 'started_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=timezone.now()).count()), - ("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()), - ("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()), + ("Today's builds" , 'started_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(started_on__gte=today_begin).count()), + ("Yesterday's builds", + 'started_on__gte!started_on__lt:' + +yesterday_begin.strftime("%Y-%m-%d")+'!' + +today_begin.strftime("%Y-%m-%d"), + queryset_all.filter( + started_on__gte=yesterday_begin, + started_on__lt=today_begin + ).count()), + ("Build date range", 'daterange', 1, '', 'started_on'), ] } - }, + }, {'name': 'Completed on', 'qhelp': "The date and time the build finished", 'orderfield': _get_toggle_order(request, "completed_on", True), @@ -3370,9 +3464,16 @@ else: 'filter' : {'class' : 'completed_on', 'label': 'Show:', 'options' : [ - ("Today's builds", 'completed_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=timezone.now()).count()), - ("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()), - ("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()), + ("Today's builds" , 'completed_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(completed_on__gte=today_begin).count()), + ("Yesterday's builds", + 'completed_on__gte!completed_on__lt:' + +yesterday_begin.strftime("%Y-%m-%d")+'!' + +today_begin.strftime("%Y-%m-%d"), + queryset_all.filter( + completed_on__gte=yesterday_begin, + completed_on__lt=today_begin + ).count()), + ("Build date range", 'daterange', 1, '', 'completed_on'), ] } }, @@ -3433,6 +3534,9 @@ else: ] } + # merge daterange values + context.update(context_date) + response = render(request, template, context) _save_parameters_cookies(response, pagesize, orderby, request) return response |