Fix hourly summary: default to today for realtime updates, add hour parameter for efficient updates

This commit is contained in:
mwpn
2025-12-17 17:41:27 +07:00
parent dae5e9d2d5
commit 1aa462d9da
4 changed files with 112 additions and 31 deletions

View File

@@ -200,14 +200,28 @@ openssl rand -hex 32
Setup di aaPanel → Cron: Setup di aaPanel → Cron:
```cron ```cron
# Daily summary (run at 1 AM every day) # Daily summary (run at 1 AM every day, rekap kemarin)
0 1 * * * cd /www/wwwroot/api.btekno.cloud/api && /www/server/php/83/bin/php bin/daily_summary.php 0 1 * * * cd /www/wwwroot/api.btekno.cloud/api && /www/server/php/83/bin/php bin/daily_summary.php
# Hourly summary (run at 1 AM every day) # Hourly summary - REALTIME UPDATE (run every hour, update jam yang baru saja berlalu)
0 1 * * * cd /www/wwwroot/api.btekno.cloud/api && /www/server/php/83/bin/php bin/hourly_summary.php # Contoh: jam 2:00 update jam 1:00, jam 3:00 update jam 2:00, dst
0 * * * * cd /www/wwwroot/api.btekno.cloud/api && /www/server/php/83/bin/php bin/hourly_summary.php today $(date -d '1 hour ago' +\%H)
# Hourly summary - FINAL RECAP (run at 1 AM every day, rekap semua jam kemarin)
# Opsional: untuk memastikan semua jam kemarin sudah ter-rekap dengan benar
0 1 * * * cd /www/wwwroot/api.btekno.cloud/api && /www/server/php/83/bin/php bin/hourly_summary.php yesterday
``` ```
**Note**: Ganti `/www/server/php/83/bin/php` dengan path PHP yang sesuai di server Anda. **Penjelasan:**
1. **Daily summary**: Rekap harian untuk kemarin (jalan jam 1 pagi)
2. **Hourly summary - REALTIME**: Update setiap jam untuk jam yang baru saja berlalu (untuk dashboard realtime)
3. **Hourly summary - FINAL RECAP**: Rekap final semua jam kemarin (opsional, untuk memastikan data lengkap)
**Note**:
- Ganti `/www/server/php/83/bin/php` dengan path PHP yang sesuai di server Anda
- Untuk update realtime, cron harus jalan **setiap jam** (`0 * * * *`)
- Script default ke `today` jika tidak ada argumen, jadi cocok untuk update realtime
## ✅ Verification ## ✅ Verification

View File

@@ -185,17 +185,32 @@ php bin/daily_summary.php [date]
### Hourly Summary ### Hourly Summary
```bash ```bash
php bin/hourly_summary.php [date] # Update hari ini (default, untuk realtime)
# Default: yesterday php bin/hourly_summary.php
# Rekap kemarin
php bin/hourly_summary.php yesterday
# Rekap tanggal tertentu
php bin/hourly_summary.php 2025-01-01
# Update jam tertentu saja (untuk efisiensi)
php bin/hourly_summary.php today 14 # Update jam 14 hari ini
php bin/hourly_summary.php 2025-01-01 13 # Update jam 13 tanggal tertentu
``` ```
### Cron Job Setup ### Cron Job Setup
```cron ```cron
# Daily summary (run at 1 AM) # Daily summary (run at 1 AM, rekap kemarin)
0 1 * * * cd /path/to/api-btekno && php bin/daily_summary.php 0 1 * * * cd /path/to/api-btekno && php bin/daily_summary.php
# Hourly summary (run at 1 AM) # Hourly summary - REALTIME (run every hour, update jam yang baru saja berlalu)
0 1 * * * cd /path/to/api-btekno && php bin/hourly_summary.php # Contoh: jam 2:00 update jam 1:00, jam 3:00 update jam 2:00
0 * * * * cd /path/to/api-btekno && php bin/hourly_summary.php today $(date -d '1 hour ago' +\%H)
# Hourly summary - FINAL RECAP (run at 1 AM, rekap semua jam kemarin)
# Opsional: untuk memastikan semua jam kemarin sudah ter-rekap
0 1 * * * cd /path/to/api-btekno && php bin/hourly_summary.php yesterday
``` ```
## 🔒 Security ## 🔒 Security

