Initial commit: existing codebase state

This commit is contained in:
root
2026-05-28 16:25:22 +08:00
commit b63cb6a3e8
16670 changed files with 2746770 additions and 0 deletions

View File

@@ -0,0 +1,375 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
// Add This For Export Data In Excel
use App\Exports\HourlyRainfallExport;
// Ni install manual in the container
use Maatwebsite\Excel\Facades\Excel;
class RainfallController extends Controller
{
// Function Retrieve Rainfall Data For Each Station
public function index(Request $request)
{
$stationFilter = $request->get('station');
$dateTImeFilter = $request->input('date');
$displayDate = $dateTImeFilter ? $dateTImeFilter : now();
$dateFilter = $dateTImeFilter ? Carbon::parse($dateTImeFilter)->format('Y-m-d')
:Carbon::today()->format('Y-m-d');
$stations = DB::table('station')->select('stationid', 'name')
->where('rainfall',1)
->orderBy('stationid')->get();
$stationCondition = '';
if ($stationFilter)
{
$stationCondition = " AND s.stationid = '{$stationFilter}'";
}
$rainfallData = collect(DB::select("
SELECT
s.stationid,
s.name,
s.district,
-- selected datetime filter
CAST('$displayDate' AS timestamp) AS selected_timestamp,
-- Latest hourly value
(
SELECT l2.hourly
FROM rainfall l2
WHERE l2.stationid = s.stationid
ORDER BY l2.timestamp DESC
LIMIT 1
) AS hourly,
-- Latest daily rainfall
(
SELECT l3.daily
FROM rainfall l3
WHERE l3.stationid = s.stationid
ORDER BY l3.timestamp DESC
LIMIT 1
) AS daily,
(
SELECT l4.timestamp
FROM rainfall l4
WHERE l4.stationid = s.stationid
AND DATE(l4.timestamp) <= CAST('$dateFilter' AS date)
ORDER BY l4.timestamp DESC
LIMIT 1
) AS last_updated,
-- Historical daily values for the past 7 days
MAX(CASE WHEN DATE(l.timestamp) = CAST('$dateFilter' as date) - INTERVAL '6 days' THEN l.daily END) AS day1,
MAX(CASE WHEN DATE(l.timestamp) = CAST('$dateFilter' as date) - INTERVAL '5 days' THEN l.daily END) AS day2,
MAX(CASE WHEN DATE(l.timestamp) = CAST('$dateFilter' as date) - INTERVAL '4 days' THEN l.daily END) AS day3,
MAX(CASE WHEN DATE(l.timestamp) = CAST('$dateFilter' as date) - INTERVAL '3 days' THEN l.daily END) AS day4,
MAX(CASE WHEN DATE(l.timestamp) = CAST('$dateFilter' as date) - INTERVAL '2 days' THEN l.daily END) AS day5,
MAX(CASE WHEN DATE(l.timestamp) = CAST('$dateFilter' as date) - INTERVAL '1 day' THEN l.daily END) AS day6,
MAX(CASE WHEN DATE(l.timestamp) = CAST('$dateFilter' as date) THEN l.daily END) AS day7
FROM station s
INNER JOIN rainfall l ON s.stationid = l.stationid
WHERE TO_CHAR(l.timestamp, 'HH24:MI:SS') != '00:00:00'
AND l.timestamp >= CAST('$dateFilter' as date) - INTERVAL '6 days'
$stationCondition
GROUP BY s.stationid, s.name, s.district
"
));
$lastupdate = DB::table('rainfall')->max('timestamp');
$dates = collect(range(6,1))->map(fn($i)=>Carbon::parse($dateFilter)->subDays($i)->format('d/m/Y'));
return view('layout.rainfall',compact('rainfallData','lastupdate','dates','stations','displayDate'));
}
// Function For 6 Hours Rainfall Data for Each Station For Early Warning Threshold
public function rainfallSum(Request $request)
{
$stationFilter = $request->input('station');
$dateFilter = $request->input('date');
// If no date submitted → use current local time
$displayDate = $dateFilter ? $dateFilter : now('Asia/Kuala_Lumpur');
// Convert to Y-m-d H:i:s for SQL comparison
$sqlDate = \Carbon\Carbon::parse($displayDate)->format('Y-m-d H:i:s');
// Fetch stations
$stations = DB::table('station')->select('stationid', 'name')->where('rainfall',1)->orderBy('stationid')->get();
// Build SQL query
$sql = "
WITH latest AS (
SELECT
s.stationid,
s.name,
s.district,
MAX(l.timestamp) AS latest_timestamp
FROM station s
INNER JOIN rainfall l ON s.stationid = l.stationid
WHERE 1=1
";
$bindings = [];
// Apply date filter
if ($dateFilter) {
$sql .= " AND l.timestamp = :dateFilter ";
$bindings['dateFilter'] = $sqlDate;
}
$sql .= "
GROUP BY s.stationid, s.name, s.district
),
hourly_intervals AS (
SELECT
s.stationid,
s.name,
s.district,
l.currentrf,
l.timestamp,
EXTRACT(EPOCH FROM (lt.latest_timestamp - l.timestamp)) / 3600 AS hours_diff,
lt.latest_timestamp
FROM station s
INNER JOIN rainfall l ON s.stationid = l.stationid
INNER JOIN latest lt ON lt.stationid = s.stationid
WHERE l.timestamp <= lt.latest_timestamp
AND l.timestamp > lt.latest_timestamp - INTERVAL '6 hours'
)
SELECT
stationid,
name,
district,
MAX(CASE WHEN hours_diff < 1 THEN currentrf END) AS hour1_value,
MAX(CASE WHEN hours_diff < 1 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour1_time,
MAX(CASE WHEN hours_diff >= 1 AND hours_diff < 2 THEN currentrf END) AS hour2_value,
MAX(CASE WHEN hours_diff >= 1 AND hours_diff < 2 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour2_time,
MAX(CASE WHEN hours_diff >= 2 AND hours_diff < 3 THEN currentrf END) AS hour3_value,
MAX(CASE WHEN hours_diff >= 2 AND hours_diff < 3 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour3_time,
MAX(CASE WHEN hours_diff >= 3 AND hours_diff < 4 THEN currentrf END) AS hour4_value,
MAX(CASE WHEN hours_diff >= 3 AND hours_diff < 4 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour4_time,
MAX(CASE WHEN hours_diff >= 4 AND hours_diff < 5 THEN currentrf END) AS hour5_value,
MAX(CASE WHEN hours_diff >= 4 AND hours_diff < 5 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour5_time,
MAX(CASE WHEN hours_diff >= 5 AND hours_diff < 6 THEN currentrf END) AS hour6_value,
MAX(CASE WHEN hours_diff >= 5 AND hours_diff < 6 THEN TO_CHAR(timestamp, 'HH24:MI:SS') END) AS hour6_time,
MAX(latest_timestamp) AS last_update
FROM hourly_intervals
WHERE 1=1
";
// Apply station filter
if ($stationFilter) {
$sql .= " AND stationid = :stationFilter ";
$bindings['stationFilter'] = $stationFilter;
}
$sql .= " GROUP BY stationid, name, district ORDER BY stationid;";
// Execute query
$thresholdData = collect(DB::select($sql, $bindings));
$lastupdate = DB::table('rainfall')->max('timestamp');
return view('layout.threshold', compact('thresholdData', 'stations', 'displayDate','lastupdate'));
}
// Function retrieve Hourly rainfall Graph For Each Station
public function rainfallGraph($stationid)
{
$graphData = DB::table('rainfall')
->select(DB::raw("TO_CHAR(timestamp, 'HH24:MI') AS hour"), DB::raw("CASE
WHEN TO_CHAR(timestamp, 'HH24:MI') LIKE '00:%'
THEN 0
ELSE hourly
END AS hourly") )
->whereDate('timestamp', today())
->whereRaw("EXTRACT(MINUTE FROM timestamp) = 0")
->where('stationid', $stationid)
->orderBy('timestamp')
->get();
return response()->json($graphData);
// $graphData = DB::table('rainfall')
// ->select(
// DB::raw("DATE(timestamp) as date"),
// DB::raw("TO_CHAR(timestamp, 'HH24:MI') AS time_slot"),
// 'currentrf'
// )
// ->where('stationid', $stationid)
// ->whereBetween('timestamp', [
// now()->subDays(7)->startOfDay(),
// now()->endOfDay()
// ])
// ->whereRaw("EXTRACT(MINUTE FROM timestamp) % 5 = 0") // only multiples of 5 minutes
// ->orderBy('timestamp')
// ->get();
}
// Function retrieve data of 6 Hours Rainfall fo Graph Page
public function graphData($stationid,$dates)
{
$dates = urldecode($dates);
$latest = DB::table('rainfall')
->where('stationid', $stationid)
->where('timestamp',$dates)
->max('timestamp');
if (!$latest) {
// no data in table
return response()->json([
'labels' => [],
'data' => []
]);
}
// 2. Generate 6 hourly timestamps counting back from latest
$labels = [];
$times = [];
$latestCarbon = Carbon::parse($latest);
for ($i = 5; $i >= 0; $i--) {
$ts = $latestCarbon->copy()->subHours($i);
$labels[] = $ts->format('H:i'); // chart label
$times[] = $ts; // for query matching
}
// 3. Query rainfall data for these 6 timestamps
$graphData = DB::table('rainfall')
->select(
DB::raw("TO_CHAR(timestamp, 'HH24:MI') as time"),
'hourly'
)
->where('stationid', $stationid)
->whereBetween('timestamp', [
$latestCarbon->copy()->subHours(5),
$latestCarbon
])
->orderBy('timestamp')
->get();
// 4. Map data to labels, fill missing hours with 0
$data = [];
foreach ($labels as $label) {
$record = $graphData->firstWhere('time', $label);
$data[] = $record ? $record->hourly : 0;
}
return response()->json([
'labels' => $labels,
'data' => $data
]);
}
//Return View Graph Page
public function graphPage($stationid,$dates)
{
// $station = DB::table('station')->where('stationid',$stationid)->first();
return view('layout.graph.rainfall',compact('stationid','dates'));
}
// Function for Retrieve Historical Rainfall Data
public function historicalRainfall(Request $request)
{
$stationFilter = $request->get('station');
$startDateInput = $request->input('startdate');
$endDateInput = $request->input('enddate');
$displayDate = $startDateInput ?: now();
$displayEndDate = $endDateInput ?: now();
$stations = DB::table('station')
->select('stationid', 'name')
->where('rainfall', 1)
->orderBy('stationid')
->get();
$startDate = Carbon::parse($startDateInput)->toDateString();
$endDate = Carbon::parse($endDateInput)->toDateString();
// Build the hourly columns without referencing grouped outer query
$hourlyColumns = [];
for ($i = 0; $i <= 23; $i++) {
$hour = sprintf("%02d", $i);
$hourlyColumns[] = "
MAX(CASE WHEN EXTRACT(HOUR FROM timestamp) = $i THEN hourly END) AS hour_$hour
";
}
$hourlyColumnSql = implode(",", $hourlyColumns);
// Use a CTE to get latest per hour per day first
$sql = "
WITH latest_per_hour AS (
SELECT DISTINCT ON (stationid, timestamp::date, EXTRACT(HOUR FROM timestamp))
stationid,
timestamp,
hourly,
daily
FROM rainfall
WHERE stationid = :stationid
AND timestamp::date BETWEEN :startDate AND :endDate
ORDER BY stationid, timestamp::date, EXTRACT(HOUR FROM timestamp), timestamp DESC
)
SELECT
timestamp::date AS date,
stationid,
$hourlyColumnSql,
MAX(daily) FILTER (WHERE EXTRACT(HOUR FROM timestamp) <> 0) AS total_24
FROM latest_per_hour
GROUP BY timestamp::date, stationid
ORDER BY timestamp::date
";
$historyData = collect(DB::select($sql, [
'stationid' => $stationFilter,
'startDate' => $startDate,
'endDate' => $endDate
]));
return view('layout.historicalrainfall', compact(
'historyData',
'stations',
'displayDate',
'displayEndDate'
));
}
// Function for export Historical Rainfall To Excel File
public function exportHourlyRainfallExcel(Request $request)
{
$stationid = $request->get('station');
$startDate = $request->input('startdate');
$endDate = $request->input('enddate');
$startDate2 = Carbon::parse($startDate)->toDateString();
$endDate2 = Carbon::parse($endDate)->toDateString();
return Excel::download(
new HourlyRainfallExport($stationid,$startDate,$endDate),"Hourly Rainfall {$stationid} {$startDate2} - {$endDate2}.xlsx"
);
}
}