Files
hydra/src/root/common.tt
Pierre Bourdon b4322edd05 web: replace 'errormsg' with 'errormsg IS NULL' in most cases
This is implement in an extremely hacky way due to poor DBIx feature
support. Ideally, what we'd need is a way to tell DBIx to ignore the
errormsg column unless explicitly requested, and to automatically add a
computed 'errormsg IS NULL' column in others. Since it does not support
that, this commit instead hacks some support via method overrides while
taking care to not break anything obvious.
2025-04-07 14:48:07 -04:00

708 lines
26 KiB
Plaintext

[%
USE date;
USE String;
USE HTML;
USE Math;
USE mibs=format("%.2f");
# Formatted date time, in hydra-local timezone.
# Use only where an HTML element cannot be used.
BLOCK dateTimeText;
date.format(timestamp, '%Y-%m-%d %H:%M:%S');
END;
# HTML-rendered date. Formatted in hydra-local timezone.
# It is enhanced with JavaScript to show user-local and UTC time zones.
BLOCK renderDateTime %]
<time [% HTML.attributes(
"data-timestamp" => timestamp,
title => date.format(timestamp, '%Y-%m-%d %H:%M:%S (%Z)'),
datetime => date.format(timestamp, "%Y-%m-%dT%H:%M:%SZ", gmt => 1),
) %] class="date is-absolute">
[% INCLUDE dateTimeText %]
</time>
[% END;
# Relative date, as text.
# Use only where an HTML element cannot be used.
BLOCK relativeDateText;
ago = date.now - timestamp;
IF ago >= 0 && ago < 60; THEN;
ago _ 's ago';
ELSIF ago >= 0 && ago < 60 * 60; THEN;
Math.int(ago / 60) _ 'm ago';
ELSIF ago >= 0 && ago < 24 * 60 * 60; THEN;
Math.int(ago / (60 * 60)) _ 'h ago';
ELSIF ago >= 0 && ago < 7 * 24 * 60 * 60; THEN;
Math.int(ago / (24 * 60 * 60)) _ 'd ago';
ELSE;
date.format(timestamp, '%Y-%m-%d');
END;
END;
# HTML-rendered relative date.
# It is enhanced with JavaScript to show user-local and UTC time zones.
BLOCK renderRelativeDate %]
<time [% HTML.attributes(
"data-timestamp" => timestamp,
title => date.format(timestamp, '%Y-%m-%d %H:%M:%S (%Z)'),
datetime => date.format(timestamp, "%Y-%m-%dT%H:%M:%SZ", gmt => 1),
) %] class="date is-relative">
[% INCLUDE relativeDateText %]
</time>
[% END;
BLOCK renderProjectName %]
<a [% IF inRow %]class="row-link"[% END %] href="[% c.uri_for('/project' project) %]"><tt>[% project %]</tt></a>
[% END;
BLOCK renderJobsetName %]
<a [% IF inRow %]class="row-link"[% END %] href="[% c.uri_for('/jobset' project jobset) %]"><tt>[% jobset %]</tt></a>
[% END;
BLOCK renderJobName %]
<a [% IF inRow %]class="row-link"[% END %] href="[% c.uri_for('/job' project jobset job) %]">[% job %]</a>
[% END;
BLOCK renderFullJobsetName %]
<tt>[% INCLUDE renderProjectName inRow=0 %]:[% INCLUDE renderJobsetName %]</tt>
[% END;
BLOCK renderFullJobName %]
<tt>[% IF !hideProjectName; INCLUDE renderProjectName inRow=0 %]:[% END; IF !hideJobsetName; INCLUDE renderJobsetName inRow=0 %]:[% END; INCLUDE renderJobName %]</tt>
[% END;
BLOCK renderFullJobNameOfBuild;
INCLUDE renderFullJobName project=build.jobset.get_column("project") jobset = build.jobset.get_column("name") job = build.get_column("job");
END;
BLOCK renderDuration;
IF duration >= 24 * 60 * 60; duration div (24 * 60 * 60) %]d&nbsp;[% END;
IF duration >= 60 * 60; duration div (60 * 60) % 24 %]h&nbsp;[% END;
IF duration >= 60; duration div 60 % 60 %]m&nbsp;[% END;
duration % 60 %]s[%
END;
BLOCK renderDrvInfo;
drvname = step.drvpath
.substr(11) # strip `/nix/store/`
.split('-').slice(1).join("-") # strip hash part
.substr(0, -4); # strip `.drv`
IF drvname != releasename;
IF step.type == 0; action = "Build"; ELSE; action = "Substitution"; END;
IF drvname; %]<em> ([% action %] of [% drvname %])</em>[% END;
END;
END;
BLOCK renderBuildListHeader %]
<table class="table table-striped table-condensed clickable-rows">
<thead>
<tr>
[% IF !hideResultInfo %]
<th></th>
[% END %]
[% IF showSchedulingInfo %]
<th></th>
[% END %]
<th>#</th>
[% IF !hideJobName %]
<th>Job</th>
[% END %]
<th>[% IF showSchedulingInfo %]Queued at[% ELSE %]Finished at[% END %]</th>
<th>Package/release name</th>
<th>System</th>
[% IF showDescription %]
<th>Description</th>
[% END %]
</tr>
</thead>
<tbody>
[% END;
BLOCK renderBuildListBody;
FOREACH build IN builds; link = c.uri_for('/build' build.id) %]
<tr>
[% IF !hideResultInfo %]
<td>
[% INCLUDE renderBuildStatusIcon size=16 buildstatus=build.buildstatus %]
</td>
[% END %]
[% IF showSchedulingInfo %]
<td>[% IF busy %]<span class="badge badge-success">Started</span>[% ELSE %]<span class="badge badge-secondary">Queued</span>[% END %]</td>
[% END %]
<td><a class="row-link" href="[% link %]">[% build.id %]</a></td>
[% IF !hideJobName %]
<td>
<a href="[%link%]">[% IF !hideJobsetName %][%build.jobset.get_column("project")%]:[%build.jobset.get_column("name")%]:[% END %][%build.get_column("job")%]</a>
[% IF showStepName %]
[% INCLUDE renderDrvInfo step=build.buildsteps releasename=build.nixname %]
[% END %]
</td>
[% END %]
<td class="nowrap">[% t = showSchedulingInfo ? build.timestamp : build.stoptime; IF t; INCLUDE renderRelativeDate timestamp=(showSchedulingInfo ? build.timestamp : build.stoptime); ELSE; "-"; END %]</td>
<td>[% !showSchedulingInfo and build.get_column('releasename') ? build.get_column('releasename') : build.nixname %]</td>
<td class="nowrap"><tt>[% build.system %]</tt></td>
[% IF showDescription %]
<td>[% build.description %]</td>
[% END %]
</tr>
[% END;
IF linkToAll %]
<tr><td class="centered" colspan="5"><a href="[% linkToAll %]"><em>More...</em></a></td></tr>
[% END;
END;
BLOCK renderBuildListFooter %]
</tbody>
</table>
[% END;
BLOCK renderBuildList;
INCLUDE renderBuildListHeader;
INCLUDE renderBuildListBody;
INCLUDE renderBuildListFooter;
END;
BLOCK renderLink %]<a href="[% uri %]">[% title %]</a>[% END;
BLOCK maybeLink;
IF uri %]<a [% HTML.attributes(href => uri, class => class); IF confirmmsg +%] onclick="javascript:return confirm('[% confirmmsg %]')"[% END %]>[% content %]</a>[% ELSE; content; END;
END;
BLOCK renderSelection;
IF edit;
IF radiobuttons; %]
<div class="controls">
[% FOREACH name IN options.keys.sort %]
<label class="radio inline">
<input type="radio" [% HTML.attributes(id => param, name => param, value => name) %]
[% IF name == curValue; "checked='1'"; END %]>
[% options.$name %]
</input>
</label>
[% END %]
</div>
[% ELSE %]
<select class="custom-select" [% HTML.attributes(id => param, name => param) %]>
[% FOREACH name IN options.keys.sort %]
<option [% IF name == curValue; "selected='selected'"; END; " "; HTML.attributes(value => name) %]>[% options.$name %]</option>
[% END %]
</select>
[% END;
ELSE;
options.$curValue;
END;
END;
BLOCK editString; %]
<input type="text" class="string" [% HTML.attributes(id => param, name => param, value => value) %] />
[% END;
BLOCK renderFullBuildLink;
INCLUDE renderFullJobNameOfBuild build=build %] <a href="[% c.uri_for('/build' build.id) %]">build [% build.id %]</a>[%
END;
BLOCK renderBuildIdLink; %]
<a href="[% c.uri_for('/build' id) %]">build [% id %]</a>
[% END;
BLOCK renderBuildLink; INCLUDE renderBuildIdLink id=build.id; END;
BLOCK renderBuildStatusIcon;
finished = build != undef ? build.finished : 1;
buildstatus = buildstatus != undef ? buildstatus : build.buildstatus;
IF finished;
IF buildstatus == 0 %]
<img src="[% c.uri_for("/static/images/emojione-check-2714.svg") %]" height="[% size %]" width="[% size %]" title="Succeeded" alt="Succeeded" class="build-status" />
[% ELSIF buildstatus == 1 %]
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="[% size %]" width="[% size %]" title="Failed" alt="Failed" class="build-status" />
[% ELSIF buildstatus == 2 %]
<img src="[% c.uri_for("/static/images/emojione-gray-x-2716.svg") %]" height="[% size %]" width="[% size %]" title="Dependency failed" alt="Dependency failed" class="build-status" />
[% ELSIF buildstatus == 3 || buildstatus == 9 %]
<img src="[% c.uri_for("/static/images/emojione-stopsign-1f6d1.svg") %]" height="[% size %]" width="[% size %]" title="Aborted" alt="Aborted" class="build-status" />
[% ELSIF buildstatus == 4 %]
<img src="[% c.uri_for("/static/images/emojione-stopsign-1f6d1.svg") %]" height="[% size %]" width="[% size %]" title="Cancelled" alt="Cancelled" class="build-status" />
[% ELSIF buildstatus == 6 %]
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="[% size %]" width="[% size %]" title="Failed with output" alt="Failed with output" class="build-status" />
[% ELSIF buildstatus == 7 %]
<img src="[% c.uri_for("/static/images/emojione-stopsign-1f6d1.svg") %]" height="[% size %]" width="[% size %]" title="Timed out" alt="Timed out" class="build-status" />
[% ELSIF buildstatus == 10 %]
<img src="[% c.uri_for("/static/images/emojione-stopsign-1f6d1.svg") %]" height="[% size %]" width="[% size %]" title="Log limit exceeded" alt="Log limit exceeded" class="build-status" />
[% ELSIF buildstatus == 11 %]
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="[% size %]" width="[% size %]" title="Output size limit exceeded" alt="Output size limit exceeded" class="build-status" />
[% ELSIF buildstatus == 12 %]
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="[% size %]" width="[% size %]" title="Non-deterministic build" alt="Non-deterministic build" class="build-status" />
[% ELSE %]
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="[% size %]" width="[% size %]" title="Failed" alt="Failed" class="build-status" />
[% END;
ELSE %]
<img src="[% c.uri_for("/static/images/emojione-question-2754.svg") %]" height="[% size %]" width="[% size %]" title="Queued" alt="Queued" class="build-status" />
[% END;
END;
BLOCK renderBusyStatus;
IF step.busy == 1 %]
<strong>Preparing</strong>
[% ELSIF step.busy == 10 %]
<strong>Connecting</strong>
[% ELSIF step.busy == 20 %]
<strong>Sending inputs</strong>
[% ELSIF step.busy == 30 %]
<strong>Building</strong>
[% ELSIF step.busy == 35 %]
<strong>Waiting to receive outputs</strong>
[% ELSIF step.busy == 40 %]
<strong>Receiving outputs</strong>
[% ELSIF step.busy == 50 %]
<strong>Post-processing</strong>
[% ELSE %]
<strong>Unknown state</strong>
[% END;
END;
BLOCK renderStatus;
IF build.finished;
buildstatus = build.buildstatus;
IF icon; INCLUDE renderBuildStatusIcon size=16; END;
IF buildstatus == 0 %]
<strong>Success</strong>
[% ELSIF buildstatus == 1 %]
<span class="error">Build returned a non-zero exit code</span>
[% ELSIF buildstatus == 2 %]
<span class="error">A dependency of the build failed</span>
[% ELSIF buildstatus == 4 %]
<span class="error">Cancelled by user</span>
[% ELSIF buildstatus == 6 %]
<span class="error">Build failed with output</span>
[% ELSIF buildstatus == 7 %]
<span class="error">Timed out</span>
[% ELSIF buildstatus == 9 %]
<span class="error">Unsupported system type</span>
[% ELSIF buildstatus == 10 %]
<span class="error">Log limit exceeded</span>
[% ELSIF buildstatus == 11 %]
<span class="error">Output limit exceeded</span>
[% ELSIF buildstatus == 12 %]
<span class="error">Non-deterministic build</span>
[% ELSE %]
<span class="error">Aborted</span>
(Hydra failure; see <a href="#nix-error">below</a>)
[% END;
ELSIF busy %]
<strong>Build in progress</strong>[%-
ELSE %]
<strong>Scheduled to be built</strong>
[% END;
END;
BLOCK renderShortInputValue;
IF input.type == "build" || input.type == "sysbuild" %]
<a href="[% c.uri_for('/build' input.dependency.id) %]">[% input.dependency.id %]</a>
[% ELSIF input.type == "string" %]
<tt>"[% HTML.escape(input.value) %]"</tt>
[% ELSIF input.type == "nix" || input.type == "boolean" %]
<tt>[% HTML.escape(input.value) %]</tt>
[% ELSE %]
<tt>[% HTML.escape(input.uri) %][% IF input.revision %] (r[% HTML.escape(input.revision) %])[% END %]</tt>
[% END %]
[% END;
BLOCK renderDiffUri;
nouri = 1;
FOREACH m IN mappers;
base = m.baseuri;
url = bi1.uri;
path = url.replace(base, '');
IF url.match(base) %]
<a target="_blank" href="[% m.uri.replace('_path_', path).replace('_1_', bi1.revision).replace('_2_', bi2.revision) %]">[% contents %]</a>
[% nouri = 0;
END;
END;
IF nouri;
res = bi1.uri.split(' ');
url = res.0;
branch = res.1;
IF bi1.type == "hg" || bi1.type == "git" %]
<a target="_blank" href="[% HTML.escape(c.uri_for('/api/scmdiff', {
uri = url,
rev1 = bi1.revision,
rev2 = bi2.revision,
type = bi1.type,
branch = branch
})) %]">[% contents %]</a>
[% ELSE;
contents;
END;
END;
END;
BLOCK renderInputs; %]
<table class="table table-striped table-condensed">
<thead>
<tr><th>Name</th><th>Type</th><th>Value</th><th>Revision</th><th>Store path</th></tr>
</thead>
<tbody>
[% FOREACH input IN inputs %]
<tr>
<td><tt>[% input.name %]</tt></td>
<td>[% type = input.type; inputTypes.$type %]</td>
<td>
[% IF input.type == "build" || input.type == "sysbuild" %]
[% INCLUDE renderFullBuildLink build=input.dependency %]
[% ELSIF input.type == "string" %]
<tt>"[% HTML.escape(input.value) %]"</tt>
[% ELSIF input.type == "nix" || input.type == "boolean" %]
<tt>[% HTML.escape(input.value) %]</tt>
[% ELSE %]
<tt>[% HTML.escape(input.uri) %]</tt>
[% END %]
</td>
<td>[% IF input.revision %][% HTML.escape(input.revision) %][% END %]</td>
<td><tt>[% input.path %]</tt></td>
</tr>
[% END %]
</tbody>
</table>
[% END;
BLOCK renderInputDiff; %]
<table class="table table-striped table-condensed">
[% IF !nestedDiff %]
<tr><th>Input</th><th>Changes</th></tr>
[% END;
IF !nestLevel;
nestLevel = 0;
END;
IF nestLevel <= 3;
FOREACH bi1 IN inputs1;
deletedInput = 1;
FOREACH bi2 IN inputs2;
IF bi1.name == bi2.name;
IF bi1.type == bi2.type;
IF bi1.value != bi2.value || bi1.uri != bi2.uri %]
<tr><td><b>[% bi1.name %]</b></td><td><tt>[% INCLUDE renderShortInputValue input=bi1 %]</tt> to <tt>[% INCLUDE renderShortInputValue input=bi2 %]</tt></td></tr>
[% ELSIF bi1.uri == bi2.uri && bi1.revision != bi2.revision %]
[% IF bi1.type == "git" %]
<tr><td>
<b>[% bi1.name %]</b></td><td><tt>[% INCLUDE renderDiffUri contents=(bi1.revision.substr(0, 12) _ ' to ' _ bi2.revision.substr(0, 12)) %]</tt>
</td></tr>
[% ELSE %]
<tr><td>
<b>[% bi1.name %]</b></td><td><tt>[% INCLUDE renderDiffUri contents=(bi1.revision _ ' to ' _ bi2.revision) %]</tt>
</td></tr>
[% END %]
[% ELSIF bi1.dependency.id != bi2.dependency.id || bi1.path != bi2.path %]
<tr><td>
<b>[% bi1.name %]</b></td><td><tt>[% INCLUDE renderShortInputValue input=bi1 %]</tt> to <tt>[% INCLUDE renderShortInputValue input=bi2 %]</tt>
<br/>
<br/>
[% INCLUDE renderInputDiff inputs1=bi1.dependency.inputs inputs2=bi2.dependency.inputs nestedDiff=1 nestLevel=nestLevel+1 %]
</td></tr>
[% END %]
[% ELSE %]
<tr><td><b>[% bi1.name %]</b></td><td>Changed input type from '[% type = bi1.type; inputTypes.$type %]' to '[% type = bi2.type; inputTypes.$type %]'</td></tr>
[% END;
deletedInput = 0;
END;
END;
IF deletedInput == 1 %]
<tr><td><b>[% bi1.name %]</b></td><td>Input not present in this build.</td></tr>
[% END;
END;
END %]
</table>
[% END;
BLOCK renderPager %]
<ul class="pagination">
<li class="page-item[% IF page == 1 %] disabled[% END %]"><a class="page-link" href="[% "$baseUri?page=1" %]">&laquo; First</a></li>
<li class="page-item[% IF page == 1 %] disabled[% END %]"><a class="page-link" href="[% "$baseUri?page="; (page - 1) %]">&lsaquo; Previous</a></li>
<li class="page-item[% IF page * resultsPerPage >= total %] disabled[% END %]"><a class="page-link" href="[% "$baseUri?page="; (page + 1) %]">Next &rsaquo;</a></li>
<li class="page-item[% IF page * resultsPerPage >= total %] disabled[% END %]"><a class="page-link" href="[% "$baseUri?page="; (total - 1) div resultsPerPage + 1 %]">Last &raquo;</a></li>
</ul>
[% END;
BLOCK renderShortEvalInput;
IF input.type == "svn" || input.type == "svn-checkout" || input.type == "bzr" || input.type == "bzr-checkout" %]
r[% input.revision %]
[% ELSIF input.type == "git" %]
<tt>[% input.revision.substr(0, 7) %]</tt>
[% ELSIF input.type == "hg" %]
<tt>[% input.revision.substr(0, 12) %]</tt>
[% ELSIF input.type == "build" || input.type == "sysbuild" %]
<a href="[% c.uri_for('/build' input.get_column('dependency')) %]">[% input.get_column('dependency') %]</a>
[% ELSE %]
<tt>[% input.revision %]</tt>
[% END;
END;
BLOCK renderEvals %]
<table class="table table-condensed table-striped clickable-rows">
<thead>
<tr>
<th rowspan="2">#</th>
[% IF !jobset && !build %]
<th rowspan="2">Jobset</th>
[% END %]
<th rowspan="2" style="width: 10em">Date</th>
<th rowspan="2">Input changes</th>
<th colspan="4">Jobs</th>
</tr>
<tr>
<th style="width: 2em">
<img src="[% c.uri_for("/static/images/emojione-check-2714.svg") %]" height="12" width="12" title="Succeeded" alt="Succeeded" class="build-status" />
</th>
<th style="width: 2em">
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="12" width="12" title="Failed" alt="Failed" class="build-status" />
</th>
<th style="width: 2em">
<img src="[% c.uri_for("/static/images/emojione-question-2754.svg") %]" height="12" width="12" title="Queued" alt="Queued" class="build-status" />
</th>
<th style="width: 2em">
<img src="[% c.uri_for("/static/images/delta.svg") %]" height="12" width="12" title="Delta" alt="Delta" class="build-status" />
</th>
</tr>
</thead>
<tbody>
[% FOREACH e IN evals;
eval = e.eval;
link = c.uri_for(c.controller('JobsetEval').action_for('view'), [eval.id]) %]
<tr>
<td><a class="row-link" href="[% link %]">[% eval.id %]</a></td>
[% IF !jobset && !build %]
<td>[% INCLUDE renderFullJobsetName project=eval.jobset.project.name jobset=eval.jobset.name %]</td>
[% END %]
<td class="nowrap">[% INCLUDE renderRelativeDate timestamp = eval.timestamp %]</td>
<td>
[% IF e.changedInputs.size > 0;
sep='';
FOREACH input IN e.changedInputs;
sep; %] [% input.name %] → [% INCLUDE renderShortEvalInput input=input;
sep=', ';
END;
ELSE %]
-
[% END %]
[% IF eval.evaluationerror.has_error %]
<span class="badge badge-warning">Eval Errors</span>
[% END %]
</td>
<td align='right' class="nowrap">
<span class="badge badge-success">[% e.nrSucceeded %]</span>
</td>
<td align="right" class="nowrap">
[% IF e.nrFailed > 0 %]
<span class="badge badge-danger">[% e.nrFailed %]</span>
[% END %]
</td>
<td align="right" class="nowrap">
[% IF e.nrScheduled > 0 %]
<span class="badge badge-secondary">[% e.nrScheduled %]</span>
[% END %]
</td>
<td align='right' class="nowrap">
[% IF e.diff > 0 %]
<span class='badge badge-success'><strong>+[% e.diff %]</strong></span>
[% ELSIF e.diff < 0 && e.nrScheduled == 0 %]
<span class='badge badge-danger'><strong>[% e.diff %]</strong></span>
[% END %]
</td>
</tr>
[% END;
IF linkToAll %]
<tr><td class="centered" colspan="7"><a href="[% linkToAll %]"><em>More...</em></a></td></tr>
[% END %]
</tbody>
</table>
[% END;
BLOCK renderLogLinks %]
(<a [% IF inRow %]class="row-link"[% END %] href="[% url %]">log</a>, <a href="[% "$url/raw" %]">raw</a>, <a href="[% "$url/tail" %]">tail</a>)
[% END;
BLOCK makeLazyTab %]
<div id="[% tabName %]" class="tab-pane">
<center><span class="spinner-border spinner-border-sm"/></center>
</div>
<script>
[% IF callback.defined %]
$(function() { makeLazyTab("[% tabName %]", "[% uri %]", [% callback %] ); });
[% ELSE %]
$(function() { makeLazyTab("[% tabName %]", "[% uri %]", null ); });
[% END %]
</script>
[% END;
BLOCK makePopover %]
<button type="button" class="btn hydra-popover [% classes %]" data-toggle="popover" data-html="true" [% HTML.attributes('data-content' => content, 'data-placement' => placement || 'bottom') %]>
[% title %]
</button>
[% END;
BLOCK menuItem %]
<a class="dropdown-item[% IF "${root}${curUri}" == uri %] active[% END %]"
[% HTML.attributes(href => uri) %] [%+ IF modal %]data-toggle="modal"[% END %]
[% IF confirmmsg %]onclick="javascript:return confirm('[% confirmmsg %]')"[% END %]>
[% IF icon %]<i class="[% icon %] icon-black"></i> [%+ END %]
[% title %]
</a>
[% END;
BLOCK navItem %]
<li class="nav-item">
<a class="nav-link[% IF "${root}${curUri}" == uri %] active[% END %]"
[% HTML.attributes(href => uri) %]>
[% title %]
</a>
</li>
[% END;
BLOCK makeStar %]
<span class="star" data-post="[% starUri %]">[% IF starred; "★"; ELSE; "☆"; END %]</span>
[% END;
BLOCK renderJobsetOverview %]
<table class="table table-striped table-condensed clickable-rows">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Description</th>
<th>Last evaluated</th>
<th style="width: 2em">
Jobs
</th>
<th style="width: 2em">
<img src="[% c.uri_for("/static/images/emojione-check-2714.svg") %]" height="12" width="12" title="Succeeded" alt="Succeeded" class="build-status" />
</th>
<th style="width: 2em">
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="12" width="12" title="Failed" alt="Failed" class="build-status" />
</th>
<th style="width: 2em">
<img src="[% c.uri_for("/static/images/emojione-question-2754.svg") %]" height="12" width="12" title="Queued" alt="Queued" class="build-status" />
</th>
</tr>
</thead>
<tbody>
[% FOREACH j IN jobsets %]
[% successrate = 0 %]
<tr class="jobset [% IF j.hidden && !showHidden %] hidden-jobset [% END %] [% IF !j.enabled %] disabled-jobset [% END %]" >
<td>
[% IF j.get_column('nrscheduled') > 0 %]
<img src="[% c.uri_for("/static/images/emojione-question-2754.svg") %]" height="16" width="16" title="Scheduled" alt="Scheduled" />
[% ELSIF j.get_column('nrfailed') == 0 %]
<img src="[% c.uri_for("/static/images/emojione-check-2714.svg") %]" height="16" width="16" title="Succeeded" alt="Succeeded" />
[% ELSIF j.get_column('nrfailed') > 0 && j.get_column('nrsucceeded') > 0 %]
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="16" width="16" title="Some Failed" alt="Some Failed" />
[% ELSE %]
<img src="[% c.uri_for("/static/images/emojione-red-x-274c.svg") %]" height="16" width="16" title="All Failed" alt="All Failed" />
[% END %]
</td>
<td><span class="[% IF !j.enabled %]disabled-jobset[% END %] [%+ IF j.hidden %]hidden-jobset[% END %]">[% IF showProject; INCLUDE renderFullJobsetName project=j.get_column('project') jobset=j.name inRow=1; ELSE; INCLUDE renderJobsetName project=j.get_column('project') jobset=j.name inRow=1; END %]</span></td>
<td>[% HTML.escape(j.description) %]</td>
<td>[% IF j.lastcheckedtime;
INCLUDE renderDateTime timestamp = j.lastcheckedtime;
IF j.has_error || j.fetcherrormsg; %]&nbsp;<span class = 'badge badge-warning'>Error</span>[% END;
ELSE; "-";
END %]</td>
[% IF j.get_column('nrtotal') > 0 %]
[% successrate = ( j.get_column('nrsucceeded') / j.get_column('nrtotal') )*100 %]
[% IF j.get_column('nrscheduled') > 0 %]
[% class = 'badge badge-secondary' %]
[% ELSIF successrate < 25 %]
[% class = 'badge badge-danger' %]
[% ELSIF successrate < 75 %]
[% class = 'badge badge-warning' %]
[% ELSIF successrate <= 100 %]
[% class = 'badge badge-success' %]
[% END %]
[% END %]
<td><span class="[% class %]">[% successrate FILTER format('%d') %]%</span></td>
<td>
[% IF j.get_column('nrsucceeded') > 0 %]
<span class="badge badge-success">[% j.get_column('nrsucceeded') %]</span>
[% END %]
</td>
<td>
[% IF j.get_column('nrfailed') > 0 %]
<span class="badge badge-danger">[% j.get_column('nrfailed') %]</span>
[% END %]
</td>
<td>
[% IF j.get_column('nrscheduled') > 0 %]
<span class="badge badge-secondary">[% j.get_column('nrscheduled') %]</span>
[% END %]
</td>
</tr>
[% END %]
</tbody>
</table>
[% END;
BLOCK includeFlot %]
<script src="[% c.uri_for("/static/js/flot/jquery.flot.min.js") %]" type="text/javascript"></script>
<script src="[% c.uri_for("/static/js/flot/jquery.flot.time.min.js") %]" type="text/javascript"></script>
<script src="[% c.uri_for("/static/js/flot/jquery.flot.selection.min.js") %]" type="text/javascript"></script>
<div id="flot-tooltip" class="flot-tooltip"></div>
[% END;
BLOCK createChart %]
<div id="[%id%]-chart" style="width: 1000px; height: 400px;"></div>
<div id="[%id%]-overview" style="margin-top: 20px; margin-left: 50px; margin-right: 50px; width: 900px; height: 100px"></div>
<script type="text/javascript">
$(function() {
showChart("[%id%]", "[%dataUrl%]", "[%yaxis%]");
});
</script>
[% END;
BLOCK renderMachineName;
machine ? stripSSHUser(machine).match('^(([^\.]|\.[0-9])*)').0 : "localhost";
END;
%]