From 3b71f10926e42e7cbd98e3c00450c3b4f39e7a8d Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 17 Sep 2023 17:06:53 +0000 Subject: [PATCH] add simple stat rendering --- Admin/Install/Navigation.install.json | 17 +- Admin/Install/db.json | 7 +- Admin/Routes/Web/Backend.php | 11 ++ Controller/BackendController.php | 88 +++++++++ Controller/Controller.php | 2 +- Models/ImpressionStat.php | 30 ++++ Models/ImpressionStatMapper.php | 6 +- Models/PermissionCategory.php | 2 + Theme/Backend/Lang/Navigation.de.lang.php | 1 + Theme/Backend/Lang/Navigation.en.lang.php | 1 + Theme/Backend/monitoring-stats.tpl.php | 207 ++++++++++++++++++++++ 11 files changed, 360 insertions(+), 12 deletions(-) create mode 100644 Models/ImpressionStat.php create mode 100644 Theme/Backend/monitoring-stats.tpl.php diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json index 505a24d..dc87000 100755 --- a/Admin/Install/Navigation.install.json +++ b/Admin/Install/Navigation.install.json @@ -48,11 +48,26 @@ "pid": "/admin/monitoring", "type": 3, "subtype": 1, + "name": "Stats", + "uri": "{/base}/admin/monitoring/stats", + "target": "self", + "icon": null, + "order": 10, + "from": "Monitoring", + "permission": { "permission": 2, "category": null, "element": null }, + "parent": 1000706001, + "children": [] + }, + { + "id": 1000706104, + "pid": "/admin/monitoring", + "type": 3, + "subtype": 1, "name": "Security", "uri": "{/base}/admin/monitoring/security/dashboard?{?}", "target": "self", "icon": null, - "order": 5, + "order": 15, "from": "Monitoring", "permission": { "permission": 2, "category": null, "element": null }, "parent": 1000706001, diff --git a/Admin/Install/db.json b/Admin/Install/db.json index c8ac210..2847e85 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -87,11 +87,6 @@ "foreignTable": "country", "foreignKey": "country_code2" }, - "monitoring_request_browser": { - "name": "monitoring_request_browser", - "type": "VARCHAR(255)", - "null": false - }, "monitoring_request_path": { "name": "monitoring_request_path", "type": "VARCHAR(255)", @@ -114,7 +109,7 @@ }, "monitoring_request_datetime": { "name": "monitoring_request_datetime", - "type": "INT", + "type": "DATETIME", "null": false } } diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index e9901d2..0f3a1d5 100755 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -29,6 +29,17 @@ return [ ], ], ], + '^.*/admin/monitoring/stats.*$' => [ + [ + 'dest' => '\Modules\Monitoring\Controller\BackendController:viewStats', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::READ, + 'state' => PermissionCategory::STATS, + ], + ], + ], '^.*/admin/monitoring/log/list.*$' => [ [ 'dest' => '\Modules\Monitoring\Controller\BackendController:viewMonitoringLogList', diff --git a/Controller/BackendController.php b/Controller/BackendController.php index aab3f23..b348309 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -14,7 +14,10 @@ declare(strict_types=1); namespace Modules\Monitoring\Controller; +use Modules\Monitoring\Models\ImpressionStatMapper; +use phpOMS\Asset\AssetType; use phpOMS\Contract\RenderableInterface; +use phpOMS\DataStorage\Database\Query\Builder; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Views\View; @@ -53,6 +56,91 @@ final class BackendController extends Controller return $view; } + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewStats(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + $view->setTemplate('/Modules/Monitoring/Theme/Backend/monitoring-stats'); + $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1000706001, $request, $response); + + $head = $response->data['Content']->head; + $head->addAsset(AssetType::CSS, 'Resources/chartjs/Chartjs/chart.css'); + $head->addAsset(AssetType::JSLATE, 'Resources/chartjs/Chartjs/chart.js'); + $head->addAsset(AssetType::JSLATE, 'Modules/ItemManagement/Controller.js', ['type' => 'module']); + + $view->data['stats'] = []; + + $query = new Builder($this->app->dbPool->get()); + $query->raw( + 'SELECT DATE(monitoring_request_datetime) as date, COUNT(*) + FROM monitoring_request + GROUP BY date(monitoring_request_datetime) + ORDER BY date ASC;' + ); + + $view->data['stats']['impressions'] = $query->execute()->fetchAll(\PDO::FETCH_COLUMN|\PDO::FETCH_GROUP); + + $query = new Builder($this->app->dbPool->get()); + $query->raw( + 'SELECT DATE(monitoring_request_datetime) as date, monitoring_request_country as country, COUNT(*) as count + FROM monitoring_request + GROUP BY date(monitoring_request_datetime), monitoring_request_country + ORDER BY date ASC;' + ); + + $view->data['stats']['country'] = []; + + $temp = $query->execute()->fetchAll(); + foreach ($temp as $t) { + if (!isset($view->data['stats']['country'][$t['country']])) { + $view->data['stats']['country'][$t['country']] = []; + } + + $view->data['stats']['country'][$t['country']][$t['date']] = $t['count']; + } + + $query = new Builder($this->app->dbPool->get()); + $query->raw( + 'SELECT monitoring_request_agent as agent, COUNT(*) + FROM monitoring_request + GROUP BY monitoring_request_agent;' + ); + + $view->data['stats']['browser'] = $query->execute()->fetchAll(\PDO::FETCH_COLUMN|\PDO::FETCH_GROUP); + + $query = new Builder($this->app->dbPool->get()); + $query->raw( + 'SELECT DATE(monitoring_request_datetime) as date, monitoring_request_host as host, COUNT(*) as count + FROM monitoring_request + GROUP BY date(monitoring_request_datetime), monitoring_request_host + ORDER BY date ASC;' + ); + + $view->data['stats']['domain'] = []; + + $temp = $query->execute()->fetchAll(); + foreach ($temp as $t) { + if (!isset($view->data['stats']['domain'][$t['host']])) { + $view->data['stats']['domain'][$t['host']] = []; + } + + $view->data['stats']['domain'][$t['host']][$t['date']] = $t['count']; + } + + return $view; + } + /** * Routing end-point for application behaviour. * diff --git a/Controller/Controller.php b/Controller/Controller.php index 53a1b37..1729f56 100755 --- a/Controller/Controller.php +++ b/Controller/Controller.php @@ -14,9 +14,9 @@ declare(strict_types=1); namespace Modules\Monitoring\Controller; +use Modules\Monitoring\Models\ImpressionStat; use Modules\Monitoring\Models\ImpressionStatMapper; use phpOMS\Message\Http\HttpRequest; -use phpOMS\Message\Statistic\ImpressionStat; use phpOMS\Module\ModuleAbstract; /** diff --git a/Models/ImpressionStat.php b/Models/ImpressionStat.php new file mode 100644 index 0000000..4456daf --- /dev/null +++ b/Models/ImpressionStat.php @@ -0,0 +1,30 @@ + ['name' => 'monitoring_request_host', 'type' => 'string', 'internal' => 'host',], 'monitoring_request_language' => ['name' => 'monitoring_request_language', 'type' => 'string', 'internal' => 'language',], 'monitoring_request_country' => ['name' => 'monitoring_request_country', 'type' => 'string', 'internal' => 'country',], - 'monitoring_request_browser' => ['name' => 'monitoring_request_browser', 'type' => 'string', 'internal' => 'browser',], 'monitoring_request_path' => ['name' => 'monitoring_request_path', 'type' => 'string', 'internal' => 'path',], 'monitoring_request_uri' => ['name' => 'monitoring_request_uri', 'type' => 'string', 'internal' => 'uri',], 'monitoring_request_referer' => ['name' => 'monitoring_request_referer', 'type' => 'string', 'internal' => 'referer',], - 'monitoring_request_agent' => ['name' => 'monitoring_request_agent', 'type' => 'string', 'internal' => 'agent',], - 'monitoring_request_datetime' => ['name' => 'monitoring_request_datetime', 'type' => 'int', 'internal' => 'datetime',], + 'monitoring_request_agent' => ['name' => 'monitoring_request_agent', 'type' => 'string', 'internal' => 'userAgent',], + 'monitoring_request_datetime' => ['name' => 'monitoring_request_datetime', 'type' => 'DateTime', 'internal' => 'datetime',], ]; /** diff --git a/Models/PermissionCategory.php b/Models/PermissionCategory.php index 3b87ee9..f064743 100755 --- a/Models/PermissionCategory.php +++ b/Models/PermissionCategory.php @@ -31,4 +31,6 @@ abstract class PermissionCategory extends Enum public const LOG = 2; public const SECURITY = 3; + + public const STATS = 3; } diff --git a/Theme/Backend/Lang/Navigation.de.lang.php b/Theme/Backend/Lang/Navigation.de.lang.php index 6d08093..5c5ae39 100755 --- a/Theme/Backend/Lang/Navigation.de.lang.php +++ b/Theme/Backend/Lang/Navigation.de.lang.php @@ -16,4 +16,5 @@ return ['Navigation' => [ 'Logs' => 'Protokoll', 'Monitoring' => 'Überwachung', 'Security' => 'Sicherheit', + 'Stats' => 'Stats', ]]; diff --git a/Theme/Backend/Lang/Navigation.en.lang.php b/Theme/Backend/Lang/Navigation.en.lang.php index acb45b1..0a86431 100755 --- a/Theme/Backend/Lang/Navigation.en.lang.php +++ b/Theme/Backend/Lang/Navigation.en.lang.php @@ -16,4 +16,5 @@ return ['Navigation' => [ 'Logs' => 'Logs', 'Monitoring' => 'Monitoring', 'Security' => 'Security', + 'Stats' => 'Stats', ]]; diff --git a/Theme/Backend/monitoring-stats.tpl.php b/Theme/Backend/monitoring-stats.tpl.php new file mode 100644 index 0000000..0343db1 --- /dev/null +++ b/Theme/Backend/monitoring-stats.tpl.php @@ -0,0 +1,207 @@ +data['nav']->render(); ?> + +
+
+
+
getHtml('Daily'); ?>
+
+ $value) { $data[] = '"' . $key . '"'; } echo \implode(',', $data); ?> + ], + "datasets": [ + { + "label": "getHtml('Daily'); ?>", + "type": "line", + "data": [ + data['stats']['impressions'] as $value) { $data[] = $value[0]; } echo \implode(',', $data); ?> + ], + "fill": false, + "borderColor": "rgb(255, 99, 132)", + "backgroundColor": "rgb(255, 99, 132)", + "tension": 0.0 + } + ] + }, + "options": { + "title": { + "display": false, + "text": "getHtml('Daily'); ?>" + }, + "scales": { + "x": { + "id": "axis-1", + "display": true + }, + "y": { + "id": "axis-2", + "display": true, + "position": "left", + "beginAtZero": true, + "ticks": { + "stepSize": 1 + } + } + } + } + }'> +
+
+
+ +
+
+
getHtml('Country'); ?>
+
+ $value) { $data[] = '"' . $key . '"'; } echo \implode(',', $data); ?> + ], + "datasets": [ + data['stats']['country'] as $country => $values) : ++$c; ?> + 1 ? ',' : ''; ?> + { + "label": "printHtml($country); ?>", + "type": "bar", + "data": [ + data['stats']['impressions'] as $key => $value) { $data[] = $values[$key] ?? 0; } echo \implode(',', $data); ?> + ] + } + + ] + }, + "options": { + "responsive": true, + "title": { + "display": false, + "text": "getHtml('Country'); ?>" + }, + "scales": { + "x": { + "id": "axis-1", + "display": true, + "stacked": true + }, + "y": { + "id": "axis-2", + "display": true, + "position": "left", + "beginAtZero": true, + "ticks": { + "stepSize": 1 + }, + "stacked": true + } + } + } + }'> +
+
+
+
+ +
+
+
+
getHtml('Browser'); ?>
+
+ $value) { $data[] = '"' . $key . '"'; } echo \implode(',', $data); ?> + ], + "datasets": [ + { + "label": "getHtml('Browser'); ?>", + "type": "pie", + "data": [ + data['stats']['browser'] as $value) { $data[] = $value[0]; } echo \implode(',', $data); ?> + ] + } + ] + }, + "options": { + "responsive": true, + "title": { + "display": false, + "text": "getHtml('Browser'); ?>" + } + } + }'> +
+
+
+ +
+
+
getHtml('Domain'); ?>
+
+ $value) { $data[] = '"' . $key . '"'; } echo \implode(',', $data); ?> + ], + "datasets": [ + data['stats']['domain'] as $host => $values) : ++$c; ?> + 1 ? ',' : ''; ?> + { + "label": "printHtml($host); ?>", + "type": "bar", + "data": [ + data['stats']['impressions'] as $key => $value) { $data[] = $values[$key] ?? 0; } echo \implode(',', $data); ?> + ] + } + + ] + }, + "options": { + "responsive": true, + "title": { + "display": false, + "text": "getHtml('Domain'); ?>" + }, + "scales": { + "x": { + "id": "axis-1", + "display": true + }, + "y": { + "id": "axis-2", + "display": true, + "position": "left", + "beginAtZero": true, + "ticks": { + "stepSize": 1 + } + } + } + } + }'> +
+
+
+
\ No newline at end of file