View File

@@ -7,17 +7,18 @@ declare(strict_types=1);
* CLI script untuk generate hourly summary * CLI script untuk generate hourly summary
* *
* Usage: * Usage:
* php bin/hourly_summary.php [date] * php bin/hourly_summary.php [date] [hour]
* *
* Examples: * Examples:
* php bin/hourly_summary.php 2025-01-01 * php bin/hourly_summary.php 2025-01-01 # Rekap semua jam untuk tanggal tertentu
* php bin/hourly_summary.php # default: yesterday * php bin/hourly_summary.php # Default: hari ini (untuk update realtime)
* php bin/hourly_summary.php 2025-01-01 14 # Rekap jam 14 saja untuk tanggal tertentu
* php bin/hourly_summary.php today 13 # Rekap jam 13 hari ini
* *
* Note: * Note:
* Default menggunakan yesterday karena: * - Default: hari ini (untuk update realtime via cron setiap jam)
* - Data hari ini mungkin belum lengkap (masih ada event yang masuk) * - Untuk rekap final kemarin, gunakan: php bin/hourly_summary.php yesterday
* - Cron biasanya jalan di akhir hari untuk rekap hari sebelumnya * - Untuk update jam tertentu saja, tambahkan parameter hour (0-23)
* - Lebih aman untuk rekap data yang sudah final
*/ */
// Get project root directory // Get project root directory
@@ -33,18 +34,43 @@ use App\Support\Database;
// Load environment variables // Load environment variables
AppConfig::loadEnv($rootPath); AppConfig::loadEnv($rootPath);
// Get date from command line argument or use yesterday (deterministic) // Get date from command line argument or use today (for realtime updates)
$date = $argv[1] ?? date('Y-m-d', strtotime('-1 day')); $dateInput = $argv[1] ?? 'today';
// Handle special keywords
if ($dateInput === 'today') {
$date = date('Y-m-d');
} elseif ($dateInput === 'yesterday') {
$date = date('Y-m-d', strtotime('-1 day'));
} else {
$date = $dateInput;
}
// Validate date format // Validate date format
$dateTime = DateTime::createFromFormat('Y-m-d', $date); $dateTime = DateTime::createFromFormat('Y-m-d', $date);
if ($dateTime === false || $dateTime->format('Y-m-d') !== $date) { if ($dateTime === false || $dateTime->format('Y-m-d') !== $date) {
echo "Error: Invalid date format. Expected Y-m-d (e.g., 2025-01-01)\n"; echo "Error: Invalid date format. Expected Y-m-d (e.g., 2025-01-01) or 'today'/'yesterday'\n";
echo "Usage: php bin/hourly_summary.php [date]\n"; echo "Usage: php bin/hourly_summary.php [date] [hour]\n";
echo " If date is omitted, defaults to yesterday\n"; echo " date: Y-m-d format, 'today', or 'yesterday' (default: today)\n";
echo " hour: 0-23 (optional, untuk update jam tertentu saja)\n";
echo "Examples:\n";
echo " php bin/hourly_summary.php # Update hari ini\n";
echo " php bin/hourly_summary.php yesterday # Rekap kemarin\n";
echo " php bin/hourly_summary.php 2025-01-01 14 # Rekap jam 14 tanggal tertentu\n";
exit(1); exit(1);
} }
// Get hour from command line (optional, 0-23)
$hour = null;
if (isset($argv[2])) {
$hourInput = (int) $argv[2];
if ($hourInput < 0 || $hourInput > 23) {
echo "Error: Hour must be between 0 and 23\n";
exit(1);
}
$hour = $hourInput;
}
try { try {
// Get database connection // Get database connection
$dbHost = AppConfig::get('DB_HOST', 'localhost'); $dbHost = AppConfig::get('DB_HOST', 'localhost');
@@ -63,12 +89,20 @@ try {
$service = new HourlySummaryService($db); $service = new HourlySummaryService($db);
// Run aggregation // Run aggregation
echo "Processing hourly summary for date: {$date}\n"; if ($hour !== null) {
echo "Processing hourly summary for date: {$date}, hour: {$hour}\n";
$result = $service->aggregateForDate($date, $hour);
echo "Success!\n";
echo "Date: {$result['date']}\n";
echo "Hour: {$result['hour']}\n";
echo "Rows processed: {$result['rows_processed']}\n";
} else {
echo "Processing hourly summary for date: {$date} (all hours)\n";
$result = $service->aggregateForDate($date); $result = $service->aggregateForDate($date);
echo "Success!\n"; echo "Success!\n";
echo "Date: {$result['date']}\n"; echo "Date: {$result['date']}\n";
echo "Rows processed: {$result['rows_processed']}\n"; echo "Rows processed: {$result['rows_processed']}\n";
}
exit(0); exit(0);

View File

@@ -20,10 +20,11 @@ class HourlySummaryService
* Aggregate hourly summary for a specific date * Aggregate hourly summary for a specific date
* *
* @param string $date Format: Y-m-d * @param string $date Format: Y-m-d
* @return array ['rows_processed' => int, 'date' => string] * @param int|null $hour Optional: 0-23, if provided only aggregate for this hour
* @return array ['rows_processed' => int, 'date' => string, 'hour' => int|null]
* @throws PDOException * @throws PDOException
*/ */
public function aggregateForDate(string $date): array public function aggregateForDate(string $date, ?int $hour = null): array
{ {
// Validate date format // Validate date format
$dateTime = \DateTime::createFromFormat('Y-m-d', $date); $dateTime = \DateTime::createFromFormat('Y-m-d', $date);
@@ -31,6 +32,11 @@ class HourlySummaryService
throw new \InvalidArgumentException('Invalid date format. Expected Y-m-d'); throw new \InvalidArgumentException('Invalid date format. Expected Y-m-d');
} }
// Validate hour if provided
if ($hour !== null && ($hour < 0 || $hour > 23)) {
throw new \InvalidArgumentException('Invalid hour. Must be between 0 and 23');
}
$this->db->beginTransaction(); $this->db->beginTransaction();
try { try {
@@ -55,6 +61,17 @@ class HourlySummaryService
AND e.gate_code = t.gate_code AND e.gate_code = t.gate_code
AND e.category = t.category AND e.category = t.category
WHERE DATE(e.event_time) = ? WHERE DATE(e.event_time) = ?
";
$params = [$date];
// Add hour filter if provided
if ($hour !== null) {
$sql .= " AND HOUR(e.event_time) = ?";
$params[] = $hour;
}
$sql .= "
GROUP BY GROUP BY
DATE(e.event_time), DATE(e.event_time),
HOUR(e.event_time), HOUR(e.event_time),
@@ -65,7 +82,7 @@ class HourlySummaryService
"; ";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
$stmt->execute([$date]); $stmt->execute($params);
$aggregated = $stmt->fetchAll(); $aggregated = $stmt->fetchAll();
$rowsProcessed = 0; $rowsProcessed = 0;
@@ -103,7 +120,8 @@ class HourlySummaryService
return [ return [
'rows_processed' => $rowsProcessed, 'rows_processed' => $rowsProcessed,
'date' => $date 'date' => $date,
'hour' => $hour
]; ];
} catch (PDOException $e) { } catch (PDOException $e) {