summaryrefslogtreecommitdiffstats
path: root/modules-available/statistics/pages/summary.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/statistics/pages/summary.inc.php')
-rw-r--r--modules-available/statistics/pages/summary.inc.php268
1 files changed, 160 insertions, 108 deletions
diff --git a/modules-available/statistics/pages/summary.inc.php b/modules-available/statistics/pages/summary.inc.php
index ce67070e..905f5d90 100644
--- a/modules-available/statistics/pages/summary.inc.php
+++ b/modules-available/statistics/pages/summary.inc.php
@@ -8,6 +8,9 @@ class SubPage
public static function doPreprocess()
{
User::assertPermission('view.summary');
+ if (!Module::isAvailable('js_chart')) {
+ ErrorHandler::traceError('js_chart not available');
+ }
}
public static function doRender()
@@ -23,7 +26,9 @@ class SubPage
// Prepare chart colors
self::$STATS_COLORS = [];
for ($i = 0; $i < 10; ++$i) {
- self::$STATS_COLORS[] = '#55' . sprintf('%02s%02s', dechex((($i + 1) * ($i + 1)) / .3922), dechex(abs((5 - $i) * 51)));
+ self::$STATS_COLORS[] = '#55' . sprintf('%02s%02s', dechex(
+ (int)((($i + 1) * ($i + 1)) / .3922)),
+ dechex((int)(abs((5 - $i) * 51))));
}
$filterSet->filterNonClients();
@@ -38,10 +43,7 @@ class SubPage
Render::closeTag('div');
}
- /**
- * @param \StatisticsFilterSet $filterSet
- */
- private static function showSummary($filterSet)
+ private static function showSummary(StatisticsFilterSet $filterSet): void
{
$filterSet->makeFragments($where, $join, $args);
$known = Database::queryFirst("SELECT Count(*) AS val FROM machine m $join WHERE $where", $args);
@@ -53,36 +55,82 @@ class SubPage
} else {
$usedpercent = 0;
}
- $data = array(
+ $data = [
'known' => $known['val'],
'online' => $on['val'],
'used' => $used['val'],
'usedpercent' => $usedpercent,
'badhdd' => $hdd['val'],
- );
+ ];
// Graph
- $cutoff = time() - 2 * 86400;
- $res = Database::simpleQuery("SELECT dateline, data FROM statistic WHERE typeid = '~stats' AND dateline > $cutoff ORDER BY dateline ASC");
- $labels = array();
- $points1 = array('data' => array(), 'label' => 'Online', 'fillColor' => '#efe', 'strokeColor' => '#aea', 'pointColor' => '#7e7', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#7e7');
- $points2 = array('data' => array(), 'label' => 'In use', 'fillColor' => '#fee', 'strokeColor' => '#eaa', 'pointColor' => '#e77', 'pointStrokeColor' => '#fff', 'pointHighlightFill' => '#fff', 'pointHighlightStroke' => '#e77');
- $sum = 0;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $x = explode('#', $row['data']);
- if ($sum === 0) {
- $labels[] = date('H:i', $row['dateline']);
+ $labels = [];
+ $points1 = [];
+ $points2 = [];
+ $lectures = [];
+ // Get locations
+ if ($filterSet->suitableForUsageGraph()) {
+ $locFilter = $filterSet->hasFilter('LocationStatisticsFilter');
+ if ($locFilter === null
+ || ($locFilter->op === '~' && ($locFilter->argument == 0
+ || (is_array($locFilter->argument) && in_array(0, $locFilter->argument))))) {
+ $locations = null;
+ $op = null;
+ } elseif ($locFilter->op === '~') {
+ $locations = array_keys(Location::getRecursiveFlat($locFilter->argument));
+ $op = $locFilter->op;
} else {
- $x[1] = max($x[1], array_pop($points1['data']));
- $x[2] = max($x[2], array_pop($points2['data']));
+ if (is_array($locFilter->argument)) {
+ $locations = $locFilter->argument;
+ } else {
+ $locations = [$locFilter->argument];
+ }
+ $op = $locFilter->op;
}
- $points1['data'][] = $x[1];
- $points2['data'][] = $x[2];
- ++$sum;
- if ($sum === 12) {
- $sum = 0;
+ //error_log($op . ' ' . print_r($locations, true));
+ $cutoff = time() - 2 * 86400;
+ $res = Database::simpleQuery("SELECT dateline, data FROM statistic
+ WHERE typeid = '~stats' AND dateline > $cutoff ORDER BY dateline DESC");
+ // Get max from 4 consecutive values, which should be 4*5 = 20m
+ $sum = 0;
+ foreach ($res as $row) {
+ if ($row['data'][0] === '{') {
+ $x = json_decode($row['data'], true);
+ if (!is_array($x) || !isset($x['usage']))
+ continue;
+ $x = self::mangleStatsJson($x, $locations, $op);
+ } else if ($locations === null) {
+ $x = explode('#', $row['data']);
+ if (count($x) < 3)
+ continue;
+ $x[] = 0;
+ } else {
+ continue;
+ }
+ if ($sum % 4 === 0) {
+ $labels[] = date('H:i', $row['dateline']);
+ } else {
+ $x[1] = max($x[1], array_pop($points1));
+ $x[2] = max($x[2], array_pop($points2));
+ $x[3] += array_pop($lectures);
+ }
+ $points1[] = $x[1];
+ $points2[] = $x[2];
+ $lectures[] = $x[3];
+ ++$sum;
}
}
- $data['json'] = json_encode(array('labels' => $labels, 'datasets' => array($points1, $points2)));
+ if (!empty($points1) && max($points1) > 0) {
+ $labels = array_reverse($labels);
+ $points1 = array_reverse($points1);
+ $points2 = array_reverse($points2);
+ $lectures = array_reverse($lectures);
+ $data['json'] = json_encode(['labels' => $labels,
+ 'datasets' => [
+ ['data' => $points1, 'label' => 'Online', 'borderColor' => '#8f3'],
+ ['data' => $points2, 'label' => 'In use', 'borderColor' => '#e76'],
+ ]]);
+ $data['markings'] = json_encode($lectures);
+ }
if (Module::get('runmode') !== false) {
$res = Database::queryFirst('SELECT Count(*) AS cnt FROM machine m INNER JOIN runmode r USING (machineuuid)'
. " $join WHERE $where", $args);
@@ -92,74 +140,71 @@ class SubPage
Render::addTemplate('summary', $data);
}
- /**
- * @param \StatisticsFilterSet $filterSet
- */
- private static function showSystemModels($filterSet)
+ private static function showSystemModels(StatisticsFilterSet $filterSet): void
{
$filterSet->makeFragments($where, $join, $args);
$res = Database::simpleQuery('SELECT systemmodel, Round(AVG(realcores)) AS cores, Count(*) AS `count` FROM machine m'
. " $join WHERE $where GROUP BY systemmodel ORDER BY `count` DESC, systemmodel ASC", $args);
- $lines = array();
- $json = array();
+ $lines = [];
+ $json = [];
$id = 0;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
if (empty($row['systemmodel'])) {
continue;
}
settype($row['count'], 'integer');
- $row['id'] = 'systemid' . $id;
$row['urlsystemmodel'] = urlencode($row['systemmodel']);
+ $row['idx'] = count($lines);
$lines[] = $row;
- $json[] = array(
+ $json[] = [
'color' => self::$STATS_COLORS[$id % count(self::$STATS_COLORS)],
- 'label' => 'systemid' . $id,
'value' => $row['count'],
- );
+ ];
++$id;
}
self::capChart($json, $lines, 0.92);
- Render::addTemplate('cpumodels', array('rows' => $lines, 'json' => json_encode($json)));
+ Render::addTemplate('cpumodels', ['rows' => $lines, 'json' => json_encode($json)]);
}
- /**
- * @param \StatisticsFilterSet $filterSet
- */
- private static function showMemory($filterSet)
+ private static function alignBySteps(int $value, array $steps): int
+ {
+ for ($i = 1; $i < count($steps); ++$i) {
+ if ($steps[$i] < $value) {
+ continue;
+ }
+ if ($steps[$i] - $value >= $value - $steps[$i - 1]) {
+ --$i;
+ }
+ return $steps[$i];
+ }
+ return $value;
+ }
+
+ private static function showMemory(StatisticsFilterSet $filterSet): void
{
$filterSet->makeFragments($where, $join, $args);
$res = Database::simpleQuery("SELECT mbram, Count(*) AS `count` FROM machine m $join
WHERE $where GROUP BY mbram", $args);
- $lines = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
- $gb = (int)ceil($row['mbram'] / 1024);
- for ($i = 1; $i < count(StatisticsFilter::SIZE_RAM); ++$i) {
- if (StatisticsFilter::SIZE_RAM[$i] < $gb) {
- continue;
- }
- if (StatisticsFilter::SIZE_RAM[$i] - $gb >= $gb - StatisticsFilter::SIZE_RAM[$i - 1]) {
- --$i;
- }
- $gb = StatisticsFilter::SIZE_RAM[$i];
- break;
- }
- if (isset($lines[$gb])) {
- $lines[$gb] += $row['count'];
- } else {
- $lines[$gb] = $row['count'];
- }
+ $lines = [];
+ foreach ($res as $row) {
+ $gb = self::alignBySteps((int)ceil($row['mbram'] / 1024), StatisticsFilter::SIZE_RAM);
+ $lines[$gb] = ($lines[$gb] ?? 0) + $row['count'];
}
asort($lines);
- $data = array('rows' => array());
- $json = array();
+ $data = ['rows' => []];
+ $json = [];
$id = 0;
foreach (array_reverse($lines, true) as $k => $v) {
- $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => StatisticsStyling::ramColorClass($k * 1024));
- $json[] = array(
+ $data['rows'][] = [
+ 'idx' => count($data['rows']),
+ 'gb' => $k,
+ 'count' => $v,
+ 'class' => StatisticsStyling::ramColorClass($k * 1024),
+ ];
+ $json[] = [
'color' => self::$STATS_COLORS[$id % count(self::$STATS_COLORS)],
- 'label' => (string)$k,
'value' => $v,
- );
+ ];
++$id;
}
self::capChart($json, $data['rows'], 0.92);
@@ -167,62 +212,47 @@ class SubPage
Render::addTemplate('memory', $data);
}
- /**
- * @param \StatisticsFilterSet $filterSet
- */
- private static function showKvmState($filterSet)
+ private static function showKvmState(StatisticsFilterSet $filterSet): void
{
$filterSet->makeFragments($where, $join, $args);
- $colors = array('UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#6d6');
+ $colors = ['UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#6d6'];
$res = Database::simpleQuery("SELECT kvmstate, Count(*) AS `count` FROM machine m $join
WHERE $where GROUP BY kvmstate ORDER BY `count` DESC", $args);
- $lines = array();
- $json = array();
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $lines = [];
+ $json = [];
+ foreach ($res as $row) {
+ $row['idx'] = count($lines);
$lines[] = $row;
$json[] = array(
- 'color' => isset($colors[$row['kvmstate']]) ? $colors[$row['kvmstate']] : '#000',
- 'label' => $row['kvmstate'],
+ 'color' => $colors[$row['kvmstate']] ?? '#000',
'value' => $row['count'],
);
}
Render::addTemplate('kvmstate', array('rows' => $lines, 'json' => json_encode($json)));
}
- /**
- * @param \StatisticsFilterSet $filterSet
- */
- private static function showId44($filterSet)
+ private static function showId44(StatisticsFilterSet $filterSet): void
{
$filterSet->makeFragments($where, $join, $args);
$res = Database::simpleQuery("SELECT id44mb, Count(*) AS `count` FROM machine m $join WHERE $where GROUP BY id44mb", $args);
$lines = array();
$total = 0;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
$total += $row['count'];
- $gb = (int)ceil($row['id44mb'] / 1024);
- for ($i = 1; $i < count(StatisticsFilter::SIZE_ID44); ++$i) {
- if (StatisticsFilter::SIZE_ID44[$i] < $gb) {
- continue;
- }
- if (StatisticsFilter::SIZE_ID44[$i] - $gb >= $gb - StatisticsFilter::SIZE_ID44[$i - 1]) {
- --$i;
- }
- $gb = StatisticsFilter::SIZE_ID44[$i];
- break;
- }
- if (isset($lines[$gb])) {
- $lines[$gb] += $row['count'];
- } else {
- $lines[$gb] = $row['count'];
- }
+ $gb = self::alignBySteps((int)ceil($row['id44mb'] / 1024), StatisticsFilter::SIZE_PARTITION);
+ $lines[$gb] = ($lines[$gb] ?? 0) + $row['count'];
}
asort($lines);
$data = array('rows' => array());
$json = array();
$id = 0;
foreach (array_reverse($lines, true) as $k => $v) {
- $data['rows'][] = array('gb' => $k, 'count' => $v, 'class' => StatisticsStyling::hddColorClass($k));
+ $data['rows'][] = [
+ 'idx' => count($data['rows']),
+ 'gb' => $k,
+ 'count' => $v,
+ 'class' => StatisticsStyling::hddColorClass($k),
+ ];
if ($k === 0) {
$color = '#e55';
} else {
@@ -230,7 +260,6 @@ class SubPage
}
$json[] = array(
'color' => $color,
- 'label' => (string)$k,
'value' => $v,
);
}
@@ -239,19 +268,18 @@ class SubPage
Render::addTemplate('id44', $data);
}
- /**
- * @param \StatisticsFilterSet $filterSet
- */
- private static function showLatestMachines($filterSet)
+ private static function showLatestMachines(StatisticsFilterSet $filterSet): void
{
$filterSet->makeFragments($where, $join, $args);
$args['cutoff'] = ceil(time() / 3600) * 3600 - 86400 * 10;
- $res = Database::simpleQuery("SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine m $join"
- . " WHERE firstseen > :cutoff AND $where ORDER BY firstseen DESC LIMIT 32", $args);
+ $res = Database::simpleQuery("SELECT m.machineuuid, m.clientip, m.hostname, m.firstseen, m.mbram, m.kvmstate, m.id44mb
+ FROM machine m $join
+ WHERE firstseen > :cutoff AND $where
+ ORDER BY firstseen DESC LIMIT 32", $args);
$rows = array();
$count = 0;
- while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ foreach ($res as $row) {
if (empty($row['hostname'])) {
$row['hostname'] = $row['clientip'];
}
@@ -259,9 +287,9 @@ class SubPage
$row['firstseen'] = Util::prettyTime($row['firstseen']);
$row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding..
$row['gbtmp'] = round($row['id44mb'] / 1024);
- $row['ramclass'] = StatisticsStyling::ramColorClass($row['mbram']);
+ $row['ramclass'] = StatisticsStyling::ramColorClass((int)$row['mbram']);
$row['kvmclass'] = StatisticsStyling::kvmColorClass($row['kvmstate']);
- $row['hddclass'] = StatisticsStyling::hddColorClass($row['gbtmp']);
+ $row['hddclass'] = StatisticsStyling::hddColorClass((int)$row['gbtmp']);
$row['kvmicon'] = $row['kvmstate'] === 'ENABLED' ? '✓' : '✗';
if (++$count > 5) {
$row['collapse'] = 'collapse';
@@ -277,7 +305,7 @@ class SubPage
- private static function capChart(&$json, &$rows, $cutoff, $minSlice = 0.015)
+ private static function capChart(array &$json, array &$rows, float $cutoff, float $minSlice = 0.015): void
{
$total = 0;
foreach ($json as $entry) {
@@ -309,4 +337,28 @@ class SubPage
}
}
+ /**
+ * @param array $json decoded json ~stats data
+ * @param ?int[] $locations
+ */
+ private static function mangleStatsJson(array $json, ?array $locations, ?string $op): array
+ {
+ // Total, On, InUse, Lectures
+ $retval = [0, 0, 0, 0];
+ foreach ($json['usage'] as $lid => $data) {
+ $lid = (int)$lid;
+ if ($locations === null
+ || ($op === '!=' && !in_array($lid, $locations))
+ || ($op !== '!=' && in_array($lid, $locations))) {
+ $retval[0] += $data['t'];
+ $retval[1] += $data['o'] ?? 0;
+ $retval[2] += $data['u'] ?? 0;
+ if (isset($data['event'])) {
+ $retval[3] += 1;
+ }
+ }
+ }
+ return $retval;
+ }
+
}