telvero_whatson_talpa/helpers.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;
}