Implements live graphs page

main
Yasen Pramatarov 2025-01-14 15:37:20 +02:00
parent 5982d5eef9
commit 8b1fd2e2c1
3 changed files with 268 additions and 170 deletions

View File

@ -494,6 +494,77 @@ class Agent {
return null;
}
/**
* Gets historical data for a specific metric from agent checks
*
* @param int $platform_id The platform ID
* @param string $agent_type The type of agent (e.g., 'jvb', 'jicofo')
* @param string $metric_type The type of metric to retrieve
* @param string $from_time Start time in Y-m-d format
* @param string $until_time End time in Y-m-d format
* @return array Array with the dataset from agent checks
*/
public function getHistoricalData($platform_id, $agent_type, $metric_type, $from_time, $until_time) {
// Get data from agent checks
$sql = 'SELECT
DATE(jac.timestamp) as date,
jac.response_content,
COUNT(*) as checks_count
FROM
jilo_agent_checks jac
JOIN
jilo_agents ja ON jac.agent_id = ja.id
JOIN
jilo_agent_types jat ON ja.agent_type_id = jat.id
WHERE
ja.platform_id = :platform_id
AND jat.description = :agent_type
AND jac.status_code = 200
AND DATE(jac.timestamp) BETWEEN :from_time AND :until_time
GROUP BY
DATE(jac.timestamp)
ORDER BY
DATE(jac.timestamp)';
$query = $this->db->prepare($sql);
$query->execute([
':platform_id' => $platform_id,
':agent_type' => $agent_type,
':from_time' => $from_time,
':until_time' => $until_time
]);
$results = $query->fetchAll(PDO::FETCH_ASSOC);
$data = [];
foreach ($results as $row) {
$json_data = json_decode($row['response_content'], true);
if (json_last_error() === JSON_ERROR_NONE) {
$api_data = [];
if ($agent_type === 'jvb') {
$api_data = $json_data['jvb_api_data'] ?? [];
} elseif ($agent_type === 'jicofo') {
$api_data = $json_data['jicofo_api_data'] ?? [];
} elseif ($agent_type === 'jigasi') {
$api_data = $json_data['jigasi_api_data'] ?? [];
} elseif ($agent_type === 'prosody') {
$api_data = $json_data['prosody_api_data'] ?? [];
} elseif ($agent_type === 'nginx') {
$api_data = $json_data['nginx_api_data'] ?? [];
}
$value = $this->getNestedValue($api_data, $metric_type);
if ($value !== null) {
$data[] = [
'date' => $row['date'],
'value' => $value
];
}
}
}
return $data;
}
}
?>

View File

