aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/toaster/toastergui/templates/base.html64
-rw-r--r--lib/toaster/toastergui/templates/basebuildpage.html69
-rw-r--r--lib/toaster/toastergui/templates/basetable.html64
-rw-r--r--lib/toaster/toastergui/templates/basetable_bottom.html60
-rw-r--r--lib/toaster/toastergui/templates/basetable_top.html66
-rw-r--r--lib/toaster/toastergui/templates/bpackage.html12
-rw-r--r--lib/toaster/toastergui/templates/build.html115
-rw-r--r--lib/toaster/toastergui/templates/builddashboard.html8
-rw-r--r--lib/toaster/toastergui/templates/buildtime.html4
-rw-r--r--lib/toaster/toastergui/templates/configuration.html10
-rw-r--r--lib/toaster/toastergui/templates/cpuusage.html4
-rw-r--r--lib/toaster/toastergui/templates/diskio.html4
-rw-r--r--lib/toaster/toastergui/templates/recipe.html15
-rw-r--r--lib/toaster/toastergui/templates/target.html8
-rw-r--r--lib/toaster/toastergui/templates/task.html11
-rw-r--r--lib/toaster/toastergui/templatetags/projecttags.py5
-rw-r--r--lib/toaster/toastergui/urls.py34
-rw-r--r--lib/toaster/toastergui/views.py167
-rw-r--r--lib/toaster/toastermain/settings.py10
19 files changed, 571 insertions, 159 deletions
diff --git a/lib/toaster/toastergui/templates/base.html b/lib/toaster/toastergui/templates/base.html
index d58cbeaed..3508962e6 100644
--- a/lib/toaster/toastergui/templates/base.html
+++ b/lib/toaster/toastergui/templates/base.html
@@ -1,30 +1,64 @@
<!DOCTYPE html>
{% load static %}
<html>
- <head>
- <title>Toaster Simple Explorer</title>
-<script src="{% static 'js/jquery-2.0.3.js' %}">
+ <head>
+ <title>Toaster</title>
+<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}" type="text/css">
+<link rel="stylesheet" href="{% static 'css/bootstrap-responsive.min.css' %}" type='text/css'>
+<link rel="stylesheet" href="{% static 'css/font-awesome.min.css' %}" type='text/css'>
+<link rel="stylesheet" href="{% static 'css/prettify.css' %}" type='text/css'>
+<link rel="stylesheet" href="{% static 'css/default.css' %}" type='text/css'>
+<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+<script src="{% static 'js/jquery-2.0.3.min.js' %}">
</script>
-<script src="{% static 'js/bootstrap.js' %}">
+<script src="{% static 'js/bootstrap.min.js' %}">
</script>
-<link href="{% static 'css/bootstrap.css' %}" rel="stylesheet" type="text/css">
- </head>
+<script src="{% static 'js/prettify.js' %}">
+</script>
+<script src="{% static 'js/main.js' %}">
+</script>
+<script>
+function reload_params(params) {
+ uri = window.location.href;
+ [url, parameters] = uri.split("?");
+ // deserialize the call parameters
+ cparams = parameters.split("&");
+ nparams = {}
+ for (i = 0; i < cparams.length; i++) {
+ temp = cparams[i].split("=");
+ nparams[temp[0]] = temp[1];
+ }
+ // update parameter values
+ for (i in params) {
+ nparams[encodeURIComponent(i)] = encodeURIComponent(params[i]);
+ }
+ // serialize the structure
+ callparams = []
+ for (i in nparams) {
+ callparams.push(i+"="+nparams[i]);
+ }
+ window.location.href = url+"?"+callparams.join('&');
+
+}
+</script>
+ </head>
<body style="height: 100%">
-<div style="width:100%; height: 100%; position:absolute">
-<div style="width: 100%; height: 3em" class="nav">
- <ul class="nav nav-tabs">
- <li><a href="{% url "all-builds" %}">All Builds</a></li>
- <li><a href="{% url "all-layers" %}">All Layers</a></li>
- </ul>
+<div class="navbar navbar-static-top">
+ <div class="navbar-inner">
+ <a class="brand logo" href="#"><img src="{% static 'img/logo.png' %}" class="" alt="Yocto logo project"/></a>
+ <a class="brand" href="/">Toaster</a>
+ <a class="pull-right manual" href="#">
+ <i class="icon-book"></i>
+ Toaster manual
+ </a>
+ </div>
</div>
-<div style="overflow-y:scroll; width: 100%; position: absolute; top: 3em; bottom:70px ">
+<div class="container-fluid">
{% block pagecontent %}
{% endblock %}
</div>
-<div class="navbar" style="position: absolute; bottom: 0; width:100%"><br/>About Toaster | Yocto Project </div>
-</div>
</body>
</html>
diff --git a/lib/toaster/toastergui/templates/basebuildpage.html b/lib/toaster/toastergui/templates/basebuildpage.html
index d590f28bc..1b037f953 100644
--- a/lib/toaster/toastergui/templates/basebuildpage.html
+++ b/lib/toaster/toastergui/templates/basebuildpage.html
@@ -1,17 +1,56 @@
-{% extends "basetable.html" %}
-
-{% block pagename %}
-<ul class="nav nav-tabs" style="display: inline-block">
- <li><a>Build {{build.target_set.all|join:"&nbsp;"}} at {{build.started_on}} : </a></li>
- <li><a href="{% url "task" build.id %}"> Tasks </a></li>
- <li><a href="{% url "bpackage" build.id %}"> Build Packages </a></li>
- {% for t in build.target_set.all %}
- {% if t.is_image %}
- <li><a href="{% url "tpackage" build.id t.pk %}"> Packages for {{t.target}} </a> </li>
- {% endif %}
- {% endfor %}
- <li><a href="{% url "configuration" build.id %}"> Configuration </a> </li>
-</ul>
- <h1>Toaster - Build {% block pagetitle %} {% endblock %}</h1>
+{% extends "base.html" %}
+{% load humanize %}
+{% block pagecontent %}
+
+
+ <div class="span12">
+<!-- Breadcrumbs -->
+ <div class="section">
+ <ul class="breadcrumb" id="breadcrumb">
+<li><a href="{% url 'all-builds' %}">All builds</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.machine}} ({{build.completed_on|naturaltime}})</a></li>
+ {% block localbreadcrumb %}{% endblock %}
+ </ul>
+ <script>
+ $( function () {
+ $('#breadcrumb > li').append("<span class=\"divider\">→</span>");
+ $('#breadcrumb > li:last').addClass("active");
+ $('#breadcrumb > li:last > span').remove();
+ });
+ </script>
+ </div>
+
+ <div class="row-fluid">
+
+ <!-- begin left sidebar container -->
+ <div id="nav" class="span2">
+ <ul class="nav nav-list well">
+ <li class="nav-header">Images</li>
+ {% for t in build.target_set.all %}
+ <li><a href="{% url 'target' build.pk t.pk %}">{{t.target}}</a><li>
+ {% endfor %}
+ <li class="nav-header">Build</li>
+ <li><a href="{% url 'configuration' build.pk %}">Configuration</a></li>
+ <li><a href="{% url 'tasks' build.pk %}">Tasks</a></li>
+ <li><a href="{% url 'recipes' build.pk %}">Recipes</a></li>
+ <li><a href="{% url 'packages' build.pk %}">Packages</a></li>
+ <li class="nav-header">Performance</li>
+ <li><a href="{% url 'buildtime' build.pk %}">Time</a></li>
+ <li><a href="{% url 'cpuusage' build.pk %}">CPU usage</a></li>
+ <li><a href="{% url 'diskio' build.pk %}">Disk I/O</a></li>
+ </ul>
+ </div>
+
+ <!-- end left sidebar container -->
+ <!-- Begin right container -->
+ <div class="row span10">
+ {% block buildinfomain %}{% endblock %}
+ </div>
+
+
+ </div>
+ </div>
+
+
{% endblock %}
diff --git a/lib/toaster/toastergui/templates/basetable.html b/lib/toaster/toastergui/templates/basetable.html
deleted file mode 100644
index 16628eafb..000000000
--- a/lib/toaster/toastergui/templates/basetable.html
+++ /dev/null
@@ -1,64 +0,0 @@
-{% extends "base.html" %}
-
-{% block pagecontent %}
-<script>
-function showhideTableColumn(i, sh) {
- if (sh)
- $('td:nth-child('+i+'),th:nth-child('+i+')').show();
- else
- $('td:nth-child('+i+'),th:nth-child('+i+')').hide();
-}
-
-
-function filterTableRows(test) {
- if (test.length > 0) {
- var r = test.split(/[ ,]+/).map(function (e) { return new RegExp(e, 'i') });
- $('tr.data').map( function (i, el) {
- (! r.map(function (j) { return j.test($(el).html())}).reduce(function (c, p) { return c && p;} )) ? $(el).hide() : $(el).show();
- });
- } else
- {
- $('tr.data').show();
- }
-}
-</script>
-<div style="margin-bottom: 0.5em">
-
- {% block pagename %}
- {% endblock %}
- <div align="left" style="display:inline-block; width: 40%; margin-left: 2em"> Filter: <input type="search" id="filterstring" style="width: 80%" onkeyup="filterTableRows($('#filterstring').val())" autocomplete="off">
- </div>
- {% if hideshowcols %}
- <div align="right" style="display: inline-block; width: 40%">Show/Hide columns:
- {% for i in hideshowcols %}
- <span>{{i.name}} <input type="checkbox" id="ct{{i.name}}" onchange="showhideTableColumn({{i.order}}, $('#ct{{i.name}}').is(':checked'))" checked autocomplete="off"></span> |
- {% endfor %}
- </div>
- {% endif %}
-</div>
-
- <div style="display: block; float:right; margin-left: auto; margin-right:5em"><span class="pagination" style="vertical-align: top; margin-right: 3em">Showing {{objects.start_index}} to {{objects.end_index}} out of {{objects.paginator.count}} entries.&nbsp;</span>
- <ul class="pagination" style="display: block-inline">
-{%if objects.has_previous %}
- <li><a href="?page={{objects.previous_page_number}}">&laquo;</a></li>
-{%else%}
- <li class="disabled"><a href="#">&laquo;</a></li>
-{%endif%}
-{% for i in objects.page_range %}
- <li{%if i == objects.number %} class="active" {%endif%}><a href="?page={{i}}">{{i}}</a></li>
-{% endfor %}
-{%if objects.has_next%}
- <li><a href="?page={{objects.next_page_number}}">&raquo;</a></li>
-{%else%}
- <li class="disabled"><a href="#">&raquo;</a></li>
-{%endif%}
- </ul>
-</div>
-
- <table class="table table-striped table-condensed" style="width:95%">
-{% block pagetable %}
-{% endblock %}
- </table>
-</div>
-
-{% endblock %}
diff --git a/lib/toaster/toastergui/templates/basetable_bottom.html b/lib/toaster/toastergui/templates/basetable_bottom.html
new file mode 100644
index 000000000..2a6f08492
--- /dev/null
+++ b/lib/toaster/toastergui/templates/basetable_bottom.html
@@ -0,0 +1,60 @@
+ </table>
+
+<!-- Show pagination controls -->
+<div class="pagination pagination-centered">
+ <div class="pull-left">
+ Showing {{objects.start_index}} to {{objects.end_index}} out of {{objects.paginator.count}} entries.
+ </div>
+
+ <ul class="pagination" style="display: block-inline">
+{%if objects.has_previous %}
+ <li><a href="?page={{objects.previous_page_number}}&count={{request.GET.count}}">&laquo;</a></li>
+{%else%}
+ <li class="disabled"><a href="#">&laquo;</a></li>
+{%endif%}
+{% for i in objects.page_range %}
+ <li{%if i == objects.number %} class="active" {%endif%}><a href="?page={{i}}&count={{request.GET.count}}">{{i}}</a></li>
+{% endfor %}
+{%if objects.has_next%}
+ <li><a href="?page={{objects.next_page_number}}&count={{request.GET.count}}">&raquo;</a></li>
+{%else%}
+ <li class="disabled"><a href="#">&raquo;</a></li>
+{%endif%}
+ </ul>
+ <div class="pull-right">
+ <span class="help-inline" style="padding-top:5px;">Show rows:</span>
+ <select style="margin-top:5px;margin-bottom:0px;" class="pagesize">
+ {% with "2 5 10 25 50 100" as list%}
+ {% for i in list.split %}<option{%if i == request.GET.count %} selected{%endif%}>{{i}}</option>
+ {% endfor %}
+ {% endwith %}
+ </select>
+ </div>
+</div>
+
+<!-- Update page display settings -->
+
+<script>
+ $(document).ready(function() {
+
+ $('.chbxtoggle').each(function () {
+ showhideTableColumn($(this).attr('id'), $(this).is(':checked'))
+ });
+
+ //turn edit columns dropdown into a multi-select menu
+ $('.dropdown-menu input, .dropdown-menu label').click(function(e) {
+ e.stopPropagation();
+ });
+
+ //show tooltip with applied filter
+ $('#filtered').tooltip({container:'table', placement:'bottom', delay:{hide:1500}, html:true});
+
+ //progress bar tooltip
+ $('.progress, .lead span').tooltip({container:'table', placement:'top'});
+
+ $(".pagesize").change(function () {
+ $(".pagesize option:selected").each(function ()
+ {reload_params({"count":$(this).text()}); });
+ });
+});
+</script>
diff --git a/lib/toaster/toastergui/templates/basetable_top.html b/lib/toaster/toastergui/templates/basetable_top.html
new file mode 100644
index 000000000..b9277b4a3
--- /dev/null
+++ b/lib/toaster/toastergui/templates/basetable_top.html
@@ -0,0 +1,66 @@
+<!-- component to display a generic table -->
+ <script>
+ function showhideTableColumn(clname, sh) {
+ if (sh) $('.' + clname).show();
+ else $('.' + clname).hide();
+ }
+
+
+ function filterTableRows(test) {
+ if (test.length > 0) {
+ var r = test.split(/[ ,]+/).map(function (e) { return new RegExp(e, 'i') });
+ $('tr.data').map( function (i, el) {
+ (! r.map(function (j) { return j.test($(el).html())}).reduce(function (c, p) { return c && p;} )) ? $(el).hide() : $(el).show();
+ });
+ } else
+ {
+ $('tr.data').show();
+ }
+ }
+ </script>
+
+<!-- control header -->
+<div class="navbar">
+ <div class="navbar-inner">
+ <form class="navbar-search input-append pull-left">
+ <input class="input-xxlarge" type="text" placeholder="Search {{objectname}}" />
+ <button class="btn" type="button">Search</button>
+ </form>
+ <div class="pull-right">
+
+ {% if tablecols %}
+ <div class="btn-group">
+ <button class="btn dropdown-toggle" data-toggle="dropdown">
+ Edit columns
+ <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu">
+
+ {% for i in tablecols %}
+ <li>
+ <label class="checkbox">
+<input type="checkbox" class="chbxtoggle" id="{{i.clclass}}" value="ct{{i.name}}" {% if i.clclass %}{% if not i.hidden %}checked="checked"{%endif%} onchange="showhideTableColumn($(this).attr('id'), $(this).is(':checked'))" {%else%} disabled{% endif %}/> {{i.name}}
+ </label>
+ </li>
+ {% endfor %}
+ </ul>
+ </div>
+ {% endif %}
+
+ <div style="display:inline">
+ <span class="divider-vertical"></span>
+ <span class="help-inline" style="padding-top:5px;">Show rows:</span>
+ <select style="margin-top:5px;margin-bottom:0px;" class="pagesize">
+ {% with "2 5 10 25 50 100" as list%}
+ {% for i in list.split %}<option{%if i == request.GET.count %} selected{%endif%}>{{i}}</option>
+ {% endfor %}
+ {% endwith %}
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+
+<!-- the actual rows of the table -->
+ <table class="table table-bordered table-hover tablesorter" id="otable">
+
diff --git a/lib/toaster/toastergui/templates/bpackage.html b/lib/toaster/toastergui/templates/bpackage.html
index 67fc65ca3..3329ddae5 100644
--- a/lib/toaster/toastergui/templates/bpackage.html
+++ b/lib/toaster/toastergui/templates/bpackage.html
@@ -1,7 +1,12 @@
{% extends "basebuildpage.html" %}
-{% block pagetitle %}Packages{% endblock %}
-{% block pagetable %}
+{% block localbreadcrumb %}
+<li>Packages</li>
+{% endblock %}
+
+{% block buildinfomain %}
+{% include "basetable_top.html" %}
+
{% if not objects %}
<p>No packages were recorded for this target!</p>
{% else %}
@@ -21,7 +26,7 @@
{% for package in objects %}
<tr class="data">
- <td><a name="#{{package.name}}" href="{% url "bfile" build.pk package.pk %}">{{package.name}} ({{package.filelist_bpackage.count}} files)</a></td>
+ <td><a name="#{{package.name}}" href="{% url "package" build.pk package.pk %}">{{package.name}} ({{package.filelist_bpackage.count}} files)</a></td>
<td>{{package.version}}-{{package.revision}}</td>
<td>{%if package.recipe%}<a href="{% url "layer_versions_recipes" package.recipe.layer_version_id %}#{{package.recipe.name}}">{{package.recipe.name}}</a>{{package.package_name}}</a>{%endif%}</td>
@@ -41,4 +46,5 @@
{% endif %}
+{% include "basetable_bottom.html" %}
{% endblock %}
diff --git a/lib/toaster/toastergui/templates/build.html b/lib/toaster/toastergui/templates/build.html
index 4fa87d527..27ce1ccbc 100644
--- a/lib/toaster/toastergui/templates/build.html
+++ b/lib/toaster/toastergui/templates/build.html
@@ -1,43 +1,96 @@
-{% extends "basetable.html" %}
+{% extends "base.html" %}
+
+
+{% load projecttags %}
+{% load humanize %}
+
+{% block pagecontent %}
+<div class="row-fluid">
+
+<div class="page-header" style="margin-top:40px;">
+ <h1>
+ Recent Builds
+ </h1>
+</div>
+{{build_mru}}
+{% for build in mru %}
+<div class="alert {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%}">
+ <div class="row-fluid">
+ <div class="lead span5">
+ {%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 href="{%url 'builddashboard' build.pk%}">
+ <span data-toggle="tooltip" {%if build.target_set.all.count > 1%}title="Targets: {%for target in build.target_set.all%}{{target.target}} {%endfor%}"{%endif%}>{{build.target_set.all.0.target}} {%if build.target_set.all.count > 1%}(+ {{build.target_set.all.count|add:"-1"}}){%endif%} {{build.machine}} ({{build.completed_on|naturaltime}})</span>
+ </a>
+ </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%}" class="error">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>
+{% endif %}
+ </div>
+ <div class="span2 lead">
+{% if build.warnings_no %}
+ <i class="icon-warning-sign yellow"></i> <a href="{%url 'builddashboard' build.pk%}" class="warning">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>
+{% endif %}
+ </div>
+ <div class="lead pull-right">
+ Build time: <a href="build-time.html">{{ build|timespent }}</a>
+ </div>
+{%endif%}{%if build.outcome == build.IN_PROGRESS %}
+ <div class="span4">
+ <div class="progress" style="margin-top:5px;" data-toggle="tooltip" title="{{build.completeper}}% of tasks complete">
+ <div style="width: {{build.completeper}}%;" class="bar"></div>
+ </div>
+ </div>
+ <div class="lead pull-right">ETA: in {{build.eta|naturaltime}}</div>
+{%endif%}
+ </div>
+</div>
+
+{% endfor %}
-{% block pagename %}
- <h1>Toaster - Builds</h1>
-{% endblock %}
-{% block pagetable %}
+<div class="page-header" style="margin-top:40px;">
+ <h1>
+ All builds
+ </h1>
+</div>
+
+{% include "basetable_top.html" %}
- {% load projecttags %}
<tr>
- <th>Outcome</th>
- <th>Started On</th>
- <th>Completed On</th>
- <th>Target</th>
- <th>Machine</th>
- <th>Time</th>
- <th>Errors</th>
- <th>Warnings</th>
- <th>Output</th>
- <th>Log</th>
- <th>Bitbake Version</th>
- <th>Build Name</th>
+ <th class="outcome span2"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="The outcome tells you if a build completed successfully or failed"></i> <a href="#" style="font-weight:normal;">Outcome</a> <div class="btn-group pull-right"> <a href="#outcome" role="button" class="btn btn-mini" data-toggle="modal"> <i class="icon-filter"></i> </a> </div> </th>
+ <th class="target"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="This is the build target(s): one or more recipes or image recipes"></i> <a href="#" style="font-weight:normal;">Target</a> </th>
+ <th class="machine span3"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="The machine is the hardware for which you are building"></i> <a href="#" style="font-weight:normal;">Machine</a> </th>
+ <th class="started_on"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="The date and time you started the build"></i> <a href="#" style="font-weight:normal;">Started on</a> <div class="btn-group pull-right"> <a href="#started-on" role="button" class="btn btn-mini" data-toggle="modal"> <i class="icon-filter"></i> </a> </div> </th>
+ <th class="completed_on"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="The date and time the build finished"></i> <a href="#" class="sorted"> Completed on </a> <div class="btn-group pull-right"> <a href="#completed-on" role="button" class="btn btn-mini" data-toggle="modal"> <i class="icon-filter"></i> </a> </div> <i class="icon-caret-down"></i> </th>
+ <th class="failed_tasks"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="How many tasks failed during the build"></i> <a href="#" style="font-weight:normal;">Failed tasks</a> <div class="btn-group pull-right"> <a href="#failed-tasks" role="button" class="btn btn-mini" data-toggle="modal"> <i class="icon-filter"></i> </a> </div> <!--div id="filtered" class="btn-group pull-right" title="<p>Showing only builds with failed tasks</p><p><a class='btn btn-mini btn-primary' href='#'>Show all builds</a></p>"> <a class="btn btn-mini btn-primary"> <i class="icon-filter"></i> </a> </div--> </th>
+ <th class="errors"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="How many errors were encountered during the build (if any)"></i> <a href="#" style="font-weight:normal;">Errors</a> <div class="btn-group pull-right"> <a href="#errors" role="button" class="btn btn-mini" data-toggle="modal"> <i class="icon-filter"></i> </a> </div> </th>
+ <th class="warnings"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="How many warnigns were encountered during the build (if any)"></i> <a href="#" style="font-weight:normal;">Warnings</a> <div class="btn-group pull-right"> <a href="#warnings" role="button" class="btn btn-mini" data-toggle="modal"> <i class="icon-filter"></i> </a> </div> <!--div id="filtered" class="btn-group pull-right" title="<p>Showing only builds without warnings</p><p><a class='btn btn-mini btn-primary' href='#'>Show all builds</a></p>"> <a class="btn btn-mini btn-primary"> <i class="icon-filter"></i> </a> </div--> </th>
+ <th class="time"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="How long it took the build to finish"></i> <a href="#" style="font-weight:normal;">Time</a> </th>
+ <th class="log span4"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="The location in disk of the build main log file"></i> <a href="#" style="font-weight:normal;">Log</a> </th>
+ <th class="output"> <i class="icon-question-sign get-help" data-toggle="tooltip" title="The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory"></i> <a href="#" style="font-weight:normal;">Output</a> </th>
+
</tr>
{% for build in objects %}
<tr class="data">
- <td><a href="{% url "configuration" build.id %}">{{build.get_outcome_display}}</a></td>
- <td>{{build.started_on}}</td>
- <td>{{build.completed_on}}</td>
- <td>{% for t in build.target_set.all %}{%if t.is_image %}<a href="{% url "tpackage" build.id t.id %}">{% endif %}{{t.target}}{% if t.is_image %}</a>{% endif %}<br/>{% endfor %}</td>
- <td>{{build.machine}}</td>
- <td>{% time_difference build.started_on build.completed_on %}</td>
- <td>{{build.errors_no}}:{% if build.errors_no %}{% for error in logs %}{% if error.build == build %}{% if error.level == 2 %}<p>{{error.message}}</p>{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %}</td>
- <td>{{build.warnings_no}}:{% if build.warnings_no %}{% for warning in logs %}{% if warning.build == build %}{% if warning.level == 1 %}<p>{{warning.message}}</p>{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %}</td>
- <td>{% if build.outcome == 0 %}{% for t in build.target_set.all %}{% if t.is_image %}{{build.image_fstypes}}{% endif %}{% endfor %}{% endif %}</td>
- <td>{{build.cooker_log_path}}</td>
- <td>{{build.bitbake_version}}</td>
- <td>{{build.build_name}}</td>
+ <td class="outcome"><a href="{% url "configuration" 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 %}{%if t.is_image %}<a href="{% url "target" build.id t.id %}">{% endif %}{{t.target}}{% if t.is_image %}</a>{% endif %}<br/>{% endfor %}</td>
+ <td class="machine">{{build.machine}}</td>
+ <td class="started_on">{{build.started_on}}</td>
+ <td class="completed_on">{{build.completed_on}}</td>
+ <td class="failed_tasks"></td>
+ <td class="errors">{% if build.errors_no %}<a class="error" href="#">{{build.errors_no}} error{{build.errors_no|pluralize}}</a>{%endif%}</td>
+ <td class="warnings">{% if build.warnings_no %}<a class="warning" href="#">{{build.warnings_no}} warning{{build.warnings_no|pluralize}}</a>{%endif%}</td>
+ <td class="time">{{build|timespent}}</td>
+ <td class="log">{{build.log}}</td>
+ <td class="output">{% if build.outcome == 0 %}{% for t in build.target_set.all %}{% if t.is_image %}{{build.image_fstypes}}{% endif %}{% endfor %}{% endif %}</td>
</tr>
{% endfor %}
-{% endblock %}
+{% include "basetable_bottom.html" %}
+
+</div>
+{% endblock %}
diff --git a/lib/toaster/toastergui/templates/builddashboard.html b/lib/toaster/toastergui/templates/builddashboard.html
new file mode 100644
index 000000000..7c58cc0b2
--- /dev/null
+++ b/lib/toaster/toastergui/templates/builddashboard.html
@@ -0,0 +1,8 @@
+{% extends "basebuildpage.html" %}
+{% block localbreadcrumb %}
+<li>Dashboard</li>
+{% endblock %}
+
+{% block buildinfomain %}
+
+{% endblock %}
diff --git a/lib/toaster/toastergui/templates/buildtime.html b/lib/toaster/toastergui/templates/buildtime.html
new file mode 100644
index 000000000..ea84ae797
--- /dev/null
+++ b/lib/toaster/toastergui/templates/buildtime.html
@@ -0,0 +1,4 @@
+{% extends "basebuildpage.html" %}
+{% block localbreadcrumb %}
+<li>Build Time</li>
+{% endblock %}
diff --git a/lib/toaster/toastergui/templates/configuration.html b/lib/toaster/toastergui/templates/configuration.html
index 521620fdc..e390a95ff 100644
--- a/lib/toaster/toastergui/templates/configuration.html
+++ b/lib/toaster/toastergui/templates/configuration.html
@@ -1,7 +1,11 @@
{% extends "basebuildpage.html" %}
+{% block localbreadcrumb %}
+<li>Configuration</li>
+{% endblock %}
+
+{% block buildinfomain %}
-{% block pagetitle %}Configuration{% endblock %}
-{% block pagetable %}
+{% include "basetable_top.html" %}
<tr>
<th>Name</th>
@@ -19,4 +23,6 @@
<td>{{variable.variable_value}}</td>
{% endfor %}
+{% include "basetable_bottom.html" %}
+
{% endblock %}
diff --git a/lib/toaster/toastergui/templates/cpuusage.html b/lib/toaster/toastergui/templates/cpuusage.html
new file mode 100644
index 000000000..02f07b760
--- /dev/null
+++ b/lib/toaster/toastergui/templates/cpuusage.html
@@ -0,0 +1,4 @@
+{% extends "basebuildpage.html" %}
+{% block localbreadcrumb %}
+<li>Cpu Usage</li>
+{% endblock %}
diff --git a/lib/toaster/toastergui/templates/diskio.html b/lib/toaster/toastergui/templates/diskio.html
new file mode 100644
index 000000000..c5cef6f38
--- /dev/null
+++ b/lib/toaster/toastergui/templates/diskio.html
@@ -0,0 +1,4 @@
+{% extends "basebuildpage.html" %}
+{% block localbreadcrumb %}
+<li>Disk I/O</li>
+{% endblock %}
diff --git a/lib/toaster/toastergui/templates/recipe.html b/lib/toaster/toastergui/templates/recipe.html
index d7f57eb9e..87c69b8d3 100644
--- a/lib/toaster/toastergui/templates/recipe.html
+++ b/lib/toaster/toastergui/templates/recipe.html
@@ -1,14 +1,11 @@
-{% extends "basetable.html" %}
+{% extends "basebuildpage.html" %}
-{% block pagename %}
-<ul class="nav nav-tabs" style="display: inline-block">
- <li><a>Layer {{layer_version.layer.name}}&nbsp;:&nbsp;{{layer_version.branch}}&nbsp;:&nbsp;{{layer_version.commit}}&nbsp;:&nbsp;{{layer_version.priority}}</a></li>
-</ul>
- <h1>Toaster - Recipes for a Layer</h1>
+{% block localbreadcrumb %}
+<li>Recipes</li>
{% endblock %}
-{% block pagetable %}
- {% load projecttags %}
+{% block buildinfomain %}
+{% include "basetable_top.html" %}
<tr>
</tr>
@@ -49,4 +46,6 @@
{% endfor %}
+{% include "basetable_bottom.html" %}
+
{% endblock %}
diff --git a/lib/toaster/toastergui/templates/target.html b/lib/toaster/toastergui/templates/target.html
new file mode 100644
index 000000000..f2d0ad461
--- /dev/null
+++ b/lib/toaster/toastergui/templates/target.html
@@ -0,0 +1,8 @@
+{% extends "basebuildpage.html" %}
+
+{% block localbreadcrumb %}
+<li>Target</li>
+{% endblock %}
+
+{% block buildinfomain %}
+{% endblock %}
diff --git a/lib/toaster/toastergui/templates/task.html b/lib/toaster/toastergui/templates/task.html
index de965ab79..6af2c5127 100644
--- a/lib/toaster/toastergui/templates/task.html
+++ b/lib/toaster/toastergui/templates/task.html
@@ -1,7 +1,13 @@
{% extends "basebuildpage.html" %}
-{% block pagetitle %}Tasks{% endblock %}
-{% block pagetable %}
+{% block localbreadcrumb %}
+<li>Tasks</li>
+{% endblock %}
+
+{% block buildinfomain %}
+{% include "basetable_top.html" %}
+
+
{% if not objects %}
<p>No tasks were executed in this build!</p>
{% else %}
@@ -64,4 +70,5 @@
{% endif %}
+{% include "basetable_bottom.html" %}
{% endblock %}
diff --git a/lib/toaster/toastergui/templatetags/projecttags.py b/lib/toaster/toastergui/templatetags/projecttags.py
index 0c0d804c0..5f6037993 100644
--- a/lib/toaster/toastergui/templatetags/projecttags.py
+++ b/lib/toaster/toastergui/templatetags/projecttags.py
@@ -24,3 +24,8 @@ register = template.Library()
@register.simple_tag
def time_difference(start_time, end_time):
return end_time - start_time
+
+@register.filter(name = 'timespent')
+def timespent(build_object):
+ tdsec = (build_object.completed_on - build_object.started_on).total_seconds()
+ return "%02d:%02d:%02d" % (int(tdsec/3600), int((tdsec - tdsec/ 3600)/ 60), int(tdsec) % 60)
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index b84c95f08..f531eb013 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -19,14 +19,34 @@
from django.conf.urls import patterns, include, url
from django.views.generic import RedirectView
-urlpatterns = patterns('bldviewer.views',
- url(r'^builds/$', 'build', name='all-builds'),
- url(r'^build/(?P<build_id>\d+)/task/$', 'task', name='task'),
- url(r'^build/(?P<build_id>\d+)/packages/$', 'bpackage', name='bpackage'),
- url(r'^build/(?P<build_id>\d+)/package/(?P<package_id>\d+)/files/$', 'bfile', name='bfile'),
- url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/packages/$', 'tpackage', name='tpackage'),
- url(r'^build/(?P<build_id>\d+)/configuration/$', 'configuration', name='configuration'),
+urlpatterns = patterns('toastergui.views',
+ # landing page
+ url(r'^builds/$', 'builds', name='all-builds'),
+ # build info navigation
+ url(r'^build/(?P<build_id>\d+)$', 'builddashboard', name="builddashboard"),
+
+ url(r'^build/(?P<build_id>\d+)/tasks/$', 'tasks', name='tasks'),
+ url(r'^build/(?P<build_id>\d+)/task/(?P<task_id>\d+)$', 'task', name='task'),
+
+ url(r'^build/(?P<build_id>\d+)/recipes/$', 'recipes', name='recipes'),
+ url(r'^build/(?P<build_id>\d+)/recipe/(?P<recipe_id>\d+)$', 'recipe', name='recipe'),
+
+ url(r'^build/(?P<build_id>\d+)/packages/$', 'bpackage', name='packages'),
+ url(r'^build/(?P<build_id>\d+)/package/(?P<package_id>\d+)$', 'bfile', name='package'),
+
+ # images are known as targets in the internal model
+ url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)$', 'target', name='target'),
+ url(r'^build/(?P<build_id>\d+)/target/(?P<target_id>\d+)/packages$', 'tpackage', name='targetpackages'),
+
+ url(r'^build/(?P<build_id>\d+)/configuration$', 'configuration', name='configuration'),
+ url(r'^build/(?P<build_id>\d+)/buildtime$', 'buildtime', name='buildtime'),
+ url(r'^build/(?P<build_id>\d+)/cpuusage$', 'cpuusage', name='cpuusage'),
+ url(r'^build/(?P<build_id>\d+)/diskio$', 'diskio', name='diskio'),
+
+
+ # urls not linked from the dashboard
url(r'^layers/$', 'layer', name='all-layers'),
url(r'^layerversions/(?P<layerversion_id>\d+)/recipes/.*$', 'layer_versions_recipes', name='layer_versions_recipes'),
+ # default redirection
url(r'^$', RedirectView.as_view( url= 'builds/')),
)
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 7cb9b4237..663e03dfd 100644
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -19,7 +19,7 @@
import operator
from django.db.models import Q
-from django.shortcuts import render
+from django.shortcuts import render, redirect
from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable
from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File, Package_Dependency
from orm.models import Target_Installed_Package
@@ -35,6 +35,7 @@ def _build_page_range(paginator, index = 1):
except EmptyPage:
page = paginator.page(paginator.num_pages)
+
page.page_range = [page.number]
crt_range = 0
for i in range(1,5):
@@ -48,22 +49,124 @@ def _build_page_range(paginator, index = 1):
break
return page
-@cache_control(no_store=True)
-def build(request):
- template = 'build.html'
- logs = LogMessage.objects.all()
- build_info = _build_page_range(Paginator(Build.objects.order_by("-id"), 10),request.GET.get('page', 1))
+def _verify_parameters(g, mandatory_parameters):
+ miss = []
+ for mp in mandatory_parameters:
+ if not mp in g:
+ miss.append(mp)
+ if len(miss):
+ return miss
+ return None
+
+def _redirect_parameters(view, g, mandatory_parameters):
+ import urllib
+ from django.core.urlresolvers import reverse
+ url = reverse(view)
+ params = {}
+ for i in g:
+ params[i] = g[i]
+ for i in mandatory_parameters:
+ if not i in params:
+ params[i] = mandatory_parameters[i]
+
+ return redirect(url + "?%s" % urllib.urlencode(params))
+
- context = {'objects': build_info, 'logs': logs ,
- 'hideshowcols' : [
- {'name': 'Output', 'order':10},
- {'name': 'Log', 'order':11},
+# shows the "all builds" page
+def builds(request):
+ template = 'build.html'
+ # define here what parameters the view needs in the GET portion in order to
+ # be able to display something. 'count' and 'page' are mandatory for all views
+ # that use paginators.
+ mandatory_parameters = { 'count': 10, 'page' : 1};
+ retval = _verify_parameters( request.GET, mandatory_parameters )
+ if retval:
+ return _redirect_parameters( builds, request.GET, mandatory_parameters)
+
+ # retrieve the objects that will be displayed in the table
+ build_info = _build_page_range(Paginator(Build.objects.exclude(outcome = Build.IN_PROGRESS).order_by("-id"), request.GET.get('count', 10)),request.GET.get('page', 1))
+
+ # build view-specific information; this is rendered specifically in the builds page
+ build_mru = Build.objects.order_by("-started_on")[:3]
+ for b in [ x for x in build_mru if x.outcome == Build.IN_PROGRESS ]:
+ tf = Task.objects.filter(build = b)
+ b.completeper = tf.exclude(order__isnull=True).count()*100/tf.count()
+ from django.utils import timezone
+ b.eta = timezone.now() + ((timezone.now() - b.started_on)*100/b.completeper)
+
+ # send the data to the template
+ context = {
+ # specific info for
+ 'mru' : build_mru,
+ # TODO: common objects for all table views, adapt as needed
+ 'objects' : build_info,
+ 'tablecols' : [
+ {'name': 'Target ', 'clclass': 'target',},
+ {'name': 'Machine ', 'clclass': 'machine'},
+ {'name': 'Completed on ', 'clclass': 'completed_on'},
+ {'name': 'Failed tasks ', 'clclass': 'failed_tasks'},
+ {'name': 'Errors ', 'clclass': 'errors_no'},
+ {'name': 'Warnings', 'clclass': 'warnings_no'},
+ {'name': 'Output ', 'clclass': 'output'},
+ {'name': 'Started on ', 'clclass': 'started_on', 'hidden' : 1},
+ {'name': 'Time ', 'clclass': 'time', 'hidden' : 1},
+ {'name': 'Output', 'clclass': 'output'},
+ {'name': 'Log', 'clclass': 'log', 'hidden': 1},
]}
return render(request, template, context)
+# build dashboard for a single build, coming in as argument
+def builddashboard(request, build_id):
+ template = "builddashboard.html"
+ if Build.objects.filter(pk=build_id).count() == 0 :
+ return redirect(builds)
+ context = {
+ 'build' : Build.objects.filter(pk=build_id)[0],
+ }
+ return render(request, template, context)
+
+def task(request, build_id, task_id):
+ template = "singletask.html"
+ if Build.objects.filter(pk=build_id).count() == 0 :
+ return redirect(builds)
+ context = {
+ 'build' : Build.objects.filter(pk=build_id)[0],
+ }
+ return render(request, template, context)
+
+def recipe(request, build_id, recipe_id):
+ template = "recipe.html"
+ if Recipe.objects.filter(pk=recipe_id).count() == 0 :
+ return redirect(builds)
+ context = {
+ 'build' : Build.objects.filter(pk=build_id)[0],
+ 'object' : Recipe.objects.filter(pk=recipe_id)[0],
+ }
+ return render(request, template, context)
+
+def package(request, build_id, package_id):
+ template = "singlepackage.html"
+ if Build.objects.filter(pk=build_id).count() == 0 :
+ return redirect(builds)
+ context = {
+ 'build' : Build.objects.filter(pk=build_id)[0],
+ }
+ return render(request, template, context)
+
+def target(request, build_id, target_id):
+ template = "target.html"
+ if Build.objects.filter(pk=build_id).count() == 0 :
+ return redirect(builds)
+ context = {
+ 'build' : Build.objects.filter(pk=build_id)[0],
+ }
+ return render(request, template, context)
+
+
+
def _find_task_revdep(task):
tp = []
for p in Task_Dependency.objects.filter(depends_on=task):
@@ -81,7 +184,7 @@ def _find_task_provider(task):
return trc
return None
-def task(request, build_id):
+def tasks(request, build_id):
template = 'task.html'
tasks = _build_page_range(Paginator(Task.objects.filter(build=build_id), 100),request.GET.get('page', 1))
@@ -94,12 +197,52 @@ def task(request, build_id):
return render(request, template, context)
+def recipes(request, build_id):
+ template = 'recipe.html'
+
+ recipes = _build_page_range(Paginator(Recipe.objects.filter(build_recipe=build_id), 100),request.GET.get('page', 1))
+
+ context = {'build': Build.objects.filter(pk=build_id)[0], 'objects': recipes}
+
+ return render(request, template, context)
+
+
def configuration(request, build_id):
template = 'configuration.html'
variables = _build_page_range(Paginator(Variable.objects.filter(build=build_id), 50), request.GET.get('page', 1))
context = {'build': Build.objects.filter(pk=build_id)[0], 'objects' : variables}
return render(request, template, context)
+def buildtime(request, build_id):
+ template = "buildtime.html"
+ if Build.objects.filter(pk=build_id).count() == 0 :
+ return redirect(builds)
+ context = {
+ 'build' : Build.objects.filter(pk=build_id)[0],
+ }
+ return render(request, template, context)
+
+def cpuusage(request, build_id):
+ template = "cpuusage.html"
+ if Build.objects.filter(pk=build_id).count() == 0 :
+ return redirect(builds)
+ context = {
+ 'build' : Build.objects.filter(pk=build_id)[0],
+ }
+ return render(request, template, context)
+
+def diskio(request, build_id):
+ template = "diskio.html"
+ if Build.objects.filter(pk=build_id).count() == 0 :
+ return redirect(builds)
+ context = {
+ 'build' : Build.objects.filter(pk=build_id)[0],
+ }
+ return render(request, template, context)
+
+
+
+
def bpackage(request, build_id):
template = 'bpackage.html'
packages = Package.objects.filter(build = build_id)
@@ -227,8 +370,8 @@ def model_explorer(request, model_name):
response_data['count'] = queryset.count()
else:
response_data['count'] = 0
-
response_data['list'] = serializers.serialize('json', queryset)
+# response_data = serializers.serialize('json', queryset)
return HttpResponse(json.dumps(response_data),
content_type='application/json')
diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index b76218b95..679035e88 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -133,6 +133,15 @@ TEMPLATE_DIRS = (
# Don't forget to use absolute paths, not relative paths.
)
+TEMPLATE_CONTEXT_PROCESSORS = ('django.contrib.auth.context_processors.auth',
+ 'django.core.context_processors.debug',
+ 'django.core.context_processors.i18n',
+ 'django.core.context_processors.media',
+ 'django.core.context_processors.static',
+ 'django.core.context_processors.tz',
+ 'django.contrib.messages.context_processors.messages',
+ "django.core.context_processors.request")
+
INSTALLED_APPS = (
#'django.contrib.auth',
#'django.contrib.contenttypes',
@@ -144,6 +153,7 @@ INSTALLED_APPS = (
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
+ 'django.contrib.humanize',
'orm',
'toastermain',
'toastergui',