388 lines
11 KiB
PHP
388 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* Helper Functions for Telvero Talpa Planning System
|
|
* Contains utility functions for color generation, time calculations, and data formatting
|
|
*/
|
|
|
|
/**
|
|
* Generate a distinct color that doesn't exist in the provided array
|
|
* @param array $existingColors Array of existing hex color codes
|
|
* @return string Hex color code
|
|
*/
|
|
function generateDistinctColor($existingColors = []) {
|
|
// Predefined palette with good contrast and visibility
|
|
$colorPalette = [
|
|
'#3498db', // Blue
|
|
'#e74c3c', // Red
|
|
'#9b59b6', // Purple
|
|
'#2ecc71', // Green
|
|
'#f39c12', // Orange
|
|
'#1abc9c', // Turquoise
|
|
'#e67e22', // Dark Orange
|
|
'#34495e', // Dark Gray
|
|
'#16a085', // Sea Green
|
|
'#c0392b', // Dark Red
|
|
'#8e44ad', // Dark Purple
|
|
'#27ae60', // Dark Green
|
|
'#d35400', // Pumpkin
|
|
'#2980b9', // Belize Blue
|
|
'#8e44ad', // Wisteria
|
|
'#16a085', // Green Sea
|
|
'#c0392b', // Pomegranate
|
|
'#f39c12', // Sun Flower
|
|
];
|
|
|
|
// Find first color not in existing colors
|
|
foreach ($colorPalette as $color) {
|
|
if (!in_array($color, $existingColors)) {
|
|
return $color;
|
|
}
|
|
}
|
|
|
|
// If all predefined colors are used, generate random distinct color
|
|
return generateRandomColor($existingColors);
|
|
}
|
|
|
|
/**
|
|
* Generate a random color with good saturation and lightness
|
|
* @param array $existingColors Array of existing colors to avoid
|
|
* @return string Hex color code
|
|
*/
|
|
function generateRandomColor($existingColors = []) {
|
|
$maxAttempts = 50;
|
|
$attempt = 0;
|
|
|
|
while ($attempt < $maxAttempts) {
|
|
$hue = rand(0, 360);
|
|
$saturation = rand(60, 90); // Good saturation
|
|
$lightness = rand(45, 65); // Good visibility
|
|
|
|
$color = hslToHex($hue, $saturation, $lightness);
|
|
|
|
// Check if color is distinct enough from existing colors
|
|
if (isColorDistinct($color, $existingColors)) {
|
|
return $color;
|
|
}
|
|
|
|
$attempt++;
|
|
}
|
|
|
|
// Fallback to a random color
|
|
return sprintf('#%06X', mt_rand(0, 0xFFFFFF));
|
|
}
|
|
|
|
/**
|
|
* Check if a color is distinct from existing colors
|
|
* @param string $newColor Hex color code
|
|
* @param array $existingColors Array of existing hex color codes
|
|
* @return bool True if distinct enough
|
|
*/
|
|
function isColorDistinct($newColor, $existingColors, $threshold = 50) {
|
|
if (empty($existingColors)) {
|
|
return true;
|
|
}
|
|
|
|
list($r1, $g1, $b1) = sscanf($newColor, "#%02x%02x%02x");
|
|
|
|
foreach ($existingColors as $existingColor) {
|
|
list($r2, $g2, $b2) = sscanf($existingColor, "#%02x%02x%02x");
|
|
|
|
// Calculate color distance (Euclidean distance in RGB space)
|
|
$distance = sqrt(
|
|
pow($r1 - $r2, 2) +
|
|
pow($g1 - $g2, 2) +
|
|
pow($b1 - $b2, 2)
|
|
);
|
|
|
|
if ($distance < $threshold) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Convert HSL to Hex color
|
|
* @param int $h Hue (0-360)
|
|
* @param int $s Saturation (0-100)
|
|
* @param int $l Lightness (0-100)
|
|
* @return string Hex color code
|
|
*/
|
|
function hslToHex($h, $s, $l) {
|
|
$h /= 360;
|
|
$s /= 100;
|
|
$l /= 100;
|
|
|
|
if ($s == 0) {
|
|
$r = $g = $b = $l;
|
|
} else {
|
|
$q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
|
|
$p = 2 * $l - $q;
|
|
|
|
$r = hueToRgb($p, $q, $h + 1/3);
|
|
$g = hueToRgb($p, $q, $h);
|
|
$b = hueToRgb($p, $q, $h - 1/3);
|
|
}
|
|
|
|
return sprintf("#%02x%02x%02x", round($r * 255), round($g * 255), round($b * 255));
|
|
}
|
|
|
|
/**
|
|
* Helper function for HSL to RGB conversion
|
|
*/
|
|
function hueToRgb($p, $q, $t) {
|
|
if ($t < 0) $t += 1;
|
|
if ($t > 1) $t -= 1;
|
|
if ($t < 1/6) return $p + ($q - $p) * 6 * $t;
|
|
if ($t < 1/2) return $q;
|
|
if ($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6;
|
|
return $p;
|
|
}
|
|
|
|
/**
|
|
* Calculate the next start time based on previous transmissions in a block
|
|
* @param PDO $db Database connection
|
|
* @param string $blockDate Date in Y-m-d format
|
|
* @param string $channel Channel name
|
|
* @param string $blockStartTime Optional block start time override
|
|
* @return string Time in H:i:s format
|
|
*/
|
|
function calculateNextStartTime($db, $blockDate, $channel, $blockStartTime = null) {
|
|
// Get all transmissions for this block, ordered by time
|
|
$stmt = $db->prepare("
|
|
SELECT start_time, duration
|
|
FROM transmissions
|
|
WHERE start_date = ? AND channel = ?
|
|
ORDER BY start_time ASC
|
|
");
|
|
$stmt->execute([$blockDate, $channel]);
|
|
$transmissions = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// If no transmissions, return block start time
|
|
if (empty($transmissions)) {
|
|
if ($blockStartTime) {
|
|
return $blockStartTime;
|
|
}
|
|
|
|
// Get block start time from daily_blocks or template
|
|
$stmt = $db->prepare("
|
|
SELECT actual_start_time
|
|
FROM daily_blocks
|
|
WHERE block_date = ? AND channel = ?
|
|
LIMIT 1
|
|
");
|
|
$stmt->execute([$blockDate, $channel]);
|
|
$time = $stmt->fetchColumn();
|
|
|
|
return $time ?: '07:00:00'; // Default fallback
|
|
}
|
|
|
|
// Calculate end time of last transmission
|
|
$lastTx = end($transmissions);
|
|
$startTime = new DateTime($lastTx['start_time']);
|
|
|
|
// Add duration to get end time
|
|
list($h, $m, $s) = explode(':', $lastTx['duration']);
|
|
$interval = new DateInterval("PT{$h}H{$m}M{$s}S");
|
|
$startTime->add($interval);
|
|
|
|
return $startTime->format('H:i:s');
|
|
}
|
|
|
|
/**
|
|
* Convert time string to seconds
|
|
* @param string $time Time in HH:MM:SS format
|
|
* @return int Seconds
|
|
*/
|
|
function timeToSeconds($time) {
|
|
list($h, $m, $s) = explode(':', $time);
|
|
return ($h * 3600) + ($m * 60) + $s;
|
|
}
|
|
|
|
/**
|
|
* Convert seconds to time string
|
|
* @param int $seconds Seconds
|
|
* @return string Time in HH:MM:SS format
|
|
*/
|
|
function secondsToTime($seconds) {
|
|
$hours = floor($seconds / 3600);
|
|
$minutes = floor(($seconds % 3600) / 60);
|
|
$secs = $seconds % 60;
|
|
|
|
return sprintf('%02d:%02d:%02d', $hours, $minutes, $secs);
|
|
}
|
|
|
|
/**
|
|
* Add time duration to a time string
|
|
* @param string $startTime Time in HH:MM:SS format
|
|
* @param string $duration Duration in HH:MM:SS format
|
|
* @return string Resulting time in HH:MM:SS format
|
|
*/
|
|
function addTimeToTime($startTime, $duration) {
|
|
$startSeconds = timeToSeconds($startTime);
|
|
$durationSeconds = timeToSeconds($duration);
|
|
$totalSeconds = $startSeconds + $durationSeconds;
|
|
|
|
return secondsToTime($totalSeconds);
|
|
}
|
|
|
|
/**
|
|
* Check if two time ranges overlap
|
|
* @param string $start1 Start time 1
|
|
* @param string $end1 End time 1
|
|
* @param string $start2 Start time 2
|
|
* @param string $end2 End time 2
|
|
* @return bool True if overlap exists
|
|
*/
|
|
function timeRangesOverlap($start1, $end1, $start2, $end2) {
|
|
$s1 = timeToSeconds($start1);
|
|
$e1 = timeToSeconds($end1);
|
|
$s2 = timeToSeconds($start2);
|
|
$e2 = timeToSeconds($end2);
|
|
|
|
return ($s1 < $e2) && ($e1 > $s2);
|
|
}
|
|
|
|
/**
|
|
* Get or create daily blocks for a specific date range
|
|
* @param PDO $db Database connection
|
|
* @param string $startDate Start date
|
|
* @param string $endDate End date
|
|
* @return array Array of daily blocks
|
|
*/
|
|
function ensureDailyBlocks($db, $startDate, $endDate) {
|
|
$start = new DateTime($startDate);
|
|
$end = new DateTime($endDate);
|
|
$interval = new DateInterval('P1D');
|
|
$period = new DatePeriod($start, $interval, $end->modify('+1 day'));
|
|
|
|
$dayMap = [
|
|
'Monday' => 'ma',
|
|
'Tuesday' => 'di',
|
|
'Wednesday' => 'wo',
|
|
'Thursday' => 'do',
|
|
'Friday' => 'vr',
|
|
'Saturday' => 'za',
|
|
'Sunday' => 'zo'
|
|
];
|
|
|
|
foreach ($period as $date) {
|
|
$dateStr = $date->format('Y-m-d');
|
|
$dayOfWeek = $dayMap[$date->format('l')];
|
|
|
|
// Get active templates for this day
|
|
$stmt = $db->prepare("
|
|
SELECT * FROM block_templates
|
|
WHERE is_active = 1
|
|
AND (day_of_week = ? OR day_of_week = 'all')
|
|
");
|
|
$stmt->execute([$dayOfWeek]);
|
|
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
foreach ($templates as $template) {
|
|
// Check if daily block already exists for this template
|
|
$stmt = $db->prepare("
|
|
SELECT id FROM daily_blocks
|
|
WHERE block_date = ? AND channel = ? AND template_id = ?
|
|
");
|
|
$stmt->execute([
|
|
$dateStr,
|
|
$template['channel'],
|
|
$template['id']
|
|
]);
|
|
|
|
if (!$stmt->fetch()) {
|
|
// Create daily block from template
|
|
$stmt = $db->prepare("
|
|
INSERT IGNORE INTO daily_blocks
|
|
(template_id, channel, block_date, actual_start_time, actual_end_time)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
");
|
|
$stmt->execute([
|
|
$template['id'],
|
|
$template['channel'],
|
|
$dateStr,
|
|
$template['default_start_time'],
|
|
$template['default_end_time']
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format date for display in Dutch
|
|
* @param string $date Date string
|
|
* @return string Formatted date
|
|
*/
|
|
function formatDateDutch($date) {
|
|
$dt = new DateTime($date);
|
|
$dayNames = ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'];
|
|
$monthNames = [
|
|
'', 'januari', 'februari', 'maart', 'april', 'mei', 'juni',
|
|
'juli', 'augustus', 'september', 'oktober', 'november', 'december'
|
|
];
|
|
|
|
$dayOfWeek = $dayNames[$dt->format('w')];
|
|
$day = $dt->format('j');
|
|
$month = $monthNames[(int)$dt->format('n')];
|
|
$year = $dt->format('Y');
|
|
|
|
return ucfirst($dayOfWeek) . ' ' . $day . ' ' . $month . ' ' . $year;
|
|
}
|
|
|
|
/**
|
|
* Get database connection
|
|
* @return PDO Database connection
|
|
*/
|
|
function getDbConnection() {
|
|
static $db = null;
|
|
|
|
if ($db === null) {
|
|
$db = new PDO(
|
|
"mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_NAME']};charset=utf8mb4",
|
|
$_ENV['DB_USER'],
|
|
$_ENV['DB_PASS'],
|
|
[
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
PDO::ATTR_EMULATE_PREPARES => false
|
|
]
|
|
);
|
|
}
|
|
|
|
return $db;
|
|
}
|
|
|
|
/**
|
|
* Send JSON response
|
|
* @param mixed $data Data to send
|
|
* @param int $statusCode HTTP status code
|
|
*/
|
|
function jsonResponse($data, $statusCode = 200) {
|
|
http_response_code($statusCode);
|
|
header('Content-Type: application/json');
|
|
echo json_encode($data);
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Validate time format (HH:MM:SS or HH:MM)
|
|
* @param string $time Time string
|
|
* @return bool True if valid
|
|
*/
|
|
function isValidTime($time) {
|
|
return preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/', $time);
|
|
}
|
|
|
|
/**
|
|
* Validate date format (YYYY-MM-DD)
|
|
* @param string $date Date string
|
|
* @return bool True if valid
|
|
*/
|
|
function isValidDate($date) {
|
|
$d = DateTime::createFromFormat('Y-m-d', $date);
|
|
return $d && $d->format('Y-m-d') === $date;
|
|
}
|