Skip to content

Commit 627dfce

Browse files
committed
add stats page
1 parent 90f0b66 commit 627dfce

File tree

3 files changed

+259
-12
lines changed

3 files changed

+259
-12
lines changed

index.php

+18-12
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ function SuspendPageRefresh() {
190190
<li class="nav-item">
191191
<a class="nav-link<?php echo ($_GET['show'] == "modules") ? ' active' : ''; ?>"
192192
href="./modules">Modules</a></li>
193+
<li class="nav-item">
194+
<a class="nav-link<?php echo ($_GET['show'] == "stats") ? ' active' : ''; ?>"
195+
href="./stats">Stats</a></li>
193196
<li class="nav-item">
194197
<a class="nav-link<?php echo ($_GET['show'] == "info") ? ' active' : ''; ?>"
195198
href="./info">Info</a></li>
@@ -249,18 +252,21 @@ function SuspendPageRefresh() {
249252
case 'reflectors' :
250253
require_once("./pgs/reflectors.php");
251254
break;
252-
case 'traffic' :
253-
require_once("./pgs/traffic.php");
254-
break;
255-
case 'info' :
256-
require_once("./pgs/info.php");
257-
break;
258-
case 'thanks' :
259-
require_once("./pgs/thanks.php");
260-
break;
261-
case 'modules' :
262-
require_once("./pgs/modules.php");
263-
break;
255+
case 'traffic' :
256+
require_once("./pgs/traffic.php");
257+
break;
258+
case 'info' :
259+
require_once("./pgs/info.php");
260+
break;
261+
case 'thanks' :
262+
require_once("./pgs/thanks.php");
263+
break;
264+
case 'modules' :
265+
require_once("./pgs/modules.php");
266+
break;
267+
case 'stats':
268+
require_once("./pgs/stats.php");
269+
break;
264270
default :
265271
require_once("./pgs/users.php");
266272

pgs/stats-data.php

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
define("CACHETTL", 3600);
4+
define("DBFILE", "/usr/local/src/activity/pb_data/data.db");
5+
6+
function fetchAll($result) {
7+
$rows = array();
8+
while ($row = $result->fetchArray(SQLITE3_NUM)) {
9+
$rows[] = $row;
10+
}
11+
return $rows;
12+
}
13+
14+
// function getData(key) - display data based on key
15+
function getData($key) {
16+
$timescale = $_GET['timescale'];
17+
$module = $_GET['module'];
18+
$moduleClause = "";
19+
if (preg_match('/^[A-Z]$/', $module)) {
20+
$moduleClause = " AND module = '$module'";
21+
}
22+
23+
$now = round(microtime(true) * 1000);
24+
$tscutoff = $now - $timescale * 24 * 60 * 60 * 1000;
25+
26+
$db = new SQLite3(DBFILE);
27+
28+
switch ($key) {
29+
case 'totals':
30+
$apcuKey = "totals-$timescale-$module";
31+
$rows = apcu_fetch($apcuKey);
32+
if ($rows === false) {
33+
$query = $db->prepare('
34+
SELECT
35+
ROUND(SUM((tsoff - ts) / 1000.0)) AS total_activity_seconds,
36+
COUNT(DISTINCT call) AS unique_call_count
37+
FROM
38+
activity
39+
WHERE
40+
tsoff > 0 AND
41+
ts > :cutoff
42+
');
43+
$query->bindValue(':cutoff', $tscutoff, SQLITE3_FLOAT);
44+
$result = $query->execute();
45+
$rows = fetchAll($result);
46+
apcu_store($apcuKey, $rows, 3600);
47+
}
48+
$row = $rows[0];
49+
echo "<p>Total transmission time: " . $row[0] . " seconds<br>";
50+
echo "Different callsigns heard: " . $row[1] . "</p>";
51+
return;
52+
53+
case 'activity':
54+
$apcuKey = "activity-$timescale-$module";
55+
$section = "Activity (by call)";
56+
$head = array('Call', 'Tx (sec)');
57+
$query = $db->prepare('
58+
SELECT
59+
call,
60+
ROUND(SUM(tsoff - ts)/1000) AS total_activity_time
61+
FROM
62+
activity
63+
WHERE
64+
tsoff > 0
65+
AND ts > :cutoff '
66+
. $moduleClause .
67+
'GROUP BY
68+
call
69+
ORDER BY
70+
total_activity_time DESC
71+
LIMIT
72+
15
73+
');
74+
break;
75+
case 'modules':
76+
$apcuKey = "modules-$timescale-$module";
77+
$section = "Activity (by module)";
78+
$head = array('Module', 'Tx (sec)');
79+
$query = $db->prepare('
80+
SELECT
81+
module,
82+
ROUND(SUM(tsoff - ts)/1000) AS total_activity_time
83+
FROM
84+
activity
85+
WHERE
86+
ts > :cutoff
87+
AND tsoff > 0 '
88+
. $moduleClause .
89+
'GROUP BY
90+
module
91+
ORDER BY
92+
total_activity_time DESC
93+
');
94+
break;
95+
case 'kerchunks':
96+
$apcuKey = "kerchunks-$timescale-$module";
97+
$section = "Kerchunks";
98+
$head = array('Call', 'Kerchunks');
99+
$query = $db->prepare('
100+
SELECT
101+
call,
102+
COUNT(*) AS kerchunk_count
103+
FROM
104+
activity
105+
WHERE
106+
ts > :cutoff
107+
AND tsoff > 0
108+
AND (tsoff - ts) < 1500 '
109+
. $moduleClause .
110+
'GROUP BY
111+
call
112+
ORDER BY
113+
kerchunk_count DESC
114+
LIMIT
115+
15
116+
');
117+
break;
118+
}
119+
$query->bindValue(':cutoff', $tscutoff, SQLITE3_FLOAT);
120+
$rows = apcu_fetch($apcuKey);
121+
if ($rows == false) {
122+
$result = $query->execute();
123+
$rows = fetchAll($result);
124+
apcu_store($apcuKey, $rows, 3600);
125+
}
126+
// Fetch and process results
127+
echo "<h4>$section</h4>\n";
128+
echo '<table class="table table-striped table-sm">';
129+
echo '<thead>';
130+
foreach ($head as $item) {
131+
echo "<th>" . $item . "</th>";
132+
}
133+
echo '</thead><tbody>';
134+
foreach ($rows as $row) {
135+
echo "<tr><td>" . htmlspecialchars($row[0]) .
136+
"</td><td>" . $row[1] . "</td></tr>\n";
137+
}
138+
echo "</tbody></table>";
139+
echo "<p></p>";
140+
$db->close();
141+
}
142+
143+
getData('totals');
144+
getData('activity');
145+
if ($_GET['module'] == "*") {
146+
getData('modules');
147+
}
148+
getData('kerchunks');
149+
150+
?>

pgs/stats.php

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
function QRZ($c) {
3+
return preg_replace('/((\d[A-Z]\d{1,3}[A-Z]{1,4})|([A-Z]{1,2}\d{1,3}[A-Z]{1,4}))/',
4+
"<a target=\"_blank\" rel=\"noreferrer\" href=\"https://qrz.com/db/$1\">$1</a>",
5+
$c);
6+
}
7+
8+
function fetchdata($period, $module) {
9+
$dbPath = "/usr/local/src/activity/pb_data/data.db";
10+
$db = new PDO("sqlite:$dbPath");
11+
12+
$sql = "SELECT call, sum(tsoff-ts) AS use FROM activity
13+
WHERE created > '2023-08-05'
14+
AND tsoff > 0
15+
GROUP BY module
16+
ORDER BY use DESC
17+
LIMIT 20;";
18+
$stmt = $db->prepare($sql);
19+
$stmt->execute();
20+
21+
$results = $stmt->fetchALL(PDO::FETCH_ASSOC);
22+
$r = "";
23+
foreach ($results as $row) {
24+
print_r($row);
25+
$r += "<tr><td>" . QRZ($row['call']) . "</td><td>" . $row['use'] . "</td></tr>";
26+
}
27+
//print_r($r);
28+
return $r;
29+
}
30+
?>
31+
32+
<div class="container">
33+
<div class="row">
34+
<h2>Statistics</h2>
35+
</div>
36+
<div class="row">
37+
<div class="col-md-6">
38+
<div class="form-group">
39+
<label for="timescale">Timescale:</label>
40+
<select id="timescale" class="form-select"
41+
hx-get="/pgs/stats-data.php" hx-target="#data-container"
42+
hx-trigger="change" name="timescale"
43+
hx-vals='js:{"timescale":event.target.value, "module":document.getElementById("module").value}'>
44+
<option value="1">1 Day</option>
45+
<option value="7">7 Days</option>
46+
<option selected value="30">30 Days</option>
47+
<option value="90">90 Days</option>
48+
<option value="180">180 Days</option>
49+
<option value="365">1 Year</option>
50+
<option value="730">2 Years</option>
51+
</select>
52+
</div>
53+
</div>
54+
<div class="col-md-6">
55+
<div class="form-group">
56+
<label for="module">Module:</label>
57+
<select id="module" class="form-select"
58+
hx-get="/pgs/stats-data.php" hx-target="#data-container"
59+
hx-trigger="change" name="module"
60+
hx-vals='js:{"timescale":document.getElementById("timescale").value, "module":event.target.value}'>
61+
<option selected value="*">All Modules</option>
62+
<?php
63+
for ($i = ord('A'); $i <= ord('Z'); $i++) {
64+
$letter = chr($i);
65+
echo "<option value=\"$letter\">Module $letter</option>";
66+
}
67+
?>
68+
</select>
69+
</div>
70+
</div>
71+
</div>
72+
<div class="row">
73+
<div id="data-container">
74+
<?php
75+
$_GET['timescale'] = 30;
76+
$_GET['module'] = '*';
77+
require 'stats-data.php';
78+
?>
79+
</div>
80+
</div>
81+
<table>
82+
</table>
83+
</div>
84+
</div>
85+
<script>
86+
clearTimeout(PageRefresh);
87+
</script>
88+
<script src="https://unpkg.com/[email protected]/dist/htmx.js"
89+
integrity="sha384-l9bYT9SL4CAW0Hl7pAOpfRc18mys1b0wK4U8UtGnWOxPVbVMgrOdB+jyz/WY8Jue"
90+
crossorigin="anonymous"></script>
91+

0 commit comments

Comments
 (0)