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> |