diff options
6 files changed, 212 insertions, 84 deletions
diff --git a/bitbake/lib/toaster/toastergui/querysetfilter.py b/bitbake/lib/toaster/toastergui/querysetfilter.py index efa8507050..10cc988bce 100644 --- a/bitbake/lib/toaster/toastergui/querysetfilter.py +++ b/bitbake/lib/toaster/toastergui/querysetfilter.py | |||
| @@ -22,7 +22,3 @@ class QuerysetFilter(object): | |||
| 22 | return queryset.filter(self.criteria) | 22 | return queryset.filter(self.criteria) |
| 23 | else: | 23 | else: |
| 24 | return queryset | 24 | return queryset |
| 25 | |||
| 26 | def count(self, queryset): | ||
| 27 | """ Returns a count of the elements in the filtered queryset """ | ||
| 28 | return self.filter(queryset).count() | ||
diff --git a/bitbake/lib/toaster/toastergui/static/js/table.js b/bitbake/lib/toaster/toastergui/static/js/table.js index b0a8ffb8f9..afe16b5e1b 100644 --- a/bitbake/lib/toaster/toastergui/static/js/table.js +++ b/bitbake/lib/toaster/toastergui/static/js/table.js | |||
| @@ -71,22 +71,11 @@ function tableInit(ctx){ | |||
| 71 | 71 | ||
| 72 | if (tableData.total === 0){ | 72 | if (tableData.total === 0){ |
| 73 | tableContainer.hide(); | 73 | tableContainer.hide(); |
| 74 | /* If we were searching show the new search bar and return */ | 74 | $("#new-search-input-"+ctx.tableName).val(tableParams.search); |
| 75 | if (tableParams.search){ | 75 | $("#no-results-"+ctx.tableName).show(); |
| 76 | $("#new-search-input-"+ctx.tableName).val(tableParams.search); | ||
| 77 | $("#no-results-"+ctx.tableName).show(); | ||
| 78 | } | ||
| 79 | table.trigger("table-done", [tableData.total, tableParams]); | 76 | table.trigger("table-done", [tableData.total, tableParams]); |
| 80 | 77 | ||
| 81 | return; | 78 | return; |
| 82 | |||
| 83 | /* We don't want to clutter the place with the table chrome if there | ||
| 84 | * are only a few results */ | ||
| 85 | } else if (tableData.total <= 10 && | ||
| 86 | !tableParams.filter && | ||
| 87 | !tableParams.search){ | ||
| 88 | $("#table-chrome-"+ctx.tableName).hide(); | ||
| 89 | pagination.hide(); | ||
| 90 | } else { | 79 | } else { |
| 91 | tableContainer.show(); | 80 | tableContainer.show(); |
| 92 | $("#no-results-"+ctx.tableName).hide(); | 81 | $("#no-results-"+ctx.tableName).hide(); |
| @@ -399,13 +388,14 @@ function tableInit(ctx){ | |||
| 399 | 388 | ||
| 400 | /** | 389 | /** |
| 401 | * Create the DOM/JS for the client side of a TableFilterActionToggle | 390 | * Create the DOM/JS for the client side of a TableFilterActionToggle |
| 391 | * or TableFilterActionDay | ||
| 402 | * | 392 | * |
| 403 | * filterName: (string) internal name for the filter action | 393 | * filterName: (string) internal name for the filter action |
| 404 | * filterActionData: (object) | 394 | * filterActionData: (object) |
| 405 | * filterActionData.count: (number) The number of items this filter will | 395 | * filterActionData.count: (number) The number of items this filter will |
| 406 | * show when selected | 396 | * show when selected |
| 407 | */ | 397 | */ |
| 408 | function createActionToggle(filterName, filterActionData) { | 398 | function createActionRadio(filterName, filterActionData) { |
| 409 | var actionStr = '<div class="radio">' + | 399 | var actionStr = '<div class="radio">' + |
| 410 | '<input type="radio" name="filter"' + | 400 | '<input type="radio" name="filter"' + |
| 411 | ' value="' + filterName + '"'; | 401 | ' value="' + filterName + '"'; |
| @@ -471,8 +461,7 @@ function tableInit(ctx){ | |||
| 471 | minDate: new Date(filterActionData.min) | 461 | minDate: new Date(filterActionData.min) |
| 472 | }; | 462 | }; |
| 473 | 463 | ||
| 474 | // create date pickers, setting currently-selected from and to | 464 | // create date pickers, setting currently-selected from and to dates |
| 475 | // dates | ||
| 476 | var selectedFrom = null; | 465 | var selectedFrom = null; |
| 477 | var selectedTo = null; | 466 | var selectedTo = null; |
| 478 | 467 | ||
| @@ -496,6 +485,20 @@ function tableInit(ctx){ | |||
| 496 | action.find('[data-date-to-for]').datepicker(options); | 485 | action.find('[data-date-to-for]').datepicker(options); |
| 497 | inputTo.val(selectedTo); | 486 | inputTo.val(selectedTo); |
| 498 | 487 | ||
| 488 | // if the radio button is checked and one or both of the datepickers are | ||
| 489 | // empty, populate them with today's date | ||
| 490 | radio.change(function () { | ||
| 491 | var now = new Date(); | ||
| 492 | |||
| 493 | if (inputFrom.val() === '') { | ||
| 494 | inputFrom.datepicker('setDate', now); | ||
| 495 | } | ||
| 496 | |||
| 497 | if (inputTo.val() === '') { | ||
| 498 | inputTo.datepicker('setDate', now); | ||
| 499 | } | ||
| 500 | }); | ||
| 501 | |||
| 499 | // set filter_value based on date pickers when | 502 | // set filter_value based on date pickers when |
| 500 | // one of their values changes | 503 | // one of their values changes |
| 501 | var changeHandler = function () { | 504 | var changeHandler = function () { |
| @@ -553,7 +556,8 @@ function tableInit(ctx){ | |||
| 553 | { | 556 | { |
| 554 | title: '<label for radio button inside the popup>', | 557 | title: '<label for radio button inside the popup>', |
| 555 | name: '<name of the filter action>', | 558 | name: '<name of the filter action>', |
| 556 | count: <number of items this filter will show> | 559 | count: <number of items this filter will show>, |
| 560 | ... additional data for the action ... | ||
| 557 | } | 561 | } |
| 558 | ] | 562 | ] |
| 559 | } | 563 | } |
| @@ -567,11 +571,12 @@ function tableInit(ctx){ | |||
| 567 | filter | 571 | filter |
| 568 | 572 | ||
| 569 | the filterName is set on the column filter icon, and corresponds | 573 | the filterName is set on the column filter icon, and corresponds |
| 570 | to a value in the table's filters property | 574 | to a value in the table's filter map |
| 571 | 575 | ||
| 572 | when the filter popup's "Apply" button is clicked, the | 576 | when the filter popup's "Apply" button is clicked, the |
| 573 | value for the radio button which is checked is passed in the | 577 | value for the radio button which is checked is passed in the |
| 574 | querystring and applied to the queryset on the table | 578 | querystring, along with a filter_value, and applied to the |
| 579 | queryset on the table | ||
| 575 | */ | 580 | */ |
| 576 | var filterActionRadios = $('#filter-actions-' + ctx.tableName); | 581 | var filterActionRadios = $('#filter-actions-' + ctx.tableName); |
| 577 | 582 | ||
| @@ -587,10 +592,12 @@ function tableInit(ctx){ | |||
| 587 | var filterName = filterData.name + ':' + | 592 | var filterName = filterData.name + ':' + |
| 588 | filterActionData.action_name; | 593 | filterActionData.action_name; |
| 589 | 594 | ||
| 590 | if (filterActionData.type === 'toggle') { | 595 | if (filterActionData.type === 'toggle' || |
| 591 | action = createActionToggle(filterName, filterActionData); | 596 | filterActionData.type === 'day') { |
| 597 | action = createActionRadio(filterName, filterActionData); | ||
| 592 | } | 598 | } |
| 593 | else if (filterActionData.type === 'daterange') { | 599 | else if (filterActionData.type === 'daterange') { |
| 600 | // current values for the from/to dates | ||
| 594 | var filterValue = tableParams.filter_value; | 601 | var filterValue = tableParams.filter_value; |
| 595 | 602 | ||
| 596 | action = createActionDateRange( | 603 | action = createActionDateRange( |
| @@ -601,7 +608,7 @@ function tableInit(ctx){ | |||
| 601 | } | 608 | } |
| 602 | 609 | ||
| 603 | if (action) { | 610 | if (action) { |
| 604 | // Setup the current selected filter, default to 'all' if | 611 | // Setup the current selected filter; default to 'all' if |
| 605 | // no current filter selected | 612 | // no current filter selected |
| 606 | var radioInput = action.children('input[name="filter"]'); | 613 | var radioInput = action.children('input[name="filter"]'); |
| 607 | if ((tableParams.filter && | 614 | if ((tableParams.filter && |
| @@ -707,13 +714,12 @@ function tableInit(ctx){ | |||
| 707 | tableParams.filter + "']"); | 714 | tableParams.filter + "']"); |
| 708 | tableParams.filter_value = checkedFilterValue.val(); | 715 | tableParams.filter_value = checkedFilterValue.val(); |
| 709 | 716 | ||
| 710 | var filterBtn = $("#" + tableParams.filter.split(":")[0]); | ||
| 711 | |||
| 712 | /* All === remove filter */ | 717 | /* All === remove filter */ |
| 713 | if (tableParams.filter.match(":all$")) { | 718 | if (tableParams.filter.match(":all$")) { |
| 714 | tableParams.filter = null; | 719 | tableParams.filter = null; |
| 715 | filterBtnActive(filterBtn, false); | 720 | tableParams.filter_value = null; |
| 716 | } else { | 721 | } else { |
| 722 | var filterBtn = $("#" + tableParams.filter.split(":")[0]); | ||
| 717 | filterBtnActive(filterBtn, true); | 723 | filterBtnActive(filterBtn, true); |
| 718 | } | 724 | } |
| 719 | 725 | ||
diff --git a/bitbake/lib/toaster/toastergui/tablefilter.py b/bitbake/lib/toaster/toastergui/tablefilter.py index 1ea30da304..bd8decd0e3 100644 --- a/bitbake/lib/toaster/toastergui/tablefilter.py +++ b/bitbake/lib/toaster/toastergui/tablefilter.py | |||
| @@ -18,13 +18,18 @@ | |||
| 18 | # You should have received a copy of the GNU General Public License along | 18 | # You should have received a copy of the GNU General Public License along |
| 19 | # with this program; if not, write to the Free Software Foundation, Inc., | 19 | # with this program; if not, write to the Free Software Foundation, Inc., |
| 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 21 | |||
| 21 | from django.db.models import Q, Max, Min | 22 | from django.db.models import Q, Max, Min |
| 22 | from django.utils import dateparse, timezone | 23 | from django.utils import dateparse, timezone |
| 24 | from datetime import timedelta | ||
| 25 | from querysetfilter import QuerysetFilter | ||
| 23 | 26 | ||
| 24 | class TableFilter(object): | 27 | class TableFilter(object): |
| 25 | """ | 28 | """ |
| 26 | Stores a filter for a named field, and can retrieve the action | 29 | Stores a filter for a named field, and can retrieve the action |
| 27 | requested from the set of actions for that filter | 30 | requested from the set of actions for that filter; |
| 31 | the order in which actions are added governs the order in which they | ||
| 32 | are returned in the JSON for the filter | ||
| 28 | """ | 33 | """ |
| 29 | 34 | ||
| 30 | def __init__(self, name, title): | 35 | def __init__(self, name, title): |
| @@ -32,7 +37,11 @@ class TableFilter(object): | |||
| 32 | self.title = title | 37 | self.title = title |
| 33 | self.__filter_action_map = {} | 38 | self.__filter_action_map = {} |
| 34 | 39 | ||
| 40 | # retains the ordering of actions | ||
| 41 | self.__filter_action_keys = [] | ||
| 42 | |||
| 35 | def add_action(self, action): | 43 | def add_action(self, action): |
| 44 | self.__filter_action_keys.append(action.name) | ||
| 36 | self.__filter_action_map[action.name] = action | 45 | self.__filter_action_map[action.name] = action |
| 37 | 46 | ||
| 38 | def get_action(self, action_name): | 47 | def get_action(self, action_name): |
| @@ -56,7 +65,8 @@ class TableFilter(object): | |||
| 56 | }) | 65 | }) |
| 57 | 66 | ||
| 58 | # add other filter actions | 67 | # add other filter actions |
| 59 | for action_name, filter_action in self.__filter_action_map.iteritems(): | 68 | for action_name in self.__filter_action_keys: |
| 69 | filter_action = self.__filter_action_map[action_name] | ||
| 60 | obj = filter_action.to_json(queryset) | 70 | obj = filter_action.to_json(queryset) |
| 61 | obj['action_name'] = action_name | 71 | obj['action_name'] = action_name |
| 62 | filter_actions.append(obj) | 72 | filter_actions.append(obj) |
| @@ -67,6 +77,40 @@ class TableFilter(object): | |||
| 67 | 'filter_actions': filter_actions | 77 | 'filter_actions': filter_actions |
| 68 | } | 78 | } |
| 69 | 79 | ||
| 80 | class TableFilterQueryHelper(object): | ||
| 81 | def dateStringsToQ(self, field_name, date_from_str, date_to_str): | ||
| 82 | """ | ||
| 83 | Convert the date strings from_date_str and to_date_str into a | ||
| 84 | set of args in the form | ||
| 85 | |||
| 86 | {'<field_name>__gte': <date from>, '<field_name>__lte': <date to>} | ||
| 87 | |||
| 88 | where date_from and date_to are Django-timezone-aware dates; then | ||
| 89 | convert that into a Django Q object | ||
| 90 | |||
| 91 | Returns the Q object based on those criteria | ||
| 92 | """ | ||
| 93 | |||
| 94 | # one of the values required for the filter is missing, so set | ||
| 95 | # it to the one which was supplied | ||
| 96 | if date_from_str == '': | ||
| 97 | date_from_str = date_to_str | ||
| 98 | elif date_to_str == '': | ||
| 99 | date_to_str = date_from_str | ||
| 100 | |||
| 101 | date_from_naive = dateparse.parse_datetime(date_from_str + ' 00:00:00') | ||
| 102 | date_to_naive = dateparse.parse_datetime(date_to_str + ' 23:59:59') | ||
| 103 | |||
| 104 | tz = timezone.get_default_timezone() | ||
| 105 | date_from = timezone.make_aware(date_from_naive, tz) | ||
| 106 | date_to = timezone.make_aware(date_to_naive, tz) | ||
| 107 | |||
| 108 | args = {} | ||
| 109 | args[field_name + '__gte'] = date_from | ||
| 110 | args[field_name + '__lte'] = date_to | ||
| 111 | |||
| 112 | return Q(**args) | ||
| 113 | |||
| 70 | class TableFilterAction(object): | 114 | class TableFilterAction(object): |
| 71 | """ | 115 | """ |
| 72 | A filter action which displays in the filter popup for a ToasterTable | 116 | A filter action which displays in the filter popup for a ToasterTable |
| @@ -99,7 +143,7 @@ class TableFilterAction(object): | |||
| 99 | return { | 143 | return { |
| 100 | 'title': self.title, | 144 | 'title': self.title, |
| 101 | 'type': self.type, | 145 | 'type': self.type, |
| 102 | 'count': self.queryset_filter.count(queryset) | 146 | 'count': self.filter(queryset).count() |
| 103 | } | 147 | } |
| 104 | 148 | ||
| 105 | class TableFilterActionToggle(TableFilterAction): | 149 | class TableFilterActionToggle(TableFilterAction): |
| @@ -113,15 +157,70 @@ class TableFilterActionToggle(TableFilterAction): | |||
| 113 | super(TableFilterActionToggle, self).__init__(*args) | 157 | super(TableFilterActionToggle, self).__init__(*args) |
| 114 | self.type = 'toggle' | 158 | self.type = 'toggle' |
| 115 | 159 | ||
| 160 | class TableFilterActionDay(TableFilterAction): | ||
| 161 | """ | ||
| 162 | A filter action which filters according to the named datetime field and a | ||
| 163 | string representing a day ("today" or "yesterday") | ||
| 164 | """ | ||
| 165 | |||
| 166 | TODAY = 'today' | ||
| 167 | YESTERDAY = 'yesterday' | ||
| 168 | |||
| 169 | def __init__(self, name, title, field, day, | ||
| 170 | queryset_filter = QuerysetFilter(), query_helper = TableFilterQueryHelper()): | ||
| 171 | """ | ||
| 172 | field: (string) the datetime field to filter by | ||
| 173 | day: (string) "today" or "yesterday" | ||
| 174 | """ | ||
| 175 | super(TableFilterActionDay, self).__init__( | ||
| 176 | name, | ||
| 177 | title, | ||
| 178 | queryset_filter | ||
| 179 | ) | ||
| 180 | self.type = 'day' | ||
| 181 | self.field = field | ||
| 182 | self.day = day | ||
| 183 | self.query_helper = query_helper | ||
| 184 | |||
| 185 | def filter(self, queryset): | ||
| 186 | """ | ||
| 187 | Apply the day filtering before returning the queryset; | ||
| 188 | this is done here as the value of the filter criteria changes | ||
| 189 | depending on when the filtering is applied | ||
| 190 | """ | ||
| 191 | |||
| 192 | criteria = None | ||
| 193 | date_str = None | ||
| 194 | now = timezone.now() | ||
| 195 | |||
| 196 | if self.day == self.YESTERDAY: | ||
| 197 | increment = timedelta(days=1) | ||
| 198 | wanted_date = now - increment | ||
| 199 | else: | ||
| 200 | wanted_date = now | ||
| 201 | |||
| 202 | wanted_date_str = wanted_date.strftime('%Y-%m-%d') | ||
| 203 | |||
| 204 | criteria = self.query_helper.dateStringsToQ( | ||
| 205 | self.field, | ||
| 206 | wanted_date_str, | ||
| 207 | wanted_date_str | ||
| 208 | ) | ||
| 209 | |||
| 210 | self.queryset_filter.set_criteria(criteria) | ||
| 211 | |||
| 212 | return self.queryset_filter.filter(queryset) | ||
| 213 | |||
| 116 | class TableFilterActionDateRange(TableFilterAction): | 214 | class TableFilterActionDateRange(TableFilterAction): |
| 117 | """ | 215 | """ |
| 118 | A filter action which will filter the queryset by a date range. | 216 | A filter action which will filter the queryset by a date range. |
| 119 | The date range can be set via set_params() | 217 | The date range can be set via set_params() |
| 120 | """ | 218 | """ |
| 121 | 219 | ||
| 122 | def __init__(self, name, title, field, queryset_filter): | 220 | def __init__(self, name, title, field, |
| 221 | queryset_filter = QuerysetFilter(), query_helper = TableFilterQueryHelper()): | ||
| 123 | """ | 222 | """ |
| 124 | field: the field to find the max/min range from in the queryset | 223 | field: (string) the field to find the max/min range from in the queryset |
| 125 | """ | 224 | """ |
| 126 | super(TableFilterActionDateRange, self).__init__( | 225 | super(TableFilterActionDateRange, self).__init__( |
| 127 | name, | 226 | name, |
| @@ -131,9 +230,13 @@ class TableFilterActionDateRange(TableFilterAction): | |||
| 131 | 230 | ||
| 132 | self.type = 'daterange' | 231 | self.type = 'daterange' |
| 133 | self.field = field | 232 | self.field = field |
| 233 | self.query_helper = query_helper | ||
| 134 | 234 | ||
| 135 | def set_filter_params(self, params): | 235 | def set_filter_params(self, params): |
| 136 | """ | 236 | """ |
| 237 | This filter depends on the user selecting some input, so it needs | ||
| 238 | to have its parameters set before its queryset is filtered | ||
| 239 | |||
| 137 | params: (str) a string of extra parameters for the filtering | 240 | params: (str) a string of extra parameters for the filtering |
| 138 | in the format "2015-12-09,2015-12-11" (from,to); this is passed in the | 241 | in the format "2015-12-09,2015-12-11" (from,to); this is passed in the |
| 139 | querystring and used to set the criteria on the QuerysetFilter | 242 | querystring and used to set the criteria on the QuerysetFilter |
| @@ -143,30 +246,18 @@ class TableFilterActionDateRange(TableFilterAction): | |||
| 143 | # if params are invalid, return immediately, resetting criteria | 246 | # if params are invalid, return immediately, resetting criteria |
| 144 | # on the QuerysetFilter | 247 | # on the QuerysetFilter |
| 145 | try: | 248 | try: |
| 146 | from_date_str, to_date_str = params.split(',') | 249 | date_from_str, date_to_str = params.split(',') |
| 147 | except ValueError: | 250 | except ValueError: |
| 148 | self.queryset_filter.set_criteria(None) | 251 | self.queryset_filter.set_criteria(None) |
| 149 | return | 252 | return |
| 150 | 253 | ||
| 151 | # one of the values required for the filter is missing, so set | 254 | # one of the values required for the filter is missing, so set |
| 152 | # it to the one which was supplied | 255 | # it to the one which was supplied |
| 153 | if from_date_str == '': | 256 | criteria = self.query_helper.dateStringsToQ( |
| 154 | from_date_str = to_date_str | 257 | self.field, |
| 155 | elif to_date_str == '': | 258 | date_from_str, |
| 156 | to_date_str = from_date_str | 259 | date_to_str |
| 157 | 260 | ) | |
| 158 | date_from_naive = dateparse.parse_datetime(from_date_str + ' 00:00:00') | ||
| 159 | date_to_naive = dateparse.parse_datetime(to_date_str + ' 23:59:59') | ||
| 160 | |||
| 161 | tz = timezone.get_default_timezone() | ||
| 162 | date_from = timezone.make_aware(date_from_naive, tz) | ||
| 163 | date_to = timezone.make_aware(date_to_naive, tz) | ||
| 164 | |||
| 165 | args = {} | ||
| 166 | args[self.field + '__gte'] = date_from | ||
| 167 | args[self.field + '__lte'] = date_to | ||
| 168 | |||
| 169 | criteria = Q(**args) | ||
| 170 | self.queryset_filter.set_criteria(criteria) | 261 | self.queryset_filter.set_criteria(criteria) |
| 171 | 262 | ||
| 172 | def to_json(self, queryset): | 263 | def to_json(self, queryset): |
| @@ -179,7 +270,8 @@ class TableFilterActionDateRange(TableFilterAction): | |||
| 179 | data['max'] = queryset.aggregate(Max(self.field))[self.field + '__max'] | 270 | data['max'] = queryset.aggregate(Max(self.field))[self.field + '__max'] |
| 180 | 271 | ||
| 181 | # a range filter has a count of None, as the number of records it | 272 | # a range filter has a count of None, as the number of records it |
| 182 | # will select depends on the date range entered | 273 | # will select depends on the date range entered and we don't know |
| 274 | # that ahead of time | ||
| 183 | data['count'] = None | 275 | data['count'] = None |
| 184 | 276 | ||
| 185 | return data | 277 | return data |
diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py index 06ced52eb1..58abe36b05 100644 --- a/bitbake/lib/toaster/toastergui/tables.py +++ b/bitbake/lib/toaster/toastergui/tables.py | |||
| @@ -32,6 +32,7 @@ import itertools | |||
| 32 | from toastergui.tablefilter import TableFilter | 32 | from toastergui.tablefilter import TableFilter |
| 33 | from toastergui.tablefilter import TableFilterActionToggle | 33 | from toastergui.tablefilter import TableFilterActionToggle |
| 34 | from toastergui.tablefilter import TableFilterActionDateRange | 34 | from toastergui.tablefilter import TableFilterActionDateRange |
| 35 | from toastergui.tablefilter import TableFilterActionDay | ||
| 35 | 36 | ||
| 36 | class ProjectFilters(object): | 37 | class ProjectFilters(object): |
| 37 | def __init__(self, project_layers): | 38 | def __init__(self, project_layers): |
| @@ -65,20 +66,20 @@ class LayersTable(ToasterTable): | |||
| 65 | 66 | ||
| 66 | criteria = Q(projectlayer__in=self.project_layers) | 67 | criteria = Q(projectlayer__in=self.project_layers) |
| 67 | 68 | ||
| 68 | in_project_filter_action = TableFilterActionToggle( | 69 | in_project_action = TableFilterActionToggle( |
| 69 | "in_project", | 70 | "in_project", |
| 70 | "Layers added to this project", | 71 | "Layers added to this project", |
| 71 | QuerysetFilter(criteria) | 72 | QuerysetFilter(criteria) |
| 72 | ) | 73 | ) |
| 73 | 74 | ||
| 74 | not_in_project_filter_action = TableFilterActionToggle( | 75 | not_in_project_action = TableFilterActionToggle( |
| 75 | "not_in_project", | 76 | "not_in_project", |
| 76 | "Layers not added to this project", | 77 | "Layers not added to this project", |
| 77 | QuerysetFilter(~criteria) | 78 | QuerysetFilter(~criteria) |
| 78 | ) | 79 | ) |
| 79 | 80 | ||
| 80 | in_current_project_filter.add_action(in_project_filter_action) | 81 | in_current_project_filter.add_action(in_project_action) |
| 81 | in_current_project_filter.add_action(not_in_project_filter_action) | 82 | in_current_project_filter.add_action(not_in_project_action) |
| 82 | self.add_filter(in_current_project_filter) | 83 | self.add_filter(in_current_project_filter) |
| 83 | 84 | ||
| 84 | def setup_queryset(self, *args, **kwargs): | 85 | def setup_queryset(self, *args, **kwargs): |
| @@ -221,20 +222,20 @@ class MachinesTable(ToasterTable): | |||
| 221 | "Filter by project machines" | 222 | "Filter by project machines" |
| 222 | ) | 223 | ) |
| 223 | 224 | ||
| 224 | in_project_filter_action = TableFilterActionToggle( | 225 | in_project_action = TableFilterActionToggle( |
| 225 | "in_project", | 226 | "in_project", |
| 226 | "Machines provided by layers added to this project", | 227 | "Machines provided by layers added to this project", |
| 227 | project_filters.in_project | 228 | project_filters.in_project |
| 228 | ) | 229 | ) |
| 229 | 230 | ||
| 230 | not_in_project_filter_action = TableFilterActionToggle( | 231 | not_in_project_action = TableFilterActionToggle( |
| 231 | "not_in_project", | 232 | "not_in_project", |
| 232 | "Machines provided by layers not added to this project", | 233 | "Machines provided by layers not added to this project", |
| 233 | project_filters.not_in_project | 234 | project_filters.not_in_project |
| 234 | ) | 235 | ) |
| 235 | 236 | ||
| 236 | in_current_project_filter.add_action(in_project_filter_action) | 237 | in_current_project_filter.add_action(in_project_action) |
| 237 | in_current_project_filter.add_action(not_in_project_filter_action) | 238 | in_current_project_filter.add_action(not_in_project_action) |
| 238 | self.add_filter(in_current_project_filter) | 239 | self.add_filter(in_current_project_filter) |
| 239 | 240 | ||
| 240 | def setup_queryset(self, *args, **kwargs): | 241 | def setup_queryset(self, *args, **kwargs): |
| @@ -354,20 +355,20 @@ class RecipesTable(ToasterTable): | |||
| 354 | 'Filter by project recipes' | 355 | 'Filter by project recipes' |
| 355 | ) | 356 | ) |
| 356 | 357 | ||
| 357 | in_project_filter_action = TableFilterActionToggle( | 358 | in_project_action = TableFilterActionToggle( |
| 358 | 'in_project', | 359 | 'in_project', |
| 359 | 'Recipes provided by layers added to this project', | 360 | 'Recipes provided by layers added to this project', |
| 360 | project_filters.in_project | 361 | project_filters.in_project |
| 361 | ) | 362 | ) |
| 362 | 363 | ||
| 363 | not_in_project_filter_action = TableFilterActionToggle( | 364 | not_in_project_action = TableFilterActionToggle( |
| 364 | 'not_in_project', | 365 | 'not_in_project', |
| 365 | 'Recipes provided by layers not added to this project', | 366 | 'Recipes provided by layers not added to this project', |
| 366 | project_filters.not_in_project | 367 | project_filters.not_in_project |
| 367 | ) | 368 | ) |
| 368 | 369 | ||
| 369 | table_filter.add_action(in_project_filter_action) | 370 | table_filter.add_action(in_project_action) |
| 370 | table_filter.add_action(not_in_project_filter_action) | 371 | table_filter.add_action(not_in_project_action) |
| 371 | self.add_filter(table_filter) | 372 | self.add_filter(table_filter) |
| 372 | 373 | ||
| 373 | def setup_queryset(self, *args, **kwargs): | 374 | def setup_queryset(self, *args, **kwargs): |
| @@ -1137,20 +1138,20 @@ class BuildsTable(ToasterTable): | |||
| 1137 | 'Filter builds by outcome' | 1138 | 'Filter builds by outcome' |
| 1138 | ) | 1139 | ) |
| 1139 | 1140 | ||
| 1140 | successful_builds_filter_action = TableFilterActionToggle( | 1141 | successful_builds_action = TableFilterActionToggle( |
| 1141 | 'successful_builds', | 1142 | 'successful_builds', |
| 1142 | 'Successful builds', | 1143 | 'Successful builds', |
| 1143 | QuerysetFilter(Q(outcome=Build.SUCCEEDED)) | 1144 | QuerysetFilter(Q(outcome=Build.SUCCEEDED)) |
| 1144 | ) | 1145 | ) |
| 1145 | 1146 | ||
| 1146 | failed_builds_filter_action = TableFilterActionToggle( | 1147 | failed_builds_action = TableFilterActionToggle( |
| 1147 | 'failed_builds', | 1148 | 'failed_builds', |
| 1148 | 'Failed builds', | 1149 | 'Failed builds', |
| 1149 | QuerysetFilter(Q(outcome=Build.FAILED)) | 1150 | QuerysetFilter(Q(outcome=Build.FAILED)) |
| 1150 | ) | 1151 | ) |
| 1151 | 1152 | ||
| 1152 | outcome_filter.add_action(successful_builds_filter_action) | 1153 | outcome_filter.add_action(successful_builds_action) |
| 1153 | outcome_filter.add_action(failed_builds_filter_action) | 1154 | outcome_filter.add_action(failed_builds_action) |
| 1154 | self.add_filter(outcome_filter) | 1155 | self.add_filter(outcome_filter) |
| 1155 | 1156 | ||
| 1156 | # started on | 1157 | # started on |
| @@ -1159,14 +1160,29 @@ class BuildsTable(ToasterTable): | |||
| 1159 | 'Filter by date when build was started' | 1160 | 'Filter by date when build was started' |
| 1160 | ) | 1161 | ) |
| 1161 | 1162 | ||
| 1162 | by_started_date_range_filter_action = TableFilterActionDateRange( | 1163 | started_today_action = TableFilterActionDay( |
| 1164 | 'today', | ||
| 1165 | 'Today\'s builds', | ||
| 1166 | 'started_on', | ||
| 1167 | 'today' | ||
| 1168 | ) | ||
| 1169 | |||
| 1170 | started_yesterday_action = TableFilterActionDay( | ||
| 1171 | 'yesterday', | ||
| 1172 | 'Yesterday\'s builds', | ||
| 1173 | 'started_on', | ||
| 1174 | 'yesterday' | ||
| 1175 | ) | ||
| 1176 | |||
| 1177 | by_started_date_range_action = TableFilterActionDateRange( | ||
| 1163 | 'date_range', | 1178 | 'date_range', |
| 1164 | 'Build date range', | 1179 | 'Build date range', |
| 1165 | 'started_on', | 1180 | 'started_on' |
| 1166 | QuerysetFilter() | ||
| 1167 | ) | 1181 | ) |
| 1168 | 1182 | ||
| 1169 | started_on_filter.add_action(by_started_date_range_filter_action) | 1183 | started_on_filter.add_action(started_today_action) |
| 1184 | started_on_filter.add_action(started_yesterday_action) | ||
| 1185 | started_on_filter.add_action(by_started_date_range_action) | ||
| 1170 | self.add_filter(started_on_filter) | 1186 | self.add_filter(started_on_filter) |
| 1171 | 1187 | ||
| 1172 | # completed on | 1188 | # completed on |
| @@ -1175,14 +1191,29 @@ class BuildsTable(ToasterTable): | |||
| 1175 | 'Filter by date when build was completed' | 1191 | 'Filter by date when build was completed' |
| 1176 | ) | 1192 | ) |
| 1177 | 1193 | ||
| 1178 | by_completed_date_range_filter_action = TableFilterActionDateRange( | 1194 | completed_today_action = TableFilterActionDay( |
| 1195 | 'today', | ||
| 1196 | 'Today\'s builds', | ||
| 1197 | 'completed_on', | ||
| 1198 | 'today' | ||
| 1199 | ) | ||
| 1200 | |||
| 1201 | completed_yesterday_action = TableFilterActionDay( | ||
| 1202 | 'yesterday', | ||
| 1203 | 'Yesterday\'s builds', | ||
| 1204 | 'completed_on', | ||
| 1205 | 'yesterday' | ||
| 1206 | ) | ||
| 1207 | |||
| 1208 | by_completed_date_range_action = TableFilterActionDateRange( | ||
| 1179 | 'date_range', | 1209 | 'date_range', |
| 1180 | 'Build date range', | 1210 | 'Build date range', |
| 1181 | 'completed_on', | 1211 | 'completed_on' |
| 1182 | QuerysetFilter() | ||
| 1183 | ) | 1212 | ) |
| 1184 | 1213 | ||
| 1185 | completed_on_filter.add_action(by_completed_date_range_filter_action) | 1214 | completed_on_filter.add_action(completed_today_action) |
| 1215 | completed_on_filter.add_action(completed_yesterday_action) | ||
| 1216 | completed_on_filter.add_action(by_completed_date_range_action) | ||
| 1186 | self.add_filter(completed_on_filter) | 1217 | self.add_filter(completed_on_filter) |
| 1187 | 1218 | ||
| 1188 | # failed tasks | 1219 | # failed tasks |
| @@ -1193,18 +1224,18 @@ class BuildsTable(ToasterTable): | |||
| 1193 | 1224 | ||
| 1194 | criteria = Q(task_build__outcome=Task.OUTCOME_FAILED) | 1225 | criteria = Q(task_build__outcome=Task.OUTCOME_FAILED) |
| 1195 | 1226 | ||
| 1196 | with_failed_tasks_filter_action = TableFilterActionToggle( | 1227 | with_failed_tasks_action = TableFilterActionToggle( |
| 1197 | 'with_failed_tasks', | 1228 | 'with_failed_tasks', |
| 1198 | 'Builds with failed tasks', | 1229 | 'Builds with failed tasks', |
| 1199 | QuerysetFilter(criteria) | 1230 | QuerysetFilter(criteria) |
| 1200 | ) | 1231 | ) |
| 1201 | 1232 | ||
| 1202 | without_failed_tasks_filter_action = TableFilterActionToggle( | 1233 | without_failed_tasks_action = TableFilterActionToggle( |
| 1203 | 'without_failed_tasks', | 1234 | 'without_failed_tasks', |
| 1204 | 'Builds without failed tasks', | 1235 | 'Builds without failed tasks', |
| 1205 | QuerysetFilter(~criteria) | 1236 | QuerysetFilter(~criteria) |
| 1206 | ) | 1237 | ) |
| 1207 | 1238 | ||
| 1208 | failed_tasks_filter.add_action(with_failed_tasks_filter_action) | 1239 | failed_tasks_filter.add_action(with_failed_tasks_action) |
| 1209 | failed_tasks_filter.add_action(without_failed_tasks_filter_action) | 1240 | failed_tasks_filter.add_action(without_failed_tasks_action) |
| 1210 | self.add_filter(failed_tasks_filter) | 1241 | self.add_filter(failed_tasks_filter) |
diff --git a/bitbake/lib/toaster/toastergui/templates/builds-toastertable.html b/bitbake/lib/toaster/toastergui/templates/builds-toastertable.html index 2e32edb100..bf13a66bd1 100644 --- a/bitbake/lib/toaster/toastergui/templates/builds-toastertable.html +++ b/bitbake/lib/toaster/toastergui/templates/builds-toastertable.html | |||
| @@ -18,7 +18,7 @@ | |||
| 18 | {% include 'mrb_section.html' %} | 18 | {% include 'mrb_section.html' %} |
| 19 | {% endwith %} | 19 | {% endwith %} |
| 20 | 20 | ||
| 21 | <h1 class="page-header top-air" data-role="page-title"></h1> | 21 | <h1 class="page-header top-air" data-role="page-title"></h1> |
| 22 | 22 | ||
| 23 | {% url 'builds' as xhr_table_url %} | 23 | {% url 'builds' as xhr_table_url %} |
| 24 | {% include 'toastertable.html' %} | 24 | {% include 'toastertable.html' %} |
diff --git a/bitbake/lib/toaster/toastergui/templates/toastertable.html b/bitbake/lib/toaster/toastergui/templates/toastertable.html index 98a715f27d..f0a3aedb74 100644 --- a/bitbake/lib/toaster/toastergui/templates/toastertable.html +++ b/bitbake/lib/toaster/toastergui/templates/toastertable.html | |||
| @@ -32,8 +32,11 @@ | |||
| 32 | <a href="#" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1"> | 32 | <a href="#" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1"> |
| 33 | <i class="icon-remove"></i> | 33 | <i class="icon-remove"></i> |
| 34 | </a> | 34 | </a> |
| 35 | <button class="btn search-submit-{{table_name}}" >Search</button> | 35 | <button class="btn search-submit-{{table_name}}"> |
| 36 | <button class="btn btn-link remove-search-btn-{{table_name}}">Show {{title|lower}} | 36 | Search |
| 37 | </button> | ||
| 38 | <button class="btn btn-link show-all-{{table_name}}"> | ||
| 39 | Show {{title|lower}} | ||
| 37 | </button> | 40 | </button> |
| 38 | </form> | 41 | </form> |
| 39 | </div> | 42 | </div> |
