src/Controller/DashboardController.php line 19

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Device;
  4. use App\Entity\Exercise;
  5. use App\Entity\DataVersion;
  6. use App\Entity\WorkoutData;
  7. use App\Entity\WorkoutTemplate;
  8. use Doctrine\ORM\EntityManagerInterface;
  9. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  10. use Symfony\Component\HttpFoundation\Response;
  11. use Symfony\Component\Routing\Annotation\Route;
  12. #[Route('/admin')]
  13. class DashboardController extends AbstractController
  14. {
  15.     #[Route('/'name'app_dashboard')]
  16.     public function index(EntityManagerInterface $em): Response
  17.     {
  18.         $workouts $em->getRepository(WorkoutData::class)
  19.             ->createQueryBuilder('w')
  20.             ->where('w.gymName IN (:gymNames)')
  21.             ->setParameter('gymNames', [
  22.                 'UPDATE_FITNESS_OSTERMUNDIGEN',
  23.                 'UPDATE_FITNESS_MARKTGASSE',
  24.                 'UPDATE_FITNESS_TRUE_MARKTGASSE',
  25.                 'UPDATE_FITNESS_MUNCHWILEN',
  26.             ])
  27.             ->getQuery()
  28.             ->getResult();
  29. // Now $workoutDataByGymByDay includes *every* date from the earliest to latest
  30. // for each gym, with missing days set to 0.
  31. //        dd($workoutDataByGymByDay);
  32.         return $this->render('dashboard/index.html.twig', [
  33.             'workout_templates' => [
  34.                 'count' => $em->getRepository(WorkoutTemplate::class)->getEntriesCount(),
  35.             ],
  36.             'exercises' => [
  37.                 'count' => $em->getRepository(Exercise::class)->getEntriesCount(),
  38.             ],
  39.             'gyms' => [
  40.                 'count' => $em->getRepository(Device::class)->getEntriesCount(),
  41.             ],
  42.             'dataVersion' => $em->getRepository(DataVersion::class)
  43.                 ->findOneBy([], ['id' => 'ASC'])
  44.                 ->getVersionCounter(),
  45.             'devices' => $em->getRepository(Device::class)->findAll(),
  46.             'workoutFrequencyByDayByGym' => $this->computeDailyWorkoutCountsByGym($workouts),
  47.             'workoutCount' => count($workouts),
  48.             'activityTime' => $this->computeTotalWorkoutTime($workouts),
  49.             'setsCount' => $this->computeTotalSets($workouts),
  50.             'repsCount' => $this->computeTotalRepetitions($workouts),
  51.             'avgBTScore' => $this->computeMedianRythmScore($workouts),
  52.             'rhythmScores' => $this->getAllRythmScores($workouts),
  53.             'moduleTypeRatios' => $this->computeModuleTypeFrequencies($workouts),
  54.             'timeToStartPoseByExercise' => $this->computeAverageTimeToStartPoseByExercise($workouts),
  55.             'rhythmScoreByExercise' => $this->computeAverageRythmScoreByExercise($workouts),
  56.             'workoutTemplateFrequency' => $this->computeWorkoutsPerTemplate($workouts$em),
  57.         ]);
  58.     }
  59.     private function computeDailyWorkoutCountsByGym(array $workouts): array
  60.     {
  61.         $validGyms = [
  62.             'UPDATE_FITNESS_OSTERMUNDIGEN',
  63.             'UPDATE_FITNESS_MARKTGASSE',
  64.             'UPDATE_FITNESS_TRUE_MARKTGASSE',
  65.             'UPDATE_FITNESS_MUNCHWILEN',
  66.         ];
  67.         $earliestTimestamp null;
  68.         foreach ($workouts as $workout) {
  69.             $start $workout->getStartTimestamp();
  70.             if ($start !== null && $start 0) {
  71.                 if ($earliestTimestamp === null || $start $earliestTimestamp) {
  72.                     $earliestTimestamp $start;
  73.                 }
  74.             }
  75.         }
  76.         if ($earliestTimestamp === null) {
  77.             return [];
  78.         }
  79.         $startDate = (new \DateTime())->setTimestamp((int) ($earliestTimestamp 1000))->setTime(000);
  80.         $today = new \DateTime('today');
  81.         $result = [];
  82.         $currentDate = clone $startDate;
  83.         $workoutCounts = [];
  84.         foreach ($workouts as $workout) {
  85.             $start $workout->getStartTimestamp();
  86.             $gymName $workout->getGymName();
  87.             if ($start === null || $start == || !in_array($gymName$validGyms)) {
  88.                 continue;
  89.             }
  90.             $date = (new \DateTime())->setTimestamp((int) ($start 1000))->format('d.m.Y');
  91.             if (!isset($workoutCounts[$date])) {
  92.                 $workoutCounts[$date] = array_fill_keys($validGyms0);
  93.             }
  94.             $workoutCounts[$date][$gymName]++;
  95.         }
  96.         while ($currentDate <= $today) {
  97.             $dayKey $currentDate->format('d.m.Y');
  98.             $counts $workoutCounts[$dayKey] ?? array_fill_keys($validGyms0);
  99.             $result[] = array_merge(['day' => $dayKey], $counts);
  100.             $currentDate->modify('+1 day');
  101.         }
  102.         return $result;
  103.     }
  104.     function getAllRythmScores(array $workouts): array
  105.     {
  106.         $rythmScores = [];
  107.         // Iterate over each workout
  108.         foreach ($workouts as $workout) {
  109.             /** @var WorkoutData $workout */
  110.             $jsonData $workout->getJsonData();
  111.             // Skip if jsonData is null or empty
  112.             if ($jsonData === null || $jsonData === '') {
  113.                 continue;
  114.             }
  115.             // Decode JSON string to array
  116.             $decodedData json_decode($jsonDatatrue); // true = return as array
  117.             // Check if decoding succeeded and setData exists
  118.             if ($decodedData === null || !isset($decodedData['setData']) || !is_array($decodedData['setData'])) {
  119.                 continue;
  120.             }
  121.             // Collect rythmScore values from setData
  122.             foreach ($decodedData['setData'] as $set) {
  123.                 if (isset($set['rythmScore']) && is_numeric($set['rythmScore'])) {
  124.                     $rythmScores[] = (float) $set['rythmScore'] * 100// Cast to float for consistency
  125.                 }
  126.             }
  127.         }
  128.         return $rythmScores;
  129.     }
  130.     function computeTotalWorkoutTime($workouts): string
  131.     {
  132.         $totalMilliseconds 0;
  133.         // Iterate over each workout
  134.         foreach ($workouts as $workout) {
  135.             /** @var WorkoutData $workout */
  136.             $start $workout->getStartTimestamp();
  137.             $end $workout->getEndTimestamp();
  138.             // Skip if timestamps are null or 0
  139.             if ($start === null || $end === null || $start == || $end == 0) {
  140.                 continue;
  141.             }
  142.             // Calculate difference in milliseconds
  143.             $diff $end $start;
  144.             if ($diff 0) { // Ensure end is after start
  145.                 $totalMilliseconds += $diff;
  146.             }
  147.         }
  148.         // Convert milliseconds to seconds
  149.         $totalSeconds $totalMilliseconds 1000;
  150.         // Convert total seconds to hours and minutes
  151.         $hours floor($totalSeconds 3600); // 3600 seconds = 1 hour
  152.         $remainingSeconds $totalSeconds 3600;
  153.         $minutes floor($remainingSeconds 60);
  154.         // Return formatted string
  155.         return sprintf('%dh %dm'$hours$minutes);
  156.     }
  157.     function computeWorkoutMinutes(array $workouts): array
  158.     {
  159.         $minutesArray = [];
  160.         // Iterate over each workout
  161.         foreach ($workouts as $workout) {
  162.             /** @var WorkoutData $workout */
  163.             $start $workout->getStartTimestamp();
  164.             $end $workout->getEndTimestamp();
  165.             // Skip if timestamps are null or 0
  166.             if ($start === null || $end === null || $start == || $end == 0) {
  167.                 continue;
  168.             }
  169.             // Calculate difference in milliseconds
  170.             $diff $end $start;
  171.             if ($diff 0) { // Ensure end is after start
  172.                 // Convert milliseconds to minutes (1 min = 60,000 ms)
  173.                 $minutes $diff 60000;
  174.                 $minutesArray[] = $minutes;
  175.             }
  176.         }
  177.         return $minutesArray;
  178.     }
  179.     function computeTotalSets(array $workouts): int
  180.     {
  181.         $totalSets 0;
  182.         // Iterate over each workout
  183.         foreach ($workouts as $workout) {
  184.             /** @var WorkoutData $workout */
  185.             $jsonData $workout->getJsonData();
  186.             // Skip if jsonData is null or empty
  187.             if ($jsonData === null || $jsonData === '') {
  188.                 continue;
  189.             }
  190.             // Decode JSON string to array/object
  191.             $decodedData json_decode($jsonDatatrue); // true = return as array
  192.             // Check if decoding succeeded and setData exists
  193.             if ($decodedData === null || !isset($decodedData['setData']) || !is_array($decodedData['setData'])) {
  194.                 continue;
  195.             }
  196.             // Add the number of sets (length of setData array)
  197.             $totalSets += count($decodedData['setData']);
  198.         }
  199.         return $totalSets;
  200.     }
  201.     function computeTotalRepetitions(array $workouts): int
  202.     {
  203.         $totalReps 0;
  204.         // Iterate over each workout
  205.         foreach ($workouts as $workout) {
  206.             /** @var WorkoutData $workout */
  207.             $jsonData $workout->getJsonData();
  208.             // Skip if jsonData is null or empty
  209.             if ($jsonData === null || $jsonData === '') {
  210.                 continue;
  211.             }
  212.             // Decode JSON string to array
  213.             $decodedData json_decode($jsonDatatrue); // true = return as array
  214.             // Check if decoding succeeded and setData exists
  215.             if ($decodedData === null || !isset($decodedData['setData']) || !is_array($decodedData['setData'])) {
  216.                 continue;
  217.             }
  218.             // Iterate over setData array and sum actualReps
  219.             foreach ($decodedData['setData'] as $set) {
  220.                 if (isset($set['actualReps']) && is_numeric($set['actualReps']) && isset($set['targetReps']) && is_numeric($set['targetReps']) && $set['targetReps'] > 1) {
  221.                     $totalReps += (int) $set['actualReps']; // Cast to int for safety
  222.                 }
  223.             }
  224.         }
  225.         return $totalReps;
  226.     }
  227.     function computeMedianBtScore(array $workouts): ?float
  228.     {
  229.         $scores = [];
  230.         // Collect all btScore values
  231.         foreach ($workouts as $workout) {
  232.             /** @var WorkoutData $workout */
  233.             $btScore $workout->getBtScore();
  234.             // Skip if btScore is null or not numeric
  235.             if ($btScore === null || !is_numeric($btScore)) {
  236.                 continue;
  237.             }
  238.             $scores[] = (float) $btScore// Cast to float for consistency
  239.         }
  240.         // If no valid scores, return null
  241.         if (empty($scores)) {
  242.             return null;
  243.         }
  244.         // Sort scores in ascending order
  245.         sort($scores);
  246.         $count count($scores);
  247.         // Calculate median
  248.         if ($count === 0) {
  249.             // Even number of scores: average the two middle values
  250.             $middleIndex $count 2;
  251.             return ($scores[$middleIndex 1] + $scores[$middleIndex]) / 2;
  252.         } else {
  253.             // Odd number of scores: take the middle value
  254.             $middleIndex floor($count 2);
  255.             return $scores[$middleIndex];
  256.         }
  257.     }
  258.     private function computeWorkoutsPerTemplate(array $workoutsEntityManagerInterface $em): array
  259.     {
  260.         $templates $em->getRepository(WorkoutTemplate::class)->findAll();
  261.         $templateMap = [];
  262.         foreach ($templates as $template) {
  263.             $templateMap[$template->getId()] = $template->getName();
  264.         }
  265.         $workoutCounts array_fill_keys($templateMap0);
  266.         foreach ($workouts as $workout) {
  267.             $jsonData $workout->getJsonData();
  268.             if ($jsonData === null || $jsonData === '') {
  269.                 continue;
  270.             }
  271.             $decodedData json_decode($jsonDatatrue);
  272.             if (
  273.                 $decodedData === null ||
  274.                 !isset($decodedData['workoutCloudId']) || !is_numeric($decodedData['workoutCloudId'])
  275.             ) {
  276.                 continue;
  277.             }
  278.             $templateId = (int) $decodedData['workoutCloudId'];
  279.             if (!isset($templateMap[$templateId])) {
  280.                 continue;
  281.             }
  282.             $templateName $templateMap[$templateId];
  283.             $workoutCounts[$templateName]++;
  284.         }
  285.         return $workoutCounts;
  286.     }
  287.     function computeMedianRythmScore(array $workouts): ?float
  288.     {
  289.         $rythmScores = [];
  290.         // Iterate over each workout
  291.         foreach ($workouts as $workout) {
  292.             /** @var WorkoutData $workout */
  293.             $jsonData $workout->getJsonData();
  294.             // Skip if jsonData is null or empty
  295.             if ($jsonData === null || $jsonData === '') {
  296.                 continue;
  297.             }
  298.             // Decode JSON string to array
  299.             $decodedData json_decode($jsonDatatrue); // true = return as array
  300.             // Check if decoding succeeded and setData exists
  301.             if ($decodedData === null || !isset($decodedData['setData']) || !is_array($decodedData['setData'])) {
  302.                 continue;
  303.             }
  304.             // Collect rythmScore values from setData
  305.             foreach ($decodedData['setData'] as $set) {
  306.                 if (isset($set['rythmScore']) && is_numeric($set['rythmScore'])) {
  307.                     $rythmScores[] = (float) $set['rythmScore']; // Cast to float for consistency
  308.                 }
  309.             }
  310.         }
  311.         // If no valid rythmScores, return null
  312.         if (empty($rythmScores)) {
  313.             return null;
  314.         }
  315.         // Sort scores in ascending order
  316.         sort($rythmScores);
  317.         $count count($rythmScores);
  318.         // Calculate median
  319.         if ($count === 0) {
  320.             // Even number of scores: average the two middle values
  321.             $middleIndex $count 2;
  322.             return ($rythmScores[$middleIndex 1] + $rythmScores[$middleIndex]) / 2;
  323.         } else {
  324.             // Odd number of scores: take the middle value
  325.             $middleIndex floor($count 2);
  326.             return $rythmScores[$middleIndex];
  327.         }
  328.     }
  329.     function computeModuleTypeFrequencies(array $workouts): array
  330.     {
  331.         // Initialize frequency array for moduleTypes 0, 1, 2, 3
  332.         $frequencies = [
  333.             => 0,
  334.             => 0,
  335.             => 0,
  336.             => 0,
  337.         ];
  338.         // Iterate over each workout
  339.         foreach ($workouts as $workout) {
  340.             /** @var WorkoutData $workout */
  341.             $moduleType $workout->getModuleType();
  342.             // Only count if moduleType is valid (0, 1, 2, or 3)
  343.             if ($moduleType !== null && array_key_exists($moduleType$frequencies)) {
  344.                 $frequencies[$moduleType]++;
  345.             }
  346.         }
  347.         $freqs = [];
  348.         $freqs[] = [
  349.             'name' => 'Guided Workouts',
  350.             'value' => $frequencies[0]
  351.         ];
  352.         $freqs[] = [
  353.             'name' => 'Learn Exercises',
  354.             'value' => $frequencies[1]
  355.         ];
  356.         $freqs[] = [
  357.             'name' => 'Fitness Challenges',
  358.             'value' => $frequencies[2]
  359.         ];
  360.         $freqs[] = [
  361.             'name' => 'Warm Up',
  362.             'value' => $frequencies[3]
  363.         ];
  364.         return $freqs;
  365.     }
  366.     private function computeAverageRythmScoreByExercise(array $workouts): array
  367.     {
  368.         $exerciseStats = [];
  369.         foreach ($workouts as $workout) {
  370.             $jsonData $workout->getJsonData();
  371.             if ($jsonData === null || $jsonData === '') {
  372.                 continue;
  373.             }
  374.             $decodedData json_decode($jsonDatatrue);
  375.             if ($decodedData === null || !isset($decodedData['setData']) || !is_array($decodedData['setData'])) {
  376.                 continue;
  377.             }
  378.             foreach ($decodedData['setData'] as $set) {
  379.                 if (
  380.                     isset($set['exerciseName']) && is_string($set['exerciseName']) &&
  381.                     isset($set['rythmScore']) && is_numeric($set['rythmScore'])
  382.                 ) {
  383.                     $exerciseName $set['exerciseName'];
  384.                     $rythmScore = (float) $set['rythmScore'];
  385.                     if ($rythmScore 0.1) continue;
  386.                     if (!isset($exerciseStats[$exerciseName])) {
  387.                         $exerciseStats[$exerciseName] = ['sum' => 0'count' => 0];
  388.                     }
  389.                     $exerciseStats[$exerciseName]['sum'] += $rythmScore 100;
  390.                     $exerciseStats[$exerciseName]['count']++;
  391.                 }
  392.             }
  393.         }
  394.         $result = [];
  395.         foreach ($exerciseStats as $exerciseName => $stats) {
  396.             if ($stats['count'] > 0) {
  397.                 $result[$exerciseName] = $stats['sum'] / $stats['count'];
  398.             }
  399.         }
  400.         return $result;
  401.     }
  402.     private function computeAverageTimeToStartPoseByExercise(array $workouts): array
  403.     {
  404.         $exerciseStats = [];
  405.         foreach ($workouts as $workout) {
  406.             $jsonData $workout->getJsonData();
  407.             if ($jsonData === null || $jsonData === '') {
  408.                 continue;
  409.             }
  410.             $decodedData json_decode($jsonDatatrue);
  411.             if ($decodedData === null || !isset($decodedData['setData']) || !is_array($decodedData['setData'])) {
  412.                 continue;
  413.             }
  414.             foreach ($decodedData['setData'] as $set) {
  415.                 if (
  416.                     isset($set['exerciseName']) && is_string($set['exerciseName']) &&
  417.                     isset($set['timeToStartPose']) && is_numeric($set['timeToStartPose'])
  418.                 ) {
  419.                     $exerciseName $set['exerciseName'];
  420.                     $timeToStartPose = (float) $set['timeToStartPose'] / 1000;
  421.                     if ($timeToStartPose 30) continue;
  422.                     if (!isset($exerciseStats[$exerciseName])) {
  423.                         $exerciseStats[$exerciseName] = ['sum' => 0'count' => 0];
  424.                     }
  425.                     $exerciseStats[$exerciseName]['sum'] += $timeToStartPose;
  426.                     $exerciseStats[$exerciseName]['count']++;
  427.                 }
  428.             }
  429.         }
  430.         $result = [];
  431.         foreach ($exerciseStats as $exerciseName => $stats) {
  432.             if ($stats['count'] > 0) {
  433.                 $result[$exerciseName] = $stats['sum'] / $stats['count'];
  434.             }
  435.         }
  436.         return $result;
  437.     }
  438. }