Skip to content

Commit

Permalink
feat: Add streak tracking functionality for user sessions; implement …
Browse files Browse the repository at this point in the history
…getStreak method in SessionRepository and corresponding controller endpoints
  • Loading branch information
MrAnyx committed Jan 27, 2025
1 parent 1cf2460 commit c334aff
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 3 deletions.
10 changes: 10 additions & 0 deletions src/Controller/Session/SessionScalarController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
13 changes: 10 additions & 3 deletions src/Controller/TestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,25 @@

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)]
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);
}
}
15 changes: 15 additions & 0 deletions src/Model/Streak.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Model;

readonly class Streak
{
public function __construct(
public int $current,
public int $longest,
public bool $inDanger,
) {
}
}
71 changes: 71 additions & 0 deletions src/Repository/SessionRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
use App\Model\Page;
use App\Model\Paginator;
use App\Model\Period;
use App\Model\Streak;
use App\Trait\UseRepositoryExtension;
use DateTimeImmutable;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

Expand Down Expand Up @@ -87,4 +89,73 @@ public function countAllByDate(User $user, ?Period $period)
->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'),
);
}
}

0 comments on commit c334aff

Please sign in to comment.