Add timeline view script and styles

refs #4190
This commit is contained in:
Johannes Meyer 2014-03-27 09:38:57 +01:00
parent 66c3dd26e5
commit f5e4331d71
6 changed files with 261 additions and 120 deletions

View File

@ -87,6 +87,10 @@ url = "monitoring/list/notifications"
title = "All Events"
url = "monitoring/list/eventhistory?raw_timestamp>=-2+days"
[History.Timeline]
title = "Timeline"
url = "monitoring/timeline"
[System.Process Info]
title = "Process Info"
url = "monitoring/process/info"

View File

@ -30,7 +30,6 @@ namespace Icinga\Util;
* Provide functions to change and convert colors.
*/
class Color {
/**
* Convert a given color string to an rgb-array containing
* each color as a decimal value.
@ -71,7 +70,6 @@ class Color {
. (strlen($b) > 1 ? $b : '0' . $b);
}
/**
* Change the saturation for a given color.
*
@ -85,10 +83,20 @@ class Color {
*/
public static function changeSaturation($color, $change)
{
$rgb = self::rgbAsArray($color);
$rgb = self::changeRgbSaturation($rgb, $change);
$col = self::arrayToRgb($rgb);
return $col;
return self::arrayToRgb(self::changeRgbSaturation(self::rgbAsArray($color), $change));
}
/**
* Change the brightness for a given color
*
* @param $color string The color to change
* @param $change float The change in percent
*
* @return string
*/
public static function changeBrightness($color, $change)
{
return self::arrayToRgb(self::changeRgbBrightness(self::rgbAsArray($color), $change));
}
/**
@ -115,4 +123,21 @@ class Color {
$rgb[2] = (int)($p + ($b - $p) * $change);
return $rgb;
}
/**
* @param $rgb array The rgb-array to change
* @param $change float The factor
*
* @return array The updated rgb-array
*/
private static function changeRgbBrightness(array $rgb, $change)
{
$red = $rgb[0] + (255 * $change);
$green = $rgb[1] + (255 * $change);
$blue = $rgb[2] + (255 * $change);
$rgb[0] = $red < 255 ? (int) $red : 255;
$rgb[1] = $green < 255 ? (int) $green : 255;
$rgb[2] = $blue < 255 ? (int) $blue : 255;
return $rgb;
}
}

View File

@ -19,6 +19,7 @@ class Monitoring_TimelineController extends ActionController
$this->setupIntervalBox();
list($displayRange, $forecastRange) = $this->buildTimeRanges();
$detailUrl = '/monitoring/list/eventhistory?raw_timestamp<=%s&raw_timestamp>=%s&type=%s';
$timeline = new TimeLine(
EventHistoryView::fromRequest(
$this->getRequest(),
@ -28,14 +29,39 @@ class Monitoring_TimelineController extends ActionController
)
),
array(
'notify' => array('label' => t('Notifications'), 'color' => 'red'),
'hard_state' => array('label' => t('Hard state changes'), 'color' => 'green'),
'comment' => array('label' => t('Comments'), 'color' => 'blue'),
'ack' => array('label' => t('Acknowledgements'), 'color' => 'black'),
'dt_start' => array('label' => t('Started downtimes'), 'color' => 'grey'),
'dt_end' => array('label' => t('Ended downtimes'), 'color' => 'white')
'notify' => array(
'detailUrl' => $detailUrl,
'label' => t('Notifications'),
'color' => '#3a71ea'
),
'hard_state' => array(
'detailUrl' => $detailUrl,
'label' => t('Hard state changes'),
'color' => '#ff7000'
),
'comment' => array(
'detailUrl' => $detailUrl,
'label' => t('Comments'),
'color' => '#79bdba'
),
'ack' => array(
'detailUrl' => $detailUrl,
'label' => t('Acknowledgements'),
'color' => '#a2721d'
),
'dt_start' => array(
'detailUrl' => $detailUrl,
'label' => t('Started downtimes'),
'color' => '#8e8e8e'
),
'dt_end' => array(
'detailUrl' => $detailUrl,
'label' => t('Ended downtimes'),
'color' => '#d5d6ad'
)
)
);
$timeline->setMaximumCircleWidth('6em');
$timeline->setDisplayRange($displayRange);
$timeline->setForecastRange($forecastRange);
$timeline->setSession($this->getWindowSession('timeline'));
@ -99,13 +125,13 @@ class Monitoring_TimelineController extends ActionController
case '1d':
return $this->getDateFormat();
case '1w':
return '\W\e\ek #W \of Y';
return '\W\e\ek #W\<b\r\>\of Y';
case '1m':
return 'F Y';
case '1y':
return 'Y';
default:
return $this->getDateFormat() . ' ' . $this->getTimeFormat();
return $this->getDateFormat() . '\<b\r\>' . $this->getTimeFormat();
}
}

