From cd66207bc7540ffab049c295eed39a99bc032c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gi=C3=B2=20Diani?= Date: Mon, 13 Jan 2025 22:50:03 +0100 Subject: [PATCH] Prediction Charts --- dashboard/app/Api.php | 10 + dashboard/app/Chart.php | 12 ++ dashboard/resources/css/app.css | 7 +- dashboard/resources/views/region.blade.php | 234 ++++++++++++++++++--- dashboard/routes/web.php | 43 +++- 5 files changed, 272 insertions(+), 34 deletions(-) create mode 100644 dashboard/app/Chart.php diff --git a/dashboard/app/Api.php b/dashboard/app/Api.php index 02d714b..f98daf7 100644 --- a/dashboard/app/Api.php +++ b/dashboard/app/Api.php @@ -74,11 +74,21 @@ class Api return self::get("/region/{$id}/properties/capacities"); } + public static function regionCapacitiesMonthly(int $id, string $date): mixed + { + return self::get("/region/{$id}/capacities/monthly/{$date}"); + } + public static function propertyCapacitiesMonthly(int $id, string $date): mixed { return self::get("/property/{$id}/capacities/monthly/{$date}"); } + public static function regionCapacitiesDaily(int $id, string $date): mixed + { + return self::get("/region/{$id}/capacities/weekdays/{$date}"); + } + public static function propertyCapacitiesDaily(int $id, string $date): mixed { return self::get("/property/{$id}/capacities/weekdays/{$date}"); diff --git a/dashboard/app/Chart.php b/dashboard/app/Chart.php new file mode 100644 index 0000000..0f6a908 --- /dev/null +++ b/dashboard/app/Chart.php @@ -0,0 +1,12 @@ +
-

Auslastung Vorhersage

+

Gleitender Mittelwert für die Auslastung von {{ $region[0]['region_name'] }}

@@ -105,7 +105,7 @@ const cCapacityOptions = { xAxis: { type: 'category', boundaryGap: false, - data: {!! json_encode($regionCapacities[1]['dates']) !!}, + data: {!! json_encode($regionCapacities['region']['dates']) !!}, name: 'Zeitpunkt Scraping', nameLocation: 'center', nameGap: 24, @@ -117,7 +117,7 @@ const cCapacityOptions = { type: 'value', min: 0, max: 100, - name: 'Auslastung in Prozent', + name: 'Auslastung in %', nameLocation: 'center', nameGap: 38, nameTextStyle: { @@ -128,24 +128,108 @@ const cCapacityOptions = { name: 'Auslastung alle Regionen', type: 'line', symbolSize: 7, - data: {!! json_encode($regionCapacities[0]['capacities']) !!} + data: {!! json_encode($regionCapacities['all']['capacities']) !!} }, { name: 'Auslastung Region', type: 'line', symbolSize: 7, - data: {!! json_encode($regionCapacities[1]['capacities']) !!} + data: {!! json_encode($regionCapacities['all']['capacities']) !!} }] }; cCapacity.setOption(cCapacityOptions); +const chartCapacityMonthly = document.getElementById('chart-capacity-monthly'); +const cCapacityMonthly = echarts.init(chartCapacityMonthly); + +const cCapacityMonthlyOptions = { + timeline: { + show: false, + data: {!! json_encode($regionCapacities['region']['dates']) !!}, + axisType: 'time', + }, + grid: { + top: 0, + bottom: 25, + left: 70, + right: 10 + }, + xAxis: { + type: 'value', + max: 100 + }, + yAxis: { + type: 'category', + }, + options: [ + @foreach ($regionCapacities['region_monthly'] as $m) + { + yAxis: { + data: {!! json_encode($m['months']) !!} + }, + series: [{ + type: 'bar', + data: {!! json_encode($m['capacities']) !!} + }] + }, + @endforeach + ] +}; + +cCapacityMonthly.setOption(cCapacityMonthlyOptions); + +const chartCapacityDaily = document.getElementById('chart-capacity-daily'); +const cCapacityDaily = echarts.init(chartCapacityDaily); + +const cCapacityDailyOptions = { + timeline: { + show: false, + data: {!! json_encode($regionCapacities['region']['dates']) !!}, + axisType: 'time', + }, + grid: { + top: 0, + bottom: 25, + left: 70, + right: 10 + }, + xAxis: { + type: 'value', + max: 100 + }, + yAxis: { + type: 'category', + }, + options: [ + @foreach ($regionCapacities['region_daily'] as $d) + { + yAxis: { + data: {!! json_encode($d['weekdays']) !!} + }, + series: [{ + type: 'bar', + data: {!! json_encode($d['capacities']) !!} + }] + }, + @endforeach + ] +}; + +cCapacityDaily.setOption(cCapacityDailyOptions); + const chartPrediction = document.getElementById('chart-prediction'); const cPrediction = echarts.init(chartPrediction); const cPredictionOptions = { + timeline: { + show: false, + data: {!! json_encode($regionCapacities['region']['dates']) !!}, + axisType: 'time', + replaceMerge: ['graphic', 'series'] + }, legend: { - data: ['Moving Average', 'Earlier', 'Later'] + show: true }, tooltip: { trigger: 'axis', @@ -161,48 +245,79 @@ const cPredictionOptions = { xAxis: { type: 'category', boundaryGap: false, - data: {!! json_encode($prediction['dates']) !!}, name: 'Zeitpunkt Scraping', nameLocation: 'center', nameGap: 24, nameTextStyle: { fontWeight: 'bold', - } + }, }, yAxis: { type: 'value', min: 0, - max: 1, - name: 'Auslastung in Prozent', + max: 100, + name: 'Auslastung in %', nameLocation: 'center', nameGap: 38, nameTextStyle: { fontWeight: 'bold', } }, - series: [{ - name: 'Moving Average', - type: 'line', - symbolSize: 7, - data: {!! json_encode($prediction['movAvg']) !!} - }, + options: [ + @foreach ($predictions as $p) + @if($p === null) { - name: 'Earlier', - type: 'line', - symbolSize: 7, - data: {!! json_encode($prediction['cap_earlierTimeframe']) !!} + graphic: { + elements: [ + { + type: 'text', + left: 'center', + top: 'center', + style: { + text: 'Keine Daten für Zeitspanne', + fontSize: 44, + fontWeight: 'bold', + } + } + ] + } }, - { - name: 'Later', - type: 'line', - symbolSize: 7, - data: {!! json_encode($prediction['cap_laterTimeframe']) !!} - }] + @else + { + graphic: { + elements: [] + }, + xAxis: { + data: {!! json_encode($p['dates']) !!} + }, + series: [ + { + name: 'Gleitender Mittelwert', + type: 'line', + symbolSize: 7, + data: {!! json_encode($p['movAvg']) !!} + }, + { + name: 'Daten vom ...', + type: 'line', + symbolSize: 7, + data: {!! json_encode($p['cap_earlierTimeframe']) !!} + }, + { + name: 'Daten vom', + type: 'line', + symbolSize: 7, + data: {!! json_encode($p['cap_laterTimeframe']) !!} + } + ] + }, + @endif + @endforeach + ] }; cPrediction.setOption(cPredictionOptions); - const chartHeatmap = document.getElementById('chart-heatmap'); const cHeatmap = echarts.init(chartHeatmap); const cHeatmapOptions = { @@ -284,5 +399,70 @@ const cHeatmapOptions = { } cHeatmap.setOption(cHeatmapOptions); + +const chartTimeline = document.getElementById('timeline'); +const cTimeline = echarts.init(chartTimeline); + +const cTimelineOptions = { + grid: { + show: false, + }, + timeline: { + data: {!! json_encode($regionCapacities['region']['dates']) !!}, + playInterval: 2000, + axisType: 'time', + left: 8, + right: 8, + bottom: 0, + label: { + show: false + } + }, +}; + +cTimeline.setOption(cTimelineOptions); + +cTimeline.on('timelinechanged', (e) => { + + // Set markpoint on linechart + let x = cCapacityOptions.xAxis.data[e.currentIndex]; + let y = cCapacityOptions.series[0].data[e.currentIndex]; + + cCapacityMonthly.dispatchAction({ + type: 'timelineChange', + currentIndex: e.currentIndex + }); + + cCapacityDaily.dispatchAction({ + type: 'timelineChange', + currentIndex: e.currentIndex + }); + + cPrediction.dispatchAction({ + type: 'timelineChange', + currentIndex: e.currentIndex + }); + + cCapacity.setOption({ + series: { + markPoint: { + data: [{ + coord: [x, y] + }] + } + } + }); + +}) + +cCapacity.on('click', 'series', (e) => { + + // Switch to correct calendar in the timeline + cTimeline.dispatchAction({ + type: 'timelineChange', + currentIndex: e.dataIndex + }); + +}); @endsection diff --git a/dashboard/routes/web.php b/dashboard/routes/web.php index f266f5e..b1a67db 100644 --- a/dashboard/routes/web.php +++ b/dashboard/routes/web.php @@ -2,6 +2,7 @@ use Illuminate\Support\Facades\Route; use App\Api; +use App\Chart; Route::get('/', function () { @@ -20,9 +21,19 @@ Route::get('/', function () { $propsPerRegionCounts[] = $el['count_properties']; } + $chartOptions = [ + 'colors' => Chart::colors() + ]; + $propertiesGeo = Api::propertiesGeo(); - return view('overview', ["regions" => $regionBase, "regionPropertiesCapacities" => $regionPropertyCapacities, "geo" => $propertiesGeo, "growth" => $propertiesGrowth, "propsPerRegion" => [json_encode($propsPerRegionId), json_encode($propsPerRegionName), json_encode($propsPerRegionCounts)]]); + return view('overview', [ + "regions" => $regionBase, + "regionPropertiesCapacities" => $regionPropertyCapacities, + "geo" => $propertiesGeo, + "growth" => $propertiesGrowth, + "chartOptions" => $chartOptions, + "propsPerRegion" => [json_encode($propsPerRegionId), json_encode($propsPerRegionName), json_encode($propsPerRegionCounts)]]); }); Route::get('/property/{id}', function (int $id) { @@ -92,14 +103,38 @@ Route::get('/region/{id}', function (int $id) { $regionBaseAll = Api::regionBase(-1); $regionBaseAll[] = ['region_name' => 'Alle Regionen', 'region_id' => -1]; $regionBaseRegion = $id >= 0 ? Api::regionBase($id) : [['region_name' => 'Alle Regionen']]; - $regionMovingAverage = Api::regionMovingAverage($id, '2024-04-25'); $regionPropertiesCapacities = Api::regionPropertiesCapacities($id); $regionCapacitiesRegion = Api::regionCapacities($id); $regionCapacitiesAll = Api::regionCapacities(-1); - $regionCapacities = [$regionCapacitiesAll, $regionCapacitiesRegion]; - return view('region', ['regions' => $regionBaseAll, 'region' => $regionBaseRegion, 'region_id' => $id, 'regionCapacities' => $regionCapacities, 'regionPropertiesCapacities' => $regionPropertiesCapacities, 'prediction' => $regionMovingAverage]); + $regionCapacitiesMonthly = []; + $regionCapacitiesDaily = []; + $regionPredictions = []; + + foreach ($regionCapacitiesRegion['dates'] as $date) { + $regionCapacitiesMonthly[] = Api::regionCapacitiesMonthly($id, $date); + $regionCapacitiesDaily[] = Api::regionCapacitiesDaily($id, $date); + $regionPredictions[] = Api::regionMovingAverage($id, $date); + } + + $regionCapacities = [ + 'all' => $regionCapacitiesAll, + 'region' => $regionCapacitiesRegion, + 'region_monthly' => $regionCapacitiesMonthly, + 'region_daily' => $regionCapacitiesDaily + + ]; + + dump($regionPredictions); + + return view('region', [ + 'regions' => $regionBaseAll, + 'region' => $regionBaseRegion, + 'region_id' => $id, + 'regionCapacities' => $regionCapacities, + 'regionPropertiesCapacities' => $regionPropertiesCapacities, + 'predictions' => $regionPredictions]); });