diff options
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 |
| 1382 | class Layer_Version(models.Model): | 1386 | class 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 | |||
| 12 | to select the base image (or remove the radio buttons, if there is only a | 12 | to select the base image (or remove the radio buttons, if there is only a |
| 13 | single base image available). | 13 | single base image available). |
| 14 | */ | 14 | */ |
| 15 | |||
| 15 | function newCustomImageModalInit(){ | 16 | function 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 | <!-- |
| 2 | modal dialog shown on the build dashboard, for editing an existing custom image | 2 | modal dialog shown on the build dashboard, for editing an existing custom image; |
| 3 | only shown if more than one custom image was built, so the user needs to | ||
| 4 | choose which one to edit | ||
| 5 | |||
| 6 | required 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 @@ | |||
| 1 | from django import template | ||
| 2 | import json | ||
| 3 | |||
| 4 | register = template.Library() | ||
| 5 | |||
| 6 | def 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 | |||
| 35 | register.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 @@ | |||
| 1 | from django import template | ||
| 2 | import json | ||
| 3 | |||
| 4 | register = template.Library() | ||
| 5 | |||
| 6 | def 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 | |||
| 26 | register.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): | |||
| 1554 | def bfile(request, build_id, package_id): | 1567 | def 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 | ||