View File

@ -1,8 +1,85 @@
<?php if (!$this->compact): ?>
<?php
use Icinga\Util\Color;
$groupInfo = $timeline->getGroupInfo();
$firstRow = true;
?>
<div class="controls">
<div style="margin: 1em;" class="dontprint">
<?= $this->intervalBox; ?>
<?= $intervalBox; ?>
</div>
<div style="margin: 1em;" class="timeline-legend">
<h2><?= $this->translate('Legend'); ?></h2>
<?php foreach ($groupInfo as $labelAndColor): ?>
<span style="background-color: <?= $labelAndColor['color']; ?>;">
<span><?= $labelAndColor['label']; ?></span>
</span>
<?php endforeach ?>
</div>
</div>
<div class="content" data-base-target="_next">
<table class="timeline">
<tbody>
<?php foreach ($timeline as $timeInfo): ?>
<tr>
<th>
<a href="<?= $this->href(
'/monitoring/list/eventhistory',
array(
'raw_timestamp<' => $timeInfo[0]->start->getTimestamp(),
'raw_timestamp>' => $timeInfo[0]->end->getTimestamp()
)
); ?>">
<?= $timeInfo[0]->end->format($intervalFormat); ?>
</a>
</th>
<?php foreach ($groupInfo as $groupName => $labelAndColor): ?>
<td>
<div class="circle-box">
<?php if (array_key_exists($groupName, $timeInfo[1])): ?>
<?php
$circleWidth = $timeline->calculateCircleWidth($timeInfo[1][$groupName], 2);
$extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$groupName], 2);
?>
<?php if ($firstRow && $extrapolatedCircleWidth !== $circleWidth): ?>
<?php
$inlineStyle = sprintf(
'width: %3$s; height: %3$s; background-color: %1$s; margin-top: -%2$s; margin-left: -%2$s;',
Color::changeBrightness($timeInfo[1][$groupName]->getColor(), 0.25),
((float) substr($extrapolatedCircleWidth, 0, -2) / 2) . 'em',
$extrapolatedCircleWidth
);
?>
<div class="outer-circle extrapolated" style="<?= $inlineStyle; ?>">
<?php else: ?>
<div class="outer-circle">
<?php endif ?>
<?= $this->timeline->buildTimeline(); ?>
<?php
$inlineStyle = sprintf(
'width: %3$s; height: %3$s; background-color: %2$s; margin-top: -%1$s; margin-left: -%1$s;',
((float) substr($circleWidth, 0, -2) / 2) . 'em',
$timeInfo[1][$groupName]->getColor(),
$circleWidth
);
?>
<a class="inner-circle" style="<?= $inlineStyle; ?>" href="<?= $this->href(
sprintf(
$timeInfo[1][$groupName]->getDetailUrl(),
$timeInfo[0]->start->getTimestamp(),
$timeInfo[0]->end->getTimestamp(),
$groupName
)
); ?>" title="<?= $timeInfo[1][$groupName]->getValue(); ?> <?= $labelAndColor['label']; ?>"></a>
</div>
<?php else: ?>
&nbsp;
<?php endif ?>
</div>
</td>
<?php endforeach ?>
</tr>
<?php $firstRow = false; ?>
<?php endforeach ?>
</tbody>
</table>
</div>

View File

