diff options
author | Elliot Smith <elliot.smith@intel.com> | 2016-01-15 13:00:54 +0200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-01-15 16:30:00 +0000 |
commit | 33b011c1589519db8176c9f5a4abb540698902e6 (patch) | |
tree | 104367160098dffb4c05c2120db1c2c8ef470449 /bitbake/lib/toaster/toastergui/tablefilter.py | |
parent | f8d383d87f0b9d4a4c9ae7b1a6c8ceebf90ef9b0 (diff) | |
download | poky-33b011c1589519db8176c9f5a4abb540698902e6.tar.gz |
bitbake: toastergui: implement "today" and "yesterday" filters
Add the "today" and "yesterday" filters to the started_on
and completed_on columns in the builds table.
During this work, some minor adjustments were made to the
behaviour of the builds table:
* Amend filter action variable names so they're more succinct.
* Retain order in which actions are added to a filter, as this
ordering is used in the UI when displaying the filter actions.
* Always show the table chrome, otherwise it's not possible
to edit the columns shown until there are 10 or more results.
* Because date range searches may return no results, make sure
that the search bar and "show all results" link are visible
when the query returns no results.
[YOCTO #8738]
(Bitbake rev: f17cfa009e58833e0e55884fa04de8abd522b6bc)
Signed-off-by: Elliot Smith <elliot.smith@intel.com>
Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui/tablefilter.py')
-rw-r--r-- | bitbake/lib/toaster/toastergui/tablefilter.py | 140 |
1 files changed, 116 insertions, 24 deletions
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 |