diff options
Diffstat (limited to 'lib')
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:" "}} 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. </span> - <ul class="pagination" style="display: block-inline"> -{%if objects.has_previous %} - <li><a href="?page={{objects.previous_page_number}}">«</a></li> -{%else%} - <li class="disabled"><a href="#">«</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}}">»</a></li> -{%else%} - <li class="disabled"><a href="#">»</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}}">«</a></li> +{%else%} + <li class="disabled"><a href="#">«</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}}">»</a></li> +{%else%} + <li class="disabled"><a href="#">»</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}} : {{layer_version.branch}} : {{layer_version.commit}} : {{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', |