From 66aac2e67e35bd987bc3750b63f2734e47d0d57d Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 19 Nov 2015 12:13:36 +0100 Subject: Implement statistics logging DB scheme update is still missing, might still change a little... --- apis/clientlog.inc.php | 75 ++++- lang/de/templates/statistics/clientlist.json | 11 + lang/de/templates/statistics/cpumodels.json | 6 + lang/de/templates/statistics/id44.json | 5 + lang/de/templates/statistics/kvmstate.json | 5 + lang/de/templates/statistics/machine-hdds.json | 7 + lang/de/templates/statistics/machine-main.json | 18 ++ lang/de/templates/statistics/memory.json | 5 + lang/en/templates/statistics/clientlist.json | 11 + lang/en/templates/statistics/cpumodels.json | 6 + lang/en/templates/statistics/id44.json | 5 + lang/en/templates/statistics/kvmstate.json | 5 + lang/en/templates/statistics/machine-hdds.json | 7 + lang/en/templates/statistics/machine-main.json | 18 ++ lang/en/templates/statistics/memory.json | 5 + lang/pt/templates/statistics/clientlist.json | 3 + lang/pt/templates/statistics/cpumodels.json | 3 + lang/pt/templates/statistics/id44.json | 3 + lang/pt/templates/statistics/kvmstate.json | 3 + lang/pt/templates/statistics/machine-hdds.json | 3 + lang/pt/templates/statistics/machine-main.json | 3 + lang/pt/templates/statistics/memory.json | 3 + modules/statistics.inc.php | 386 +++++++++++++++++++++++++ script/chart.min.js | 11 + templates/main-menu.html | 4 +- templates/statistics/clientlist.html | 26 ++ templates/statistics/cpumodels.html | 49 ++++ templates/statistics/id44.html | 48 +++ templates/statistics/kvmstate.html | 47 +++ templates/statistics/machine-hdds.html | 55 ++++ templates/statistics/machine-main.html | 79 +++++ templates/statistics/memory.html | 47 +++ 32 files changed, 959 insertions(+), 3 deletions(-) create mode 100644 lang/de/templates/statistics/clientlist.json create mode 100644 lang/de/templates/statistics/cpumodels.json create mode 100644 lang/de/templates/statistics/id44.json create mode 100644 lang/de/templates/statistics/kvmstate.json create mode 100644 lang/de/templates/statistics/machine-hdds.json create mode 100644 lang/de/templates/statistics/machine-main.json create mode 100644 lang/de/templates/statistics/memory.json create mode 100644 lang/en/templates/statistics/clientlist.json create mode 100644 lang/en/templates/statistics/cpumodels.json create mode 100644 lang/en/templates/statistics/id44.json create mode 100644 lang/en/templates/statistics/kvmstate.json create mode 100644 lang/en/templates/statistics/machine-hdds.json create mode 100644 lang/en/templates/statistics/machine-main.json create mode 100644 lang/en/templates/statistics/memory.json create mode 100644 lang/pt/templates/statistics/clientlist.json create mode 100644 lang/pt/templates/statistics/cpumodels.json create mode 100644 lang/pt/templates/statistics/id44.json create mode 100644 lang/pt/templates/statistics/kvmstate.json create mode 100644 lang/pt/templates/statistics/machine-hdds.json create mode 100644 lang/pt/templates/statistics/machine-main.json create mode 100644 lang/pt/templates/statistics/memory.json create mode 100644 modules/statistics.inc.php create mode 100644 script/chart.min.js create mode 100644 templates/statistics/clientlist.html create mode 100644 templates/statistics/cpumodels.html create mode 100644 templates/statistics/id44.html create mode 100644 templates/statistics/kvmstate.html create mode 100644 templates/statistics/machine-hdds.html create mode 100644 templates/statistics/machine-main.html create mode 100644 templates/statistics/memory.html diff --git a/apis/clientlog.inc.php b/apis/clientlog.inc.php index 43f31917..3f1e02ea 100644 --- a/apis/clientlog.inc.php +++ b/apis/clientlog.inc.php @@ -1,10 +1,81 @@ 17) die("Invalid MAC.\n"); + if ($uptime < 0 || $uptime > 4000000) die("Implausible uptime.\n"); + $realcores = Request::post('realcores', 0, 'integer'); + if ($realcores < 0 || $realcores > 512) $realcores = 0; + $mbram = Request::post('mbram', 0, 'integer'); + if ($mbram < 0 || $mbram > 102400) $mbram = 0; + $kvmstate = Request::post('kvmstate', 'UNKNOWN', 'string'); + $valid = array('UNKNOWN', 'UNSUPPORTED', 'DISABLED', 'ENABLED'); + if (!in_array($kvmstate, $valid)) $kvmstate = 'UNKNOWN'; + $cpumodel = Request::post('cpumodel', '', 'string'); + $id44mb = Request::post('id44mb', 0, 'integer'); + if ($id44mb < 0 || $id44mb > 10240000) $id44mb = 0; + $hostname = gethostbyaddr($ip); + if (!is_string($hostname) || $hostname === $ip) { + $hostname = ''; + } + $data = Request::post('data', '', 'string'); + Database::exec('INSERT INTO machine ' + . '(machineuuid, macaddr, clientip, firstseen, lastseen, logintime, position, lastboot, realcores, mbram,' + . ' kvmstate, cpumodel, id44mb, data, hostname) VALUES ' + . "(:uuid, :macaddr, :clientip, :firstseen, :lastseen, 0, '', :lastboot, :realcores, :mbram," + . ' :kvmstate, :cpumodel, :id44mb, :data, :hostname)' + . ' ON DUPLICATE KEY UPDATE' + . ' macaddr = VALUES(macaddr),' + . ' clientip = VALUES(clientip),' + . ' lastseen = VALUES(lastseen),' + . ' logintime = 0,' + . ' lastboot = VALUES(lastboot),' + . ' realcores = VALUES(realcores),' + . ' mbram = VALUES(mbram),' + . ' kvmstate = VALUES(kvmstate),' + . ' cpumodel = VALUES(cpumodel),' + . ' id44mb = VALUES(id44mb),' + . ' data = VALUES(data),' + . " hostname = If(VALUES(hostname) = '', hostname, VALUES(hostname))", array( + 'uuid' => $uuid, + 'macaddr' => $macaddr, + 'clientip' => $ip, + 'firstseen' => $NOW, + 'lastseen' => $NOW, + 'lastboot' => $NOW - $uptime, + 'realcores' => $realcores, + 'mbram' => $mbram, + 'kvmstate' => $kvmstate, + 'cpumodel' => $cpumodel, + 'id44mb' => $id44mb, + 'data' => $data, + 'hostname' => $hostname, + )); + } + die("OK.\n"); +} + +/* + * Normal logging + */ + +if (!isset($_POST['description'])) die('Missing options..'); + $description = $_POST['description']; $longdesc = ''; if (isset($_POST['longdesc'])) $longdesc = $_POST['longdesc']; diff --git a/lang/de/templates/statistics/clientlist.json b/lang/de/templates/statistics/clientlist.json new file mode 100644 index 00000000..14e21549 --- /dev/null +++ b/lang/de/templates/statistics/clientlist.json @@ -0,0 +1,11 @@ +{ + "lang_address": "Adresse", + "lang_clientList": "Liste ausgew\u00e4hlter Rechner", + "lang_cpuModel": "CPU", + "lang_gbRam": "RAM", + "lang_kvmSupport": "64\u2009Bit G\u00e4ste", + "lang_lastBoot": "Letzter boot", + "lang_machine": "Rechner", + "lang_realCores": "Kerne", + "lang_tmpGb": "HDD-Temp" +} \ No newline at end of file diff --git a/lang/de/templates/statistics/cpumodels.json b/lang/de/templates/statistics/cpumodels.json new file mode 100644 index 00000000..fc7cf503 --- /dev/null +++ b/lang/de/templates/statistics/cpumodels.json @@ -0,0 +1,6 @@ +{ + "lang_cpuCores": "Kerne", + "lang_cpuCount": "Anzahl", + "lang_cpuName": "CPU Typ", + "lang_cpuStats": "Prozessoren" +} \ No newline at end of file diff --git a/lang/de/templates/statistics/id44.json b/lang/de/templates/statistics/id44.json new file mode 100644 index 00000000..e2660f20 --- /dev/null +++ b/lang/de/templates/statistics/id44.json @@ -0,0 +1,5 @@ +{ + "lang_machineCount": "Anzahl", + "lang_partitionSize": "Gr\u00f6\u00dfe", + "lang_tempPartStats": "Tempor\u00e4re Partition" +} \ No newline at end of file diff --git a/lang/de/templates/statistics/kvmstate.json b/lang/de/templates/statistics/kvmstate.json new file mode 100644 index 00000000..3e8f1a96 --- /dev/null +++ b/lang/de/templates/statistics/kvmstate.json @@ -0,0 +1,5 @@ +{ + "lang_kvmState": "Status", + "lang_kvmStats": "64\u2009Bit Gast-Support", + "lang_machineCount": "Anzahl" +} \ No newline at end of file diff --git a/lang/de/templates/statistics/machine-hdds.json b/lang/de/templates/statistics/machine-hdds.json new file mode 100644 index 00000000..731376e3 --- /dev/null +++ b/lang/de/templates/statistics/machine-hdds.json @@ -0,0 +1,7 @@ +{ + "lang_hdds": "Festplatten", + "lang_partName": "Name", + "lang_partSize": "Gr\u00f6\u00dfe", + "lang_partType": "Typ", + "lang_total": "Gesamt" +} \ No newline at end of file diff --git a/lang/de/templates/statistics/machine-main.json b/lang/de/templates/statistics/machine-main.json new file mode 100644 index 00000000..751ff8c7 --- /dev/null +++ b/lang/de/templates/statistics/machine-main.json @@ -0,0 +1,18 @@ +{ + "lang_64bitSupport": "64\u2009Bit Gast-Support", + "lang_cores": "Kerne", + "lang_cpuModel": "CPU-Modell", + "lang_firstSeen": "Erste Aktivit\u00e4t", + "lang_hardwareSummary": "Hardware", + "lang_hostname": "Hostname", + "lang_ip": "IP-Adresse", + "lang_lastBoot": "Letzter Boot", + "lang_lastSeen": "Letzte Aktivit\u00e4t", + "lang_macAddr": "MAC-Adresse", + "lang_machineSummary": "Zusammenfassung", + "lang_ram": "Arbeitsspeicher", + "lang_sockets": "Sockel", + "lang_tempPart": "Temp. Partition", + "lang_uuid": "UUID", + "lang_virtualCores": "Virtuelle Kerne" +} \ No newline at end of file diff --git a/lang/de/templates/statistics/memory.json b/lang/de/templates/statistics/memory.json new file mode 100644 index 00000000..b5fdac89 --- /dev/null +++ b/lang/de/templates/statistics/memory.json @@ -0,0 +1,5 @@ +{ + "lang_machineCount": "Anzahl", + "lang_memoryStats": "Arbeitsspeicher", + "lang_ramSize": "Gr\u00f6\u00dfe" +} \ No newline at end of file diff --git a/lang/en/templates/statistics/clientlist.json b/lang/en/templates/statistics/clientlist.json new file mode 100644 index 00000000..09ea04fd --- /dev/null +++ b/lang/en/templates/statistics/clientlist.json @@ -0,0 +1,11 @@ +{ + "lang_address": "Address", + "lang_clientList": "List of selected machines", + "lang_cpuModel": "CPU", + "lang_gbRam": "RAM", + "lang_kvmSupport": "64\u2009Bit guests", + "lang_lastBoot": "Last boot", + "lang_machine": "Machine", + "lang_realCores": "Cores", + "lang_tmpGb": "HDD temp" +} \ No newline at end of file diff --git a/lang/en/templates/statistics/cpumodels.json b/lang/en/templates/statistics/cpumodels.json new file mode 100644 index 00000000..c73cbb22 --- /dev/null +++ b/lang/en/templates/statistics/cpumodels.json @@ -0,0 +1,6 @@ +{ + "lang_cpuCores": "Cores", + "lang_cpuCount": "Count", + "lang_cpuName": "CPU type", + "lang_cpuStats": "Processors" +} \ No newline at end of file diff --git a/lang/en/templates/statistics/id44.json b/lang/en/templates/statistics/id44.json new file mode 100644 index 00000000..0d0081a8 --- /dev/null +++ b/lang/en/templates/statistics/id44.json @@ -0,0 +1,5 @@ +{ + "lang_machineCount": "Count", + "lang_partitionSize": "Size", + "lang_tempPartStats": "Temporary partition" +} \ No newline at end of file diff --git a/lang/en/templates/statistics/kvmstate.json b/lang/en/templates/statistics/kvmstate.json new file mode 100644 index 00000000..b4846473 --- /dev/null +++ b/lang/en/templates/statistics/kvmstate.json @@ -0,0 +1,5 @@ +{ + "lang_kvmState": "State", + "lang_kvmStats": "64\u2009Bit guest support", + "lang_machineCount": "Count" +} \ No newline at end of file diff --git a/lang/en/templates/statistics/machine-hdds.json b/lang/en/templates/statistics/machine-hdds.json new file mode 100644 index 00000000..85afe2e8 --- /dev/null +++ b/lang/en/templates/statistics/machine-hdds.json @@ -0,0 +1,7 @@ +{ + "lang_hdds": "Hard disk drives", + "lang_partName": "Name", + "lang_partSize": "Size", + "lang_partType": "Type", + "lang_total": "Total" +} \ No newline at end of file diff --git a/lang/en/templates/statistics/machine-main.json b/lang/en/templates/statistics/machine-main.json new file mode 100644 index 00000000..2aa3b9ba --- /dev/null +++ b/lang/en/templates/statistics/machine-main.json @@ -0,0 +1,18 @@ +{ + "lang_64bitSupport": "64\u2009Bit guest support", + "lang_cores": "Cores", + "lang_cpuModel": "CPU model", + "lang_firstSeen": "First seen", + "lang_hardwareSummary": "Hardware", + "lang_hostname": "Hostname", + "lang_ip": "IP address", + "lang_lastBoot": "Last boot", + "lang_lastSeen": "Last activity", + "lang_macAddr": "MAC address", + "lang_machineSummary": "Summary", + "lang_ram": "Memory", + "lang_sockets": "Sockets", + "lang_tempPart": "Temp. partition", + "lang_uuid": "UUID", + "lang_virtualCores": "Virtual cores" +} \ No newline at end of file diff --git a/lang/en/templates/statistics/memory.json b/lang/en/templates/statistics/memory.json new file mode 100644 index 00000000..decdd021 --- /dev/null +++ b/lang/en/templates/statistics/memory.json @@ -0,0 +1,5 @@ +{ + "lang_machineCount": "Count", + "lang_memoryStats": "Memory", + "lang_ramSize": "Size" +} \ No newline at end of file diff --git a/lang/pt/templates/statistics/clientlist.json b/lang/pt/templates/statistics/clientlist.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/lang/pt/templates/statistics/clientlist.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/lang/pt/templates/statistics/cpumodels.json b/lang/pt/templates/statistics/cpumodels.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/lang/pt/templates/statistics/cpumodels.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/lang/pt/templates/statistics/id44.json b/lang/pt/templates/statistics/id44.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/lang/pt/templates/statistics/id44.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/lang/pt/templates/statistics/kvmstate.json b/lang/pt/templates/statistics/kvmstate.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/lang/pt/templates/statistics/kvmstate.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/lang/pt/templates/statistics/machine-hdds.json b/lang/pt/templates/statistics/machine-hdds.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/lang/pt/templates/statistics/machine-hdds.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/lang/pt/templates/statistics/machine-main.json b/lang/pt/templates/statistics/machine-main.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/lang/pt/templates/statistics/machine-main.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/lang/pt/templates/statistics/memory.json b/lang/pt/templates/statistics/memory.json new file mode 100644 index 00000000..c44dc44f --- /dev/null +++ b/lang/pt/templates/statistics/memory.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/modules/statistics.inc.php b/modules/statistics.inc.php new file mode 100644 index 00000000..c459528f --- /dev/null +++ b/modules/statistics.inc.php @@ -0,0 +1,386 @@ +showMachine($uuid); + return; + } + $filter = Request::get('filter', false, 'string'); + if ($filter !== false) { + $argument = Request::get('argument', false, 'string'); + $this->showMachineList($filter, $argument); + return; + } + Render::addScriptBottom('chart.min'); + Render::openTag('div', array('class' => 'row')); + $this->showCpuModels(); + $this->showMemory(); + $this->showKvmState(); + $this->showId44(); + Render::closeTag('div'); + } + + private function showCpuModels() + { + global $STATS_COLORS; + $res = Database::simpleQuery("SELECT cpumodel, realcores, Count(*) AS `count` FROM machine GROUP BY cpumodel ORDER BY `count` DESC, cpumodel ASC"); + $lines = array(); + $json = array(); + $id = 0; + $total = 0; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + settype($row['count'], 'integer'); + $total += $row['count']; + $row['id'] = 'cpuid' . $id; + $row['urlcpumodel'] = urlencode($row['cpumodel']); + $lines[] = $row; + $json[] = array( + 'color' => $STATS_COLORS[$id % count($STATS_COLORS)], + 'label' => 'cpuid' . $id, + 'value' => $row['count'] + ); + ++$id; + } + $cap = ceil($total * 0.95); + $total = 0; + $id = 0; + foreach ($json as $entry) { + $total += $entry['value']; + if ($total <= $cap) $id++; + } + $json = array_slice($json, 0, $id); + Render::addTemplate('statistics/cpumodels', array('rows' => $lines, 'json' => json_encode($json))); + } + + private function showMemory() + { + global $STATS_COLORS, $SIZE_RAM; + $res = Database::simpleQuery("SELECT mbram, Count(*) AS `count` FROM machine GROUP BY mbram"); + $lines = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $gb = ceil($row['mbram'] / 1024); + for ($i = 1; $i < count($SIZE_RAM); ++$i) { + if ($SIZE_RAM[$i] < $gb) continue; + if ($SIZE_RAM[$i] - $gb >= $gb - $SIZE_RAM[$i-1]) --$i; + $gb = $SIZE_RAM[$i]; + break; + } + if (isset($lines[$gb])) { + $lines[$gb] += $row['count']; + } else { + $lines[$gb] = $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); + $json[] = array( + 'color' => $STATS_COLORS[$id % count($STATS_COLORS)], + 'label' => (string)$k, + 'value' => $v + ); + ++$id; + } + $data['json'] = json_encode($json); + Render::addTemplate('statistics/memory', $data); + } + + private function showKvmState() + { + $colors = array('UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#4d4'); + $res = Database::simpleQuery("SELECT kvmstate, Count(*) AS `count` FROM machine GROUP BY kvmstate ORDER BY `count` DESC"); + $lines = array(); + $json = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $lines[] = $row; + $json[] = array( + 'color' => isset($colors[$row['kvmstate']]) ? $colors[$row['kvmstate']] : '#000', + 'label' => $row['kvmstate'], + 'value' => $row['count'] + ); + } + Render::addTemplate('statistics/kvmstate', array('rows' => $lines, 'json' => json_encode($json))); + } + + private function showId44() + { + global $STATS_COLORS, $SIZE_ID44; + $res = Database::simpleQuery("SELECT id44mb, Count(*) AS `count` FROM machine GROUP BY id44mb"); + $lines = array(); + $total = 0; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $total += $row['count']; + $gb = ceil($row['id44mb'] / 1024); + for ($i = 1; $i < count($SIZE_ID44); ++$i) { + if ($SIZE_ID44[$i] < $gb) continue; + if ($SIZE_ID44[$i] - $gb >= $gb - $SIZE_ID44[$i-1]) --$i; + $gb = $SIZE_ID44[$i]; + break; + } + if (isset($lines[$gb])) { + $lines[$gb] += $row['count']; + } else { + $lines[$gb] = $row['count']; + } + } + asort($lines); + $data = array('rows' => array()); + $json = array(); + $id = 1; + $cap = ceil($total * 0.95); + $total = 0; + foreach (array_reverse($lines, true) as $k => $v) { + $data['rows'][] = array('gb' => $k, 'count' => $v); + $total += $v; + if ($total <= $cap) { + if ($k === 0) { + $color = $STATS_COLORS[0]; + } else { + $color = $STATS_COLORS[$id % count($STATS_COLORS)]; + } + $json[] = array( + 'color' => $color, + 'label' => (string)$k, + 'value' => $v + ); + ++$id; + } + } + $data['json'] = json_encode($json); + Render::addTemplate('statistics/id44', $data); + } + + private function showMachineList($filter, $argument) + { + global $SIZE_RAM, $SIZE_ID44; + $filters = array('cpumodel', 'realcores', 'kvmstate', 'clientip', 'macaddr', 'machineuuid'); + if (in_array($filter, $filters)) { + // Simple filters mapping into db + $where = " $filter = :argument"; + $args = array('argument' => $argument); + } elseif ($filter === 'gbram') { + // Memory by rounded GB + $lower = floor($this->findBestValue($SIZE_RAM, $argument, false) * 1024 - 100); + $upper = ceil($this->findBestValue($SIZE_RAM, $argument, true) * 1024 + 100); + $where = " mbram BETWEEN $lower AND $upper"; + $args = array(); + } elseif ($filter === 'hddgb') { + // HDD by rounded GB + $lower = floor($this->findBestValue($SIZE_ID44, $argument, false) * 1024 - 100); + $upper = ceil($this->findBestValue($SIZE_ID44, $argument, true) * 1024 + 100); + $where = " id44mb BETWEEN $lower AND $upper"; + $args = array(); + } elseif ($filter === 'subnet') { + $argument = preg_replace('/[^0-9\.:]/', '', $argument); + $where = " clientip LIKE '$argument%'"; + $args = array(); + } else { + Message::addError('invalid-filter'); + return; + } + $res = Database::simpleQuery("SELECT machineuuid, macaddr, clientip, firstseen, lastseen," + . " logintime, lastboot, realcores, mbram, kvmstate, cpumodel, id44mb, hostname FROM machine" + . " WHERE $where ORDER BY lastseen DESC, clientip ASC", $args); + $rows = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['firstseen'] = date('d.m.Y H:i', $row['firstseen']); + $row['lastseen'] = date('d.m. H:i', $row['lastseen']); + $row['lastboot'] = date('d.m. H:i', $row['lastboot']); + $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); // Trial and error until we got "expected" rounding.. + $row['gbtmp'] = round($row['id44mb'] / 1024); + $octets = explode('.', $row['clientip']); + if (count($octets) === 4) { + $row['subnet'] = "$octets[0].$octets[1].$octets[2]."; + $row['lastoctet'] = $octets[3]; + } + $row['ramclass'] = $this->ramColorClass($row['mbram']); + $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']); + $row['hddclass'] = $this->hddColorClass($row['gbtmp']); + if (empty($row['hostname'])) $row['hostname'] = $row['clientip']; + $rows[] = $row; + } + Render::addTemplate('statistics/clientlist', array('rows' => $rows, 'filter' => $filter, 'argument' => $argument)); + } + + private function ramColorClass($mb) + { + if ($mb < 1500) + return 'danger'; + if ($mb < 2500) + return 'warning'; + return ''; + } + + private function kvmColorClass($state) + { + if ($state === 'DISABLED') + return 'danger'; + if ($state === 'UNKNOWN' || $state === 'UNSUPPORTED') + return 'warning'; + return ''; + } + + private function hddColorClass($gb) + { + if ($gb < 7) + return 'danger'; + if ($gb < 25) + return 'warning'; + return ''; + } + + private function findBestValue($array, $value, $up) + { + $best = 0; + for ($i = 0; $i < count($array); ++$i) { + if (abs($array[$i] - $value) < abs($array[$best] - $value)) { + $best = $i; + } + } + if (!$up && $best === 0) { + return $array[0]; + } + if ($up && $best + 1 === count($array)) { + return $array[$best]; + } + if ($up) { + return ($array[$best] + $array[$best + 1]) / 2; + } + return ($array[$best] + $array[$best - 1]) / 2; + } + + private function showMachine($uuid) + { + $row = Database::queryFirst("SELECT machineuuid, macaddr, clientip, firstseen, lastseen, logintime, lastboot," + . " mbram, kvmstate, cpumodel, id44mb, data, hostname FROM machine WHERE machineuuid = :uuid", + array('uuid' => $uuid)); + // Mangle fields + $row['firstseen'] = date('d.m.Y H:i', $row['firstseen']); + $row['lastseen'] = date('d.m.Y H:i', $row['lastseen']); + $row['lastboot'] = date('d.m.Y H:i', $row['lastboot']); + $row['gbram'] = round(round($row['mbram'] / 500) / 2, 1); + $row['gbtmp'] = round($row['id44mb'] / 1024); + $row['ramclass'] = $this->ramColorClass($row['mbram']); + $row['kvmclass'] = $this->kvmColorClass($row['kvmstate']); + $row['hddclass'] = $this->hddColorClass($row['gbtmp']); + // Parse the giant blob of data + $hdds = array(); + if (preg_match_all('/##### ([^#]+) #+$(.*?)^#####/ims', $row['data'], $out, PREG_SET_ORDER)) { + foreach ($out as $section) { + if ($section[1] === 'CPU') { + $this->parseCpu($row, $section[2]); + } + if ($section[1] === 'Partition tables') { + $this->parseHdd($hdds, $section[2]); + } + } + } + // Throw output at user + Render::addTemplate('statistics/machine-main', $row); + // Any hdds? + if (!empty($hdds['hdds'])) { + Render::addScriptBottom('chart.min'); + Render::addTemplate('statistics/machine-hdds', $hdds); + } + } + + private function parseCpu(&$row, $data) + { + if (0 >= preg_match_all('/^(.+):\s+(\d+)$/im', $data, $out, PREG_SET_ORDER)) return; + foreach ($out as $entry) { + $row[str_replace(' ', '', $entry[1])] = $entry[2]; + } + } + + private function parseHdd(&$row, $data) + { + $hdds = array(); + // Could have more than one partition - linear scan + $lines = preg_split("/[\r\n]+/", $data); + $dev = false; + $i = 0; + foreach ($lines as $line) { + if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) { + // disk total size and name + unset($hdd); + $unit = 0; + $hdd = array( + 'devid' => 'devid-' . ++$i, + 'dev' => $out[1], + 'size' => round($out[2] / (1024 * 1024 * 1024)), + 'used' => 0, + 'partitions' => array(), + 'json' => array(), + ); + $hdds[] = &$hdd; + } elseif (preg_match('/^Units =.*= (\d+) bytes/i', $line, $out)) { + // Unit for start and end + $unit = $out[1] / (1024 * 1024); // Convert so that multiplying by unit yields MiB + } else if (isset($hdd) && $unit !== 0 && preg_match(',^/dev/(\S+)\s+.*\s(\d+)\s+(\d+)\s+\d+\s+([0-9a-f]+)\s+(.*)$,i', $line, $out)) { + // Some partition + $type = (string)$out[4]; + if ($type === '5') continue; + $partsize = round(($out[3] - $out[2]) * $unit); + $hdd['partitions'][] = array( + 'id' => $out[1], + 'name' => $out[1], + 'size' => round($partsize / 1024), + 'type' => ($type === '44' ? 'OpenSLX' : $out[5]), + ); + $hdd['json'][] = array( + 'label' => $out[1], + 'value' => $partsize, + 'color' => ($type === '44' ? '#4d4' : ($type === '82' ? '#48f' : '#e55')), + ); + $hdd['used'] += $partsize; + } + } + unset($hdd); + $i = 0; + foreach ($hdds as &$hdd) { + $hdd['used'] = round($hdd['used'] / 1024); + $free = $hdd['size'] - $hdd['used']; + if ($free > 5) { + $hdd['partitions'][] = array( + 'id' => 'free-id-' . $i, + 'name' => Dictionary::translate('unused'), + 'size' => $free, + 'type' => '-', + ); + $hdd['json'][] = array( + 'label' => 'free-id-' . $i, + 'value' => $free * 1024, + 'color' => '#aaa', + ); + ++$i; + } + $hdd['json'] = json_encode($hdd['json']); + } + unset($hdd); + $row['hdds'] = &$hdds; + } + +} diff --git a/script/chart.min.js b/script/chart.min.js new file mode 100644 index 00000000..3a0a2c87 --- /dev/null +++ b/script/chart.min.js @@ -0,0 +1,11 @@ +/*! + * Chart.js + * http://chartjs.org/ + * Version: 1.0.2 + * + * Copyright 2015 Nick Downie + * Released under the MIT license + * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md + */ +(function(){"use strict";var t=this,i=t.Chart,e=function(t){this.canvas=t.canvas,this.ctx=t;var i=function(t,i){return t["offset"+i]?t["offset"+i]:document.defaultView.getComputedStyle(t).getPropertyValue(i)},e=this.width=i(t.canvas,"Width"),n=this.height=i(t.canvas,"Height");t.canvas.width=e,t.canvas.height=n;var e=this.width=t.canvas.width,n=this.height=t.canvas.height;return this.aspectRatio=this.width/this.height,s.retinaScale(this),this};e.defaults={global:{animation:!0,animationSteps:60,animationEasing:"easeOutQuart",showScale:!0,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleIntegersOnly:!0,scaleBeginAtZero:!1,scaleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",responsive:!1,maintainAspectRatio:!0,showTooltips:!0,customTooltips:!1,tooltipEvents:["mousemove","touchstart","touchmove","mouseout"],tooltipFillColor:"rgba(0,0,0,0.8)",tooltipFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontSize:14,tooltipFontStyle:"normal",tooltipFontColor:"#fff",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipTitleFontSize:14,tooltipTitleFontStyle:"bold",tooltipTitleFontColor:"#fff",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:8,tooltipCornerRadius:6,tooltipXOffset:10,tooltipTemplate:"<%if (label){%><%=label%>: <%}%><%= value %>",multiTooltipTemplate:"<%= value %>",multiTooltipKeyBackground:"#fff",onAnimationProgress:function(){},onAnimationComplete:function(){}}},e.types={};var s=e.helpers={},n=s.each=function(t,i,e){var s=Array.prototype.slice.call(arguments,3);if(t)if(t.length===+t.length){var n;for(n=0;n=0;s--){var n=t[s];if(i(n))return n}},s.inherits=function(t){var i=this,e=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return i.apply(this,arguments)},s=function(){this.constructor=e};return s.prototype=i.prototype,e.prototype=new s,e.extend=r,t&&a(e.prototype,t),e.__super__=i.prototype,e}),c=s.noop=function(){},u=s.uid=function(){var t=0;return function(){return"chart-"+t++}}(),d=s.warn=function(t){window.console&&"function"==typeof window.console.warn&&console.warn(t)},p=s.amd="function"==typeof define&&define.amd,f=s.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},g=s.max=function(t){return Math.max.apply(Math,t)},m=s.min=function(t){return Math.min.apply(Math,t)},v=(s.cap=function(t,i,e){if(f(i)){if(t>i)return i}else if(f(e)&&e>t)return e;return t},s.getDecimalPlaces=function(t){return t%1!==0&&f(t)?t.toString().split(".")[1].length:0}),S=s.radians=function(t){return t*(Math.PI/180)},x=(s.getAngleFromPoint=function(t,i){var e=i.x-t.x,s=i.y-t.y,n=Math.sqrt(e*e+s*s),o=2*Math.PI+Math.atan2(s,e);return 0>e&&0>s&&(o+=2*Math.PI),{angle:o,distance:n}},s.aliasPixel=function(t){return t%2===0?0:.5}),y=(s.splineCurve=function(t,i,e,s){var n=Math.sqrt(Math.pow(i.x-t.x,2)+Math.pow(i.y-t.y,2)),o=Math.sqrt(Math.pow(e.x-i.x,2)+Math.pow(e.y-i.y,2)),a=s*n/(n+o),h=s*o/(n+o);return{inner:{x:i.x-a*(e.x-t.x),y:i.y-a*(e.y-t.y)},outer:{x:i.x+h*(e.x-t.x),y:i.y+h*(e.y-t.y)}}},s.calculateOrderOfMagnitude=function(t){return Math.floor(Math.log(t)/Math.LN10)}),C=(s.calculateScaleRange=function(t,i,e,s,n){var o=2,a=Math.floor(i/(1.5*e)),h=o>=a,l=g(t),r=m(t);l===r&&(l+=.5,r>=.5&&!s?r-=.5:l+=.5);for(var c=Math.abs(l-r),u=y(c),d=Math.ceil(l/(1*Math.pow(10,u)))*Math.pow(10,u),p=s?0:Math.floor(r/(1*Math.pow(10,u)))*Math.pow(10,u),f=d-p,v=Math.pow(10,u),S=Math.round(f/v);(S>a||a>2*S)&&!h;)if(S>a)v*=2,S=Math.round(f/v),S%1!==0&&(h=!0);else if(n&&u>=0){if(v/2%1!==0)break;v/=2,S=Math.round(f/v)}else v/=2,S=Math.round(f/v);return h&&(S=o,v=f/S),{steps:S,stepValue:v,min:p,max:p+S*v}},s.template=function(t,i){function e(t,i){var e=/\W/.test(t)?new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+t.replace(/[\r\t\n]/g," ").split("<%").join(" ").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split(" ").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');"):s[t]=s[t];return i?e(i):e}if(t instanceof Function)return t(i);var s={};return e(t,i)}),w=(s.generateLabels=function(t,i,e,s){var o=new Array(i);return labelTemplateString&&n(o,function(i,n){o[n]=C(t,{value:e+s*(n+1)})}),o},s.easingEffects={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-1*t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-0.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return 1*((t=t/1-1)*t*t+1)},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-1*((t=t/1-1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-0.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return 1*(t/=1)*t*t*t*t},easeOutQuint:function(t){return 1*((t=t/1-1)*t*t*t*t+1)},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return-1*Math.cos(t/1*(Math.PI/2))+1},easeOutSine:function(t){return 1*Math.sin(t/1*(Math.PI/2))},easeInOutSine:function(t){return-0.5*(Math.cos(Math.PI*t/1)-1)},easeInExpo:function(t){return 0===t?1:1*Math.pow(2,10*(t/1-1))},easeOutExpo:function(t){return 1===t?1:1*(-Math.pow(2,-10*t/1)+1)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(-Math.pow(2,-10*--t)+2)},easeInCirc:function(t){return t>=1?t:-1*(Math.sqrt(1-(t/=1)*t)-1)},easeOutCirc:function(t){return 1*Math.sqrt(1-(t=t/1-1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-0.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var i=1.70158,e=0,s=1;return 0===t?0:1==(t/=1)?1:(e||(e=.3),st?-.5*s*Math.pow(2,10*(t-=1))*Math.sin(2*(1*t-i)*Math.PI/e):s*Math.pow(2,-10*(t-=1))*Math.sin(2*(1*t-i)*Math.PI/e)*.5+1)},easeInBack:function(t){var i=1.70158;return 1*(t/=1)*t*((i+1)*t-i)},easeOutBack:function(t){var i=1.70158;return 1*((t=t/1-1)*t*((i+1)*t+i)+1)},easeInOutBack:function(t){var i=1.70158;return(t/=.5)<1?.5*t*t*(((i*=1.525)+1)*t-i):.5*((t-=2)*t*(((i*=1.525)+1)*t+i)+2)},easeInBounce:function(t){return 1-w.easeOutBounce(1-t)},easeOutBounce:function(t){return(t/=1)<1/2.75?7.5625*t*t:2/2.75>t?1*(7.5625*(t-=1.5/2.75)*t+.75):2.5/2.75>t?1*(7.5625*(t-=2.25/2.75)*t+.9375):1*(7.5625*(t-=2.625/2.75)*t+.984375)},easeInOutBounce:function(t){return.5>t?.5*w.easeInBounce(2*t):.5*w.easeOutBounce(2*t-1)+.5}}),b=s.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),P=s.cancelAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t,1e3/60)}}(),L=(s.animationLoop=function(t,i,e,s,n,o){var a=0,h=w[e]||w.linear,l=function(){a++;var e=a/i,r=h(e);t.call(o,r,e,a),s.call(o,r,e),i>a?o.animationFrame=b(l):n.apply(o)};b(l)},s.getRelativePosition=function(t){var i,e,s=t.originalEvent||t,n=t.currentTarget||t.srcElement,o=n.getBoundingClientRect();return s.touches?(i=s.touches[0].clientX-o.left,e=s.touches[0].clientY-o.top):(i=s.clientX-o.left,e=s.clientY-o.top),{x:i,y:e}},s.addEvent=function(t,i,e){t.addEventListener?t.addEventListener(i,e):t.attachEvent?t.attachEvent("on"+i,e):t["on"+i]=e}),k=s.removeEvent=function(t,i,e){t.removeEventListener?t.removeEventListener(i,e,!1):t.detachEvent?t.detachEvent("on"+i,e):t["on"+i]=c},F=(s.bindEvents=function(t,i,e){t.events||(t.events={}),n(i,function(i){t.events[i]=function(){e.apply(t,arguments)},L(t.chart.canvas,i,t.events[i])})},s.unbindEvents=function(t,i){n(i,function(i,e){k(t.chart.canvas,e,i)})}),R=s.getMaximumWidth=function(t){var i=t.parentNode;return i.clientWidth},T=s.getMaximumHeight=function(t){var i=t.parentNode;return i.clientHeight},A=(s.getMaximumSize=s.getMaximumWidth,s.retinaScale=function(t){var i=t.ctx,e=t.canvas.width,s=t.canvas.height;window.devicePixelRatio&&(i.canvas.style.width=e+"px",i.canvas.style.height=s+"px",i.canvas.height=s*window.devicePixelRatio,i.canvas.width=e*window.devicePixelRatio,i.scale(window.devicePixelRatio,window.devicePixelRatio))}),M=s.clear=function(t){t.ctx.clearRect(0,0,t.width,t.height)},W=s.fontString=function(t,i,e){return i+" "+t+"px "+e},z=s.longestText=function(t,i,e){t.font=i;var s=0;return n(e,function(i){var e=t.measureText(i).width;s=e>s?e:s}),s},B=s.drawRoundedRectangle=function(t,i,e,s,n,o){t.beginPath(),t.moveTo(i+o,e),t.lineTo(i+s-o,e),t.quadraticCurveTo(i+s,e,i+s,e+o),t.lineTo(i+s,e+n-o),t.quadraticCurveTo(i+s,e+n,i+s-o,e+n),t.lineTo(i+o,e+n),t.quadraticCurveTo(i,e+n,i,e+n-o),t.lineTo(i,e+o),t.quadraticCurveTo(i,e,i+o,e),t.closePath()};e.instances={},e.Type=function(t,i,s){this.options=i,this.chart=s,this.id=u(),e.instances[this.id]=this,i.responsive&&this.resize(),this.initialize.call(this,t)},a(e.Type.prototype,{initialize:function(){return this},clear:function(){return M(this.chart),this},stop:function(){return P(this.animationFrame),this},resize:function(t){this.stop();var i=this.chart.canvas,e=R(this.chart.canvas),s=this.options.maintainAspectRatio?e/this.chart.aspectRatio:T(this.chart.canvas);return i.width=this.chart.width=e,i.height=this.chart.height=s,A(this.chart),"function"==typeof t&&t.apply(this,Array.prototype.slice.call(arguments,1)),this},reflow:c,render:function(t){return t&&this.reflow(),this.options.animation&&!t?s.animationLoop(this.draw,this.options.animationSteps,this.options.animationEasing,this.options.onAnimationProgress,this.options.onAnimationComplete,this):(this.draw(),this.options.onAnimationComplete.call(this)),this},generateLegend:function(){return C(this.options.legendTemplate,this)},destroy:function(){this.clear(),F(this,this.events);var t=this.chart.canvas;t.width=this.chart.width,t.height=this.chart.height,t.style.removeProperty?(t.style.removeProperty("width"),t.style.removeProperty("height")):(t.style.removeAttribute("width"),t.style.removeAttribute("height")),delete e.instances[this.id]},showTooltip:function(t,i){"undefined"==typeof this.activeElements&&(this.activeElements=[]);var o=function(t){var i=!1;return t.length!==this.activeElements.length?i=!0:(n(t,function(t,e){t!==this.activeElements[e]&&(i=!0)},this),i)}.call(this,t);if(o||i){if(this.activeElements=t,this.draw(),this.options.customTooltips&&this.options.customTooltips(!1),t.length>0)if(this.datasets&&this.datasets.length>1){for(var a,h,r=this.datasets.length-1;r>=0&&(a=this.datasets[r].points||this.datasets[r].bars||this.datasets[r].segments,h=l(a,t[0]),-1===h);r--);var c=[],u=[],d=function(){var t,i,e,n,o,a=[],l=[],r=[];return s.each(this.datasets,function(i){t=i.points||i.bars||i.segments,t[h]&&t[h].hasValue()&&a.push(t[h])}),s.each(a,function(t){l.push(t.x),r.push(t.y),c.push(s.template(this.options.multiTooltipTemplate,t)),u.push({fill:t._saved.fillColor||t.fillColor,stroke:t._saved.strokeColor||t.strokeColor})},this),o=m(r),e=g(r),n=m(l),i=g(l),{x:n>this.chart.width/2?n:i,y:(o+e)/2}}.call(this,h);new e.MultiTooltip({x:d.x,y:d.y,xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,xOffset:this.options.tooltipXOffset,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,titleTextColor:this.options.tooltipTitleFontColor,titleFontFamily:this.options.tooltipTitleFontFamily,titleFontStyle:this.options.tooltipTitleFontStyle,titleFontSize:this.options.tooltipTitleFontSize,cornerRadius:this.options.tooltipCornerRadius,labels:c,legendColors:u,legendColorBackground:this.options.multiTooltipKeyBackground,title:t[0].label,chart:this.chart,ctx:this.chart.ctx,custom:this.options.customTooltips}).draw()}else n(t,function(t){var i=t.tooltipPosition();new e.Tooltip({x:Math.round(i.x),y:Math.round(i.y),xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,caretHeight:this.options.tooltipCaretSize,cornerRadius:this.options.tooltipCornerRadius,text:C(this.options.tooltipTemplate,t),chart:this.chart,custom:this.options.customTooltips}).draw()},this);return this}},toBase64Image:function(){return this.chart.canvas.toDataURL.apply(this.chart.canvas,arguments)}}),e.Type.extend=function(t){var i=this,s=function(){return i.apply(this,arguments)};if(s.prototype=o(i.prototype),a(s.prototype,t),s.extend=e.Type.extend,t.name||i.prototype.name){var n=t.name||i.prototype.name,l=e.defaults[i.prototype.name]?o(e.defaults[i.prototype.name]):{};e.defaults[n]=a(l,t.defaults),e.types[n]=s,e.prototype[n]=function(t,i){var o=h(e.defaults.global,e.defaults[n],i||{});return new s(t,o,this)}}else d("Name not provided for this chart, so it hasn't been registered");return i},e.Element=function(t){a(this,t),this.initialize.apply(this,arguments),this.save()},a(e.Element.prototype,{initialize:function(){},restore:function(t){return t?n(t,function(t){this[t]=this._saved[t]},this):a(this,this._saved),this},save:function(){return this._saved=o(this),delete this._saved._saved,this},update:function(t){return n(t,function(t,i){this._saved[i]=this[i],this[i]=t},this),this},transition:function(t,i){return n(t,function(t,e){this[e]=(t-this._saved[e])*i+this._saved[e]},this),this},tooltipPosition:function(){return{x:this.x,y:this.y}},hasValue:function(){return f(this.value)}}),e.Element.extend=r,e.Point=e.Element.extend({display:!0,inRange:function(t,i){var e=this.hitDetectionRadius+this.radius;return Math.pow(t-this.x,2)+Math.pow(i-this.y,2)=this.startAngle&&e.angle<=this.endAngle,o=e.distance>=this.innerRadius&&e.distance<=this.outerRadius;return n&&o},tooltipPosition:function(){var t=this.startAngle+(this.endAngle-this.startAngle)/2,i=(this.outerRadius-this.innerRadius)/2+this.innerRadius;return{x:this.x+Math.cos(t)*i,y:this.y+Math.sin(t)*i}},draw:function(t){var i=this.ctx;i.beginPath(),i.arc(this.x,this.y,this.outerRadius,this.startAngle,this.endAngle),i.arc(this.x,this.y,this.innerRadius,this.endAngle,this.startAngle,!0),i.closePath(),i.strokeStyle=this.strokeColor,i.lineWidth=this.strokeWidth,i.fillStyle=this.fillColor,i.fill(),i.lineJoin="bevel",this.showStroke&&i.stroke()}}),e.Rectangle=e.Element.extend({draw:function(){var t=this.ctx,i=this.width/2,e=this.x-i,s=this.x+i,n=this.base-(this.base-this.y),o=this.strokeWidth/2;this.showStroke&&(e+=o,s-=o,n+=o),t.beginPath(),t.fillStyle=this.fillColor,t.strokeStyle=this.strokeColor,t.lineWidth=this.strokeWidth,t.moveTo(e,this.base),t.lineTo(e,n),t.lineTo(s,n),t.lineTo(s,this.base),t.fill(),this.showStroke&&t.stroke()},height:function(){return this.base-this.y},inRange:function(t,i){return t>=this.x-this.width/2&&t<=this.x+this.width/2&&i>=this.y&&i<=this.base}}),e.Tooltip=e.Element.extend({draw:function(){var t=this.chart.ctx;t.font=W(this.fontSize,this.fontStyle,this.fontFamily),this.xAlign="center",this.yAlign="above";var i=this.caretPadding=2,e=t.measureText(this.text).width+2*this.xPadding,s=this.fontSize+2*this.yPadding,n=s+this.caretHeight+i;this.x+e/2>this.chart.width?this.xAlign="left":this.x-e/2<0&&(this.xAlign="right"),this.y-n<0&&(this.yAlign="below");var o=this.x-e/2,a=this.y-n;if(t.fillStyle=this.fillColor,this.custom)this.custom(this);else{switch(this.yAlign){case"above":t.beginPath(),t.moveTo(this.x,this.y-i),t.lineTo(this.x+this.caretHeight,this.y-(i+this.caretHeight)),t.lineTo(this.x-this.caretHeight,this.y-(i+this.caretHeight)),t.closePath(),t.fill();break;case"below":a=this.y+i+this.caretHeight,t.beginPath(),t.moveTo(this.x,this.y+i),t.lineTo(this.x+this.caretHeight,this.y+i+this.caretHeight),t.lineTo(this.x-this.caretHeight,this.y+i+this.caretHeight),t.closePath(),t.fill()}switch(this.xAlign){case"left":o=this.x-e+(this.cornerRadius+this.caretHeight);break;case"right":o=this.x-(this.cornerRadius+this.caretHeight)}B(t,o,a,e,s,this.cornerRadius),t.fill(),t.fillStyle=this.textColor,t.textAlign="center",t.textBaseline="middle",t.fillText(this.text,o+e/2,a+s/2)}}}),e.MultiTooltip=e.Element.extend({initialize:function(){this.font=W(this.fontSize,this.fontStyle,this.fontFamily),this.titleFont=W(this.titleFontSize,this.titleFontStyle,this.titleFontFamily),this.height=this.labels.length*this.fontSize+(this.labels.length-1)*(this.fontSize/2)+2*this.yPadding+1.5*this.titleFontSize,this.ctx.font=this.titleFont;var t=this.ctx.measureText(this.title).width,i=z(this.ctx,this.font,this.labels)+this.fontSize+3,e=g([i,t]);this.width=e+2*this.xPadding;var s=this.height/2;this.y-s<0?this.y=s:this.y+s>this.chart.height&&(this.y=this.chart.height-s),this.x>this.chart.width/2?this.x-=this.xOffset+this.width:this.x+=this.xOffset},getLineHeight:function(t){var i=this.y-this.height/2+this.yPadding,e=t-1;return 0===t?i+this.titleFontSize/2:i+(1.5*this.fontSize*e+this.fontSize/2)+1.5*this.titleFontSize},draw:function(){if(this.custom)this.custom(this);else{B(this.ctx,this.x,this.y-this.height/2,this.width,this.height,this.cornerRadius);var t=this.ctx;t.fillStyle=this.fillColor,t.fill(),t.closePath(),t.textAlign="left",t.textBaseline="middle",t.fillStyle=this.titleTextColor,t.font=this.titleFont,t.fillText(this.title,this.x+this.xPadding,this.getLineHeight(0)),t.font=this.font,s.each(this.labels,function(i,e){t.fillStyle=this.textColor,t.fillText(i,this.x+this.xPadding+this.fontSize+3,this.getLineHeight(e+1)),t.fillStyle=this.legendColorBackground,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize),t.fillStyle=this.legendColors[e].fill,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize)},this)}}}),e.Scale=e.Element.extend({initialize:function(){this.fit()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(C(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}));this.yLabelWidth=this.display&&this.showLabels?z(this.ctx,this.font,this.yLabels):0},addXLabel:function(t){this.xLabels.push(t),this.valuesCount++,this.fit()},removeXLabel:function(){this.xLabels.shift(),this.valuesCount--,this.fit()},fit:function(){this.startPoint=this.display?this.fontSize:0,this.endPoint=this.display?this.height-1.5*this.fontSize-5:this.height,this.startPoint+=this.padding,this.endPoint-=this.padding;var t,i=this.endPoint-this.startPoint;for(this.calculateYRange(i),this.buildYLabels(),this.calculateXLabelRotation();i>this.endPoint-this.startPoint;)i=this.endPoint-this.startPoint,t=this.yLabelWidth,this.calculateYRange(i),this.buildYLabels(),tthis.yLabelWidth+10?e/2:this.yLabelWidth+10,this.xLabelRotation=0,this.display){var n,o=z(this.ctx,this.font,this.xLabels);this.xLabelWidth=o;for(var a=Math.floor(this.calculateX(1)-this.calculateX(0))-6;this.xLabelWidth>a&&0===this.xLabelRotation||this.xLabelWidth>a&&this.xLabelRotation<=90&&this.xLabelRotation>0;)n=Math.cos(S(this.xLabelRotation)),t=n*e,i=n*s,t+this.fontSize/2>this.yLabelWidth+8&&(this.xScalePaddingLeft=t+this.fontSize/2),this.xScalePaddingRight=this.fontSize/2,this.xLabelRotation++,this.xLabelWidth=n*o;this.xLabelRotation>0&&(this.endPoint-=Math.sin(S(this.xLabelRotation))*o+3)}else this.xLabelWidth=0,this.xScalePaddingRight=this.padding,this.xScalePaddingLeft=this.padding},calculateYRange:c,drawingArea:function(){return this.startPoint-this.endPoint},calculateY:function(t){var i=this.drawingArea()/(this.min-this.max);return this.endPoint-i*(t-this.min)},calculateX:function(t){var i=(this.xLabelRotation>0,this.width-(this.xScalePaddingLeft+this.xScalePaddingRight)),e=i/Math.max(this.valuesCount-(this.offsetGridLines?0:1),1),s=e*t+this.xScalePaddingLeft;return this.offsetGridLines&&(s+=e/2),Math.round(s)},update:function(t){s.extend(this,t),this.fit()},draw:function(){var t=this.ctx,i=(this.endPoint-this.startPoint)/this.steps,e=Math.round(this.xScalePaddingLeft);this.display&&(t.fillStyle=this.textColor,t.font=this.font,n(this.yLabels,function(n,o){var a=this.endPoint-i*o,h=Math.round(a),l=this.showHorizontalLines;t.textAlign="right",t.textBaseline="middle",this.showLabels&&t.fillText(n,e-10,a),0!==o||l||(l=!0),l&&t.beginPath(),o>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),h+=s.aliasPixel(t.lineWidth),l&&(t.moveTo(e,h),t.lineTo(this.width,h),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(e-5,h),t.lineTo(e,h),t.stroke(),t.closePath()},this),n(this.xLabels,function(i,e){var s=this.calculateX(e)+x(this.lineWidth),n=this.calculateX(e-(this.offsetGridLines?.5:0))+x(this.lineWidth),o=this.xLabelRotation>0,a=this.showVerticalLines;0!==e||a||(a=!0),a&&t.beginPath(),e>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),a&&(t.moveTo(n,this.endPoint),t.lineTo(n,this.startPoint-3),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(n,this.endPoint),t.lineTo(n,this.endPoint+5),t.stroke(),t.closePath(),t.save(),t.translate(s,o?this.endPoint+12:this.endPoint+8),t.rotate(-1*S(this.xLabelRotation)),t.font=this.font,t.textAlign=o?"right":"center",t.textBaseline=o?"middle":"top",t.fillText(i,0,0),t.restore()},this))}}),e.RadialScale=e.Element.extend({initialize:function(){this.size=m([this.height,this.width]),this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2},calculateCenterOffset:function(t){var i=this.drawingArea/(this.max-this.min);return(t-this.min)*i},update:function(){this.lineArc?this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2:this.setScaleSize(),this.buildYLabels()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(C(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}))},getCircumference:function(){return 2*Math.PI/this.valuesCount},setScaleSize:function(){var t,i,e,s,n,o,a,h,l,r,c,u,d=m([this.height/2-this.pointLabelFontSize-5,this.width/2]),p=this.width,g=0;for(this.ctx.font=W(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),i=0;ip&&(p=t.x+s,n=i),t.x-sp&&(p=t.x+e,n=i):i>this.valuesCount/2&&t.x-e0){var s,n=e*(this.drawingArea/this.steps),o=this.yCenter-n;if(this.lineWidth>0)if(t.strokeStyle=this.lineColor,t.lineWidth=this.lineWidth,this.lineArc)t.beginPath(),t.arc(this.xCenter,this.yCenter,n,0,2*Math.PI),t.closePath(),t.stroke();else{t.beginPath();for(var a=0;a=0;i--){if(this.angleLineWidth>0){var e=this.getPointPosition(i,this.calculateCenterOffset(this.max));t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(e.x,e.y),t.stroke(),t.closePath()}var s=this.getPointPosition(i,this.calculateCenterOffset(this.max)+5);t.font=W(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),t.fillStyle=this.pointLabelFontColor;var o=this.labels.length,a=this.labels.length/2,h=a/2,l=h>i||i>o-h,r=i===h||i===o-h;t.textAlign=0===i?"center":i===a?"center":a>i?"left":"right",t.textBaseline=r?"middle":l?"bottom":"top",t.fillText(this.labels[i],s.x,s.y)}}}}}),s.addEvent(window,"resize",function(){var t;return function(){clearTimeout(t),t=setTimeout(function(){n(e.instances,function(t){t.options.responsive&&t.resize(t.render,!0)})},50)}}()),p?define(function(){return e}):"object"==typeof module&&module.exports&&(module.exports=e),t.Chart=e,e.noConflict=function(){return t.Chart=i,e}}).call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleBeginAtZero:!0,scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,scaleShowHorizontalLines:!0,scaleShowVerticalLines:!0,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,legendTemplate:'
    <% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>
'};i.Type.extend({name:"Bar",defaults:s,initialize:function(t){var s=this.options;this.ScaleClass=i.Scale.extend({offsetGridLines:!0,calculateBarX:function(t,i,e){var n=this.calculateBaseWidth(),o=this.calculateX(e)-n/2,a=this.calculateBarWidth(t);return o+a*i+i*s.barDatasetSpacing+a/2},calculateBaseWidth:function(){return this.calculateX(1)-this.calculateX(0)-2*s.barValueSpacing},calculateBarWidth:function(t){var i=this.calculateBaseWidth()-(t-1)*s.barDatasetSpacing;return i/t}}),this.datasets=[],this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getBarsAtEvent(t):[];this.eachBars(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),this.BarClass=i.Rectangle.extend({strokeWidth:this.options.barStrokeWidth,showStroke:this.options.barShowStroke,ctx:this.chart.ctx}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,bars:[]};this.datasets.push(s),e.each(i.data,function(e,n){s.bars.push(new this.BarClass({value:e,label:t.labels[n],datasetLabel:i.label,strokeColor:i.strokeColor,fillColor:i.fillColor,highlightFill:i.highlightFill||i.fillColor,highlightStroke:i.highlightStroke||i.strokeColor}))},this)},this),this.buildScale(t.labels),this.BarClass.prototype.base=this.scale.endPoint,this.eachBars(function(t,i,s){e.extend(t,{width:this.scale.calculateBarWidth(this.datasets.length),x:this.scale.calculateBarX(this.datasets.length,s,i),y:this.scale.endPoint}),t.save()},this),this.render()},update:function(){this.scale.update(),e.each(this.activeElements,function(t){t.restore(["fillColor","strokeColor"])}),this.eachBars(function(t){t.save()}),this.render()},eachBars:function(t){e.each(this.datasets,function(i,s){e.each(i.bars,t,this,s)},this)},getBarsAtEvent:function(t){for(var i,s=[],n=e.getRelativePosition(t),o=function(t){s.push(t.bars[i])},a=0;a<% for (var i=0; i
  • <%if(segments[i].label){%><%=segments[i].label%><%}%>
  • <%}%>'};i.Type.extend({name:"Doughnut",defaults:s,initialize:function(t){this.segments=[],this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,this.SegmentArc=i.Arc.extend({ctx:this.chart.ctx,x:this.chart.width/2,y:this.chart.height/2}),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.calculateTotal(t),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({value:t.value,outerRadius:this.options.animateScale?0:this.outerRadius,innerRadius:this.options.animateScale?0:this.outerRadius/100*this.options.percentageInnerCutout,fillColor:t.color,highlightColor:t.highlight||t.color,showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,startAngle:1.5*Math.PI,circumference:this.options.animateRotate?0:this.calculateCircumference(t.value),label:t.label})),e||(this.reflow(),this.update())},calculateCircumference:function(t){return 2*Math.PI*(Math.abs(t)/this.total)},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=Math.abs(t.value)},this)},update:function(){this.calculateTotal(this.segments),e.each(this.activeElements,function(t){t.restore(["fillColor"])}),e.each(this.segments,function(t){t.save()}),this.render()},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,e.each(this.segments,function(t){t.update({outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout})},this)},draw:function(t){var i=t?t:1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.calculateCircumference(t.value),outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout},i),t.endAngle=t.startAngle+t.circumference,t.draw(),0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'};i.Type.extend({name:"Line",defaults:s,initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx,inRange:function(t){return Math.pow(t-this.x,2)0&&ithis.scale.endPoint?t.controlPoints.outer.y=this.scale.endPoint:t.controlPoints.outer.ythis.scale.endPoint?t.controlPoints.inner.y=this.scale.endPoint:t.controlPoints.inner.y0&&(s.lineTo(h[h.length-1].x,this.scale.endPoint),s.lineTo(h[0].x,this.scale.endPoint),s.fillStyle=t.fillColor,s.closePath(),s.fill()),e.each(h,function(t){t.draw()})},this)}})}.call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBeginAtZero:!0,scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,scaleShowLine:!0,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,legendTemplate:'
      <% for (var i=0; i
    • <%if(segments[i].label){%><%=segments[i].label%><%}%>
    • <%}%>
    '};i.Type.extend({name:"PolarArea",defaults:s,initialize:function(t){this.segments=[],this.SegmentArc=i.Arc.extend({showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,ctx:this.chart.ctx,innerRadius:0,x:this.chart.width/2,y:this.chart.height/2}),this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,lineArc:!0,width:this.chart.width,height:this.chart.height,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,valuesCount:t.length}),this.updateScaleRange(t),this.scale.update(),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({fillColor:t.color,highlightColor:t.highlight||t.color,label:t.label,value:t.value,outerRadius:this.options.animateScale?0:this.scale.calculateCenterOffset(t.value),circumference:this.options.animateRotate?0:this.scale.getCircumference(),startAngle:1.5*Math.PI})),e||(this.reflow(),this.update())},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=t.value},this),this.scale.valuesCount=this.segments.length},updateScaleRange:function(t){var i=[];e.each(t,function(t){i.push(t.value)});var s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s,{size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2})},update:function(){this.calculateTotal(this.segments),e.each(this.segments,function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.updateScaleRange(this.segments),this.scale.update(),e.extend(this.scale,{xCenter:this.chart.width/2,yCenter:this.chart.height/2}),e.each(this.segments,function(t){t.update({outerRadius:this.scale.calculateCenterOffset(t.value)})},this)},draw:function(t){var i=t||1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.scale.getCircumference(),outerRadius:this.scale.calculateCenterOffset(t.value)},i),t.endAngle=t.startAngle+t.circumference,0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i
  • <%if(datasets[i].label){%><%=datasets[i].label%><%}%>
  • <%}%>'},initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx}),this.datasets=[],this.buildScale(t),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getPointsAtEvent(t):[];this.eachPoints(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,pointColor:i.pointColor,pointStrokeColor:i.pointStrokeColor,points:[]};this.datasets.push(s),e.each(i.data,function(e,n){var o;this.scale.animation||(o=this.scale.getPointPosition(n,this.scale.calculateCenterOffset(e))),s.points.push(new this.PointClass({value:e,label:t.labels[n],datasetLabel:i.label,x:this.options.animation?this.scale.xCenter:o.x,y:this.options.animation?this.scale.yCenter:o.y,strokeColor:i.pointStrokeColor,fillColor:i.pointColor,highlightFill:i.pointHighlightFill||i.pointColor,highlightStroke:i.pointHighlightStroke||i.pointStrokeColor}))},this)},this),this.render()},eachPoints:function(t){e.each(this.datasets,function(i){e.each(i.points,t,this)},this)},getPointsAtEvent:function(t){var i=e.getRelativePosition(t),s=e.getAngleFromPoint({x:this.scale.xCenter,y:this.scale.yCenter},i),n=2*Math.PI/this.scale.valuesCount,o=Math.round((s.angle-1.5*Math.PI)/n),a=[];return(o>=this.scale.valuesCount||0>o)&&(o=0),s.distance<=this.scale.drawingArea&&e.each(this.datasets,function(t){a.push(t.points[o])}),a},buildScale:function(t){this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,angleLineColor:this.options.angleLineColor,angleLineWidth:this.options.angleShowLineOut?this.options.angleLineWidth:0,pointLabelFontColor:this.options.pointLabelFontColor,pointLabelFontSize:this.options.pointLabelFontSize,pointLabelFontFamily:this.options.pointLabelFontFamily,pointLabelFontStyle:this.options.pointLabelFontStyle,height:this.chart.height,width:this.chart.width,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,labels:t.labels,valuesCount:t.datasets[0].data.length}),this.scale.setScaleSize(),this.updateScaleRange(t.datasets),this.scale.buildYLabels()},updateScaleRange:function(t){var i=function(){var i=[];return e.each(t,function(t){t.data?i=i.concat(t.data):e.each(t.points,function(t){i.push(t.value)})}),i}(),s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s)},addData:function(t,i){this.scale.valuesCount++,e.each(t,function(t,e){var s=this.scale.getPointPosition(this.scale.valuesCount,this.scale.calculateCenterOffset(t));this.datasets[e].points.push(new this.PointClass({value:t,label:i,x:s.x,y:s.y,strokeColor:this.datasets[e].pointStrokeColor,fillColor:this.datasets[e].pointColor}))},this),this.scale.labels.push(i),this.reflow(),this.update()},removeData:function(){this.scale.valuesCount--,this.scale.labels.shift(),e.each(this.datasets,function(t){t.points.shift()},this),this.reflow(),this.update()},update:function(){this.eachPoints(function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.scale,{width:this.chart.width,height:this.chart.height,size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2}),this.updateScaleRange(this.datasets),this.scale.setScaleSize(),this.scale.buildYLabels()},draw:function(t){var i=t||1,s=this.chart.ctx;this.clear(),this.scale.draw(),e.each(this.datasets,function(t){e.each(t.points,function(t,e){t.hasValue()&&t.transition(this.scale.getPointPosition(e,this.scale.calculateCenterOffset(t.value)),i)},this),s.lineWidth=this.options.datasetStrokeWidth,s.strokeStyle=t.strokeColor,s.beginPath(),e.each(t.points,function(t,i){0===i?s.moveTo(t.x,t.y):s.lineTo(t.x,t.y)},this),s.closePath(),s.stroke(),s.fillStyle=t.fillColor,s.fill(),e.each(t.points,function(t){t.hasValue()&&t.draw()})},this)}})}.call(this); \ No newline at end of file diff --git a/templates/main-menu.html b/templates/main-menu.html index 57576fd7..3e3a1d90 100644 --- a/templates/main-menu.html +++ b/templates/main-menu.html @@ -32,8 +32,10 @@ {{lang_status}}
  • diff --git a/templates/statistics/clientlist.html b/templates/statistics/clientlist.html new file mode 100644 index 00000000..e5f91493 --- /dev/null +++ b/templates/statistics/clientlist.html @@ -0,0 +1,26 @@ +

    {{lang_clientList}}

    +
    {{filter}} ~= {{argument}}
    +
    + + + + + + + + + + + + {{#rows}} + + + + + + + + + + {{/rows}} +
    {{lang_machine}}{{lang_address}}{{lang_lastBoot}}{{lang_kvmSupport}}{{lang_gbRam}}{{lang_tmpGb}}{{lang_cpuModel}}
    {{hostname}}
    {{machineuuid}}
    {{subnet}}{{lastoctet}}
    {{macaddr}}
    {{lastboot}}{{kvmstate}}{{gbram}} GiB{{gbtmp}} GiB{{lang_realCores}}: {{realcores}}
    {{cpumodel}}
    \ No newline at end of file diff --git a/templates/statistics/cpumodels.html b/templates/statistics/cpumodels.html new file mode 100644 index 00000000..c3d43aa4 --- /dev/null +++ b/templates/statistics/cpumodels.html @@ -0,0 +1,49 @@ +
    +
    +
    + {{lang_cpuStats}} +
    +
    +
    +
    + + + + + + + {{#rows}} + + + + + + {{/rows}} +
    {{lang_cpuName}}{{lang_cpuCores}}{{lang_cpuCount}}
    {{cpumodel}}{{realcores}}{{count}}
    +
    +
    + + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/statistics/id44.html b/templates/statistics/id44.html new file mode 100644 index 00000000..cd4e32d7 --- /dev/null +++ b/templates/statistics/id44.html @@ -0,0 +1,48 @@ +
    +
    +
    + {{lang_tempPartStats}} +
    +
    +
    +
    + + + + + + {{#rows}} + + + + + {{/rows}} +
    {{lang_partitionSize}}{{lang_machineCount}}
    {{gb}} GiB{{count}}
    +
    +
    + + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/statistics/kvmstate.html b/templates/statistics/kvmstate.html new file mode 100644 index 00000000..ea95df4e --- /dev/null +++ b/templates/statistics/kvmstate.html @@ -0,0 +1,47 @@ +
    +
    +
    + {{lang_kvmStats}} +
    +
    +
    +
    + + + + + + {{#rows}} + + + + + {{/rows}} +
    {{lang_kvmState}}{{lang_machineCount}}
    {{kvmstate}}{{count}}
    +
    +
    + + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/statistics/machine-hdds.html b/templates/statistics/machine-hdds.html new file mode 100644 index 00000000..98cf65c5 --- /dev/null +++ b/templates/statistics/machine-hdds.html @@ -0,0 +1,55 @@ +

    {{lang_hdds}}

    +
    + {{#hdds}} +
    +
    +
    + {{dev}} +
    +
    +
    +
    + + + + + + + {{#partitions}} + + + + + + {{/partitions}} +
    {{lang_partName}}{{lang_partSize}}{{lang_partType}}
    {{name}}{{size}} GiB{{type}}
    +
    {{lang_total}}: {{size}} GiB
    +
    +
    + + +
    +
    +
    +
    +
    + {{/hdds}} +
    \ No newline at end of file diff --git a/templates/statistics/machine-main.html b/templates/statistics/machine-main.html new file mode 100644 index 00000000..ac8b8001 --- /dev/null +++ b/templates/statistics/machine-main.html @@ -0,0 +1,79 @@ +

    {{hostname}} {{#hostname}}–{{/hostname}} {{clientip}}

    + +
    +
    +
    +
    + {{lang_machineSummary}} +
    +
    + + + + + + + + + + + + + + {{#hostname}} + + + + + {{/hostname}} + + + + + + + + + + + + +
    {{lang_uuid}}{{machineuuid}}
    {{lang_macAddr}}{{macaddr}}
    {{lang_ip}}{{clientip}}
    {{lang_hostname}}{{hostname}}
    {{lang_firstSeen}}{{firstseen}}
    {{lang_lastBoot}}{{lastboot}}
    {{lang_lastSeen}}{{lastseen}}
    +
    +
    +
    +
    +
    +
    + {{lang_hardwareSummary}} +
    +
    + + + + + + + + + + + + + + + + + +
    {{lang_cpuModel}} + {{cpumodel}} + {{#Sockets}} +
    + {{lang_sockets}}: {{Sockets}}, {{lang_cores}}: {{Realcores}}, {{lang_virtualCores}}: {{Virtualcores}} +
    + {{/Sockets}} +
    {{lang_ram}}{{gbram}} GiB
    {{lang_tempPart}}{{gbtmp}} GiB
    {{lang_64bitSupport}}{{kvmstate}}
    +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/statistics/memory.html b/templates/statistics/memory.html new file mode 100644 index 00000000..b9ee77fb --- /dev/null +++ b/templates/statistics/memory.html @@ -0,0 +1,47 @@ +
    +
    +
    + {{lang_memoryStats}} +
    +
    +
    +
    + + + + + + {{#rows}} + + + + + {{/rows}} +
    {{lang_ramSize}}{{lang_machineCount}}
    {{gb}} GiB{{count}}
    +
    +
    + + +
    +
    +
    +
    +
    \ No newline at end of file -- cgit v1.2.3-55-g7522