Improved calendar performance
This commit is contained in:
parent
0fbc7ddd20
commit
603bf0a5a2
@ -1,4 +1,4 @@
|
|||||||
FROM php:8.2-cli
|
FROM php:8.2-cli as base
|
||||||
|
|
||||||
# Install system dependencies
|
# Install system dependencies
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
@ -21,14 +21,15 @@ RUN curl -sS https://get.symfony.com/cli/installer | bash && \
|
|||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
|
CMD ["symfony", "serve", "--port=8000", "--no-tls", "--allow-http", "--allow-all-ip"]
|
||||||
|
|
||||||
|
FROM base as production
|
||||||
|
|
||||||
# Copy application files
|
# Copy application files
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
RUN composer install --no-interaction --optimize-autoloader
|
RUN composer install --no-interaction --optimize-autoloader
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 8000
|
|
||||||
|
|
||||||
# Command to run Symfony server
|
# Command to run Symfony server
|
||||||
CMD ["symfony", "serve", "--port=8000", "--no-tls", "--allow-http", "--allow-all-ip"]
|
CMD ["symfony", "serve", "--port=8000", "--no-tls", "--allow-http", "--allow-all-ip"]
|
||||||
|
|||||||
@ -5,8 +5,9 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
target: production
|
||||||
environment:
|
environment:
|
||||||
- APP_ENV=dev
|
- APP_ENV=prod
|
||||||
- APP_DEBUG=1
|
- APP_DEBUG=1
|
||||||
networks:
|
networks:
|
||||||
- proxy
|
- proxy
|
||||||
|
|||||||
@ -3,6 +3,11 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
target: base
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
environment:
|
environment:
|
||||||
- APP_ENV=dev
|
- APP_ENV=dev
|
||||||
- APP_DEBUG=1
|
- APP_DEBUG=1
|
||||||
|
volumes:
|
||||||
|
- ./:/var/www/html
|
||||||
|
|||||||
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\HomeAssistant\HomeAssistantClient;
|
||||||
use App\Service\AbsenceManager;
|
use App\Service\AbsenceManager;
|
||||||
use App\Service\CalendarExportService;
|
use App\Service\CalendarExportService;
|
||||||
use App\Service\CalendarService;
|
use App\Service\CalendarService;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
use DateTimeImmutable;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
@ -17,6 +19,7 @@ final class CalendarController extends AbstractController
|
|||||||
private readonly AbsenceManager $absenceManager,
|
private readonly AbsenceManager $absenceManager,
|
||||||
private readonly CalendarService $calendarService,
|
private readonly CalendarService $calendarService,
|
||||||
private readonly CalendarExportService $calendarExportService,
|
private readonly CalendarExportService $calendarExportService,
|
||||||
|
private readonly HomeAssistantClient $homeAssistantClient,
|
||||||
private readonly string $requiredApiKey
|
private readonly string $requiredApiKey
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@ -57,12 +60,18 @@ final class CalendarController extends AbstractController
|
|||||||
|
|
||||||
$days = $this->calendarService->getAllDays($startDate, $endDate);
|
$days = $this->calendarService->getAllDays($startDate, $endDate);
|
||||||
$days = $this->calendarService->processAbsences($days, $absences);
|
$days = $this->calendarService->processAbsences($days, $absences);
|
||||||
|
|
||||||
|
$entityHistory = $this->homeAssistantClient->getEntityStateHistory(
|
||||||
|
'person.tim',
|
||||||
|
new DateTimeImmutable('-1 year'),
|
||||||
|
new DateTimeImmutable('+180 days')
|
||||||
|
);
|
||||||
|
|
||||||
$icsContent = $this->calendarExportService->generateIcsContent($days, $absences);
|
$icsContent = $this->calendarExportService->generateIcsContent($days, $entityHistory, $absences);
|
||||||
|
|
||||||
$response = new Response($icsContent);
|
$response = new Response($icsContent);
|
||||||
$response->headers->set('Content-Type', 'text/calendar; charset=utf-8');
|
//$response->headers->set('Content-Type', 'text/calendar; charset=utf-8');
|
||||||
$response->headers->set('Content-Disposition', 'attachment; filename="work_calendar.ics"');
|
//$response->headers->set('Content-Disposition', 'attachment; filename="work_calendar.ics"');
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ final readonly class HomeAssistantClient
|
|||||||
'filter_entity_id' => $entityId,
|
'filter_entity_id' => $entityId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->request('GET', "/api/history/period/{$startDate->format('Y-m-d\TH:i:s\Z')}?{$queryParams}");
|
return $this->request('GET', "/api/history/period/{$startDate->format('Y-m-d\TH:i:s\Z')}?{$queryParams}")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function callService(string $domain, string $service, array $data = []): array
|
public function callService(string $domain, string $service, array $data = []): array
|
||||||
|
|||||||
@ -14,7 +14,6 @@ final class CalendarExportService
|
|||||||
private const KAMEN_ADDRESS = 'Privat \nHeidkamp 21\n59174 Kamen\nDeutschland';
|
private const KAMEN_ADDRESS = 'Privat \nHeidkamp 21\n59174 Kamen\nDeutschland';
|
||||||
private const COMPANY_NAME = 'CHECK24';
|
private const COMPANY_NAME = 'CHECK24';
|
||||||
private const TIMEZONE = 'Europe/Berlin';
|
private const TIMEZONE = 'Europe/Berlin';
|
||||||
private const PERSON_ENTITY = 'person.tim';
|
|
||||||
private const OFFICE_STATE = 'CHECK24';
|
private const OFFICE_STATE = 'CHECK24';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@ -22,7 +21,7 @@ final class CalendarExportService
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateIcsContent(array $days, array $absences): string
|
public function generateIcsContent(array $days, array $entityHistory, array $absences): string
|
||||||
{
|
{
|
||||||
$icsContent = [
|
$icsContent = [
|
||||||
'BEGIN:VCALENDAR',
|
'BEGIN:VCALENDAR',
|
||||||
@ -39,7 +38,14 @@ final class CalendarExportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$dayDate = clone $day['date'];
|
$dayDate = clone $day['date'];
|
||||||
$startAndEndTimes = $this->getWorkTimesFromHomeAssistant($dayDate);
|
$startOfDay = DateTimeImmutable::createFromInterface($dayDate)->setTime(0, 0, 0);
|
||||||
|
$typicalEndOfDay = DateTimeImmutable::createFromInterface($dayDate)->setTime(17, 30, 0);
|
||||||
|
|
||||||
|
$filteredEntityHistory = array_filter($entityHistory, function (array $state) use ($startOfDay, $typicalEndOfDay) {
|
||||||
|
return $state['last_changed'] >= $startOfDay && $state['last_changed'] <= $typicalEndOfDay;
|
||||||
|
});
|
||||||
|
|
||||||
|
$startAndEndTimes = $this->getWorkTimesFromHomeAssistant($filteredEntityHistory, $dayDate);
|
||||||
|
|
||||||
if ($startAndEndTimes === null) {
|
if ($startAndEndTimes === null) {
|
||||||
$eventStart = clone $day['date'];
|
$eventStart = clone $day['date'];
|
||||||
@ -74,17 +80,9 @@ final class CalendarExportService
|
|||||||
return implode("\r\n", $icsContent);
|
return implode("\r\n", $icsContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getWorkTimesFromHomeAssistant(DateTimeInterface $date): ?array
|
private function getWorkTimesFromHomeAssistant(array $stateHistory, DateTimeInterface $date): ?array
|
||||||
{
|
{
|
||||||
$startOfDay = DateTimeImmutable::createFromInterface($date)->setTime(0, 0, 0);
|
|
||||||
$typicalEndOfDay = DateTimeImmutable::createFromInterface($date)->setTime(17, 30, 0);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$stateHistory = $this->homeAssistantClient->getEntityStateHistory(
|
|
||||||
self::PERSON_ENTITY,
|
|
||||||
$startOfDay,
|
|
||||||
$typicalEndOfDay
|
|
||||||
);
|
|
||||||
|
|
||||||
if (empty($stateHistory) || empty($stateHistory[0])) {
|
if (empty($stateHistory) || empty($stateHistory[0])) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
73
src/Service/CalendarStorageService.php
Normal file
73
src/Service/CalendarStorageService.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
final class CalendarStorageService
|
||||||
|
{
|
||||||
|
private const STORAGE_FILE = 'storage/past_events.json';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store past events in the storage file
|
||||||
|
*/
|
||||||
|
public function storePastEvents(array $events): void
|
||||||
|
{
|
||||||
|
$dataDirectory = dirname(self::STORAGE_FILE);
|
||||||
|
|
||||||
|
if (!is_dir($dataDirectory)) {
|
||||||
|
if (mkdir($dataDirectory, 0777, true) === false) {
|
||||||
|
throw new RuntimeException('Failed to create storage directory');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_put_contents(self::STORAGE_FILE, json_encode($events)) === false) {
|
||||||
|
throw new RuntimeException('Failed to write past events to storage file');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get past events from the storage file
|
||||||
|
*/
|
||||||
|
public function getPastEvents(): array
|
||||||
|
{
|
||||||
|
if (!file_exists(self::STORAGE_FILE)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = file_get_contents(self::STORAGE_FILE);
|
||||||
|
|
||||||
|
if ($content === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$events = json_decode($content, true);
|
||||||
|
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $events;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if past events need to be updated by comparing with cutoff date
|
||||||
|
*/
|
||||||
|
public function shouldUpdatePastEvents(DateTime $cutoffDate): bool
|
||||||
|
{
|
||||||
|
if (!file_exists(self::STORAGE_FILE)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileModTime = filemtime(self::STORAGE_FILE);
|
||||||
|
|
||||||
|
if ($fileModTime === false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileModDate = (new DateTime())->setTimestamp($fileModTime);
|
||||||
|
|
||||||
|
return $fileModDate < $cutoffDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user