diff options
Diffstat (limited to 'bitbake/lib/toaster')
-rw-r--r-- | bitbake/lib/toaster/bldcontrol/models.py | 33 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/static/css/default.css | 4 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/static/js/projectapp.js | 24 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/base.html | 3 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/basetable_bottom.html | 13 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/layers.html | 124 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/mrb_section.html | 11 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/project.html | 48 | ||||
-rw-r--r-- | bitbake/lib/toaster/toastergui/templates/targets.html | 340 | ||||
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 157 |
10 files changed, 458 insertions, 299 deletions
diff --git a/bitbake/lib/toaster/bldcontrol/models.py b/bitbake/lib/toaster/bldcontrol/models.py index f72fb8f..e643d08 100644 --- a/bitbake/lib/toaster/bldcontrol/models.py +++ b/bitbake/lib/toaster/bldcontrol/models.py @@ -42,11 +42,34 @@ class BuildEnvironment(models.Model): def get_artifact_type(self, path): if self.betype == BuildEnvironment.TYPE_LOCAL: - import magic - m = magic.open(magic.MAGIC_MIME_TYPE) - m.load() - return m.file(path) - raise Exception("FIXME: not implemented") + try: + import magic + + # fair warning: this is a mess; there are multiple competeing and incompatible + # magic modules floating around, so we try some of the most common combinations + + try: # we try ubuntu's python-magic 5.4 + m = magic.open(magic.MAGIC_MIME_TYPE) + m.load() + return m.file(path) + except AttributeError: + pass + + try: # we try python-magic 0.4.6 + m = magic.Magic(magic.MAGIC_MIME) + return m.from_file(path) + except AttributeError: + pass + + try: # we try pip filemagic 1.6 + m = magic.Magic(flags=magic.MAGIC_MIME_TYPE) + return m.id_filename(path) + except AttributeError: + pass + + return "binary/octet-stream" + except ImportError: + return "binary/octet-stream" def get_artifact(self, path): if self.betype == BuildEnvironment.TYPE_LOCAL: diff --git a/bitbake/lib/toaster/toastergui/static/css/default.css b/bitbake/lib/toaster/toastergui/static/css/default.css index 9e62c6c..fb20fc9 100644 --- a/bitbake/lib/toaster/toastergui/static/css/default.css +++ b/bitbake/lib/toaster/toastergui/static/css/default.css @@ -61,8 +61,8 @@ dd p { line-height: 20px; } /* Override default Twitter Boostrap styles for anchor tags inside tables */ td a, td a > code { color: #333333; } -td a > code { white-space: normal; } -td a:hover { color: #000000; text-decoration: underline; } +td code { white-space: normal; } +td a:hover, td a > code:hover { color: #000000; text-decoration: underline; } /* Override default Twitter Bootstrap styles for tr.error */ .table tbody tr.error > td { background-color: transparent; } /* override default Bootstrap behaviour */ diff --git a/bitbake/lib/toaster/toastergui/static/js/projectapp.js b/bitbake/lib/toaster/toastergui/static/js/projectapp.js index e674d8f..b347451 100644 --- a/bitbake/lib/toaster/toastergui/static/js/projectapp.js +++ b/bitbake/lib/toaster/toastergui/static/js/projectapp.js @@ -92,6 +92,24 @@ projectApp.config(function($interpolateProvider) { $interpolateProvider.endSymbol("]}"); }); + +// add time interval to HH:mm filter +projectApp.filter('timediff', function() { + return function(input) { + function pad(j) { + if (parseInt(j) < 10) {return "0" + j} + return j; + } + seconds = parseInt(input); + minutes = Math.floor(seconds / 60); + seconds = seconds - minutes * 60; + hours = Math.floor(seconds / 3600); + seconds = seconds - hours * 3600; + return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds); + } +}); + + // main controller for the project page projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $location, $cookies, $q, $sce) { @@ -150,7 +168,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc // identify canceled builds here, so we can display them. _diffArrays(oldbuilds, $scope.builds, - function (e,f) { return e.status == f.status && e.id == f.id }, // compare + function (e,f) { return e.id == f.id }, // compare undefined, // added function (e) { // deleted if (e.status == "deleted") return; @@ -421,7 +439,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc }).then( function () { $scope.toggle(elementid); if (data['projectVersion'] != undefined) { - alertText += "<b>" + $scope.release.name + "</b>"; + alertText += "<b>" + $scope.project.release.name + "</b>"; } $scope.displayAlert(alertZone, alertText, "alert-info"); }); @@ -506,7 +524,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc // init code // $scope.init = function() { - $scope.pollHandle = $interval(function () { $scope._makeXHRCall({method: "POST", url: $scope.urls.xhr_edit, data: undefined});}, 4000, 0); + $scope.pollHandle = $interval(function () { $scope._makeXHRCall({method: "GET", url: $scope.urls.xhr_edit, data: undefined});}, 4000, 0); } $scope.init(); diff --git a/bitbake/lib/toaster/toastergui/templates/base.html b/bitbake/lib/toaster/toastergui/templates/base.html index d414bfb..f377081 100644 --- a/bitbake/lib/toaster/toastergui/templates/base.html +++ b/bitbake/lib/toaster/toastergui/templates/base.html @@ -1,6 +1,6 @@ <!DOCTYPE html> {% load static %} -<html> +<html lang="en"> <head> <title>{% if objectname %} {{objectname|title}} - {% endif %}Toaster</title> <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}" type="text/css"> @@ -9,6 +9,7 @@ <link rel="stylesheet" href="{% static 'css/prettify.css' %}" type='text/css'> <link rel="stylesheet" href="{% static 'css/default.css' %}" type='text/css'> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> +<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <script src="{% static 'js/jquery-2.0.3.min.js' %}"> </script> <script src="{% static 'js/jquery.cookie.js' %}"> diff --git a/bitbake/lib/toaster/toastergui/templates/basetable_bottom.html b/bitbake/lib/toaster/toastergui/templates/basetable_bottom.html index 091e11a..d48ad92 100644 --- a/bitbake/lib/toaster/toastergui/templates/basetable_bottom.html +++ b/bitbake/lib/toaster/toastergui/templates/basetable_bottom.html @@ -58,9 +58,12 @@ } // load cookie for number of entries to be displayed on page - pagesize = $.cookie('count'); - if (!pagesize) - pagesize = 10; + if ({{request.GET.count}} != "") { + pagesize = {{request.GET.count}}; + } else { + pagesize = $.cookie('_count'); + } + $('.pagesize option').prop('selected', false) .filter('[value="' + pagesize + '"]') .attr('selected', true); @@ -81,9 +84,9 @@ $('.progress, .lead span').tooltip({container:'table', placement:'top'}); $(".pagesize").change(function () { - reload_params({"count":$(this).val()}); // save cookie with pagesize - $.cookie("count", $(this).val(), { path : $(location).attr('pathname') }); + $.cookie("_count", $(this).val(), { path : $(location).attr('pathname') }); + reload_params({"count":$(this).val()}); }); }); </script> diff --git a/bitbake/lib/toaster/toastergui/templates/layers.html b/bitbake/lib/toaster/toastergui/templates/layers.html index 51f4dd9..db34fe4 100644 --- a/bitbake/lib/toaster/toastergui/templates/layers.html +++ b/bitbake/lib/toaster/toastergui/templates/layers.html @@ -9,67 +9,66 @@ {% block projectinfomain %} <div class="page-header"> <h1> - {% if request.GET.search and objects.paginator.count > 0 %} + {% if request.GET.filter and objects.paginator.count > 0 or request.GET.search and objects.paginator.count > 0 %} {{objects.paginator.count}} layer{{objects.paginator.count|pluralize}} found - {%elif request.GET.search and objects.paginator.count == 0%} - No layer found + {% elif request.GET.filter and objects.paginator.count == 0 or request.GET.search and objects.paginator.count == 0 %} + No layers found {%else%} All layers {%endif%} - <i class="icon-question-sign get-help heading-help" title="This page lists all the layers compatible with " + {{project.release.name}} + " that Toaster knows about."></i> + <i class="icon-question-sign get-help heading-help" title="This page lists all the layers compatible with {{project.release.name}} that Toaster knows about."></i> </h1> </div> <div id="zone1alerts"> - </div> - <div id="layer-added" class="alert alert-info lead" style="display:none;"></div> - - {% include "basetable_top_layers.html" %} - {% for lv in objects %} + {% for o in objects %} <tr class="data"> - <td class="layer"><a href="{% url 'layerdetails' lv.id %}">{{lv.layer.name}}</a></td> - <td class="description">{{lv.layer.summary}}</td> - <td class="source"><a href="{% url 'layerdetails' lv.pk %}">{{lv.layer_source.name}}</a></td> - <td class="git-repo"><a href="{% url 'layerdetails' lv.pk %}"><code>{{lv.layer.vcs_url}}</code></a> - {% if lv.get_vcs_link_url %} - <a target="_blank" href="{{ lv.get_vcs_link_url }}"><i class="icon-share get-info"></i></a> + <td class="layer"><a href="{% url 'layerdetails' o.id %}">{{o.layer.name}}</a></td> + <td class="description">{% if o.layer.summary %}{{o.layer.summary}}{%endif%}</td> + <td class="source"><a href="{% url 'layerdetails' o.pk %}">{{o.layer_source.name}}</a></td> + <td class="git-repo"><a href="{% url 'layerdetails' o.pk %}"><code>{{o.layer.vcs_url}}</code></a> + {% if o.get_vcs_link_url %} + <a target="_blank" href="{{ o.get_vcs_link_url }}"><i class="icon-share get-info"></i></a> {% endif %} </td> - <td class="git-subdir" style="display: table-cell;"><a href="{% url 'layerdetails' lv.pk %}"><code>{{lv.dirpath}}</code></a> - {% if lv.dirpath and lv.get_vcs_dirpath_link_url %} - <a target="_blank" href="{{ lv.get_vcs_dirpath_link_url }}"><i class="icon-share get-info"></i></a> + <td class="git-subdir" style="display: table-cell;"><a href="{% url 'layerdetails' o.pk %}"><code>{{o.dirpath}}</code></a> + {% if o.dirpath and o.get_vcs_dirpath_link_url %} + <a target="_blank" href="{{ o.get_vcs_dirpath_link_url }}"><i class="icon-share get-info"></i></a> {% endif %} </td> - <td class="branch">{% if lv.branch %}{{lv.branch}}{% else %}{{lv.up_branch.name}}{% endif %}</td> + <td class="branch">{% if o.branch %}{{o.branch}}{% else %}{{o.up_branch.name}}{% endif %}</td> <td class="dependencies"> - {% with lvds=lv.dependencies.all%} - {% if lvds.count %} + {% with ods=o.dependencies.all%} + {% if ods.count %} <a class="btn" - title="<a href='{% url "layerdetails" lv.pk %}'>{{lv.layer.name}}</a> dependencies" + title="<a href='{% url "layerdetails" o.pk %}'>{{o.layer.name}}</a> dependencies" data-content="<ul class='unstyled'> - {% for i in lvds%} + {% for i in ods%} <li><a href='{% url "layerdetails" i.depends_on.pk %}'>{{i.depends_on.layer.name}}</a></li> {% endfor %} </ul>"> - {{lvds.count}} + {{ods.count}} </a> {% endif %} {% endwith %} </td> - <td class="add-del-layers" value="{{lv.pk}}"> - <button id="layer-del-{{lv.pk}}" class="btn btn-danger btn-block remove-layer" style="display:none;" onclick="layerDel({{lv.pk}}, '{{lv.layer.name}}', '{%url 'layerdetails' lv.pk%}')"> + {% if project %} + <td class="add-del-layers" value="{{o.pk}}"> + <div id="layer-tooltip-{{o.pk}}" style="display: none; font-size: 11px; line-height: 1.3;" class="tooltip-inner">layer was modified</div> + <button id="layer-del-{{o.pk}}" class="btn btn-danger btn-block remove-layer layerbtn" style="display:none;" onclick="layerDel({{o.pk}}, '{{o.layer.name}}', '{%url 'layerdetails' o.pk%}')" > <i class="icon-trash"></i> Delete layer </button> - <button id="layer-add-{{lv.pk}}" class="btn btn-block" style="display:none;" onclick="layerAdd({{lv.pk}}, '{{lv.layer.name}}', '{%url 'layerdetails' lv.pk%}')" > + <button id="layer-add-{{o.pk}}" class="btn btn-block layerbtn" style="display:none;" onclick="layerAdd({{o.pk}}, '{{o.layer.name}}', '{%url 'layerdetails' o.pk%}')" title="layer added"> <i class="icon-plus"></i> Add layer </button> </td> + {% endif %} </tr> {% endfor %} {% include "basetable_bottom.html" %} @@ -78,7 +77,7 @@ <!-- 'Layer dependencies modal' --> <div id="dependencies_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true"> - <form id="dependencies_modal_form"> + <form id="dependencies_modal_form" style="margin: 0px"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button> <h3><span class="layer-name"></span> dependencies</h3> @@ -95,8 +94,11 @@ </form> </div> +{% if project %} <script> +var tooltipUpdateText; + function _makeXHREditCall(data, onsuccess, onfail) { $.ajax( { type: "POST", @@ -120,13 +122,14 @@ function _makeXHREditCall(data, onsuccess, onfail) { function layerDel(layerId, layerName, layerURL) { + tooltipUpdateText = "1 layer deleted"; _makeXHREditCall({ 'layerDel': layerId }, function () { - show_alert("<strong>1</strong> layer deleted from <a href=\"{% url 'project' project.id%}\">{{project.name}}</a>: <a href=\""+layerURL+"\">" + layerName +"</a>"); + show_alert("You have deleted <strong>1</strong> layer from <a href=\"{% url 'project' project.id%}\">{{project.name}}</a>: <a href=\""+layerURL+"\">" + layerName +"</a>"); }); } function show_alert(text, cls) { - $("#zone1alerts").html("<div class=\"alert alert-info\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\">×</button>" + text + "</div>"); + $("#zone1alerts").html("<div class=\"alert alert-info lead\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\">×</button>" + text + "</div>"); } function show_dependencies_modal(layerId, layerName, layerURL, dependencies) { @@ -142,25 +145,35 @@ function show_dependencies_modal(layerId, layerName, layerURL, dependencies) { } $('#dependencies_list').html(deplistHtml); + var selected = [layerId]; + var layer_link_list = "<a href='"+layerURL+"'>"+layerName+"</a>"; + $("#dependencies_modal_form").submit(function (e) { e.preventDefault(); - var selected = [layerId]; $("input[name='dependencies']:checked").map(function () { selected.push(parseInt($(this).val()))}); + if (selected.length > 1) { + tooltipUpdateText = "" + selected.length + " layers added"; + } else { + tooltipUpdateText = "1 layer added"; + } - _makeXHREditCall({ 'layerAdd': selected.join(",") }, function () { - var layer_link_list = "<a href='"+layerURL+"'>"+layerName+"</a>"; - for (var i = 0; i < selected.length; i++) { - for (var j = 0; j < dependencies.length; i++) { - if (dependencies[j].id == selected[i]) { - layer_link_list+= ", <a href='"+dependencies[j].layerdetailurl+"'>"+dependencies[j].name+"</a>" - break; - } + for (var i = 0; i < selected.length; i++) { + for (var j = 0; j < dependencies.length; j++) { + if (dependencies[j].id == selected[i]) { + layer_link_list+= ", <a href='"+dependencies[j].layerdetailurl+"'>"+dependencies[j].name+"</a>" + break; } } + } + + $('#dependencies_modal').modal('hide'); - $('#dependencies_modal').modal('hide'); - show_alert("<strong>"+selected.length+"</strong> layers added to <a href=\"{% url 'project' project.id%}\">{{project.name}}</a>:" + layer_link_list); + {% if project %} + _makeXHREditCall({ 'layerAdd': selected.join(",") }, function () { + show_alert("You have added <strong>"+selected.length+"</strong> layers to <a href=\"{% url 'project' project.id%}\">{{project.name}}</a>: " + layer_link_list); }); + {% endif %} + }); $('#dependencies_modal').modal('show'); } @@ -178,8 +191,9 @@ function layerAdd(layerId, layerName, layerURL) { show_dependencies_modal(layerId, layerName, layerURL, _data.list); } else { + tooltipUpdateText = "1 layer added"; _makeXHREditCall({ 'layerAdd': layerId }, function () { - show_alert("<strong>1</strong> layer added to <a href=\"{% url 'project' project.id%}\">{{project.name}}</a>: <a href=\""+layerURL+"\">" + layerName +"</a>"); + show_alert("You have added <strong>1</strong> layer to <a href=\"{% url 'project' project.id%}\">{{project.name}}</a>: <a href=\""+layerURL+"\">" + layerName +"</a>"); }); } } @@ -188,15 +202,31 @@ function layerAdd(layerId, layerName, layerURL) { } function button_set(id, state) { + var tohide, toshow; if (state == "add") { - $("#layer-add-" + id).show(); - $("#layer-del-" + id).hide(); + tohide = "#layer-del-"; + toshow = "#layer-add-"; } else if (state == "del") { - $("#layer-add-" + id).hide(); - $("#layer-del-" + id).show(); + tohide = "#layer-add-"; + toshow = "#layer-del-"; + } + + + var previouslyvisible = $(tohide + id).is(":visible"); + if (previouslyvisible) { + $(tohide + id).fadeOut( function() { + $("#layer-tooltip-" + id).text(tooltipUpdateText); + $("#layer-tooltip-" + id).fadeIn().delay(2000).fadeOut(function(){ + $(toshow + id).delay(300).fadeIn(); + }); + }); + } else { + $(tohide + id).hide(); + $("#layer-tooltip-" + id).hide(); + $(toshow + id).show(); } }; @@ -214,9 +244,11 @@ function updateButtons(projectLayers) { } $(document).ready(function (){ + $('.layerbtn').tooltip({ trigger: 'manual' }); updateButtons({{projectlayerset}}); }); </script> +{%endif%} {% endblock %} diff --git a/bitbake/lib/toaster/toastergui/templates/mrb_section.html b/bitbake/lib/toaster/toastergui/templates/mrb_section.html index 3d17ac6..586c47b 100644 --- a/bitbake/lib/toaster/toastergui/templates/mrb_section.html +++ b/bitbake/lib/toaster/toastergui/templates/mrb_section.html @@ -44,7 +44,7 @@ Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a> </span> {% if build.project %} - <a class="btn {%if build.outcome == build.SUCCEEDED%}btn-success{%elif build.outcome == build.FAILED%}btn-danger{%else%}btn-info{%endif%} pull-right" onclick="scheduleBuild({%url 'xhr_projectbuild' build.project.id as myurl %}{{myurl|json}}, {{build.project.name|json}}, {{build.get_sorted_target_list|mapselect:'target'|json}})">Run again</a> + <a class="btn {%if build.outcome == build.SUCCEEDED%}btn-success{%elif build.outcome == build.FAILED%}btn-danger{%else%}btn-info{%endif%} pull-right" onclick="scheduleBuild({% url 'xhr_projectbuild' build.project.id as bpi%}{{bpi|json}}, {{build.project.name|json}}, {{build.get_sorted_target_list|mapselect:'target'|json}})">Run again</a> {% endif %} </div> {%endif%} @@ -81,19 +81,18 @@ function _makeXHRBuildCall(url, data, onsuccess, onfail) { alert("Call failed"); console.log(_data); if (onfail) onfail(data); - } - }); + } }); } function scheduleBuild(url, projectName, buildlist) { console.log("scheduleBuild"); -// _makeXHRBuildCall(url , {targets: buildlist.join(" ")}, function (_data) { + _makeXHRBuildCall(url, {targets: buildlist.join(" ")}, function (_data) { - $('#latest-builds').prepend('<div class="alert alert-info" style="padding-top:0px">' + '<span class="label label-info" style="font-weight: normal; margin-bottom: 5px; margin-left:-15px; padding-top:5px;">'+projectName+'</span><div class="row-fluid">' + + $('#latest-builds').prepend('<div class="alert alert-info" style="padding-top:0px">' + '<span class="label label-info" style="font-weight: normal; margin-bottom: 5px; margin-left:-15px; padding-top:5px;">'+projectName+'</span><div class="row-fluid">' + '<div class="span4 lead">' + buildlist.join(" ") + '</div><div class="span4 lead pull-right">Build queued. Your build will start shortly.</div></div></div>'); -// } + }); } </script> diff --git a/bitbake/lib/toaster/toastergui/templates/project.html b/bitbake/lib/toaster/toastergui/templates/project.html index 9399b31..6a81283 100644 --- a/bitbake/lib/toaster/toastergui/templates/project.html +++ b/bitbake/lib/toaster/toastergui/templates/project.html @@ -76,6 +76,14 @@ vim: expandtab tabstop=2 </div> </script> + <script type="text/ng-template" id="target_display"> + <div ng-switch on="t.task.length"> + <div ng-switch-when="0">{[t.target]}</div> + <div ng-switch-default>{[t.target]}:{[t.task]}</div> + </div> + </script> + + <!-- build form --> <div class="well"> <form class="build-form" ng-submit="targetNamedBuild()"> @@ -99,11 +107,11 @@ vim: expandtab tabstop=2 <h2 class="air" ng-if="builds.length">Latest builds</h2> - <div class="alert" ng-repeat="b in builds" ng-class="{'queued':'alert-info', 'deleted':'alert-info', 'in progress': 'alert-info', 'In Progress':'alert-info', 'Succeeded':'alert-success', 'failed':'alert-error', 'Failed':'alert-error'}[b.status]"> + <div class="alert" ng-repeat="b in builds" ng-class="{'queued':'alert-info', 'deleted':'alert-info', 'in progress': 'alert-info', 'failed':'alert-error', 'completed':{'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.build[0].status]}[b.status]" > <div class="row-fluid"> <switch ng-switch="b.status"> <case ng-switch-when="failed"> - <div class="lead span3"> <span ng-repeat="t in b.targets">{[t.target]} </span> </div> + <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> <div class="row-fluid"> <div class="air well" ng-repeat="e in b.errors"> {[e.type]}: <pre>{[e.msg]}</pre> @@ -111,53 +119,51 @@ vim: expandtab tabstop=2 </div> </case> <case ng-switch-when="queued"> - <div class="lead span5"> <span ng-repeat="t in b.targets">{[t.target]} </span> </div> + <div class="lead span5"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> <div class="span4 lead" >Build queued <i title="This build will start as soon as a build server is available" class="icon-question-sign get-help get-help-blue heading-help" data-toggle="tooltip"></i> </div> <button class="btn pull-right btn-info" ng-click="buildCancel(b.id)">Cancel</button> </case> <case ng-switch-when="created"> - <div class="lead span3"> <span ng-repeat="t in b.targets">{[t.target]} </span> </div> + <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> <div class="span6" > <span class="lead">Creating build</span> </div> <button class="btn pull-right btn-info" ng-click="buildCancel(b.id)">Cancel</button> </case> <case ng-switch-when="deleted"> - <div class="lead span3"> <span ng-repeat="t in b.targets">{[t.target]} </span> </div> + <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> <div class="span6" id="{[b.id]}-deleted" > <span class="lead">Build deleted</span> </div> <button class="btn pull-right btn-info" ng-click="builds.splice(builds.indexOf(b), 1)">Close</button> </case> <case ng-switch-when="in progress"> - <div class="lead span3"> <span ng-repeat="t in b.targets">{[t.target]} </span> </div> - <div class="span4" > - </div> - <div class="lead pull-right">Build starting shortly</div> - </case> - <case ng-switch-when="In Progress"> + <div class="lead span3"> <span ng-repeat="t in b.targets" ng-include src="'target_display'"></span> </div> <div class="span4" > - <div class="progress" style="margin-top:5px;" data-toggle="tooltip" tooltip="{[b.completeper]}% of tasks complete"> - <div style="width: {[b.completeper]}%;" class="bar"></div> + <div class="progress" style="margin-top:5px;" data-toggle="tooltip" tooltip="{[b.build[0].completeper]}% of tasks complete"> + <div style="width: {[b.build[0].completeper]}%;" class="bar"></div> </div> </div> - <div class="lead pull-right">ETA: at {[b.eta]}</div> + <div class="lead pull-right">ETA: at {[b.build[0].eta|date:"shortDate"]}</div> </case> - <case ng-switch-default=""> - <div class="lead span3"><a href="{[b.build_page_url]}"><span ng-repeat="t in b.targets">{[t.target]} </span> </div></a> + <case ng-switch-when="completed"> + <div class="lead span3"><a href="{[b.build[0].build_page_url]}"><span ng-repeat="t in b.targets" ng-include src="'target_display'"></span></a></div> <div class="span2 lead"> - {[b.completed_on|date:'dd/MM/yy HH:mm']} + {[b.build[0].completed_on|date:'dd/MM/yy HH:mm']} </div> - <div class="span2"><span>{[b.errors.len]}</span></div> - <div class="span2"><span>{[b.warnings.len]}</span></div> - <div> <span class="lead">Build time: {[b.build_time|date:"HH:mm"]}</span> - <button class="btn pull-right" ng-class="{'Succeeded': 'btn-success', 'Failed': 'btn-danger'}[b.status]" + <div class="span2"><span><a href="{[b.build[0].build_page_url]}#errors" class="lead error" ng-if="b.build[0].errors">{[b.build[0].errors]}</a></span></div> + <div class="span2"><span><a href="{[b.build[0].build_page_url]}#warnings" class="lead warning" ng-if="b.build[0].warnings">{[b.build[0].warnings]}</a></span></div> + <div> <span class="lead">Build time: {[b.build[0].build_time|timediff]}</span> + <button class="btn pull-right" ng-class="{'Succeeded': 'btn-success', 'Failed': 'btn-danger'}[b.build[0].status]" ng-click="targetExistingBuild(b.targets)">Run again</button> </div> </case> + <case ng-switch-default=""> + <div>FIXME!</div> + </case> </switch> <div class="lead pull-right"> </div> diff --git a/bitbake/lib/toaster/toastergui/templates/targets.html b/bitbake/lib/toaster/toastergui/templates/targets.html index 3afdf0a..32a644a 100644 --- a/bitbake/lib/toaster/toastergui/templates/targets.html +++ b/bitbake/lib/toaster/toastergui/templates/targets.html @@ -9,55 +9,74 @@ {% block projectinfomain %} <div class="page-header"> <h1> - All targets - <i class="icon-question-sign get-help heading-help" title="This page lists all the targets compatible with Yocto Project 1.7 'Dxxxx' that Toaster knows about. They include community-created targets suitable for use on top of OpenEmbedded Core and any targets you have imported"></i> + {% if request.GET.filter and objects.paginator.count > 0 or request.GET.search and objects.paginator.count > 0 %} + {{objects.paginator.count}} target{{objects.paginator.count|pluralize}} found + {% elif request.GET.filter and objects.paginator.count == 0 or request.GET.search and objects.paginator.count == 0 %} + No targets found + {%else%} + All targets + {%endif%} + <i class="icon-question-sign get-help heading-help" title="This page lists all the targets compatible with " + {{project.release.name}} + " that Toaster knows about."></i> </h1> </div> - <!--div class="alert"> - <div class="input-append" style="margin-bottom:0px;"> - <input class="input-xxlarge" type="text" placeholder="Search targets" value="browser" /> - <a class="add-on btn"> - <i class="icon-remove"></i> - </a> - <button class="btn" type="button">Search</button> - <a class="btn btn-link" href="#">Show all targets</a> - </div> - </div--> - <div id="target-added" class="alert alert-info lead" style="display:none;"></div> - <div id="target-removed" class="alert alert-info lead" style="display:none;"> - <button type="button" class="close" data-dismiss="alert">×</button> - <strong>1</strong> target deleted from <a href="project-with-targets.html">your project</a>: <a href="#">meta-aarch64</a> - </div> + <div id="zone1alerts"> + + </div> + +{% if objects.paginator.count == 0 %} + <div class="row-fluid"> + <div class="alert"> + <form class="no-results input-append" id="searchform"> + <input id="search" name="search" class="input-xxlarge" type="text" value="{{request.GET.search}}"/>{% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" class="add-on btn" tabindex="-1"><i class="icon-remove"></i></a>{% endif %} + <button class="btn" type="submit" value="Search">Search</button> + <button class="btn btn-link" onclick="javascript:$('#search').val('');searchform.submit()">Show all targets</button> + </form> + </div> + </div> + +{% else %} {% include "basetable_top.html" %} {% for o in objects %} <tr class="data"> - <td class="target"> - {{o.name}} ({{o.id}}, {{o.up_id}}) - <a target="_blank" href="{{o.get_layersource_view_url}}"><i class="icon-share get-info"></i></a> - </td> - <td class="version">{{o.version}}</td> - <td class="description">{{o.description}}</td> - <td class="recipe-file"> - <code>{{o.file_path}}</code> - <a href="{{o.get_vcs_link_url}}" target="_blank"><i class="icon-share get-info"></i></a> - </td> - <td class="target-section">{{o.section}}</td> - <td class="license">{{o.license}}</td> - <td class="layer"><a href="#">{{o.layer_version.layer.name}}</a></td> - <td class="source">{{o.layer_source.name}}</td> - <td class="branch">{{o.layer_version.commit}}</td> - <td class="build"> - <a id="build-target" href="project-with-targets.html?target=3g-router-image" class="btn btn-block" style="display:none;"> - Build target - </a> - <a id="add-layer" href="#" class="btn btn-block nopop" title="1 layer added"> - <i class="icon-plus"></i> - Add layer - <i class="icon-question-sign get-help" title="To build this target, you must first add the meta-embeddedgeeks layer to your project"></i> - </a> - </td> + <td class="target"> + {{o.name}} + <a target="_blank" href="{{o.get_layersource_view_url}}"><i class="icon-share get-info"></i></a> + </td> + <td class="version">{{o.version}}</td> + <td class="description">{% if o.description %}{{o.description}}{% else %}{{o.summary}}{%endif%}</td> + <td class="recipe-file"> + <code>{{o.file_path}}</code> + <a href="{{o.get_vcs_link_url}}{{o.file_path}}" target="_blank"><i class="icon-share get-info"></i></a> + </td> + <td class="target-section">{{o.section}}</td> + <td class="license">{{o.license}}</td> + <td class="layer"><a href="{% url 'layerdetails' o.layer_version.id%}">{{o.layer_version.layer.name}}</a></td> + <td class="source">{{o.layer_source.name}}</td> + <td class="branch"> + {% if o.layer_version.up_branch %} + {{o.layer_version.up_branch.name}} + {% else %} + <a class="btn" + data-content="<ul class='unstyled'> + <li>{{o.layer_version.commit}}</li> + </ul>"> + {{o.layer_version.commit|truncatechars:13}} + </a> + {% endif %} + </td> + <td class="add-layer" value="{{o.pk}}" layerversion_id="{{o.layer_version.pk}}"> + <div id="layer-tooltip-{{o.pk}}" style="display: none; font-size: 11px; line-height: 1.3;" class="tooltip-inner">layer was modified</div> + <a href="{% url 'project' project.id %}#/targetbuild={{o.name}}" id="target-build-{{o.pk}}" class="btn btn-block remove-layer" style="display:none;" > + Build target + </a> + <a id="layer-add-{{o.pk}}" class="btn btn-block" style="display:none;" href="javascript:layerAdd({{o.layer_version.pk}}, '{{o.layer_version.layer.name}}', '{%url 'layerdetails' o.layer_version.pk%}', {{o.pk}})" > + <i class="icon-plus"></i> + Add layer + <i title="" class="icon-question-sign get-help" data-original-title="To build this target, you must first add the {{o.layer_version.layer.name}} layer to your project"></i> + </a> + </td> </tr> {% endfor %} {% include "basetable_bottom.html" %} @@ -65,122 +84,177 @@ <!-- Modals --> <!-- 'Layer dependencies modal' --> - <div id="dependencies-message" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true"> + <div id="dependencies_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true"> + <form id="dependencies_modal_form"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button> - <h3>meta-acer dependencies</h3> + <h3><span class="layer-name"></span> dependencies</h3> </div> <div class="modal-body"> - <p><strong>meta-acer</strong> depends on some targets that are not added to your project. Select the ones you want to add:</p> - <ul class="unstyled"> - <li> - <label class="checkbox"> - <input type="checkbox" checked="checked"> - meta-android - </label> - </li> - <li> - <label class="checkbox"> - <input type="checkbox" checked="checked"> - meta-oe - </label> - </li> + <p><strong class="layer-name"></strong> depends on some layers that are not added to your project. Select the ones you want to add:</p> + <ul class="unstyled" id="dependencies_list"> </ul> </div> <div class="modal-footer"> - <button id="add-target-dependencies" type="submit" class="btn btn-primary" data-dismiss="modal" >Add targets</button> - <button class="btn" data-dismiss="modal">Cancel</button> + <button class="btn btn-primary" type="submit">Add layers</button> + <button class="btn" type="reset" data-dismiss="modal">Cancel</button> </div> + </form> </div> - <script src="assets/js/jquery-1.9.1.min.js" type='text/javascript'></script> - <script src="assets/js/jquery.tablesorter.min.js" type='text/javascript'></script> - <script src="assets/js/jquery-ui-1.10.3.custom.min.js"></script> - <script src="assets/js/bootstrap.min.js" type='text/javascript'></script> - <script src="assets/js/prettify.js" type='text/javascript'></script> - <script src="assets/js/jit.js" type='text/javascript'></script> - <script src="assets/js/main.js" type='text/javascript'></script> - - <script> - $(document).ready(function() { - - //show or hide selected columns on load - $("input:checkbox").each(function(){ - var selectedType = $(this).val(); - if($(this).is(":checked")){ - $("."+selectedType).show(); +{% endif %} + +{% if project %} +<script> + +var tooltipUpdateText; + +function _makeXHREditCall(data, onsuccess, onfail) { + $.ajax( { + type: "POST", + url: "{% url 'xhr_projectedit' project.id %}", + data: data, + headers: { 'X-CSRFToken' : $.cookie('csrftoken')}, + success: function (_data) { + if (_data.error != "ok") { + alert(_data.error); + } else { + updateButtons(_data.layers.map(function (e) {return e.id})); + if (onsuccess != undefined) onsuccess(_data); } - else{ - $("."+selectedType).hide(); + }, + error: function (_data) { + alert("Call failed"); + console.log(_data); + } + }); +} + +function show_alert(text, cls) { + $("#zone1alerts").html("<div class=\"alert alert-info lead\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\">×</button>" + text + "</div>"); +} + + +function show_dependencies_modal(layerId, layerName, layerURL, dependencies) { + // update layer name + $('.layer-name').text(layerName); + var deplistHtml = ""; + for (var i = 0; i < dependencies.length; i++) { + deplistHtml += "<li><label class=\"checkbox\"><input name=\"dependencies\" value=\"" + deplistHtml += dependencies[i].id; + deplistHtml +="\" type=\"checkbox\" checked=\"checked\"/>"; + deplistHtml += dependencies[i].name; + deplistHtml += "</label></li>"; + } + $('#dependencies_list').html(deplistHtml); + + var selected = [layerId]; + var layer_link_list = undefined; + + $("#dependencies_modal_form").submit(function (e) { + e.preventDefault(); + $("input[name='dependencies']:checked").map(function () { selected.push(parseInt($(this).val()))}); + layer_link_list = "<a href='"+layerURL+"'>"+layerName+"</a>"; + if (selected.length > 1) { + tooltipUpdateText = "" + selected.length + " layers added"; + } else { + tooltipUpdateText = "1 layer added"; + } + + for (var i = 0; i < selected.length; i++) { + for (var j = 0; j < dependencies.length; j++) { + if (dependencies[j].id == selected[i]) { + layer_link_list+= ", <a href='"+dependencies[j].layerdetailurl+"'>"+dependencies[j].name+"</a>" + break; + } } + } + + $('#dependencies_modal').modal('hide'); + + {% if project %} + _makeXHREditCall({ 'layerAdd': selected.join(",") }, function onXHRSuccess() { + show_alert("You have added <strong>"+selected.length+"</strong> layer(s) to <a href=\"{% url 'project' project.id%}\">{{project.name}}</a>:" + layer_link_list); }); + {% endif %} - // enable add target button - $('#add-target-with-deps').removeAttr('disabled'); + }); + $('#dependencies_modal').modal('show'); +} - //edit columns functionality (show / hide table columns) - $("input:checkbox").change(); - $("input:checkbox").change(function(){ - var selectedType = $(this).val(); - if($(this).is(":checked")){ - $("."+selectedType).show(); - } - else{ - $("."+selectedType).hide(); - } - }); - //turn edit columns dropdown into a multi-select menu - $('.dropdown-menu input, .dropdown-menu label').click(function(e) { - e.stopPropagation(); - }); +var pressedButton = undefined; - //show tooltip with applied filter - $('#filtered').tooltip({container:'table', placement:'bottom', delay:{hide:1500}, html:true}); +function layerAdd(layerId, layerName, layerURL, pressedButtonId) { + pressedButton = pressedButtonId; + $.ajax({ + url: '{% url "xhr_datatypeahead" %}', + data: {'type': 'layerdeps','value':layerId}, + success: function(_data) { + if (_data.error != "ok") { + alert(_data.error); + } else { + if (_data.list.length > 0) { + show_dependencies_modal(layerId, layerName, layerURL, _data.list); + } + else { + tooltipUpdateText = "1 layer added"; + _makeXHREditCall({ 'layerAdd': layerId }, function () { + show_alert("You have added <strong>1</strong> layer to <a href=\"{% url 'project' project.id%}\">{{project.name}}</a>: <a href=\""+layerURL+"\">" + layerName +"</a>"); + }); + } + } + } + }) +} - $('#filtered').click(function() { - $(this).tooltip('hide'); - }); +function buttonSet(id, state) { + var tohide, toshow; + if (state == "add") + { + toshow = "#layer-add-"; + tohide = "#target-build-"; + } + else if (state == "build") + { + tohide = "#layer-add-"; + toshow = "#target-build-"; + } - //show target added tooltip - $("#remove-target, #add-target, #add-target-with-deps2").tooltip({ trigger: 'manual' }); - - // add target without dependencies - $("#add-target").click(function(){ - $('#target-removed').hide(); - $('#target-added').html('<button type="button" class="close" data-dismiss="alert">×</button><strong>1</strong> target added to <a href="project-with-targets.html">your project</a>: <a href="#">meta-aarch64</a>').fadeIn(); - $('#add-target').tooltip('show'); - $("#add-target").hide(); - $(".add-targets .tooltip").delay(2000).fadeOut(function(){ - $("#remove-target").delay(300).fadeIn(); + var previouslyvisible = $(tohide + id).is(":visible"); + if (previouslyvisible && id == pressedButton) { + $(tohide + id).fadeOut( function() { + $("#layer-tooltip-" + id).text(tooltipUpdateText); + $("#layer-tooltip-" + id).fadeIn().delay(2000).fadeOut(function(){ + $(toshow + id).delay(300).fadeIn(); }); }); + } else { + $(tohide + id).hide(); + $("#layer-tooltip-" + id).hide(); + $(toshow + id).show(); + } +}; - // add target with dependencies - $(document).on("click", "#add-target-dependencies", function() { - $('#target-removed').hide(); - $('#target-added').html('<button type="button" class="close" data-dismiss="alert">×</button><strong>3</strong> targets added to <a href="project-with-targets.html">your project</a>: <a href="#">meta-acer</a> and its dependencies <a href="#">meta-android</a> and <a href="#">meta-oe</a>').delay(400).fadeIn(function(){ - $('#add-target-with-deps').tooltip('show'); - $("#add-target-with-deps, #add-target-with-deps").hide(); - $(".add-targets .tooltip").delay(2000).fadeOut(function(){ - $("#remove-target-with-deps").delay(300).fadeIn(); - }); - }); - }); - // delete target - $("#remove-target").click(function(){ - $('#target-added').hide(); - $('#target-removed').show(); - $('#remove-target').tooltip('show'); - $("#remove-target").hide(); - $(".add-targets .tooltip").delay(2000).fadeOut(function(){ - $("#add-target").delay(300).fadeIn(); - }); - }); +function updateButtons(projectLayers) { + var displayedLayers = []; + $(".add-layer").map(function () { displayedLayers.push( { "l": parseInt($(this).attr('layerversion_id')), "i": parseInt($(this).attr('value'))})}); + for (var i=0; i < displayedLayers.length; i++) { + if (projectLayers.indexOf(displayedLayers[i].l) > -1) { + buttonSet(displayedLayers[i].i, "build"); + } + else { + buttonSet(displayedLayers[i].i, "add"); + } + } +} - }); +$(document).ready(function (){ + updateButtons({{projectlayerset}}); +}); </script> +{%endif%} {% endblock %} diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index e568ee7..fb80750 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py @@ -123,7 +123,7 @@ def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs): params[i] = g[i] for i in mandatory_parameters: if not i in params: - params[i] = mandatory_parameters[i] + params[i] = urllib.unquote(str(mandatory_parameters[i])) return redirect(url + "?%s" % urllib.urlencode(params), *args, **kwargs) @@ -202,6 +202,7 @@ def _get_search_results(search_term, queryset, model): search_objects.append(reduce(operator.or_, q_map)) search_object = reduce(operator.and_, search_objects) + print "search objects", search_object queryset = queryset.filter(search_object) return queryset @@ -1914,8 +1915,7 @@ if toastermain.settings.MANAGED: login(request, user) # save the project - prj = Project.objects.create_project(name = request.POST['projectname'], - release = Release.objects.get(pk = request.POST['projectversion'])) + prj = Project.objects.create_project(name = request.POST['projectname'], release = Release.objects.get(pk = request.POST['projectversion'])) prj.user_id = request.user.pk prj.save() return redirect(reverse(project, args = (prj.pk,)) + "#/newproject") @@ -1933,36 +1933,21 @@ if toastermain.settings.MANAGED: def _project_recent_build_list(prj): - # build requests not yet started - return (map(lambda x: { - "id": x.pk, - "targets" : map(lambda y: {"target": y.target }, x.brtarget_set.all()), - "status": x.get_state_display(), - }, prj.buildrequest_set.filter(state__lt = BuildRequest.REQ_INPROGRESS).order_by("-pk")) + - # build requests started, but with no build yet - map(lambda x: { + return map(lambda x: { "id": x.pk, - "targets" : map(lambda y: {"target": y.target }, x.brtarget_set.all()), - "status": x.get_state_display(), - }, prj.buildrequest_set.filter(state = BuildRequest.REQ_INPROGRESS, build = None).order_by("-pk")) + - # build requests that failed - map(lambda x: { - "id": x.pk, - "targets" : map(lambda y: {"target": y.target }, x.brtarget_set.all()), + "targets" : map(lambda y: {"target": y.target, "task": y.task }, x.brtarget_set.all()), "status": x.get_state_display(), "errors": map(lambda y: {"type": y.errtype, "msg": y.errmsg, "tb": y.traceback}, x.brerror_set.all()), - }, prj.buildrequest_set.filter(state = BuildRequest.REQ_FAILED).order_by("-pk")) + - # and already made builds - map(lambda x: { - "id": x.pk, - "targets": map(lambda y: {"target": y.target }, x.target_set.all()), - "status": x.get_outcome_display(), - "completed_on" : x.completed_on.strftime('%s')+"000", - "build_time" : (x.completed_on - x.started_on).total_seconds(), - "build_page_url" : reverse('builddashboard', args=(x.pk,)), - "completeper": x.completeper(), - "eta": x.eta().ctime(), - }, prj.build_set.all())) + "build" : map( lambda y: {"id": y.pk, + "status": y.get_outcome_display(), + "completed_on" : y.completed_on.strftime('%s')+"000", + "build_time" : (y.completed_on - y.started_on).total_seconds(), + "build_page_url" : reverse('builddashboard', args=(y.pk,)), + "errors": y.errors_no, + "warnings": y.warnings_no, + "completeper": y.completeper(), + "eta": y.eta().ctime()}, Build.objects.filter(buildrequest = x)), + }, prj.buildrequest_set.order_by("-pk")[:5]) # Shows the edit project page @@ -2025,10 +2010,14 @@ if toastermain.settings.MANAGED: if 'buildCancel' in request.POST: for i in request.POST['buildCancel'].strip().split(" "): - br = BuildRequest.objects.select_for_update().get(project = prj, pk = i, state__lte = BuildRequest.REQ_QUEUED) - print "selected for delete", br.pk - br.delete() - print "selected for delete", br.pk + try: + br = BuildRequest.objects.select_for_update().get(project = prj, pk = i, state__lte = BuildRequest.REQ_QUEUED) + print "selected for delete", br.pk + br.delete() + print "selected for delete", br.pk + except BuildRequest.DoesNotExist: + pass + if 'targets' in request.POST: ProjectTarget.objects.filter(project = prj).delete() @@ -2059,7 +2048,7 @@ if toastermain.settings.MANAGED: # remove layers if 'layerDel' in request.POST: for t in request.POST['layerDel'].strip().split(" "): - pt = ProjectLayer.objects.get(project = prj, layercommit_id = int(t)).delete() + pt = ProjectLayer.objects.filter(project = prj, layercommit_id = int(t)).delete() if 'projectName' in request.POST: prj.name = request.POST['projectName'] @@ -2071,8 +2060,8 @@ if toastermain.settings.MANAGED: prj.save() # we need to change the layers for i in prj.projectlayer_set.all(): - # find and add a similarly-named layer from the same layer source on the new branch - lv = Layer_Version.objects.filter(layer_source = i.layercommit.layer_source, layer__name = i.layercommit.layer.name, up_branch__in = Branch.objects.filter(name = prj.release.branch)) + # find and add a similarly-named layer on the new branch + lv = Layer_Version.objects.filter(layer__name = i.layercommit.layer.name, up_branch__release = prj.release) if lv.count() == 1: ProjectLayer.objects.get_or_create(project = prj, layercommit = lv[0]) # get rid of the old entry @@ -2095,17 +2084,19 @@ if toastermain.settings.MANAGED: @csrf_exempt def xhr_datatypeahead(request): try: + # returns layers for current project release that are not in the project set if request.GET['type'] == "layers": queryset_all = Layer_Version.objects.all() if 'project_id' in request.session: prj = Project.objects.get(pk = request.session['project_id']) - queryset_all = queryset_all.filter(up_branch__in = Branch.objects.filter(name = prj.release.name)).exclude(pk__in = map(lambda x: x.layercommit_id, prj.projectlayer_set.all())) + queryset_all = queryset_all.filter(up_branch__release = prj.release).exclude(pk__in = map(lambda x: x.layercommit_id, prj.projectlayer_set.all())) queryset_all = queryset_all.filter(layer__name__icontains=request.GET.get('value','')) return HttpResponse(json.dumps( { "error":"ok", "list" : map( lambda x: {"id": x.pk, "name": x.layer.name, "detail": "(" + x.layer.layer_source.name + (")" if x.up_branch == None else " | "+x.up_branch.name+")")}, queryset_all[:8]) }), content_type = "application/json") + # returns layer dependencies for a layer, excluding current project layers if request.GET['type'] == "layerdeps": queryset_all = LayerVersionDependency.objects.filter(layer_version_id = request.GET['value']) @@ -2122,6 +2113,7 @@ if toastermain.settings.MANAGED: map(lambda x: x.depends_on, queryset_all)) }), content_type = "application/json") + # returns layer versions that would be deleted on the new release__pk if request.GET['type'] == "versionlayers": if not 'project_id' in request.session: raise Exception("This call cannot makes no sense outside a project context") @@ -2129,8 +2121,8 @@ if toastermain.settings.MANAGED: retval = [] prj = Project.objects.get(pk = request.session['project_id']) for i in prj.projectlayer_set.all(): - lv = Layer_Version.objects.filter(layer_source = i.layercommit.layer_source, layer__name = i.layercommit.layer.name, up_branch__in = Branch.objects.filter(name = Release.objects.get(pk=request.GET['value']).branch)) - if lv.count() != 1: + lv = Layer_Version.objects.filter(layer__name = i.layercommit.layer.name, up_branch__release__pk=request.GET['value']) + if lv.count() != 1: # there is no layer_version with the new release id, and the same name retval.append(i) return HttpResponse(json.dumps( {"error":"ok", @@ -2138,14 +2130,14 @@ if toastermain.settings.MANAGED: lambda x: {"id": x.layercommit.pk, "name": x.layercommit.layer.name, "detail": "(" + x.layercommit.layer.layer_source.name + (")" if x.layercommit.up_branch == None else " | "+x.layercommit.up_branch.name+")")}, retval) }), content_type = "application/json") - + # returns targets provided by current project layers if request.GET['type'] == "targets": queryset_all = Recipe.objects.all() if 'project_id' in request.session: queryset_all = queryset_all.filter(layer_version__layer__in = map(lambda x: x.layercommit.layer, ProjectLayer.objects.filter(project_id=request.session['project_id']))) return HttpResponse(json.dumps({ "error":"ok", "list" : map ( lambda x: {"id": x.pk, "name": x.name, "detail":"[" + x.layer_version.layer.name+ (" | " + x.layer_version.up_branch.name + "]" if x.layer_version.up_branch is not None else "]")}, - queryset_all.filter(name__istartswith=request.GET.get('value',''))[:8]), + queryset_all.filter(name__icontains=request.GET.get('value',''))[:8]), }), content_type = "application/json") @@ -2155,7 +2147,7 @@ if toastermain.settings.MANAGED: queryset_all = queryset_all.filter(layer_version__layer__in = map(lambda x: x.layercommit.layer, ProjectLayer.objects.filter(project_id=request.session['project_id']))) return HttpResponse(json.dumps({ "error":"ok", "list" : map ( lambda x: {"id": x.pk, "name": x.name, "detail":"[" + x.layer_version.layer.name+ (" | " + x.layer_version.up_branch.name + "]" if x.layer_version.up_branch is not None else "]")}, - queryset_all.filter(name__istartswith=request.GET.get('value',''))[:8]), + queryset_all.filter(name__icontains=request.GET.get('value',''))[:8]), }), content_type = "application/json") @@ -2173,11 +2165,15 @@ if toastermain.settings.MANAGED: def layers(request): + if not 'project_id' in request.session: + raise Exception("invalid page: cannot show page without a project") + template = "layers.html" # define here what parameters the view needs in the GET portion in order to # be able to display something. 'count' and 'page' are mandatory for all views # that use paginators. - mandatory_parameters = { 'count': 10, 'page' : 1, 'orderby' : 'layer__name:+' }; + (pagesize, orderby) = _get_parameters_values(request, 100, 'layer__name:+') + mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby }; retval = _verify_parameters( request.GET, mandatory_parameters ) if retval: return _redirect_parameters( 'layers', request.GET, mandatory_parameters) @@ -2187,18 +2183,9 @@ if toastermain.settings.MANAGED: (filter_string, search_term, ordering_string) = _search_tuple(request, Layer_Version) queryset_all = Layer_Version.objects.all() - # mock an empty Project if we are outside project context - class _mockProject(object): - id = -1 - class _mockManager(object): - def all(self): - return [] - projectlayer_set = _mockManager() - prj = _mockProject() - if 'project_id' in request.session: - prj = Project.objects.get(pk = request.session['project_id']) - queryset_all = queryset_all.filter(up_branch__in = Branch.objects.filter(name = prj.release.name)) + prj = Project.objects.get(pk = request.session['project_id']) + queryset_all = queryset_all.filter(up_branch__release = prj.release) queryset_with_search = _get_queryset(Layer_Version, queryset_all, None, search_term, ordering_string, '-layer__name') queryset = _get_queryset(Layer_Version, queryset_all, filter_string, search_term, ordering_string, '-layer__name') @@ -2265,7 +2252,6 @@ if toastermain.settings.MANAGED: } }, - ] } @@ -2279,31 +2265,30 @@ if toastermain.settings.MANAGED: return render(request, template, context) def targets(request): - template = "targets.html" - # define here what parameters the view needs in the GET portion in order to - # be able to display something. 'count' and 'page' are mandatory for all views - # that use paginators. - mandatory_parameters = { 'count': 10, 'page' : 1, 'orderby' : 'name:+' }; + if not 'project_id' in request.session: + raise Exception("invalid page: cannot show page without a project") + + template = 'targets.html' + (pagesize, orderby) = _get_parameters_values(request, 100, 'name:+') + mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } retval = _verify_parameters( request.GET, mandatory_parameters ) if retval: return _redirect_parameters( 'targets', request.GET, mandatory_parameters) - - # boilerplate code that takes a request for an object type and returns a queryset - # for that object type. copypasta for all needed table searches (filter_string, search_term, ordering_string) = _search_tuple(request, Recipe) - queryset_all = Recipe.objects.all() - if 'project_id' in request.session: - queryset_all = queryset_all.filter(Q(layer_version__up_branch__in = Branch.objects.filter(name = Project.objects.get(pk=request.session['project_id']).release.name)) | Q(layer_version__build__in = Project.objects.get(pk = request.session['project_id']).build_set.all())) + prj = Project.objects.get(pk = request.session['project_id']) + queryset_all = Recipe.objects.filter(Q(layer_version__up_branch__release = prj.release) | Q(layer_version__build__in = prj.build_set.all())) queryset_with_search = _get_queryset(Recipe, queryset_all, None, search_term, ordering_string, '-name') - queryset = _get_queryset(Recipe, queryset_all, filter_string, search_term, ordering_string, '-name') + + queryset_with_search.prefetch_related("layer_source") # retrieve the objects that will be displayed in the table; targets a paginator and gets a page range to display - target_info = _build_page_range(Paginator(queryset, request.GET.get('count', 10)),request.GET.get('page', 1)) + target_info = _build_page_range(Paginator(queryset_with_search, request.GET.get('count', 10)),request.GET.get('page', 1)) context = { + 'projectlayerset' : json.dumps(map(lambda x: x.layercommit.id, prj.projectlayer_set.all())), 'objects' : target_info, 'objectname' : "targets", 'default_orderby' : 'name:+', @@ -2329,13 +2314,19 @@ if toastermain.settings.MANAGED: { 'name': 'Section', 'clclass': 'target-section', 'hidden': 1, + 'orderfield': _get_toggle_order(request, "section"), + 'ordericon': _get_toggle_order_icon(request, "section"), }, { 'name': 'License', 'clclass': 'license', 'hidden': 1, + 'orderfield': _get_toggle_order(request, "license"), + 'ordericon': _get_toggle_order_icon(request, "license"), }, { 'name': 'Layer', 'clclass': 'layer', + 'orderfield': _get_toggle_order(request, "layer_version__layer__name"), + 'ordericon': _get_toggle_order_icon(request, "layer_version__layer__name"), }, { 'name': 'Layer source', 'clclass': 'source', @@ -2345,20 +2336,32 @@ if toastermain.settings.MANAGED: 'filter': { 'class': 'target', 'label': 'Show:', - 'options': map(lambda x: (x.name, 'layer_source__pk:' + str(x.id), queryset_with_search.filter(layer_source__pk = x.id).count() ), LayerSource.objects.all()), + 'options': map(lambda x: ("Targets provided by " + x.name + " layers", 'layer_source__pk:' + str(x.id), queryset_with_search.filter(layer_source__pk = x.id).count() ), LayerSource.objects.all()), } }, { 'name': 'Branch, tag or commit', 'clclass': 'branch', 'hidden': 1, }, + ] + } + + if 'project_id' in request.session: + context['tablecols'] += [ { 'name': 'Build', 'dclass': 'span2', 'qhelp': "Add or delete targets to / from your project ", - }, + 'filter': { + 'class': 'add-layer', + 'label': 'Show:', + 'options': [ + ('Targets provided by layers added to this project', "layer_version__projectlayer__project:" + str(prj.id), queryset_with_search.filter(layer_version__projectlayer__project = prj.id).count()), + ('Targets provided by layers not added to this project', "layer_version__projectlayer__project:NOT" + str(prj.id), queryset_with_search.exclude(layer_version__projectlayer__project = prj.id).count()), + ] + + } + }, ] - ] - } return render(request, template, context) @@ -2378,7 +2381,7 @@ if toastermain.settings.MANAGED: queryset_all = Machine.objects.all() # if 'project_id' in request.session: -# queryset_all = queryset_all.filter(Q(layer_version__up_branch__in = Branch.objects.filter(name = Project.objects.get(request.session['project_id']).release.name)) | Q(layer_version__build__in = Project.objects.get(request.session['project_id']).build_set.all())) +# queryset_all = queryset_all.filter(Q(layer_version__up_branch__release = Project.objects.get(request.session['project_id']).release) | Q(layer_version__build__in = Project.objects.get(request.session['project_id']).build_set.all())) queryset_with_search = _get_queryset(Machine, queryset_all, None, search_term, ordering_string, '-name') queryset = _get_queryset(Machine, queryset_all, filter_string, search_term, ordering_string, '-name') @@ -2643,7 +2646,7 @@ if toastermain.settings.MANAGED: def projects(request): template="projects.html" - (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-') + (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:+') mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } retval = _verify_parameters( request.GET, mandatory_parameters ) if retval: @@ -2654,8 +2657,8 @@ if toastermain.settings.MANAGED: # boilerplate code that takes a request for an object type and returns a queryset # for that object type. copypasta for all needed table searches (filter_string, search_term, ordering_string) = _search_tuple(request, Project) - queryset_with_search = _get_queryset(Project, queryset_all, None, search_term, ordering_string, 'updated:-') - queryset = _get_queryset(Project, queryset_all, filter_string, search_term, ordering_string, 'updated:-') + queryset_with_search = _get_queryset(Project, queryset_all, None, search_term, ordering_string, '-updated') + queryset = _get_queryset(Project, queryset_all, filter_string, search_term, ordering_string, '-updated') # retrieve the objects that will be displayed in the table; projects a paginator and gets a page range to display project_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1)) |