summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster
diff options
context:
space:
mode:
authorMichael Wood <michael.g.wood@intel.com>2016-09-26 13:59:29 +0300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-09-30 16:52:21 +0100
commit8ba7ccd25247539cbea8786026b375a2c9169aad (patch)
tree753608bcad31b9a4ba027215261cf3a0b4daf2d8 /bitbake/lib/toaster
parent4d0bc8d52130fcc62c0d239fd7e0c79bec188c88 (diff)
downloadpoky-8ba7ccd25247539cbea8786026b375a2c9169aad.tar.gz
bitbake: toaster: move MostRecentBuildsView to its own widget
This view is specific to the builds dashboard rather than gernic api so like ToasterTable and ToasterTypeAhead we class it as a widget as it has a single purpose. Also clean up some flake8 identified issues. Original author of the code moved in this commit is Elliot Smith. (Bitbake rev: 05428514e64ec896faae4055619e149e98bc8f57) Signed-off-by: Michael Wood <michael.g.wood@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')
-rw-r--r--bitbake/lib/toaster/toastergui/api.py108
-rw-r--r--bitbake/lib/toaster/toastergui/urls.py3
-rw-r--r--bitbake/lib/toaster/toastergui/widgets.py141
3 files changed, 127 insertions, 125 deletions
diff --git a/bitbake/lib/toaster/toastergui/api.py b/bitbake/lib/toaster/toastergui/api.py
index 8876409964..5589118027 100644
--- a/bitbake/lib/toaster/toastergui/api.py
+++ b/bitbake/lib/toaster/toastergui/api.py
@@ -33,10 +33,8 @@ from bldcontrol import bbcontroller
33from django.http import HttpResponse, JsonResponse 33from django.http import HttpResponse, JsonResponse
34from django.views.generic import View 34from django.views.generic import View
35from django.core.urlresolvers import reverse 35from django.core.urlresolvers import reverse
36from django.utils import timezone
37from django.db.models import Q, F 36from django.db.models import Q, F
38from django.db import Error 37from django.db import Error
39from toastergui.templatetags.projecttags import json, sectohms, get_tasks
40from toastergui.templatetags.projecttags import filtered_filesizeformat 38from toastergui.templatetags.projecttags import filtered_filesizeformat
41 39
42logger = logging.getLogger("toaster") 40logger = logging.getLogger("toaster")
@@ -227,112 +225,6 @@ class XhrLayer(View):
227 }) 225 })
228 226
229 227
230class MostRecentBuildsView(View):
231 def _was_yesterday_or_earlier(self, completed_on):
232 now = timezone.now()
233 delta = now - completed_on
234
235 if delta.days >= 1:
236 return True
237
238 return False
239
240 def get(self, request, *args, **kwargs):
241 """
242 Returns a list of builds in JSON format.
243 """
244 project = None
245
246 project_id = request.GET.get('project_id', None)
247 if project_id:
248 try:
249 project = Project.objects.get(pk=project_id)
250 except:
251 # if project lookup fails, assume no project
252 pass
253
254 recent_build_objs = Build.get_recent(project)
255 recent_builds = []
256
257 for build_obj in recent_build_objs:
258 dashboard_url = reverse('builddashboard', args=(build_obj.pk,))
259 buildtime_url = reverse('buildtime', args=(build_obj.pk,))
260 rebuild_url = \
261 reverse('xhr_buildrequest', args=(build_obj.project.pk,))
262 cancel_url = \
263 reverse('xhr_buildrequest', args=(build_obj.project.pk,))
264
265 build = {}
266 build['id'] = build_obj.pk
267 build['dashboard_url'] = dashboard_url
268
269 buildrequest_id = None
270 if hasattr(build_obj, 'buildrequest'):
271 buildrequest_id = build_obj.buildrequest.pk
272 build['buildrequest_id'] = buildrequest_id
273
274 build['recipes_parsed_percentage'] = \
275 int((build_obj.recipes_parsed /
276 build_obj.recipes_to_parse) * 100)
277
278 tasks_complete_percentage = 0
279 if build_obj.outcome in (Build.SUCCEEDED, Build.FAILED):
280 tasks_complete_percentage = 100
281 elif build_obj.outcome == Build.IN_PROGRESS:
282 tasks_complete_percentage = build_obj.completeper()
283 build['tasks_complete_percentage'] = tasks_complete_percentage
284
285 build['state'] = build_obj.get_state()
286
287 build['errors'] = build_obj.errors.count()
288 build['dashboard_errors_url'] = dashboard_url + '#errors'
289
290 build['warnings'] = build_obj.warnings.count()
291 build['dashboard_warnings_url'] = dashboard_url + '#warnings'
292
293 build['buildtime'] = sectohms(build_obj.timespent_seconds)
294 build['buildtime_url'] = buildtime_url
295
296 build['rebuild_url'] = rebuild_url
297 build['cancel_url'] = cancel_url
298
299 build['is_default_project_build'] = build_obj.project.is_default
300
301 build['build_targets_json'] = \
302 json(get_tasks(build_obj.target_set.all()))
303
304 # convert completed_on time to user's timezone
305 completed_on = timezone.localtime(build_obj.completed_on)
306
307 completed_on_template = '%H:%M'
308 if self._was_yesterday_or_earlier(completed_on):
309 completed_on_template = '%d/%m/%Y ' + completed_on_template
310 build['completed_on'] = completed_on.strftime(
311 completed_on_template)
312
313 targets = []
314 target_objs = build_obj.get_sorted_target_list()
315 for target_obj in target_objs:
316 if target_obj.task:
317 targets.append(target_obj.target + ':' + target_obj.task)
318 else:
319 targets.append(target_obj.target)
320 build['targets'] = ' '.join(targets)
321
322 # abbreviated form of the full target list
323 abbreviated_targets = ''
324 num_targets = len(targets)
325 if num_targets > 0:
326 abbreviated_targets = targets[0]
327 if num_targets > 1:
328 abbreviated_targets += (' +%s' % (num_targets - 1))
329 build['targets_abbreviated'] = abbreviated_targets
330
331 recent_builds.append(build)
332
333 return JsonResponse(recent_builds, safe=False)
334
335
336class XhrCustomRecipe(View): 228class XhrCustomRecipe(View):
337 """ Create a custom image recipe """ 229 """ Create a custom image recipe """
338 230
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 1232611e2e..0002a5a2ee 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -24,6 +24,7 @@ from toastergui import tables
24from toastergui import buildtables 24from toastergui import buildtables
25from toastergui import typeaheads 25from toastergui import typeaheads
26from toastergui import api 26from toastergui import api
27from toastergui import widgets
27 28
28urlpatterns = patterns('toastergui.views', 29urlpatterns = patterns('toastergui.views',
29 # landing page 30 # landing page
@@ -224,7 +225,7 @@ urlpatterns = patterns('toastergui.views',
224 api.XhrProject.as_view(), 225 api.XhrProject.as_view(),
225 name='xhr_project'), 226 name='xhr_project'),
226 227
227 url(r'^mostrecentbuilds$', api.MostRecentBuildsView.as_view(), 228 url(r'^mostrecentbuilds$', widgets.MostRecentBuildsView.as_view(),
228 name='most_recent_builds'), 229 name='most_recent_builds'),
229 230
230 # default redirection 231 # default redirection
diff --git a/bitbake/lib/toaster/toastergui/widgets.py b/bitbake/lib/toaster/toastergui/widgets.py
index 005a5620db..026903d35d 100644
--- a/bitbake/lib/toaster/toastergui/widgets.py
+++ b/bitbake/lib/toaster/toastergui/widgets.py
@@ -22,23 +22,24 @@
22from django.views.generic import View, TemplateView 22from django.views.generic import View, TemplateView
23from django.views.decorators.cache import cache_control 23from django.views.decorators.cache import cache_control
24from django.shortcuts import HttpResponse 24from django.shortcuts import HttpResponse
25from django.http import HttpResponseBadRequest
26from django.core import serializers
27from django.core.cache import cache 25from django.core.cache import cache
28from django.core.paginator import Paginator, EmptyPage 26from django.core.paginator import Paginator, EmptyPage
29from django.db.models import Q 27from django.db.models import Q
30from orm.models import Project, ProjectLayer, Layer_Version 28from orm.models import Project, Build
31from django.template import Context, Template 29from django.template import Context, Template
32from django.template import VariableDoesNotExist 30from django.template import VariableDoesNotExist
33from django.template import TemplateSyntaxError 31from django.template import TemplateSyntaxError
34from django.core.serializers.json import DjangoJSONEncoder 32from django.core.serializers.json import DjangoJSONEncoder
35from django.core.exceptions import FieldError 33from django.core.exceptions import FieldError
36from django.conf.urls import url, patterns 34from django.utils import timezone
35from toastergui.templatetags.projecttags import sectohms, get_tasks
36from toastergui.templatetags.projecttags import json as template_json
37from django.http import JsonResponse
38from django.core.urlresolvers import reverse
37 39
38import types 40import types
39import json 41import json
40import collections 42import collections
41import operator
42import re 43import re
43 44
44try: 45try:
@@ -55,6 +56,7 @@ from toastergui.tablefilter import TableFilterMap
55class NoFieldOrDataName(Exception): 56class NoFieldOrDataName(Exception):
56 pass 57 pass
57 58
59
58class ToasterTable(TemplateView): 60class ToasterTable(TemplateView):
59 def __init__(self, *args, **kwargs): 61 def __init__(self, *args, **kwargs):
60 super(ToasterTable, self).__init__() 62 super(ToasterTable, self).__init__()
@@ -81,7 +83,7 @@ class ToasterTable(TemplateView):
81 def get_context_data(self, **kwargs): 83 def get_context_data(self, **kwargs):
82 context = super(ToasterTable, self).get_context_data(**kwargs) 84 context = super(ToasterTable, self).get_context_data(**kwargs)
83 context['title'] = self.title 85 context['title'] = self.title
84 context['table_name'] = type(self).__name__.lower() 86 context['table_name'] = type(self).__name__.lower()
85 context['empty_state'] = self.empty_state 87 context['empty_state'] = self.empty_state
86 88
87 return context 89 return context
@@ -406,7 +408,6 @@ class ToasterTable(TemplateView):
406 return data 408 return data
407 409
408 410
409
410class ToasterTypeAhead(View): 411class ToasterTypeAhead(View):
411 """ A typeahead mechanism to support the front end typeahead widgets """ 412 """ A typeahead mechanism to support the front end typeahead widgets """
412 MAX_RESULTS = 6 413 MAX_RESULTS = 6
@@ -427,34 +428,142 @@ class ToasterTypeAhead(View):
427 error = "ok" 428 error = "ok"
428 429
429 search_term = request.GET.get("search", None) 430 search_term = request.GET.get("search", None)
430 if search_term == None: 431 if search_term is None:
431 # We got no search value so return empty reponse 432 # We got no search value so return empty reponse
432 return response({'error' : error , 'results': []}) 433 return response({'error': error, 'results': []})
433 434
434 try: 435 try:
435 prj = Project.objects.get(pk=kwargs['pid']) 436 prj = Project.objects.get(pk=kwargs['pid'])
436 except KeyError: 437 except KeyError:
437 prj = None 438 prj = None
438 439
439 results = self.apply_search(search_term, prj, request)[:ToasterTypeAhead.MAX_RESULTS] 440 results = self.apply_search(search_term,
441 prj,
442 request)[:ToasterTypeAhead.MAX_RESULTS]
440 443
441 if len(results) > 0: 444 if len(results) > 0:
442 try: 445 try:
443 self.validate_fields(results[0]) 446 self.validate_fields(results[0])
444 except MissingFieldsException as e: 447 except self.MissingFieldsException as e:
445 error = e 448 error = e
446 449
447 data = { 'results' : results, 450 data = {'results': results,
448 'error' : error, 451 'error': error}
449 }
450 452
451 return response(data) 453 return response(data)
452 454
453 def validate_fields(self, result): 455 def validate_fields(self, result):
454 if 'name' in result == False or 'detail' in result == False: 456 if 'name' in result is False or 'detail' in result is False:
455 raise MissingFieldsException("name and detail are required fields") 457 raise self.MissingFieldsException(
458 "name and detail are required fields")
456 459
457 def apply_search(self, search_term, prj): 460 def apply_search(self, search_term, prj):
458 """ Override this function to implement search. Return an array of 461 """ Override this function to implement search. Return an array of
459 dictionaries with a minium of a name and detail field""" 462 dictionaries with a minium of a name and detail field"""
460 pass 463 pass
464
465
466class MostRecentBuildsView(View):
467 def _was_yesterday_or_earlier(self, completed_on):
468 now = timezone.now()
469 delta = now - completed_on
470
471 if delta.days >= 1:
472 return True
473
474 return False
475
476 def get(self, request, *args, **kwargs):
477 """
478 Returns a list of builds in JSON format.
479 """
480 project = None
481
482 project_id = request.GET.get('project_id', None)
483 if project_id:
484 try:
485 project = Project.objects.get(pk=project_id)
486 except:
487 # if project lookup fails, assume no project
488 pass
489
490 recent_build_objs = Build.get_recent(project)
491 recent_builds = []
492
493 for build_obj in recent_build_objs:
494 dashboard_url = reverse('builddashboard', args=(build_obj.pk,))
495 buildtime_url = reverse('buildtime', args=(build_obj.pk,))
496 rebuild_url = \
497 reverse('xhr_buildrequest', args=(build_obj.project.pk,))
498 cancel_url = \
499 reverse('xhr_buildrequest', args=(build_obj.project.pk,))
500
501 build = {}
502 build['id'] = build_obj.pk
503 build['dashboard_url'] = dashboard_url
504
505 buildrequest_id = None
506 if hasattr(build_obj, 'buildrequest'):
507 buildrequest_id = build_obj.buildrequest.pk
508 build['buildrequest_id'] = buildrequest_id
509
510 build['recipes_parsed_percentage'] = \
511 int((build_obj.recipes_parsed /
512 build_obj.recipes_to_parse) * 100)
513
514 tasks_complete_percentage = 0
515 if build_obj.outcome in (Build.SUCCEEDED, Build.FAILED):
516 tasks_complete_percentage = 100
517 elif build_obj.outcome == Build.IN_PROGRESS:
518 tasks_complete_percentage = build_obj.completeper()
519 build['tasks_complete_percentage'] = tasks_complete_percentage
520
521 build['state'] = build_obj.get_state()
522
523 build['errors'] = build_obj.errors.count()
524 build['dashboard_errors_url'] = dashboard_url + '#errors'
525
526 build['warnings'] = build_obj.warnings.count()
527 build['dashboard_warnings_url'] = dashboard_url + '#warnings'
528
529 build['buildtime'] = sectohms(build_obj.timespent_seconds)
530 build['buildtime_url'] = buildtime_url
531
532 build['rebuild_url'] = rebuild_url
533 build['cancel_url'] = cancel_url
534
535 build['is_default_project_build'] = build_obj.project.is_default
536
537 build['build_targets_json'] = \
538 template_json(get_tasks(build_obj.target_set.all()))
539
540 # convert completed_on time to user's timezone
541 completed_on = timezone.localtime(build_obj.completed_on)
542
543 completed_on_template = '%H:%M'
544 if self._was_yesterday_or_earlier(completed_on):
545 completed_on_template = '%d/%m/%Y ' + completed_on_template
546 build['completed_on'] = completed_on.strftime(
547 completed_on_template)
548
549 targets = []
550 target_objs = build_obj.get_sorted_target_list()
551 for target_obj in target_objs:
552 if target_obj.task:
553 targets.append(target_obj.target + ':' + target_obj.task)
554 else:
555 targets.append(target_obj.target)
556 build['targets'] = ' '.join(targets)
557
558 # abbreviated form of the full target list
559 abbreviated_targets = ''
560 num_targets = len(targets)
561 if num_targets > 0:
562 abbreviated_targets = targets[0]
563 if num_targets > 1:
564 abbreviated_targets += (' +%s' % (num_targets - 1))
565 build['targets_abbreviated'] = abbreviated_targets
566
567 recent_builds.append(build)
568
569 return JsonResponse(recent_builds, safe=False)