--- /dev/null
+"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;
+ });
+
+};
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
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
# 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'))
'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
}
)
+ # 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",
'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'),
]
}
}
'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'),
]
}
}
# 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')
# 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:
'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
'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),
'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'),
]
}
},
]
}
+ # merge daterange values
+ context.update(context_date)
+
response = render(request, template, context)
_save_parameters_cookies(response, pagesize, orderby, request)
return response