diff options
| author | Michael Wood <michael.g.wood@intel.com> | 2016-05-16 14:50:40 +0100 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-05-17 14:43:30 +0100 |
| commit | 54bf7cce48547289489203812f2e01cb755f0db4 (patch) | |
| tree | b744123fed2c7e15ab4d1a45c1dce988d3dd4a58 /bitbake/lib/toaster/toastergui/widgets.py | |
| parent | a906a09c730683ee14b84fd890d109f52c9e3b02 (diff) | |
| download | poky-54bf7cce48547289489203812f2e01cb755f0db4.tar.gz | |
bitbake: toaster: toastertables: Clean up and fix up a number of items
- Remove the unused 'computation' field
- Remove the code to try to make the tables behave like an api
- Remove custom JSON encoder in favour of DjangoJSONEncoder
- Simplify get_data and add comments
- Add exception type instead of using generic Exception
- Clean up python style warnings
(Bitbake rev: 16d8198e8f6668c5fa5467ff4bda86c5d66a6cad)
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
Signed-off-by: Elliot Smith <elliot.smith@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui/widgets.py')
| -rw-r--r-- | bitbake/lib/toaster/toastergui/widgets.py | 127 |
1 files changed, 71 insertions, 56 deletions
diff --git a/bitbake/lib/toaster/toastergui/widgets.py b/bitbake/lib/toaster/toastergui/widgets.py index d2ef5d3dba..4117031830 100644 --- a/bitbake/lib/toaster/toastergui/widgets.py +++ b/bitbake/lib/toaster/toastergui/widgets.py | |||
| @@ -43,9 +43,12 @@ import urllib | |||
| 43 | import logging | 43 | import logging |
| 44 | logger = logging.getLogger("toaster") | 44 | logger = logging.getLogger("toaster") |
| 45 | 45 | ||
| 46 | from toastergui.views import objtojson | ||
| 47 | from toastergui.tablefilter import TableFilterMap | 46 | from toastergui.tablefilter import TableFilterMap |
| 48 | 47 | ||
| 48 | |||
| 49 | class NoFieldOrDataNme(Exception): | ||
| 50 | pass | ||
| 51 | |||
| 49 | class ToasterTable(TemplateView): | 52 | class ToasterTable(TemplateView): |
| 50 | def __init__(self, *args, **kwargs): | 53 | def __init__(self, *args, **kwargs): |
| 51 | super(ToasterTable, self).__init__() | 54 | super(ToasterTable, self).__init__() |
| @@ -63,25 +66,19 @@ class ToasterTable(TemplateView): | |||
| 63 | self.empty_state = "Sorry - no data found" | 66 | self.empty_state = "Sorry - no data found" |
| 64 | self.default_orderby = "" | 67 | self.default_orderby = "" |
| 65 | 68 | ||
| 66 | # add the "id" column, undisplayable, by default | ||
| 67 | self.add_column(title="Id", | ||
| 68 | displayable=False, | ||
| 69 | orderable=True, | ||
| 70 | field_name="id") | ||
| 71 | |||
| 72 | # prevent HTTP caching of table data | 69 | # prevent HTTP caching of table data |
| 73 | @cache_control(must_revalidate=True, max_age=0, no_store=True, no_cache=True) | 70 | @cache_control(must_revalidate=True, |
| 71 | max_age=0, no_store=True, no_cache=True) | ||
| 74 | def dispatch(self, *args, **kwargs): | 72 | def dispatch(self, *args, **kwargs): |
| 75 | return super(ToasterTable, self).dispatch(*args, **kwargs) | 73 | return super(ToasterTable, self).dispatch(*args, **kwargs) |
| 76 | 74 | ||
| 77 | def get_context_data(self, **kwargs): | 75 | def get_context_data(self, **kwargs): |
| 78 | context = super(ToasterTable, self).get_context_data(**kwargs) | 76 | context = super(ToasterTable, self).get_context_data(**kwargs) |
| 79 | context['title'] = self.title | 77 | context['title'] = self.title |
| 80 | context['table_name'] = type(self).__name__.lower() | 78 | context['table_name'] = type(self).__name__.lower() |
| 81 | 79 | ||
| 82 | return context | 80 | return context |
| 83 | 81 | ||
| 84 | |||
| 85 | def get(self, request, *args, **kwargs): | 82 | def get(self, request, *args, **kwargs): |
| 86 | if request.GET.get('format', None) == 'json': | 83 | if request.GET.get('format', None) == 'json': |
| 87 | 84 | ||
| @@ -102,8 +99,6 @@ class ToasterTable(TemplateView): | |||
| 102 | return super(ToasterTable, self).get(request, *args, **kwargs) | 99 | return super(ToasterTable, self).get(request, *args, **kwargs) |
| 103 | 100 | ||
| 104 | def get_filter_info(self, request, **kwargs): | 101 | def get_filter_info(self, request, **kwargs): |
| 105 | data = None | ||
| 106 | |||
| 107 | self.setup_filters(**kwargs) | 102 | self.setup_filters(**kwargs) |
| 108 | 103 | ||
| 109 | search = request.GET.get("search", None) | 104 | search = request.GET.get("search", None) |
| @@ -117,13 +112,18 @@ class ToasterTable(TemplateView): | |||
| 117 | cls=DjangoJSONEncoder) | 112 | cls=DjangoJSONEncoder) |
| 118 | 113 | ||
| 119 | def setup_columns(self, *args, **kwargs): | 114 | def setup_columns(self, *args, **kwargs): |
| 120 | """ function to implement in the subclass which sets up the columns """ | 115 | """ function to implement in the subclass which sets up |
| 116 | the columns """ | ||
| 121 | pass | 117 | pass |
| 118 | |||
| 122 | def setup_filters(self, *args, **kwargs): | 119 | def setup_filters(self, *args, **kwargs): |
| 123 | """ function to implement in the subclass which sets up the filters """ | 120 | """ function to implement in the subclass which sets up the |
| 121 | filters """ | ||
| 124 | pass | 122 | pass |
| 123 | |||
| 125 | def setup_queryset(self, *args, **kwargs): | 124 | def setup_queryset(self, *args, **kwargs): |
| 126 | """ function to implement in the subclass which sets up the queryset""" | 125 | """ function to implement in the subclass which sets up the |
| 126 | queryset""" | ||
| 127 | pass | 127 | pass |
| 128 | 128 | ||
| 129 | def add_filter(self, table_filter): | 129 | def add_filter(self, table_filter): |
| @@ -137,7 +137,6 @@ class ToasterTable(TemplateView): | |||
| 137 | def add_column(self, title="", help_text="", | 137 | def add_column(self, title="", help_text="", |
| 138 | orderable=False, hideable=True, hidden=False, | 138 | orderable=False, hideable=True, hidden=False, |
| 139 | field_name="", filter_name=None, static_data_name=None, | 139 | field_name="", filter_name=None, static_data_name=None, |
| 140 | displayable=True, computation=None, | ||
| 141 | static_data_template=None): | 140 | static_data_template=None): |
| 142 | """Add a column to the table. | 141 | """Add a column to the table. |
| 143 | 142 | ||
| @@ -155,18 +154,15 @@ class ToasterTable(TemplateView): | |||
| 155 | as data | 154 | as data |
| 156 | """ | 155 | """ |
| 157 | 156 | ||
| 158 | self.columns.append({'title' : title, | 157 | self.columns.append({'title': title, |
| 159 | 'help_text' : help_text, | 158 | 'help_text': help_text, |
| 160 | 'orderable' : orderable, | 159 | 'orderable': orderable, |
| 161 | 'hideable' : hideable, | 160 | 'hideable': hideable, |
| 162 | 'hidden' : hidden, | 161 | 'hidden': hidden, |
| 163 | 'field_name' : field_name, | 162 | 'field_name': field_name, |
| 164 | 'filter_name' : filter_name, | 163 | 'filter_name': filter_name, |
| 165 | 'static_data_name': static_data_name, | 164 | 'static_data_name': static_data_name, |
| 166 | 'static_data_template': static_data_template, | 165 | 'static_data_template': static_data_template}) |
| 167 | 'displayable': displayable, | ||
| 168 | 'computation': computation, | ||
| 169 | }) | ||
| 170 | 166 | ||
| 171 | def set_column_hidden(self, title, hidden): | 167 | def set_column_hidden(self, title, hidden): |
| 172 | """ | 168 | """ |
| @@ -190,8 +186,8 @@ class ToasterTable(TemplateView): | |||
| 190 | """Utility function to render the static data template""" | 186 | """Utility function to render the static data template""" |
| 191 | 187 | ||
| 192 | context = { | 188 | context = { |
| 193 | 'extra' : self.static_context_extra, | 189 | 'extra': self.static_context_extra, |
| 194 | 'data' : row, | 190 | 'data': row, |
| 195 | } | 191 | } |
| 196 | 192 | ||
| 197 | context = Context(context) | 193 | context = Context(context) |
| @@ -241,7 +237,7 @@ class ToasterTable(TemplateView): | |||
| 241 | 237 | ||
| 242 | if not hasattr(self.queryset.model, 'search_allowed_fields'): | 238 | if not hasattr(self.queryset.model, 'search_allowed_fields'): |
| 243 | raise Exception("Search fields aren't defined in the model %s" | 239 | raise Exception("Search fields aren't defined in the model %s" |
| 244 | % self.queryset.model) | 240 | % self.queryset.model) |
| 245 | 241 | ||
| 246 | search_queries = [] | 242 | search_queries = [] |
| 247 | for st in search_term.split(" "): | 243 | for st in search_term.split(" "): |
| @@ -254,7 +250,6 @@ class ToasterTable(TemplateView): | |||
| 254 | 250 | ||
| 255 | self.queryset = self.queryset.filter(search_queries) | 251 | self.queryset = self.queryset.filter(search_queries) |
| 256 | 252 | ||
| 257 | |||
| 258 | def get_data(self, request, **kwargs): | 253 | def get_data(self, request, **kwargs): |
| 259 | """ | 254 | """ |
| 260 | Returns the data for the page requested with the specified | 255 | Returns the data for the page requested with the specified |
| @@ -262,7 +257,8 @@ class ToasterTable(TemplateView): | |||
| 262 | 257 | ||
| 263 | filters: filter and action name, e.g. "outcome:build_succeeded" | 258 | filters: filter and action name, e.g. "outcome:build_succeeded" |
| 264 | filter_value: value to pass to the named filter+action, e.g. "on" | 259 | filter_value: value to pass to the named filter+action, e.g. "on" |
| 265 | (for a toggle filter) or "2015-12-11,2015-12-12" (for a date range filter) | 260 | (for a toggle filter) or "2015-12-11,2015-12-12" |
| 261 | (for a date range filter) | ||
| 266 | """ | 262 | """ |
| 267 | 263 | ||
| 268 | page_num = request.GET.get("page", 1) | 264 | page_num = request.GET.get("page", 1) |
| @@ -313,16 +309,16 @@ class ToasterTable(TemplateView): | |||
| 313 | page = paginator.page(1) | 309 | page = paginator.page(1) |
| 314 | 310 | ||
| 315 | data = { | 311 | data = { |
| 316 | 'total' : self.queryset.count(), | 312 | 'total': self.queryset.count(), |
| 317 | 'default_orderby' : self.default_orderby, | 313 | 'default_orderby': self.default_orderby, |
| 318 | 'columns' : self.columns, | 314 | 'columns': self.columns, |
| 319 | 'rows' : [], | 315 | 'rows': [], |
| 320 | 'error' : "ok", | 316 | 'error': "ok", |
| 321 | } | 317 | } |
| 322 | 318 | ||
| 323 | try: | 319 | try: |
| 324 | for row in page.object_list: | 320 | for model_obj in page.object_list: |
| 325 | #Use collection to maintain the order | 321 | # Use collection to maintain the order |
| 326 | required_data = collections.OrderedDict() | 322 | required_data = collections.OrderedDict() |
| 327 | 323 | ||
| 328 | for col in self.columns: | 324 | for col in self.columns: |
| @@ -330,38 +326,57 @@ class ToasterTable(TemplateView): | |||
| 330 | if not field: | 326 | if not field: |
| 331 | field = col['static_data_name'] | 327 | field = col['static_data_name'] |
| 332 | if not field: | 328 | if not field: |
| 333 | raise Exception("Must supply a field_name or static_data_name for column %s.%s" % (self.__class__.__name__,col)) | 329 | raise NoFieldOrDataNme("Must supply a field_name or" |
| 330 | "static_data_name for column" | ||
| 331 | "%s.%s" % | ||
| 332 | (self.__class__.__name__, col)) | ||
| 333 | |||
| 334 | # Check if we need to process some static data | 334 | # Check if we need to process some static data |
| 335 | if "static_data_name" in col and col['static_data_name']: | 335 | if "static_data_name" in col and col['static_data_name']: |
| 336 | required_data["static:%s" % col['static_data_name']] = self.render_static_data(col['static_data_template'], row) | ||
| 337 | |||
| 338 | # Overwrite the field_name with static_data_name | 336 | # Overwrite the field_name with static_data_name |
| 339 | # so that this can be used as the html class name | 337 | # so that this can be used as the html class name |
| 340 | |||
| 341 | col['field_name'] = col['static_data_name'] | 338 | col['field_name'] = col['static_data_name'] |
| 342 | 339 | ||
| 343 | # compute the computation on the raw data if needed | 340 | # Render the template given |
| 344 | model_data = row | 341 | required_data[col['static_data_name']] = \ |
| 345 | if col['computation']: | 342 | self.render_static_data( |
| 346 | model_data = col['computation'](row) | 343 | col['static_data_template'], model_obj) |
| 347 | else: | 344 | else: |
| 348 | # Traverse to any foriegn key in the object hierachy | 345 | # Traverse to any foriegn key in the field |
| 349 | for subfield in field.split("__"): | 346 | # e.g. recipe__layer_version__name |
| 350 | if hasattr(model_data, subfield): | 347 | model_data = None |
| 351 | model_data = getattr(model_data, subfield) | 348 | |
| 352 | # The field could be a function on the model so check | 349 | if "__" in field: |
| 353 | # If it is then call it | 350 | for subfield in field.split("__"): |
| 351 | if not model_data: | ||
| 352 | # The first iteration is always going to | ||
| 353 | # be on the actual model object instance. | ||
| 354 | # Subsequent ones are on the result of | ||
| 355 | # that. e.g. forieng key objects | ||
| 356 | model_data = getattr(model_obj, | ||
| 357 | subfield) | ||
| 358 | else: | ||
| 359 | model_data = getattr(model_data, | ||
| 360 | subfield) | ||
| 361 | |||
| 362 | else: | ||
| 363 | model_data = getattr(model_obj, | ||
| 364 | col['field_name']) | ||
| 365 | |||
| 366 | # We might have a model function as the field so | ||
| 367 | # call it to return the data needed | ||
| 354 | if isinstance(model_data, types.MethodType): | 368 | if isinstance(model_data, types.MethodType): |
| 355 | model_data = model_data() | 369 | model_data = model_data() |
| 356 | 370 | ||
| 357 | required_data[col['field_name']] = model_data | 371 | required_data[col['field_name']] = model_data |
| 358 | 372 | ||
| 359 | data['rows'].append(required_data) | 373 | data['rows'].append(required_data) |
| 360 | 374 | ||
| 361 | except FieldError: | 375 | except FieldError: |
| 362 | # pass it to the user - programming-error here | 376 | # pass it to the user - programming-error here |
| 363 | raise | 377 | raise |
| 364 | data = json.dumps(data, indent=2, default=objtojson) | 378 | |
| 379 | data = json.dumps(data, indent=2, cls=DjangoJSONEncoder) | ||
| 365 | cache.set(cache_name, data, 60*30) | 380 | cache.set(cache_name, data, 60*30) |
| 366 | 381 | ||
| 367 | return data | 382 | return data |
