feat: default statistics filter to a rolling 12-month window
This commit is contained in:
@@ -35,8 +35,8 @@ class ApplicationsByDestinationController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$statistics = $this->applicationRepository->getApplicationsByDestination(
|
$statistics = $this->applicationRepository->getApplicationsByDestination(
|
||||||
$filterDto->getDateFrom(),
|
$filterDto->getEffectiveDateFrom(),
|
||||||
$filterDto->getDateTo(),
|
$filterDto->getEffectiveDateTo(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->render('administrative/statistics/applications_by_destination.html.twig', [
|
return $this->render('administrative/statistics/applications_by_destination.html.twig', [
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ class FeedbackByDestinationController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$statistics = $this->dispositionRepository->getFeedbackStatisticsByNormalizedHotelCode(
|
$statistics = $this->dispositionRepository->getFeedbackStatisticsByNormalizedHotelCode(
|
||||||
$filterDto->getDateFrom(),
|
$filterDto->getEffectiveDateFrom(),
|
||||||
$filterDto->getDateTo(),
|
$filterDto->getEffectiveDateTo(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->render('administrative/statistics/feedback_by_destination.html.twig', [
|
return $this->render('administrative/statistics/feedback_by_destination.html.twig', [
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ class FeedbackRatingsByDestinationController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$statistics = $this->feedbackRepository->getAverageRatingsByDestinationAndFeedbackSet(
|
$statistics = $this->feedbackRepository->getAverageRatingsByDestinationAndFeedbackSet(
|
||||||
$filterDto->getDateFrom(),
|
$filterDto->getEffectiveDateFrom(),
|
||||||
$filterDto->getDateTo(),
|
$filterDto->getEffectiveDateTo(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->render('administrative/statistics/feedback_ratings_by_destination.html.twig', [
|
return $this->render('administrative/statistics/feedback_ratings_by_destination.html.twig', [
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ class FeedbackRatingsController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$statistics = $this->feedbackRepository->getAverageRatingsByQuestionAndFeedbackSet(
|
$statistics = $this->feedbackRepository->getAverageRatingsByQuestionAndFeedbackSet(
|
||||||
$filterDto->getDateFrom(),
|
$filterDto->getEffectiveDateFrom(),
|
||||||
$filterDto->getDateTo(),
|
$filterDto->getEffectiveDateTo(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prepare chart data for each feedback set
|
// Prepare chart data for each feedback set
|
||||||
|
|||||||
@@ -32,4 +32,27 @@ class StatisticsFilterDto extends AbstractFilterDto
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The lower bound actually applied to a query: the picked date, or a rolling
|
||||||
|
* 12-month window ending today when nothing is picked.
|
||||||
|
*
|
||||||
|
* The default lives here rather than in a nullable property so that
|
||||||
|
* AbstractFilterDto::isActive() still sees a fresh instance as "not filtered"
|
||||||
|
* (it compares properties with !==, which no two DateTimeImmutable share).
|
||||||
|
*/
|
||||||
|
public function getEffectiveDateFrom(): \DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->dateFrom ?? (new \DateTimeImmutable('today'))->modify('-12 months');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEffectiveDateTo(): \DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->dateTo ?? new \DateTimeImmutable('today 23:59:59');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isUsingDefaultRange(): bool
|
||||||
|
{
|
||||||
|
return null === $this->dateFrom && null === $this->dateTo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,12 @@
|
|||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if filterDto.active %}
|
<p class="text-sm text-gray-600 mb-4">
|
||||||
<p class="text-sm text-gray-600 mb-4">
|
Zeitraum: {{ filterDto.effectiveDateFrom|date('d.m.Y') }} – {{ filterDto.effectiveDateTo|date('d.m.Y') }}
|
||||||
Filter aktiv:
|
<span class="text-gray-400">
|
||||||
{% if filterDto.dateFrom %}von {{ filterDto.dateFrom|date('d.m.Y') }}{% endif %}
|
{% if filterDto.usingDefaultRange %}(Standard: letzte 12 Monate){% else %}(Filter aktiv){% endif %}
|
||||||
{% if filterDto.dateTo %}bis {{ filterDto.dateTo|date('d.m.Y') }}{% endif %}
|
</span>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if statistics is empty %}
|
{% if statistics is empty %}
|
||||||
<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">
|
<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">
|
||||||
|
|||||||
@@ -24,13 +24,12 @@
|
|||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if filterDto.active %}
|
<p class="text-sm text-gray-600 mb-4">
|
||||||
<p class="text-sm text-gray-600 mb-4">
|
Zeitraum: {{ filterDto.effectiveDateFrom|date('d.m.Y') }} – {{ filterDto.effectiveDateTo|date('d.m.Y') }}
|
||||||
Filter aktiv:
|
<span class="text-gray-400">
|
||||||
{% if filterDto.dateFrom %}von {{ filterDto.dateFrom|date('d.m.Y') }}{% endif %}
|
{% if filterDto.usingDefaultRange %}(Standard: letzte 12 Monate){% else %}(Filter aktiv){% endif %}
|
||||||
{% if filterDto.dateTo %}bis {{ filterDto.dateTo|date('d.m.Y') }}{% endif %}
|
</span>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if statistics is empty %}
|
{% if statistics is empty %}
|
||||||
<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">
|
<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">
|
||||||
|
|||||||
@@ -24,13 +24,12 @@
|
|||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if filterDto.active %}
|
<p class="text-sm text-gray-600 mb-4">
|
||||||
<p class="text-sm text-gray-600 mb-4">
|
Zeitraum: {{ filterDto.effectiveDateFrom|date('d.m.Y') }} – {{ filterDto.effectiveDateTo|date('d.m.Y') }}
|
||||||
Filter aktiv:
|
<span class="text-gray-400">
|
||||||
{% if filterDto.dateFrom %}von {{ filterDto.dateFrom|date('d.m.Y') }}{% endif %}
|
{% if filterDto.usingDefaultRange %}(Standard: letzte 12 Monate){% else %}(Filter aktiv){% endif %}
|
||||||
{% if filterDto.dateTo %}bis {{ filterDto.dateTo|date('d.m.Y') }}{% endif %}
|
</span>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if chartsData is empty %}
|
{% if chartsData is empty %}
|
||||||
<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">
|
<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">
|
||||||
|
|||||||
@@ -24,13 +24,12 @@
|
|||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if filterDto.active %}
|
<p class="text-sm text-gray-600 mb-4">
|
||||||
<p class="text-sm text-gray-600 mb-4">
|
Zeitraum: {{ filterDto.effectiveDateFrom|date('d.m.Y') }} – {{ filterDto.effectiveDateTo|date('d.m.Y') }}
|
||||||
Filter aktiv:
|
<span class="text-gray-400">
|
||||||
{% if filterDto.dateFrom %}von {{ filterDto.dateFrom|date('d.m.Y') }}{% endif %}
|
{% if filterDto.usingDefaultRange %}(Standard: letzte 12 Monate){% else %}(Filter aktiv){% endif %}
|
||||||
{% if filterDto.dateTo %}bis {{ filterDto.dateTo|date('d.m.Y') }}{% endif %}
|
</span>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if destinations is empty %}
|
{% if destinations is empty %}
|
||||||
<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">
|
<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Model;
|
||||||
|
|
||||||
|
use App\Model\StatisticsFilterDto;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class StatisticsFilterDtoTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testFreshDtoFallsBackToARollingTwelveMonthWindow(): void
|
||||||
|
{
|
||||||
|
$dto = new StatisticsFilterDto();
|
||||||
|
|
||||||
|
$expectedFrom = (new \DateTimeImmutable('today'))->modify('-12 months');
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$expectedFrom->format('Y-m-d'),
|
||||||
|
$dto->getEffectiveDateFrom()->format('Y-m-d'),
|
||||||
|
);
|
||||||
|
$this->assertSame(
|
||||||
|
(new \DateTimeImmutable('today'))->format('Y-m-d'),
|
||||||
|
$dto->getEffectiveDateTo()->format('Y-m-d'),
|
||||||
|
);
|
||||||
|
$this->assertTrue($dto->isUsingDefaultRange());
|
||||||
|
$this->assertFalse($dto->isActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAPickedLowerBoundStopsTheFromFallbackButNotTheToFallback(): void
|
||||||
|
{
|
||||||
|
$from = new \DateTimeImmutable('2025-03-01');
|
||||||
|
|
||||||
|
$dto = (new StatisticsFilterDto())->setDateFrom($from);
|
||||||
|
|
||||||
|
$this->assertSame($from, $dto->getEffectiveDateFrom());
|
||||||
|
$this->assertSame(
|
||||||
|
(new \DateTimeImmutable('today'))->format('Y-m-d'),
|
||||||
|
$dto->getEffectiveDateTo()->format('Y-m-d'),
|
||||||
|
);
|
||||||
|
$this->assertFalse($dto->isUsingDefaultRange());
|
||||||
|
$this->assertTrue($dto->isActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBothBoundsPickedAreReturnedVerbatim(): void
|
||||||
|
{
|
||||||
|
$from = new \DateTimeImmutable('2025-01-01');
|
||||||
|
$to = new \DateTimeImmutable('2025-06-30');
|
||||||
|
|
||||||
|
$dto = (new StatisticsFilterDto())
|
||||||
|
->setDateFrom($from)
|
||||||
|
->setDateTo($to)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->assertSame($from, $dto->getEffectiveDateFrom());
|
||||||
|
$this->assertSame($to, $dto->getEffectiveDateTo());
|
||||||
|
$this->assertFalse($dto->isUsingDefaultRange());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user