* [PATCH 01/23] toastergui: fix angular error
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 02/23] toaster: improve the buildenvironment API Alex DAMIAN
` (21 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
Due to invalid identification of builds in the array update
code (by id and status), we could end up with multiple entries
sharing the same primary id, visible in the UI as an angular error.
We modify the code to identify the builds exclusively by id.
[YOCTO #7611]
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/toastergui/static/js/projectapp.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/toaster/toastergui/static/js/projectapp.js b/lib/toaster/toastergui/static/js/projectapp.js
index 44e244d..b2e65c5 100644
--- a/lib/toaster/toastergui/static/js/projectapp.js
+++ b/lib/toaster/toastergui/static/js/projectapp.js
@@ -341,7 +341,7 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
var toDelete = [];
// step 1 - delete entries not found
$scope.builds.forEach(function (elem) {
- if (-1 == _data.builds.findIndex(function (elemX) { return elemX.id == elem.id && elemX.status == elem.status; })) {
+ if (-1 == _data.builds.findIndex(function (elemX) { return elemX.id == elem.id; })) {
toDelete.push(elem);
}
});
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 02/23] toaster: improve the buildenvironment API
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
2015-06-25 10:33 ` [PATCH 01/23] toastergui: fix angular error Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 03/23] toastergui: enable strict variable checking Alex DAMIAN
` (20 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
We improve the buildenvironment API by reducing it to a single
command: triggerBuild.
This command is specifically implemented in each BE controller
type, so the runbuilds management command is only concerned
with scheduling the next build, and not with the details
of how a build is actually started.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/bldcontrol/bbcontroller.py | 3 +++
lib/toaster/bldcontrol/localhostbecontroller.py | 22 ++++++++++++++++
.../bldcontrol/management/commands/runbuilds.py | 30 +++++-----------------
lib/toaster/bldcontrol/sshbecontroller.py | 21 +++++++++++++++
4 files changed, 53 insertions(+), 23 deletions(-)
diff --git a/lib/toaster/bldcontrol/bbcontroller.py b/lib/toaster/bldcontrol/bbcontroller.py
index 42675d3..9dd01e0 100644
--- a/lib/toaster/bldcontrol/bbcontroller.py
+++ b/lib/toaster/bldcontrol/bbcontroller.py
@@ -190,6 +190,9 @@ class BuildEnvironmentController(object):
"""
raise Exception("Must override BE release")
+ def triggerBuild(self, bitbake, layers, variables, targets):
+ raise Exception("Must override BE release")
+
class ShellCmdException(Exception):
pass
diff --git a/lib/toaster/bldcontrol/localhostbecontroller.py b/lib/toaster/bldcontrol/localhostbecontroller.py
index bc3566a..d0f8632 100644
--- a/lib/toaster/bldcontrol/localhostbecontroller.py
+++ b/lib/toaster/bldcontrol/localhostbecontroller.py
@@ -317,3 +317,25 @@ class LocalhostBEController(BuildEnvironmentController):
import shutil
shutil.rmtree(os.path.join(self.be.sourcedir, "build"))
assert not os.path.exists(self.be.builddir)
+
+
+ def triggerBuild(self, bitbake, layers, variables, targets):
+ # set up the buid environment with the needed layers
+ self.setLayers(bitbake, layers)
+ self.writeConfFile("conf/toaster-pre.conf", )
+ self.writeConfFile("conf/toaster.conf", raw = "INHERIT+=\"toaster buildhistory\"")
+
+ # get the bb server running with the build req id and build env id
+ bbctrl = self.getBBController()
+
+ # trigger the build command
+ task = reduce(lambda x, y: x if len(y)== 0 else y, map(lambda y: y.task, targets))
+ if len(task) == 0:
+ task = None
+
+ bbctrl.build(list(map(lambda x:x.target, targets)), task)
+
+ logger.debug("localhostbecontroller: Build launched, exiting. Follow build logs at %s/toaster_ui.log" % self.be.builddir)
+
+ # disconnect from the server
+ bbctrl.disconnect()
diff --git a/lib/toaster/bldcontrol/management/commands/runbuilds.py b/lib/toaster/bldcontrol/management/commands/runbuilds.py
index 808318f..920d9ef 100644
--- a/lib/toaster/bldcontrol/management/commands/runbuilds.py
+++ b/lib/toaster/bldcontrol/management/commands/runbuilds.py
@@ -50,33 +50,16 @@ class Command(NoArgsCommand):
# write the build identification variable
BRVariable.objects.create(req = br, name="TOASTER_BRBE", value="%d:%d" % (br.pk, bec.be.pk))
+
# let the build request know where it is being executed
br.environment = bec.be
br.save()
- # set up the buid environment with the needed layers
- bec.setLayers(br.brbitbake_set.all(), br.brlayer_set.all())
- bec.writeConfFile("conf/toaster-pre.conf", br.brvariable_set.all())
- bec.writeConfFile("conf/toaster.conf", raw = "INHERIT+=\"toaster buildhistory\"")
-
- # get the bb server running with the build req id and build env id
- bbctrl = bec.getBBController()
-
- # trigger the build command
- task = reduce(lambda x, y: x if len(y)== 0 else y, map(lambda y: y.task, br.brtarget_set.all()))
- if len(task) == 0:
- task = None
- bbctrl.build(list(map(lambda x:x.target, br.brtarget_set.all())), task)
-
- logger.debug("runbuilds: Build launched, exiting. Follow build logs at %s/toaster_ui.log" % bec.be.builddir)
- # disconnect from the server
- bbctrl.disconnect()
-
- # cleanup to be performed by toaster when the deed is done
-
+ # this triggers an async build
+ bec.triggerBuild(br.brbitbake_set.all(), br.brlayer_set.all(), br.brvariable_set.all(), br.brtarget_set.all())
except Exception as e:
- logger.error("runbuilds: Error executing shell command %s" % e)
+ logger.error("runbuilds: Error launching build %s" % e)
traceback.print_exc(e)
if "[Errno 111] Connection refused" in str(e):
# Connection refused, read toaster_server.out
@@ -124,8 +107,9 @@ class Command(NoArgsCommand):
def cleanup(self):
from django.utils import timezone
from datetime import timedelta
- # environments locked for more than 30 seconds - they should be unlocked
- BuildEnvironment.objects.filter(lock=BuildEnvironment.LOCK_LOCK).filter(updated__lt = timezone.now() - timedelta(seconds = 30)).update(lock = BuildEnvironment.LOCK_FREE)
+ # DISABLED environments locked for more than 30 seconds - they should be unlocked
+ #BuildEnvironment.objects.filter(lock=BuildEnvironment.LOCK_LOCK).filter(updated__lt = timezone.now() - timedelta(seconds = 30)).update(lock = BuildEnvironment.LOCK_FREE)
+ pass
def handle_noargs(self, **options):
diff --git a/lib/toaster/bldcontrol/sshbecontroller.py b/lib/toaster/bldcontrol/sshbecontroller.py
index 29ed0a7..8ef434b 100644
--- a/lib/toaster/bldcontrol/sshbecontroller.py
+++ b/lib/toaster/bldcontrol/sshbecontroller.py
@@ -156,3 +156,24 @@ class SSHBEController(BuildEnvironmentController):
import shutil
shutil.rmtree(os.path.join(self.be.sourcedir, "build"))
assert not self._pathexists(self.be.builddir)
+
+ def triggerBuild(self, bitbake, layers, variables, targets):
+ # set up the buid environment with the needed layers
+ self.setLayers(bitbake, layers)
+ self.writeConfFile("conf/toaster-pre.conf", )
+ self.writeConfFile("conf/toaster.conf", raw = "INHERIT+=\"toaster buildhistory\"")
+
+ # get the bb server running with the build req id and build env id
+ bbctrl = self.getBBController()
+
+ # trigger the build command
+ task = reduce(lambda x, y: x if len(y)== 0 else y, map(lambda y: y.task, targets))
+ if len(task) == 0:
+ task = None
+
+ bbctrl.build(list(map(lambda x:x.target, targets)), task)
+
+ logger.debug("localhostbecontroller: Build launched, exiting. Follow build logs at %s/toaster_ui.log" % self.be.builddir)
+
+ # disconnect from the server
+ bbctrl.disconnect()
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 03/23] toastergui: enable strict variable checking
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
2015-06-25 10:33 ` [PATCH 01/23] toastergui: fix angular error Alex DAMIAN
2015-06-25 10:33 ` [PATCH 02/23] toaster: improve the buildenvironment API Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 04/23] toaster: fixing undefined variables Alex DAMIAN
` (19 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
In order to make sure we don't use undefined variables in the
templates, we enforce strict variable checking in the templating
engine.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/toastermain/settings.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index 3c7cb3b..141ad08 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -386,3 +386,10 @@ connection_created.connect(activate_synchronous_off)
#
+class InvalidString(str):
+ def __mod__(self, other):
+ from django.template.base import TemplateSyntaxError
+ raise TemplateSyntaxError(
+ "Undefined variable or unknown value for: \"%s\"" % other)
+
+TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 04/23] toaster: fixing undefined variables
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (2 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 03/23] toastergui: enable strict variable checking Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 05/23] toaster: remove MANAGED references Alex DAMIAN
` (18 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
This patchset fixes usage of undefined variables in
the base page.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/toastergui/templates/base.html | 11 +++++++----
lib/toaster/toastergui/templates/basetable_top.html | 11 ++++++++---
lib/toaster/toastergui/templates/filtersnippet.html | 2 +-
3 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/lib/toaster/toastergui/templates/base.html b/lib/toaster/toastergui/templates/base.html
index 9f19c03..3f3253d 100644
--- a/lib/toaster/toastergui/templates/base.html
+++ b/lib/toaster/toastergui/templates/base.html
@@ -29,17 +29,18 @@
{% endif %}
<script>
libtoaster.ctx = {
- projectId : {{project.id|default:'undefined'}},
jsUrl : "{% static 'js/' %}",
htmlUrl : "{% static 'html/' %}",
projectsUrl : "{% url 'all-projects' %}",
{% if project.id %}
+ projectId : {{project.id}},
projectPageUrl : {% url 'project' project.id as purl%}{{purl|json}},
projectName : {{project.name|json}},
projectTargetsUrl: {% url 'projectavailabletargets' project.id as paturl%}{{paturl|json}},
projectBuildsUrl: {% url 'projectbuilds' project.id as pburl %}{{pburl|json}},
projectId : {{project.id}},
{% else %}
+ projectId : undefined,
projectPageUrl : undefined,
projectName : undefined,
projectId : undefined,
@@ -101,8 +102,10 @@
<h3>New build</h3>
<h6>Project:</h6>
<span id="project">
- <a class="lead" href="{% if project.id %}{% url 'project' project.id %}{% endif %}">{{project.name}}</a>
- <i class="icon-pencil"></i>
+ {% if project.id %}
+ <a class="lead" href="{% url 'project' project.id %}">{{project.name}}</a>
+ <i class="icon-pencil"></i>
+ {% endif %}
</span>
<form id="change-project-form" style="display:none;">
<div class="input-append">
@@ -122,7 +125,7 @@
<form>
<input type="text" class="input-xlarge" id="build-target-input" placeholder="Type a recipe name" autocomplete="off" data-minLength="1" data-autocomplete="off" data-provide="typeahead" disabled/>
<div>
- <button class="btn btn-primary" id="build-button" data-project-id="{{project.id}}" disabled>Build</button>
+ <button class="btn btn-primary" id="build-button" disabled>Build</button>
</div>
</form>
</li>
diff --git a/lib/toaster/toastergui/templates/basetable_top.html b/lib/toaster/toastergui/templates/basetable_top.html
index fcd2f03..8dd56ed 100644
--- a/lib/toaster/toastergui/templates/basetable_top.html
+++ b/lib/toaster/toastergui/templates/basetable_top.html
@@ -162,7 +162,7 @@
<div class="navbar">
<div class="navbar-inner">
<form class="navbar-search input-append pull-left" id="searchform">
- <input class="input-xxlarge" id="search" name="search" type="text" placeholder="Search {%if object_search_display %}{{object_search_display}}{%else%}{{objectname}}{%endif%}" 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%}
+ <input class="input-xxlarge" id="search" name="search" type="text" placeholder="Search {%if object_search_display %}{{object_search_display}}{%else%}{{objectname}}{%endif%}" value="{%if request.GET.search %}{{request.GET.search}}{% endif %}"/>{% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" class="add-on btn" tabindex="-1"><i class="icon-remove"></i></a>{%endif%}
<input type="hidden" name="orderby" value="{{request.GET.orderby}}">
<input type="hidden" name="page" value="1">
<button class="btn" type="submit" value="Search">Search</button>
@@ -191,7 +191,12 @@
onclick="showhideTableColumn(
$(this).attr('id'),
$(this).is(':checked'),
- '{{i.orderkey}}' )"
+ {% if i.ordericon %}
+ '{{i.orderkey}}'
+ {% else %}
+ undefined
+ {% endif %}
+ )"
{%else%}
checked disabled
{% endif %}/> {{i.name}}
@@ -221,7 +226,7 @@
<thead>
<!-- Table header row; generated from "tablecols" entry in the context dict -->
<tr>
- {% for tc in tablecols %}<th class="{{tc.dclass}} {{tc.clclass}}">
+ {% for tc in tablecols %}<th class="{%if tc.dclass%}{{tc.dclass}}{%endif%} {% if tc.clclass %}{{tc.clclass}}{% endif %}">
{%if tc.qhelp%}<i class="icon-question-sign get-help" title="{{tc.qhelp}}"></i>{%endif%}
{%if tc.orderfield%}<a {%if tc.ordericon%} class="sorted" {%endif%}href="javascript:reload_params({'page': 1, 'orderby' : '{{tc.orderfield}}' })">{{tc.name}}</a>{%else%}<span class="muted">{{tc.name}}</span>{%endif%}
{%if tc.ordericon%} <i class="icon-caret-{{tc.ordericon}}"></i>{%endif%}
diff --git a/lib/toaster/toastergui/templates/filtersnippet.html b/lib/toaster/toastergui/templates/filtersnippet.html
index f624d88..56e2b21 100644
--- a/lib/toaster/toastergui/templates/filtersnippet.html
+++ b/lib/toaster/toastergui/templates/filtersnippet.html
@@ -2,7 +2,7 @@
<!-- '{{f.class}}' filter -->
{% with f.class as key %}
<form id="filter_{{f.class}}" class="modal hide fade" tabindex="-1" role="dialog" aria-hidden="true">
- <input type="hidden" name="search" value="{{request.GET.search}}"/>
+ <input type="hidden" name="search" value="{%if request.GET.search %}{{request.GET.search}}{%endif%}"/>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
{% if search_term %}
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 05/23] toaster: remove MANAGED references
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (3 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 04/23] toaster: fixing undefined variables Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 06/23] toaster: remove BuildRequest references Alex DAMIAN
` (17 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
We conflate the managed and analysis modes by
deleting alternative code paths, favouring the MANAGED mode,
always considering the MANAGED variable True.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/orm/models.py | 4 +-
lib/toaster/toastergui/templates/base.html | 9 +-
.../toastergui/templates/basebuilddetailpage.html | 4 +-
.../toastergui/templates/basebuildpage.html | 6 +-
.../templates/basetable_top_buildprojects.html | 2 -
.../templates/basetable_top_projectbuilds.html | 2 -
lib/toaster/toastergui/templates/bpackage.html | 3 -
lib/toaster/toastergui/templates/build.html | 8 --
.../toastergui/templates/builddashboard.html | 16 ----
lib/toaster/toastergui/views.py | 103 +--------------------
10 files changed, 8 insertions(+), 149 deletions(-)
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 8819450..422f2bf 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -288,9 +288,7 @@ class BuildArtifact(models.Model):
def is_available(self):
- if settings.MANAGED and build.project is not None:
- return build.buildrequest.environment.has_artifact(file_path)
- return False
+ return build.buildrequest.environment.has_artifact(file_path)
class ProjectTarget(models.Model):
project = models.ForeignKey(Project)
diff --git a/lib/toaster/toastergui/templates/base.html b/lib/toaster/toastergui/templates/base.html
index 3f3253d..7430338 100644
--- a/lib/toaster/toastergui/templates/base.html
+++ b/lib/toaster/toastergui/templates/base.html
@@ -48,7 +48,6 @@
};
</script>
<script src="{% static 'js/base.js' %}"></script>
-{%if MANAGED %}
<script>
$(document).ready(function () {
/* Vars needed for base.js */
@@ -59,10 +58,6 @@
basePageInit(ctx);
});
</script>
-{% endif %}
-<script>
-
-</script>
{% block extraheadcontent %}
{% endblock %}
@@ -80,14 +75,13 @@
<span class="brand">
<a href="/">Toaster</a>
{% if DEBUG %}
- <i class="icon-info-sign" title="<strong>Toaster version information</strong>" data-content="<dl><dt>Branch</dt><dd>{{TOASTER_BRANCH}}</dd><dt>Revision</dt><dd>{{TOASTER_REVISION}}</dd><dt>Mode</dt><dd>{%if MANAGED%}Build{%else%}Analysis{%endif%}</dd></dl>"></i>
+ <i class="icon-info-sign" title="<strong>Toaster version information</strong>" data-content="<dl><dt>Branch</dt><dd>{{TOASTER_BRANCH}}</dd><dt>Revision</dt><dd>{{TOASTER_REVISION}}</dd></dl>"></i>
{% endif %}
</span>
<a class="pull-right manual" target="_blank" href="http://www.yoctoproject.org/docs/latest/toaster-manual/toaster-manual.html">
<i class="icon-book"></i>
Toaster manual
</a>
- {%if MANAGED %}
<div class="btn-group pull-right">
<a class="btn" id="new-project-button" href="{% url 'newproject' %}">New project</a>
</div>
@@ -132,7 +126,6 @@
</ul>
</div>
- {%endif%}
</div>
</div>
diff --git a/lib/toaster/toastergui/templates/basebuilddetailpage.html b/lib/toaster/toastergui/templates/basebuilddetailpage.html
index c8e217e..d324c90 100644
--- a/lib/toaster/toastergui/templates/basebuilddetailpage.html
+++ b/lib/toaster/toastergui/templates/basebuilddetailpage.html
@@ -7,10 +7,8 @@
<div class="section">
<ul class="breadcrumb" id="breadcrumb">
<li><a href="{% url 'all-builds' %}">All builds</a></li>
- {% if MANAGED and build.project %}
<li><a href="{% url 'project' build.project.id %}">{{build.project.name}}</a></li>
- {%endif%}
- <li><a href="{%url 'builddashboard' build.pk%}">{{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%}{%if not MANAGED %}{{build.machine}}{%endif%} ({{build.completed_on|date:"d/m/y H:i"}})</a></li>
+ <li><a href="{%url 'builddashboard' build.pk%}">{{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%} ({{build.completed_on|date:"d/m/y H:i"}})</a></li>
{% block localbreadcrumb %}{% endblock %}
</ul>
<script>
diff --git a/lib/toaster/toastergui/templates/basebuildpage.html b/lib/toaster/toastergui/templates/basebuildpage.html
index c03f1b4..b7a4dd2 100644
--- a/lib/toaster/toastergui/templates/basebuildpage.html
+++ b/lib/toaster/toastergui/templates/basebuildpage.html
@@ -8,14 +8,12 @@
<!-- Breadcrumbs -->
<div class="section">
<ul class="breadcrumb" id="breadcrumb">
- <li><a href="{% url 'all-builds' %}">All builds</a></li>
- {% if MANAGED and build.project %}
+ <li><a href="{% url 'all-builds' %}">All builds</a></li>
<li><a href="{% url 'project' build.project.id %}">{{build.project.name}}</a></li>
- {%endif%}
<li>
{% block parentbreadcrumb %}
<a href="{%url 'builddashboard' build.pk%}">
- {{build.get_sorted_target_list.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%} {%if not MANAGED %}{{build.machine}}{% endif %} ({{build.completed_on|date:"d/m/y H:i"}})
+ {{build.get_sorted_target_list.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%} ({{build.completed_on|date:"d/m/y H:i"}})
</a>
{% endblock %}
</li>
diff --git a/lib/toaster/toastergui/templates/basetable_top_buildprojects.html b/lib/toaster/toastergui/templates/basetable_top_buildprojects.html
index d517179..7b19f4b 100644
--- a/lib/toaster/toastergui/templates/basetable_top_buildprojects.html
+++ b/lib/toaster/toastergui/templates/basetable_top_buildprojects.html
@@ -1,7 +1,6 @@
{% extends "basetable_top.html" %}
{%block custombuttons %}
-{% if MANAGED %}
<div class="btn-group builds-projects">
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="selection">Show all builds</span>
@@ -12,5 +11,4 @@
<li><a href="{% url 'all-projects'%}">Show all projects</a></li>
</ul>
</div>
-{% endif %}
{%endblock%}
diff --git a/lib/toaster/toastergui/templates/basetable_top_projectbuilds.html b/lib/toaster/toastergui/templates/basetable_top_projectbuilds.html
index bfefff5..f5b01c8 100644
--- a/lib/toaster/toastergui/templates/basetable_top_projectbuilds.html
+++ b/lib/toaster/toastergui/templates/basetable_top_projectbuilds.html
@@ -1,7 +1,6 @@
{% extends "basetable_top.html" %}
{%block custombuttons %}
-{% if MANAGED %}
<div class="btn-group builds-projects">
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="selection">Show all projects</span>
@@ -12,5 +11,4 @@
<li><a href="{% url 'all-projects'%}">Show all projects</a></li>
</ul>
</div>
-{% endif %}
{%endblock%}
diff --git a/lib/toaster/toastergui/templates/bpackage.html b/lib/toaster/toastergui/templates/bpackage.html
index 1c47354..d775fec 100644
--- a/lib/toaster/toastergui/templates/bpackage.html
+++ b/lib/toaster/toastergui/templates/bpackage.html
@@ -88,9 +88,6 @@
</a>
</td>
<!-- Layer directory -->
- {% if not MANAGED or not build.project %}
- <td class="recipe__layer_version__local_path">{{package.recipe.layer_version.local_path}}</td>
- {% endif %}
{%else%}
<td class="recipe__name"></td>
<td class="recipe__version"></td>
diff --git a/lib/toaster/toastergui/templates/build.html b/lib/toaster/toastergui/templates/build.html
index f7ad2f4..f0b5ea5 100644
--- a/lib/toaster/toastergui/templates/build.html
+++ b/lib/toaster/toastergui/templates/build.html
@@ -68,11 +68,9 @@
{% query build.task_build outcome=4 order__gt=0 as exectask%}
{% if exectask.count == 1 %}
<a href="{% url "task" build.id exectask.0.id %}">{{exectask.0.recipe.name}}.{{exectask.0.task_name}}</a>
- {% if MANAGED and build.project %}
<a href="{% url 'build_artifact' build.id "tasklogfile" exectask.0.id %}">
<i class="icon-download-alt" title="" data-original-title="Download task log file"></i>
</a>
- {% endif %}
{% elif exectask.count > 1%}
<a href="{% url "tasks" build.id %}?filter=outcome%3A4">{{exectask.count}} task{{exectask.count|pluralize}}</a>
{%endif%}
@@ -84,21 +82,15 @@
</td>
<td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td>
<td class="time"><a href="{% url "buildtime" build.id %}">{{build.timespent|sectohms}}</a></td>
- {% if not MANAGED or not build.project %}
- <td class="log">{{build.cooker_log_path}}</td>
- {% endif %}
<td class="output">
{% if build.outcome == build.SUCCEEDED %}
<a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
{% endif %}
</td>
- {% if MANAGED %}
- <td class="project">
{% if build.project %}
<a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
{% endif %}
</td>
- {% endif %}
</tr>
{% endfor %}
diff --git a/lib/toaster/toastergui/templates/builddashboard.html b/lib/toaster/toastergui/templates/builddashboard.html
index ad497e6..47b8d7a 100644
--- a/lib/toaster/toastergui/templates/builddashboard.html
+++ b/lib/toaster/toastergui/templates/builddashboard.html
@@ -37,9 +37,7 @@
<span > <i class="icon-warning-sign yellow"></i><strong><a href="#warnings" class="warning show-warnings"> {{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a></strong></span>
{% endif %}
<span class="pull-right">Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a>
- {% if MANAGED and build.project %}
<a class="btn {%if build.outcome == build.SUCCEEDED%}btn-success{%else%}btn-danger{%endif%} pull-right log" href="{% url 'build_artifact' build.id "cookerlog" build.id %}">Download build log</a>
- {% endif %}
</span>
{%endif%}
</div>
@@ -117,19 +115,11 @@
<dt>
<i class="icon-question-sign get-help" title="The location in disk of the license manifest, a document listing all packages installed in your image and their licenses"></i>
- {% if MANAGED and build.project %}
License manifest
- {% else %}
- <a href="{% url 'targetpkg' build.pk target.target.pk %}">License manifest</a>
- {% endif %}
</dt>
- {% if MANAGED and build.project %}
<dd>
<a href="{% url 'targetpkg' build.pk target.target.pk %}">View in Toaster</a> |
<a href="{% url 'build_artifact' build.pk 'licensemanifest' target.target.pk %}">Download</a></dd>
- {% else %}
- <dd><code>{{target.target.license_manifest_path}}</code></dd>
- {% endif %}
<dt>
<i class="icon-question-sign get-help" title="Image files are stored in <code>/build/tmp/deploy/images/</code>"></i>
Image files
@@ -170,13 +160,9 @@
Other artifacts</dt>
<dd><div>
{% for ba in build.buildartifact_set.all|dictsort:"file_name" %}
- {% if MANAGED and build.project %}
<a href="{%url 'build_artifact' build.id 'buildartifact' ba.id %}">
- {% endif %}
{{ba.get_local_file_name}}
- {% if MANAGED and build.project %}
</a>
- {% endif %}
({{ba.file_size|filtered_filesizeformat}}) <br/>
{% endfor %}
@@ -211,11 +197,9 @@
<span class="task-name">{{exectask.0.task_name}}</span>
</a>
- {% if MANAGED and build.project %}
<a href="{% url 'build_artifact' build.id "tasklogfile" exectask.0.id %}">
<i class="icon-download-alt" title="" data-original-title="Download task log file"></i>
</a>
- {% endif %}
{% elif exectask.count > 1%}
<a class="error" href="{% url "tasks" build.id %}?filter=outcome%3A4">{{exectask.count}}</a>
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 4dac62c..bfae304 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -807,18 +807,6 @@ eans multiple licenses exist that cover different parts of the source',
]
}
- if not toastermain.settings.MANAGED or build.project is None:
-
- tc_layerDir = {
- 'name':'Layer directory',
- 'qhelp':'Location in disk of the layer providing the recipe that builds the package',
- 'orderfield' : _get_toggle_order( request, "recipe__layer_version__local_path" ),
- 'ordericon' : _get_toggle_order_icon( request, "recipe__layer_version__local_path" ),
- 'orderkey' : "recipe__layer_version__local_path",
- 'clclass' : 'layer_directory',
- 'hidden' : 1,
- }
- context['tablecols'].append(tc_layerDir)
response = render(request, template, context)
_set_parameters_values(pagesize, orderby, request)
@@ -1209,9 +1197,6 @@ def tasks_common(request, build_id, variant, task_anchor):
]}
- if not toastermain.settings.MANAGED or build.project is None:
- context['tablecols'].append(tc_log)
-
response = render(request, template, context)
_set_parameters_values(pagesize, orderby, request)
return response
@@ -1336,18 +1321,6 @@ def recipes(request, build_id):
]
}
- if not toastermain.settings.MANAGED or build.project is None:
- context['tablecols'].append(
- {
- 'name':'Layer directory',
- 'qhelp':'Path to the layer prodiving the recipe',
- 'orderfield': _get_toggle_order(request, "layer_version__local_path"),
- 'ordericon':_get_toggle_order_icon(request, "layer_version__local_path"),
- 'orderkey' : 'layer_version__local_path',
- 'clclass': 'layer_version__local_path', 'hidden': 1,
- })
-
-
response = render(request, template, context)
_set_parameters_values(pagesize, orderby, request)
return response
@@ -1550,18 +1523,6 @@ def bpackage(request, build_id):
]
}
- if not toastermain.settings.MANAGED or build.project is None:
-
- tc_layerDir = {
- 'name':'Layer directory',
- 'qhelp':'Path to the layer providing the recipe that builds the package',
- 'orderfield': _get_toggle_order(request, "recipe__layer_version__local_path"),
- 'ordericon':_get_toggle_order_icon(request, "recipe__layer_version__local_path"),
- 'orderkey' : 'recipe__layer_version__local_path',
- 'clclass': 'recipe__layer_version__local_path', 'hidden': 1,
- }
- context['tablecols'].append(tc_layerDir)
-
response = render(request, template, context)
_set_parameters_values(pagesize, orderby, request)
return response
@@ -1877,7 +1838,6 @@ def managedcontextprocessor(request):
import subprocess
ret = {
"projects": Project.objects.all(),
- "MANAGED" : toastermain.settings.MANAGED,
"DEBUG" : toastermain.settings.DEBUG,
"TOASTER_BRANCH": toastermain.settings.TOASTER_BRANCH,
"TOASTER_REVISION" : toastermain.settings.TOASTER_REVISION,
@@ -1892,8 +1852,8 @@ from orm.models import Project, ProjectLayer, ProjectTarget, ProjectVariable
# we have a set of functions if we're in managed mode, or
# a default "page not available" simple functions for interactive mode
-if toastermain.settings.MANAGED:
+if True:
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required
@@ -2937,11 +2897,8 @@ if toastermain.settings.MANAGED:
return context
-else:
- # shows the "all builds" page for interactive mode; this is the old code, simply moved
-
- @_template_renderer('build.html')
- def builds(request):
+ @_template_renderer('builds.html')
+ def builds_old(request):
# 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.
@@ -3132,57 +3089,3 @@ else:
_set_parameters_values(pagesize, orderby, request)
return context
-
-
-
-
- @_template_renderer('landing_not_managed.html')
- def newproject(request):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def project(request, pid):
- return {}
-
- from django.views.decorators.csrf import csrf_exempt
- @csrf_exempt
- @_template_renderer('landing_not_managed.html')
- def xhr_datatypeahead(request, pid):
- return {}
-
-
- @_template_renderer('landing_not_managed.html')
- def xhr_configvaredit(request, pid):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def importlayer(request):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def projectconf(request, pid):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def projectbuilds(request, pid):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def build_artifact(request, build_id, artifact_type, artifact_id):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def projects(request):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def xhr_importlayer(request):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def xhr_updatelayer(request):
- return {}
-
- @_template_renderer('landing_not_managed.html')
- def buildrequestdetails(request, pid, brid):
- return {}
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 06/23] toaster: remove BuildRequest references
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (4 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 05/23] toaster: remove MANAGED references Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 07/23] toaster: refactor the builds pages Alex DAMIAN
` (16 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
In the toastergui application we should not display
implementation details about how the builds are run.
This patch removes the references to BuildRequest on the
majority of the views (except Builds page itself, as
that is more complicated).
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
.../toastergui/templates/basebuildpage.html | 1 +
lib/toaster/toastergui/templates/configvars.html | 4 +-
lib/toaster/toastergui/views.py | 66 +++++++++-------------
3 files changed, 30 insertions(+), 41 deletions(-)
diff --git a/lib/toaster/toastergui/templates/basebuildpage.html b/lib/toaster/toastergui/templates/basebuildpage.html
index b7a4dd2..105ad85 100644
--- a/lib/toaster/toastergui/templates/basebuildpage.html
+++ b/lib/toaster/toastergui/templates/basebuildpage.html
@@ -24,6 +24,7 @@
$('#breadcrumb > li').append("<span class=\"divider\">→</span>");
$('#breadcrumb > li:last').addClass("active");
$('#breadcrumb > li:last > span').remove();
+ console.log("done");
});
</script>
</div>
diff --git a/lib/toaster/toastergui/templates/configvars.html b/lib/toaster/toastergui/templates/configvars.html
index cbe3c68..8957673 100644
--- a/lib/toaster/toastergui/templates/configvars.html
+++ b/lib/toaster/toastergui/templates/configvars.html
@@ -56,7 +56,7 @@
<td class="file"><a data-toggle="modal" href="#variable-{{variable.pk}}">
{% if variable.vhistory.all %}
{% for path in variable.vhistory.all|filter_setin_files:file_filter %}
- {{path|cut_path_prefix:dirstostrip}}<br/>
+ {{path}}<br/>
{% endfor %}
{% endif %}
</a></td>
@@ -117,7 +117,7 @@
<tbody>
{% for vh in variable.vhistory.all %}
<tr>
- <td>{{forloop.counter}}</td><td>{{vh.file_name|cut_path_prefix:dirstostrip}}</td><td>{{vh.operation}}</td><td>{{vh.line_number}}</td>
+ <td>{{forloop.counter}}</td><td>{{vh.file_name}}</td><td>{{vh.operation}}</td><td>{{vh.line_number}}</td>
</tr>
{%endfor%}
</tbody>
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index bfae304..060d680 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -27,7 +27,6 @@ from django.shortcuts import render, redirect
from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable
from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File, Package_Dependency
from orm.models import Target_Installed_Package, Target_File, Target_Image_File, BuildArtifact
-from bldcontrol.models import BuildEnvironment, BuildRequest
from bldcontrol import bbcontroller
from django.views.decorators.cache import cache_control
from django.core.urlresolvers import reverse
@@ -41,36 +40,33 @@ from django.utils import formats
from toastergui.templatetags.projecttags import json as jsonfilter
import json
from os.path import dirname
+import itertools
# all new sessions should come through the landing page;
# determine in which mode we are running in, and redirect appropriately
def landing(request):
- if toastermain.settings.MANAGED:
- from bldcontrol.models import BuildRequest
- if BuildRequest.objects.count() == 0 and Project.objects.count() > 0:
- return redirect(reverse('all-projects'), permanent = False)
+ if Build.objects.count() == 0 and Project.objects.count() > 0:
+ return redirect(reverse('all-projects'), permanent = False)
- if BuildRequest.objects.all().count() > 0:
- return redirect(reverse('all-builds'), permanent = False)
- else:
- if Build.objects.all().count() > 0:
- return redirect(reverse('all-builds'), permanent = False)
+ if Build.objects.all().count() > 0:
+ return redirect(reverse('all-builds'), permanent = False)
- context = {}
- if toastermain.settings.MANAGED:
- context['lvs_nos'] = Layer_Version.objects.all().count()
+ context = {'lvs_nos' : Layer_Version.objects.all().count()}
return render(request, 'landing.html', context)
# returns a list for most recent builds; for use in the Project page, xhr_ updates, and other places, as needed
def _project_recent_build_list(prj):
- return map(lambda x: {
+ data = []
+ # take the most recent 3 completed builds, plus any builds in progress
+ for x in itertools.chain(prj.build_set.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3], prj.build_set.filter(outcome=Build.IN_PROGRESS).order_by("-pk")):
+ d = {
"id": x.pk,
- "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()),
- "updated": x.updated.strftime('%s')+"000",
- "command_time": (x.updated - x.created).total_seconds(),
+ "targets" : map(lambda y: {"target": y.target, "task": None }, x.target_set.all()), # TODO: create the task entry in the Target table
+ "status": x.get_outcome_display(),
+ "errors": map(lambda y: {"type": y.lineno, "msg": y.message, "tb": y.pathname}, x.logmessage_set.filter(level__gte=LogMessage.WARNING)),
+ "updated": x.completed_on.strftime('%s')+"000",
+ "command_time": (x.completed_on - x.started_on).total_seconds(),
"br_page_url": reverse('buildrequestdetails', args=(x.project.id, x.pk) ),
"build" : map( lambda y: {"id": y.pk,
"status": y.get_outcome_display(),
@@ -82,9 +78,11 @@ def _project_recent_build_list(prj):
"warnings": y.warnings_no,
"completeper": y.completeper() if y.outcome == Build.IN_PROGRESS else "0",
"eta": y.eta().strftime('%s')+"000" if y.outcome == Build.IN_PROGRESS else "0",
- }, Build.objects.filter(buildrequest = x)),
- }, list(prj.buildrequest_set.filter(Q(state__lt=BuildRequest.REQ_COMPLETED) or Q(state=BuildRequest.REQ_DELETED)).order_by("-pk")) +
- list(prj.buildrequest_set.filter(state__in=[BuildRequest.REQ_COMPLETED, BuildRequest.REQ_FAILED]).order_by("-pk")[:3]))
+ }, [x]),
+ }
+ data.append(d)
+
+ return data
@@ -1372,15 +1370,6 @@ def configvars(request, build_id):
file_filter += '/bitbake.conf'
build_dir=re.sub("/tmp/log/.*","",Build.objects.get(pk=build_id).cooker_log_path)
- clones = []
- for breq in BuildRequest.objects.filter(build_id=build_id):
- bc = bbcontroller.getBuildEnvironmentController(pk = breq.environment.id)
- for brl in breq.brlayer_set.all():
- localdirname = bc.getGitCloneDirectory(brl.giturl, brl.commit)
- if not localdirname.startswith("/"):
- localdirname = os.path.join(bc.be.sourcedir, localdirname)
- clones.append(localdirname)
-
context = {
'objectname': 'configvars',
'object_search_display':'BitBake variables',
@@ -1391,7 +1380,6 @@ def configvars(request, build_id):
'total_count':queryset_with_search.count(),
'default_orderby' : 'variable_name:+',
'search_term':search_term,
- 'dirstostrip': clones + [dirname(build_dir), dirname(dirname(build_dir))],
# Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns
'tablecols' : [
{'name': 'Variable',
@@ -1872,7 +1860,7 @@ if True:
# shows the "all builds" page for managed mode; it displays build requests (at least started!) instead of actual builds
- @_template_renderer("managed_builds.html")
+ @_template_renderer("builds.html")
def builds(request):
# 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
@@ -2184,10 +2172,7 @@ if True:
# Shows the edit project page
@_template_renderer('project.html')
def project(request, pid):
- try:
- prj = Project.objects.get(id = pid)
- except Project.DoesNotExist:
- return HttpResponseNotFound("<h1>Project id " + pid + " is unavailable</h1>")
+ prj = Project.objects.get(id = pid)
try:
puser = User.objects.get(id = prj.user_id)
@@ -2249,8 +2234,8 @@ if True:
context = {
"project" : prj,
"lvs_nos" : Layer_Version.objects.all().count(),
- "completedbuilds": BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED),
- "prj" : {"name": prj.name, "release": { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}},
+ "completedbuilds": Build.objects.filter(project_id = pid).filter(outcome__lte = Build.IN_PROGRESS),
+ "prj" : {"name": prj.name, },
#"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED),
"builds" : _project_recent_build_list(prj),
"layers" : map(lambda x: {
@@ -2270,6 +2255,9 @@ if True:
"project_html": 1,
}
+ if prj.release is not None:
+ context["prj"]["release"] = { "id": prj.release.pk, "name": prj.release.name, "desc": prj.release.description}
+
try:
context["machine"] = {"name": prj.projectvariable_set.get(name="MACHINE").value}
except ProjectVariable.DoesNotExist:
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 07/23] toaster: refactor the builds pages
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (5 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 06/23] toaster: remove BuildRequest references Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 08/23] toaster: fill in build data from buildrequest Alex DAMIAN
` (15 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
Taking out the managed mode-specific bits in build-related
pages, as there is always only one mode available.
Also refactors the build pages in order to always display
Build objects instead of BuildRequest objects.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/toastergui/static/js/projectapp.js | 14 +-
.../templates/{build.html => builds.html} | 7 +-
.../toastergui/templates/managed_builds.html | 167 --------
.../toastergui/templates/managed_mrb_section.html | 193 ---------
lib/toaster/toastergui/templates/mrb_section.html | 41 +-
lib/toaster/toastergui/templates/project.html | 40 +-
.../toastergui/templates/projectbuilds.html | 1 -
lib/toaster/toastergui/templates/projects.html | 22 +-
lib/toaster/toastergui/urls.py | 2 +-
lib/toaster/toastergui/views.py | 437 +++++----------------
10 files changed, 175 insertions(+), 749 deletions(-)
rename lib/toaster/toastergui/templates/{build.html => builds.html} (97%)
delete mode 100644 lib/toaster/toastergui/templates/managed_builds.html
delete mode 100644 lib/toaster/toastergui/templates/managed_mrb_section.html
diff --git a/lib/toaster/toastergui/static/js/projectapp.js b/lib/toaster/toastergui/static/js/projectapp.js
index b2e65c5..40e2e1f 100644
--- a/lib/toaster/toastergui/static/js/projectapp.js
+++ b/lib/toaster/toastergui/static/js/projectapp.js
@@ -429,13 +429,17 @@ projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $loc
*/
$scope.validateData = function () {
- if ($scope.layers.length === 0) {
+ if ($scope.project.release) {
+ if ($scope.layers.length === 0) {
$scope.layeralert = $scope.displayAlert($scope.zone1alerts, "You need to add some layers to this project. <a href=\""+$scope.urls.layers+"\">View all layers available in Toaster</a> or <a href=\""+$scope.urls.importlayer+"\">import a layer</a>");
- } else {
- if ($scope.layeralert !== undefined) {
- $scope.layeralert.close();
- $scope.layeralert = undefined;
+ } else {
+ if ($scope.layeralert !== undefined) {
+ $scope.layeralert.close();
+ $scope.layeralert = undefined;
+ }
}
+ } else {
+ $scope.layeralert = $scope.displayAlert($scope.zone1alerts, "This project is not set to run builds.");
}
};
diff --git a/lib/toaster/toastergui/templates/build.html b/lib/toaster/toastergui/templates/builds.html
similarity index 97%
rename from lib/toaster/toastergui/templates/build.html
rename to lib/toaster/toastergui/templates/builds.html
index f0b5ea5..e9211af 100644
--- a/lib/toaster/toastergui/templates/build.html
+++ b/lib/toaster/toastergui/templates/builds.html
@@ -14,13 +14,15 @@
{% block pagecontent %}
+{% if last_date_from and last_date_to %}
<script>
- // intiialize the date range controls
+ // initialize the date range controls
$(document).ready(function () {
date_init('started_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_started_on}}','{{dateMax_started_on}}','{{daterange_selected}}');
date_init('completed_on','{{last_date_from}}','{{last_date_to}}','{{dateMin_completed_on}}','{{dateMax_completed_on}}','{{daterange_selected}}');
});
</script>
+{%endif%} {# last_date_from and last_date_to #}
<div class="row-fluid">
@@ -87,9 +89,8 @@
<a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
{% endif %}
</td>
- {% if build.project %}
+ <td>
<a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
- {% endif %}
</td>
</tr>
diff --git a/lib/toaster/toastergui/templates/managed_builds.html b/lib/toaster/toastergui/templates/managed_builds.html
deleted file mode 100644
index 63ae540..0000000
--- a/lib/toaster/toastergui/templates/managed_builds.html
+++ /dev/null
@@ -1,167 +0,0 @@
-{% extends "base.html" %}
-
-{% load static %}
-{% load projecttags %}
-{% load humanize %}
-
-{% block extraheadcontent %}
-<link rel="stylesheet" href="/static/css/jquery-ui.min.css" type='text/css'>
-<link rel="stylesheet" href="/static/css/jquery-ui.structure.min.css" type='text/css'>
-<link rel="stylesheet" href="/static/css/jquery-ui.theme.min.css" type='text/css'>
-<script src="/static/js/jquery-ui.min.js"></script>
-<script src="/static/js/filtersnippet.js"></script>
-{% endblock %}
-
-{% block pagecontent %}
-
-<script>
- // initialize the date range controls
- $(document).ready(function () {
- date_init('created','{{last_date_from}}','{{last_date_to}}','{{dateMin_created}}','{{dateMax_created}}','{{daterange_selected}}');
- date_init('updated','{{last_date_from}}','{{last_date_to}}','{{dateMin_updated}}','{{dateMax_updated}}','{{daterange_selected}}');
- });
-</script>
-
-<div class="row-fluid">
-
- {% include "managed_mrb_section.html" %}
-
-
- {% if 1 %}
- <div class="page-header top-air">
- <h1>
- {% if request.GET.filter and objects.paginator.count > 0 or request.GET.search and objects.paginator.count > 0 %}
- {{objects.paginator.count}} build{{objects.paginator.count|pluralize}} found
- {%elif request.GET.filter and objects.paginator.count == 0 or request.GET.search and objects.paginator.count == 0 %}
- No builds found
- {%else%}
- All builds
- {%endif%}
- </h1>
- </div>
-
- {% if objects.paginator.count == 0 %}
- <div class="row-fluid">
- {% if request.GET.filter or request.GET.search %}
- <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 builds</button>
- </form>
- </div>
- {% else %}
- <div class="alert alert-info">
- <p class="lead">Toaster has not recorded any builds yet. To run a build, <a href="{% url 'all-projects' %}">select the project</a> for which you want to build.
- </div>
- {% endif %}
- </div>
-
-
- {% else %} {# We have builds to display #}
- {% include "basetable_top_buildprojects.html" %}
- <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work -->
- {% for buildrequest in objects %}{% if buildrequest.build %} {% with build=buildrequest.build %} {# if we have a build, just display it #}
- <tr class="data">
- <td class="outcome"><a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a>
- {% if build.project %}
- <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}">
- <i class="icon-download-alt" title="" data-original-title="Download build log"></i>
- </a>
- {% endif %}
-
- </td>
- <td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td>
- <td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td>
- {% if MANAGED %}
- <td class="project">
- {% if build.project %}
- <a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
- {% endif %}
- </td>
- {% endif %}
- <td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td>
- <td class="completed_on"><a href="{% url "builddashboard" build.id %}">{{build.completed_on|date:"d/m/y H:i"}}</a></td>
- <td class="failed_tasks error">
- {% query build.task_build outcome=4 order__gt=0 as exectask%}
- {% if exectask.count == 1 %}
- <a href="{% url "task" build.id exectask.0.id %}">{{exectask.0.recipe.name}}.{{exectask.0.task_name}}</a>
- {% if MANAGED and build.project %}
- <a href="{% url 'build_artifact' build.id "tasklogfile" exectask.0.id %}">
- <i class="icon-download-alt" title="" data-original-title="Download task log file"></i>
- </a>
- {% endif %}
- {% elif exectask.count > 1%}
- <a href="{% url "tasks" build.id %}?filter=outcome%3A4">{{exectask.count}} task{{exectask.count|pluralize}}</a>
- {%endif%}
- </td>
- <td class="errors_no">
- {% if build.errors_no %}
- <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
- {% if MANAGED and build.project and build.buildartifact_set.count %}
- <a href="{% url 'build_artifact' build.id "cookerlog" build.id %}">
- <i class="icon-download-alt" title="" data-original-title="Download build log"></i>
- </a>
- {% endif %}
- {%endif%}
- </td>
- <td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td>
- <td class="time"><a href="{% url "buildtime" build.id %}">{{build.timespent|sectohms}}</a></td>
- {% if not MANAGED or not build.project %}
- <td class="log">{{build.cooker_log_path}}</td>
- {% endif %}
- <td class="output">
- {% if build.outcome == build.SUCCEEDED %}
- <a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
- {% endif %}
- </td>
- </tr>
-
-
- {%endwith%}
- {% else %} {# we don't have a build for this build request, mask the data with build request data #}
-
-
-
- <tr class="data">
- <td class="outcome">{% if buildrequest.state == buildrequest.REQ_FAILED %}<i class="icon-minus-sign error"></i>{%else%}FIXME_build_request_state{%endif%}</td>
- <td class="target">
- <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}"><span data-toggle="tooltip" {%if buildrequest.brtarget_set.all.count > 1%}title="Targets: {%for target in buildrequest.brtarget_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{buildrequest.brtarget_set.all.0.target}} {%if buildrequest.brtarget_set.all.count > 1%}(+ {{buildrequest.brtarget_set.all.count|add:"-1"}}){%endif%} </span></a>
- </td>
- <td class="machine">
- <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.machine}}</a>
- </td>
- {% if MANAGED %}
- <td class="project">
- <a href="{% url 'project' buildrequest.project.id %}">{{buildrequest.project.name}}</a>
- </td>
- {% endif %}
- <td class="started_on">
- <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.created|date:"d/m/y H:i"}}</a>
- </td>
- <td class="completed_on">
- <a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.updated|date:"d/m/y H:i"}}</a>
- </td>
- <td class="failed_tasks error">
- </td>
- <td class="errors_no">
- <a class="errors_no error" href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}#errors">{{buildrequest.brerror_set.all.count}} error{{buildrequest.brerror_set.all.count|pluralize}}</a>
- </td>
- <td class="warnings_no">
- </td>
- <td class="time">
- {{br.timespent.total_seconds|sectohms}}
- </td>
- <td class="output"> {# we have no output here #}
- </td>
- </tr>
- {%endif%}
- {% endfor %}
-
-
- {% include "basetable_bottom.html" %}
- {% endif %} {# objects.paginator.count #}
-{% endif %} {# empty #}
-</div><!-- end row-fluid-->
-
-{% endblock %}
diff --git a/lib/toaster/toastergui/templates/managed_mrb_section.html b/lib/toaster/toastergui/templates/managed_mrb_section.html
deleted file mode 100644
index 47e64ea..0000000
--- a/lib/toaster/toastergui/templates/managed_mrb_section.html
+++ /dev/null
@@ -1,193 +0,0 @@
-{% load static %}
-{% load projecttags %}
-{% load humanize %}
-
-{%if mru|length > 0%}
-{# Template provides the latest builds section requires mru in the context which can be added from _managed_get_latest_builds #}
- <div class="page-header top-air">
- <h1>
- Latest builds
- </h1>
- </div>
- <div id="latest-builds">
- {% for buildrequest in mru %}{% with build=buildrequest.build %}
-
- {% if build %} {# if we have a build, just display it #}
-
- <div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%} {% if MANAGED and build.project %}project-name{% endif %} ">
- {% if MANAGED and build.project %}
- <span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-important{%else%}label-info{%endif%}">
- <a href="{% url 'project' build.project.id %}"> {{build.project.name}} </a>
- </span>
- {% endif %}
-
- <div class="row-fluid">
- <div class="span3 lead">
- {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
- <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
- {% endif %}
- {% include "brtargets.html" %}
- {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
- </a>
- {% endif %}
- </div>
- {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
- <div class="span2 lead">
- {% if build.completed_on|format_build_date %}
- {{ build.completed_on|date:'d/m/y H:i' }}
- {% else %}
- {{ build.completed_on|date:'H:i' }}
- {% endif %}
- </div>
- <div class="span2 lead">
- {% if build.errors_no %}
- <i class="icon-minus-sign red"></i> <a href="{%url 'builddashboard' build.pk%}#errors" class="error">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
- {% endif %}
- </div>
- <div class="span2 lead">
- {% if build.warnings_no %}
- <i class="icon-warning-sign yellow"></i> <a href="{%url 'builddashboard' build.pk%}#warnings" class="warning">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>
- {% endif %}
- </div>
- <div class="lead ">
- <span class="lead{%if not MANAGED or not build.project%} pull-right{%endif%}">
- Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a>
- </span>
- {% if build.project %}
- <button class="btn
- {% if build.outcome == build.SUCCEEDED %}
- btn-success
- {% elif build.outcome == build.FAILED %}
- btn-danger
- {% else %}
- btn-info
- {%endif%}
- pull-right"
- {% include "runagain.html" %}
- </button>
- {% endif %}
- </div>
- {%endif%}
- {%if build.outcome == build.IN_PROGRESS %}
- <div class="span4 offset1">
- <div class="progress" style="margin-top:5px;" data-toggle="tooltip" title="{{build.completeper}}% of tasks complete">
- <div style="width: {{build.completeper}}%;" class="bar"></div>
- </div>
- </div>
- <div class="lead pull-right">{{build.completeper}}% tasks completed</div>
- {%endif%}
- </div>
- </div>
-
- {% else %} {# we use the project's page recent build design #}
-
-
-
-
- <div class="alert {% if buildrequest.state == buildrequest.REQ_FAILED %}alert-error{% else %}alert-info{% endif %} project-name">
- <span class="label {% if buildrequest.state == buildrequest.REQ_FAILED %}label-important{% else%}label-info{% endif %}">
- <a href="{% url 'project' buildrequest.project.id %}"> {{buildrequest.project.name}} </a>
- </span>
- <div class="row-fluid">
-
- {% if buildrequest.state == buildrequest.REQ_FAILED %}
- <div class="span3 lead">
- <a href="{%url 'buildrequestdetails' buildrequest.project.id buildrequest.pk%}" class="error">
- {% include "brtargets.html" %}
- </a>
- </div>
- <div class="span2 lead">
- {% if buildrequest.updated|format_build_date %}
- {{ buildrequest.updated|date:'d/m/y H:i' }}
- {% else %}
- {{ buildrequest.updated|date:'H:i' }}
- {% endif %}
- </div>
- <div class="span2 lead">
- {% if buildrequest.brerror_set.all.count %}
- <i class="icon-minus-sign red"></i> <a href="{%url 'buildrequestdetails' buildrequest.project.id buildrequest.pk %}#errors" class="error">{{buildrequest.brerror_set.all.count}} error{{buildrequest.brerror_set.all.count|pluralize}}</a>
- {% endif %}
- </div>
- <div class="span2 lead"> {# there are no warnings for buildrequests #}
- </div>
- <div class="lead ">
- <span class="lead{%if not MANAGED or not buildrequest.project%} pull-right{%endif%}">
- Build time: {{ buildrequest.get_duration|sectohms }}
- </span>
-
- <button class="btn btn-danger pull-right"
- {% include "runagain.html" %}
- </button>
- </div>
-
-
- {% elif buildrequest.state == buildrequest.REQ_QUEUED %}
-
- <div class="lead span5">
- {% include "brtargets.html" %}
- </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 btn-info pull-right cancel-build-btn" data-build-id="{{buildrequest.id}}" data-request-url="{% url 'projectbuilds' buildrequest.project.id %}" >Cancel</button>
-
- {% elif buildrequest.state == buildrequest.REQ_CREATED %}
-
- <div class="lead span3">
- {% include "brtargets.html" %}
- </div>
- <div class="span6" >
- <span class="lead">Creating build</span>
- </div>
-
- {% elif buildrequest.state == buildrequest.REQ_INPROGRESS %}
-
- <div class="lead span5">
- {% include "brtargets.html" %}
- </div>
- <div class="span4 lead">
- Checking out layers
- </div>
- {% else %}
-
- <div>FIXME!</div>
-
- {% endif %}
- <div class="lead pull-right">
- </div>
- </div>
- </div>
-
-
-
- {% endif %} {# this ends the build request most recent build section #}
-
-{%endwith%}{% endfor %}
- </div>
-
-<script>
-
-function scheduleBuild(url, projectName, projectUrl, buildlist) {
- console.log("scheduleBuild");
- libtoaster.startABuild(url, null, buildlist.join(" "), function(){
- window.location.reload();
- }, null);
-}
-
-$(document).ready(function(){
-
- $(".cancel-build-btn").click(function (){
- var url = $(this).data('request-url');
- var buildIds = $(this).data('build-id');
- var btn = $(this);
-
- libtoaster.cancelABuild(url, buildIds, function(){
- btn.parents(".alert").fadeOut();
- }, null);
- });
-});
-
-</script>
-
-{%endif%}
-
diff --git a/lib/toaster/toastergui/templates/mrb_section.html b/lib/toaster/toastergui/templates/mrb_section.html
index c7bddf0..7e84e41 100644
--- a/lib/toaster/toastergui/templates/mrb_section.html
+++ b/lib/toaster/toastergui/templates/mrb_section.html
@@ -12,18 +12,24 @@
</div>
<div id="latest-builds">
{% for build in mru %}
- <div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%} {% if MANAGED and build.project %}project-name{% endif %} ">
- {% if MANAGED and build.project %}
+ <div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%} project-name ">
<span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-danger{%else%}label-info{%endif%}"> {{build.project.name}} </span>
- {% endif %}
<div class="row-fluid">
<div class="span3 lead">
{%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
<a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
{% endif %}
- <span data-toggle="tooltip" {%if build.target_set.all.count > 1%}title="Targets: {%for target in build.target_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%}
- </span>
+ {% if build.target_set.all.count > 0 %}
+ <span data-toggle="tooltip"
+ {%if build.target_set.all.count > 1%}
+ title="Targets: {%for target in build.target_set.all%}{{target.target}} {%endfor%}"
+ {%endif%}
+ >
+
+ {{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%}
+ </span>
+ {% endif %}
{%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
</a>
{% endif %}
@@ -65,5 +71,30 @@
{% endfor %}
</div>
+
+<script>
+
+function scheduleBuild(url, projectName, projectUrl, buildlist) {
+ console.log("scheduleBuild");
+ libtoaster.startABuild(url, null, buildlist.join(" "), function(){
+ window.location.reload();
+ }, null);
+}
+
+$(document).ready(function(){
+
+ $(".cancel-build-btn").click(function (){
+ var url = $(this).data('request-url');
+ var buildIds = $(this).data('build-id');
+ var btn = $(this);
+
+ libtoaster.cancelABuild(url, buildIds, function(){
+ btn.parents(".alert").fadeOut();
+ }, null);
+ });
+});
+
+</script>
+
{%endif%}
diff --git a/lib/toaster/toastergui/templates/project.html b/lib/toaster/toastergui/templates/project.html
index bca703a..0f6a77b 100644
--- a/lib/toaster/toastergui/templates/project.html
+++ b/lib/toaster/toastergui/templates/project.html
@@ -124,7 +124,7 @@ vim: expandtab tabstop=2
<div class="well">
<form class="build-form" data-ng-submit="buildNamedTarget()">
<div class="input-append controls">
- <input type="text" class="huge input-xxlarge" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-ng-model="targetName" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!layers.length"/>
+ <input type="text" class="huge input-xxlarge" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-ng-model="targetName" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!project.release || !layers.length"/>
<button type="submit" class="btn btn-large btn-primary" data-ng-disabled="!targetName.length">
Build
</button>
@@ -145,11 +145,11 @@ vim: expandtab tabstop=2
<a id="buildslist"></a>
<h2 class="air" data-ng-if="builds.length">Latest builds</h2>
- <div class="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-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="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-ng-class="{'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.status]">
<div class="row-fluid">
<switch data-ng-switch="b.status">
- <case data-ng-switch-when="failed">
+ <case data-ng-switch-when="Failed">
<div class="lead span3">
<a data-ng-class="{'succeeded': 'success', 'failed': 'error'}[b.status]" href="{[b.br_page_url]}">
<span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span>
@@ -182,32 +182,8 @@ vim: expandtab tabstop=2
</div>
</case>
- <case data-ng-switch-when="queued">
- <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-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" data-ng-click="buildCancel(b)">Cancel</button>
- </case>
-
- <case data-ng-switch-when="created">
- <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
- <div class="span4">
- <span class="lead">Creating build</span>
- </div>
- <button class="btn pull-right btn-info" data-ng-click="buildCancel(b)">Cancel</button>
- </case>
-
- <case data-ng-switch-when="deleted">
- <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
- <div class="span4" id="{[b.id]}-deleted" >
- <span class="lead">Build cancelled</span>
- </div>
- <button class="btn pull-right btn-info" data-ng-click="buildDelete(b)">Close</button>
- </case>
-
- <case data-ng-switch-when="in progress">
+ <case data-ng-switch-when="In Progress">
<switch data-ng-switch="b.build.length">
<case data-ng-switch-when="0">
<div class="lead span5"> <span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span> </div>
@@ -227,7 +203,7 @@ vim: expandtab tabstop=2
</case>
- <case data-ng-switch-when="completed">
+ <case data-ng-switch-when="Succeeded">
<div class="lead span3">
<a data-ng-class="{'Succeeded': 'success', 'Failed': 'error'}[b.build[0].status]" href="{[b.build[0].build_page_url]}">
<span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span>
@@ -293,7 +269,7 @@ vim: expandtab tabstop=2
Layers <span class="muted counter">({[layers.length]})</span>
<i class="icon-question-sign get-help heading-help" title="Bitbake reads metadata files from modules called 'layers'. Layers allow you to isolate different types of customizations from each other. <a href='http://www.yoctoproject.org/docs/current/dev-manual/dev-manual.html#understanding-and-creating-layers' target='_blank'>More on layers</a>"></i>
</h3>
- <div class="alert" data-ng-if="!layers.length">
+ <div class="alert" data-ng-if="project.release && !layers.length">
<b>You need to add some layers </b>
<p>
You can:
@@ -307,7 +283,7 @@ vim: expandtab tabstop=2
</div>
<form data-ng-submit="layerAdd()">
<div class="input-append">
- <input type="text" class="input-xlarge" id="layer" autocomplete="off" placeholder="Type a layer name" data-minLength="1" data-ng-model="layerAddName" data-typeahead="e for e in getLayersAutocompleteSuggestions($viewValue)" data-typeahead-template-url="layers_suggestion_details" data-typeahead-on-select="onLayerSelect($item, $model, $label)" data-typeahead-editable="false" data-ng-class="{ 'has-error': layerAddName.$invalid }" />
+ <input type="text" class="input-xlarge" id="layer" autocomplete="off" placeholder="Type a layer name" data-minLength="1" data-ng-model="layerAddName" data-typeahead="e for e in getLayersAutocompleteSuggestions($viewValue)" data-typeahead-template-url="layers_suggestion_details" data-typeahead-on-select="onLayerSelect($item, $model, $label)" data-typeahead-editable="false" data-ng-class="{ 'has-error': layerAddName.$invalid }" data-ng-disabled="!project.release" />
<input type="submit" id="add-layer" class="btn" value="Add" data-ng-disabled="!layerAddName.length"/>
</div>
{% csrf_token %}
@@ -334,7 +310,7 @@ vim: expandtab tabstop=2
</h3>
<form data-ng-submit="buildNamedTarget()">
<div class="input-append">
- <input type="text" class="input-xlarge" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-minLength="1" data-ng-model="targetName1" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!layers.length">
+ <input type="text" class="input-xlarge" placeholder="Type the recipe(s) you want to build" autocomplete="off" data-minLength="1" data-ng-model="targetName1" data-typeahead="a.name for a in getRecipesAutocompleteSuggestions($viewValue)" data-typeahead-template-url="recipes_suggestion_details" data-ng-disabled="!project.release || !layers.length">
<button type="submit" class="btn btn-primary" data-ng-disabled="!targetName1.length">
Build </button>
</div>
diff --git a/lib/toaster/toastergui/templates/projectbuilds.html b/lib/toaster/toastergui/templates/projectbuilds.html
index 18b2495..896c3b5 100644
--- a/lib/toaster/toastergui/templates/projectbuilds.html
+++ b/lib/toaster/toastergui/templates/projectbuilds.html
@@ -105,7 +105,6 @@
</td>
</tr>
-
{%endwith%}
{% else %} {# we don't have a build for this build request, mask the data with build request data #}
diff --git a/lib/toaster/toastergui/templates/projects.html b/lib/toaster/toastergui/templates/projects.html
index 2334008..9c4346c 100644
--- a/lib/toaster/toastergui/templates/projects.html
+++ b/lib/toaster/toastergui/templates/projects.html
@@ -7,7 +7,7 @@
{% block pagecontent %}
- {% include "managed_mrb_section.html" %}
+ {% include "mrb_section.html" %}
<div class="page-header top-air">
@@ -39,7 +39,13 @@
<tr class="data">
<td><a href="{% url 'project' o.id %}">{{o.name}}</a></td>
<td class="updated"><a href="{% url 'project' o.id %}">{{o.updated|date:"d/m/y H:i"}}</a></td>
- <td><a href="{% url 'project' o.id %}#project-details">{{o.release.name}}</a></td>
+ <td>
+ {% if o.release %}
+ <a href="{% url 'project' o.id %}#project-details">{{o.release.name}}</a>
+ {% else %}
+ No release available
+ {% endif %}
+ </td>
<td><a href="{% url 'project' o.id %}#machine-distro">{{o.get_current_machine_name}}</a></td>
{% if o.get_number_of_builds == 0 %}
<td class="muted">{{o.get_number_of_builds}}</td>
@@ -52,13 +58,13 @@
<td><a href="{% url 'projectbuilds' o.id %}">{{o.get_number_of_builds}}</a></td>
<td class="loutcome"><a href="{% url "builddashboard" o.get_last_build_id %}">{%if o.get_last_outcome == build_SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif o.get_last_outcome == build_FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a></td>
<td class="ltarget"><a href="{% url "builddashboard" o.get_last_build_id %}">{{o.get_last_target}} </a></td>
- <td class="lerrors">{% if o.get_last_errors %}<a class="errors_no error" href="{% url "builddashboard" o.get_last_build_id %}#errors">{{o.get_last_errors}} error{{o.get_last_errors|pluralize}}</a>{%endif%}</td>
- <td class="lwarnings">{% if o.get_last_warnings %}<a class="warnings_no warning" href="{% url "builddashboard" o.get_last_build_id %}#warnings">{{o.get_last_warnings}} warning{{o.get_last_warnings|pluralize}}</a>{%endif%}</td>
+ <td class="lerrors">{% if o.get_last_errors %}<a class="errors_no error" href="{% url "builddashboard" o.get_last_build_id %}#errors">{{o.get_last_errors}} error{{o.get_last_errors|pluralize}}</a>{%endif%}</td>
+ <td class="lwarnings">{% if o.get_last_warnings %}<a class="warnings_no warning" href="{% url "builddashboard" o.get_last_build_id %}#warnings">{{o.get_last_warnings}} warning{{o.get_last_warnings|pluralize}}</a>{%endif%}</td>
<td class="limagefiles">
- {% if o.get_last_outcome == build_SUCCEEDED %}
- <a href="{%url "builddashboard" o.get_last_build_id %}#images">{{fstypes|get_dict_value:o.id}}</a>
- {% endif %}
- </td>
+ {% if o.get_last_outcome == build_SUCCEEDED %}
+ <a href="{%url "builddashboard" o.get_last_build_id %}#images">{{fstypes|get_dict_value:o.id}}</a>
+ {% endif %}
+ </td>
{% endif %}
</tr>
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index bd3eb40..681f067 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -132,7 +132,7 @@ urlpatterns = patterns('toastergui.views',
url(r'^xhr_updatelayer/$', 'xhr_updatelayer', name='xhr_updatelayer'),
# dashboard for failed build requests
- url(r'^project/(?P<pid>\d+)/buildrequest/(?P<brid>\d+)$', 'buildrequestdetails', name='buildrequestdetails'),
+ url(r'^project/(?P<pid>\d+)/buildrequest/(?P<bid>\d+)$', 'buildrequestdetails', name='buildrequestdetails'),
# default redirection
url(r'^$', RedirectView.as_view( url= 'landing')),
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 060d680..0324d17 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -55,11 +55,23 @@ def landing(request):
return render(request, 'landing.html', context)
-# returns a list for most recent builds; for use in the Project page, xhr_ updates, and other places, as needed
+
+
+# returns a list for most recent builds;
+def _get_latest_builds(prj=None):
+ queryset = Build.objects.all()
+
+ if prj is not None:
+ queryset = queryset.filter(project = prj)
+
+ return itertools.chain(queryset.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3], queryset.filter(outcome=Build.IN_PROGRESS).order_by("-pk"))
+
+
+# a JSON-able dict of recent builds; for use in the Project page, xhr_ updates, and other places, as needed
def _project_recent_build_list(prj):
data = []
# take the most recent 3 completed builds, plus any builds in progress
- for x in itertools.chain(prj.build_set.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3], prj.build_set.filter(outcome=Build.IN_PROGRESS).order_by("-pk")):
+ for x in _get_latest_builds(prj):
d = {
"id": x.pk,
"targets" : map(lambda y: {"target": y.target, "task": None }, x.target_set.all()), # TODO: create the task entry in the Target table
@@ -1866,10 +1878,10 @@ if True:
# be able to display something. 'count' and 'page' are mandatory for all views
# that use paginators.
- buildrequests = BuildRequest.objects.exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED)
+ queryset = Build.objects.filter(outcome__lte = Build.IN_PROGRESS)
try:
- context, pagesize, orderby = _build_list_helper(request, buildrequests, True)
+ context, pagesize, orderby = _build_list_helper(request, queryset)
except InvalidRequestException as e:
raise RedirectException( builds, request.GET, e.response)
@@ -1878,66 +1890,37 @@ if True:
# helper function, to be used on "all builds" and "project builds" pages
- def _build_list_helper(request, buildrequests, insert_projects):
- # ATTN: we use here the ordering parameters for interactive mode; the translation for BuildRequest fields will happen below
+ def _build_list_helper(request, queryset_all):
+
default_orderby = 'completed_on:-'
(pagesize, orderby) = _get_parameters_values(request, 10, default_orderby)
mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby }
retval = _verify_parameters( request.GET, mandatory_parameters )
if retval:
- raise InvalidRequestException(mandatory_parameters)
-
- orig_orderby = orderby
- # translate interactive mode ordering to managed mode ordering
- ordering_params = orderby.split(":")
- if ordering_params[0] == "completed_on":
- ordering_params[0] = "updated"
- if ordering_params[0] == "started_on":
- ordering_params[0] = "created"
- if ordering_params[0] == "errors_no":
- ordering_params[0] = "build__errors_no"
- if ordering_params[0] == "warnings_no":
- ordering_params[0] = "build__warnings_no"
- if ordering_params[0] == "machine":
- ordering_params[0] = "build__machine"
- if ordering_params[0] == "target__target":
- ordering_params[0] = "brtarget__target"
- if ordering_params[0] == "timespent":
- ordering_params[0] = "id"
- orderby = default_orderby
-
- request.GET = request.GET.copy() # get a mutable copy of the GET QueryDict
- request.GET['orderby'] = ":".join(ordering_params)
+ raise RedirectException( 'all-builds', 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, BuildRequest)
+ (filter_string, search_term, ordering_string) = _search_tuple(request, Build)
# post-process any date range filters
filter_string,daterange_selected = _modify_date_range_filter(filter_string)
-
- # we don't display in-progress or deleted builds
- queryset_all = buildrequests.exclude(state = BuildRequest.REQ_DELETED)
- queryset_all = queryset_all.select_related("build", "build__project").annotate(Count('brerror'))
- queryset_with_search = _get_queryset(BuildRequest, queryset_all, filter_string, search_term, ordering_string, '-updated')
-
+ queryset_all = queryset_all.select_related("project")
+ queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on')
+ queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on')
# retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display
- build_info = _build_page_range(Paginator(queryset_with_search, pagesize), request.GET.get('page', 1))
+ build_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1))
# build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
- # most recent build is like projects' most recent builds, but across all projects
- build_mru = _managed_get_latest_builds()
+ build_mru = Build.objects.order_by("-started_on")[:3]
+
+ # calculate the exact begining of local today and yesterday, append context
+ context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'started_on','completed_on'})
+ # set up list of fstypes for each build
fstypes_map = {};
- for build_request in build_info:
- # set display variables for build request
- build_request.machine = build_request.brvariable_set.get(name="MACHINE").value
- build_request.timespent = build_request.updated - build_request.created
-
- # set up list of fstypes for each build
- if build_request.build is None:
- continue
- targets = Target.objects.filter( build_id = build_request.build.id )
+ for build in build_info:
+ targets = Target.objects.filter( build_id = build.id )
comma = "";
extensions = "";
for t in targets:
@@ -1951,8 +1934,7 @@ if True:
if None == re.search(s,extensions):
extensions += comma + s
comma = ", "
- fstypes_map[build_request.build.id]=extensions
-
+ fstypes_map[build.id]=extensions
# send the data to the template
context = {
@@ -1961,7 +1943,7 @@ if True:
# TODO: common objects for all table views, adapt as needed
'objects' : build_info,
'objectname' : "builds",
- 'default_orderby' : 'updated:-',
+ 'default_orderby' : default_orderby,
'fstypes' : fstypes_map,
'search_term' : search_term,
'total_count' : queryset_with_search.count(),
@@ -1971,151 +1953,137 @@ if True:
{'name': 'Outcome', # column with a single filter
'qhelp' : "The outcome tells you if a build successfully completed or failed", # the help button content
'dclass' : "span2", # indication about column width; comes from the design
- 'orderfield': _get_toggle_order(request, "state"), # adds ordering by the field value; default ascending unless clicked from ascending into descending
- 'ordericon':_get_toggle_order_icon(request, "state"),
+ 'orderfield': _get_toggle_order(request, "outcome"), # adds ordering by the field value; default ascending unless clicked from ascending into descending
+ 'ordericon':_get_toggle_order_icon(request, "outcome"),
# filter field will set a filter on that column with the specs in the filter description
# the class field in the filter has no relation with clclass; the control different aspects of the UI
# still, it is recommended for the values to be identical for easy tracking in the generated HTML
'filter' : {'class' : 'outcome',
'label': 'Show:',
'options' : [
- ('Successful builds', 'build__outcome:' + str(Build.SUCCEEDED), queryset_all.filter(build__outcome = Build.SUCCEEDED).count()), # this is the field search expression
- ('Failed builds', 'build__outcome:NOT'+ str(Build.SUCCEEDED), queryset_all.exclude(build__outcome = Build.SUCCEEDED).count()),
+ ('Successful builds', 'outcome:' + str(Build.SUCCEEDED), queryset_with_search.filter(outcome=str(Build.SUCCEEDED)).count()), # this is the field search expression
+ ('Failed builds', 'outcome:'+ str(Build.FAILED), queryset_with_search.filter(outcome=str(Build.FAILED)).count()),
]
}
},
{'name': 'Recipe', # default column, disabled box, with just the name in the list
'qhelp': "What you built (i.e. one or more recipes or image recipes)",
- 'orderfield': _get_toggle_order(request, "brtarget__target"),
- 'ordericon':_get_toggle_order_icon(request, "brtarget__target"),
+ 'orderfield': _get_toggle_order(request, "target__target"),
+ 'ordericon':_get_toggle_order_icon(request, "target__target"),
},
{'name': 'Machine',
'qhelp': "The machine is the hardware for which you are building a recipe or image recipe",
- 'orderfield': _get_toggle_order(request, "build__machine"),
- 'ordericon':_get_toggle_order_icon(request, "build__machine"),
+ 'orderfield': _get_toggle_order(request, "machine"),
+ 'ordericon':_get_toggle_order_icon(request, "machine"),
'dclass': 'span3'
}, # a slightly wider column
- ]
- }
-
- if (insert_projects):
- context['tablecols'].append(
- {'name': 'Project', 'clclass': 'project_column',
- }
- )
-
- # calculate the exact begining of local today and yesterday
- context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'created','updated'})
- context.update(context_date)
-
- context['tablecols'].append(
{'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column
'qhelp': "The date and time you started the build",
- 'orderfield': _get_toggle_order(request, "created", True),
- 'ordericon':_get_toggle_order_icon(request, "created"),
- 'filter' : {'class' : 'created',
+ 'orderfield': _get_toggle_order(request, "started_on", True),
+ 'ordericon':_get_toggle_order_icon(request, "started_on"),
+ 'orderkey' : "started_on",
+ 'filter' : {'class' : 'started_on',
'label': 'Show:',
'options' : [
- ("Today's builds" , 'created__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(created__gte=today_begin).count()),
+ ("Today's builds" , 'started_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(started_on__gte=today_begin).count()),
("Yesterday's builds",
- 'created__gte!created__lt:'
+ 'started_on__gte!started_on__lt:'
+yesterday_begin.strftime("%Y-%m-%d")+'!'
+today_begin.strftime("%Y-%m-%d"),
queryset_all.filter(
- created__gte=yesterday_begin,
- created__lt=today_begin
+ started_on__gte=yesterday_begin,
+ started_on__lt=today_begin
).count()),
- ("Build date range", 'daterange', 1, '', 'created'),
+ ("Build date range", 'daterange', 1, '', 'started_on'),
]
}
- }
- )
- context['tablecols'].append(
+ },
{'name': 'Completed on',
'qhelp': "The date and time the build finished",
- 'orderfield': _get_toggle_order(request, "updated", True),
- 'ordericon':_get_toggle_order_icon(request, "updated"),
- 'orderkey' : 'updated',
- 'filter' : {'class' : 'updated',
+ 'orderfield': _get_toggle_order(request, "completed_on", True),
+ 'ordericon':_get_toggle_order_icon(request, "completed_on"),
+ 'orderkey' : 'completed_on',
+ 'filter' : {'class' : 'completed_on',
'label': 'Show:',
'options' : [
- ("Today's builds" , 'updated__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=today_begin).count()),
+ ("Today's builds" , 'completed_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(completed_on__gte=today_begin).count()),
("Yesterday's builds",
- 'updated__gte!updated__lt:'
+ 'completed_on__gte!completed_on__lt:'
+yesterday_begin.strftime("%Y-%m-%d")+'!'
+today_begin.strftime("%Y-%m-%d"),
queryset_all.filter(
- updated__gte=yesterday_begin,
- updated__lt=today_begin
+ completed_on__gte=yesterday_begin,
+ completed_on__lt=today_begin
).count()),
- ("Build date range", 'daterange', 1, '', 'updated'),
+ ("Build date range", 'daterange', 1, '', 'completed_on'),
]
}
- }
- )
- context['tablecols'].append(
+ },
{'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox
'qhelp': "How many tasks failed during the build",
'filter' : {'class' : 'failed_tasks',
'label': 'Show:',
'options' : [
- ('Builds with failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED,
- queryset_all.filter(build__task_build__outcome=Task.OUTCOME_FAILED).count()),
- ('Builds without failed tasks', 'build__task_build__outcome:%d' % Task.OUTCOME_FAILED,
- queryset_all.filter(~Q(build__task_build__outcome=Task.OUTCOME_FAILED)).count()),
+ ('Builds with failed tasks', 'task_build__outcome:4', queryset_with_search.filter(task_build__outcome=4).count()),
+ ('Builds without failed tasks', 'task_build__outcome:NOT4', queryset_with_search.filter(~Q(task_build__outcome=4)).count()),
]
}
- }
- )
- context['tablecols'].append(
+ },
{'name': 'Errors', 'clclass': 'errors_no',
'qhelp': "How many errors were encountered during the build (if any)",
- 'orderfield': _get_toggle_order(request, "build__errors_no", True),
- 'ordericon':_get_toggle_order_icon(request, "build__errors_no"),
+ 'orderfield': _get_toggle_order(request, "errors_no", True),
+ 'ordericon':_get_toggle_order_icon(request, "errors_no"),
'orderkey' : 'errors_no',
'filter' : {'class' : 'errors_no',
'label': 'Show:',
'options' : [
- ('Builds with errors', 'build|build__errors_no__gt:None|0',
- queryset_all.filter(Q(build=None) | Q(build__errors_no__gt=0)).count()),
- ('Builds without errors', 'build__errors_no:0',
- queryset_all.filter(build__errors_no=0).count()),
+ ('Builds with errors', 'errors_no__gte:1', queryset_with_search.filter(errors_no__gte=1).count()),
+ ('Builds without errors', 'errors_no:0', queryset_with_search.filter(errors_no=0).count()),
]
}
- }
- )
- context['tablecols'].append(
+ },
{'name': 'Warnings', 'clclass': 'warnings_no',
'qhelp': "How many warnings were encountered during the build (if any)",
- 'orderfield': _get_toggle_order(request, "build__warnings_no", True),
- 'ordericon':_get_toggle_order_icon(request, "build__warnings_no"),
- 'orderkey' : 'build__warnings_no',
- 'filter' : {'class' : 'build__warnings_no',
+ 'orderfield': _get_toggle_order(request, "warnings_no", True),
+ 'ordericon':_get_toggle_order_icon(request, "warnings_no"),
+ 'orderkey' : 'warnings_no',
+ 'filter' : {'class' : 'warnings_no',
'label': 'Show:',
'options' : [
- ('Builds with warnings','build__warnings_no__gte:1', queryset_all.filter(build__warnings_no__gte=1).count()),
- ('Builds without warnings','build__warnings_no:0', queryset_all.filter(build__warnings_no=0).count()),
+ ('Builds with warnings','warnings_no__gte:1', queryset_with_search.filter(warnings_no__gte=1).count()),
+ ('Builds without warnings','warnings_no:0', queryset_with_search.filter(warnings_no=0).count()),
]
}
- }
- )
- context['tablecols'].append(
+ },
+ {'name': 'Log',
+ 'dclass': "span4",
+ 'qhelp': "Path to the build main log file",
+ 'clclass': 'log', 'hidden': 1,
+ 'orderfield': _get_toggle_order(request, "cooker_log_path"),
+ 'ordericon':_get_toggle_order_icon(request, "cooker_log_path"),
+ 'orderkey' : 'cooker_log_path',
+ },
{'name': 'Time', 'clclass': 'time', 'hidden' : 1,
'qhelp': "How long it took the build to finish",
-# 'orderfield': _get_toggle_order(request, "timespent", True),
-# 'ordericon':_get_toggle_order_icon(request, "timespent"),
+ 'orderfield': _get_toggle_order(request, "timespent", True),
+ 'ordericon':_get_toggle_order_icon(request, "timespent"),
'orderkey' : 'timespent',
- }
- )
- context['tablecols'].append(
+ },
{'name': 'Image files', 'clclass': 'output',
'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory",
# TODO: compute image fstypes from Target_Image_File
+ },
+ {'name': 'Project', 'clcalss': 'project_column',
}
- )
+ ]
+ }
+ # merge daterange values
+ context.update(context_date)
return context, pagesize, orderby
+
+
# new project
def newproject(request):
template = "newproject.html"
@@ -2236,7 +2204,7 @@ if True:
"lvs_nos" : Layer_Version.objects.all().count(),
"completedbuilds": Build.objects.filter(project_id = pid).filter(outcome__lte = Build.IN_PROGRESS),
"prj" : {"name": prj.name, },
- #"buildrequests" : prj.buildrequest_set.filter(state=BuildRequest.REQ_QUEUED),
+ "buildrequests" : prj.build_set.filter(outcome=Build.IN_PROGRESS),
"builds" : _project_recent_build_list(prj),
"layers" : map(lambda x: {
"id": x.layercommit.pk,
@@ -2607,9 +2575,10 @@ if True:
@_template_renderer('projectbuilds.html')
def projectbuilds(request, pid):
- # process any build request
prj = Project.objects.get(id = pid)
+
if request.method == "POST":
+ # process any build request
if 'buildCancel' in request.POST:
for i in request.POST['buildCancel'].strip().split(" "):
@@ -2641,10 +2610,10 @@ if True:
br = prj.schedule_build()
- buildrequests = BuildRequest.objects.filter(project = prj).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED)
+ queryset = Build.objects.filter(outcome__lte = Build.IN_PROGRESS)
try:
- context, pagesize, orderby = _build_list_helper(request, buildrequests, False)
+ context, pagesize, orderby = _build_list_helper(request, queryset)
except InvalidRequestException as e:
raise RedirectException('projectbuilds', request.GET, e.response, pid = pid)
@@ -2759,12 +2728,6 @@ if True:
}
return render(request, "unavailable_artifact.html", context)
- # This returns the mru object that is needed for the
- # managed_mrb_section.html template
- def _managed_get_latest_builds():
- build_mru = BuildRequest.objects.all()
- build_mru = list(build_mru.filter(Q(state__lt=BuildRequest.REQ_COMPLETED) or Q(state=BuildRequest.REQ_DELETED)).order_by("-pk")) + list(build_mru.filter(state__in=[BuildRequest.REQ_COMPLETED, BuildRequest.REQ_FAILED]).order_by("-pk")[:3])
- return build_mru
@@ -2796,7 +2759,7 @@ if True:
p.projectTargetsUrl = reverse('projectavailabletargets', args=(p.id,))
# build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
- build_mru = _managed_get_latest_builds()
+ build_mru = _get_latest_builds()
# translate the project's build target strings
fstypes_map = {};
@@ -2878,202 +2841,8 @@ if True:
return context
@_template_renderer("buildrequestdetails.html")
- def buildrequestdetails(request, pid, brid):
+ def buildrequestdetails(request, pid, bid):
context = {
- 'buildrequest' : BuildRequest.objects.get(pk = brid, project_id = pid)
+ 'buildrequest' : Build.objects.get(pk = bid, project_id = pid).buildrequest
}
return context
-
-
- @_template_renderer('builds.html')
- def builds_old(request):
- # 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.
- (pagesize, orderby) = _get_parameters_values(request, 10, 'completed_on:-')
- mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby }
- retval = _verify_parameters( request.GET, mandatory_parameters )
- if retval:
- raise RedirectException( 'all-builds', 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, Build)
- # post-process any date range filters
- filter_string,daterange_selected = _modify_date_range_filter(filter_string)
- queryset_all = Build.objects.exclude(outcome = Build.IN_PROGRESS)
- queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on')
- queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on')
-
- # retrieve the objects that will be displayed in the table; builds a paginator and gets a page range to display
- build_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1))
-
- # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
- build_mru = Build.objects.order_by("-started_on")[:3]
-
- # calculate the exact begining of local today and yesterday, append context
- context_date,today_begin,yesterday_begin = _add_daterange_context(queryset_all, request, {'started_on','completed_on'})
-
- # set up list of fstypes for each build
- fstypes_map = {};
- for build in build_info:
- targets = Target.objects.filter( build_id = build.id )
- comma = "";
- extensions = "";
- for t in targets:
- if ( not t.is_image ):
- continue
- tif = Target_Image_File.objects.filter( target_id = t.id )
- for i in tif:
- s=re.sub('.*tar.bz2', 'tar.bz2', i.file_name)
- if s == i.file_name:
- s=re.sub('.*\.', '', i.file_name)
- if None == re.search(s,extensions):
- extensions += comma + s
- comma = ", "
- fstypes_map[build.id]=extensions
-
- # send the data to the template
- context = {
- # specific info for
- 'mru' : build_mru,
- # TODO: common objects for all table views, adapt as needed
- 'objects' : build_info,
- 'objectname' : "builds",
- 'default_orderby' : 'completed_on:-',
- 'fstypes' : fstypes_map,
- 'search_term' : search_term,
- 'total_count' : queryset_with_search.count(),
- 'daterange_selected' : daterange_selected,
- # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns
- 'tablecols' : [
- {'name': 'Outcome', # column with a single filter
- 'qhelp' : "The outcome tells you if a build successfully completed or failed", # the help button content
- 'dclass' : "span2", # indication about column width; comes from the design
- 'orderfield': _get_toggle_order(request, "outcome"), # adds ordering by the field value; default ascending unless clicked from ascending into descending
- 'ordericon':_get_toggle_order_icon(request, "outcome"),
- # filter field will set a filter on that column with the specs in the filter description
- # the class field in the filter has no relation with clclass; the control different aspects of the UI
- # still, it is recommended for the values to be identical for easy tracking in the generated HTML
- 'filter' : {'class' : 'outcome',
- 'label': 'Show:',
- 'options' : [
- ('Successful builds', 'outcome:' + str(Build.SUCCEEDED), queryset_with_search.filter(outcome=str(Build.SUCCEEDED)).count()), # this is the field search expression
- ('Failed builds', 'outcome:'+ str(Build.FAILED), queryset_with_search.filter(outcome=str(Build.FAILED)).count()),
- ]
- }
- },
- {'name': 'Recipe', # default column, disabled box, with just the name in the list
- 'qhelp': "What you built (i.e. one or more recipes or image recipes)",
- 'orderfield': _get_toggle_order(request, "target__target"),
- 'ordericon':_get_toggle_order_icon(request, "target__target"),
- },
- {'name': 'Machine',
- 'qhelp': "The machine is the hardware for which you are building a recipe or image recipe",
- 'orderfield': _get_toggle_order(request, "machine"),
- 'ordericon':_get_toggle_order_icon(request, "machine"),
- 'dclass': 'span3'
- }, # a slightly wider column
- {'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column
- 'qhelp': "The date and time you started the build",
- 'orderfield': _get_toggle_order(request, "started_on", True),
- 'ordericon':_get_toggle_order_icon(request, "started_on"),
- 'filter' : {'class' : 'started_on',
- 'label': 'Show:',
- 'options' : [
- ("Today's builds" , 'started_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(started_on__gte=today_begin).count()),
- ("Yesterday's builds",
- 'started_on__gte!started_on__lt:'
- +yesterday_begin.strftime("%Y-%m-%d")+'!'
- +today_begin.strftime("%Y-%m-%d"),
- queryset_all.filter(
- started_on__gte=yesterday_begin,
- started_on__lt=today_begin
- ).count()),
- ("Build date range", 'daterange', 1, '', 'started_on'),
- ]
- }
- },
- {'name': 'Completed on',
- 'qhelp': "The date and time the build finished",
- 'orderfield': _get_toggle_order(request, "completed_on", True),
- 'ordericon':_get_toggle_order_icon(request, "completed_on"),
- 'orderkey' : 'completed_on',
- 'filter' : {'class' : 'completed_on',
- 'label': 'Show:',
- 'options' : [
- ("Today's builds" , 'completed_on__gte:'+today_begin.strftime("%Y-%m-%d"), queryset_all.filter(completed_on__gte=today_begin).count()),
- ("Yesterday's builds",
- 'completed_on__gte!completed_on__lt:'
- +yesterday_begin.strftime("%Y-%m-%d")+'!'
- +today_begin.strftime("%Y-%m-%d"),
- queryset_all.filter(
- completed_on__gte=yesterday_begin,
- completed_on__lt=today_begin
- ).count()),
- ("Build date range", 'daterange', 1, '', 'completed_on'),
- ]
- }
- },
- {'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox
- 'qhelp': "How many tasks failed during the build",
- 'filter' : {'class' : 'failed_tasks',
- 'label': 'Show:',
- 'options' : [
- ('Builds with failed tasks', 'task_build__outcome:4', queryset_with_search.filter(task_build__outcome=4).count()),
- ('Builds without failed tasks', 'task_build__outcome:NOT4', queryset_with_search.filter(~Q(task_build__outcome=4)).count()),
- ]
- }
- },
- {'name': 'Errors', 'clclass': 'errors_no',
- 'qhelp': "How many errors were encountered during the build (if any)",
- 'orderfield': _get_toggle_order(request, "errors_no", True),
- 'ordericon':_get_toggle_order_icon(request, "errors_no"),
- 'orderkey' : 'errors_no',
- 'filter' : {'class' : 'errors_no',
- 'label': 'Show:',
- 'options' : [
- ('Builds with errors', 'errors_no__gte:1', queryset_with_search.filter(errors_no__gte=1).count()),
- ('Builds without errors', 'errors_no:0', queryset_with_search.filter(errors_no=0).count()),
- ]
- }
- },
- {'name': 'Warnings', 'clclass': 'warnings_no',
- 'qhelp': "How many warnings were encountered during the build (if any)",
- 'orderfield': _get_toggle_order(request, "warnings_no", True),
- 'ordericon':_get_toggle_order_icon(request, "warnings_no"),
- 'orderkey' : 'warnings_no',
- 'filter' : {'class' : 'warnings_no',
- 'label': 'Show:',
- 'options' : [
- ('Builds with warnings','warnings_no__gte:1', queryset_with_search.filter(warnings_no__gte=1).count()),
- ('Builds without warnings','warnings_no:0', queryset_with_search.filter(warnings_no=0).count()),
- ]
- }
- },
- {'name': 'Log',
- 'dclass': "span4",
- 'qhelp': "Path to the build main log file",
- 'clclass': 'log', 'hidden': 1,
- 'orderfield': _get_toggle_order(request, "cooker_log_path"),
- 'ordericon':_get_toggle_order_icon(request, "cooker_log_path"),
- 'orderkey' : 'cooker_log_path',
- },
- {'name': 'Time', 'clclass': 'time', 'hidden' : 1,
- 'qhelp': "How long it took the build to finish",
- 'orderfield': _get_toggle_order(request, "timespent", True),
- 'ordericon':_get_toggle_order_icon(request, "timespent"),
- 'orderkey' : 'timespent',
- },
- {'name': 'Image files', 'clclass': 'output',
- 'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory",
- # TODO: compute image fstypes from Target_Image_File
- },
- ]
- }
-
- # merge daterange values
- context.update(context_date)
- _set_parameters_values(pagesize, orderby, request)
-
- return context
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 08/23] toaster: fill in build data from buildrequest
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (6 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 07/23] toaster: refactor the builds pages Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 09/23] toaster: fixes after replacing BuildRequest with Build Alex DAMIAN
` (14 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
This patch adds logic to complete changing the interface
from showing BuildRequests to showing Build data.
The BuildRequest data is now transformed in Build data with
proper Toaster exceptions being recorded instead of listing
problems during startup as build errors.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/bb/ui/buildinfohelper.py | 16 ++++++++++-
.../bldcontrol/management/commands/runbuilds.py | 32 +++++++++++++++++++++-
lib/toaster/orm/models.py | 28 +++++++++++++++++--
lib/toaster/toastergui/templates/project.html | 15 +++++++---
lib/toaster/toastergui/views.py | 4 +--
5 files changed, 84 insertions(+), 11 deletions(-)
diff --git a/lib/bb/ui/buildinfohelper.py b/lib/bb/ui/buildinfohelper.py
index 3ea842c..8b63f70 100644
--- a/lib/bb/ui/buildinfohelper.py
+++ b/lib/bb/ui/buildinfohelper.py
@@ -133,7 +133,21 @@ class ORMWrapper(object):
logger.debug(1, "buildinfohelper: project is not specified, defaulting to %s" % prj)
- build = Build.objects.create(
+ if buildrequest is not None:
+ build = buildrequest.build
+ build.machine=build_info['machine'],
+ build.distro=build_info['distro'],
+ build.distro_version=build_info['distro_version'],
+ build.completed_on=build_info['started_on'],
+ build.cooker_log_path=build_info['cooker_log_path'],
+ build.build_name=build_info['build_name'],
+ build.bitbake_version=build_info['bitbake_version']
+ build.save()
+
+ build.target_set.delete()
+
+ else:
+ build = Build.objects.create(
project = prj,
machine=build_info['machine'],
distro=build_info['distro'],
diff --git a/lib/toaster/bldcontrol/management/commands/runbuilds.py b/lib/toaster/bldcontrol/management/commands/runbuilds.py
index 920d9ef..da7d4af 100644
--- a/lib/toaster/bldcontrol/management/commands/runbuilds.py
+++ b/lib/toaster/bldcontrol/management/commands/runbuilds.py
@@ -1,6 +1,6 @@
from django.core.management.base import NoArgsCommand, CommandError
from django.db import transaction
-from orm.models import Build, ToasterSetting
+from orm.models import Build, ToasterSetting, LogMessage, Target
from bldcontrol.bbcontroller import getBuildEnvironmentController, ShellCmdException, BuildSetupException
from bldcontrol.models import BuildRequest, BuildEnvironment, BRError, BRVariable
import os
@@ -109,6 +109,36 @@ class Command(NoArgsCommand):
from datetime import timedelta
# DISABLED environments locked for more than 30 seconds - they should be unlocked
#BuildEnvironment.objects.filter(lock=BuildEnvironment.LOCK_LOCK).filter(updated__lt = timezone.now() - timedelta(seconds = 30)).update(lock = BuildEnvironment.LOCK_FREE)
+
+
+ # update all Builds that failed to start
+
+ for br in BuildRequest.objects.filter(state = BuildRequest.REQ_FAILED):
+ br.build.outcome = Build.FAILED
+ # transpose the launch errors in ToasterExceptions
+ for brerror in br.brerror_set.all():
+ LogMessage.objects.create(build = br.build, level = LogMessage.EXCEPTION, message = brerror.errmsg)
+ br.build.save()
+
+
+
+ # update all BuildRequests without a build created
+ for br in BuildRequest.objects.filter(build = None):
+ br.build = Build.objects.create(project = br.project, completed_on = br.updated, started_on = br.created)
+ br.build.outcome = Build.REQ_FAILED
+ try:
+ br.build.machine = br.brvariable_set.get(name='MACHINE').value
+ except BRVariable.DoesNotExist:
+ pass
+ br.save()
+ # transpose target information
+ for brtarget in br.brtarget_set.all():
+ Target.objects.create(build = br.build, target= brtarget.target)
+ # transpose the launch errors in ToasterExceptions
+ for brerror in br.brerror_set.all():
+ LogMessage.objects.create(build = br.build, level = LogMessage.EXCEPTION, message = brerror.errmsg)
+
+ br.build.save()
pass
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 422f2bf..d97eadb 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -201,16 +201,32 @@ class Project(models.Model):
commit = l.layercommit.get_vcs_reference()
print("ii Building layer ", l.layercommit.layer.name, " at vcs point ", commit)
BRLayer.objects.create(req = br, name = l.layercommit.layer.name, giturl = l.layercommit.layer.vcs_url, commit = commit, dirpath = l.layercommit.dirpath)
+
+ br.state = BuildRequest.REQ_QUEUED
+ now = timezone.now()
+ br.build = Build.objects.create(project = self,
+ completed_on=now,
+ started_on=now,
+ )
for t in self.projecttarget_set.all():
BRTarget.objects.create(req = br, target = t.target, task = t.task)
+ Target.objects.create(build = br.build, target = t.target)
+
for v in self.projectvariable_set.all():
BRVariable.objects.create(req = br, name = v.name, value = v.value)
- br.state = BuildRequest.REQ_QUEUED
+
+ try:
+ br.build.machine = self.projectvariable_set.get(name = 'MACHINE').value
+ br.build.save()
+ except ProjectVariable.DoesNotExist:
+ pass
br.save()
except Exception as e:
br.delete()
- raise e
+ import sys
+ et, ei, tb = sys.exc_info()
+ raise type(e), e, tb
return br
class Build(models.Model):
@@ -250,7 +266,6 @@ class Build(models.Model):
return completeper
def eta(self):
- from django.utils import timezone
eta = timezone.now()
completeper = self.completeper()
if self.completeper() > 0:
@@ -266,6 +281,12 @@ class Build(models.Model):
def toaster_exceptions(self):
return self.logmessage_set.filter(level=LogMessage.EXCEPTION)
+
+ def get_current_status(self):
+ if self.outcome == Build.IN_PROGRESS and self.build_name == "":
+ return "Queued"
+ return self.get_outcome_display()
+
def __str__(self):
return "%d %s %s" % (self.id, self.project, ",".join([t.target for t in self.target_set.all()]))
@@ -299,6 +320,7 @@ class Target(models.Model):
search_allowed_fields = ['target', 'file_name']
build = models.ForeignKey(Build)
target = models.CharField(max_length=100)
+ task = models.CharField(max_length=100, null=True)
is_image = models.BooleanField(default = False)
image_size = models.IntegerField(default=0)
license_manifest_path = models.CharField(max_length=500, null=True)
diff --git a/lib/toaster/toastergui/templates/project.html b/lib/toaster/toastergui/templates/project.html
index 0f6a77b..1a8991f 100644
--- a/lib/toaster/toastergui/templates/project.html
+++ b/lib/toaster/toastergui/templates/project.html
@@ -114,7 +114,7 @@ vim: expandtab tabstop=2
<script type="text/ng-template" id="target_display">
<div data-ng-switch on="t.task.length">
- <div data-ng-switch-when="0">{[t.target]}</div>
+ <div data-ng-switch-when="undefined">{[t.target]}</div>
<div data-ng-switch-default>{[t.target]}:{[t.task]}</div>
</div>
</script>
@@ -145,13 +145,13 @@ vim: expandtab tabstop=2
<a id="buildslist"></a>
<h2 class="air" data-ng-if="builds.length">Latest builds</h2>
- <div class="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-ng-class="{'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.status]">
+ <div class="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-ng-class="{'Queued':'alert-info', 'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.status]">
<div class="row-fluid">
<switch data-ng-switch="b.status">
<case data-ng-switch-when="Failed">
<div class="lead span3">
- <a data-ng-class="{'succeeded': 'success', 'failed': 'error'}[b.status]" href="{[b.br_page_url]}">
+ <a data-ng-class="{'Succeeded': 'success', 'Failed': 'error'}[b.status]" href="{[b.br_page_url]}">
<span data-ng-repeat="t in b.targets" data-ng-include src="'target_display'"></span>
</a>
</div>
@@ -176,12 +176,19 @@ vim: expandtab tabstop=2
<!-- we don't have warnings in this case -->
</div>
<div> <span class="lead">Build time: {[b.command_time|timediff]}</span>
- <button class="btn pull-right" data-ng-class="{'succeeded': 'btn-success', 'failed': 'btn-danger'}[b.status]"
+ <button class="btn pull-right" data-ng-class="{'Succeeded': 'btn-success', 'Failed': 'btn-danger'}[b.status]"
data-ng-click="buildExistingTarget(b.targets)">Run again</button>
</div>
</case>
+ <case data-ng-switch-when="Queued">
+ <div class="lead span5"> <span data-ng-repeat="t in b.targets" data-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" data-ng-click="buildCancel(b)">Cancel</button>
+ </case>
<case data-ng-switch-when="In Progress">
<switch data-ng-switch="b.build.length">
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 0324d17..2336ae3 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -74,8 +74,8 @@ def _project_recent_build_list(prj):
for x in _get_latest_builds(prj):
d = {
"id": x.pk,
- "targets" : map(lambda y: {"target": y.target, "task": None }, x.target_set.all()), # TODO: create the task entry in the Target table
- "status": x.get_outcome_display(),
+ "targets" : map(lambda y: {"target": y.target, "task": y.task }, x.target_set.all()), # TODO: create the task entry in the Target table
+ "status": x.get_current_status(),
"errors": map(lambda y: {"type": y.lineno, "msg": y.message, "tb": y.pathname}, x.logmessage_set.filter(level__gte=LogMessage.WARNING)),
"updated": x.completed_on.strftime('%s')+"000",
"command_time": (x.completed_on - x.started_on).total_seconds(),
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 09/23] toaster: fixes after replacing BuildRequest with Build
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (7 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 08/23] toaster: fill in build data from buildrequest Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 10/23] toaster: add django-aggregate-if Alex DAMIAN
` (13 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
This is a set of fixes that repair the interface after
we switched from displaying BuildRequest data to Build data
in the formerly "managed" mode.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/bb/ui/toasterui.py | 24 +-
lib/toaster/bldcontrol/localhostbecontroller.py | 2 +-
.../bldcontrol/management/commands/runbuilds.py | 7 +-
lib/toaster/bldcontrol/models.py | 3 +
...ect__chg_field_project_bitbake_version__chg_.py | 5 +-
...k__add_field_layer_version_local_path__del_f.py | 343 +++++++++++++++++++++
lib/toaster/orm/models.py | 8 +-
lib/toaster/toastergui/static/js/projectapp.js | 4 +-
lib/toaster/toastergui/templates/project.html | 4 +-
lib/toaster/toastergui/views.py | 6 +-
10 files changed, 378 insertions(+), 28 deletions(-)
create mode 100644 lib/toaster/orm/migrations/0022_auto__add_field_target_task__add_field_layer_version_local_path__del_f.py
diff --git a/lib/bb/ui/toasterui.py b/lib/bb/ui/toasterui.py
index 3054048..9c59fd0 100644
--- a/lib/bb/ui/toasterui.py
+++ b/lib/bb/ui/toasterui.py
@@ -43,7 +43,7 @@ import xmlrpclib
featureSet = [bb.cooker.CookerFeatures.HOB_EXTRA_CACHES, bb.cooker.CookerFeatures.SEND_DEPENDS_TREE, bb.cooker.CookerFeatures.BASEDATASTORE_TRACKING, bb.cooker.CookerFeatures.SEND_SANITYEVENTS]
-logger = logging.getLogger("BitBake")
+logger = logging.getLogger("ToasterLogger")
interactive = sys.stdout.isatty()
@@ -66,7 +66,6 @@ def _log_settings_from_server(server):
def main(server, eventHandler, params ):
-
helper = uihelper.BBUIHelper()
console = logging.StreamHandler(sys.stdout)
@@ -235,12 +234,18 @@ def main(server, eventHandler, params ):
if isinstance(event, (bb.event.TreeDataPreparationStarted, bb.event.TreeDataPreparationCompleted)):
continue
- if isinstance(event, (bb.event.BuildCompleted)):
+ if isinstance(event, (bb.event.BuildCompleted, bb.command.CommandFailed)):
+
+ if (isinstance(event, bb.command.CommandFailed)):
+ errors += 1
+ errorcode = 1
+ logger.error("Command execution failed: %s", event.error)
+
# update the build info helper on BuildCompleted, not on CommandXXX
buildinfohelper.update_build_information(event, errors, warnings, taskfailures)
buildinfohelper.close(errorcode)
# mark the log output; controllers may kill the toasterUI after seeing this log
- logger.info("ToasterUI build done")
+ logger.info("ToasterUI build done 1, brbe: %s" % buildinfohelper.brbe )
# we start a new build info
if buildinfohelper.brbe is not None:
@@ -254,22 +259,13 @@ def main(server, eventHandler, params ):
taskfailures = []
buildinfohelper = BuildInfoHelper(server, build_history_enabled)
+ logger.info("ToasterUI build done 2")
continue
if isinstance(event, (bb.command.CommandCompleted,
bb.command.CommandFailed,
bb.command.CommandExit)):
errorcode = 0
- if (isinstance(event, bb.command.CommandFailed)):
- event.levelno = format.ERROR
- event.msg = "Command Failed " + event.error
- event.pathname = ""
- event.lineno = 0
- buildinfohelper.store_log_event(event)
- errors += 1
- errorcode = 1
- logger.error("Command execution failed: %s", event.error)
-
continue
diff --git a/lib/toaster/bldcontrol/localhostbecontroller.py b/lib/toaster/bldcontrol/localhostbecontroller.py
index d0f8632..ad0a561 100644
--- a/lib/toaster/bldcontrol/localhostbecontroller.py
+++ b/lib/toaster/bldcontrol/localhostbecontroller.py
@@ -322,7 +322,7 @@ class LocalhostBEController(BuildEnvironmentController):
def triggerBuild(self, bitbake, layers, variables, targets):
# set up the buid environment with the needed layers
self.setLayers(bitbake, layers)
- self.writeConfFile("conf/toaster-pre.conf", )
+ self.writeConfFile("conf/toaster-pre.conf", variables)
self.writeConfFile("conf/toaster.conf", raw = "INHERIT+=\"toaster buildhistory\"")
# get the bb server running with the build req id and build env id
diff --git a/lib/toaster/bldcontrol/management/commands/runbuilds.py b/lib/toaster/bldcontrol/management/commands/runbuilds.py
index da7d4af..bcf3b04 100644
--- a/lib/toaster/bldcontrol/management/commands/runbuilds.py
+++ b/lib/toaster/bldcontrol/management/commands/runbuilds.py
@@ -6,7 +6,7 @@ from bldcontrol.models import BuildRequest, BuildEnvironment, BRError, BRVariabl
import os
import logging
-logger = logging.getLogger("toaster")
+logger = logging.getLogger("ToasterScheduler")
class Command(NoArgsCommand):
args = ""
@@ -35,7 +35,7 @@ class Command(NoArgsCommand):
# select the build environment and the request to build
br = self._selectBuildRequest()
except IndexError as e:
- # logger.debug("runbuilds: No build request")
+ #logger.debug("runbuilds: No build request")
return
try:
bec = self._selectBuildEnvironment()
@@ -113,10 +113,11 @@ class Command(NoArgsCommand):
# update all Builds that failed to start
- for br in BuildRequest.objects.filter(state = BuildRequest.REQ_FAILED):
+ for br in BuildRequest.objects.filter(state = BuildRequest.REQ_FAILED, build__outcome = Build.IN_PROGRESS):
br.build.outcome = Build.FAILED
# transpose the launch errors in ToasterExceptions
for brerror in br.brerror_set.all():
+ logger.debug("Saving error %s" % brerror)
LogMessage.objects.create(build = br.build, level = LogMessage.EXCEPTION, message = brerror.errmsg)
br.build.save()
diff --git a/lib/toaster/bldcontrol/models.py b/lib/toaster/bldcontrol/models.py
index b789446..6f40866 100644
--- a/lib/toaster/bldcontrol/models.py
+++ b/lib/toaster/bldcontrol/models.py
@@ -159,3 +159,6 @@ class BRError(models.Model):
errtype = models.CharField(max_length=100)
errmsg = models.TextField()
traceback = models.TextField()
+
+ def __str__(self):
+ return "%s (%s)" % (self.errmsg, self.req)
diff --git a/lib/toaster/orm/migrations/0021_auto__chg_field_build_project__chg_field_project_bitbake_version__chg_.py b/lib/toaster/orm/migrations/0021_auto__chg_field_build_project__chg_field_project_bitbake_version__chg_.py
index b82cc8d..924a5c4 100644
--- a/lib/toaster/orm/migrations/0021_auto__chg_field_build_project__chg_field_project_bitbake_version__chg_.py
+++ b/lib/toaster/orm/migrations/0021_auto__chg_field_build_project__chg_field_project_bitbake_version__chg_.py
@@ -105,7 +105,6 @@ class Migration(SchemaMigration):
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
- 'local_path': ('django.db.models.fields.FilePathField', [], {'default': 'None', 'max_length': '255', 'null': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'summary': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
@@ -124,6 +123,7 @@ class Migration(SchemaMigration):
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'local_path': ('django.db.models.fields.FilePathField', [], {'default': "'/'", 'max_length': '1024'}),
'priority': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.Project']", 'null': 'True'}),
'up_branch': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.Branch']", 'null': 'True'}),
@@ -229,7 +229,7 @@ class Migration(SchemaMigration):
'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
u'orm.recipe': {
- 'Meta': {'unique_together': "(('layer_version', 'file_path'),)", 'object_name': 'Recipe'},
+ 'Meta': {'unique_together': "(('layer_version', 'file_path', 'pathflags'),)", 'object_name': 'Recipe'},
'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
@@ -239,6 +239,7 @@ class Migration(SchemaMigration):
'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'pathflags': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
diff --git a/lib/toaster/orm/migrations/0022_auto__add_field_target_task__add_field_layer_version_local_path__del_f.py b/lib/toaster/orm/migrations/0022_auto__add_field_target_task__add_field_layer_version_local_path__del_f.py
new file mode 100644
index 0000000..3dec391
--- /dev/null
+++ b/lib/toaster/orm/migrations/0022_auto__add_field_target_task__add_field_layer_version_local_path__del_f.py
@@ -0,0 +1,343 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Target.task'
+ db.add_column(u'orm_target', 'task',
+ self.gf('django.db.models.fields.CharField')(max_length=100, null=True),
+ keep_default=False)
+
+
+
+
+
+ def backwards(self, orm):
+ # Deleting field 'Target.task'
+ db.delete_column(u'orm_target', 'task')
+
+
+ models = {
+ u'orm.bitbakeversion': {
+ 'Meta': {'object_name': 'BitbakeVersion'},
+ 'branch': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'dirpath': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'giturl': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'})
+ },
+ u'orm.branch': {
+ 'Meta': {'unique_together': "(('layer_source', 'name'), ('layer_source', 'up_id'))", 'object_name': 'Branch'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'True', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'short_description': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'})
+ },
+ u'orm.build': {
+ 'Meta': {'object_name': 'Build'},
+ 'bitbake_version': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'build_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'completed_on': ('django.db.models.fields.DateTimeField', [], {}),
+ 'cooker_log_path': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'distro': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'distro_version': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'errors_no': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'machine': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'outcome': ('django.db.models.fields.IntegerField', [], {'default': '2'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+ 'started_on': ('django.db.models.fields.DateTimeField', [], {}),
+ 'timespent': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'warnings_no': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+ },
+ u'orm.buildartifact': {
+ 'Meta': {'object_name': 'BuildArtifact'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+ 'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+ 'file_size': ('django.db.models.fields.IntegerField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ u'orm.helptext': {
+ 'Meta': {'object_name': 'HelpText'},
+ 'area': ('django.db.models.fields.IntegerField', [], {}),
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'helptext_build'", 'to': u"orm['orm.Build']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ u'orm.layer': {
+ 'Meta': {'unique_together': "(('layer_source', 'up_id'), ('layer_source', 'name'))", 'object_name': 'Layer'},
+ 'description': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'}),
+ 'vcs_url': ('django.db.models.fields.URLField', [], {'default': 'None', 'max_length': '200', 'null': 'True'}),
+ 'vcs_web_file_base_url': ('django.db.models.fields.URLField', [], {'default': 'None', 'max_length': '200', 'null': 'True'}),
+ 'vcs_web_tree_base_url': ('django.db.models.fields.URLField', [], {'default': 'None', 'max_length': '200', 'null': 'True'}),
+ 'vcs_web_url': ('django.db.models.fields.URLField', [], {'default': 'None', 'max_length': '200', 'null': 'True'})
+ },
+ u'orm.layer_version': {
+ 'Meta': {'unique_together': "(('layer_source', 'up_id'),)", 'object_name': 'Layer_Version'},
+ 'branch': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'layer_version_build'", 'null': 'True', 'to': u"orm['orm.Build']"}),
+ 'commit': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'dirpath': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'local_path': ('django.db.models.fields.FilePathField', [], {'default': "'/'", 'max_length': '1024'}),
+ 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.Project']", 'null': 'True'}),
+ 'up_branch': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.Branch']", 'null': 'True'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'})
+ },
+ u'orm.layersource': {
+ 'Meta': {'unique_together': "(('sourcetype', 'apiurl'),)", 'object_name': 'LayerSource'},
+ 'apiurl': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '63'}),
+ 'sourcetype': ('django.db.models.fields.IntegerField', [], {})
+ },
+ u'orm.layerversiondependency': {
+ 'Meta': {'unique_together': "(('layer_source', 'up_id'),)", 'object_name': 'LayerVersionDependency'},
+ 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'dependees'", 'to': u"orm['orm.Layer_Version']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'dependencies'", 'to': u"orm['orm.Layer_Version']"}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'})
+ },
+ u'orm.logmessage': {
+ 'Meta': {'object_name': 'LogMessage'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'lineno': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+ 'pathname': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+ 'task': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Task']", 'null': 'True', 'blank': 'True'})
+ },
+ u'orm.machine': {
+ 'Meta': {'unique_together': "(('layer_source', 'up_id'),)", 'object_name': 'Machine'},
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Layer_Version']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'})
+ },
+ u'orm.package': {
+ 'Meta': {'object_name': 'Package'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'installed_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+ 'installed_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'recipe': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Recipe']", 'null': 'True'}),
+ 'revision': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'section': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+ 'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+ },
+ u'orm.package_dependency': {
+ 'Meta': {'object_name': 'Package_Dependency'},
+ 'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+ 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_target'", 'to': u"orm['orm.Package']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_source'", 'to': u"orm['orm.Package']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']", 'null': 'True'})
+ },
+ u'orm.package_file': {
+ 'Meta': {'object_name': 'Package_File'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildfilelist_package'", 'to': u"orm['orm.Package']"}),
+ 'path': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+ 'size': ('django.db.models.fields.IntegerField', [], {})
+ },
+ u'orm.project': {
+ 'Meta': {'object_name': 'Project'},
+ 'bitbake_version': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.BitbakeVersion']", 'null': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Release']", 'null': 'True'}),
+ 'short_description': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'user_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
+ },
+ u'orm.projectlayer': {
+ 'Meta': {'unique_together': "(('project', 'layercommit'),)", 'object_name': 'ProjectLayer'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layercommit': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Layer_Version']", 'null': 'True'}),
+ 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"})
+ },
+ u'orm.projecttarget': {
+ 'Meta': {'object_name': 'ProjectTarget'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'task': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+ },
+ u'orm.projectvariable': {
+ 'Meta': {'object_name': 'ProjectVariable'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+ 'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+ },
+ u'orm.recipe': {
+ 'Meta': {'unique_together': "(('layer_version', 'file_path', 'pathflags'),)", 'object_name': 'Recipe'},
+ 'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'pathflags': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+ 'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+ },
+ u'orm.recipe_dependency': {
+ 'Meta': {'object_name': 'Recipe_Dependency'},
+ 'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+ 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_depends'", 'to': u"orm['orm.Recipe']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_recipe'", 'to': u"orm['orm.Recipe']"})
+ },
+ u'orm.release': {
+ 'Meta': {'object_name': 'Release'},
+ 'bitbake_version': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.BitbakeVersion']"}),
+ 'branch_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}),
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'helptext': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'})
+ },
+ u'orm.releasedefaultlayer': {
+ 'Meta': {'object_name': 'ReleaseDefaultLayer'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+ 'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Release']"})
+ },
+ u'orm.releaselayersourcepriority': {
+ 'Meta': {'unique_together': "(('release', 'layer_source'),)", 'object_name': 'ReleaseLayerSourcePriority'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.LayerSource']"}),
+ 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Release']"})
+ },
+ u'orm.target': {
+ 'Meta': {'object_name': 'Target'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'is_image': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'license_manifest_path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'task': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+ },
+ u'orm.target_file': {
+ 'Meta': {'object_name': 'Target_File'},
+ 'directory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'directory_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inodetype': ('django.db.models.fields.IntegerField', [], {}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+ 'permission': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'size': ('django.db.models.fields.IntegerField', [], {}),
+ 'sym_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'symlink_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+ },
+ u'orm.target_image_file': {
+ 'Meta': {'object_name': 'Target_Image_File'},
+ 'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '254'}),
+ 'file_size': ('django.db.models.fields.IntegerField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+ },
+ u'orm.target_installed_package': {
+ 'Meta': {'object_name': 'Target_Installed_Package'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildtargetlist_package'", 'to': u"orm['orm.Package']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+ },
+ u'orm.task': {
+ 'Meta': {'ordering': "('order', 'recipe')", 'unique_together': "(('build', 'recipe', 'task_name'),)", 'object_name': 'Task'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_build'", 'to': u"orm['orm.Build']"}),
+ 'cpu_usage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '8', 'decimal_places': '2'}),
+ 'disk_io': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'elapsed_time': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '8', 'decimal_places': '2'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'line_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'logfile': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+ 'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'outcome': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+ 'path_to_sstate_obj': ('django.db.models.fields.FilePathField', [], {'max_length': '500', 'blank': 'True'}),
+ 'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'to': u"orm['orm.Recipe']"}),
+ 'script_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'source_url': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+ 'sstate_checksum': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'sstate_result': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'task_executed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'task_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'work_directory': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ u'orm.task_dependency': {
+ 'Meta': {'object_name': 'Task_Dependency'},
+ 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_depends'", 'to': u"orm['orm.Task']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'task': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_task'", 'to': u"orm['orm.Task']"})
+ },
+ u'orm.toastersetting': {
+ 'Meta': {'object_name': 'ToasterSetting'},
+ 'helptext': ('django.db.models.fields.TextField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '63'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ u'orm.variable': {
+ 'Meta': {'object_name': 'Variable'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_build'", 'to': u"orm['orm.Build']"}),
+ 'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'human_readable_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'variable_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'variable_value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+ },
+ u'orm.variablehistory': {
+ 'Meta': {'object_name': 'VariableHistory'},
+ 'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'line_number': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'operation': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'variable': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vhistory'", 'to': u"orm['orm.Variable']"})
+ }
+ }
+
+ complete_apps = ['orm']
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index d97eadb..e93629b 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -283,8 +283,9 @@ class Build(models.Model):
def get_current_status(self):
- if self.outcome == Build.IN_PROGRESS and self.build_name == "":
- return "Queued"
+ from bldcontrol.models import BuildRequest
+ if self.outcome == Build.IN_PROGRESS and self.buildrequest.state != BuildRequest.REQ_INPROGRESS:
+ return self.buildrequest.get_state_display()
return self.get_outcome_display()
def __str__(self):
@@ -1168,6 +1169,9 @@ class LogMessage(models.Model):
pathname = models.FilePathField(max_length=255, blank=True)
lineno = models.IntegerField(null=True)
+ def __str__(self):
+ return "%s %s %s" % (self.get_level_display(), self.message, self.build)
+
def invalidate_cache(**kwargs):
from django.core.cache import cache
try:
diff --git a/lib/toaster/toastergui/static/js/projectapp.js b/lib/toaster/toastergui/static/js/projectapp.js
index 40e2e1f..ea44bf3 100644
--- a/lib/toaster/toastergui/static/js/projectapp.js
+++ b/lib/toaster/toastergui/static/js/projectapp.js
@@ -132,8 +132,8 @@ projectApp.filter('timediff', function() {
var seconds = parseInt(input);
var minutes = Math.floor(seconds / 60);
seconds = seconds - minutes * 60;
- var hours = Math.floor(seconds / 3600);
- seconds = seconds - hours * 3600;
+ var hours = Math.floor(minutes / 60);
+ minutes = minutes - hours * 60;
return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds);
};
});
diff --git a/lib/toaster/toastergui/templates/project.html b/lib/toaster/toastergui/templates/project.html
index 1a8991f..b5d97cb 100644
--- a/lib/toaster/toastergui/templates/project.html
+++ b/lib/toaster/toastergui/templates/project.html
@@ -145,7 +145,7 @@ vim: expandtab tabstop=2
<a id="buildslist"></a>
<h2 class="air" data-ng-if="builds.length">Latest builds</h2>
- <div class="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-ng-class="{'Queued':'alert-info', 'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.status]">
+ <div class="animate-repeat alert" data-ng-repeat="b in builds track by b.id" data-ng-class="{'queued':'alert-info', 'In Progress':'alert-info', 'Succeeded':'alert-success', 'Failed':'alert-error'}[b.status]">
<div class="row-fluid">
<switch data-ng-switch="b.status">
@@ -182,7 +182,7 @@ vim: expandtab tabstop=2
</div>
</case>
- <case data-ng-switch-when="Queued">
+ <case data-ng-switch-when="queued">
<div class="lead span5"> <span data-ng-repeat="t in b.targets" data-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>
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 2336ae3..00b1387 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -64,7 +64,9 @@ def _get_latest_builds(prj=None):
if prj is not None:
queryset = queryset.filter(project = prj)
- return itertools.chain(queryset.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3], queryset.filter(outcome=Build.IN_PROGRESS).order_by("-pk"))
+ return itertools.chain(
+ queryset.filter(outcome=Build.IN_PROGRESS).order_by("-pk"),
+ queryset.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3] )
# a JSON-able dict of recent builds; for use in the Project page, xhr_ updates, and other places, as needed
@@ -76,7 +78,7 @@ def _project_recent_build_list(prj):
"id": x.pk,
"targets" : map(lambda y: {"target": y.target, "task": y.task }, x.target_set.all()), # TODO: create the task entry in the Target table
"status": x.get_current_status(),
- "errors": map(lambda y: {"type": y.lineno, "msg": y.message, "tb": y.pathname}, x.logmessage_set.filter(level__gte=LogMessage.WARNING)),
+ "errors": map(lambda y: {"type": y.lineno, "msg": y.message, "tb": y.pathname}, (x.logmessage_set.filter(level__gte=LogMessage.WARNING)|x.logmessage_set.filter(level=LogMessage.EXCEPTION))),
"updated": x.completed_on.strftime('%s')+"000",
"command_time": (x.completed_on - x.started_on).total_seconds(),
"br_page_url": reverse('buildrequestdetails', args=(x.project.id, x.pk) ),
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 10/23] toaster: add django-aggregate-if
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (8 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 09/23] toaster: fixes after replacing BuildRequest with Build Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 11/23] toaster: bring django-aggregate-if into the project Alex DAMIAN
` (12 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
Import library that provides conditional aggregates
in Django. The tests are removed as they conflict
with the Django startup.
The sources are
https://github.com/henriquebastos/django-aggregate-if
Licence is MIT
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
.../contrib/django-aggregate-if-master/.gitignore | 10 ++
.../contrib/django-aggregate-if-master/.travis.yml | 50 ++++++
.../contrib/django-aggregate-if-master/LICENSE | 21 +++
.../contrib/django-aggregate-if-master/README.rst | 156 ++++++++++++++++
.../django-aggregate-if-master/aggregate_if.py | 164 +++++++++++++++++
.../contrib/django-aggregate-if-master/runtests.py | 48 +++++
.../contrib/django-aggregate-if-master/setup.py | 33 ++++
.../contrib/django-aggregate-if-master/tox.ini | 198 +++++++++++++++++++++
8 files changed, 680 insertions(+)
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/.gitignore
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/.travis.yml
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/LICENSE
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/README.rst
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
create mode 100755 lib/toaster/contrib/django-aggregate-if-master/runtests.py
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/setup.py
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/tox.ini
diff --git a/lib/toaster/contrib/django-aggregate-if-master/.gitignore b/lib/toaster/contrib/django-aggregate-if-master/.gitignore
new file mode 100644
index 0000000..c45652d
--- /dev/null
+++ b/lib/toaster/contrib/django-aggregate-if-master/.gitignore
@@ -0,0 +1,10 @@
+*.pyc
+*.swp
+*.swo
+*.kpf
+*.egg-info/
+.idea
+.tox
+tmp/
+dist/
+.DS_Store
diff --git a/lib/toaster/contrib/django-aggregate-if-master/.travis.yml b/lib/toaster/contrib/django-aggregate-if-master/.travis.yml
new file mode 100644
index 0000000..a920f39
--- /dev/null
+++ b/lib/toaster/contrib/django-aggregate-if-master/.travis.yml
@@ -0,0 +1,50 @@
+language: python
+python:
+ - "2.7"
+ - "3.4"
+services:
+ - mysql
+ - postgresql
+env:
+ - DJANGO=1.4 DB=sqlite
+ - DJANGO=1.4 DB=mysql
+ - DJANGO=1.4 DB=postgres
+ - DJANGO=1.5 DB=sqlite
+ - DJANGO=1.5 DB=mysql
+ - DJANGO=1.5 DB=postgres
+ - DJANGO=1.6 DB=sqlite
+ - DJANGO=1.6 DB=mysql
+ - DJANGO=1.6 DB=postgres
+ - DJANGO=1.7 DB=sqlite
+ - DJANGO=1.7 DB=mysql
+ - DJANGO=1.7 DB=postgres
+
+matrix:
+ exclude:
+ - python: "3.4"
+ env: DJANGO=1.4 DB=sqlite
+ - python: "3.4"
+ env: DJANGO=1.4 DB=mysql
+ - python: "3.4"
+ env: DJANGO=1.4 DB=postgres
+ - python: "3.4"
+ env: DJANGO=1.5 DB=sqlite
+ - python: "3.4"
+ env: DJANGO=1.5 DB=mysql
+ - python: "3.4"
+ env: DJANGO=1.5 DB=postgres
+ - python: "3.4"
+ env: DJANGO=1.6 DB=mysql
+ - python: "3.4"
+ env: DJANGO=1.7 DB=mysql
+
+before_script:
+ - mysql -e 'create database aggregation;'
+ - psql -c 'create database aggregation;' -U postgres
+install:
+ - pip install six
+ - if [ "$DB" == "mysql" ]; then pip install mysql-python; fi
+ - if [ "$DB" == "postgres" ]; then pip install psycopg2; fi
+ - pip install -q Django==$DJANGO --use-mirrors
+script:
+ - ./runtests.py --settings=tests.test_$DB
diff --git a/lib/toaster/contrib/django-aggregate-if-master/LICENSE b/lib/toaster/contrib/django-aggregate-if-master/LICENSE
new file mode 100644
index 0000000..6b7c3b1
--- /dev/null
+++ b/lib/toaster/contrib/django-aggregate-if-master/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2012 Henrique Bastos
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lib/toaster/contrib/django-aggregate-if-master/README.rst b/lib/toaster/contrib/django-aggregate-if-master/README.rst
new file mode 100644
index 0000000..739d4da
--- /dev/null
+++ b/lib/toaster/contrib/django-aggregate-if-master/README.rst
@@ -0,0 +1,156 @@
+Django Aggregate If: Condition aggregates for Django
+====================================================
+
+.. image:: https://travis-ci.org/henriquebastos/django-aggregate-if.png?branch=master
+ :target: https://travis-ci.org/henriquebastos/django-aggregate-if
+ :alt: Test Status
+
+.. image:: https://landscape.io/github/henriquebastos/django-aggregate-if/master/landscape.png
+ :target: https://landscape.io/github/henriquebastos/django-aggregate-if/master
+ :alt: Code Helth
+
+.. image:: https://pypip.in/v/django-aggregate-if/badge.png
+ :target: https://crate.io/packages/django-aggregate-if/
+ :alt: Latest PyPI version
+
+.. image:: https://pypip.in/d/django-aggregate-if/badge.png
+ :target: https://crate.io/packages/django-aggregate-if/
+ :alt: Number of PyPI downloads
+
+*Aggregate-if* adds conditional aggregates to Django.
+
+Conditional aggregates can help you reduce the ammount of queries to obtain
+aggregated information, like statistics for example.
+
+Imagine you have a model ``Offer`` like this one:
+
+.. code-block:: python
+
+ class Offer(models.Model):
+ sponsor = models.ForeignKey(User)
+ price = models.DecimalField(max_digits=9, decimal_places=2)
+ status = models.CharField(max_length=30)
+ expire_at = models.DateField(null=True, blank=True)
+ created_at = models.DateTimeField(auto_now_add=True)
+ updated_at = models.DateTimeField(auto_now=True)
+
+ OPEN = "OPEN"
+ REVOKED = "REVOKED"
+ PAID = "PAID"
+
+Let's say you want to know:
+
+#. How many offers exists in total;
+#. How many of them are OPEN, REVOKED or PAID;
+#. How much money was offered in total;
+#. How much money is in OPEN, REVOKED and PAID offers;
+
+To get these informations, you could query:
+
+.. code-block:: python
+
+ from django.db.models import Count, Sum
+
+ Offer.objects.count()
+ Offer.objects.filter(status=Offer.OPEN).aggregate(Count('pk'))
+ Offer.objects.filter(status=Offer.REVOKED).aggregate(Count('pk'))
+ Offer.objects.filter(status=Offer.PAID).aggregate(Count('pk'))
+ Offer.objects.aggregate(Sum('price'))
+ Offer.objects.filter(status=Offer.OPEN).aggregate(Sum('price'))
+ Offer.objects.filter(status=Offer.REVOKED).aggregate(Sum('price'))
+ Offer.objects.filter(status=Offer.PAID).aggregate(Sum('price'))
+
+In this case, **8 queries** were needed to retrieve the desired information.
+
+With conditional aggregates you can get it all with only **1 query**:
+
+.. code-block:: python
+
+ from django.db.models import Q
+ from aggregate_if import Count, Sum
+
+ Offer.objects.aggregate(
+ pk__count=Count('pk'),
+ pk__open__count=Count('pk', only=Q(status=Offer.OPEN)),
+ pk__revoked__count=Count('pk', only=Q(status=Offer.REVOKED)),
+ pk__paid__count=Count('pk', only=Q(status=Offer.PAID)),
+ pk__sum=Sum('price'),
+ pk__open__sum=Sum('price', only=Q(status=Offer.OPEN)),
+ pk__revoked__sum=Sum('price'), only=Q(status=Offer.REVOKED)),
+ pk__paid__sum=Sum('price'), only=Q(status=Offer.PAID))
+ )
+
+Installation
+------------
+
+*Aggregate-if* works with Django 1.4, 1.5, 1.6 and 1.7.
+
+To install it, simply:
+
+.. code-block:: bash
+
+ $ pip install django-aggregate-if
+
+Inspiration
+-----------
+
+There is a 5 years old `ticket 11305`_ that will (*hopefully*) implement this feature into
+Django 1.8.
+
+Using Django 1.6, I still wanted to avoid creating custom queries for very simple
+conditional aggregations. So I've cherry picked those ideas and others from the
+internet and built this library.
+
+This library uses the same API and tests proposed on `ticket 11305`_, so when the
+new feature is available you can easily replace ``django-aggregate-if``.
+
+Limitations
+-----------
+
+Conditions involving joins with aliases are not supported yet. If you want to
+help adding this feature, you're welcome to check the `first issue`_.
+
+Contributors
+------------
+
+* `Henrique Bastos <http://github.com/henriquebastos>`_
+* `Iuri de Silvio <https://github.com/iurisilvio>`_
+* `Hampus Stjernhav <https://github.com/champ>`_
+* `Bradley Martsberger <https://github.com/martsberger>`_
+* `Markus Bertheau <https://github.com/mbertheau>`_
+* `end0 <https://github.com/end0>`_
+* `Scott Sexton <https://github.com/scottsexton>`_
+* `Mauler <https://github.com/mauler>`_
+* `trbs <https://github.com/trbs>`_
+
+Changelog
+---------
+
+0.5
+ - Support for Django 1.7
+
+0.4
+ - Use tox to run tests.
+ - Add support for Django 1.6.
+ - Add support for Python3.
+ - The ``only`` parameter now freely supports joins independent of the main query.
+ - Adds support for alias relabeling permitting excludes and updates with aggregates filtered on remote foreign key relations.
+
+0.3.1
+ - Fix quotation escaping.
+ - Fix boolean casts on Postgres.
+
+0.2
+ - Fix postgres issue with LIKE conditions.
+
+0.1
+ - Initial release.
+
+
+License
+=======
+
+The MIT License.
+
+.. _ticket 11305: https://code.djangoproject.com/ticket/11305
+.. _first issue: https://github.com/henriquebastos/django-aggregate-if/issues/1
diff --git a/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py b/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
new file mode 100644
index 0000000..4ea9e8c
--- /dev/null
+++ b/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
@@ -0,0 +1,164 @@
+# coding: utf-8
+'''
+Implements conditional aggregates.
+
+This code was based on the work of others found on the internet:
+
+1. http://web.archive.org/web/20101115170804/http://www.voteruniverse.com/Members/jlantz/blog/conditional-aggregates-in-django
+2. https://code.djangoproject.com/ticket/11305
+3. https://groups.google.com/forum/?fromgroups=#!topic/django-users/cjzloTUwmS0
+4. https://groups.google.com/forum/?fromgroups=#!topic/django-users/vVprMpsAnPo
+'''
+from __future__ import unicode_literals
+import six
+import django
+from django.db.models.aggregates import Aggregate as DjangoAggregate
+from django.db.models.sql.aggregates import Aggregate as DjangoSqlAggregate
+
+
+VERSION = django.VERSION[:2]
+
+
+class SqlAggregate(DjangoSqlAggregate):
+ conditional_template = '%(function)s(CASE WHEN %(condition)s THEN %(field)s ELSE null END)'
+
+ def __init__(self, col, source=None, is_summary=False, condition=None, **extra):
+ super(SqlAggregate, self).__init__(col, source, is_summary, **extra)
+ self.condition = condition
+
+ def relabel_aliases(self, change_map):
+ if VERSION < (1, 7):
+ super(SqlAggregate, self).relabel_aliases(change_map)
+ if self.has_condition:
+ condition_change_map = dict((k, v) for k, v in \
+ change_map.items() if k in self.condition.query.alias_map
+ )
+ self.condition.query.change_aliases(condition_change_map)
+
+ def relabeled_clone(self, change_map):
+ self.relabel_aliases(change_map)
+ return super(SqlAggregate, self).relabeled_clone(change_map)
+
+ def as_sql(self, qn, connection):
+ if self.has_condition:
+ self.sql_template = self.conditional_template
+ self.extra['condition'] = self._condition_as_sql(qn, connection)
+
+ return super(SqlAggregate, self).as_sql(qn, connection)
+
+ @property
+ def has_condition(self):
+ # Warning: bool(QuerySet) will hit the database
+ return self.condition is not None
+
+ def _condition_as_sql(self, qn, connection):
+ '''
+ Return sql for condition.
+ '''
+ def escape(value):
+ if isinstance(value, bool):
+ value = str(int(value))
+ if isinstance(value, six.string_types):
+ # Escape params used with LIKE
+ if '%' in value:
+ value = value.replace('%', '%%')
+ # Escape single quotes
+ if "'" in value:
+ value = value.replace("'", "''")
+ # Add single quote to text values
+ value = "'" + value + "'"
+ return value
+
+ sql, param = self.condition.query.where.as_sql(qn, connection)
+ param = map(escape, param)
+
+ return sql % tuple(param)
+
+
+class SqlSum(SqlAggregate):
+ sql_function = 'SUM'
+
+
+class SqlCount(SqlAggregate):
+ is_ordinal = True
+ sql_function = 'COUNT'
+ sql_template = '%(function)s(%(distinct)s%(field)s)'
+ conditional_template = '%(function)s(%(distinct)sCASE WHEN %(condition)s THEN %(field)s ELSE null END)'
+
+ def __init__(self, col, distinct=False, **extra):
+ super(SqlCount, self).__init__(col, distinct=distinct and 'DISTINCT ' or '', **extra)
+
+
+class SqlAvg(SqlAggregate):
+ is_computed = True
+ sql_function = 'AVG'
+
+
+class SqlMax(SqlAggregate):
+ sql_function = 'MAX'
+
+
+class SqlMin(SqlAggregate):
+ sql_function = 'MIN'
+
+
+class Aggregate(DjangoAggregate):
+ def __init__(self, lookup, only=None, **extra):
+ super(Aggregate, self).__init__(lookup, **extra)
+ self.only = only
+ self.condition = None
+
+ def _get_fields_from_Q(self, q):
+ fields = []
+ for child in q.children:
+ if hasattr(child, 'children'):
+ fields.extend(self._get_fields_from_Q(child))
+ else:
+ fields.append(child)
+ return fields
+
+ def add_to_query(self, query, alias, col, source, is_summary):
+ if self.only:
+ self.condition = query.model._default_manager.filter(self.only)
+ for child in self._get_fields_from_Q(self.only):
+ field_list = child[0].split('__')
+ # Pop off the last field if it's a query term ('gte', 'contains', 'isnull', etc.)
+ if field_list[-1] in query.query_terms:
+ field_list.pop()
+ # setup_joins have different returns in Django 1.5 and 1.6, but the order of what we need remains.
+ result = query.setup_joins(field_list, query.model._meta, query.get_initial_alias(), None)
+ join_list = result[3]
+
+ fname = 'promote_alias_chain' if VERSION < (1, 5) else 'promote_joins'
+ args = (join_list, True) if VERSION < (1, 7) else (join_list,)
+
+ promote = getattr(query, fname)
+ promote(*args)
+
+ aggregate = self.sql_klass(col, source=source, is_summary=is_summary, condition=self.condition, **self.extra)
+ query.aggregates[alias] = aggregate
+
+
+class Sum(Aggregate):
+ name = 'Sum'
+ sql_klass = SqlSum
+
+
+class Count(Aggregate):
+ name = 'Count'
+ sql_klass = SqlCount
+
+
+class Avg(Aggregate):
+ name = 'Avg'
+ sql_klass = SqlAvg
+
+
+class Max(Aggregate):
+ name = 'Max'
+ sql_klass = SqlMax
+
+
+class Min(Aggregate):
+ name = 'Min'
+ sql_klass = SqlMin
diff --git a/lib/toaster/contrib/django-aggregate-if-master/runtests.py b/lib/toaster/contrib/django-aggregate-if-master/runtests.py
new file mode 100755
index 0000000..2e55864
--- /dev/null
+++ b/lib/toaster/contrib/django-aggregate-if-master/runtests.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+import os
+import sys
+from optparse import OptionParser
+
+
+def parse_args():
+ parser = OptionParser()
+ parser.add_option('-s', '--settings', help='Define settings.')
+ parser.add_option('-t', '--unittest', help='Define which test to run. Default all.')
+ options, args = parser.parse_args()
+
+ if not options.settings:
+ parser.print_help()
+ sys.exit(1)
+
+ if not options.unittest:
+ options.unittest = ['aggregation']
+
+ return options
+
+
+def get_runner(settings_module):
+ '''
+ Asks Django for the TestRunner defined in settings or the default one.
+ '''
+ os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
+
+ import django
+ from django.test.utils import get_runner
+ from django.conf import settings
+
+ if hasattr(django, 'setup'):
+ django.setup()
+
+ return get_runner(settings)
+
+
+def runtests():
+ options = parse_args()
+ TestRunner = get_runner(options.settings)
+ runner = TestRunner(verbosity=1, interactive=True, failfast=False)
+ sys.exit(runner.run_tests([]))
+
+
+if __name__ == '__main__':
+ runtests()
diff --git a/lib/toaster/contrib/django-aggregate-if-master/setup.py b/lib/toaster/contrib/django-aggregate-if-master/setup.py
new file mode 100644
index 0000000..aed3db1
--- /dev/null
+++ b/lib/toaster/contrib/django-aggregate-if-master/setup.py
@@ -0,0 +1,33 @@
+# coding: utf-8
+from setuptools import setup
+import os
+
+
+setup(name='django-aggregate-if',
+ version='0.5',
+ description='Conditional aggregates for Django, just like the famous SumIf in Excel.',
+ long_description=open(os.path.join(os.path.dirname(__file__), "README.rst")).read(),
+ author="Henrique Bastos", author_email="henrique@bastos.net",
+ license="MIT",
+ py_modules=['aggregate_if'],
+ install_requires=[
+ 'six>=1.6.1',
+ ],
+ zip_safe=False,
+ platforms='any',
+ include_package_data=True,
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Framework :: Django',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: MIT License',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Topic :: Database',
+ 'Topic :: Software Development :: Libraries',
+ ],
+ url='http://github.com/henriquebastos/django-aggregate-if/',
+)
diff --git a/lib/toaster/contrib/django-aggregate-if-master/tox.ini b/lib/toaster/contrib/django-aggregate-if-master/tox.ini
new file mode 100644
index 0000000..78beb14
--- /dev/null
+++ b/lib/toaster/contrib/django-aggregate-if-master/tox.ini
@@ -0,0 +1,198 @@
+[tox]
+envlist =
+ py27-django1.4-sqlite,
+ py27-django1.4-postgres,
+ py27-django1.4-mysql,
+
+ py27-django1.5-sqlite,
+ py27-django1.5-postgres,
+ py27-django1.5-mysql,
+
+ py27-django1.6-sqlite,
+ py27-django1.6-postgres,
+ py27-django1.6-mysql,
+
+ py27-django1.7-sqlite,
+ py27-django1.7-postgres,
+ py27-django1.7-mysql,
+
+ py34-django1.6-sqlite,
+ py34-django1.6-postgres,
+ #py34-django1.6-mysql
+
+ py34-django1.7-sqlite,
+ py34-django1.7-postgres,
+ #py34-django1.7-mysql
+
+[testenv]
+whitelist_externals=
+ mysql
+ psql
+
+# Python 2.7
+# Django 1.4
+[testenv:py27-django1.4-sqlite]
+basepython = python2.7
+deps =
+ django==1.4
+commands = python runtests.py --settings tests.test_sqlite
+
+[testenv:py27-django1.4-postgres]
+basepython = python2.7
+deps =
+ django==1.4
+ psycopg2
+commands =
+ psql -c 'create database aggregation;' postgres
+ python runtests.py --settings tests.test_postgres
+ psql -c 'drop database aggregation;' postgres
+
+[testenv:py27-django1.4-mysql]
+basepython = python2.7
+deps =
+ django==1.4
+ mysql-python
+commands =
+ mysql -e 'create database aggregation;'
+ python runtests.py --settings tests.test_mysql
+ mysql -e 'drop database aggregation;'
+
+# Django 1.5
+[testenv:py27-django1.5-sqlite]
+basepython = python2.7
+deps =
+ django==1.5
+commands = python runtests.py --settings tests.test_sqlite
+
+[testenv:py27-django1.5-postgres]
+basepython = python2.7
+deps =
+ django==1.5
+ psycopg2
+commands =
+ psql -c 'create database aggregation;' postgres
+ python runtests.py --settings tests.test_postgres
+ psql -c 'drop database aggregation;' postgres
+
+[testenv:py27-django1.5-mysql]
+basepython = python2.7
+deps =
+ django==1.5
+ mysql-python
+commands =
+ mysql -e 'create database aggregation;'
+ python runtests.py --settings tests.test_mysql
+ mysql -e 'drop database aggregation;'
+
+# Django 1.6
+[testenv:py27-django1.6-sqlite]
+basepython = python2.7
+deps =
+ django==1.6
+commands = python runtests.py --settings tests.test_sqlite
+
+[testenv:py27-django1.6-postgres]
+basepython = python2.7
+deps =
+ django==1.6
+ psycopg2
+commands =
+ psql -c 'create database aggregation;' postgres
+ python runtests.py --settings tests.test_postgres
+ psql -c 'drop database aggregation;' postgres
+
+[testenv:py27-django1.6-mysql]
+basepython = python2.7
+deps =
+ django==1.6
+ mysql-python
+commands =
+ mysql -e 'create database aggregation;'
+ python runtests.py --settings tests.test_mysql
+ mysql -e 'drop database aggregation;'
+
+
+# Python 2.7 and Django 1.7
+[testenv:py27-django1.7-sqlite]
+basepython = python2.7
+deps =
+ django==1.7
+commands = python runtests.py --settings tests.test_sqlite
+
+[testenv:py27-django1.7-postgres]
+basepython = python2.7
+deps =
+ django==1.7
+ psycopg2
+commands =
+ psql -c 'create database aggregation;' postgres
+ python runtests.py --settings tests.test_postgres
+ psql -c 'drop database aggregation;' postgres
+
+[testenv:py27-django1.7-mysql]
+basepython = python2.7
+deps =
+ django==1.7
+ mysql-python
+commands =
+ mysql -e 'create database aggregation;'
+ python runtests.py --settings tests.test_mysql
+ mysql -e 'drop database aggregation;'
+
+
+# Python 3.4
+# Django 1.6
+[testenv:py34-django1.6-sqlite]
+basepython = python3.4
+deps =
+ django==1.6
+commands = python runtests.py --settings tests.test_sqlite
+
+[testenv:py34-django1.6-postgres]
+basepython = python3.4
+deps =
+ django==1.6
+ psycopg2
+commands =
+ psql -c 'create database aggregation;' postgres
+ python runtests.py --settings tests.test_postgres
+ psql -c 'drop database aggregation;' postgres
+
+[testenv:py34-django1.6-mysql]
+basepython = python3.4
+deps =
+ django==1.6
+ mysql-python3
+commands =
+ mysql -e 'create database aggregation;'
+ python runtests.py --settings tests.test_mysql
+ mysql -e 'drop database aggregation;'
+
+
+# Python 3.4
+# Django 1.7
+[testenv:py34-django1.7-sqlite]
+basepython = python3.4
+deps =
+ django==1.7
+commands = python runtests.py --settings tests.test_sqlite
+
+[testenv:py34-django1.7-postgres]
+basepython = python3.4
+deps =
+ django==1.7
+ psycopg2
+commands =
+ psql -c 'create database aggregation;' postgres
+ python runtests.py --settings tests.test_postgres
+ psql -c 'drop database aggregation;' postgres
+
+[testenv:py34-django1.7-mysql]
+basepython = python3.4
+deps =
+ django==1.7
+ mysql-python3
+commands =
+ mysql -e 'create database aggregation;'
+ python runtests.py --settings tests.test_mysql
+ mysql -e 'drop database aggregation;'
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 11/23] toaster: bring django-aggregate-if into the project
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (9 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 10/23] toaster: add django-aggregate-if Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 12/23] toaster: refactor build model Alex DAMIAN
` (11 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
We bring the django-aggregate-if module (with minor changes to import)
into the project.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py | 2 +-
lib/toaster/toastermain/settings.py | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py b/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
index 4ea9e8c..d5f3427 100644
--- a/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
+++ b/lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
@@ -10,7 +10,7 @@ This code was based on the work of others found on the internet:
4. https://groups.google.com/forum/?fromgroups=#!topic/django-users/vVprMpsAnPo
'''
from __future__ import unicode_literals
-import six
+from django.utils import six
import django
from django.db.models.aggregates import Aggregate as DjangoAggregate
from django.db.models.sql.aggregates import Aggregate as DjangoSqlAggregate
diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index 141ad08..60e80de 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -393,3 +393,12 @@ class InvalidString(str):
"Undefined variable or unknown value for: \"%s\"" % other)
TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
+
+import sys
+sys.path.append(
+ os.path.join(
+ os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ "contrib"),
+ "django-aggregate-if-master")
+ )
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 00/23] toaster patchset
@ 2015-06-25 10:33 Alex DAMIAN
2015-06-25 10:33 ` [PATCH 01/23] toastergui: fix angular error Alex DAMIAN
` (22 more replies)
0 siblings, 23 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
This toaster patchset brings in filters for new toaster tables,
removes the differences between managed and analysis modes
in favour of project types, and more minor fixes.
Can you please cherry-pick ?
Thank you,
Alex
The following changes since commit 32166ac3c85ff3c04081580ae76bd63590d6ff3e:
runqueue: Sanity check BB_NUMBER_THREADS (2015-06-19 22:43:48 +0100)
are available in the git repository at:
git://git.yoctoproject.org/poky-contrib adamian/20150625-submission-bb
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=adamian/20150625-submission-bb
Alexandru DAMIAN (16):
toastergui: fix angular error
toaster: improve the buildenvironment API
toastergui: enable strict variable checking
toaster: fixing undefined variables
toaster: remove MANAGED references
toaster: remove BuildRequest references
toaster: refactor the builds pages
toaster: fill in build data from buildrequest
toaster: fixes after replacing BuildRequest with Build
toaster: add django-aggregate-if
toaster: bring django-aggregate-if into the project
toaster: refactor build model
toastergui: select project types
toaster: delete multiple builds
toasterui: verify variable before usage
toasterui: fixes after html5 compliance testing
Michael Wood (7):
toaster: Enable toastertable cache
toaster: toastertable Pass up the kwargs for setup_filter
toaster: table.js fix filter visual indicator and interaction
toaster: Restore 'in project' filters to main tables
toaster: bldcontrol Ignore toasterconf files in own directories
toaster: split orm app into it's own module and app
toaster: Add url pattern for backward compatibility
lib/bb/ui/buildinfohelper.py | 73 ++-
lib/bb/ui/toasterui.py | 27 +-
lib/toaster/bldcollector/__init__.py | 0
lib/toaster/{orm => bldcollector}/admin.py | 3 +-
lib/toaster/{orm => bldcollector}/urls.py | 3 +-
lib/toaster/{orm => bldcollector}/views.py | 0
lib/toaster/bldcontrol/bbcontroller.py | 3 +
lib/toaster/bldcontrol/localhostbecontroller.py | 24 +-
.../management/commands/checksettings.py | 2 +
.../bldcontrol/management/commands/runbuilds.py | 69 ++-
lib/toaster/bldcontrol/models.py | 3 +
lib/toaster/bldcontrol/sshbecontroller.py | 21 +
.../contrib/django-aggregate-if-master/.gitignore | 10 +
.../contrib/django-aggregate-if-master/.travis.yml | 50 ++
.../contrib/django-aggregate-if-master/LICENSE | 21 +
.../contrib/django-aggregate-if-master/README.rst | 156 +++++
.../django-aggregate-if-master/aggregate_if.py | 164 ++++++
.../contrib/django-aggregate-if-master/runtests.py | 48 ++
.../contrib/django-aggregate-if-master/setup.py | 33 ++
.../contrib/django-aggregate-if-master/tox.ini | 198 +++++++
...ect__chg_field_project_bitbake_version__chg_.py | 5 +-
...k__add_field_layer_version_local_path__del_f.py | 343 +++++++++++
...ings_no__del_field_build_errors_no__del_fiel.py | 353 ++++++++++++
lib/toaster/orm/models.py | 67 ++-
lib/toaster/toastergui/static/js/base.js | 2 +-
lib/toaster/toastergui/static/js/projectapp.js | 20 +-
lib/toaster/toastergui/static/js/table.js | 59 +-
lib/toaster/toastergui/tables.py | 75 ++-
lib/toaster/toastergui/templates/base.html | 20 +-
.../toastergui/templates/basebuilddetailpage.html | 4 +-
.../toastergui/templates/basebuildpage.html | 7 +-
.../toastergui/templates/basetable_top.html | 11 +-
.../templates/basetable_top_buildprojects.html | 2 -
.../templates/basetable_top_projectbuilds.html | 2 -
lib/toaster/toastergui/templates/bpackage.html | 3 -
.../toastergui/templates/builddashboard.html | 39 +-
.../templates/{build.html => builds.html} | 35 +-
.../toastergui/templates/configuration.html | 6 -
lib/toaster/toastergui/templates/configvars.html | 6 +-
.../toastergui/templates/detail_search_header.html | 2 +-
.../toastergui/templates/detail_sorted_header.html | 2 +-
.../toastergui/templates/filtersnippet.html | 4 +-
.../templates/generic-toastertable-page.html | 2 +
lib/toaster/toastergui/templates/importlayer.html | 15 +-
lib/toaster/toastergui/templates/landing.html | 37 +-
.../toastergui/templates/managed_builds.html | 167 ------
.../toastergui/templates/managed_mrb_section.html | 193 -------
lib/toaster/toastergui/templates/mrb_section.html | 70 ++-
lib/toaster/toastergui/templates/newproject.html | 99 ++--
.../toastergui/templates/package_detail_base.html | 7 -
lib/toaster/toastergui/templates/project.html | 39 +-
.../toastergui/templates/projectbuilds.html | 30 +-
lib/toaster/toastergui/templates/projects.html | 27 +-
lib/toaster/toastergui/templates/recipe.html | 9 +-
lib/toaster/toastergui/templates/recipes.html | 7 +-
lib/toaster/toastergui/templates/runagain.html | 7 -
lib/toaster/toastergui/templates/target.html | 7 +-
lib/toaster/toastergui/templates/task.html | 36 +-
lib/toaster/toastergui/templates/tasks.html | 7 +-
.../toastergui/templates/toastertable-simple.html | 4 +-
lib/toaster/toastergui/templates/toastertable.html | 4 +-
lib/toaster/toastergui/urls.py | 3 -
lib/toaster/toastergui/views.py | 633 +++++----------------
lib/toaster/toastergui/widgets.py | 19 +-
.../toastermain/management/commands/builddelete.py | 56 +-
lib/toaster/toastermain/settings.py | 18 +-
lib/toaster/toastermain/urls.py | 4 +
67 files changed, 2191 insertions(+), 1284 deletions(-)
create mode 100644 lib/toaster/bldcollector/__init__.py
rename lib/toaster/{orm => bldcollector}/admin.py (92%)
rename lib/toaster/{orm => bldcollector}/urls.py (95%)
rename lib/toaster/{orm => bldcollector}/views.py (100%)
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/.gitignore
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/.travis.yml
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/LICENSE
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/README.rst
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py
create mode 100755 lib/toaster/contrib/django-aggregate-if-master/runtests.py
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/setup.py
create mode 100644 lib/toaster/contrib/django-aggregate-if-master/tox.ini
create mode 100644 lib/toaster/orm/migrations/0022_auto__add_field_target_task__add_field_layer_version_local_path__del_f.py
create mode 100644 lib/toaster/orm/migrations/0023_auto__del_field_build_warnings_no__del_field_build_errors_no__del_fiel.py
rename lib/toaster/toastergui/templates/{build.html => builds.html} (75%)
delete mode 100644 lib/toaster/toastergui/templates/managed_builds.html
delete mode 100644 lib/toaster/toastergui/templates/managed_mrb_section.html
delete mode 100644 lib/toaster/toastergui/templates/runagain.html
--
1.9.1
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 12/23] toaster: refactor build model
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (10 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 11/23] toaster: bring django-aggregate-if into the project Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 13/23] toastergui: select project types Alex DAMIAN
` (10 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
We remove the "timespent", "errors_no" and "warnings_no" fields
in favor of computing the needed values at runtime. This prevents
inconsistencies in the UI.
Also removeing all references to BuildRequests from the interface -
all build details now display in the build dashboard.
Minor fixes related to data logging.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/bb/ui/buildinfohelper.py | 70 ++--
lib/bb/ui/toasterui.py | 3 +
lib/toaster/bldcontrol/localhostbecontroller.py | 2 +-
.../bldcontrol/management/commands/runbuilds.py | 10 +-
...ings_no__del_field_build_errors_no__del_fiel.py | 353 +++++++++++++++++++++
lib/toaster/orm/models.py | 19 +-
.../toastergui/templates/builddashboard.html | 23 +-
lib/toaster/toastergui/templates/builds.html | 22 +-
.../toastergui/templates/configuration.html | 6 -
.../toastergui/templates/filtersnippet.html | 2 +
lib/toaster/toastergui/templates/landing.html | 37 +--
lib/toaster/toastergui/templates/mrb_section.html | 29 +-
.../toastergui/templates/package_detail_base.html | 7 -
.../toastergui/templates/projectbuilds.html | 29 +-
lib/toaster/toastergui/templates/projects.html | 9 +-
lib/toaster/toastergui/templates/recipe.html | 9 +-
lib/toaster/toastergui/templates/recipes.html | 7 +-
lib/toaster/toastergui/templates/runagain.html | 7 -
lib/toaster/toastergui/templates/target.html | 5 -
lib/toaster/toastergui/templates/task.html | 36 +--
lib/toaster/toastergui/templates/tasks.html | 7 +-
lib/toaster/toastergui/urls.py | 3 -
lib/toaster/toastergui/views.py | 17 +-
23 files changed, 503 insertions(+), 209 deletions(-)
create mode 100644 lib/toaster/orm/migrations/0023_auto__del_field_build_warnings_no__del_field_build_errors_no__del_fiel.py
delete mode 100644 lib/toaster/toastergui/templates/runagain.html
diff --git a/lib/bb/ui/buildinfohelper.py b/lib/bb/ui/buildinfohelper.py
index 8b63f70..63976b5 100644
--- a/lib/bb/ui/buildinfohelper.py
+++ b/lib/bb/ui/buildinfohelper.py
@@ -16,7 +16,6 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-import datetime
import sys
import bb
import re
@@ -24,6 +23,7 @@ import ast
os.environ["DJANGO_SETTINGS_MODULE"] = "toaster.toastermain.settings"
+from django.utils import timezone
import toaster.toastermain.settings as toaster_django_settings
from toaster.orm.models import Build, Task, Recipe, Layer_Version, Layer, Target, LogMessage, HelpText
from toaster.orm.models import Target_Image_File, BuildArtifact
@@ -41,7 +41,7 @@ import logging
from django.db import transaction, connection
-logger = logging.getLogger("BitBake")
+logger = logging.getLogger("ToasterLogger")
class NotExisting(Exception):
@@ -135,16 +135,18 @@ class ORMWrapper(object):
if buildrequest is not None:
build = buildrequest.build
- build.machine=build_info['machine'],
- build.distro=build_info['distro'],
- build.distro_version=build_info['distro_version'],
- build.completed_on=build_info['started_on'],
- build.cooker_log_path=build_info['cooker_log_path'],
- build.build_name=build_info['build_name'],
+ logger.info("Updating existing build, with %s" % build_info)
+ build.machine=build_info['machine']
+ build.distro=build_info['distro']
+ build.distro_version=build_info['distro_version']
+ started_on=build_info['started_on']
+ completed_on=build_info['started_on']
+ build.cooker_log_path=build_info['cooker_log_path']
+ build.build_name=build_info['build_name']
build.bitbake_version=build_info['bitbake_version']
build.save()
- build.target_set.delete()
+ Target.objects.filter(build = build).delete()
else:
build = Build.objects.create(
@@ -188,10 +190,7 @@ class ORMWrapper(object):
if errors or taskfailures:
outcome = Build.FAILED
- build.completed_on = datetime.datetime.now()
- build.timespent = int((build.completed_on - build.started_on).total_seconds())
- build.errors_no = errors
- build.warnings_no = warnings
+ build.completed_on = timezone.now()
build.outcome = outcome
build.save()
@@ -687,8 +686,8 @@ class BuildInfoHelper(object):
build_info['machine'] = self.server.runCommand(["getVariable", "MACHINE"])[0]
build_info['distro'] = self.server.runCommand(["getVariable", "DISTRO"])[0]
build_info['distro_version'] = self.server.runCommand(["getVariable", "DISTRO_VERSION"])[0]
- build_info['started_on'] = datetime.datetime.now()
- build_info['completed_on'] = datetime.datetime.now()
+ build_info['started_on'] = timezone.now()
+ build_info['completed_on'] = timezone.now()
build_info['cooker_log_path'] = self.server.runCommand(["getVariable", "BB_CONSOLELOG"])[0]
build_info['build_name'] = self.server.runCommand(["getVariable", "BUILDNAME"])[0]
build_info['bitbake_version'] = self.server.runCommand(["getVariable", "BB_VERSION"])[0]
@@ -857,6 +856,30 @@ class BuildInfoHelper(object):
# Save build configuration
data = self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0]
+
+ # convert the paths from absolute to relative to either the build directory or layer checkouts
+ path_prefixes = []
+
+ br_id, be_id = self.brbe.split(":")
+ from bldcontrol.models import BuildEnvironment, BuildRequest
+ be = BuildEnvironment.objects.get(pk = be_id)
+ path_prefixes.append(be.builddir)
+
+ for layer in sorted(self.orm_wrapper.layer_version_objects, key = lambda x:len(x.local_path), reverse=True):
+ path_prefixes.append(layer.local_path)
+
+ # we strip the prefixes
+ for k in data:
+ if not bool(data[k]['func']):
+ for vh in data[k]['history']:
+ if not 'documentation.conf' in vh['file']:
+ abs_file_name = vh['file']
+ for pp in path_prefixes:
+ if abs_file_name.startswith(pp + "/"):
+ vh['file']=abs_file_name[len(pp + "/"):]
+ break
+
+ # save the variables
self.orm_wrapper.save_build_variables(build_obj, data)
return self.brbe
@@ -1031,7 +1054,7 @@ class BuildInfoHelper(object):
mevent.taskhash = taskhash
task_information = self._get_task_information(mevent,recipe)
- task_information['start_time'] = datetime.datetime.now()
+ task_information['start_time'] = timezone.now()
task_information['outcome'] = Task.OUTCOME_NA
task_information['sstate_checksum'] = taskhash
task_information['sstate_result'] = Task.SSTATE_MISS
@@ -1206,6 +1229,7 @@ class BuildInfoHelper(object):
)
def _store_build_done(self, errorcode):
+ logger.info("Build exited with errorcode %d", errorcode)
br_id, be_id = self.brbe.split(":")
from bldcontrol.models import BuildEnvironment, BuildRequest
be = BuildEnvironment.objects.get(pk = be_id)
@@ -1225,7 +1249,7 @@ class BuildInfoHelper(object):
mockevent.levelno = format.ERROR
mockevent.msg = text
mockevent.pathname = '-- None'
- mockevent.lineno = -1
+ mockevent.lineno = LogMessage.ERROR
self.store_log_event(mockevent)
def store_log_exception(self, text, backtrace = ""):
@@ -1249,13 +1273,12 @@ class BuildInfoHelper(object):
if not 'backlog' in self.internal_state:
self.internal_state['backlog'] = []
self.internal_state['backlog'].append(event)
- else: # we're under Toaster control, post the errors to the build request
+ return
+ else: # we're under Toaster control, the build is already created
from bldcontrol.models import BuildRequest, BRError
br, be = self.brbe.split(":")
buildrequest = BuildRequest.objects.get(pk = br)
- brerror = BRError.objects.create(req = buildrequest, errtype="build", errmsg = event.msg)
-
- return
+ self.internal_state['build'] = buildrequest.build
if 'build' in self.internal_state and 'backlog' in self.internal_state:
# if we have a backlog of events, do our best to save them here
@@ -1273,14 +1296,15 @@ class BuildInfoHelper(object):
log_information['level'] = LogMessage.ERROR
elif event.levelno == format.WARNING:
log_information['level'] = LogMessage.WARNING
- elif event.levelno == -1: # toaster self-logging
- log_information['level'] = -1
+ elif event.levelno == -2: # toaster self-logging
+ log_information['level'] = -2
else:
log_information['level'] = LogMessage.INFO
log_information['message'] = event.msg
log_information['pathname'] = event.pathname
log_information['lineno'] = event.lineno
+ logger.info("Logging error 2: %s" % log_information)
self.orm_wrapper.create_logmessage(log_information)
def close(self, errorcode):
diff --git a/lib/bb/ui/toasterui.py b/lib/bb/ui/toasterui.py
index 9c59fd0..28fdd82 100644
--- a/lib/bb/ui/toasterui.py
+++ b/lib/bb/ui/toasterui.py
@@ -140,6 +140,9 @@ def main(server, eventHandler, params ):
continue
if isinstance(event, logging.LogRecord):
+ if event.levelno == -1:
+ event.levelno = format.ERROR
+
buildinfohelper.store_log_event(event)
if event.levelno >= format.ERROR:
errors = errors + 1
diff --git a/lib/toaster/bldcontrol/localhostbecontroller.py b/lib/toaster/bldcontrol/localhostbecontroller.py
index ad0a561..3e16837 100644
--- a/lib/toaster/bldcontrol/localhostbecontroller.py
+++ b/lib/toaster/bldcontrol/localhostbecontroller.py
@@ -113,7 +113,7 @@ class LocalhostBEController(BuildEnvironmentController):
# get the file length; we need to detect the _last_ start of the toaster UI, not the first
toaster_ui_log_filelength = 0
if os.path.exists(toaster_ui_log_filepath):
- with open(toaster_ui_log_filepath, "r") as f:
+ with open(toaster_ui_log_filepath, "w") as f:
f.seek(0, 2) # jump to the end
toaster_ui_log_filelength = f.tell()
diff --git a/lib/toaster/bldcontrol/management/commands/runbuilds.py b/lib/toaster/bldcontrol/management/commands/runbuilds.py
index bcf3b04..33cc94e 100644
--- a/lib/toaster/bldcontrol/management/commands/runbuilds.py
+++ b/lib/toaster/bldcontrol/management/commands/runbuilds.py
@@ -107,20 +107,24 @@ class Command(NoArgsCommand):
def cleanup(self):
from django.utils import timezone
from datetime import timedelta
- # DISABLED environments locked for more than 30 seconds - they should be unlocked
- #BuildEnvironment.objects.filter(lock=BuildEnvironment.LOCK_LOCK).filter(updated__lt = timezone.now() - timedelta(seconds = 30)).update(lock = BuildEnvironment.LOCK_FREE)
+ # environments locked for more than 30 seconds - they should be unlocked
+ BuildEnvironment.objects.filter(buildrequest__state__in=[BuildRequest.REQ_FAILED, BuildRequest.REQ_COMPLETED]).filter(lock=BuildEnvironment.LOCK_LOCK).filter(updated__lt = timezone.now() - timedelta(seconds = 30)).update(lock = BuildEnvironment.LOCK_FREE)
# update all Builds that failed to start
for br in BuildRequest.objects.filter(state = BuildRequest.REQ_FAILED, build__outcome = Build.IN_PROGRESS):
- br.build.outcome = Build.FAILED
# transpose the launch errors in ToasterExceptions
+ br.build.outcome = Build.FAILED
for brerror in br.brerror_set.all():
logger.debug("Saving error %s" % brerror)
LogMessage.objects.create(build = br.build, level = LogMessage.EXCEPTION, message = brerror.errmsg)
br.build.save()
+ # we don't have a true build object here; hence, toasterui didn't have a change to release the BE lock
+ br.environment.lock = BuildEnvironment.LOCK_FREE
+ br.environment.save()
+
# update all BuildRequests without a build created
diff --git a/lib/toaster/orm/migrations/0023_auto__del_field_build_warnings_no__del_field_build_errors_no__del_fiel.py b/lib/toaster/orm/migrations/0023_auto__del_field_build_warnings_no__del_field_build_errors_no__del_fiel.py
new file mode 100644
index 0000000..b5b200cd
--- /dev/null
+++ b/lib/toaster/orm/migrations/0023_auto__del_field_build_warnings_no__del_field_build_errors_no__del_fiel.py
@@ -0,0 +1,353 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Deleting field 'Build.warnings_no'
+ db.delete_column(u'orm_build', 'warnings_no')
+
+ # Deleting field 'Build.errors_no'
+ db.delete_column(u'orm_build', 'errors_no')
+
+ # Deleting field 'Build.timespent'
+ db.delete_column(u'orm_build', 'timespent')
+
+
+ def backwards(self, orm):
+ # Adding field 'Build.warnings_no'
+ db.add_column(u'orm_build', 'warnings_no',
+ self.gf('django.db.models.fields.IntegerField')(default=0),
+ keep_default=False)
+
+ # Adding field 'Build.errors_no'
+ db.add_column(u'orm_build', 'errors_no',
+ self.gf('django.db.models.fields.IntegerField')(default=0),
+ keep_default=False)
+
+ # Adding field 'Build.timespent'
+ db.add_column(u'orm_build', 'timespent',
+ self.gf('django.db.models.fields.IntegerField')(default=0),
+ keep_default=False)
+
+
+ models = {
+ u'orm.bitbakeversion': {
+ 'Meta': {'object_name': 'BitbakeVersion'},
+ 'branch': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'dirpath': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'giturl': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'})
+ },
+ u'orm.branch': {
+ 'Meta': {'unique_together': "(('layer_source', 'name'), ('layer_source', 'up_id'))", 'object_name': 'Branch'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'True', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'short_description': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'})
+ },
+ u'orm.build': {
+ 'Meta': {'object_name': 'Build'},
+ 'bitbake_version': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'build_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'completed_on': ('django.db.models.fields.DateTimeField', [], {}),
+ 'cooker_log_path': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+ 'distro': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'distro_version': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'machine': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'outcome': ('django.db.models.fields.IntegerField', [], {'default': '2'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+ 'started_on': ('django.db.models.fields.DateTimeField', [], {})
+ },
+ u'orm.buildartifact': {
+ 'Meta': {'object_name': 'BuildArtifact'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+ 'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+ 'file_size': ('django.db.models.fields.IntegerField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ u'orm.helptext': {
+ 'Meta': {'object_name': 'HelpText'},
+ 'area': ('django.db.models.fields.IntegerField', [], {}),
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'helptext_build'", 'to': u"orm['orm.Build']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ u'orm.layer': {
+ 'Meta': {'unique_together': "(('layer_source', 'up_id'), ('layer_source', 'name'))", 'object_name': 'Layer'},
+ 'description': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'}),
+ 'vcs_url': ('django.db.models.fields.URLField', [], {'default': 'None', 'max_length': '200', 'null': 'True'}),
+ 'vcs_web_file_base_url': ('django.db.models.fields.URLField', [], {'default': 'None', 'max_length': '200', 'null': 'True'}),
+ 'vcs_web_tree_base_url': ('django.db.models.fields.URLField', [], {'default': 'None', 'max_length': '200', 'null': 'True'}),
+ 'vcs_web_url': ('django.db.models.fields.URLField', [], {'default': 'None', 'max_length': '200', 'null': 'True'})
+ },
+ u'orm.layer_version': {
+ 'Meta': {'unique_together': "(('layer_source', 'up_id'),)", 'object_name': 'Layer_Version'},
+ 'branch': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'layer_version_build'", 'null': 'True', 'to': u"orm['orm.Build']"}),
+ 'commit': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'dirpath': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'local_path': ('django.db.models.fields.FilePathField', [], {'default': "'/'", 'max_length': '1024'}),
+ 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.Project']", 'null': 'True'}),
+ 'up_branch': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.Branch']", 'null': 'True'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'})
+ },
+ u'orm.layersource': {
+ 'Meta': {'unique_together': "(('sourcetype', 'apiurl'),)", 'object_name': 'LayerSource'},
+ 'apiurl': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '63'}),
+ 'sourcetype': ('django.db.models.fields.IntegerField', [], {})
+ },
+ u'orm.layerversiondependency': {
+ 'Meta': {'unique_together': "(('layer_source', 'up_id'),)", 'object_name': 'LayerVersionDependency'},
+ 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'dependees'", 'to': u"orm['orm.Layer_Version']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'dependencies'", 'to': u"orm['orm.Layer_Version']"}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'})
+ },
+ u'orm.logmessage': {
+ 'Meta': {'object_name': 'LogMessage'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'lineno': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+ 'pathname': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+ 'task': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Task']", 'null': 'True', 'blank': 'True'})
+ },
+ u'orm.machine': {
+ 'Meta': {'unique_together': "(('layer_source', 'up_id'),)", 'object_name': 'Machine'},
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Layer_Version']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'})
+ },
+ u'orm.package': {
+ 'Meta': {'object_name': 'Package'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'installed_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+ 'installed_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'recipe': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Recipe']", 'null': 'True'}),
+ 'revision': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+ 'section': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+ 'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+ },
+ u'orm.package_dependency': {
+ 'Meta': {'object_name': 'Package_Dependency'},
+ 'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+ 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_target'", 'to': u"orm['orm.Package']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_source'", 'to': u"orm['orm.Package']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']", 'null': 'True'})
+ },
+ u'orm.package_file': {
+ 'Meta': {'object_name': 'Package_File'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildfilelist_package'", 'to': u"orm['orm.Package']"}),
+ 'path': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+ 'size': ('django.db.models.fields.IntegerField', [], {})
+ },
+ u'orm.project': {
+ 'Meta': {'object_name': 'Project'},
+ 'bitbake_version': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.BitbakeVersion']", 'null': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Release']", 'null': 'True'}),
+ 'short_description': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'user_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
+ },
+ u'orm.projectlayer': {
+ 'Meta': {'unique_together': "(('project', 'layercommit'),)", 'object_name': 'ProjectLayer'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layercommit': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Layer_Version']", 'null': 'True'}),
+ 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"})
+ },
+ u'orm.projecttarget': {
+ 'Meta': {'object_name': 'ProjectTarget'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'task': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+ },
+ u'orm.projectvariable': {
+ 'Meta': {'object_name': 'ProjectVariable'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+ 'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+ },
+ u'orm.recipe': {
+ 'Meta': {'unique_together': "(('layer_version', 'file_path', 'pathflags'),)", 'object_name': 'Recipe'},
+ 'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+ 'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['orm.LayerSource']", 'null': 'True'}),
+ 'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
+ 'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'pathflags': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+ 'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'up_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'up_id': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+ },
+ u'orm.recipe_dependency': {
+ 'Meta': {'object_name': 'Recipe_Dependency'},
+ 'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+ 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_depends'", 'to': u"orm['orm.Recipe']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_recipe'", 'to': u"orm['orm.Recipe']"})
+ },
+ u'orm.release': {
+ 'Meta': {'object_name': 'Release'},
+ 'bitbake_version': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.BitbakeVersion']"}),
+ 'branch_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50'}),
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'helptext': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'})
+ },
+ u'orm.releasedefaultlayer': {
+ 'Meta': {'object_name': 'ReleaseDefaultLayer'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+ 'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Release']"})
+ },
+ u'orm.releaselayersourcepriority': {
+ 'Meta': {'unique_together': "(('release', 'layer_source'),)", 'object_name': 'ReleaseLayerSourcePriority'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.LayerSource']"}),
+ 'priority': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Release']"})
+ },
+ u'orm.target': {
+ 'Meta': {'object_name': 'Target'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'is_image': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'license_manifest_path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'task': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
+ },
+ u'orm.target_file': {
+ 'Meta': {'object_name': 'Target_File'},
+ 'directory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'directory_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'inodetype': ('django.db.models.fields.IntegerField', [], {}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+ 'permission': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'size': ('django.db.models.fields.IntegerField', [], {}),
+ 'sym_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'symlink_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+ },
+ u'orm.target_image_file': {
+ 'Meta': {'object_name': 'Target_Image_File'},
+ 'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '254'}),
+ 'file_size': ('django.db.models.fields.IntegerField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+ },
+ u'orm.target_installed_package': {
+ 'Meta': {'object_name': 'Target_Installed_Package'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildtargetlist_package'", 'to': u"orm['orm.Package']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+ },
+ u'orm.task': {
+ 'Meta': {'ordering': "('order', 'recipe')", 'unique_together': "(('build', 'recipe', 'task_name'),)", 'object_name': 'Task'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_build'", 'to': u"orm['orm.Build']"}),
+ 'cpu_usage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '8', 'decimal_places': '2'}),
+ 'disk_io': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'elapsed_time': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '8', 'decimal_places': '2'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'line_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'logfile': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+ 'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'outcome': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+ 'path_to_sstate_obj': ('django.db.models.fields.FilePathField', [], {'max_length': '500', 'blank': 'True'}),
+ 'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'to': u"orm['orm.Recipe']"}),
+ 'script_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'source_url': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+ 'sstate_checksum': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'sstate_result': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'task_executed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'task_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'work_directory': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ u'orm.task_dependency': {
+ 'Meta': {'object_name': 'Task_Dependency'},
+ 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_depends'", 'to': u"orm['orm.Task']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'task': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_task'", 'to': u"orm['orm.Task']"})
+ },
+ u'orm.toastersetting': {
+ 'Meta': {'object_name': 'ToasterSetting'},
+ 'helptext': ('django.db.models.fields.TextField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '63'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ u'orm.variable': {
+ 'Meta': {'object_name': 'Variable'},
+ 'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_build'", 'to': u"orm['orm.Build']"}),
+ 'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'human_readable_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'variable_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'variable_value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+ },
+ u'orm.variablehistory': {
+ 'Meta': {'object_name': 'VariableHistory'},
+ 'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'line_number': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'operation': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'variable': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vhistory'", 'to': u"orm['orm.Variable']"})
+ }
+ }
+
+ complete_apps = ['orm']
\ No newline at end of file
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index e93629b..077c94d 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -147,7 +147,7 @@ class Project(models.Model):
if (-1 == build_id):
return( 0 )
try:
- return Build.objects.filter(id = build_id)[ 0 ].errors_no
+ return Build.objects.filter(id = build_id)[ 0 ].errors.count()
except (Build.DoesNotExist,IndexError):
return( "not_found" )
@@ -156,7 +156,7 @@ class Project(models.Model):
if (-1 == build_id):
return( 0 )
try:
- return Build.objects.filter(id = build_id)[ 0 ].warnings_no
+ return Build.objects.filter(id = build_id)[ 0 ].warnings.count()
except (Build.DoesNotExist,IndexError):
return( "not_found" )
@@ -248,10 +248,7 @@ class Build(models.Model):
distro_version = models.CharField(max_length=100)
started_on = models.DateTimeField()
completed_on = models.DateTimeField()
- timespent = models.IntegerField(default=0)
outcome = models.IntegerField(choices=BUILD_OUTCOME, default=IN_PROGRESS)
- errors_no = models.IntegerField(default=0)
- warnings_no = models.IntegerField(default=0)
cooker_log_path = models.CharField(max_length=500)
build_name = models.CharField(max_length=100)
bitbake_version = models.CharField(max_length=50)
@@ -281,6 +278,17 @@ class Build(models.Model):
def toaster_exceptions(self):
return self.logmessage_set.filter(level=LogMessage.EXCEPTION)
+ @property
+ def errors(self):
+ return (self.logmessage_set.filter(level=LogMessage.ERROR)|self.logmessage_set.filter(level=LogMessage.EXCEPTION))
+
+ @property
+ def warnings(self):
+ return self.logmessage_set.filter(level=LogMessage.WARNING)
+
+ @property
+ def timespent_seconds(self):
+ return (self.completed_on - self.started_on).total_seconds()
def get_current_status(self):
from bldcontrol.models import BuildRequest
@@ -298,7 +306,6 @@ class BuildArtifact(models.Model):
file_name = models.FilePathField()
file_size = models.IntegerField()
-
def get_local_file_name(self):
try:
deploydir = Variable.objects.get(build = self.build, variable_name="DEPLOY_DIR").variable_value
diff --git a/lib/toaster/toastergui/templates/builddashboard.html b/lib/toaster/toastergui/templates/builddashboard.html
index 47b8d7a..bab8e38 100644
--- a/lib/toaster/toastergui/templates/builddashboard.html
+++ b/lib/toaster/toastergui/templates/builddashboard.html
@@ -23,22 +23,23 @@
</strong> on
{{build.completed_on|date:"d/m/y H:i"}}
</span>
-{% if build.warnings_no or build.errors_no %}
+{% if build.warnings.count or build.errors.count %}
with
{% endif %}
{%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
-{% if build.errors_no %}
- <span > <i class="icon-minus-sign red"></i><strong><a href="#errors" class="error show-errors"> {{build.errors_no}} error{{build.errors_no|pluralize}}</a></strong></span>
+{% if build.errors.count %}
+ <span > <i class="icon-minus-sign red"></i><strong><a href="#errors" class="error show-errors"> {{build.errors.count}} error{{build.errors.count|pluralize}}</a></strong></span>
{% endif %}
-{% if build.warnings_no %}
-{% if build.errors_no %}
+{% if build.warnings.count %}
+{% if build.errors.count %}
and
{% endif %}
- <span > <i class="icon-warning-sign yellow"></i><strong><a href="#warnings" class="warning show-warnings"> {{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a></strong></span>
+ <span > <i class="icon-warning-sign yellow"></i><strong><a href="#warnings" class="warning show-warnings"> {{build.warnings.count}} warning{{build.warnings.count|pluralize}}</a></strong></span>
{% endif %}
- <span class="pull-right">Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a>
+ <span class="pull-right">Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent_seconds|sectohms }}</a>
<a class="btn {%if build.outcome == build.SUCCEEDED%}btn-success{%else%}btn-danger{%endif%} pull-right log" href="{% url 'build_artifact' build.id "cookerlog" build.id %}">Download build log</a>
</span>
+
{%endif%}
</div>
{% if build.toaster_exceptions.count > 0 %}
@@ -52,14 +53,14 @@
</div>
</div>
-{% if build.errors_no %}
+{% if build.errors.count %}
<div class="accordion span10 pull-right" id="errors">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle error toggle-errors">
<h2 id="error-toggle">
<i class="icon-minus-sign"></i>
- {{build.errors_no}} error{{build.errors_no|pluralize}}
+ {{build.errors.count}} error{{build.errors.count|pluralize}}
</h2>
</a>
</div>
@@ -241,14 +242,14 @@
</div>
</div>
-{% if build.warnings_no %}
+{% if build.warnings.count %}
<div class="accordion span10 pull-right" id="warnings">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle warning toggle-warnings">
<h2 id="warning-toggle">
<i class="icon-warning-sign"></i>
- {{build.warnings_no}} warning{{build.warnings_no|pluralize}}
+ {{build.warnings.count}} warning{{build.warnings.count|pluralize}}
</h2>
</a>
</div>
diff --git a/lib/toaster/toastergui/templates/builds.html b/lib/toaster/toastergui/templates/builds.html
index e9211af..00d1d4e 100644
--- a/lib/toaster/toastergui/templates/builds.html
+++ b/lib/toaster/toastergui/templates/builds.html
@@ -46,7 +46,11 @@
<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 %}
+ <input id="search" name="search" class="input-xxlarge" type="text" value="
+ {% if request.GET.search %}
+ {{request.GET.search}}
+ {% endif %}"/>
+ {% 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 builds</button>
</form>
@@ -60,8 +64,8 @@
{% for build in objects %}
<tr class="data">
<td class="outcome">
- <a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a>
- </td>
+ <a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a>
+ </td>
<td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td>
<td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td>
<td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td>
@@ -77,19 +81,19 @@
<a href="{% url "tasks" build.id %}?filter=outcome%3A4">{{exectask.count}} task{{exectask.count|pluralize}}</a>
{%endif%}
</td>
- <td class="errors_no">
- {% if build.errors_no %}
- <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
+ <td class="errors.count">
+ {% if build.errors.count %}
+ <a class="errors.count error" href="{% url "builddashboard" build.id %}#errors">{{build.errors.count}} error{{build.errors.count|pluralize}}</a>
{%endif%}
</td>
- <td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td>
- <td class="time"><a href="{% url "buildtime" build.id %}">{{build.timespent|sectohms}}</a></td>
+ <td class="warnings.count">{% if build.warnings.count %}<a class="warnings.count warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings.count}} warning{{build.warnings.count|pluralize}}</a>{%endif%}</td>
+ <td class="time"><a href="{% url "buildtime" build.id %}">{{build.timespent_seconds|sectohms}}</a></td>
<td class="output">
{% if build.outcome == build.SUCCEEDED %}
<a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
{% endif %}
</td>
- <td>
+ <td>
<a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
</td>
</tr>
diff --git a/lib/toaster/toastergui/templates/configuration.html b/lib/toaster/toastergui/templates/configuration.html
index 2aa1ae1..3e48991 100644
--- a/lib/toaster/toastergui/templates/configuration.html
+++ b/lib/toaster/toastergui/templates/configuration.html
@@ -50,9 +50,6 @@
<th>Layer</th>
<th>Layer branch</th>
<th>Layer commit</th>
- {% if not MANAGED or not build.project %}
- <th>Layer directory</th>
- {% endif %}
</tr>
</thead>
<tbody>{% for lv in build.layer_version_build.all|dictsort:"layer.name" %}
@@ -63,9 +60,6 @@
<li>{{lv.commit}}</li> </ul>">
{{lv.commit|truncatechars:13}}
</a></td>
- {% if not MANAGED or not build.project %}
- <td>{{lv.local_path}}</td>
- {% endif %}
</tr>{% endfor %}
</tbody>
</table>
diff --git a/lib/toaster/toastergui/templates/filtersnippet.html b/lib/toaster/toastergui/templates/filtersnippet.html
index 56e2b21..1101aa8 100644
--- a/lib/toaster/toastergui/templates/filtersnippet.html
+++ b/lib/toaster/toastergui/templates/filtersnippet.html
@@ -40,8 +40,10 @@
{% endif %}
{% endfor %}
<!-- daterange persistence -->
+ {% if last_date_from and last_date_to %}
<input type="hidden" id="last_date_from_{{key}}" name="last_date_from" value="{{last_date_from}}"/>
<input type="hidden" id="last_date_to_{{key}}" name="last_date_to" value="{{last_date_to}}"/>
+ {% endif %}
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" data-key="{{key}}">Apply</button>
diff --git a/lib/toaster/toastergui/templates/landing.html b/lib/toaster/toastergui/templates/landing.html
index 4af849c..15cac47 100644
--- a/lib/toaster/toastergui/templates/landing.html
+++ b/lib/toaster/toastergui/templates/landing.html
@@ -9,7 +9,7 @@
<div class="container-fluid">
<div class="row-fluid">
<!-- Empty - no data in database -->
- <div class="hero-unit span12 {%if MANAGED%}well-transparent{%endif%}">
+ <div class="hero-unit span12 well-transparent">
<div class="row-fluid">
<div class="span6">
<h1>
@@ -17,7 +17,6 @@
</h1>
<p>A web interface to <a href="http://www.openembedded.org">OpenEmbedded</a> and <a href="http://www.yoctoproject.org/tools-resources/projects/bitbake">BitBake</a>, the <a href="http://www.yoctoproject.org">Yocto Project</a> build system.</p>
- {% if MANAGED %}
{% if lvs_nos %}
<p class="hero-actions">
@@ -37,7 +36,7 @@
</li>
</ul>
</div>
- {% endif %}
+ {% endif %}
<ul class="unstyled">
<li>
@@ -48,18 +47,6 @@
</li>
</ul>
- {% else %}
-
- <p class="hero-actions">
- <a class="btn btn-primary btn-large" href="http://www.yoctoproject.org/docs/latest/toaster-manual/toaster-manual.html">
- Show me the manual
- </a>
- <a class="btn btn-large" href="https://wiki.yoctoproject.org/wiki/Contribute_to_Toaster">
- I want to contribute
- </a>
- </p>
-
- {% endif %}
</div>
<div class="span6">
{% if MANAGED %}
@@ -70,26 +57,6 @@
</div>
</div>
</div>
-
- {% if not MANAGED %}
- <!-- Empty - no data in database -->
- <div class="page-header top-air">
- <h1>
- All builds
- </h1>
- </div>
- <div class="alert alert-info lead">
- Toaster has not recorded any builds yet. Go build something with
- <a href="http://www.yoctoproject.org/docs/current/yocto-project-qs/yocto-project-qs.html#test-run">
- Knotty
- </a>
- or
- <a href="https://www.yoctoproject.org/documentation/hob-manual">
- Hob
- </a>
- </div>
- {% endif %}
-
</div>
{% endblock %}
diff --git a/lib/toaster/toastergui/templates/mrb_section.html b/lib/toaster/toastergui/templates/mrb_section.html
index 7e84e41..d37b694 100644
--- a/lib/toaster/toastergui/templates/mrb_section.html
+++ b/lib/toaster/toastergui/templates/mrb_section.html
@@ -43,19 +43,35 @@
</div>
{%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
<div class="span2 lead">
- {% if build.errors_no %}
- <i class="icon-minus-sign red"></i> <a href="{%url 'builddashboard' build.pk%}#errors" class="error">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
+ {% if build.errors.count %}
+ <i class="icon-minus-sign red"></i> <a href="{%url 'builddashboard' build.pk%}#errors" class="error">{{build.errors.count}} error{{build.errors.count|pluralize}}</a>
{% endif %}
</div>
<div class="span2 lead">
- {% if build.warnings_no %}
- <i class="icon-warning-sign yellow"></i> <a href="{%url 'builddashboard' build.pk%}#warnings" class="warning">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>
+ {% if build.warnings.count %}
+ <i class="icon-warning-sign yellow"></i> <a href="{%url 'builddashboard' build.pk%}#warnings" class="warning">{{build.warnings.count}} warning{{build.warnings.count|pluralize}}</a>
{% endif %}
</div>
<div class="lead ">
- <span class="lead{%if not MANAGED or not build.project%} pull-right{%endif%}">
- Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent|sectohms }}</a>
+ <span class="lead">
+ Build time: <a href="{% url 'buildtime' build.pk %}">{{ build.timespent_seconds|sectohms }}</a>
</span>
+ <button 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 'projectbuilds' build.project.id as bpi %}{{bpi|json}},
+ {{build.project.name|json}},
+ {% url 'project' build.project.id as bpurl %}{{bpurl|json}},
+ {{build.target_set.all|get_tasks|json}})'>
+
+ Run again
+ </button>
</div>
{%endif%}
{%if build.outcome == build.IN_PROGRESS %}
@@ -77,6 +93,7 @@
function scheduleBuild(url, projectName, projectUrl, buildlist) {
console.log("scheduleBuild");
libtoaster.startABuild(url, null, buildlist.join(" "), function(){
+ console.log("reloading page");
window.location.reload();
}, null);
}
diff --git a/lib/toaster/toastergui/templates/package_detail_base.html b/lib/toaster/toastergui/templates/package_detail_base.html
index e2ec75d..a24bc8e 100644
--- a/lib/toaster/toastergui/templates/package_detail_base.html
+++ b/lib/toaster/toastergui/templates/package_detail_base.html
@@ -136,13 +136,6 @@
<dd class="iscommit">{{package.recipe.layer_version.commit}}</dd>
- {% if not MANAGED or not build.project %}
- <dt>
- Layer directory
- <i class="icon-question-sign get-help" title="Path to the layer providing the recipe that builds this package"></i>
- </dt>
- <dd><code>{{package.recipe.layer_version.local_path}}</code></dd>
- {% endif %}
</dl>
</div> <!-- row4 well -->
{% endblock twocolumns %}
diff --git a/lib/toaster/toastergui/templates/projectbuilds.html b/lib/toaster/toastergui/templates/projectbuilds.html
index 896c3b5..ac87d8c 100644
--- a/lib/toaster/toastergui/templates/projectbuilds.html
+++ b/lib/toaster/toastergui/templates/projectbuilds.html
@@ -79,25 +79,20 @@
{% query build.task_build outcome=4 order__gt=0 as exectask%}
{% if exectask.count == 1 %}
<a href="{% url "task" build.id exectask.0.id %}">{{exectask.0.recipe.name}}.{{exectask.0.task_name}}</a>
- {% if MANAGED and build.project %}
<a href="{% url 'build_artifact' build.id "tasklogfile" exectask.0.id %}">
<i class="icon-download-alt" title="" data-original-title="Download task log file"></i>
</a>
- {% endif %}
{% elif exectask.count > 1%}
<a href="{% url "tasks" build.id %}?filter=outcome%3A4">{{exectask.count}} task{{exectask.count|pluralize}}</a>
{%endif%}
</td>
- <td class="errors_no">
- {% if build.errors_no %}
- <a class="errors_no error" href="{% url "builddashboard" build.id %}#errors">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
+ <td class="errors.count">
+ {% if build.errors.count %}
+ <a class="errors.count error" href="{% url "builddashboard" build.id %}#errors">{{build.errors.count}} error{{build.errors.count|pluralize}}</a>
{%endif%}
</td>
- <td class="warnings_no">{% if build.warnings_no %}<a class="warnings_no warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td>
- <td class="time"><a href="{% url "buildtime" build.id %}">{{build.timespent|sectohms}}</a></td>
- {% if not MANAGED or not build.project %}
- <td class="log">{{build.cooker_log_path}}</td>
- {% endif %}
+ <td class="warnings.count">{% if build.warnings.count %}<a class="warnings.count warning" href="{% url "builddashboard" build.id %}#warnings">{{build.warnings.count}} warning{{build.warnings.count|pluralize}}</a>{%endif%}</td>
+ <td class="time"><a href="{% url "buildtime" build.id %}">{{build.timespent_seconds|sectohms}}</a></td>
<td class="output">
{% if build.outcome == build.SUCCEEDED %}
<a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
@@ -113,23 +108,23 @@
<tr class="data">
<td class="outcome">{% if br.state == br.REQ_FAILED %}<i class="icon-minus-sign error"></i>{%else%}FIXME_build_request_state{%endif%}</td>
<td class="target">
- <a href="{% url "buildrequestdetails" br.project.id br.id %}"><span data-toggle="tooltip" {%if br.brtarget_set.all.count > 1%}title="Targets: {%for target in br.brtarget_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{br.brtarget_set.all.0.target}} {%if br.brtarget_set.all.count > 1%}(+ {{br.brtarget_set.all.count|add:"-1"}}){%endif%} </span></a>
+ <a href="{% url "builddashboard" br.id %}"><span data-toggle="tooltip" {%if br.brtarget_set.all.count > 1%}title="Targets: {%for target in br.brtarget_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{br.brtarget_set.all.0.target}} {%if br.brtarget_set.all.count > 1%}(+ {{br.brtarget_set.all.count|add:"-1"}}){%endif%} </span></a>
</td>
<td class="machine">
- <a href="{% url "buildrequestdetails" br.project.id br.id %}">{{br.machine}}</a>
+ <a href="{% url "builddashboard" br.id %}">{{br.machine}}</a>
</td>
<td class="started_on">
- <a href="{% url "buildrequestdetails" br.project.id br.id %}">{{br.created|date:"d/m/y H:i"}}</a>
+ <a href="{% url "builddashboard" br.id %}">{{br.created|date:"d/m/y H:i"}}</a>
</td>
<td class="completed_on">
- <a href="{% url "buildrequestdetails" br.project.id br.id %}">{{br.updated|date:"d/m/y H:i"}}</a>
+ <a href="{% url "builddashboard" br.id %}">{{br.updated|date:"d/m/y H:i"}}</a>
</td>
<td class="failed_tasks error">
</td>
- <td class="errors_no">
- <a class="errors_no error" href="{% url "buildrequestdetails" br.project.id br.id %}#errors">{{br.brerror_set.all.count}} error{{br.brerror_set.all.count|pluralize}}</a>
+ <td class="errors.count">
+ <a class="errors.count error" href="{% url "builddashboard" br.id %}#errors">{{br.brerror_set.all.count}} error{{br.brerror_set.all.count|pluralize}}</a>
</td>
- <td class="warnings_no">
+ <td class="warnings.count">
</td>
<td class="time">
{{br.timespent.total_seconds|sectohms}}
diff --git a/lib/toaster/toastergui/templates/projects.html b/lib/toaster/toastergui/templates/projects.html
index 9c4346c..e651953 100644
--- a/lib/toaster/toastergui/templates/projects.html
+++ b/lib/toaster/toastergui/templates/projects.html
@@ -26,7 +26,10 @@
<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 %}
+ <input id="search" name="search" class="input-xxlarge" type="text" value="
+ {% if request.GET.search %}
+ {{request.GET.search}}
+ {% endif %}"/>{% 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 projects</button>
</form>
@@ -58,8 +61,8 @@
<td><a href="{% url 'projectbuilds' o.id %}">{{o.get_number_of_builds}}</a></td>
<td class="loutcome"><a href="{% url "builddashboard" o.get_last_build_id %}">{%if o.get_last_outcome == build_SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif o.get_last_outcome == build_FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a></td>
<td class="ltarget"><a href="{% url "builddashboard" o.get_last_build_id %}">{{o.get_last_target}} </a></td>
- <td class="lerrors">{% if o.get_last_errors %}<a class="errors_no error" href="{% url "builddashboard" o.get_last_build_id %}#errors">{{o.get_last_errors}} error{{o.get_last_errors|pluralize}}</a>{%endif%}</td>
- <td class="lwarnings">{% if o.get_last_warnings %}<a class="warnings_no warning" href="{% url "builddashboard" o.get_last_build_id %}#warnings">{{o.get_last_warnings}} warning{{o.get_last_warnings|pluralize}}</a>{%endif%}</td>
+ <td class="lerrors">{% if o.get_last_errors %}<a class="errors.count error" href="{% url "builddashboard" o.get_last_build_id %}#errors">{{o.get_last_errors}} error{{o.get_last_errors|pluralize}}</a>{%endif%}</td>
+ <td class="lwarnings">{% if o.get_last_warnings %}<a class="warnings.count warning" href="{% url "builddashboard" o.get_last_build_id %}#warnings">{{o.get_last_warnings}} warning{{o.get_last_warnings|pluralize}}</a>{%endif%}</td>
<td class="limagefiles">
{% if o.get_last_outcome == build_SUCCEEDED %}
<a href="{%url "builddashboard" o.get_last_build_id %}#images">{{fstypes|get_dict_value:o.id}}</a>
diff --git a/lib/toaster/toastergui/templates/recipe.html b/lib/toaster/toastergui/templates/recipe.html
index dd8753d..b5e4192 100644
--- a/lib/toaster/toastergui/templates/recipe.html
+++ b/lib/toaster/toastergui/templates/recipe.html
@@ -53,13 +53,6 @@
</dt>
<dd>{{layer.name}}</dd>
- {% if not MANAGED or not build.project %}
- <dt>
- <i class="icon-question-sign get-help" title="Path to the layer providing the recipe"></i>
- Layer directory
- </dt>
- <dd><code>{{object.layer_version.local_path}}</code></dd>
- {% endif %}
<dt>
<i class="icon-question-sign get-help" title="Path to the recipe .bb file"></i>
Recipe file
@@ -126,7 +119,7 @@
<td>
<a {{ task|task_color }} href="{% url "task" build.pk task.pk %}">{{task.get_outcome_display}} </a>
- {% if MANAGED and build.project and task.outcome = task.OUTCOME_FAILED %}
+ {% if task.outcome = task.OUTCOME_FAILED %}
<a href="{% url 'build_artifact' build.pk "tasklogfile" task.pk %}">
<i class="icon-download-alt" title="Download task log file"></i>
</a>
diff --git a/lib/toaster/toastergui/templates/recipes.html b/lib/toaster/toastergui/templates/recipes.html
index 8d4494e..5cdac43 100644
--- a/lib/toaster/toastergui/templates/recipes.html
+++ b/lib/toaster/toastergui/templates/recipes.html
@@ -28,7 +28,7 @@
<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 %}
+ <input id="search" name="search" class="input-xxlarge" type="text" value="{%if request.GET.search%}{{request.GET.search}}{%endif%}"/>{% 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 recipes</button>
</form>
@@ -102,11 +102,6 @@
{{recipe.layer_version.commit|truncatechars:13}}
</a>
</td>
-
- {% if not MANAGED or not build.project %}
- <!-- Layer directory -->
- <td class="layer_version__local_path">{{recipe.layer_version.local_path}}</td>
- {% endif %}
</tr>
{% endfor %}
diff --git a/lib/toaster/toastergui/templates/runagain.html b/lib/toaster/toastergui/templates/runagain.html
deleted file mode 100644
index b4ba5fb..0000000
--- a/lib/toaster/toastergui/templates/runagain.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% load projecttags %}
-onclick='scheduleBuild(
- {% url 'projectbuilds' buildrequest.project.id as bpi %}{{bpi|json}},
- {{buildrequest.project.name|json}},
- {% url 'project' buildrequest.project.id as bpurl %}{{bpurl|json}},
- {{buildrequest.brtarget_set.all|get_tasks|json}})
-'>Run again
diff --git a/lib/toaster/toastergui/templates/target.html b/lib/toaster/toastergui/templates/target.html
index e7febaf..fa59f4e 100644
--- a/lib/toaster/toastergui/templates/target.html
+++ b/lib/toaster/toastergui/templates/target.html
@@ -152,11 +152,6 @@
{{package.recipe.layer_version.commit|truncatechars:13}}
</a>
</td>
- {% if not MANAGED or not build.project %}
- <td class="layer_directory">
- {{ package.recipe.layer_version.local_path }}
- </td>
- {% endif %}
</tr>
{% endfor %}
diff --git a/lib/toaster/toastergui/templates/task.html b/lib/toaster/toastergui/templates/task.html
index 6e06ddf..635098a 100644
--- a/lib/toaster/toastergui/templates/task.html
+++ b/lib/toaster/toastergui/templates/task.html
@@ -23,18 +23,7 @@
{%if task.task_executed %}
{# executed tasks outcome #}
{% if task.logfile %}
- {% if MANAGED and build.project %}
<a class="btn btn-large" href="{% url 'build_artifact' build.id "tasklogfile" task.pk %}" style="margin:15px;">Download task log</a>
- {% else %}
- <dl class="dl-horizontal">
- <dt>
- <i class="icon-question-sign get-help" title="Path the task log file"></i> Log file
- </dt>
- <dd>
- <code>{{task.logfile}}</code>
- </dd>
- </dl>
- {% endif %}
{% endif %}
{# show stack trace for failed task #}
{% if task.outcome == task.OUTCOME_FAILED and log_head %}
@@ -130,22 +119,12 @@
</dd>
</dl>
{%elif task.outcome == task.OUTCOME_CACHED%}
- {% if MANAGED and build.project %}
{% for t in task.get_related_setscene %}
{% if forloop.last %}
<a class="btn btn-large" href="{% url 'build_artifact' build.id "tasklogfile" t.pk %}" style="margin:15px;">Download task log</a>
{% endif %}
{% endfor %}
- {% else %}
- <dl class="dl-horizontal">
- <dt>
- <i class="icon-question-sign get-help" title="Path the task log file"></i> Log file
- </dt>
- <dd>
- <code>{% for t in task.get_related_setscene %} {{t.logfile}} {% endfor %}</code>
- </dd>
- </dl>
- {% endif %}
+
{%elif task.outcome == task.OUTCOME_EMPTY%}
<div class="alert alert-info details">
This task is empty because it has the <code>noexec</code> flag set to <code>1</code>, or the task function is empty
@@ -200,20 +179,7 @@
<strong>Failed</strong> to restore output from sstate cache. The file was found but could not be unpacked.
</div>
<dl class="dl-horizontal">
- {% if MANAGED and build.project %}
<a href="{% url 'build_artifact' build.id "tasklogfile" task.pk %}" style="margin:15px;">Download log</a>
- {% else %}
- <dt>
- <i class="icon-question-sign get-help" title="Path to the cache attempt log file"></i>
- Log file
- </dt>
- <dd><code>{{task.logfile}}</code></dd>
- <dt>
- <i class="icon-question-sign get-help" title="How long it took the cache attempt to finish in seconds"></i>
- Time (secs)
- </dt>
- <dd>{{task.elapsed_time|format_none_and_zero}}</dd>
- {% endif %}
</dl>
<div class="alert alert-info">
Running the real task instead.
diff --git a/lib/toaster/toastergui/templates/tasks.html b/lib/toaster/toastergui/templates/tasks.html
index 32c0552..b18b5c7 100644
--- a/lib/toaster/toastergui/templates/tasks.html
+++ b/lib/toaster/toastergui/templates/tasks.html
@@ -93,7 +93,7 @@
</td>
<td class="outcome">
<a href="{%url "task" build.pk task.pk%}">{{task.get_outcome_display}} </a>
- {% if MANAGED and build.project and task.outcome = task.OUTCOME_FAILED %}
+ {% if task.outcome = task.OUTCOME_FAILED %}
<a href="{% url 'build_artifact' build.pk "tasklogfile" task.pk %}">
<i class="icon-download-alt" title="Download task log file"></i>
</a>
@@ -113,11 +113,6 @@
{{task.disk_io|format_none_and_zero}}
</td>
- {% if not MANAGED or not build.project %}
- <td class="task_log">
- {{task.logfile}}
- </td>
- {% endif %}
</tr>
{% endfor %}
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index 681f067..feb1513 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -131,9 +131,6 @@ urlpatterns = patterns('toastergui.views',
url(r'^xhr_importlayer/$', 'xhr_importlayer', name='xhr_importlayer'),
url(r'^xhr_updatelayer/$', 'xhr_updatelayer', name='xhr_updatelayer'),
- # dashboard for failed build requests
- url(r'^project/(?P<pid>\d+)/buildrequest/(?P<bid>\d+)$', 'buildrequestdetails', name='buildrequestdetails'),
-
# default redirection
url(r'^$', RedirectView.as_view( url= 'landing')),
)
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 00b1387..8c6f9fa 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -21,7 +21,7 @@
import operator,re
-from django.db.models import Q, Sum, Count, Max
+from django.db.models import F, Q, Sum, Count, Max
from django.db import IntegrityError
from django.shortcuts import render, redirect
from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable
@@ -81,15 +81,15 @@ def _project_recent_build_list(prj):
"errors": map(lambda y: {"type": y.lineno, "msg": y.message, "tb": y.pathname}, (x.logmessage_set.filter(level__gte=LogMessage.WARNING)|x.logmessage_set.filter(level=LogMessage.EXCEPTION))),
"updated": x.completed_on.strftime('%s')+"000",
"command_time": (x.completed_on - x.started_on).total_seconds(),
- "br_page_url": reverse('buildrequestdetails', args=(x.project.id, x.pk) ),
+ "br_page_url": reverse('builddashboard', args=(x.pk,) ),
"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,)),
'build_time_page_url': reverse('buildtime', args=(y.pk,)),
- "errors": y.errors_no,
- "warnings": y.warnings_no,
+ "errors": y.errors.count(),
+ "warnings": y.warnings.count(),
"completeper": y.completeper() if y.outcome == Build.IN_PROGRESS else "0",
"eta": y.eta().strftime('%s')+"000" if y.outcome == Build.IN_PROGRESS else "0",
}, [x]),
@@ -1906,7 +1906,7 @@ if True:
(filter_string, search_term, ordering_string) = _search_tuple(request, Build)
# post-process any date range filters
filter_string,daterange_selected = _modify_date_range_filter(filter_string)
- queryset_all = queryset_all.select_related("project")
+ queryset_all = queryset_all.select_related("project").annotate(errors_no = Count('logmessage', only=Q(logmessage__level=LogMessage.ERROR)|Q(logmessage__level=LogMessage.EXCEPTION))).annotate(warnings_no = Count('logmessage', only=Q(logmessage__level=LogMessage.WARNING))).extra(select={'timespent':'completed_on - started_on'})
queryset_with_search = _get_queryset(Build, queryset_all, None, search_term, ordering_string, '-completed_on')
queryset = _get_queryset(Build, queryset_all, filter_string, search_term, ordering_string, '-completed_on')
@@ -2841,10 +2841,3 @@ if True:
_set_parameters_values(pagesize, orderby, request)
return context
-
- @_template_renderer("buildrequestdetails.html")
- def buildrequestdetails(request, pid, bid):
- context = {
- 'buildrequest' : Build.objects.get(pk = bid, project_id = pid).buildrequest
- }
- return context
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 13/23] toastergui: select project types
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (11 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 12/23] toaster: refactor build model Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 14/23] toaster: delete multiple builds Alex DAMIAN
` (9 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
This brings in project types in the New Project page.
The "analisys" projects are the projects with no "release"
set, and have read-only properties in the project page.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/orm/models.py | 8 +-
lib/toaster/toastergui/templates/newproject.html | 99 +++++++++++++++---------
lib/toaster/toastergui/views.py | 11 ++-
3 files changed, 80 insertions(+), 38 deletions(-)
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 077c94d..8488aa4 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -57,7 +57,11 @@ class ToasterSetting(models.Model):
class ProjectManager(models.Manager):
def create_project(self, name, release):
- prj = self.model(name = name, bitbake_version = release.bitbake_version, release = release)
+ if release is not None:
+ prj = self.model(name = name, bitbake_version = release.bitbake_version, release = release)
+ else:
+ prj = self.model(name = name, bitbake_version = None, release = None)
+
prj.save()
for defaultconf in ToasterSetting.objects.filter(name__startswith="DEFCONF_"):
@@ -66,6 +70,8 @@ class ProjectManager(models.Manager):
name = name,
value = defaultconf.value)
+ if release is None:
+ return prj
for rdl in release.releasedefaultlayer_set.all():
try:
diff --git a/lib/toaster/toastergui/templates/newproject.html b/lib/toaster/toastergui/templates/newproject.html
index ed3a279..1159d71 100644
--- a/lib/toaster/toastergui/templates/newproject.html
+++ b/lib/toaster/toastergui/templates/newproject.html
@@ -11,60 +11,74 @@
<div class="alert alert-error row-fluid" role="alert">{{alert}}</div>
{% endif %}
</div>
- {% if releases.count > 0 %}
+
+ <div class="row-fluid">
+ <div class="span6">
<form method="POST">{% csrf_token %}
+
<fieldset>
<label>Project name <span class="muted">(required)</span></label>
<input type="text" class="input-xlarge" required id="new-project-name" name="projectname">
- {% if releases.count > 1 %}
+ </fieldset>
+
+ <fieldset>
+ <label class="project-form">Project type</label>
+ <label class="project-form radio"><input type="radio" name="ptype" value="analysis" checked/> Analysis Project</label>
+
+ {% if releases.count > 0 %}
+ <label class="project-form radio"><input type="radio" name="ptype" value="build" checked /> Build Project</label>
+ {% endif %}
+ </fieldset>
+
+ {% if releases.count > 0 %}
+ <fieldset class="release">
+ {% if releases.count > 1 %}
<label class="project-form">
Release
<i class="icon-question-sign get-help" title="The version of the build system you want to use"></i>
</label>
<select name="projectversion" id="projectversion">
- {% for release in releases %}
- <option value="{{release.id}}"
- {%if defaultbranch == release.name %}
- selected
- {%endif%}
-
- >{{release.description}}</option>
- {% endfor %}
+ {% for release in releases %}
+ <option value="{{release.id}}"
+ {%if defaultbranch == release.name %}
+ selected
+ {%endif%}
+ >{{release.description}}</option>
+ {% endfor %}
</select>
- {% for release in releases %}
- <div class="row-fluid helptext" id="description-{{release.id}}" style="display: none">
+ {% for release in releases %}
+ <div class="row-fluid helptext" id="description-{{release.id}}" style="display: none">
<span class="help-block span5">{{release.helptext|safe}}</span>
- </div>
- {% endfor %}
- {% else %}
- <input type="hidden" name="projectversion" value="{{releases.0.id}}"/>
- {% endif %}
-
+ </div>
+ {% endfor %}
+ {% else %}
+ <input type="hidden" name="projectversion" value="{{releases.0.id}}"/>
+ {% endif %}
</fieldset>
+ {% endif %}
<div class="form-actions">
<input type="submit" class="btn btn-primary btn-large" value="Create project"/>
<span class="help-inline" style="vertical-align:middle;">To create a project, you need to enter a project name</span>
</div>
</form>
- {% else %}
- <br/>
- <div class="alert alert-warning row-fluid span6">
- <h3>No releases configured</h3>
- <p>
- It looks like Toaster releases have not been configured properly. Contact the person who set up Toaster, and tell them about it.
- </p>
- <p>
- If you are the Toaster administrator, we are sorry: setting up Toaster is not easy.
- <ul>
- <li><a href="{% url 'admin:orm_release_changelist' %}">Log in to the Django administration interface</a> and check the "Releases" section.</li>
- <li>Check out the <a href="https://wiki.yoctoproject.org/wiki/Setting_up_a_hosted_managed_mode_for_Toaster#Releases">documentation about configuring releases</a></li>
- </ul>
- </p>
- </div>
- {% endif %}
+ </div>
-</div>
+ <div class="span5 well">
+ <span class="help-block">
+ <h4>Toaster project types</h4>
+ <p>With a <strong>build project</strong> you configure and run your builds from Toaster.</p>
+ <p>With an <strong>analysis project</strong>, the builds are configured and run by another tool
+ (something like Buildbot or Jenkins), and the project only collects the information about the
+ builds (packages, recipes, dependencies, logs, etc). </p>
+ <p>You can read more on <a href="#">how to set up an analysis project</a>
+ in the Toaster manual.</p>
+ <h4>Release</h4>
+ <p>If you create a <strong>build project</strong>, you will need to select a <strong>release</strong>,
+ which is the version of the build system you want to use to run your builds.</p>
+ </div>
+ </div>
+ </div>
<script type="text/javascript">
$(document).ready(function () {
@@ -94,6 +108,21 @@
$(".helptext").hide();
$('#description-' + new_release).fadeIn();
});
+
+ // Hide the project release when you select an analysis project
+ function projectType() {
+ if ($("input[type='radio']:checked").val() == 'build') {
+ $('.release').fadeIn();
+ }
+ else {
+ $('.release').fadeOut();
+ }
+ }
+ projectType();
+
+ $('input:radio').change(function(){
+ projectType();
+ });
})
</script>
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 8c6f9fa..1a504b8 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -2104,8 +2104,11 @@ if True:
# render new project page
return render(request, template, context)
elif request.method == "POST":
- mandatory_fields = ['projectname', 'projectversion']
+ mandatory_fields = ['projectname', 'ptype']
try:
+ ptype = request.POST.get('ptype')
+ if ptype == "build":
+ mandatory_fields.append('projectversion')
# make sure we have values for all mandatory_fields
if reduce( lambda x, y: x or y, map(lambda x: len(request.POST.get(x, '')) == 0, mandatory_fields)):
# set alert for missing fields
@@ -2121,7 +2124,11 @@ if True:
login(request, user)
# save the project
- prj = Project.objects.create_project(name = request.POST['projectname'], release = Release.objects.get(pk = request.POST['projectversion']))
+ release = Release.objects.get(pk = request.POST.get('projectversion', None ))
+ if ptype == "analysis":
+ release = None
+
+ prj = Project.objects.create_project(name = request.POST['projectname'], release = release)
prj.user_id = request.user.pk
prj.save()
return redirect(reverse(project, args=(prj.pk,)) + "#/newproject")
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 14/23] toaster: delete multiple builds
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (12 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 13/23] toastergui: select project types Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 15/23] toasterui: verify variable before usage Alex DAMIAN
` (8 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
This patch fixes the build deletion on unmigrated databases,
and enhances it to delete multiple builds in a single run.
[YOCTO #7726]
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/toastergui/views.py | 4 +-
.../toastermain/management/commands/builddelete.py | 56 ++++++++++++++--------
2 files changed, 38 insertions(+), 22 deletions(-)
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 1a504b8..ec65903 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -64,9 +64,9 @@ def _get_latest_builds(prj=None):
if prj is not None:
queryset = queryset.filter(project = prj)
- return itertools.chain(
+ return list(itertools.chain(
queryset.filter(outcome=Build.IN_PROGRESS).order_by("-pk"),
- queryset.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3] )
+ queryset.filter(outcome__lt=Build.IN_PROGRESS).order_by("-pk")[:3] ))
# a JSON-able dict of recent builds; for use in the Project page, xhr_ updates, and other places, as needed
diff --git a/lib/toaster/toastermain/management/commands/builddelete.py b/lib/toaster/toastermain/management/commands/builddelete.py
index 5cec436..343d311 100644
--- a/lib/toaster/toastermain/management/commands/builddelete.py
+++ b/lib/toaster/toastermain/management/commands/builddelete.py
@@ -1,33 +1,49 @@
from django.core.management.base import BaseCommand, CommandError
from orm.models import Build
+from django.db import OperationalError
import os
class Command(BaseCommand):
args = "buildId"
- help = "Deletes selected build"
+ help = "Deletes selected build(s)"
def handle(self, buildId, *args, **options):
- b = Build.objects.get(pk = buildId)
- # theoretically, just b.delete() would suffice
- # however SQLite runs into problems when you try to
- # delete too many rows at once, so we delete some direct
- # relationships from Build manually.
+ for bid in buildId.split(","):
+ b = Build.objects.get(pk = bid)
+ # theoretically, just b.delete() would suffice
+ # however SQLite runs into problems when you try to
+ # delete too many rows at once, so we delete some direct
+ # relationships from Build manually.
+ for t in b.target_set.all():
+ t.delete()
+ for t in b.task_build.all():
+ t.delete()
+ for p in b.package_set.all():
+ p.delete()
+ for lv in b.layer_version_build.all():
+ lv.delete()
+ for v in b.variable_build.all():
+ v.delete()
+ for l in b.logmessage_set.all():
+ l.delete()
- for t in b.target_set.all():
- t.delete()
- for t in b.task_build.all():
- t.delete()
- for p in b.package_set.all():
- p.delete()
- for lv in b.layer_version_build.all():
- lv.delete()
- for v in b.variable_build.all():
- v.delete()
- for l in b.logmessage_set.all():
- l.delete()
+ # delete the build; some databases might have had problem with migration of the bldcontrol app
+ retry_count = 0
+ need_bldcontrol_migration = False
+ while True:
+ if retry_count >= 5:
+ break
+ retry_count += 1
+ if need_bldcontrol_migration:
+ from django.core import management
+ management.call_command('migrate', 'bldcontrol', interactive=False)
- # this should take care of the rest
- b.delete()
+ try:
+ b.delete()
+ break
+ except OperationalError as e:
+ # execute migrations
+ need_bldcontrol_migration = True
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 15/23] toasterui: verify variable before usage
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (13 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 14/23] toaster: delete multiple builds Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 16/23] toasterui: fixes after html5 compliance testing Alex DAMIAN
` (7 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
This patch verifies that BRBE is set before trying to use
it to read the checkout paths. This is needed for builds
ran outside Toaster control.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/bb/ui/buildinfohelper.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/lib/bb/ui/buildinfohelper.py b/lib/bb/ui/buildinfohelper.py
index 63976b5..cce6da5 100644
--- a/lib/bb/ui/buildinfohelper.py
+++ b/lib/bb/ui/buildinfohelper.py
@@ -860,10 +860,11 @@ class BuildInfoHelper(object):
# convert the paths from absolute to relative to either the build directory or layer checkouts
path_prefixes = []
- br_id, be_id = self.brbe.split(":")
- from bldcontrol.models import BuildEnvironment, BuildRequest
- be = BuildEnvironment.objects.get(pk = be_id)
- path_prefixes.append(be.builddir)
+ if self.brbe is not None:
+ br_id, be_id = self.brbe.split(":")
+ from bldcontrol.models import BuildEnvironment, BuildRequest
+ be = BuildEnvironment.objects.get(pk = be_id)
+ path_prefixes.append(be.builddir)
for layer in sorted(self.orm_wrapper.layer_version_objects, key = lambda x:len(x.local_path), reverse=True):
path_prefixes.append(layer.local_path)
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 16/23] toasterui: fixes after html5 compliance testing
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (14 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 15/23] toasterui: verify variable before usage Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-29 14:22 ` Michael Wood
2015-06-25 10:33 ` [PATCH 17/23] toaster: Enable toastertable cache Alex DAMIAN
` (6 subsequent siblings)
22 siblings, 1 reply; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Alexandru DAMIAN <alexandru.damian@intel.com>
This patch brings fixes for issues highlighted by
HTML5 compliance testing.
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
lib/toaster/orm/models.py | 4 +++-
lib/toaster/toastergui/static/js/base.js | 2 +-
lib/toaster/toastergui/templates/configvars.html | 2 +-
.../toastergui/templates/detail_search_header.html | 2 +-
.../toastergui/templates/detail_sorted_header.html | 2 +-
.../toastergui/templates/generic-toastertable-page.html | 2 ++
lib/toaster/toastergui/templates/importlayer.html | 15 +++++++++++++--
lib/toaster/toastergui/templates/target.html | 2 +-
lib/toaster/toastergui/templates/toastertable-simple.html | 4 ++--
lib/toaster/toastergui/templates/toastertable.html | 4 ++--
lib/toaster/toastergui/views.py | 11 ++++++-----
11 files changed, 33 insertions(+), 17 deletions(-)
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 8488aa4..7e8ab16 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -180,7 +180,9 @@ class Project(models.Model):
if release == None:
release = self.release
# layers on the same branch or layers specifically set for this project
- queryset = Layer_Version.objects.filter((Q(up_branch__name = release.branch_name) & Q(project = None)) | Q(project = self) | Q(build__project = self))
+ queryset = Layer_Version.objects.filter(Q(project = self) | Q(build__project = self))
+ if release is not None:
+ queryset = queryset.filter(Q(up_branch__name = release.branch_name) & Q(project = None))
if layer_name is not None:
# we select only a layer name
queryset = queryset.filter(layer__name = layer_name)
diff --git a/lib/toaster/toastergui/static/js/base.js b/lib/toaster/toastergui/static/js/base.js
index d079f23..f1711c1 100644
--- a/lib/toaster/toastergui/static/js/base.js
+++ b/lib/toaster/toastergui/static/js/base.js
@@ -39,7 +39,7 @@ function basePageInit(ctx) {
libtoaster.getProjectInfo(selectedProject.projectPageUrl,
function (data) {
- if (data.machine.name === undefined || data.layers.length === 0) {
+ if (data.machine === null || data.machine.name === undefined || data.layers.length === 0) {
/* we can't build anything with out a machine and some layers */
$("#new-build-button #targets-form").hide();
$("#new-build-button .alert").show();
diff --git a/lib/toaster/toastergui/templates/configvars.html b/lib/toaster/toastergui/templates/configvars.html
index 8957673..8a572ae 100644
--- a/lib/toaster/toastergui/templates/configvars.html
+++ b/lib/toaster/toastergui/templates/configvars.html
@@ -39,7 +39,7 @@
<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 %}
+ <input id="search" name="search" class="input-xxlarge" type="text" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>{% 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 variables</button>
</form>
diff --git a/lib/toaster/toastergui/templates/detail_search_header.html b/lib/toaster/toastergui/templates/detail_search_header.html
index ca8e158..7bea3f4 100644
--- a/lib/toaster/toastergui/templates/detail_search_header.html
+++ b/lib/toaster/toastergui/templates/detail_search_header.html
@@ -28,7 +28,7 @@ $(document).ready(function() {
<form id="searchform" class="navbar-search input-append pull-left">
{% endif %}
- <input id="search" class="input-xlarge" type="text" placeholder="Search {{search_what}}" name="search" value="{{request.GET.search}}">
+ <input id="search" class="input-xlarge" type="text" placeholder="Search {{search_what}}" name="search" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}">
<input type="hidden" value="name:+" name="orderby">
<input type="hidden" value="l" name="page">
{% if request.GET.search %}
diff --git a/lib/toaster/toastergui/templates/detail_sorted_header.html b/lib/toaster/toastergui/templates/detail_sorted_header.html
index 5214444..6ce292e 100644
--- a/lib/toaster/toastergui/templates/detail_sorted_header.html
+++ b/lib/toaster/toastergui/templates/detail_sorted_header.html
@@ -9,7 +9,7 @@
<thead>
<!-- Table header row; generated from "tablecols" entry in the context dict -->
<tr>
- {% for tc in tablecols %}<th class="{{tc.dclass}} {{tc.clclass}}">
+ {% for tc in tablecols %}<th class="{%if tc.dclass%}{{tc.dclass}}{% endif %} {%if tc.class %}{{tc.clclass}}{% endif %}">
{%if tc.qhelp%}<i class="icon-question-sign get-help" title="{{tc.qhelp}}"></i>{%endif%}
{%if tc.orderfield%}<a {%if tc.ordericon%} class="sorted" {%endif%}href="javascript:reload_params({'page': 1, 'orderby' : '{{tc.orderfield}}' })">{{tc.name}}</a>{%else%}<span class="muted">{{tc.name}}</span>{%endif%}
{%if tc.ordericon%} <i class="icon-caret-{{tc.ordericon}}"></i>{%endif%}
diff --git a/lib/toaster/toastergui/templates/generic-toastertable-page.html b/lib/toaster/toastergui/templates/generic-toastertable-page.html
index d7ad2e7..78e942c 100644
--- a/lib/toaster/toastergui/templates/generic-toastertable-page.html
+++ b/lib/toaster/toastergui/templates/generic-toastertable-page.html
@@ -11,7 +11,9 @@
{% block projectinfomain %}
<div class="page-header">
<h1>{{title}} (<span class="table-count-{{table_name}}"></span>)
+ {% if project.release %}
<i class="icon-question-sign get-help heading-help" title="This page lists {{title}} compatible with the release selected for this project, which is {{project.release.description}}"></i>
+ {% endif %}
</h1>
</div>
<div id="zone1alerts" style="display:none">
diff --git a/lib/toaster/toastergui/templates/importlayer.html b/lib/toaster/toastergui/templates/importlayer.html
index 498a204..6a5d412d 100644
--- a/lib/toaster/toastergui/templates/importlayer.html
+++ b/lib/toaster/toastergui/templates/importlayer.html
@@ -9,6 +9,7 @@
{% block projectinfomain %}
+ {% if project and project.release %}
<script src="{% static 'js/layerDepsModal.js' %}"></script>
<script src="{% static 'js/importlayer.js' %}"></script>
<script>
@@ -31,9 +32,7 @@
</div>
<form>
- {% if project %}
<span class="help-block" style="padding-left:19px;">The layer you are importing must be compatible with <strong>{{project.release.description}}</strong>, which is the release you are using in this project.</span>
- {% endif %}
<fieldset class="air">
<legend>Layer repository information</legend>
<div class="alert alert-error" id="import-error" style="display:none">
@@ -131,4 +130,16 @@
</div>
</form>
+ {% else %} {#project and project release#}
+ <div class="page-header">
+ <h1>Import layer</h1>
+ </div>
+ <div class="alert alert-info" id="import-error" >
+ <h3>Unsupported project type</h3>
+ <p>This project does not support importing layers.</p>
+ <ul></ul>
+ </div>
+
+ {% endif %}
+
{% endblock %}
diff --git a/lib/toaster/toastergui/templates/target.html b/lib/toaster/toastergui/templates/target.html
index fa59f4e..65e6c4a 100644
--- a/lib/toaster/toastergui/templates/target.html
+++ b/lib/toaster/toastergui/templates/target.html
@@ -53,7 +53,7 @@
<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 %}
+ <input id="search" name="search" class="input-xxlarge" type="text" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>{% 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 packages</button>
</form>
diff --git a/lib/toaster/toastergui/templates/toastertable-simple.html b/lib/toaster/toastergui/templates/toastertable-simple.html
index ea7b38e..212318b 100644
--- a/lib/toaster/toastergui/templates/toastertable-simple.html
+++ b/lib/toaster/toastergui/templates/toastertable-simple.html
@@ -29,7 +29,7 @@
<div class="row-fluid" id="no-results-{{table_name}}" style="display:none">
<div class="alert">
<form class="no-results input-append">
- <input class="input-xlarge" id="new-search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{{request.GET.search}}"/>
+ <input class="input-xlarge" id="new-search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>
<a href="#" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1">
<i class="icon-remove"></i>
</a>
@@ -44,7 +44,7 @@
<div class="row-fluid" id="table-chrome-{{table_name}}">
<div class="navbar-search input-append pull-left">
- <input class="input-xlarge" id="search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{{request.GET.search}}"/>
+ <input class="input-xlarge" id="search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>
<a href="#" style="display:none" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1">
<i class="icon-remove"></i>
</a>
diff --git a/lib/toaster/toastergui/templates/toastertable.html b/lib/toaster/toastergui/templates/toastertable.html
index c7c7a84..0473116 100644
--- a/lib/toaster/toastergui/templates/toastertable.html
+++ b/lib/toaster/toastergui/templates/toastertable.html
@@ -29,7 +29,7 @@
<div class="row-fluid" id="no-results-{{table_name}}" style="display:none">
<div class="alert">
<form class="no-results input-append">
- <input class="input-xxlarge" id="new-search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{{request.GET.search}}"/>
+ <input class="input-xxlarge" id="new-search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{%if request.GET.search %}{{request.GET.search}}{%endif%}"/>
<a href="#" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1">
<i class="icon-remove"></i>
</a>
@@ -46,7 +46,7 @@
<div class="navbar-inner">
<div class="navbar-search input-append pull-left">
- <input class="input-xxlarge" id="search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{{request.GET.search}}"/>
+ <input class="input-xxlarge" id="search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{%if request.GET.search%}{{request.GET.search}}{%endif%}"/>
<a href="#" style="display:none" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1">
<i class="icon-remove"></i>
</a>
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index ec65903..82650d0 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -165,8 +165,8 @@ def _lv_to_dict(prj, x = None):
return {"id": x.pk,
"name": x.layer.name,
- "tooltip": x.layer.vcs_url+" | "+x.get_vcs_reference(),
- "detail": "(" + x.layer.vcs_url + (")" if x.up_branch == None else " | "+x.get_vcs_reference()+")"),
+ "tooltip": "%s | %s" % (x.layer.vcs_url,x.get_vcs_reference()),
+ "detail": "(%s" % x.layer.vcs_url + (")" if x.up_branch == None else " | "+x.get_vcs_reference()+")"),
"giturl": x.layer.vcs_url,
"layerdetailurl" : reverse('layerdetails', args=(prj.id,x.pk)),
"revision" : x.get_vcs_reference(),
@@ -559,10 +559,10 @@ def task( request, build_id, task_id ):
uri_list= [ ]
variables = Variable.objects.filter(build=build_id)
v=variables.filter(variable_name='SSTATE_DIR')
- if v.count > 0:
+ if v.count() > 0:
uri_list.append(v[0].variable_value)
v=variables.filter(variable_name='SSTATE_MIRRORS')
- if (v.count > 0):
+ if (v.count() > 0):
for mirror in v[0].variable_value.split('\\n'):
s=re.sub('.* ','',mirror.strip(' \t\n\r'))
if len(s): uri_list.append(s)
@@ -2124,9 +2124,10 @@ if True:
login(request, user)
# save the project
- release = Release.objects.get(pk = request.POST.get('projectversion', None ))
if ptype == "analysis":
release = None
+ else:
+ release = Release.objects.get(pk = request.POST.get('projectversion', None ))
prj = Project.objects.create_project(name = request.POST['projectname'], release = release)
prj.user_id = request.user.pk
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 17/23] toaster: Enable toastertable cache
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (15 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 16/23] toasterui: fixes after html5 compliance testing Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 18/23] toaster: toastertable Pass up the kwargs for setup_filter Alex DAMIAN
` (5 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Michael Wood <michael.g.wood@intel.com>
Enable the cache and fix warning on the toastertable cache which happens
on the memcache backend "CacheKeyWarning: Cache key contains characters that
will cause errors if used with memcached"
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
lib/toaster/toastergui/widgets.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/lib/toaster/toastergui/widgets.py b/lib/toaster/toastergui/widgets.py
index f5a1b3e..e257c70 100644
--- a/lib/toaster/toastergui/widgets.py
+++ b/lib/toaster/toastergui/widgets.py
@@ -36,6 +36,7 @@ import types
import json
import collections
import operator
+import re
from toastergui.views import objtojson
@@ -251,10 +252,12 @@ class ToasterTable(TemplateView):
for key, val in kwargs.iteritems():
cache_name = cache_name + str(key) + str(val)
+ # No special chars allowed in the cache name
+ cache_name = re.sub(r'[^A-Za-z0-9]', "", cache_name)
data = cache.get(cache_name)
- #if data:
- # return data
+ if data:
+ return data
self.setup_columns(**kwargs)
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 18/23] toaster: toastertable Pass up the kwargs for setup_filter
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (16 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 17/23] toaster: Enable toastertable cache Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 19/23] toaster: table.js fix filter visual indicator and interaction Alex DAMIAN
` (4 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Michael Wood <michael.g.wood@intel.com>
This allows us to setup_filter in tables using args such as the current
project.
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
lib/toaster/toastergui/widgets.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/lib/toaster/toastergui/widgets.py b/lib/toaster/toastergui/widgets.py
index e257c70..5f6b47b 100644
--- a/lib/toaster/toastergui/widgets.py
+++ b/lib/toaster/toastergui/widgets.py
@@ -72,7 +72,7 @@ class ToasterTable(TemplateView):
cmd = request.GET.get('cmd', None)
if cmd and 'filterinfo' in cmd:
- data = self.get_filter_info(request)
+ data = self.get_filter_info(request, **kwargs)
else:
# If no cmd is specified we give you the table data
data = self.get_data(request, **kwargs)
@@ -81,10 +81,10 @@ class ToasterTable(TemplateView):
return super(ToasterTable, self).get(request, *args, **kwargs)
- def get_filter_info(self, request):
+ def get_filter_info(self, request, **kwargs):
data = None
- self.setup_filters()
+ self.setup_filters(**kwargs)
search = request.GET.get("search", None)
if search:
@@ -194,8 +194,8 @@ class ToasterTable(TemplateView):
return template.render(context)
- def apply_filter(self, filters):
- self.setup_filters()
+ def apply_filter(self, filters, **kwargs):
+ self.setup_filters(**kwargs)
try:
filter_name, filter_action = filters.split(':')
@@ -264,7 +264,7 @@ class ToasterTable(TemplateView):
if search:
self.apply_search(search)
if filters:
- self.apply_filter(filters)
+ self.apply_filter(filters, **kwargs)
if orderby:
self.apply_orderby(orderby)
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 19/23] toaster: table.js fix filter visual indicator and interaction
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (17 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 18/23] toaster: toastertable Pass up the kwargs for setup_filter Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 20/23] toaster: Restore 'in project' filters to main tables Alex DAMIAN
` (3 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Michael Wood <michael.g.wood@intel.com>
- Fix the id of the filter modal dialog.
- Fix the visual indicator toggle button
- Add check to see if the number of items to filter on is !=0 if it is
then don't allow applying this filter.
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
lib/toaster/toastergui/static/js/table.js | 59 +++++++++++++++++++++++--------
1 file changed, 44 insertions(+), 15 deletions(-)
diff --git a/lib/toaster/toastergui/static/js/table.js b/lib/toaster/toastergui/static/js/table.js
index 1072c75..d06a3f5 100644
--- a/lib/toaster/toastergui/static/js/table.js
+++ b/lib/toaster/toastergui/static/js/table.js
@@ -261,23 +261,14 @@ function tableInit(ctx){
var filterBtn = $('<a href="#" role="button" class="pull-right btn btn-mini" data-toggle="modal"><i class="icon-filter filtered"></i></a>');
filterBtn.data('filter-name', col.filter_name);
+ filterBtn.prop('id', col.filter_name);
filterBtn.click(filterOpenClicked);
/* If we're currently being filtered setup the visial indicator */
if (tableParams.filter &&
tableParams.filter.match('^'+col.filter_name)) {
- filterBtn.addClass("btn-primary");
-
- filterBtn.tooltip({
- html: true,
- title: '<button class="btn btn-small btn-primary" onClick=\'$("#clear-filter-btn").click();\'>Clear filter</button>',
- placement: 'bottom',
- delay: {
- hide: 1500,
- show: 400,
- },
- });
+ filterBtnActive(filterBtn, true);
}
header.append(filterBtn);
}
@@ -310,6 +301,26 @@ function tableInit(ctx){
tableChromeDone = true;
}
+ /* Toggles the active state of the filter button */
+ function filterBtnActive(filterBtn, active){
+ if (active) {
+ filterBtn.addClass("btn-primary");
+
+ filterBtn.tooltip({
+ html: true,
+ title: '<button class="btn btn-small btn-primary" onClick=\'$("#clear-filter-btn-'+ ctx.tableName +'").click();\'>Clear filter</button>',
+ placement: 'bottom',
+ delay: {
+ hide: 1500,
+ show: 400,
+ },
+ });
+ } else {
+ filterBtn.removeClass("btn-primary");
+ filterBtn.tooltip('destroy');
+ }
+ }
+
/* Display or hide table columns based on the cookie preference or defaults */
function loadColumnsPreference(){
var cookie_data = $.cookie("cols");
@@ -427,6 +438,10 @@ function tableInit(ctx){
var radioInput = action.children("input");
+ if (Number(filterAction.count) == 0){
+ radioInput.attr("disabled", "disabled");
+ }
+
action.children(".filter-title").text(actionTitle);
radioInput.val(filterName + ':' + filterAction.name);
@@ -475,7 +490,13 @@ function tableInit(ctx){
tableParams.page = 1;
tableParams.search = searchTerm;
- tableParams.filter = null;
+
+ /* If a filter was active we remove it */
+ if (tableParams.filter) {
+ var filterBtn = $("#" + tableParams.filter.split(":")[0]);
+ filterBtnActive(filterBtn, false);
+ tableParams.filter = null;
+ }
loadData(tableParams);
@@ -504,6 +525,9 @@ function tableInit(ctx){
});
$("#clear-filter-btn-"+ctx.tableName).click(function(){
+ var filterBtn = $("#" + tableParams.filter.split(":")[0]);
+ filterBtnActive(filterBtn, false);
+
tableParams.filter = null;
loadData(tableParams);
});
@@ -513,13 +537,18 @@ function tableInit(ctx){
tableParams.filter = $(this).find("input[type='radio']:checked").val();
+ var filterBtn = $("#" + tableParams.filter.split(":")[0]);
+
/* All === remove filter */
- if (tableParams.filter.match(":all$"))
+ if (tableParams.filter.match(":all$")) {
tableParams.filter = null;
+ filterBtnActive(filterBtn, false);
+ } else {
+ filterBtnActive(filterBtn, true);
+ }
loadData(tableParams);
-
- $('#filter-modal').modal('hide');
+ $(this).parent().modal('hide');
});
}
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 20/23] toaster: Restore 'in project' filters to main tables
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (18 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 19/23] toaster: table.js fix filter visual indicator and interaction Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 21/23] toaster: bldcontrol Ignore toasterconf files in own directories Alex DAMIAN
` (2 subsequent siblings)
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Michael Wood <michael.g.wood@intel.com>
Restores the previously removed filters from recipes, machines and layers
table. These filters allow filtering down the results to just displaying
the current added layers, machines or recipes in the project.
[YOCTO #7851]
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
lib/toaster/toastergui/tables.py | 75 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 73 insertions(+), 2 deletions(-)
diff --git a/lib/toaster/toastergui/tables.py b/lib/toaster/toastergui/tables.py
index 43b81a3..146a8dd 100644
--- a/lib/toaster/toastergui/tables.py
+++ b/lib/toaster/toastergui/tables.py
@@ -26,6 +26,24 @@ from django.conf.urls import url
from django.core.urlresolvers import reverse
from django.views.generic import TemplateView
+
+class ProjectFiltersMixin(object):
+ """Common mixin for recipe, machine in project filters"""
+
+ def filter_in_project(self, count_only=False):
+ query = self.queryset.filter(layer_version__in=self.project_layers)
+ if count_only:
+ return query.count()
+
+ self.queryset = query
+
+ def filter_not_in_project(self, count_only=False):
+ query = self.queryset.exclude(layer_version__in=self.project_layers)
+ if count_only:
+ return query.count()
+
+ self.queryset = query
+
class LayersTable(ToasterTable):
"""Table of layers in Toaster"""
@@ -37,12 +55,40 @@ class LayersTable(ToasterTable):
context = super(LayersTable, self).get_context_data(**kwargs)
project = Project.objects.get(pk=kwargs['pid'])
+
context['project'] = project
context['projectlayers'] = map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=project))
return context
+ def setup_filters(self, *args, **kwargs):
+ project = Project.objects.get(pk=kwargs['pid'])
+ self.project_layers = ProjectLayer.objects.filter(project=project)
+
+
+ self.add_filter(title="Filter by project layers",
+ name="in_current_project",
+ filter_actions=[
+ self.make_filter_action("in_project", "Layers added to this project", self.filter_in_project),
+ self.make_filter_action("not_in_project", "Layers not added to this project", self.filter_not_in_project)
+ ])
+
+ def filter_in_project(self, count_only=False):
+ query = self.queryset.filter(projectlayer__in=self.project_layers)
+ if count_only:
+ return query.count()
+
+ self.queryset = query
+
+ def filter_not_in_project(self, count_only=False):
+ query = self.queryset.exclude(projectlayer__in=self.project_layers)
+ if count_only:
+ return query.count()
+
+ self.queryset = query
+
+
def setup_queryset(self, *args, **kwargs):
prj = Project.objects.get(pk = kwargs['pid'])
compatible_layers = prj.compatible_layerversions()
@@ -140,6 +186,7 @@ class LayersTable(ToasterTable):
self.add_column(title="Add | Delete",
help_text="Add or delete layers to / from your project",
hideable=False,
+ filter_name="in_current_project",
static_data_name="add-del-layers",
static_data_template='{% include "layer_btn.html" %}')
@@ -169,7 +216,7 @@ class LayerDetails(ToasterTemplateView):
return context
-class MachinesTable(ToasterTable):
+class MachinesTable(ToasterTable, ProjectFiltersMixin):
"""Table of Machines in Toaster"""
def __init__(self, *args, **kwargs):
@@ -183,6 +230,16 @@ class MachinesTable(ToasterTable):
context['projectlayers'] = map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=context['project']))
return context
+ def setup_filters(self, *args, **kwargs):
+ project = Project.objects.get(pk=kwargs['pid'])
+ self.project_layers = project.projectlayer_equivalent_set()
+
+ self.add_filter(title="Filter by project machines",
+ name="in_current_project",
+ filter_actions=[
+ self.make_filter_action("in_project", "Machines provided by layers added to this project", self.filter_in_project),
+ self.make_filter_action("not_in_project", "Machines provided by layers not added to this project", self.filter_not_in_project)
+ ])
def setup_queryset(self, *args, **kwargs):
prj = Project.objects.get(pk = kwargs['pid'])
@@ -226,6 +283,7 @@ class MachinesTable(ToasterTable):
self.add_column(title="Select",
help_text="Sets the selected machine as the project machine. You can only have one machine per project",
hideable=False,
+ filter_name="in_current_project",
static_data_name="add-del-layers",
static_data_template='{% include "machine_btn.html" %}')
@@ -264,7 +322,7 @@ class LayerMachinesTable(MachinesTable):
static_data_template=select_btn_template)
-class RecipesTable(ToasterTable):
+class RecipesTable(ToasterTable, ProjectFiltersMixin):
"""Table of Recipes in Toaster"""
def __init__(self, *args, **kwargs):
@@ -282,6 +340,17 @@ class RecipesTable(ToasterTable):
return context
+ def setup_filters(self, *args, **kwargs):
+ project = Project.objects.get(pk=kwargs['pid'])
+ self.project_layers = project.projectlayer_equivalent_set()
+
+ self.add_filter(title="Filter by project recipes",
+ name="in_current_project",
+ filter_actions=[
+ self.make_filter_action("in_project", "Recipes provided by layers added to this project", self.filter_in_project),
+ self.make_filter_action("not_in_project", "Recipes provided by layers not added to this project", self.filter_not_in_project)
+ ])
+
def setup_queryset(self, *args, **kwargs):
prj = Project.objects.get(pk = kwargs['pid'])
@@ -318,6 +387,7 @@ class RecipesTable(ToasterTable):
self.add_column(title="Recipe file",
help_text="Path to the recipe .bb file",
+ hidden=True,
static_data_name="recipe-file",
static_data_template=recipe_file_template)
@@ -348,6 +418,7 @@ class RecipesTable(ToasterTable):
self.add_column(title="Build",
help_text="Add or delete recipes to and from your project",
hideable=False,
+ filter_name="in_current_project",
static_data_name="add-del-layers",
static_data_template='{% include "recipe_btn.html" %}')
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 21/23] toaster: bldcontrol Ignore toasterconf files in own directories
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (19 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 20/23] toaster: Restore 'in project' filters to main tables Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:33 ` [PATCH 22/23] toaster: split orm app into it's own module and app Alex DAMIAN
2015-06-25 10:34 ` [PATCH 23/23] toaster: Add url pattern for backward compatibility Alex DAMIAN
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Michael Wood <michael.g.wood@intel.com>
If toaster has previously cloned poky or a layer with a toasterconf
file we don't want this to be picked up as a choice as these clones are
"internal" to toaster and may have undesired effects on toaster's setup.
[YOCTO #7741]
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
lib/toaster/bldcontrol/management/commands/checksettings.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/toaster/bldcontrol/management/commands/checksettings.py b/lib/toaster/bldcontrol/management/commands/checksettings.py
index 3c52446..3002e4c 100644
--- a/lib/toaster/bldcontrol/management/commands/checksettings.py
+++ b/lib/toaster/bldcontrol/management/commands/checksettings.py
@@ -166,6 +166,8 @@ class Command(NoArgsCommand):
conffilepath, error = subprocess.Popen('bash -c ". '+os.path.join(dirname, ".templateconf")+'; echo \"\$TEMPLATECONF\""', shell=True, stdout=subprocess.PIPE).communicate()
conffilepath = os.path.join(conffilepath.strip(), "toasterconf.json")
candidatefilepath = os.path.join(dirname, conffilepath)
+ if "toaster_cloned" in candidatefilepath:
+ continue
if os.path.exists(candidatefilepath):
config_files.append(candidatefilepath)
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 22/23] toaster: split orm app into it's own module and app
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (20 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 21/23] toaster: bldcontrol Ignore toasterconf files in own directories Alex DAMIAN
@ 2015-06-25 10:33 ` Alex DAMIAN
2015-06-25 10:34 ` [PATCH 23/23] toaster: Add url pattern for backward compatibility Alex DAMIAN
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:33 UTC (permalink / raw)
To: bitbake-devel
From: Michael Wood <michael.g.wood@intel.com>
The orm application was also the django application to collect the build
information. Splitting this module up into it's functional parts. orm
for the data module and bldcollector for build collection data.
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
lib/toaster/bldcollector/__init__.py | 0
lib/toaster/{orm => bldcollector}/admin.py | 3 +--
lib/toaster/{orm => bldcollector}/urls.py | 3 +--
lib/toaster/{orm => bldcollector}/views.py | 0
lib/toaster/toastermain/settings.py | 2 +-
5 files changed, 3 insertions(+), 5 deletions(-)
create mode 100644 lib/toaster/bldcollector/__init__.py
rename lib/toaster/{orm => bldcollector}/admin.py (92%)
rename lib/toaster/{orm => bldcollector}/urls.py (95%)
rename lib/toaster/{orm => bldcollector}/views.py (100%)
diff --git a/lib/toaster/bldcollector/__init__.py b/lib/toaster/bldcollector/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/lib/toaster/orm/admin.py b/lib/toaster/bldcollector/admin.py
similarity index 92%
rename from lib/toaster/orm/admin.py
rename to lib/toaster/bldcollector/admin.py
index 706e517..c1f85d7 100644
--- a/lib/toaster/orm/admin.py
+++ b/lib/toaster/bldcollector/admin.py
@@ -1,6 +1,6 @@
from django.contrib import admin
from django.contrib.admin.filters import RelatedFieldListFilter
-from .models import BitbakeVersion, Release, LayerSource, ToasterSetting
+from orm.models import BitbakeVersion, Release, LayerSource, ToasterSetting
from django.forms.widgets import Textarea
from django import forms
import django.db.models as models
@@ -31,4 +31,3 @@ admin.site.register(LayerSource, LayerSourceAdmin)
admin.site.register(BitbakeVersion, BitbakeVersionAdmin)
admin.site.register(Release, ReleaseAdmin)
admin.site.register(ToasterSetting, ToasterSettingAdmin)
-
diff --git a/lib/toaster/orm/urls.py b/lib/toaster/bldcollector/urls.py
similarity index 95%
rename from lib/toaster/orm/urls.py
rename to lib/toaster/bldcollector/urls.py
index 961bc19..144387b 100644
--- a/lib/toaster/orm/urls.py
+++ b/lib/toaster/bldcollector/urls.py
@@ -20,8 +20,7 @@
from django.conf.urls import patterns, include, url
from django.views.generic import RedirectView
-urlpatterns = patterns('orm.views',
+urlpatterns = patterns('bldcollector.views',
# landing point for pushing a bitbake_eventlog.json file to this toaster instace
url(r'^eventfile$', 'eventfile', name='eventfile'),
)
-
diff --git a/lib/toaster/orm/views.py b/lib/toaster/bldcollector/views.py
similarity index 100%
rename from lib/toaster/orm/views.py
rename to lib/toaster/bldcollector/views.py
diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index 60e80de..c72a904 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -268,7 +268,7 @@ INSTALLED_APPS = (
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'django.contrib.humanize',
- 'orm',
+ 'bldcollector',
'toastermain',
'south',
)
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH 23/23] toaster: Add url pattern for backward compatibility
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
` (21 preceding siblings ...)
2015-06-25 10:33 ` [PATCH 22/23] toaster: split orm app into it's own module and app Alex DAMIAN
@ 2015-06-25 10:34 ` Alex DAMIAN
22 siblings, 0 replies; 25+ messages in thread
From: Alex DAMIAN @ 2015-06-25 10:34 UTC (permalink / raw)
To: bitbake-devel
From: Michael Wood <michael.g.wood@intel.com>
This adds an url to match the old orm application.
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
lib/toaster/toastermain/urls.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/lib/toaster/toastermain/urls.py b/lib/toaster/toastermain/urls.py
index 26ad3a2..521588a 100644
--- a/lib/toaster/toastermain/urls.py
+++ b/lib/toaster/toastermain/urls.py
@@ -40,6 +40,10 @@ urlpatterns = patterns('',
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+ # This is here to maintain backward compatibility and will be deprecated
+ # in the future.
+ url(r'^orm/eventfile$', 'bldcollector.views.eventfile'),
+
# if no application is selected, we have the magic toastergui app here
url(r'^$', never_cache(RedirectView.as_view(url='/toastergui/'))),
)
--
1.9.1
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH 16/23] toasterui: fixes after html5 compliance testing
2015-06-25 10:33 ` [PATCH 16/23] toasterui: fixes after html5 compliance testing Alex DAMIAN
@ 2015-06-29 14:22 ` Michael Wood
0 siblings, 0 replies; 25+ messages in thread
From: Michael Wood @ 2015-06-29 14:22 UTC (permalink / raw)
To: bitbake-devel
On 25/06/15 11:33, Alex DAMIAN wrote:
> From: Alexandru DAMIAN <alexandru.damian@intel.com>
>
> This patch brings fixes for issues highlighted by
> HTML5 compliance testing.
>
> Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
> ---
> lib/toaster/orm/models.py | 4 +++-
> lib/toaster/toastergui/static/js/base.js | 2 +-
> lib/toaster/toastergui/templates/configvars.html | 2 +-
> .../toastergui/templates/detail_search_header.html | 2 +-
> .../toastergui/templates/detail_sorted_header.html | 2 +-
> .../toastergui/templates/generic-toastertable-page.html | 2 ++
> lib/toaster/toastergui/templates/importlayer.html | 15 +++++++++++++--
> lib/toaster/toastergui/templates/target.html | 2 +-
> lib/toaster/toastergui/templates/toastertable-simple.html | 4 ++--
> lib/toaster/toastergui/templates/toastertable.html | 4 ++--
> lib/toaster/toastergui/views.py | 11 ++++++-----
> 11 files changed, 33 insertions(+), 17 deletions(-)
>
> diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
> index 8488aa4..7e8ab16 100644
> --- a/lib/toaster/orm/models.py
> +++ b/lib/toaster/orm/models.py
> @@ -180,7 +180,9 @@ class Project(models.Model):
> if release == None:
> release = self.release
> # layers on the same branch or layers specifically set for this project
> - queryset = Layer_Version.objects.filter((Q(up_branch__name = release.branch_name) & Q(project = None)) | Q(project = self) | Q(build__project = self))
> + queryset = Layer_Version.objects.filter(Q(project = self) | Q(build__project = self))
> + if release is not None:
> + queryset = queryset.filter(Q(up_branch__name = release.branch_name) & Q(project = None))
> if layer_name is not None:
> # we select only a layer name
> queryset = queryset.filter(layer__name = layer_name)
> diff --git a/lib/toaster/toastergui/static/js/base.js b/lib/toaster/toastergui/static/js/base.js
> index d079f23..f1711c1 100644
> --- a/lib/toaster/toastergui/static/js/base.js
> +++ b/lib/toaster/toastergui/static/js/base.js
> @@ -39,7 +39,7 @@ function basePageInit(ctx) {
>
> libtoaster.getProjectInfo(selectedProject.projectPageUrl,
> function (data) {
> - if (data.machine.name === undefined || data.layers.length === 0) {
> + if (data.machine === null || data.machine.name === undefined || data.layers.length === 0) {
> /* we can't build anything with out a machine and some layers */
> $("#new-build-button #targets-form").hide();
> $("#new-build-button .alert").show();
> diff --git a/lib/toaster/toastergui/templates/configvars.html b/lib/toaster/toastergui/templates/configvars.html
> index 8957673..8a572ae 100644
> --- a/lib/toaster/toastergui/templates/configvars.html
> +++ b/lib/toaster/toastergui/templates/configvars.html
> @@ -39,7 +39,7 @@
> <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 %}
> + <input id="search" name="search" class="input-xxlarge" type="text" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>{% 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 variables</button>
> </form>
> diff --git a/lib/toaster/toastergui/templates/detail_search_header.html b/lib/toaster/toastergui/templates/detail_search_header.html
> index ca8e158..7bea3f4 100644
> --- a/lib/toaster/toastergui/templates/detail_search_header.html
> +++ b/lib/toaster/toastergui/templates/detail_search_header.html
> @@ -28,7 +28,7 @@ $(document).ready(function() {
> <form id="searchform" class="navbar-search input-append pull-left">
> {% endif %}
>
> - <input id="search" class="input-xlarge" type="text" placeholder="Search {{search_what}}" name="search" value="{{request.GET.search}}">
> + <input id="search" class="input-xlarge" type="text" placeholder="Search {{search_what}}" name="search" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}">
> <input type="hidden" value="name:+" name="orderby">
> <input type="hidden" value="l" name="page">
> {% if request.GET.search %}
> diff --git a/lib/toaster/toastergui/templates/detail_sorted_header.html b/lib/toaster/toastergui/templates/detail_sorted_header.html
> index 5214444..6ce292e 100644
> --- a/lib/toaster/toastergui/templates/detail_sorted_header.html
> +++ b/lib/toaster/toastergui/templates/detail_sorted_header.html
> @@ -9,7 +9,7 @@
> <thead>
> <!-- Table header row; generated from "tablecols" entry in the context dict -->
> <tr>
> - {% for tc in tablecols %}<th class="{{tc.dclass}} {{tc.clclass}}">
> + {% for tc in tablecols %}<th class="{%if tc.dclass%}{{tc.dclass}}{% endif %} {%if tc.class %}{{tc.clclass}}{% endif %}">
> {%if tc.qhelp%}<i class="icon-question-sign get-help" title="{{tc.qhelp}}"></i>{%endif%}
> {%if tc.orderfield%}<a {%if tc.ordericon%} class="sorted" {%endif%}href="javascript:reload_params({'page': 1, 'orderby' : '{{tc.orderfield}}' })">{{tc.name}}</a>{%else%}<span class="muted">{{tc.name}}</span>{%endif%}
> {%if tc.ordericon%} <i class="icon-caret-{{tc.ordericon}}"></i>{%endif%}
> diff --git a/lib/toaster/toastergui/templates/generic-toastertable-page.html b/lib/toaster/toastergui/templates/generic-toastertable-page.html
> index d7ad2e7..78e942c 100644
> --- a/lib/toaster/toastergui/templates/generic-toastertable-page.html
> +++ b/lib/toaster/toastergui/templates/generic-toastertable-page.html
> @@ -11,7 +11,9 @@
> {% block projectinfomain %}
> <div class="page-header">
> <h1>{{title}} (<span class="table-count-{{table_name}}"></span>)
> + {% if project.release %}
> <i class="icon-question-sign get-help heading-help" title="This page lists {{title}} compatible with the release selected for this project, which is {{project.release.description}}"></i>
> + {% endif %}
> </h1>
> </div>
> <div id="zone1alerts" style="display:none">
> diff --git a/lib/toaster/toastergui/templates/importlayer.html b/lib/toaster/toastergui/templates/importlayer.html
> index 498a204..6a5d412d 100644
> --- a/lib/toaster/toastergui/templates/importlayer.html
> +++ b/lib/toaster/toastergui/templates/importlayer.html
> @@ -9,6 +9,7 @@
>
> {% block projectinfomain %}
>
> + {% if project and project.release %}
> <script src="{% static 'js/layerDepsModal.js' %}"></script>
> <script src="{% static 'js/importlayer.js' %}"></script>
> <script>
> @@ -31,9 +32,7 @@
> </div>
>
> <form>
> - {% if project %}
> <span class="help-block" style="padding-left:19px;">The layer you are importing must be compatible with <strong>{{project.release.description}}</strong>, which is the release you are using in this project.</span>
> - {% endif %}
> <fieldset class="air">
> <legend>Layer repository information</legend>
> <div class="alert alert-error" id="import-error" style="display:none">
> @@ -131,4 +130,16 @@
> </div>
> </form>
>
> + {% else %} {#project and project release#}
> + <div class="page-header">
> + <h1>Import layer</h1>
> + </div>
> + <div class="alert alert-info" id="import-error" >
> + <h3>Unsupported project type</h3>
> + <p>This project does not support importing layers.</p>
> + <ul></ul>
> + </div>
> +
> + {% endif %}
> +
> {% endblock %}
> diff --git a/lib/toaster/toastergui/templates/target.html b/lib/toaster/toastergui/templates/target.html
> index fa59f4e..65e6c4a 100644
> --- a/lib/toaster/toastergui/templates/target.html
> +++ b/lib/toaster/toastergui/templates/target.html
> @@ -53,7 +53,7 @@
> <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 %}
> + <input id="search" name="search" class="input-xxlarge" type="text" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>{% 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 packages</button>
> </form>
> diff --git a/lib/toaster/toastergui/templates/toastertable-simple.html b/lib/toaster/toastergui/templates/toastertable-simple.html
> index ea7b38e..212318b 100644
> --- a/lib/toaster/toastergui/templates/toastertable-simple.html
> +++ b/lib/toaster/toastergui/templates/toastertable-simple.html
> @@ -29,7 +29,7 @@
> <div class="row-fluid" id="no-results-{{table_name}}" style="display:none">
> <div class="alert">
> <form class="no-results input-append">
> - <input class="input-xlarge" id="new-search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{{request.GET.search}}"/>
> + <input class="input-xlarge" id="new-search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>
> <a href="#" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1">
> <i class="icon-remove"></i>
> </a>
> @@ -44,7 +44,7 @@
> <div class="row-fluid" id="table-chrome-{{table_name}}">
> <div class="navbar-search input-append pull-left">
>
> - <input class="input-xlarge" id="search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{{request.GET.search}}"/>
> + <input class="input-xlarge" id="search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{% if request.GET.search %}{{request.GET.search}}{% endif %}"/>
> <a href="#" style="display:none" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1">
> <i class="icon-remove"></i>
> </a>
> diff --git a/lib/toaster/toastergui/templates/toastertable.html b/lib/toaster/toastergui/templates/toastertable.html
> index c7c7a84..0473116 100644
> --- a/lib/toaster/toastergui/templates/toastertable.html
> +++ b/lib/toaster/toastergui/templates/toastertable.html
> @@ -29,7 +29,7 @@
> <div class="row-fluid" id="no-results-{{table_name}}" style="display:none">
> <div class="alert">
> <form class="no-results input-append">
> - <input class="input-xxlarge" id="new-search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{{request.GET.search}}"/>
> + <input class="input-xxlarge" id="new-search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{%if request.GET.search %}{{request.GET.search}}{%endif%}"/>
> <a href="#" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1">
> <i class="icon-remove"></i>
> </a>
> @@ -46,7 +46,7 @@
> <div class="navbar-inner">
> <div class="navbar-search input-append pull-left">
>
> - <input class="input-xxlarge" id="search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{{request.GET.search}}"/>
> + <input class="input-xxlarge" id="search-input-{{table_name}}" name="search" type="text" placeholder="Search {{title|lower}}" value="{%if request.GET.search%}{{request.GET.search}}{%endif%}"/>
> <a href="#" style="display:none" class="add-on btn remove-search-btn-{{table_name}}" tabindex="-1">
> <i class="icon-remove"></i>
> </a>
> diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
> index ec65903..82650d0 100755
> --- a/lib/toaster/toastergui/views.py
> +++ b/lib/toaster/toastergui/views.py
> @@ -165,8 +165,8 @@ def _lv_to_dict(prj, x = None):
>
> return {"id": x.pk,
> "name": x.layer.name,
> - "tooltip": x.layer.vcs_url+" | "+x.get_vcs_reference(),
> - "detail": "(" + x.layer.vcs_url + (")" if x.up_branch == None else " | "+x.get_vcs_reference()+")"),
> + "tooltip": "%s | %s" % (x.layer.vcs_url,x.get_vcs_reference()),
> + "detail": "(%s" % x.layer.vcs_url + (")" if x.up_branch == None else " | "+x.get_vcs_reference()+")"),
> "giturl": x.layer.vcs_url,
> "layerdetailurl" : reverse('layerdetails', args=(prj.id,x.pk)),
> "revision" : x.get_vcs_reference(),
> @@ -559,10 +559,10 @@ def task( request, build_id, task_id ):
> uri_list= [ ]
> variables = Variable.objects.filter(build=build_id)
> v=variables.filter(variable_name='SSTATE_DIR')
> - if v.count > 0:
> + if v.count() > 0:
> uri_list.append(v[0].variable_value)
> v=variables.filter(variable_name='SSTATE_MIRRORS')
> - if (v.count > 0):
> + if (v.count() > 0):
> for mirror in v[0].variable_value.split('\\n'):
> s=re.sub('.* ','',mirror.strip(' \t\n\r'))
> if len(s): uri_list.append(s)
> @@ -2124,9 +2124,10 @@ if True:
> login(request, user)
>
> # save the project
> - release = Release.objects.get(pk = request.POST.get('projectversion', None ))
> if ptype == "analysis":
> release = None
> + else:
> + release = Release.objects.get(pk = request.POST.get('projectversion', None ))
>
> prj = Project.objects.create_project(name = request.POST['projectname'], release = release)
> prj.user_id = request.user.pk
Note: This patch as far as I can see wasn't submitted for review on the
Toaster mailing list. It currently breaks Toaster.
As this touches things which should not have been in this patch; js,
template changes, views and orm/models.py we can't revert it as it will
break other things. I will try to roll a patch which reverts part of
this patch.
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2015-06-29 14:22 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-06-25 10:33 [PATCH 00/23] toaster patchset Alex DAMIAN
2015-06-25 10:33 ` [PATCH 01/23] toastergui: fix angular error Alex DAMIAN
2015-06-25 10:33 ` [PATCH 02/23] toaster: improve the buildenvironment API Alex DAMIAN
2015-06-25 10:33 ` [PATCH 03/23] toastergui: enable strict variable checking Alex DAMIAN
2015-06-25 10:33 ` [PATCH 04/23] toaster: fixing undefined variables Alex DAMIAN
2015-06-25 10:33 ` [PATCH 05/23] toaster: remove MANAGED references Alex DAMIAN
2015-06-25 10:33 ` [PATCH 06/23] toaster: remove BuildRequest references Alex DAMIAN
2015-06-25 10:33 ` [PATCH 07/23] toaster: refactor the builds pages Alex DAMIAN
2015-06-25 10:33 ` [PATCH 08/23] toaster: fill in build data from buildrequest Alex DAMIAN
2015-06-25 10:33 ` [PATCH 09/23] toaster: fixes after replacing BuildRequest with Build Alex DAMIAN
2015-06-25 10:33 ` [PATCH 10/23] toaster: add django-aggregate-if Alex DAMIAN
2015-06-25 10:33 ` [PATCH 11/23] toaster: bring django-aggregate-if into the project Alex DAMIAN
2015-06-25 10:33 ` [PATCH 12/23] toaster: refactor build model Alex DAMIAN
2015-06-25 10:33 ` [PATCH 13/23] toastergui: select project types Alex DAMIAN
2015-06-25 10:33 ` [PATCH 14/23] toaster: delete multiple builds Alex DAMIAN
2015-06-25 10:33 ` [PATCH 15/23] toasterui: verify variable before usage Alex DAMIAN
2015-06-25 10:33 ` [PATCH 16/23] toasterui: fixes after html5 compliance testing Alex DAMIAN
2015-06-29 14:22 ` Michael Wood
2015-06-25 10:33 ` [PATCH 17/23] toaster: Enable toastertable cache Alex DAMIAN
2015-06-25 10:33 ` [PATCH 18/23] toaster: toastertable Pass up the kwargs for setup_filter Alex DAMIAN
2015-06-25 10:33 ` [PATCH 19/23] toaster: table.js fix filter visual indicator and interaction Alex DAMIAN
2015-06-25 10:33 ` [PATCH 20/23] toaster: Restore 'in project' filters to main tables Alex DAMIAN
2015-06-25 10:33 ` [PATCH 21/23] toaster: bldcontrol Ignore toasterconf files in own directories Alex DAMIAN
2015-06-25 10:33 ` [PATCH 22/23] toaster: split orm app into it's own module and app Alex DAMIAN
2015-06-25 10:34 ` [PATCH 23/23] toaster: Add url pattern for backward compatibility Alex DAMIAN
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.