@ -162,6 +162,22 @@ class TimeLine implements IteratorAggregate
}
}
/**
* Return all known group types (identifiers) with their respective labels and colors as array
*
* @return array
*/
public function getGroupInfo()
{
$groupInfo = array();
foreach ($this->identifiers as $name => $attributes) {
$groupInfo[$name]['label'] = $attributes['label'];
$groupInfo[$name]['color'] = $attributes['color'];
}
return $groupInfo;
}
/**
* Return the circle's diameter for the given event group
*
@ -410,106 +426,4 @@ class TimeLine implements IteratorAggregate
return $array;
}
/**
* Build the legend
*/
private function buildLegend()
{
// TODO: Put this in some sort of dedicated stylesheet
$circleStyle = 'width:75px;height:75px;border-radius:50%;box-shadow:4px 4px 8px grey;border:2px solid;margin:auto;';
$labelStyle = 'font-size:12px;margin-top:10px;text-align:center;';
$titleStyle = 'margin-left:25px;';
$elements = array();
foreach ($this->getGroups() as $groupName => $groupInfo) {
$groupColor = $groupInfo['color'] !== null ? $groupInfo['color'] : $this->getRandomCssColor();
$elements[] = '' .
'<div style="' . $circleStyle . 'background-color: ' . $groupColor . '"></div>' .
'<p style="' . $labelStyle . '">' . $groupName . '</p>';
}
$legend = '' .
'<h2 style="' . $titleStyle . '">' . t('Shown event groups') . '</h2>' .
'<div class="row">' .
implode(
'',
array_map(
function ($e) { return '<div class="col-sm-6 col-xs-3 col-md-2 col-lg-1">' . $e . '</div>'; },
$elements
)
) .
'</div>';
return $legend;
}
/**
* Build the timeline
*/
public function buildTimeline()
{
$timelineGroups = array();
foreach ($this->displayData as $group) {
$timestamp = $group->getDateTime()->getTimestamp();
if (!array_key_exists($timestamp, $timelineGroups)) {
$timelineGroups[$timestamp] = array();
}
$timelineGroups[$timestamp][] = $group;
}
$elements = array();
foreach ($this->range as $timestamp => $timeframe) {
$elementGroups = array();
$biggestWidth = 0;
if (array_key_exists($timestamp, $timelineGroups)) {
foreach ($timelineGroups[$timestamp] as $group) {
$circleWidth = $this->calculateCircleWidth(
empty($elements) ? $this->extrapolateEventCount($group, 4) : $group->getValue()
);
$groupColor = $group->getColor() !== null ? $group->getColor() : $this->getRandomCssColor();
$elementGroups[] = sprintf(
'<div class="col-sm-12 col-xs-12 col-md-6 col-lg-3" style="width:%4$s%2$s;margin:10px 10px;float:left;">' .
' <a href="%1$s" data-icinga-target="detail">' .
' <div style="width:%4$s%2$s;height:%4$s%2$s;border-radius:50%%;' . // TODO: Put this in some sort of dedicated stylesheet
'box-shadow:4px 4px 8px grey;border:2px solid black;' .
'margin:auto;background-color:%5$s;text-align:center;' .
'padding-top:25%%;color:black;">' .
' %3$s' .
' </div>' .
' </a>' .
'</div>',
$group->getDetailUrl(),
$this->diameterUnit,
$group->getValue(),
$circleWidth,
$groupColor
);
if ($circleWidth > $biggestWidth) {
$biggestWidth = $circleWidth;
}
}
}
$timeframeUrl = '';/*$this->getRequest()->getBaseUrl() . '/monitoring/list/eventhistory?timestamp<=' .
$timeframe->start->getTimestamp() . '&timestamp>=' . $timeframe->end->getTimestamp();*/
$elements[] = sprintf(
'<div class="row" style="height:%3$s%2$s;">%1$s</div>',
implode('', $elementGroups),
$this->diameterUnit,
$biggestWidth
);
$elements[] = '<br style="clear:all;" />';
$elements[] = '<div><a href="' . $timeframeUrl . '" data-icinga-target="detail">' .
$timeframe->end->format($this->getIntervalFormat()) . '</a>' .
'<hr style="margin-top:0;"></div>';
}
$elements[] = '<span id="TimelineEnd"></span>';
return implode('', $elements);
}
}

View File

@ -630,6 +630,101 @@ table.pivot {
/* End of monitoring pivot table styles */
/* Monitoring timeline styles */
div.timeline-legend {
float: left;
padding: 0.5em;
border: 1px solid #ccc;
border-radius: 0.5em;
background-color: #eee;
&:after {
clear: left;
}
h2 {
margin: 0;
margin-left: 0.5em;
line-height: 1.1em;
}
& > span {
display: inline-block;
padding: 0.5em;
margin: 0.5em;
border-radius: 0.5em;
span {
color: white;
font-size: 0.8em;
font-weight: bold;
white-space: nowrap;
}
}
}
table.timeline {
th {
padding-right: 1.5em;
a {
color: black;
font-size: 0.8em;
text-decoration: none;
white-space: nowrap;
&:hover {
color: #666;
}
}
}
td {
padding: 0.5em;
div.circle-box {
width: 6em;
height: 6em;
position: relative;
div.outer-circle {
width: 100%;
height: 100%;
position: absolute;
&.extrapolated {
// width: inline-style;
// height: inline-style;
top: 49%; // Compensate border
left: 49%; // Compensate border
// margin-top: inline-style;
// margin-left: inline-style;
border: 1px dashed #666;
border-radius: 100%;
// background-color: inline-style;
}
a.inner-circle {
// width: inline-style;
// height: inline-style;
display: block;
position: absolute;
top: 49%; // Compensate border
left: 49%; // Compensate border
// margin-top: inline-style;
// margin-left: inline-style;
border: 1px solid black;
border-radius: 100%;
// background-color: inline-style;
}
}
}
}
}
/* End of monitoring timeline styles */
.controls {
font-size: 0.9em;
}