diff --git a/src/Controller/Session/SessionScalarController.php b/src/Controller/Session/SessionScalarController.php index dadce4d..fecd94c 100644 --- a/src/Controller/Session/SessionScalarController.php +++ b/src/Controller/Session/SessionScalarController.php @@ -32,4 +32,14 @@ public function countSessions( return $this->json($count); } + + #[Route('/sessions/streak', name: 'sessions_streak', methods: ['GET'])] + public function getStreak( + SessionRepository $sessionRepository, + #[CurrentUser] User $user, + ) { + $streak = $sessionRepository->getStreak($user); + + return $this->json($streak); + } } diff --git a/src/Controller/TestController.php b/src/Controller/TestController.php index c731530..3f1a7d8 100644 --- a/src/Controller/TestController.php +++ b/src/Controller/TestController.php @@ -6,9 +6,12 @@ use App\Attribute\RelativeToEntity; use App\Entity\Topic; -use App\Model\Period; +use App\Entity\User; +use App\Repository\SessionRepository; +use DateTimeImmutable; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\CurrentUser; #[Route('/_internal', name: 'api_', format: 'json')] #[RelativeToEntity(Topic::class)] @@ -16,8 +19,12 @@ class TestController extends AbstractRestController { #[Route('/test', name: 'test')] public function index( - ?Period $period, + SessionRepository $sessionRepository, + #[CurrentUser] + User $user ): JsonResponse { - return $this->json($period); + $streak = $sessionRepository->getStreak($user); + + return $this->json($streak); } } diff --git a/src/Model/Streak.php b/src/Model/Streak.php new file mode 100644 index 0000000..fd35707 --- /dev/null +++ b/src/Model/Streak.php @@ -0,0 +1,15 @@ +getQuery() ->getResult(); } + + public function getStreak(User $user) + { + $datesRaw = $this->createQueryBuilder('s') + ->select('DATE(s.startedAt) AS date') // Select only the date + ->leftJoin('s.reviews', 'r') // Join with the reviews table + ->where('s.author = :user') // Filter by the author + ->andWhere('r.id IS NOT NULL') // Ensure at least one review exists + ->andWhere('r.reset = :reset') // And the review is not a reset + // ->groupBy('date') // Group by the date + ->setParameter('user', $user) // Bind the user parameter + ->setParameter('reset', false) // Bind the reset parameter + ->getQuery() + ->getResult(); + + if (count($datesRaw) === 0) { + return new Streak(current: 0, longest: 0, inDanger: true); + } + + $dates = array_map(fn ($item) => new DateTimeImmutable($item['date']), $datesRaw); + usort($dates, fn ($a, $b) => $a <=> $b); + + $previousDate = null; + $longestStreak = 0; + $currentStreak = 0; + $currentPeriod = 0; + $today = new DateTimeImmutable(); + $streakIncludesToday = false; + + foreach ($dates as $date) { + if ($previousDate) { + $interval = $date->diff($previousDate)->days; + + if ($interval === 1) { // Consecutive day + $currentPeriod++; + } else { // Streak breaks + $longestStreak = max($longestStreak, $currentPeriod); + $currentPeriod = 1; // Reset streak + } + } else { + $currentPeriod = 1; // First date starts the streak + } + + // Check if the current streak includes today or yesterday + if ($date->format('Y-m-d') === $today->format('Y-m-d')) { + $streakIncludesToday = true; + $currentStreak = $currentPeriod; + } elseif ($date->format('Y-m-d') === $today->modify('-1 day')->format('Y-m-d') && !$streakIncludesToday) { + $streakIncludesToday = true; + $currentStreak = $currentPeriod; + } + + $previousDate = $date; + } + + // Final check to update the longest streak + $longestStreak = max($longestStreak, $currentPeriod); + + // If today or yesterday doesn't continue the streak, set current streak to 0 + if (!$streakIncludesToday) { + $currentStreak = 0; + } + + return new Streak( + current: $currentStreak, + longest: $longestStreak, + inDanger: end($dates)?->format('Y-m-d') !== $today->format('Y-m-d'), + ); + } }