diff options
Diffstat (limited to 'bitbake/lib/toaster/toastergui/tables.py')
| -rw-r--r-- | bitbake/lib/toaster/toastergui/tables.py | 184 |
1 files changed, 151 insertions, 33 deletions
diff --git a/bitbake/lib/toaster/toastergui/tables.py b/bitbake/lib/toaster/toastergui/tables.py index 58abe36b05..d0ed49625d 100644 --- a/bitbake/lib/toaster/toastergui/tables.py +++ b/bitbake/lib/toaster/toastergui/tables.py | |||
| @@ -23,9 +23,11 @@ from toastergui.widgets import ToasterTable | |||
| 23 | from toastergui.querysetfilter import QuerysetFilter | 23 | from toastergui.querysetfilter import QuerysetFilter |
| 24 | from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project | 24 | from orm.models import Recipe, ProjectLayer, Layer_Version, Machine, Project |
| 25 | from orm.models import CustomImageRecipe, Package, Build, LogMessage, Task | 25 | from orm.models import CustomImageRecipe, Package, Build, LogMessage, Task |
| 26 | from orm.models import ProjectTarget | ||
| 26 | from django.db.models import Q, Max, Count | 27 | from django.db.models import Q, Max, Count |
| 27 | from django.conf.urls import url | 28 | from django.conf.urls import url |
| 28 | from django.core.urlresolvers import reverse | 29 | from django.core.urlresolvers import reverse, resolve |
| 30 | from django.http import HttpResponse | ||
| 29 | from django.views.generic import TemplateView | 31 | from django.views.generic import TemplateView |
| 30 | import itertools | 32 | import itertools |
| 31 | 33 | ||
| @@ -775,7 +777,7 @@ class ProjectsTable(ToasterTable): | |||
| 775 | ''' | 777 | ''' |
| 776 | 778 | ||
| 777 | errors_template = ''' | 779 | errors_template = ''' |
| 778 | {% if data.get_number_of_builds > 0 %} | 780 | {% if data.get_number_of_builds > 0 and data.get_last_errors > 0 %} |
| 779 | <a class="errors.count error" | 781 | <a class="errors.count error" |
| 780 | href="{% url "builddashboard" data.get_last_build_id %}#errors"> | 782 | href="{% url "builddashboard" data.get_last_build_id %}#errors"> |
| 781 | {{data.get_last_errors}} error{{data.get_last_errors | pluralize}} | 783 | {{data.get_last_errors}} error{{data.get_last_errors | pluralize}} |
| @@ -784,7 +786,7 @@ class ProjectsTable(ToasterTable): | |||
| 784 | ''' | 786 | ''' |
| 785 | 787 | ||
| 786 | warnings_template = ''' | 788 | warnings_template = ''' |
| 787 | {% if data.get_number_of_builds > 0 %} | 789 | {% if data.get_number_of_builds > 0 and data.get_last_warnings > 0 %} |
| 788 | <a class="warnings.count warning" | 790 | <a class="warnings.count warning" |
| 789 | href="{% url "builddashboard" data.get_last_build_id %}#warnings"> | 791 | href="{% url "builddashboard" data.get_last_build_id %}#warnings"> |
| 790 | {{data.get_last_warnings}} warning{{data.get_last_warnings | pluralize}} | 792 | {{data.get_last_warnings}} warning{{data.get_last_warnings | pluralize}} |
| @@ -886,30 +888,45 @@ class BuildsTable(ToasterTable): | |||
| 886 | def __init__(self, *args, **kwargs): | 888 | def __init__(self, *args, **kwargs): |
| 887 | super(BuildsTable, self).__init__(*args, **kwargs) | 889 | super(BuildsTable, self).__init__(*args, **kwargs) |
| 888 | self.default_orderby = '-completed_on' | 890 | self.default_orderby = '-completed_on' |
| 889 | self.title = 'All builds' | ||
| 890 | self.static_context_extra['Build'] = Build | 891 | self.static_context_extra['Build'] = Build |
| 891 | self.static_context_extra['Task'] = Task | 892 | self.static_context_extra['Task'] = Task |
| 892 | 893 | ||
| 894 | # attributes that are overridden in subclasses | ||
| 895 | |||
| 896 | # title for the page | ||
| 897 | self.title = '' | ||
| 898 | |||
| 899 | # 'project' or 'all'; determines how the mrb (most recent builds) | ||
| 900 | # section is displayed | ||
| 901 | self.mrb_type = '' | ||
| 902 | |||
| 903 | def get_builds(self): | ||
| 904 | """ | ||
| 905 | overridden in ProjectBuildsTable to return builds for a | ||
| 906 | single project | ||
| 907 | """ | ||
| 908 | return Build.objects.all() | ||
| 909 | |||
| 893 | def get_context_data(self, **kwargs): | 910 | def get_context_data(self, **kwargs): |
| 894 | context = super(BuildsTable, self).get_context_data(**kwargs) | 911 | context = super(BuildsTable, self).get_context_data(**kwargs) |
| 895 | 912 | ||
| 896 | # for the latest builds section | 913 | # for the latest builds section |
| 897 | queryset = Build.objects.all() | 914 | builds = self.get_builds() |
| 898 | 915 | ||
| 899 | finished_criteria = Q(outcome=Build.SUCCEEDED) | Q(outcome=Build.FAILED) | 916 | finished_criteria = Q(outcome=Build.SUCCEEDED) | Q(outcome=Build.FAILED) |
| 900 | 917 | ||
| 901 | latest_builds = itertools.chain( | 918 | latest_builds = itertools.chain( |
| 902 | queryset.filter(outcome=Build.IN_PROGRESS).order_by("-started_on"), | 919 | builds.filter(outcome=Build.IN_PROGRESS).order_by("-started_on"), |
| 903 | queryset.filter(finished_criteria).order_by("-completed_on")[:3] | 920 | builds.filter(finished_criteria).order_by("-completed_on")[:3] |
| 904 | ) | 921 | ) |
| 905 | 922 | ||
| 906 | context['mru'] = list(latest_builds) | 923 | context['mru'] = list(latest_builds) |
| 907 | context['mrb_type'] = 'all' | 924 | context['mrb_type'] = self.mrb_type |
| 908 | 925 | ||
| 909 | return context | 926 | return context |
| 910 | 927 | ||
| 911 | def setup_queryset(self, *args, **kwargs): | 928 | def setup_queryset(self, *args, **kwargs): |
| 912 | queryset = Build.objects.all() | 929 | queryset = self.get_builds() |
| 913 | 930 | ||
| 914 | # don't include in progress builds | 931 | # don't include in progress builds |
| 915 | queryset = queryset.exclude(outcome=Build.IN_PROGRESS) | 932 | queryset = queryset.exclude(outcome=Build.IN_PROGRESS) |
| @@ -949,7 +966,8 @@ class BuildsTable(ToasterTable): | |||
| 949 | {% if data.cooker_log_path %} | 966 | {% if data.cooker_log_path %} |
| 950 | | 967 | |
| 951 | <a href="{% url "build_artifact" data.id "cookerlog" data.id %}"> | 968 | <a href="{% url "build_artifact" data.id "cookerlog" data.id %}"> |
| 952 | <i class="icon-download-alt" title="Download build log"></i> | 969 | <i class="icon-download-alt get-help" |
| 970 | data-original-title="Download build log"></i> | ||
| 953 | </a> | 971 | </a> |
| 954 | {% endif %} | 972 | {% endif %} |
| 955 | ''' | 973 | ''' |
| @@ -1031,19 +1049,6 @@ class BuildsTable(ToasterTable): | |||
| 1031 | {% endif %} | 1049 | {% endif %} |
| 1032 | ''' | 1050 | ''' |
| 1033 | 1051 | ||
| 1034 | project_template = ''' | ||
| 1035 | {% load project_url_tag %} | ||
| 1036 | <a href="{% project_url data.project %}"> | ||
| 1037 | {{data.project.name}} | ||
| 1038 | </a> | ||
| 1039 | {% if data.project.is_default %} | ||
| 1040 | <i class="icon-question-sign get-help hover-help" title="" | ||
| 1041 | data-original-title="This project shows information about | ||
| 1042 | the builds you start from the command line while Toaster is | ||
| 1043 | running" style="visibility: hidden;"></i> | ||
| 1044 | {% endif %} | ||
| 1045 | ''' | ||
| 1046 | |||
| 1047 | self.add_column(title='Outcome', | 1052 | self.add_column(title='Outcome', |
| 1048 | help_text='Final state of the build (successful \ | 1053 | help_text='Final state of the build (successful \ |
| 1049 | or failed)', | 1054 | or failed)', |
| @@ -1098,16 +1103,16 @@ class BuildsTable(ToasterTable): | |||
| 1098 | help_text='The number of errors encountered during \ | 1103 | help_text='The number of errors encountered during \ |
| 1099 | the build (if any)', | 1104 | the build (if any)', |
| 1100 | hideable=True, | 1105 | hideable=True, |
| 1101 | orderable=False, | 1106 | orderable=True, |
| 1102 | static_data_name='errors', | 1107 | static_data_name='errors_no', |
| 1103 | static_data_template=errors_template) | 1108 | static_data_template=errors_template) |
| 1104 | 1109 | ||
| 1105 | self.add_column(title='Warnings', | 1110 | self.add_column(title='Warnings', |
| 1106 | help_text='The number of warnings encountered during \ | 1111 | help_text='The number of warnings encountered during \ |
| 1107 | the build (if any)', | 1112 | the build (if any)', |
| 1108 | hideable=True, | 1113 | hideable=True, |
| 1109 | orderable=False, | 1114 | orderable=True, |
| 1110 | static_data_name='warnings', | 1115 | static_data_name='warnings_no', |
| 1111 | static_data_template=warnings_template) | 1116 | static_data_template=warnings_template) |
| 1112 | 1117 | ||
| 1113 | self.add_column(title='Time', | 1118 | self.add_column(title='Time', |
| @@ -1125,12 +1130,6 @@ class BuildsTable(ToasterTable): | |||
| 1125 | static_data_name='image_files', | 1130 | static_data_name='image_files', |
| 1126 | static_data_template=image_files_template) | 1131 | static_data_template=image_files_template) |
| 1127 | 1132 | ||
| 1128 | self.add_column(title='Project', | ||
| 1129 | hideable=True, | ||
| 1130 | orderable=False, | ||
| 1131 | static_data_name='project-name', | ||
| 1132 | static_data_template=project_template) | ||
| 1133 | |||
| 1134 | def setup_filters(self, *args, **kwargs): | 1133 | def setup_filters(self, *args, **kwargs): |
| 1135 | # outcomes | 1134 | # outcomes |
| 1136 | outcome_filter = TableFilter( | 1135 | outcome_filter = TableFilter( |
| @@ -1239,3 +1238,122 @@ class BuildsTable(ToasterTable): | |||
| 1239 | failed_tasks_filter.add_action(with_failed_tasks_action) | 1238 | failed_tasks_filter.add_action(with_failed_tasks_action) |
| 1240 | failed_tasks_filter.add_action(without_failed_tasks_action) | 1239 | failed_tasks_filter.add_action(without_failed_tasks_action) |
| 1241 | self.add_filter(failed_tasks_filter) | 1240 | self.add_filter(failed_tasks_filter) |
| 1241 | |||
| 1242 | def post(self, request, *args, **kwargs): | ||
| 1243 | """ Process HTTP POSTs which make build requests """ | ||
| 1244 | |||
| 1245 | project = Project.objects.get(pk=kwargs['pid']) | ||
| 1246 | |||
| 1247 | if 'buildCancel' in request.POST: | ||
| 1248 | for i in request.POST['buildCancel'].strip().split(" "): | ||
| 1249 | try: | ||
| 1250 | br = BuildRequest.objects.select_for_update().get(project = project, pk = i, state__lte = BuildRequest.REQ_QUEUED) | ||
| 1251 | br.state = BuildRequest.REQ_DELETED | ||
| 1252 | br.save() | ||
| 1253 | except BuildRequest.DoesNotExist: | ||
| 1254 | pass | ||
| 1255 | |||
| 1256 | if 'buildDelete' in request.POST: | ||
| 1257 | for i in request.POST['buildDelete'].strip().split(" "): | ||
| 1258 | try: | ||
| 1259 | BuildRequest.objects.select_for_update().get(project = project, pk = i, state__lte = BuildRequest.REQ_DELETED).delete() | ||
| 1260 | except BuildRequest.DoesNotExist: | ||
| 1261 | pass | ||
| 1262 | |||
| 1263 | if 'targets' in request.POST: | ||
| 1264 | ProjectTarget.objects.filter(project = project).delete() | ||
| 1265 | s = str(request.POST['targets']) | ||
| 1266 | for t in s.translate(None, ";%|\"").split(" "): | ||
| 1267 | if ":" in t: | ||
| 1268 | target, task = t.split(":") | ||
| 1269 | else: | ||
| 1270 | target = t | ||
| 1271 | task = "" | ||
| 1272 | ProjectTarget.objects.create(project = project, | ||
| 1273 | target = target, | ||
| 1274 | task = task) | ||
| 1275 | project.schedule_build() | ||
| 1276 | |||
| 1277 | # redirect back to builds page so any new builds in progress etc. | ||
| 1278 | # are visible | ||
| 1279 | response = HttpResponse() | ||
| 1280 | response.status_code = 302 | ||
| 1281 | response['Location'] = request.build_absolute_uri() | ||
| 1282 | return response | ||
| 1283 | |||
| 1284 | class AllBuildsTable(BuildsTable): | ||
| 1285 | """ Builds page for all builds """ | ||
| 1286 | |||
| 1287 | def __init__(self, *args, **kwargs): | ||
| 1288 | super(AllBuildsTable, self).__init__(*args, **kwargs) | ||
| 1289 | self.title = 'All builds' | ||
| 1290 | self.mrb_type = 'all' | ||
| 1291 | |||
| 1292 | def setup_columns(self, *args, **kwargs): | ||
| 1293 | """ | ||
| 1294 | All builds page shows a column for the project | ||
| 1295 | """ | ||
| 1296 | |||
| 1297 | super(AllBuildsTable, self).setup_columns(*args, **kwargs) | ||
| 1298 | |||
| 1299 | project_template = ''' | ||
| 1300 | {% load project_url_tag %} | ||
| 1301 | <a href="{% project_url data.project %}"> | ||
| 1302 | {{data.project.name}} | ||
| 1303 | </a> | ||
| 1304 | {% if data.project.is_default %} | ||
| 1305 | <i class="icon-question-sign get-help hover-help" title="" | ||
| 1306 | data-original-title="This project shows information about | ||
| 1307 | the builds you start from the command line while Toaster is | ||
| 1308 | running" style="visibility: hidden;"></i> | ||
| 1309 | {% endif %} | ||
| 1310 | ''' | ||
| 1311 | |||
| 1312 | self.add_column(title='Project', | ||
| 1313 | hideable=True, | ||
| 1314 | orderable=True, | ||
| 1315 | static_data_name='project', | ||
| 1316 | static_data_template=project_template) | ||
| 1317 | |||
| 1318 | class ProjectBuildsTable(BuildsTable): | ||
| 1319 | """ | ||
| 1320 | Builds page for a single project; a BuildsTable, with the queryset | ||
| 1321 | filtered by project | ||
| 1322 | """ | ||
| 1323 | |||
| 1324 | def __init__(self, *args, **kwargs): | ||
| 1325 | super(ProjectBuildsTable, self).__init__(*args, **kwargs) | ||
| 1326 | self.title = 'All project builds' | ||
| 1327 | self.mrb_type = 'project' | ||
| 1328 | |||
| 1329 | # set from the querystring | ||
| 1330 | self.project_id = None | ||
| 1331 | |||
| 1332 | def setup_queryset(self, *args, **kwargs): | ||
| 1333 | """ | ||
| 1334 | NOTE: self.project_id must be set before calling super(), | ||
| 1335 | as it's used in setup_queryset() | ||
| 1336 | """ | ||
| 1337 | self.project_id = kwargs['pid'] | ||
| 1338 | super(ProjectBuildsTable, self).setup_queryset(*args, **kwargs) | ||
| 1339 | |||
| 1340 | project = Project.objects.get(pk=self.project_id) | ||
| 1341 | self.queryset = self.queryset.filter(project=project) | ||
| 1342 | |||
| 1343 | def get_context_data(self, **kwargs): | ||
| 1344 | """ | ||
| 1345 | NOTE: self.project_id must be set before calling super(), | ||
| 1346 | as it's used in get_context_data() | ||
| 1347 | """ | ||
| 1348 | self.project_id = kwargs['pid'] | ||
| 1349 | |||
| 1350 | context = super(ProjectBuildsTable, self).get_context_data(**kwargs) | ||
| 1351 | context['project'] = Project.objects.get(pk=self.project_id) | ||
| 1352 | |||
| 1353 | return context | ||
| 1354 | |||
| 1355 | def get_builds(self): | ||
| 1356 | """ override: only return builds for the relevant project """ | ||
| 1357 | |||
| 1358 | project = Project.objects.get(pk=self.project_id) | ||
| 1359 | return Build.objects.filter(project=project) | ||