@ -5,40 +5,30 @@
<script>
var ctx = document.getElementById('graph_<?= $data['graph_name'] ?>').getContext('2d');
var chartData0 = <?php echo json_encode($data['data0']); ?>;
var chartData1 = <?php echo json_encode($data['data1']); ?>;
var timeRangeName = '';
var labels = chartData0.map(function(item) {
return item.date;
});
var values0 = chartData0.map(function(item) {
return item.value;
});
var values1 = chartData1.map(function(item) {
return item.value;
});
// Prepare datasets
var datasets = [];
<?php foreach ($data['datasets'] as $dataset): ?>
var chartData = <?php echo json_encode($dataset['data']); ?>;
datasets.push({
label: '<?= $dataset['label'] ?>',
data: chartData.map(function(item) {
return {
x: item.date,
y: item.value
};
}),
borderColor: '<?= $dataset['color'] ?>',
borderWidth: 1,
fill: false
});
<?php endforeach; ?>
var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: '<?= $data['graph_data0_label'] ?? '' ?>',
data: values0,
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1,
fill: false
},
{
label: '<?= $data['graph_data1_label'] ?? '' ?>',
data: values1,
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1,
fill: false
}
]
datasets: datasets
},
options: {
layout: {
@ -64,7 +54,6 @@ var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
mode: 'x'
},
zoom: {
// enabled: true,
mode: 'x',
drag: {
enabled: true, // Enable drag to select range
@ -99,29 +88,25 @@ var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
}
});
// Store the graphs in an array
// Store graph instance and title for later reference
graphs.push({
graph: graph_<?= $data['graph_name'] ?>,
label: document.getElementById('current-period-<?= $data['graph_name'] ?>')
label: '<?= $data['graph_title'] ?>'
});
// Update the time range label
function updatePeriodLabel(chart, labelElement) {
var startDate = chart.scales.x.min;
var endDate = chart.scales.x.max;
if (timeRangeName == 'today') {
labelElement.innerHTML = 'Currently displaying: ' + timeRangeName + ' (' + new Date(startDate).toLocaleDateString() + ')';
// Function to update the period label
function updatePeriodLabel(chart, label) {
var startDate = new Date(chart.scales.x.min);
var endDate = new Date(chart.scales.x.max);
var periodLabel = document.getElementById('current-period-<?= $data['graph_name'] ?>');
if (timeRangeName) {
periodLabel.textContent = label + ' (' + timeRangeName + ')';
} else {
labelElement.innerHTML = 'Currently displaying: ' + timeRangeName + ' (' + new Date(startDate).toLocaleDateString() + ' - ' + new Date(endDate).toLocaleDateString() + ')';
periodLabel.textContent = label + ' (from ' + startDate.toLocaleDateString() + ' to ' + endDate.toLocaleDateString() + ')';
}
}
// Attach the update function to the 'zoom' event
graph_<?= $data['graph_name'] ?>.options.plugins.zoom.onZoom = function({ chart }) {
updatePeriodLabel(chart, document.getElementById('current-period-<?= $data['graph_name'] ?>'));
};
// Update the label initially when the chart is rendered
updatePeriodLabel(graph_<?= $data['graph_name'] ?>, document.getElementById('current-period-<?= $data['graph_name'] ?>'));
// Initial label update
updatePeriodLabel(graph_<?= $data['graph_name'] ?>, '<?= $data['graph_title'] ?>');
</script>

View File

@ -9,154 +9,196 @@ $agent = $_REQUEST['agent'] ?? '';
require '../app/classes/config.php';
require '../app/classes/agent.php';
require '../app/classes/conference.php';
$configObject = new Config();
$agentObject = new Agent($dbWeb);
switch ($item) {
// connect to Jilo database
$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
case 'graphs':
// FIXME example data
$one = date('Y-m-d',strtotime("-5 days"));
$two = date('Y-m-d',strtotime("-4 days"));
$three = date('Y-m-d',strtotime("-2 days"));
$four = date('Y-m-d',strtotime("-1 days"));
// if DB connection has error, display it and stop here
if ($response['db'] === null) {
$error = $response['error'];
include '../app/templates/block-message.php';
$graph[0]['data0'] = [
['date' => $one, 'value' => 10],
['date' => $two, 'value' => 20],
['date' => $three, 'value' => 15],
['date' => $four, 'value' => 25],
];
// otherwise if DB connection is OK, go on
} else {
$db = $response['db'];
$graph[0]['data1'] = [
['date' => $one, 'value' => 12],
['date' => $two, 'value' => 23],
['date' => $three, 'value' => 11],
['date' => $four, 'value' => 27],
];
$conferenceObject = new Conference($db);
$graph[0]['graph_name'] = 'conferences';
$graph[0]['graph_title'] = 'Conferences in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time';
$graph[0]['graph_data0_label'] = 'Conferences from Jitsi logs (Jilo)';
$graph[0]['graph_data1_label'] = 'Conferences from Jitsi API (Jilo Agents)';
switch ($item) {
$graph[1]['data0'] = [
['date' => $one, 'value' => 20],
['date' => $two, 'value' => 30],
['date' => $three, 'value' => 15],
['date' => $four, 'value' => 55],
];
case 'graphs':
// Connect to Jilo database for log data
$jilo_response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
if ($jilo_response['db'] === null) {
$error = $jilo_response['error'];
include '../app/templates/block-message.php';
break;
}
$jilo_db = $jilo_response['db'];
$graph[1]['data1'] = [
['date' => $one, 'value' => 22],
['date' => $two, 'value' => 33],
['date' => $three, 'value' => 11],
['date' => $four, 'value' => 57],
];
// Get date range for the last 7 days
$from_time = date('Y-m-d', strtotime('-7 days'));
$until_time = date('Y-m-d');
$graph[1]['graph_name'] = 'participants';
$graph[1]['graph_title'] = 'Participants in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time';
$graph[1]['graph_data0_label'] = 'Participants from Jitsi logs (Jilo)';
$graph[1]['graph_data1_label'] = 'Participants from Jitsi API (Jilo Agents)';
include '../app/templates/graphs-combined.php';
break;
case 'latest':
// Define metrics to display
$metrics = [
'Basic stats' => [
'conferences' => ['label' => 'Current conferences', 'link' => 'conferences'],
'participants' => ['label' => 'Current participants', 'link' => 'participants'],
'total_conferences_created' => ['label' => 'Total conferences created'],
'total_participants' => ['label' => 'Total participants']
],
'Bridge stats' => [
'bridge_selector.bridge_count' => ['label' => 'Bridge count'],
'bridge_selector.operational_bridge_count' => ['label' => 'Operational bridges'],
'bridge_selector.in_shutdown_bridge_count' => ['label' => 'Bridges in shutdown']
],
'Jibri stats' => [
'jibri_detector.count' => ['label' => 'Jibri count'],
'jibri_detector.available' => ['label' => 'Jibri idle'],
'jibri.live_streaming_active' => ['label' => 'Jibri active streaming'],
'jibri.recording_active' => ['label' => 'Jibri active recording'],
],
'System stats' => [
'threads' => ['label' => 'Threads'],
'stress_level' => ['label' => 'Stress level'],
'version' => ['label' => 'Version']
]
];
// Get latest data for all the agents
$agents = ['jvb', 'jicofo', 'jibri', 'prosody', 'nginx'];
$widget['records'] = [];
// Initialize records for each agent
foreach ($agents as $agent) {
$record = [
'table_headers' => strtoupper($agent),
'metrics' => [],
'timestamp' => null
// Define graphs to show
$graphs = [
[
'graph_name' => 'conferences',
'graph_title' => 'Conferences in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time',
'datasets' => []
],
[
'graph_name' => 'participants',
'graph_title' => 'Participants in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time',
'datasets' => []
]
];
// Fetch all metrics for this agent
foreach ($metrics as $section => $section_metrics) {
foreach ($section_metrics as $metric => $config) {
$data = $agentObject->getLatestData($platform_id, $agent, $metric);
if ($data !== null) {
$record['metrics'][$section][$metric] = [
'value' => $data['value'],
'label' => $config['label'],
'link' => isset($config['link']) ? $config['link'] : null
];
// Use the most recent timestamp
if ($record['timestamp'] === null || strtotime($data['timestamp']) > strtotime($record['timestamp'])) {
$record['timestamp'] = $data['timestamp'];
// Get Jitsi API data
$conferences_api = $agentObject->getHistoricalData(
$platform_id,
'jicofo',
'conferences',
$from_time,
$until_time
);
$graphs[0]['datasets'][] = [
'data' => $conferences_api,
'label' => 'Conferences from Jitsi API',
'color' => 'rgba(75, 192, 192, 1)'
];
// Get conference data from logs
$conferences_logs = $conferenceObject->conferenceNumber(
$from_time,
$until_time
);
$graphs[0]['datasets'][] = [
'data' => $conferences_logs,
'label' => 'Conferences from Logs',
'color' => 'rgba(255, 99, 132, 1)'
];
// Get participants data
$participants_api = $agentObject->getHistoricalData(
$platform_id,
'jicofo',
'participants',
$from_time,
$until_time
);
$graphs[1]['datasets'][] = [
'data' => $participants_api,
'label' => 'Participants from Jitsi API',
'color' => 'rgba(75, 192, 192, 1)'
];
// Prepare data for template
$graph = $graphs;
include '../app/templates/graphs-combined.php';
break;
case 'latest':
// Define metrics to display
$metrics = [
'Basic stats' => [
'conferences' => ['label' => 'Current conferences', 'link' => 'conferences'],
'participants' => ['label' => 'Current participants', 'link' => 'participants'],
'total_conferences_created' => ['label' => 'Total conferences created'],
'total_participants' => ['label' => 'Total participants']
],
'Bridge stats' => [
'bridge_selector.bridge_count' => ['label' => 'Bridge count'],
'bridge_selector.operational_bridge_count' => ['label' => 'Operational bridges'],
'bridge_selector.in_shutdown_bridge_count' => ['label' => 'Bridges in shutdown']
],
'Jibri stats' => [
'jibri_detector.count' => ['label' => 'Jibri count'],
'jibri_detector.available' => ['label' => 'Jibri idle'],
'jibri.live_streaming_active' => ['label' => 'Jibri active streaming'],
'jibri.recording_active' => ['label' => 'Jibri active recording'],
],
'System stats' => [
'threads' => ['label' => 'Threads'],
'stress_level' => ['label' => 'Stress level'],
'version' => ['label' => 'Version']
]
];
// Get latest data for all the agents
$agents = ['jvb', 'jicofo', 'jibri', 'prosody', 'nginx'];
$widget['records'] = [];
// Initialize records for each agent
foreach ($agents as $agent) {
$record = [
'table_headers' => strtoupper($agent),
'metrics' => [],
'timestamp' => null
];
// Fetch all metrics for this agent
foreach ($metrics as $section => $section_metrics) {
foreach ($section_metrics as $metric => $config) {
$data = $agentObject->getLatestData($platform_id, $agent, $metric);
if ($data !== null) {
$record['metrics'][$section][$metric] = [
'value' => $data['value'],
'label' => $config['label'],
'link' => isset($config['link']) ? $config['link'] : null
];
// Use the most recent timestamp
if ($record['timestamp'] === null || strtotime($data['timestamp']) > strtotime($record['timestamp'])) {
$record['timestamp'] = $data['timestamp'];
}
}
}
}
if (!empty($record['metrics'])) {
$widget['records'][] = $record;
}
}
if (!empty($record['metrics'])) {
$widget['records'][] = $record;
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'LatestData';
$widget['title'] = 'Latest data from Jilo Agents';
$widget['collapsible'] = false;
$widget['collapsed'] = false;
$widget['filter'] = false;
$widget['metrics'] = $metrics; // Pass metrics configuration to template
if (!empty($widget['records'])) {
$widget['full'] = true;
}
}
$widget['pagination'] = false;
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'LatestData';
$widget['title'] = 'Latest data from Jilo Agents';
$widget['collapsible'] = false;
$widget['collapsed'] = false;
$widget['filter'] = false;
$widget['metrics'] = $metrics; // Pass metrics configuration to template
if (!empty($widget['records'])) {
$widget['full'] = true;
}
$widget['pagination'] = false;
include '../app/templates/latest-data.php';
break;
include '../app/templates/latest-data.php';
break;
case 'configjs':
$mode = $_REQUEST['mode'] ?? '';
$raw = ($mode === 'raw');
$platformConfigjs = $configObject->getPlatformConfigjs($platformDetails[0]['jitsi_url'], $raw);
include '../app/templates/data-configjs.php';
break;
case 'configjs':
$mode = $_REQUEST['mode'] ?? '';
$raw = ($mode === 'raw');
$platformConfigjs = $configObject->getPlatformConfigjs($platformDetails[0]['jitsi_url'], $raw);
include '../app/templates/data-configjs.php';
break;
case 'interfaceconfigjs':
$mode = $_REQUEST['mode'] ?? '';
$raw = ($mode === 'raw');
$platformInterfaceConfigjs = $configObject->getPlatformInterfaceConfigjs($platformDetails[0]['jitsi_url'], $raw);
include '../app/templates/data-interfaceconfigjs.php';
break;
case 'interfaceconfigjs':
$mode = $_REQUEST['mode'] ?? '';
$raw = ($mode === 'raw');
$platformInterfaceConfigjs = $configObject->getPlatformInterfaceConfigjs($platformDetails[0]['jitsi_url'], $raw);
include '../app/templates/data-interfaceconfigjs.php';
break;
default:
}
default:
}
?>