summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliot Smith <elliot.smith@intel.com>2016-04-19 17:28:46 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-04-19 21:11:26 +0100
commit1cf8f215b3543ce0f39ebd3321d58cfb518f1c1f (patch)
treeac2ec1be24db0eb3f845d53cfa7ce6c18c36ab70
parenta40a3e6defefd69521a417a366b8752be7e778f9 (diff)
downloadpoky-1cf8f215b3543ce0f39ebd3321d58cfb518f1c1f.tar.gz
bitbake: toaster: add modal to select custom image for editing
Add functionality to the placeholder button on the build dashboard to open a modal dialog displaying editable custom images, in cases where multiple custom images were built by the build. Where there is only one editable custom image, go direct to its edit page. The images shown in the modal are custom recipes for the project which were built during the build shown in the dashboard. This also affects the new custom image dialog, as that also has to show custom image recipes as well as image recipes built during the build. Modify the API on the Build object to support both. Also modify and rename the queryset_to_list template filter so that it can deal with lists as well as querysets, as the new custom image modal has to show a list of image recipes which is an amalgam of two querysets. [YOCTO #9123] (Bitbake rev: 8c2aea3fa8e1071de60390e86e2536904fa9b7c0) Signed-off-by: Elliot Smith <elliot.smith@intel.com> Signed-off-by: Michael Wood <michael.g.wood@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--bitbake/lib/toaster/orm/models.py45
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js15
-rw-r--r--bitbake/lib/toaster/toastergui/templates/base.html1
-rw-r--r--bitbake/lib/toaster/toastergui/templates/basebuildpage.html62
-rw-r--r--bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html68
-rw-r--r--bitbake/lib/toaster/toastergui/templatetags/objects_to_dictionaries_filter.py35
-rw-r--r--bitbake/lib/toaster/toastergui/templatetags/queryset_to_list_filter.py26
-rwxr-xr-xbitbake/lib/toaster/toastergui/views.py26
8 files changed, 186 insertions, 92 deletions
diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py
index 75e6ea3996..0b83b991b9 100644
--- a/bitbake/lib/toaster/orm/models.py
+++ b/bitbake/lib/toaster/orm/models.py
@@ -503,33 +503,37 @@ class Build(models.Model):
503 return Recipe.objects.filter(criteria) \ 503 return Recipe.objects.filter(criteria) \
504 .select_related('layer_version', 'layer_version__layer') 504 .select_related('layer_version', 'layer_version__layer')
505 505
506 def get_custom_image_recipe_names(self):
507 """
508 Get the names of custom image recipes for this build's project
509 as a list; this is used to screen out custom image recipes from the
510 recipes for the build by name, and to distinguish image recipes from
511 custom image recipes
512 """
513 custom_image_recipes = \
514 CustomImageRecipe.objects.filter(project=self.project)
515 return custom_image_recipes.values_list('name', flat=True)
516
517 def get_image_recipes(self): 506 def get_image_recipes(self):
518 """ 507 """
519 Returns a queryset of image recipes related to this build, sorted 508 Returns a list of image Recipes (custom and built-in) related to this
520 by name 509 build, sorted by name; note that this has to be done in two steps, as
510 there's no way to get all the custom image recipes and image recipes
511 in one query
521 """ 512 """
522 criteria = Q(is_image=True) 513 custom_image_recipes = self.get_custom_image_recipes()
523 return self.get_recipes().filter(criteria).order_by('name') 514 custom_image_recipe_names = custom_image_recipes.values_list('name', flat=True)
515
516 not_custom_image_recipes = ~Q(name__in=custom_image_recipe_names) & \
517 Q(is_image=True)
518
519 built_image_recipes = self.get_recipes().filter(not_custom_image_recipes)
520
521 # append to the custom image recipes and sort
522 customisable_image_recipes = list(
523 itertools.chain(custom_image_recipes, built_image_recipes)
524 )
525
526 return sorted(customisable_image_recipes, key=lambda recipe: recipe.name)
524 527
525 def get_custom_image_recipes(self): 528 def get_custom_image_recipes(self):
526 """ 529 """
527 Returns a queryset of custom image recipes related to this build, 530 Returns a queryset of CustomImageRecipes related to this build,
528 sorted by name 531 sorted by name
529 """ 532 """
530 custom_image_recipe_names = self.get_custom_image_recipe_names() 533 built_recipe_names = self.get_recipes().values_list('name', flat=True)
531 criteria = Q(is_image=True) & Q(name__in=custom_image_recipe_names) 534 criteria = Q(name__in=built_recipe_names) & Q(project=self.project)
532 return self.get_recipes().filter(criteria).order_by('name') 535 queryset = CustomImageRecipe.objects.filter(criteria).order_by('name')
536 return queryset
533 537
534 def get_outcome_text(self): 538 def get_outcome_text(self):
535 return Build.BUILD_OUTCOME[int(self.outcome)][1] 539 return Build.BUILD_OUTCOME[int(self.outcome)][1]
@@ -1380,6 +1384,9 @@ class Layer(models.Model):
1380 1384
1381# LayerCommit class is synced with layerindex.LayerBranch 1385# LayerCommit class is synced with layerindex.LayerBranch
1382class Layer_Version(models.Model): 1386class Layer_Version(models.Model):
1387 """
1388 A Layer_Version either belongs to a single project or no project
1389 """
1383 search_allowed_fields = ["layer__name", "layer__summary", "layer__description", "layer__vcs_url", "dirpath", "up_branch__name", "commit", "branch"] 1390 search_allowed_fields = ["layer__name", "layer__summary", "layer__description", "layer__vcs_url", "dirpath", "up_branch__name", "commit", "branch"]
1384 build = models.ForeignKey(Build, related_name='layer_version_build', default = None, null = True) 1391 build = models.ForeignKey(Build, related_name='layer_version_build', default = None, null = True)
1385 layer = models.ForeignKey(Layer, related_name='layer_version_layer') 1392 layer = models.ForeignKey(Layer, related_name='layer_version_layer')
diff --git a/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js b/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js
index 1ae0d34e90..cb9ed4da05 100644
--- a/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js
+++ b/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js
@@ -12,6 +12,7 @@ for the new custom image. This will manage the addition of radio buttons
12to select the base image (or remove the radio buttons, if there is only a 12to select the base image (or remove the radio buttons, if there is only a
13single base image available). 13single base image available).
14*/ 14*/
15
15function newCustomImageModalInit(){ 16function newCustomImageModalInit(){
16 17
17 var newCustomImgBtn = $("#create-new-custom-image-btn"); 18 var newCustomImgBtn = $("#create-new-custom-image-btn");
@@ -21,7 +22,8 @@ function newCustomImageModalInit(){
21 var nameInput = imgCustomModal.find('input'); 22 var nameInput = imgCustomModal.find('input');
22 23
23 var invalidNameMsg = "Image names cannot contain spaces or capital letters. The only allowed special character is dash (-)."; 24 var invalidNameMsg = "Image names cannot contain spaces or capital letters. The only allowed special character is dash (-).";
24 var duplicateNameMsg = "An image with this name already exists. Image names must be unique."; 25 var duplicateNameMsg = "A recipe with this name already exists. Image names must be unique.";
26 var duplicateImageInProjectMsg = "An image with this name already exists in this project."
25 var invalidBaseRecipeIdMsg = "Please select an image to customise."; 27 var invalidBaseRecipeIdMsg = "Please select an image to customise.";
26 28
27 // capture clicks on radio buttons inside the modal; when one is selected, 29 // capture clicks on radio buttons inside the modal; when one is selected,
@@ -51,9 +53,12 @@ function newCustomImageModalInit(){
51 if (ret.error === "invalid-name") { 53 if (ret.error === "invalid-name") {
52 showNameError(invalidNameMsg); 54 showNameError(invalidNameMsg);
53 return; 55 return;
54 } else if (ret.error === "already-exists") { 56 } else if (ret.error === "recipe-already-exists") {
55 showNameError(duplicateNameMsg); 57 showNameError(duplicateNameMsg);
56 return; 58 return;
59 } else if (ret.error === "image-already-exists") {
60 showNameError(duplicateImageInProjectMsg);
61 return;
57 } 62 }
58 } else { 63 } else {
59 imgCustomModal.modal('hide'); 64 imgCustomModal.modal('hide');
@@ -112,13 +117,13 @@ function newCustomImageModalSetRecipes(baseRecipes) {
112 var imageSelector = $('#new-custom-image-modal [data-role="image-selector"]'); 117 var imageSelector = $('#new-custom-image-modal [data-role="image-selector"]');
113 var imageSelectRadiosContainer = $('#new-custom-image-modal [data-role="image-selector-radios"]'); 118 var imageSelectRadiosContainer = $('#new-custom-image-modal [data-role="image-selector-radios"]');
114 119
120 // remove any existing radio buttons + labels
121 imageSelector.remove('[data-role="image-radio"]');
122
115 if (baseRecipes.length === 1) { 123 if (baseRecipes.length === 1) {
116 // hide the radio button container 124 // hide the radio button container
117 imageSelector.hide(); 125 imageSelector.hide();
118 126
119 // remove any radio buttons + labels
120 imageSelector.remove('[data-role="image-radio"]');
121
122 // set the single recipe ID on the modal as it's the only one 127 // set the single recipe ID on the modal as it's the only one
123 // we can build from 128 // we can build from
124 imgCustomModal.data('recipe', baseRecipes[0].id); 129 imgCustomModal.data('recipe', baseRecipes[0].id);
diff --git a/bitbake/lib/toaster/toastergui/templates/base.html b/bitbake/lib/toaster/toastergui/templates/base.html
index 192f9fb556..210cf3360c 100644
--- a/bitbake/lib/toaster/toastergui/templates/base.html
+++ b/bitbake/lib/toaster/toastergui/templates/base.html
@@ -43,7 +43,6 @@
43 recipesTypeAheadUrl: {% url 'xhr_recipestypeahead' project.id as paturl%}{{paturl|json}}, 43 recipesTypeAheadUrl: {% url 'xhr_recipestypeahead' project.id as paturl%}{{paturl|json}},
44 layersTypeAheadUrl: {% url 'xhr_layerstypeahead' project.id as paturl%}{{paturl|json}}, 44 layersTypeAheadUrl: {% url 'xhr_layerstypeahead' project.id as paturl%}{{paturl|json}},
45 machinesTypeAheadUrl: {% url 'xhr_machinestypeahead' project.id as paturl%}{{paturl|json}}, 45 machinesTypeAheadUrl: {% url 'xhr_machinestypeahead' project.id as paturl%}{{paturl|json}},
46
47 projectBuildsUrl: {% url 'projectbuilds' project.id as pburl %}{{pburl|json}}, 46 projectBuildsUrl: {% url 'projectbuilds' project.id as pburl %}{{pburl|json}},
48 xhrCustomRecipeUrl : "{% url 'xhr_customrecipe' %}", 47 xhrCustomRecipeUrl : "{% url 'xhr_customrecipe' %}",
49 projectId : {{project.id}}, 48 projectId : {{project.id}},
diff --git a/bitbake/lib/toaster/toastergui/templates/basebuildpage.html b/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
index 4a8e2a7abd..0d8c8820da 100644
--- a/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
+++ b/bitbake/lib/toaster/toastergui/templates/basebuildpage.html
@@ -1,7 +1,7 @@
1{% extends "base.html" %} 1{% extends "base.html" %}
2{% load projecttags %} 2{% load projecttags %}
3{% load project_url_tag %} 3{% load project_url_tag %}
4{% load queryset_to_list_filter %} 4{% load objects_to_dictionaries_filter %}
5{% load humanize %} 5{% load humanize %}
6{% block pagecontent %} 6{% block pagecontent %}
7 <!-- breadcrumbs --> 7 <!-- breadcrumbs -->
@@ -81,33 +81,40 @@
81 </p> 81 </p>
82 </li> 82 </li>
83 83
84 <li> 84 {% with build.get_custom_image_recipes as custom_image_recipes %}
85 <!-- edit custom image built during this build --> 85 {% if custom_image_recipes.count > 0 %}
86 <p class="navbar-btn" data-role="edit-custom-image-trigger"> 86 <!-- edit custom image built during this build -->
87 <button class="btn btn-block">Edit custom image</button> 87 <li>
88 </p> 88 <p class="navbar-btn" data-role="edit-custom-image-trigger">
89 {% include 'editcustomimage_modal.html' %} 89 <button class="btn btn-block">Edit custom image</button>
90 <script> 90 {% include 'editcustomimage_modal.html' %}
91 $(document).ready(function () { 91 <script>
92 var editableCustomImageRecipes = {{ build.get_custom_image_recipes | queryset_to_list:"id,name" | json }}; 92 var editableCustomImageRecipes = {{ custom_image_recipes | objects_to_dictionaries:"id,name" | json }};
93
94 // edit custom image which was built during this build
95 var editCustomImageModal = $('#edit-custom-image-modal');
96 var editCustomImageTrigger = $('[data-role="edit-custom-image-trigger"]');
97 93
98 editCustomImageTrigger.click(function () { 94 $(document).ready(function () {
99 // if there is a single editable custom image, go direct to the edit 95 var editCustomImageTrigger = $('[data-role="edit-custom-image-trigger"]');
100 // page for it; if there are multiple editable custom images, show 96 var editCustomImageModal = $('#edit-custom-image-modal');
101 // dialog to select one of them for editing
102 97
103 // single editable custom image 98 // edit custom image which was built during this build
104 99 editCustomImageTrigger.click(function () {
105 // multiple editable custom images 100 // single editable custom image: redirect to the edit page
106 editCustomImageModal.modal('show'); 101 // for that image
107 }); 102 if (editableCustomImageRecipes.length === 1) {
108 }); 103 var url = '{% url "customrecipe" build.project.id custom_image_recipes.first.id %}';
109 </script> 104 document.location.href = url;
110 </li> 105 }
106 // multiple editable custom images: show modal to select
107 // one of them for editing
108 else {
109 editCustomImageModal.modal('show');
110 }
111 });
112 });
113 </script>
114 </p>
115 </li>
116 {% endif %}
117 {% endwith %}
111 118
112 <li> 119 <li>
113 <!-- new custom image from image recipe in this build --> 120 <!-- new custom image from image recipe in this build -->
@@ -119,7 +126,7 @@
119 // imageRecipes includes both custom image recipes and built-in 126 // imageRecipes includes both custom image recipes and built-in
120 // image recipes, any of which can be used as the basis for a 127 // image recipes, any of which can be used as the basis for a
121 // new custom image 128 // new custom image
122 var imageRecipes = {{ build.get_image_recipes | queryset_to_list:"id,name" | json }}; 129 var imageRecipes = {{ build.get_image_recipes | objects_to_dictionaries:"id,name" | json }};
123 130
124 $(document).ready(function () { 131 $(document).ready(function () {
125 var newCustomImageModal = $('#new-custom-image-modal'); 132 var newCustomImageModal = $('#new-custom-image-modal');
@@ -131,6 +138,7 @@
131 if (!imageRecipes.length) { 138 if (!imageRecipes.length) {
132 return; 139 return;
133 } 140 }
141
134 newCustomImageModalSetRecipes(imageRecipes); 142 newCustomImageModalSetRecipes(imageRecipes);
135 newCustomImageModal.modal('show'); 143 newCustomImageModal.modal('show');
136 }); 144 });
diff --git a/bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html b/bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html
index fd998f63eb..8046c08fb5 100644
--- a/bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html
+++ b/bitbake/lib/toaster/toastergui/templates/editcustomimage_modal.html
@@ -1,23 +1,71 @@
1<!-- 1<!--
2modal dialog shown on the build dashboard, for editing an existing custom image 2modal dialog shown on the build dashboard, for editing an existing custom image;
3only shown if more than one custom image was built, so the user needs to
4choose which one to edit
5
6required context:
7 build - a Build object
3--> 8-->
4<div class="modal hide fade in" aria-hidden="false" id="edit-custom-image-modal"> 9<div class="modal hide fade in" aria-hidden="false" id="edit-custom-image-modal">
5 <div class="modal-header"> 10 <div class="modal-header">
6 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> 11 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
7 <h3>Select custom image to edit</h3> 12 <h3>Which image do you want to edit?</h3>
8 </div> 13 </div>
14
9 <div class="modal-body"> 15 <div class="modal-body">
10 <div class="row-fluid"> 16 <div class="row-fluid">
11 <span class="help-block"> 17 {% for recipe in build.get_custom_image_recipes %}
12 Explanation of what this modal is for 18 <label class="radio">
13 </span> 19 {{recipe.name}}
14 </div> 20 <input type="radio" class="form-control" name="select-custom-image"
15 <div class="control-group controls"> 21 data-url="{% url 'customrecipe' build.project.id recipe.id %}">
16 <input type="text" class="huge" placeholder="input box" required> 22 </label>
17 <span class="help-block error" style="display:none">Error text</span> 23 {% endfor %}
18 </div> 24 </div>
25 <span class="help-block error" id="invalid-custom-image-help" style="display:none">
26 Please select a custom image to edit.
27 </span>
19 </div> 28 </div>
29
20 <div class="modal-footer"> 30 <div class="modal-footer">
21 <button class="btn btn-primary btn-large" disabled>Action</button> 31 <button class="btn btn-primary btn-large" data-url="#"
32 data-action="edit-custom-image" disabled>
33 Edit custom image
34 </button>
22 </div> 35 </div>
23</div> 36</div>
37
38<script>
39$(document).ready(function () {
40 var editCustomImageButton = $('[data-action="edit-custom-image"]');
41 var error = $('#invalid-custom-image-help');
42 var radios = $('[name="select-custom-image"]');
43
44 // return custom image radio buttons which are selected
45 var getSelectedRadios = function () {
46 return $('[name="select-custom-image"]:checked');
47 };
48
49 radios.change(function () {
50 if (getSelectedRadios().length === 1) {
51 editCustomImageButton.removeAttr('disabled');
52 error.hide();
53 }
54 else {
55 editCustomImageButton.attr('disabled', 'disabled');
56 error.show();
57 }
58 });
59
60 editCustomImageButton.click(function () {
61 var selectedRadios = getSelectedRadios();
62
63 if (selectedRadios.length === 1) {
64 document.location.href = selectedRadios.first().attr('data-url');
65 }
66 else {
67 error.show();
68 }
69 });
70});
71</script>
diff --git a/bitbake/lib/toaster/toastergui/templatetags/objects_to_dictionaries_filter.py b/bitbake/lib/toaster/toastergui/templatetags/objects_to_dictionaries_filter.py
new file mode 100644
index 0000000000..0dcc7d2714
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/templatetags/objects_to_dictionaries_filter.py
@@ -0,0 +1,35 @@
1from django import template
2import json
3
4register = template.Library()
5
6def objects_to_dictionaries(iterable, fields):
7 """
8 Convert an iterable into a list of dictionaries; fields should be set
9 to a comma-separated string of properties for each item included in the
10 resulting list; e.g. for a queryset:
11
12 {{ queryset | objects_to_dictionaries:"id,name" }}
13
14 will return a list like
15
16 [{'id': 1, 'name': 'foo'}, ...]
17
18 providing queryset has id and name fields
19
20 This is mostly to support serialising querysets or lists of model objects
21 to JSON
22 """
23 objects = []
24
25 if fields:
26 fields_list = [field.strip() for field in fields.split(',')]
27 for item in iterable:
28 out = {}
29 for field in fields_list:
30 out[field] = getattr(item, field)
31 objects.append(out)
32
33 return objects
34
35register.filter('objects_to_dictionaries', objects_to_dictionaries)
diff --git a/bitbake/lib/toaster/toastergui/templatetags/queryset_to_list_filter.py b/bitbake/lib/toaster/toastergui/templatetags/queryset_to_list_filter.py
deleted file mode 100644
index dfc094b591..0000000000
--- a/bitbake/lib/toaster/toastergui/templatetags/queryset_to_list_filter.py
+++ /dev/null
@@ -1,26 +0,0 @@
1from django import template
2import json
3
4register = template.Library()
5
6def queryset_to_list(queryset, fields):
7 """
8 Convert a queryset to a list; fields can be set to a comma-separated
9 string of fields for each record included in the resulting list; if
10 omitted, all fields are included for each record, e.g.
11
12 {{ queryset | queryset_to_list:"id,name" }}
13
14 will return a list like
15
16 [{'id': 1, 'name': 'foo'}, ...]
17
18 (providing queryset has id and name fields)
19 """
20 if fields:
21 fields_list = [field.strip() for field in fields.split(',')]
22 return list(queryset.values(*fields_list))
23 else:
24 return list(queryset.values())
25
26register.filter('queryset_to_list', queryset_to_list)
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 942dc31ae9..bd5bf63341 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -507,6 +507,7 @@ def builddashboard( request, build_id ):
507 507
508 context = { 508 context = {
509 'build' : build, 509 'build' : build,
510 'project' : build.project,
510 'hasImages' : hasImages, 511 'hasImages' : hasImages,
511 'ntargets' : ntargets, 512 'ntargets' : ntargets,
512 'targets' : targets, 513 'targets' : targets,
@@ -797,6 +798,7 @@ eans multiple licenses exist that cover different parts of the source',
797 context = { 798 context = {
798 'objectname': variant, 799 'objectname': variant,
799 'build' : build, 800 'build' : build,
801 'project' : build.project,
800 'target' : Target.objects.filter( pk = target_id )[ 0 ], 802 'target' : Target.objects.filter( pk = target_id )[ 0 ],
801 'objects' : packages, 803 'objects' : packages,
802 'packages_sum' : packages_sum[ 'installed_size__sum' ], 804 'packages_sum' : packages_sum[ 'installed_size__sum' ],
@@ -937,7 +939,10 @@ def dirinfo(request, build_id, target_id, file_path=None):
937 if head != sep: 939 if head != sep:
938 dir_list.insert(0, head) 940 dir_list.insert(0, head)
939 941
940 context = { 'build': Build.objects.get(pk=build_id), 942 build = Build.objects.get(pk=build_id)
943
944 context = { 'build': build,
945 'project': build.project,
941 'target': Target.objects.get(pk=target_id), 946 'target': Target.objects.get(pk=target_id),
942 'packages_sum': packages_sum['installed_size__sum'], 947 'packages_sum': packages_sum['installed_size__sum'],
943 'objects': objects, 948 'objects': objects,
@@ -1211,6 +1216,7 @@ def tasks_common(request, build_id, variant, task_anchor):
1211 'filter_search_display': filter_search_display, 1216 'filter_search_display': filter_search_display,
1212 'mainheading': title_variant, 1217 'mainheading': title_variant,
1213 'build': build, 1218 'build': build,
1219 'project': build.project,
1214 'objects': task_objects, 1220 'objects': task_objects,
1215 'default_orderby' : default_orderby, 1221 'default_orderby' : default_orderby,
1216 'search_term': search_term, 1222 'search_term': search_term,
@@ -1282,6 +1288,7 @@ def recipes(request, build_id):
1282 context = { 1288 context = {
1283 'objectname': 'recipes', 1289 'objectname': 'recipes',
1284 'build': build, 1290 'build': build,
1291 'project': build.project,
1285 'objects': recipes, 1292 'objects': recipes,
1286 'default_orderby' : 'name:+', 1293 'default_orderby' : 'name:+',
1287 'recipe_deps' : deps, 1294 'recipe_deps' : deps,
@@ -1366,10 +1373,12 @@ def configuration(request, build_id):
1366 'MACHINE', 'DISTRO', 'DISTRO_VERSION', 'TUNE_FEATURES', 'TARGET_FPU') 1373 'MACHINE', 'DISTRO', 'DISTRO_VERSION', 'TUNE_FEATURES', 'TARGET_FPU')
1367 context = dict(Variable.objects.filter(build=build_id, variable_name__in=var_names)\ 1374 context = dict(Variable.objects.filter(build=build_id, variable_name__in=var_names)\
1368 .values_list('variable_name', 'variable_value')) 1375 .values_list('variable_name', 'variable_value'))
1376 build = Build.objects.get(pk=build_id)
1369 context.update({'objectname': 'configuration', 1377 context.update({'objectname': 'configuration',
1370 'object_search_display':'variables', 1378 'object_search_display':'variables',
1371 'filter_search_display':'variables', 1379 'filter_search_display':'variables',
1372 'build': Build.objects.get(pk=build_id), 1380 'build': build,
1381 'project': build.project,
1373 'targets': Target.objects.filter(build=build_id)}) 1382 'targets': Target.objects.filter(build=build_id)})
1374 return render(request, template, context) 1383 return render(request, template, context)
1375 1384
@@ -1406,12 +1415,15 @@ def configvars(request, build_id):
1406 file_filter += '/bitbake.conf' 1415 file_filter += '/bitbake.conf'
1407 build_dir=re.sub("/tmp/log/.*","",Build.objects.get(pk=build_id).cooker_log_path) 1416 build_dir=re.sub("/tmp/log/.*","",Build.objects.get(pk=build_id).cooker_log_path)
1408 1417
1418 build = Build.objects.get(pk=build_id)
1419
1409 context = { 1420 context = {
1410 'objectname': 'configvars', 1421 'objectname': 'configvars',
1411 'object_search_display':'BitBake variables', 1422 'object_search_display':'BitBake variables',
1412 'filter_search_display':'variables', 1423 'filter_search_display':'variables',
1413 'file_filter': file_filter, 1424 'file_filter': file_filter,
1414 'build': Build.objects.get(pk=build_id), 1425 'build': build,
1426 'project': build.project,
1415 'objects' : variables, 1427 'objects' : variables,
1416 'total_count':queryset_with_search.count(), 1428 'total_count':queryset_with_search.count(),
1417 'default_orderby' : 'variable_name:+', 1429 'default_orderby' : 'variable_name:+',
@@ -1480,6 +1492,7 @@ def bpackage(request, build_id):
1480 context = { 1492 context = {
1481 'objectname': 'packages built', 1493 'objectname': 'packages built',
1482 'build': build, 1494 'build': build,
1495 'project': build.project,
1483 'objects' : packages, 1496 'objects' : packages,
1484 'default_orderby' : 'name:+', 1497 'default_orderby' : 'name:+',
1485 'tablecols':[ 1498 'tablecols':[
@@ -1554,7 +1567,12 @@ def bpackage(request, build_id):
1554def bfile(request, build_id, package_id): 1567def bfile(request, build_id, package_id):
1555 template = 'bfile.html' 1568 template = 'bfile.html'
1556 files = Package_File.objects.filter(package = package_id) 1569 files = Package_File.objects.filter(package = package_id)
1557 context = {'build': Build.objects.get(pk=build_id), 'objects' : files} 1570 build = Build.objects.get(pk=build_id)
1571 context = {
1572 'build': build,
1573 'project': build.project,
1574 'objects' : files
1575 }
1558 return render(request, template, context) 1576 return render(request, template, context)
1559 1577
1560 1578