toaster patchset
cover toaster patchset
From: Alexandru DAMIAN <[email protected]>
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.htmlSignature · 1 line
1.9.1
PATCH 1/23 toastergui: fix angular error
From: Alexandru DAMIAN <[email protected]>
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]
--- lib/toaster/toastergui/static/js/projectapp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/toaster/toastergui/static/js/projectapp.js +1 -1
@@ -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); } });
Signature · 1 line
1.9.1
PATCH 2/23 toaster: improve the buildenvironment API
From: Alexandru DAMIAN <[email protected]>
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.
--- 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(-)
lib/toaster/bldcontrol/bbcontroller.py +3 -0
@@ -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
lib/toaster/bldcontrol/localhostbecontroller.py +22 -0
@@ -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()
lib/toaster/bldcontrol/management/commands/runbuilds.py +7 -23
@@ -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):
lib/toaster/bldcontrol/sshbecontroller.py +21 -0
@@ -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()
Signature · 1 line
1.9.1
PATCH 3/23 toastergui: enable strict variable checking
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
In order to make sure we don't use undefined variables in the templates, we enforce strict variable checking in the templating engine.
--- lib/toaster/toastermain/settings.py | 7 +++++++ 1 file changed, 7 insertions(+)
lib/toaster/toastermain/settings.py +7 -0
@@ -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")
Signature · 1 line
1.9.1
PATCH 4/23 toaster: fixing undefined variables
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
This patchset fixes usage of undefined variables in the base page.
--- 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(-)
lib/toaster/toastergui/templates/base.html +7 -4
@@ -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>
lib/toaster/toastergui/templates/basetable_top.html +8 -3
@@ -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%}
lib/toaster/toastergui/templates/filtersnippet.html +1 -1
@@ -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 %}
Signature · 1 line
1.9.1
PATCH 5/23 toaster: remove MANAGED references
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
We conflate the managed and analysis modes by deleting alternative code paths, favouring the MANAGED mode, always considering the MANAGED variable True.
--- 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(-)
lib/toaster/orm/models.py +1 -3
@@ -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)
lib/toaster/toastergui/templates/base.html +1 -8
@@ -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>
lib/toaster/toastergui/templates/basebuilddetailpage.html +1 -3
@@ -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>
lib/toaster/toastergui/templates/basebuildpage.html +2 -4
@@ -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>
lib/toaster/toastergui/templates/basetable_top_buildprojects.html +0 -2
@@ -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%}
lib/toaster/toastergui/templates/basetable_top_projectbuilds.html +0 -2
@@ -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%}
lib/toaster/toastergui/templates/bpackage.html +0 -3
@@ -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>
lib/toaster/toastergui/templates/build.html +0 -8
@@ -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 %}
lib/toaster/toastergui/templates/builddashboard.html +0 -16
@@ -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>
lib/toaster/toastergui/views.py +3 -100
149 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH 6/23 toaster: remove BuildRequest references
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
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).
--- .../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(-)
lib/toaster/toastergui/templates/basebuildpage.html +1 -0
@@ -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>
lib/toaster/toastergui/templates/configvars.html +2 -2
@@ -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>
lib/toaster/toastergui/views.py +27 -39
132 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH 7/23 toaster: refactor the builds pages
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
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.
---
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.htmllib/toaster/toastergui/static/js/projectapp.js +9 -5
@@ -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."); } };
lib/toaster/toastergui/templates/builds.html renamed from lib/toaster/toastergui/templates/build.html +4 -3
@@ -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>
lib/toaster/toastergui/templates/managed_builds.html deleted +0 -167
167 lines not on this page · raw message
lib/toaster/toastergui/templates/managed_mrb_section.html deleted +0 -193
193 lines not on this page · raw message
lib/toaster/toastergui/templates/mrb_section.html +36 -5
59 lines not on this page · raw message
lib/toaster/toastergui/templates/project.html +8 -32
@@ -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>
lib/toaster/toastergui/templates/projectbuilds.html +0 -1
@@ -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 #}
lib/toaster/toastergui/templates/projects.html +14 -8
@@ -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>
lib/toaster/toastergui/urls.py +1 -1
@@ -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')),
lib/toaster/toastergui/views.py +103 -334
603 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH 8/23 toaster: fill in build data from buildrequest
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
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.
--- 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(-)
lib/bb/ui/buildinfohelper.py +15 -1
@@ -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'],
lib/toaster/bldcontrol/management/commands/runbuilds.py +31 -1
@@ -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
lib/toaster/orm/models.py +25 -3
@@ -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)
lib/toaster/toastergui/templates/project.html +11 -4
@@ -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">
lib/toaster/toastergui/views.py +2 -2
@@ -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(),
Signature · 1 line
1.9.1
PATCH 9/23 toaster: fixes after replacing BuildRequest with Build
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
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.
--- 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
lib/bb/ui/toasterui.py +10 -14
@@ -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
lib/toaster/bldcontrol/localhostbecontroller.py +1 -1
@@ -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
lib/toaster/bldcontrol/management/commands/runbuilds.py +4 -3
@@ -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()
lib/toaster/bldcontrol/models.py +3 -0
@@ -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)
lib/toaster/orm/migrations/0021_auto__chg_field_build_project__chg_field_project_bitbake_version__chg_.py +3 -2
@@ -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'}),
lib/toaster/orm/migrations/0022_auto__add_field_target_task__add_field_layer_version_local_path__del_f.py new file +343 -0
343 lines not on this page · raw message
lib/toaster/orm/models.py +6 -2
@@ -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:
lib/toaster/toastergui/static/js/projectapp.js +2 -2
@@ -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); }; });
lib/toaster/toastergui/templates/project.html +2 -2
@@ -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>
lib/toaster/toastergui/views.py +4 -2
@@ -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) ),
Signature · 1 line
1.9.1
PATCH 10/23 toaster: add django-aggregate-if
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
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
--- .../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
lib/toaster/contrib/django-aggregate-if-master/.gitignore new file +10 -0
@@ -0,0 +1,10 @@ +*.pyc +*.swp +*.swo +*.kpf +*.egg-info/ +.idea +.tox +tmp/ +dist/ +.DS_Store
lib/toaster/contrib/django-aggregate-if-master/.travis.yml new file +50 -0
50 lines not on this page · raw message
lib/toaster/contrib/django-aggregate-if-master/LICENSE new file +21 -0
@@ -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.
lib/toaster/contrib/django-aggregate-if-master/README.rst new file +156 -0
156 lines not on this page · raw message
lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py new file +164 -0
164 lines not on this page · raw message
lib/toaster/contrib/django-aggregate-if-master/runtests.py new file +48 -0
48 lines not on this page · raw message
lib/toaster/contrib/django-aggregate-if-master/setup.py new file +33 -0
@@ -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="[email protected]", + 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/', +)
lib/toaster/contrib/django-aggregate-if-master/tox.ini new file +198 -0
198 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH 11/23 toaster: bring django-aggregate-if into the project
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
We bring the django-aggregate-if module (with minor changes to import) into the project.
--- lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py | 2 +- lib/toaster/toastermain/settings.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-)
lib/toaster/contrib/django-aggregate-if-master/aggregate_if.py +1 -1
@@ -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
lib/toaster/toastermain/settings.py +9 -0
@@ -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") + )
Signature · 1 line
1.9.1
PATCH 12/23 toaster: refactor build model
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
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.
--- 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
lib/bb/ui/buildinfohelper.py +47 -23
@@ -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):
lib/bb/ui/toasterui.py +3 -0
@@ -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
lib/toaster/bldcontrol/localhostbecontroller.py +1 -1
@@ -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()
lib/toaster/bldcontrol/management/commands/runbuilds.py +7 -3
@@ -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
lib/toaster/orm/migrations/0023_auto__del_field_build_warnings_no__del_field_build_errors_no__del_fiel.py new file +353 -0
354 lines not on this page · raw message
lib/toaster/orm/models.py +13 -6
@@ -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
lib/toaster/toastergui/templates/builddashboard.html +12 -11
@@ -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>
lib/toaster/toastergui/templates/builds.html +13 -9
@@ -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>
lib/toaster/toastergui/templates/configuration.html +0 -6
@@ -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>
lib/toaster/toastergui/templates/filtersnippet.html +2 -0
@@ -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>
lib/toaster/toastergui/templates/landing.html +2 -35
@@ -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 %}
lib/toaster/toastergui/templates/mrb_section.html +23 -6
@@ -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); }
lib/toaster/toastergui/templates/package_detail_base.html +0 -7
@@ -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 %}
lib/toaster/toastergui/templates/projectbuilds.html +12 -17
@@ -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}}
lib/toaster/toastergui/templates/projects.html +6 -3
@@ -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>
lib/toaster/toastergui/templates/recipe.html +1 -8
@@ -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>
lib/toaster/toastergui/templates/recipes.html +1 -6
@@ -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 %}
lib/toaster/toastergui/templates/runagain.html deleted +0 -7
@@ -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
lib/toaster/toastergui/templates/target.html +0 -5
@@ -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 %}
lib/toaster/toastergui/templates/task.html +1 -35
@@ -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.
lib/toaster/toastergui/templates/tasks.html +1 -6
@@ -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 %}
lib/toaster/toastergui/urls.py +0 -3
@@ -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')), )
lib/toaster/toastergui/views.py +5 -12
@@ -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
Signature · 1 line
1.9.1
PATCH 13/23 toastergui: select project types
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
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.
--- 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(-)
lib/toaster/orm/models.py +7 -1
@@ -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:
lib/toaster/toastergui/templates/newproject.html +64 -35
130 lines not on this page · raw message
lib/toaster/toastergui/views.py +9 -2
@@ -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")
Signature · 1 line
1.9.1
PATCH 14/23 toaster: delete multiple builds
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
This patch fixes the build deletion on unmigrated databases, and enhances it to delete multiple builds in a single run.
[YOCTO #7726]
--- lib/toaster/toastergui/views.py | 4 +- .../toastermain/management/commands/builddelete.py | 56 ++++++++++++++-------- 2 files changed, 38 insertions(+), 22 deletions(-)
lib/toaster/toastergui/views.py +2 -2
@@ -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
lib/toaster/toastermain/management/commands/builddelete.py +36 -20
69 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH 15/23 toasterui: verify variable before usage
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
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.
--- lib/bb/ui/buildinfohelper.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
lib/bb/ui/buildinfohelper.py +5 -4
@@ -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)
Signature · 1 line
1.9.1
PATCH 16/23 toasterui: fixes after html5 compliance testing
In reply to Alex DAMIANFrom: Alexandru DAMIAN <[email protected]>
This patch brings fixes for issues highlighted by
HTML5 compliance testing.
--- 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(-)
lib/toaster/orm/models.py +3 -1
@@ -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)
lib/toaster/toastergui/static/js/base.js +1 -1
@@ -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();
lib/toaster/toastergui/templates/configvars.html +1 -1
@@ -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>
lib/toaster/toastergui/templates/detail_search_header.html +1 -1
@@ -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 %}
lib/toaster/toastergui/templates/detail_sorted_header.html +1 -1
@@ -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%}
lib/toaster/toastergui/templates/generic-toastertable-page.html +2 -0
@@ -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">
lib/toaster/toastergui/templates/importlayer.html +13 -2
@@ -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 %}
lib/toaster/toastergui/templates/target.html +1 -1
@@ -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>
lib/toaster/toastergui/templates/toastertable-simple.html +2 -2
@@ -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>
lib/toaster/toastergui/templates/toastertable.html +2 -2
@@ -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>
lib/toaster/toastergui/views.py +6 -5
@@ -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
Signature · 1 line
1.9.1
Re: [PATCH 16/23] toasterui: fixes after html5 compliance testing
Quoted 227 lines · Alex DAMIAN
From: Alexandru DAMIAN <[email protected]>
This patch brings fixes for issues highlighted by
HTML5 compliance testing.Signed-off-by Alexandru DAMIAN <[email protected]>--- 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(-)lib/toaster/orm/models.py +3
-1@@ -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)lib/toaster/toastergui/static/js/base.js +1
-1@@ -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();lib/toaster/toastergui/templates/configvars.html +1
-1@@ -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>lib/toaster/toastergui/templates/detail_search_header.html +1
-1@@ -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 %}lib/toaster/toastergui/templates/detail_sorted_header.html +1
-1@@ -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%}lib/toaster/toastergui/templates/generic-toastertable-page.html +2
-0@@ -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">lib/toaster/toastergui/templates/importlayer.html +13
-2@@ -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 %}lib/toaster/toastergui/templates/target.html +1
-1@@ -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>lib/toaster/toastergui/templates/toastertable-simple.html +2
-2@@ -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>lib/toaster/toastergui/templates/toastertable.html +2
-2@@ -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>lib/toaster/toastergui/views.py +4
-4@@ -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)
lib/toaster/toastergui/views.py if True: · line 2133
Quoted 10 lines
lib/toaster/toastergui/views.py +2
-1# 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.
PATCH 17/23 toaster: Enable toastertable cache
In reply to Alex DAMIANFrom: Michael Wood <[email protected]>
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"
--- lib/toaster/toastergui/widgets.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
lib/toaster/toastergui/widgets.py +5 -2
@@ -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)
Signature · 1 line
1.9.1
PATCH 18/23 toaster: toastertable Pass up the kwargs for setup_filter
In reply to Alex DAMIANFrom: Michael Wood <[email protected]>
This allows us to setup_filter in tables using args such as the current project.
--- lib/toaster/toastergui/widgets.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
lib/toaster/toastergui/widgets.py +6 -6
@@ -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)
Signature · 1 line
1.9.1
PATCH 19/23 toaster: table.js fix filter visual indicator and interaction
In reply to Alex DAMIANFrom: Michael Wood <[email protected]>
- 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.
--- lib/toaster/toastergui/static/js/table.js | 59 +++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 15 deletions(-)
lib/toaster/toastergui/static/js/table.js +44 -15
@@ -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'); }); }
Signature · 1 line
1.9.1
PATCH 20/23 toaster: Restore 'in project' filters to main tables
In reply to Alex DAMIANFrom: Michael Wood <[email protected]>
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]
--- lib/toaster/toastergui/tables.py | 75 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-)
lib/toaster/toastergui/tables.py +73 -2
@@ -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" %}')
Signature · 1 line
1.9.1
PATCH 21/23 toaster: bldcontrol Ignore toasterconf files in own directories
In reply to Alex DAMIANFrom: Michael Wood <[email protected]>
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]
--- lib/toaster/bldcontrol/management/commands/checksettings.py | 2 ++ 1 file changed, 2 insertions(+)
lib/toaster/bldcontrol/management/commands/checksettings.py +2 -0
@@ -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)
Signature · 1 line
1.9.1
PATCH 22/23 toaster: split orm app into it's own module and app
In reply to Alex DAMIANFrom: Michael Wood <[email protected]>
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.
---
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%)lib/toaster/bldcollector/__init__.py new file +0 -0
lib/toaster/bldcollector/admin.py renamed from lib/toaster/orm/admin.py +1 -2
@@ -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) -
lib/toaster/bldcollector/urls.py renamed from lib/toaster/orm/urls.py +1 -2
@@ -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'), ) -
lib/toaster/bldcollector/views.py renamed from lib/toaster/orm/views.py +0 -0
lib/toaster/toastermain/settings.py +1 -1
@@ -268,7 +268,7 @@ INSTALLED_APPS = ( # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', 'django.contrib.humanize', - 'orm', + 'bldcollector', 'toastermain', 'south', )
Signature · 1 line
1.9.1
PATCH 23/23 toaster: Add url pattern for backward compatibility
In reply to Alex DAMIANFrom: Michael Wood <[email protected]>
This adds an url to match the old orm application.
--- lib/toaster/toastermain/urls.py | 4 ++++ 1 file changed, 4 insertions(+)
lib/toaster/toastermain/urls.py +4 -0
@@ -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/'))), )
Signature · 1 line
1.9.1