src/Controller/ScenarioControllerFrontend.php line 183

  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Link;
  4. use App\Entity\Mcq;
  5. use App\Entity\Node;
  6. use App\Entity\PlayerSessionhist;
  7. use App\Entity\ScenarioTimeTrack;
  8. use App\Entity\Scenario;
  9. use App\Entity\Media;
  10. use App\Manager\RestrictedAccessManager;
  11. use App\Manager\StatManager;
  12. use Doctrine\ORM\OptimisticLockException;
  13. use Doctrine\Persistence\ManagerRegistry;
  14. use Exception;
  15. use Symfony\Component\HttpFoundation\RedirectResponse;
  16. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  17. use Symfony\Component\Routing\Annotation\Route;
  18. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  19. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  20. use Symfony\Component\HttpFoundation\JsonResponse;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  23. use Symfony\Component\HttpFoundation\Response;
  24. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  25. use Symfony\Component\Security\Core\User\UserInterface;
  26. use BeSimple\I18nRoutingBundle\Routing\Annotation\I18nRoute;
  27. use Symfony\Component\Stopwatch\Stopwatch;
  28. use Symfony\Contracts\Translation\TranslatorInterface;
  29. use App\Service\PdfService;
  30. class ScenarioControllerFrontend extends AbstractController
  31. {
  32.     public TranslatorInterface $translator;
  33.     private PdfService $pdfService;
  34.     private UrlGeneratorInterface $router;
  35.     public function __construct(PdfService $pdfServiceUrlGeneratorInterface $routerTranslatorInterface $translator, private ManagerRegistry $managerRegistry)
  36.     {
  37.         $this->pdfService $pdfService;
  38.         $this->router $router;
  39.         $this->translator $translator;
  40.     }
  41.     /**
  42.      * Methods enable translation of error|notice flash messages.
  43.      *
  44.      * @param $key
  45.      * @param array $params
  46.      * @param null $domain
  47.      * @return mixed
  48.      */
  49.     public function trans($key, array $params = [], $domain null)
  50.     {
  51.         return $this->translator->trans($key$params$domain);
  52.     }
  53.     public function addFlashMessage($type$key, array $params = [], $domain null)
  54.     {
  55.         $this->addFlash($type$this->trans($key$params$domain));
  56.     }
  57.     public function addNotFoundException($type$key, array $params = [], $domain null)
  58.     {
  59.         $this->createNotFoundException($this->trans($key$params$domain));
  60.     }
  61. //    /**
  62.     //     * @Route("/scenarios", name="scenarios")
  63.     //     * @Method ({"GET"})
  64.     //     * @param Request $request
  65.     //     * @return void
  66.     //     */
  67.     //    public function indexAction(Request $request)
  68.     //    {
  69.     //
  70.     //        //$translated = $this->get('translator')->trans("Hello");
  71.     //        //var_dump($translated);
  72.     //
  73.     //        /*  NEPOTREBUJEME
  74.     //        $em = $this->getDoctrine()->getManager();
  75.     //        $scenarios = $em->getRepository(Scenario::class)->findBy(
  76.     //            array('isDeleted' => false,
  77.     //                'state' => 1)
  78.     //        );
  79.     //
  80.     //        return $this->render('frontend/index.html.twig', [
  81.     //            'h1' => "All scenarios",
  82.     //            'body_text' => "All scenarios",
  83.     //            'scenarios' => $scenarios,
  84.     //        ]);*/
  85.     //    }
  86.     /**
  87.      * @param Request $request
  88.      * @param Scenario $scenario
  89.      * @return Response
  90.      */
  91.     #[Route(path'/scenario/{scenario}'name'scenario-start'requirements: ['scenario' => '\d+'], methods: ['GET'])]
  92.     public function scenarioStart(Scenario $scenario): RedirectResponse
  93.     {
  94.         #TODO: doplnit funkcionalitu, ktora sa deje len pri starte scenara, premenovat route 'scenario' na 'scenario-play' alebo nieco podobne
  95.         return $this->redirectToRoute('scenario', ['scenario' => $scenario->getId()]);
  96.     }
  97.     /**
  98.      * @param Request $request
  99.      * @param RestrictedAccessManager $restrictedAccessManager
  100.      * @param StatManager $statManager
  101.      * @param int $scenario
  102.      * @param string|null $node
  103.      * @param UserInterface|null $user
  104.      * @return Response
  105.      * @throws Exception
  106.      */
  107.     #[Route(path'/scenario/{scenario}/node/{node}'name'scenario'defaults: ['node' => 0], requirements: ['scenario' => '\d+'], methods: ['GET''POST'])]
  108.     public function scenarioShow(Request $requestRestrictedAccessManager $restrictedAccessManagerStatManager $statManager,int $scenariostring $node nullUserInterface $user null)
  109.     {
  110.         $em $this->managerRegistry->getManager();
  111.         $scenario $em->getRepository(Scenario::class)->findOneBy(array('id' => $scenario));
  112.         if (!$scenario) {
  113.             $this->addFlashMessage('error''scenario_not_found', [], 'controller');
  114.             return $this->redirectToRoute('virtual-patients');
  115.         }
  116.         $decodedNodeId null;
  117.         if ($node) {
  118.             $decodedNodeId Node::staticDecode($node);
  119.         }
  120.         try {
  121.             $this->isScenarioAvailable($request$restrictedAccessManager$scenario);
  122.         } catch (NotFoundHttpException $exception) {
  123.             return $this->redirectToRoute('virtual-patients');
  124.         }
  125.         $session $request->getSession();
  126.         if ($scenario->getState() == 2) {
  127.             if (!($user $this->getUser()) || ($user->getRoles() == ['ROLE_USER'])) { // user bez prav na in progress
  128.                 throw $this->createAccessDeniedException();
  129.             }
  130.         }
  131.         // all nodes, for navigation
  132.         $repository $em->getRepository(Node::class);
  133.         $qb $repository->createQueryBuilder('n');
  134.         $qb $qb->where(
  135.             $qb->expr()->eq('n.scenarioId'':scenarioId'),
  136.             $qb->expr()->eq('n.isDeleted'':isDeleted'),
  137.             $qb->expr()->eq('n.active'':active'))
  138.             ->setParameter('scenarioId'$scenario->getId())
  139.             ->setParameter('isDeleted'0)
  140.             ->setParameter('active'1)
  141.             ->orderBy('n.nodeOrder''ASC');
  142.         $query $qb->getQuery();
  143.         $nodes $query->getResult();
  144.         if (empty($nodes)) {
  145.             return $this->render('frontend/scenario_zero_nodes.html.twig', array(
  146.                 'scenario' => $scenario
  147.             ));
  148.         }
  149.         $scenarioMcq = isset($session->get('mcq_nodes')[0]['scenario']) ? $session->get('mcq_nodes')[0]['scenario'] : 0;
  150.         $currentNode $session->get('current_node');
  151.         if (!empty($scenarioMcq) && $currentNode 2) {
  152.             $finished $session->get('mcq_nodes');
  153.             $nodeId $finished[0]['node'];
  154.             $node $em->getRepository(Node::class)->findOneBy(array('id' => $nodeId));
  155.         } elseif (!$node) {
  156.             $session->set('total_score'0); // init
  157.             $session->set('current_node'1); // init
  158.             $session->set('player_sessionhist'null); //init
  159.             $session->set('node_score', []);
  160.             $session->set('mcq_nodes', []);
  161.             $session->set('mcq_score'0);
  162.             $session->set('nodes_visited', []);
  163.             $session->set('node_time_durations', []);
  164.             $session->set('is_fatal'false);
  165.             $session->set('hAxis'"");
  166.             $session->set('vAxis'"");
  167.             $session->set('vAxisMax'0);
  168.             $node $nodes[0];
  169.         } else {
  170.             if (!is_numeric($decodedNodeId)) {
  171.                 return $this->render('exceptions/error404.html.twig');
  172.             }
  173.             $node $em->getRepository(Node::class)->findOneBy(array('id' => $decodedNodeId));
  174.         }
  175.         $stop true;
  176.         $nodesAnswered $session->get('node_score',[]);
  177.         foreach ($nodesAnswered as $answered) {
  178.             if ($answered['node']->getId() == $decodedNodeId) {
  179.                 $stop false;
  180.                 break;
  181.             }
  182.         }
  183.         $nodesVisited $session->get('nodes_visited', []);
  184.         foreach ($nodesVisited as $visited) {
  185.             if ($visited->getId() == $decodedNodeId) {
  186.                 $stop false;
  187.                 break;
  188.             }
  189.         }
  190.         if ($session->get('stopwatch') && $stop) {
  191.             $event $session->get('stopwatch')->stop('nodeTimeCounter');
  192.             $session->set('stopwatch', []);
  193.             $event round($event->getDuration() * 0.001);
  194.             $convertedTime gmdate('H:i:s'$event);
  195.             $currentDate date("Y-m-d");
  196.             $join $currentDate ' ' $convertedTime;
  197.             $dateTime = new \DateTime($join);
  198.             $nodeTime = new ScenarioTimeTrack();
  199.             $user $this->getUser();
  200.             $nodeTime->setUsers($user);
  201.             $sessionId $session->getId();
  202.             if($sessionId) {
  203.                 $nodeTime->setSessionId($sessionId);
  204.             }
  205.             $nodesVisited $session->get('nodes_visited', []);
  206.             $time $session->get('node_time_durations');
  207.             $lastNode $em->getRepository('App\Entity\Node')->findOneBy(array('id' => array_key_last($nodesVisited)));
  208.             $overall date_parse($join);
  209.             $seconds 0;
  210.             $seconds += $overall['hour'] * 3600 $overall['minute'] * 60 $overall['second'];
  211.             if ($lastNode) {
  212.                 $nodeTime->setNode($lastNode);
  213.                 $time[$lastNode->getId()] = [
  214.                     'node' => $lastNode->getId(),
  215.                     'scenario' => $scenario->getId(),
  216.                     'time' => $convertedTime,
  217.                     'seconds' => $seconds
  218.                 ];
  219.             } else {
  220.                 $nodeTime->setNode($node);
  221.                 $time[$node->getId()] = [
  222.                     'node' => $node->getId(),
  223.                     'scenario' => $scenario->getId(),
  224.                     'time' => $convertedTime,
  225.                     'seconds' => $seconds
  226.                 ];
  227.             }
  228.             $nodeTime->setScenario($scenario);
  229.             $nodeTime->setTime($dateTime);
  230.             $em->persist($nodeTime);
  231.             $em->flush();
  232.             $session->set('node_time_durations'$time);
  233.         }
  234.         $answeredNodes $session->get('node_score',[]);
  235.         if (empty($answeredNodes)) {
  236.             $stopwatch = new Stopwatch();
  237.             $event $stopwatch->start('nodeTimeCounter');
  238.             $session->set('stopwatch'$event);
  239.         } else {
  240.             foreach ($answeredNodes as $answered) {
  241.                 if ($stop && $answered['node']->getId() != $decodedNodeId || $decodedNodeId == null) {
  242.                     $stopwatch = new Stopwatch();
  243.                     $event $stopwatch->start('nodeTimeCounter');
  244.                     $session->set('stopwatch'$event);
  245.                     break;
  246.                 }
  247.             }
  248.         }
  249.         $session $request->getSession();
  250.         if ($session->get("total_score") === null) {
  251.             $session->set('total_score'0);
  252.         }
  253.         if ($session->get("current_node") === null) {
  254.             $session->set('current_node'1);
  255.         }
  256.         $firstScenarioNode $nodes[0];
  257.         $answeredNodes $session->get('node_score');
  258.         $unavailable true;
  259.         $lastPlayedNode null;
  260.         // stat processing
  261.         $user $this->getUser();
  262.         $statId $request->get('stat'0);
  263.         $statDataByAttempt = [];
  264.         $statByNodeAttemptSum = [];
  265.         $linksInfo = [];
  266.         $nodesInfo = [];
  267.         $isStatAccess false;
  268.         if ($statId && $user && (in_array('ROLE_ADMIN'$user->getRoles()) || in_array('ROLE_STAT'$user->getRoles()))) {
  269.             $data $statManager->getStatData($statId$scenario->getId());
  270.             $statByNodeAttemptSum $data['statByNodeAttemptSum'];
  271.             $statDataByAttempt $data['statByAttempts'];
  272.             $nodesInfo $statManager->nodesInfo($scenario->getId());
  273.             if (count($nodesInfo)) {
  274.                 $linksInfo $statManager->linksInfo(array_keys($nodesInfo));
  275.                 $statDataByAttempt $statManager->sortLinksByOrder($statDataByAttemptarray_keys($linksInfo));
  276.             }
  277.             $isStatAccess true;
  278.             $unavailable false;
  279.         }
  280.         $nodesVisited $session->get('nodes_visited', []);
  281.         if ($node === null) {
  282.             return $this->render('exceptions/error404.html.twig');
  283.         }
  284.         if ($decodedNodeId == null || $answeredNodes == null) {
  285.         } else {
  286.             foreach ($answeredNodes as $answered) {
  287.                 if ($node->getId() == $answered['node']->getId() || (($node->getNodeOrder() - $answered['node']->getNodeOrder()) == 1)) {
  288.                     $unavailable false;
  289.                     break;
  290.                 }
  291.             }
  292.             if (count($answeredNodes)) {
  293.                 $lastKey array_key_last($answeredNodes);
  294.                 $lastPlayedNode $answeredNodes[$lastKey]['node']->encodeNodeId();
  295.             }
  296.         }
  297.         if ($firstScenarioNode->getId() == $decodedNodeId || $decodedNodeId == null) {
  298.             $unavailable false;
  299.         }
  300.         if ($unavailable == true) {
  301.             $nodeId $em->getRepository(Node::class)->findOneBy(array('id' => array_key_last($nodesVisited)));
  302.             $this->addFlashMessage('warning''scenario_not_available', array(), 'controller');
  303.             if ($lastPlayedNode != null) {
  304.                 return $this->redirectToRoute('scenario', [
  305.                     'scenario' => $scenario->getId(),
  306.                     'node' => $nodeId->encodeNodeId(),
  307.                 ]);
  308.             } else {
  309.                 return $this->redirectToRoute('scenario', [
  310.                     'scenario' => $scenario->getId(),
  311.                 ]);
  312.             }
  313.         } else {
  314.             $nodesVisited[$node->getId()] = $node;
  315.             $session->set('nodes_visited'$nodesVisited);
  316.             $nodeId $node->getId();
  317.             $progress_counter $node->getNodeOrder();
  318.             $node_next sizeof($nodes) - $progress_counter null $nodes[$progress_counter];
  319.             $node_prev $progress_counter null $nodes[$progress_counter 2];
  320.             $session->set('current_node'max($session->get("current_node"), $progress_counter));
  321.             /*
  322.             // if linksOrder == 1  ...order by preferredOrder, jinak RAND order
  323.             */
  324.             $repository $em->getRepository(Link::class);
  325.             $qb $repository->createQueryBuilder('l');
  326.             $qb $qb->where(
  327.                 $qb->expr()->eq('l.nodeId'':nodeId'),
  328.                 $qb->expr()->eq('l.isDeleted'':isDeleted'),
  329.                 $qb->expr()->eq('l.active'':active'))
  330.                 ->setParameter('nodeId'$nodeId)
  331.                 ->setParameter('isDeleted'0)
  332.                 ->setParameter('active'1)
  333.                 ->orderBy('l.preferredOrder''ASC'); // v pripade $node->getLinksOrder == 1
  334.             $query $qb->getQuery();
  335.             $links $query->getResult();
  336.             if (!$isStatAccess && $node->getLinksOrder() != 1) {
  337.                 shuffle($links);
  338.             }
  339.             $repository $em->getRepository(Mcq::class);
  340.             $qb $repository->createQueryBuilder('mcq');
  341.             $qb $qb->where(
  342.                 $qb->expr()->eq('mcq.nodeId'':nodeId'),
  343.                 $qb->expr()->eq('mcq.isDeleted'':isDeleted'),
  344.                 $qb->expr()->eq('mcq.active'':active'))
  345.                 ->setParameter('nodeId'$nodeId)
  346.                 ->setParameter('isDeleted'0)
  347.                 ->setParameter('active'1);
  348.             $query $qb->getQuery();
  349.             $mcqs $query->getResult();
  350.             $mcq null;
  351.             $media_mcq null;
  352.             $mcq_options = array(); // json_decode($mcq->getOptions(), true);
  353.             if (sizeof($mcqs) > 1) {// nasledujici
  354.                 $this->createNotFoundException($this->trans('node_mcq', [], 'controller'));
  355.             } elseif (sizeof($mcqs) == 1) {
  356.                 $mcq $mcqs[0];
  357.                 $mcq_options $mcq->getOptions(); // json_decode($mcq->getOptions(), true);
  358.                 $response_order 0;
  359.                 foreach ($mcq_options as $mcq_option) {
  360.                     $mcq_options[$response_order]["order"] = $response_order;
  361.                     $response_order++;
  362.                 }
  363.                 // media - vsechny aktivni pre mcq
  364.                 $repository $em->getRepository(Media::class);
  365.                 $qb $repository->createQueryBuilder('m');
  366.                 $qb $qb->where(
  367.                     $qb->expr()->eq('m.mcqId'':mcqId'),
  368.                     $qb->expr()->isNotNull('m.filename'),
  369.                     $qb->expr()->eq('m.isDeleted'':isDeleted'),
  370.                     $qb->expr()->eq('m.active'':active'))
  371.                     ->setParameter('mcqId'$mcq->getId())
  372.                     ->setParameter('isDeleted'0)
  373.                     ->setParameter('active'1)
  374.                     ->orderBy('m.mediaOrder''ASC');
  375.                 $query $qb->getQuery();
  376.                 $media_mcq $query->getResult();
  377.             }
  378.             // media - vsechny aktivni pre node
  379.             $repository $em->getRepository(Media::class);
  380.             $qb $repository->createQueryBuilder('m''m.id');
  381.             $qb $qb->where(
  382.                 $qb->expr()->eq('m.nodeId'':nodeId'),
  383.                 $qb->expr()->isNotNull('m.filename'),
  384.                 $qb->expr()->eq('m.isDeleted'':isDeleted'),
  385.                 $qb->expr()->eq('m.active'':active'))
  386.                 ->setParameter('nodeId'$nodeId)
  387.                 ->setParameter('isDeleted'0)
  388.                 ->setParameter('active'1)
  389.                 ->orderBy('m.mediaOrder''ASC');
  390.             $query $qb->getQuery();
  391.             $media_node $query->getResult();
  392.             /*
  393.             $player = new PlayerSessionhist();
  394.             // $player->setScenarioId(1);
  395.             $player->setScenario($scenario);
  396.             // $player->setSessionId($session->getId());
  397.             $player->setCreatedAt(new \DateTime());
  398.             $em->persist($player);
  399.             $em->flush();*/
  400.             if ($statId) {
  401.                 $nodesVisitedNav = [];
  402.                 foreach ($nodes as $nodeItem) {
  403.                     $nodesVisitedNav[$nodeItem->getId()] = $nodeItem;
  404.                 }
  405.             } else {
  406.                 $nodesVisitedNav $nodesVisited;
  407.             }
  408.             return $this->render('frontend/scenario.html.twig', [
  409.                 'scenario' => $scenario,
  410.                 // 'media_scenario' => $media_scenario,
  411.                 'node' => $node,
  412.                 'media_node' => $media_node// media k node
  413.                 'node_prev' => $node_prev// v pripade 1. uzlu bude prazdna
  414.                 'node_next' => $node_next// v pripade koncoveho uzlu bude prazdna
  415.                 'progress_counter' => $progress_counter// poradie nodu ktory sa zobrazuje
  416.                 'numb_of_nodes' => sizeof($nodes),
  417.                 'current_node' => $session->get("current_node"), // maximalny dosiahnuty node
  418.                 'total_score' => $session->get('total_score'), // nacteme ze session, TODO overit
  419.                 'links' => $links,
  420.                 //'media_links' => $media_links,
  421.                 'mcq' => $mcq,
  422.                 'mcq_options' => $mcq_options,
  423.                 'media_mcq' => $media_mcq,
  424.                 'isRestrictedScenario' => (int)$scenario->isRestricted(),
  425.                 'finishedMcq' => $session->get('mcq_nodes'),
  426.                 'isStat' => $isStatAccess,
  427.                 'statId' => $statId,
  428.                 'statData' => $statDataByAttempt,
  429.                 'statByNodeAttemptSum' => $statByNodeAttemptSum,
  430.                 'nodesInfo' => $nodesInfo,
  431.                 'linksInfo' => $linksInfo,
  432.                 'nodesVisitedNav' => $nodesVisitedNav,
  433.             ]);
  434.         }
  435.     }
  436.     /**
  437.      * @param Request $request
  438.      * @param RestrictedAccessManager $restrictedAccessManager
  439.      * @param Scenario $scenario
  440.      * @return JsonResponse|Response
  441.      * @throws OptimisticLockException
  442.      */
  443.     #[Route(path'/scenario-progress/{scenario}'name'scenario-progress'requirements: ['scenario' => '\d+'], methods: ['GET''POST'])]
  444.     public function scenarioSaveProgress(Request $requestRestrictedAccessManager $restrictedAccessManagerScenario $scenario)
  445.     {
  446.         $em $this->managerRegistry->getManager();
  447.         try {
  448.             $this->isScenarioAvailable($request$restrictedAccessManager$scenario);
  449.         } catch (NotFoundHttpException $exception) {
  450.             return $this->redirectToRoute('virtual-patients');
  451.         }
  452.         $session $request->getSession();
  453.         // dump($session->getId());
  454.         $statId $request->get('stat'0);
  455.         $isStatAccess false;
  456.         $user null;
  457.         if (!($user $this->getUser()) || ($user->getRoles() == ['ROLE_USER'])) {
  458.             $user null;
  459.         }
  460.         if ($statId && $user && (in_array('ROLE_ADMIN'$user->getRoles()) || in_array('ROLE_STAT'$user->getRoles()))) {
  461.             $isStatAccess true;
  462.         }
  463.         $node $em->getRepository(Node::class)->find($request->request->get('nodeId'));
  464.         $idNode $node->getId();
  465.         $linkId intval($request->request->get('linkId'));
  466.         $link $em->getRepository(Link::class)->find($linkId);
  467.         $score $link->getScore();
  468.         $current_node $session->get('current_node');
  469.         $progress_counter $node->getNodeOrder();
  470.         $total_score $session->get('total_score'); // a take do session
  471.         if ($request->request->get('nextNodeId')) {
  472.             $nextNodeId intval($request->request->get('nextNodeId'));
  473.             $nodeNext $em->getRepository(Node::class)->find($nextNodeId);
  474.         } else {
  475.             $nodeNext null;
  476.         }
  477.         $isHistory = ($current_node $progress_counter);
  478.         $arrData = [];
  479.         $arrData['sid'] = $session->getId();  // just for test
  480.         if(!$isHistory) {
  481.             $player = new PlayerSessionhist();
  482.             //$scenario = $em->getRepository(Scenario::class)->find($scenarioId);
  483.             $player->setScenario($scenario);
  484.             //$arrData['user'] = $this->get('Users')->g;
  485.             $user =  $this->getUser();   //TODO: skontrolovat ci sa rovnaju
  486.             $player->setUsers($user);
  487.             $player->setSessionId($session->getId());   //TODO: skontrolovat ci sa rovnaju
  488.             $player->setCreatedAt(new \DateTime());
  489.             $player->setNode($node);
  490.             $total_score 0;
  491.             if ($playerOldId $session->get('player_sessionhist')) {
  492.                 $playerOld $em->getRepository(PlayerSessionhist::class)->find($playerOldId);
  493.                 if ((
  494.                         (is_null($user) && is_null($playerOld->getUsers()))
  495.                         ||
  496.                         ($playerOld->getUsers()->getId() == $user->getId())
  497.                     )
  498.                     &&
  499.                     ($playerOld->getScenario()->getId() == $scenario->getId())
  500.                 ) {
  501.                     $total_score $playerOld->getScore();
  502.                 }
  503.             }
  504.             $player->setLink($link);
  505.             $repository $em->getRepository(Link::class);
  506.             $qb $repository->createQueryBuilder('l');
  507.             $qb $qb->where(
  508.                 $qb->expr()->eq('l.nodeId'':nodeId'),
  509.                 $qb->expr()->eq('l.isDeleted'':isDeleted'),
  510.                 $qb->expr()->eq('l.active'':active'),
  511.                 $qb->expr()->eq('l.correct'':correct'))
  512.                 ->setParameter('nodeId'$idNode)
  513.                 ->setParameter('isDeleted'0)
  514.                 ->setParameter('active'1)
  515.                 ->setParameter('correct'1);
  516.             $query $qb->getQuery();
  517.             $links $query->getResult();
  518.             $total_score += $link->getScore();
  519.             $player->setScore($total_score);  // ulozim do db - SOUCET
  520.             $points $session->get('node_score', []);
  521.             $nodePoints = isset($points[$node->getId()]) ? $points[$node->getId()] : 0;
  522.             if (!empty($nodePoints)) {
  523.                 $points[$node->getId()]  =  [
  524.                     'node' => $node,
  525.                     'linkScore' => $link->getScore(),
  526.                     'nodeScore' => $nodePoints['nodeScore'] + $link->getScore(),
  527.                     'question_repetition' => 1
  528.                 ];
  529.             } else {
  530.                 $points[$node->getId()]  =  [
  531.                     'node' => $node,
  532.                     'linkScore' => $link->getScore(),
  533.                     'nodeScore' => $link->getScore(),
  534.                     'question_repetition' => 0
  535.                 ];
  536.             }
  537.             $session->set('node_score'$points);
  538.             $session->set('total_score'$total_score); // a take do session
  539.             if ($link->getCorrect() == -2) {
  540.                 $session->set('is_fatal'true);
  541.             }
  542.             if ($nodeNext) {
  543.                 if ($link->getCorrect() == -2) {
  544.                     $player->setIsEnded(-2);
  545.                     $session->get('stopwatch');
  546.                     $event $session->get('stopwatch')->stop('nodeTimeCounter');
  547.                     $session->set('stopwatch', []);
  548.                     $event round($event->getDuration() * 0.001);
  549.                     $convertedTime gmdate('H:i:s'$event);
  550.                     $currentDate date("Y-m-d");
  551.                     $join $currentDate ' ' $convertedTime;
  552.                     $dateTime = new \DateTime($join);
  553.                     $nodeTime = new ScenarioTimeTrack();
  554.                     $user $this->getUser();
  555.                     $nodeTime->setUsers($user);
  556.                     $sessionId $session->getId();
  557.                     if($sessionId) {
  558.                         $nodeTime->setSessionId($sessionId);
  559.                     }
  560.                     $nodesVisited $session->get('nodes_visited', []);
  561.                     $time $session->get('node_time_durations');
  562.                     $lastNode $em->getRepository('App\Entity\Node')->findOneBy(array('id' => array_key_last($nodesVisited)));
  563.                     $overall date_parse($join);
  564.                     $seconds 0;
  565.                     $seconds += $overall['hour'] * 3600 $overall['minute'] * 60 $overall['second'];
  566.                     if ($lastNode) {
  567.                         $nodeTime->setNode($lastNode);
  568.                         $time[$lastNode->getId()] = [
  569.                             'node' => $lastNode->getId(),
  570.                             'scenario' => $scenario->getId(),
  571.                             'time' => $convertedTime,
  572.                             'seconds' => $seconds
  573.                         ];
  574.                     } else {
  575.                         $nodeTime->setNode($node);
  576.                         $time[$node->getId()] = [
  577.                             'node' => $node->getId(),
  578.                             'scenario' => $scenario->getId(),
  579.                             'time' => $convertedTime,
  580.                             'seconds' => $seconds
  581.                         ];
  582.                     }
  583.                     $nodeTime->setScenario($scenario);
  584.                     $nodeTime->setTime($dateTime);
  585.                     $em->persist($nodeTime);
  586.                     $em->flush();
  587.                     $session->set('node_time_durations'$time);
  588.                 }
  589.             } else {
  590.                 $player->setIsEnded($link->getCorrect());
  591.             }
  592.             $em->persist($player);
  593.             $em->flush();
  594.             $session->set('player_sessionhist'$player->getId()); // a take do session
  595.         }
  596.         $repository $em->getRepository(Media::class);
  597.         $qb $repository->createQueryBuilder('m');
  598.         $qb $qb->where(
  599.             $qb->expr()->isNotNull('m.filename'),
  600.             $qb->expr()->eq('m.isDeleted'':isDeleted'),
  601.             $qb->expr()->eq('m.active'':active'),
  602.             $qb->expr()->eq('m.linkId'':linkId'))
  603.             ->setParameter('isDeleted'0)
  604.             ->setParameter('active'1)
  605.             ->setParameter('linkId'$linkId)
  606.             ->orderBy('m.mediaOrder''ASC');
  607.         $query $qb->getQuery();
  608.         $media $query->getResult();
  609.         $arrData['content'] = $this->render('frontend/link.html.twig', [
  610.             'scenario' => $scenario,
  611.             'link' => $link,
  612.             'media_links' => $media,
  613.             'progress_counter' => $current_node,
  614.             'current_node' => $node->getNodeOrder(),
  615.             'node_next' => $nodeNext,
  616.             'node' => $node,
  617.             'score' => $score,
  618.             'isStat' => $isStatAccess,
  619.             'statId' => $statId,
  620.         ])->getContent();
  621.         if (!$isHistory && ($link->getCorrect() > 0)) {
  622.             $session->set('current_node'$current_node+1);
  623.         }
  624.         $arrData['code'] = 100// vlastni code == success
  625.         $arrData['total_score'] = $total_score// vlastni code == success
  626.         return new JsonResponse($arrData);
  627.     }
  628.     /**
  629.      * @param Request $request
  630.      * @param RestrictedAccessManager $restrictedAccessManager
  631.      * @param $scenarioId
  632.      * @return JsonResponse|Response
  633.      * @throws OptimisticLockException
  634.      */
  635.     #[Route(path'/scenario-progress-mcq/{scenarioId}'name'scenario-progress-mcq'requirements: ['scenarioId' => '\d+'], methods: ['GET''POST'])]
  636.     public function scenarioSaveProgressMcq(Request $requestRestrictedAccessManager $restrictedAccessManager$scenarioId)
  637.     {
  638.         $em $this->managerRegistry->getManager();
  639.         $scenario $em->getRepository(Scenario::class)->find($scenarioId);
  640.         try {
  641.             $this->isScenarioAvailable($request$restrictedAccessManager$scenario);
  642.         } catch (NotFoundHttpException $exception) {
  643.             return $this->redirectToRoute('virtual-patients');
  644.         }
  645.         $mcqId $request->get('mcqId'0);
  646.         if (!$mcqId) {  // zatim jako povinne
  647.             $this->createNotFoundException($this->trans('scenario_not_found', [], 'controller'));
  648.         }
  649.         $session $request->getSession();
  650.         $arrData = [];
  651.         $scenario $em->getRepository(Scenario::class)->find($scenarioId);
  652.         $mcq $em->getRepository(Mcq::class)->find($mcqId);
  653.         $nodeId $request->get('nodeId'0);
  654.         if ($nodeId) {
  655.             $node $em->getRepository(Node::class)->find($nodeId);
  656.         }
  657.         $player = new PlayerSessionhist();
  658.         $player->setMcq($mcq);
  659.         $mcq_score 0;
  660.         $lastIndex 0;
  661.         $requestScore $request->get('score', []);
  662.         if ($requestScore) foreach ($requestScore as $idx => $score) {
  663.             $mcq_score += intval($score);
  664.             $lastIndex $idx;
  665.         }
  666.         $player->setMcqScore$mcq_score );  // SINGULARNI
  667.         $user =  $this->getUser();
  668.         $player->setUsers($user);
  669.         $player->setScenario($scenario);
  670.         $player->setSessionId($session->getId());
  671.         $player->setCreatedAt(new \DateTime());
  672.         $requestTotalScore $request->get('total_score', []);
  673.         if ($requestTotalScore[$lastIndex]) {
  674.             $total_score intval$requestTotalScore[$lastIndex] );
  675.             $player->setScore($total_score);  // ulozim do db - SOUCET
  676.             $session->set('total_score'$total_score); // a take do session
  677.         }
  678.         $requestIsEnded $request->get('isEnded', []);
  679.         if ($requestIsEnded[$lastIndex]) {
  680.             $isEnded intval$requestIsEnded[$lastIndex] );
  681.             if ($isEnded == -2) {
  682.                 $player->setIsEnded(-2); // ukoncil jako fatal
  683.             }
  684.             elseif ($isEnded == 1) {
  685.                 $player->setIsEnded(1); // ukoncil uspesne
  686.             }
  687.         }
  688.         $mcqNodes $session->get('mcq_nodes', []);
  689.         array_push($mcqNodes, [
  690.            'node' => intval($nodeId),
  691.            'mcqScore' => $mcq_score,
  692.            'scenario' => $scenario->getId()
  693.         ]);
  694.         $session->set('mcq_nodes'$mcqNodes);
  695.         $mcqScore $session->get('mcq_score');
  696.         $requestMcqScore $request->get('mcqScore'0);
  697.         $updatedMcqScore $mcqScore intval($requestMcqScore);
  698.         $session->set('mcq_score'$updatedMcqScore);
  699.         if (!empty($node)) {
  700.             $player->setNode($node);
  701.         }
  702.         $em->persist($player);
  703.         $em->flush();
  704.         $session->set('player_sessionhist'$player->getId());
  705.         $arrData['code'] = 100// vlastni code == success
  706.         return new JsonResponse($arrData);
  707.     }
  708.     /**
  709.      * @param Request $request
  710.      * @param RestrictedAccessManager $restrictedAccessManager
  711.      * @param StatManager $statManager
  712.      * @param $scenarioId
  713.      * @return Response
  714.      * @throws Exception
  715.      */
  716.     #[Route(path'/scenario-end/{scenarioId}'name'scenario-end'requirements: ['scenarioId' => '\d+'], methods: ['GET''POST'])]
  717.     public function scenarioEnd(Request $requestRestrictedAccessManager $restrictedAccessManagerStatManager $statManager$scenarioId): Response
  718.     {
  719.         $em $this->managerRegistry->getManager();
  720.         $scenario $em->getRepository(Scenario::class)->find($scenarioId);
  721.         try {
  722.             $this->isScenarioAvailable($request$restrictedAccessManager$scenario);
  723.         } catch (NotFoundHttpException $exception) {
  724.             return $this->redirectToRoute('virtual-patients');
  725.         }
  726.         $repository $em->getRepository(Node::class);
  727.         $qb $repository->createQueryBuilder('n');
  728.         $qb $qb->where(
  729.             $qb->expr()->eq('n.scenarioId'':scenarioId'),
  730.             $qb->expr()->eq('n.isDeleted'':isDeleted'),
  731.             $qb->expr()->eq('n.active'':active'))
  732.             ->setParameter('scenarioId'$scenario->getId())
  733.             ->setParameter('isDeleted'0)
  734.             ->setParameter('active'1)
  735.             ->orderBy('n.nodeOrder''ASC');
  736.         $query $qb->getQuery();
  737.         $nodes $query->getResult();
  738.         if (empty($nodes)) {
  739.             $this->createNotFoundException($this->trans('scenario_no_nodes', [], 'controller'));
  740.         }
  741.         $node_prev $nodes[sizeof($nodes)-1];
  742.         $repository $em->getRepository(Media::class);
  743.         $qb $repository->createQueryBuilder('m');
  744.         $qb $qb->where(
  745.             $qb->expr()->isNotNull('m.filename'),
  746.             $qb->expr()->eq('m.isDeleted'':isDeleted'),
  747.             $qb->expr()->eq('m.active'':active'),
  748.             $qb->expr()->eq('m.scenarioId'':scenarioId'),
  749.             $qb->expr()->eq('m.mediaFlag'':mediaFlag'))
  750.             ->setParameter('isDeleted'0)
  751.             ->setParameter('active'1)
  752.             ->setParameter('scenarioId'$scenarioId)
  753.             ->setParameter('mediaFlag'Media::MEDIA_FLAG_SCENARIO_OUTRO)
  754.             ->orderBy('m.mediaOrder''ASC');
  755.         $query $qb->getQuery();
  756.         $media $query->getResult();
  757.         $session $request->getSession();
  758.         if ($session->get("total_score") === null) {
  759.             $session->set('total_score'0);
  760.         }
  761.         if ($session->get("current_node") === null) {
  762.             $session->set('current_node'1);
  763.         }
  764.         // stat processing
  765.         $user $this->getUser();
  766.         $statId $request->get('stat'0);
  767.         $statDataByAttempt = [];
  768.         $statByNodeAttemptSum = [];
  769.         $linksInfo = [];
  770.         $nodesInfo = [];
  771.         $isStatAccess false;
  772.         if ($statId && $user && (in_array('ROLE_ADMIN'$user->getRoles()) || in_array('ROLE_STAT'$user->getRoles()))) {
  773.             $data $statManager->getStatData($statId$scenario->getId());
  774.             $statByNodeAttemptSum $data['statByNodeAttemptSum'];
  775.             $statDataByAttempt $data['statByAttempts'];
  776.             $nodesInfo $statManager->nodesInfo($scenario->getId());
  777.             if (count($nodesInfo)) {
  778.                 $linksInfo $statManager->linksInfo(array_keys($nodesInfo));
  779.                 $statDataByAttempt $statManager->sortLinksByOrder($statDataByAttemptarray_keys($linksInfo));
  780.             }
  781.             $isStatAccess true;
  782.         }
  783.         if ($isStatAccess) {
  784.             $nodesVisited = [];
  785.             foreach ($nodes as $nodeItem) {
  786.                 $nodesVisited[$nodeItem->getId()] = $nodeItem;
  787.             }
  788.         } else {
  789.             $nodesVisited $session->get('nodes_visited', []);
  790.         }
  791.         if (empty($nodesVisited)) {
  792.             $nodeId $em->getRepository(Node::class)->findOneBy(array('id' => $nodes['0']->getId()));
  793.             $this->addFlashMessage('warning''scenario_end_not_available', array(), 'controller');
  794.             return $this->redirectToRoute('scenario', [
  795.                 'scenario' => $scenario->getId(),
  796.                 'node' => $nodeId->encodeNodeId()
  797.             ]);
  798.         } else {
  799.             $session $request->getSession();
  800.             if (!$isStatAccess) {
  801.                 $stopwatch $session->get('stopwatch');
  802.                 if (!empty($stopwatch)) {
  803.                     $event $stopwatch->stop('nodeTimeCounter');
  804.                     $session->set('stopwatch', []);
  805.                     $event round($event->getDuration() * 0.001);
  806.                     $convertedTime gmdate('H:i:s'$event);
  807.                     $currentDate date("Y-m-d");
  808.                     $join $currentDate ' ' $convertedTime;
  809.                     $dateTime = new \DateTime($join);
  810.                     $nodeTime = new ScenarioTimeTrack();
  811.                     $user $this->getUser();
  812.                     $nodeTime->setUsers($user);
  813.                     $sessionId $session->getId();
  814.                     if ($sessionId) {
  815.                         $nodeTime->setSessionId($sessionId);
  816.                     }
  817.                     $lastNode $em->getRepository('App\Entity\Node')->findOneBy(array('id' => array_key_last($nodesVisited)));
  818.                     $overall date_parse($join);
  819.                     $seconds 0;
  820.                     $seconds += $overall['hour'] * 3600 $overall['minute'] * 60 $overall['second'];
  821.                     $nodeTime->setNode($lastNode);
  822.                     $nodeTime->setScenario($scenario);
  823.                     $nodeTime->setTime($dateTime);
  824.                     $em->persist($nodeTime);
  825.                     $em->flush();
  826.                     $time $session->get('node_time_durations');
  827.                     $time[$lastNode->getId()] = [
  828.                         'node' => $lastNode->getId(),
  829.                         'scenario' => $scenario->getId(),
  830.                         'time' => $convertedTime,
  831.                         'seconds' => $seconds
  832.                     ];
  833.                     $session->set('node_time_durations'$time);
  834.                 }
  835.             }
  836.             $sumScenarioNodes sizeof($nodesVisited);
  837.             $nodeId $em->getRepository(Node::class)->findOneBy(array('id' => array_key_last($nodesVisited)));
  838.             if ($sumScenarioNodes == sizeof($nodes)) {
  839.                 return $this->render('frontend/scenario_end.html.twig', [
  840.                     'node' => null,
  841.                     'scenario' => $scenario,
  842.                     'node_prev' => $node_prev,
  843.                     'media' => $media,
  844.                     'progress_counter' => $session->get('current_node'),
  845.                     'numb_of_nodes' => sizeof($nodes),
  846.                     'total_score' => $session->get('total_score'),
  847.                     'isRestrictedScenario' => (int)$scenario->isRestricted(),
  848.                     'nodesVisitedNav' => $nodesVisited,
  849.                     'isStat' => $isStatAccess,
  850.                     'statId' => $statId,
  851.                     'statData' => $statDataByAttempt,
  852.                     'statByNodeAttemptSum' => $statByNodeAttemptSum,
  853.                     'nodesInfo' => $nodesInfo,
  854.                     'linksInfo' => $linksInfo,
  855.                 ]);
  856.             } else {
  857.                 $this->addFlashMessage('warning''scenario_end_not_available', array(), 'controller');
  858.                 return $this->redirectToRoute('scenario', [
  859.                     'scenario' => $scenario->getId(),
  860.                     'node' => $nodeId->encodeNodeId()
  861.                 ]);
  862.             }
  863.         }
  864.     }
  865.     /**
  866.      * @param Request $request
  867.      * @param RestrictedAccessManager $restrictedAccessManager
  868.      * @param $scenarioId
  869.      * @return Response
  870.      */
  871.     #[Route(path'/scenario-results/{scenarioId}'name'scenario-results'requirements: ['scenarioId' => '\d+'], methods: ['GET'])]
  872.     public function scenarioResults(Request $requestRestrictedAccessManager $restrictedAccessManager$scenarioId): Response
  873.     {
  874.         $em $this->managerRegistry->getManager();
  875.         $scenario $em->getRepository(Scenario::class)->find($scenarioId);
  876.         try {
  877.             $this->isScenarioAvailable($request$restrictedAccessManager$scenario);
  878.         } catch (NotFoundHttpException $exception) {
  879.             return $this->redirectToRoute('virtual-patients');
  880.         }
  881.         $repository $em->getRepository(Node::class);
  882.         $qb $repository->createQueryBuilder('n');
  883.         $qb $qb->where(
  884.             $qb->expr()->eq('n.scenarioId'':scenarioId'),
  885.             $qb->expr()->eq('n.isDeleted'':isDeleted'),
  886.             $qb->expr()->eq('n.active'':active'))
  887.             ->setParameter('scenarioId'$scenario->getId())
  888.             ->setParameter('isDeleted'0)
  889.             ->setParameter('active'1)
  890.             ->orderBy('n.nodeOrder''ASC');
  891.         $query $qb->getQuery();
  892.         $nodes $query->getResult();
  893.         if (empty($nodes)) {
  894.             $this->createNotFoundException($this->trans('scenario_no_nodes', [], 'controller'));
  895.         }
  896.         $session $request->getSession();
  897.         $nodePoints $session->get('node_score', []);
  898.         $mcqPoints $session->get('mcq_nodes', []);
  899.         foreach ($nodePoints as $nodeAnswered) {
  900.             foreach ($mcqPoints as $mcq) {
  901.                 if ($nodeAnswered['node']->getId() == $mcq['node']) {
  902.                     $nodePoints[$nodeAnswered['node']->getId()] = [
  903.                         'node' => $nodeAnswered['node'],
  904.                         'nodeScore' => $nodeAnswered['nodeScore'] += $mcq['mcqScore'],
  905.                         'question_repetition' => $nodeAnswered['question_repetition'],
  906.                         'linkScore' => $nodeAnswered['linkScore']
  907.                     ];
  908.                 }
  909.             }
  910.         }
  911.         $session->set('node_score'$nodePoints);
  912.         $maxScore 0;
  913.         $scoreMcq 0;
  914.         $maxPossibleMcqScore 0;
  915.         $scoreLink 0;
  916.         $maxPossibleLinkScore 0;
  917.         foreach($nodes as $node) {
  918.             $repository $em->getRepository(Link::class);
  919.             $qb $repository->createQueryBuilder('l');
  920.             $qb $qb->where(
  921.                 $qb->expr()->eq('l.nodeId'':nodeId'),
  922.                 $qb->expr()->eq('l.isDeleted'':isDeleted'),
  923.                 $qb->expr()->eq('l.active'':active'))
  924.                 ->setParameter('nodeId'$node->getId())
  925.                 ->setParameter('isDeleted'0)
  926.                 ->setParameter('active'1);
  927.                 $query $qb->getQuery();
  928.                 $links $query->getResult();
  929.             $repository $em->getRepository(Mcq::class);
  930.             $qb $repository->createQueryBuilder('mcq');
  931.             $qb $qb->where(
  932.                 $qb->expr()->eq('mcq.nodeId'':nodeId'),
  933.                 $qb->expr()->eq('mcq.isDeleted'':isDeleted'),
  934.                 $qb->expr()->eq('mcq.active'':active'))
  935.                 ->setParameter('nodeId'$node->getId())
  936.                 ->setParameter('isDeleted'0)
  937.                 ->setParameter('active'1);
  938.             $query $qb->getQuery();
  939.             $mcqs $query->getResult();
  940.             if (!empty($mcqs)) {
  941.                 $mcq $mcqs[0];
  942.                 $data $mcq->getOptions();
  943.                 foreach ($data as $maxMcqScore) {
  944.                     if ($maxMcqScore['score'] >= 0) {
  945.                         $scoreMcq += $maxMcqScore['score'];
  946.                     }
  947.                 }
  948.                 $maxPossibleMcqScore += $scoreMcq;
  949.                 $scoreMcq 0;
  950.             }
  951.             if (!empty($links)) {
  952.                 foreach ($links as $linkScore) {
  953.                     if (sizeof($links) == && $links[0]->getScore() == 0) {
  954.                         $scoreLink 0;
  955.                     } else {
  956.                         if ($linkScore->getScore() > $scoreLink) {
  957.                             $scoreLink $linkScore->getScore();
  958.                         }
  959.                     }
  960.                 }
  961.                 $maxPossibleLinkScore += $scoreLink;
  962.                 $scoreLink 0;
  963.             }
  964.         }
  965.         $maxScore += $maxPossibleMcqScore $maxPossibleLinkScore;
  966.         $session $request->getSession();
  967.         $session->set('max_possible_score'$maxScore);
  968.         if ($session->get("total_score") === null) {
  969.             $session->set('total_score'0);
  970.         }
  971.         if ($session->get("current_node") === null) {
  972.             $session->set('current_node'1);
  973.         }
  974.         $nodeScore $session->get('node_score', []);
  975.         $nodesVisited $session->get('nodes_visited', []);
  976.         if (empty($nodesVisited)) {
  977.             $nodeId $em->getRepository(Node::class)->findOneBy(array('id' => $nodes['0']->getId()));
  978.             $this->addFlashMessage('warning''scenario_results_not_available', array(), 'controller');
  979.             return $this->redirectToRoute('scenario', [
  980.                 'scenario' => $scenario->getId(),
  981.                 'node' => $nodeId->encodeNodeId()
  982.             ]);
  983.         } else {
  984.             $sumScenarioNodes sizeof($nodesVisited);
  985.             $nodeId $em->getRepository(Node::class)->findOneBy(array('id' => array_key_last($nodesVisited)));
  986.             $fatal $session->get('is_fatal');
  987.             if ($sumScenarioNodes == sizeof($nodes) || $fatal) {
  988.                 $time $session->get('node_time_durations');
  989.                 $mcqNodes $session->get('mcq_nodes');
  990.                 $total 0;
  991.                 foreach ($time as $times) {
  992.                     $totalTime $times['time'];
  993.                     $overall date_parse($totalTime);
  994.                     $total += $overall['hour'] * 3600 $overall['minute'] * 60 $overall['second'];
  995.                 }
  996.                 $totalTime gmdate('H:i:s'$total);
  997.                 $session->set('total_time'$totalTime);
  998.                 $editedNodes $session->get('node_score');
  999.                 foreach($editedNodes as $node) {
  1000.                     $repository $em->getRepository(Mcq::class);
  1001.                     $qb $repository->createQueryBuilder('mcq');
  1002.                     $qb $qb->where(
  1003.                         $qb->expr()->eq('mcq.nodeId'':nodeId'),
  1004.                         $qb->expr()->eq('mcq.isDeleted'':isDeleted'),
  1005.                         $qb->expr()->eq('mcq.active'':active'))
  1006.                         ->setParameter('nodeId'$node['node']->getId())
  1007.                         ->setParameter('isDeleted'0)
  1008.                         ->setParameter('active'1);
  1009.                     $query $qb->getQuery();
  1010.                     $mcqs $query->getResult();
  1011.                     $repository $em->getRepository(Link::class);
  1012.                     $qb $repository->createQueryBuilder('l');
  1013.                     $qb $qb->where(
  1014.                         $qb->expr()->eq('l.nodeId'':nodeId'),
  1015.                         $qb->expr()->eq('l.isDeleted'':isDeleted'),
  1016.                         $qb->expr()->eq('l.active'':active'),
  1017.                         $qb->expr()->eq('l.correct'':correct'))
  1018.                         ->setParameter('nodeId'$node['node']->getId())
  1019.                         ->setParameter('isDeleted'0)
  1020.                         ->setParameter('active'1)
  1021.                         ->setParameter('correct'1);
  1022.                     $query $qb->getQuery();
  1023.                     $links $query->getResult();
  1024.                     $maxPossibleNodeScore 0;
  1025.                     if (!empty($links)) {
  1026.                         foreach ($links as $linkScore) {
  1027.                             if (sizeof($links) == && $links[0]->getScore() == 0) {
  1028.                                 $scoreLink 0;
  1029.                             } else {
  1030.                                 if ($linkScore->getScore() > $scoreLink) {
  1031.                                     $scoreLink $linkScore->getScore();
  1032.                                 }
  1033.                             }
  1034.                         }
  1035.                         $maxPossibleNodeScore += $scoreLink;
  1036.                         $scoreLink 0;
  1037.                     }
  1038.                     if (!empty($mcqs)) {
  1039.                         $mcq $mcqs[0];
  1040.                         $data $mcq->getOptions();
  1041.                         foreach ($data as $maxMcqScore) {
  1042.                             if ($maxMcqScore['score'] >= 0) {
  1043.                                 $scoreMcq += $maxMcqScore['score'];
  1044.                             }
  1045.                         }
  1046.                         $maxPossibleNodeScore += $scoreMcq;
  1047.                         $scoreMcq 0;
  1048.                     }
  1049.                     $bonus[$node['node']->getId()] = [
  1050.                         'node' => $node['node'],
  1051.                         'nodeScore' => $node['nodeScore'],
  1052.                         'question_repetition' => $node['question_repetition'],
  1053.                         'maxNodeScore' => $maxPossibleNodeScore,
  1054.                         'mcq' => $mcqs,
  1055.                         'linkScore' => $node['linkScore']
  1056.                     ];
  1057.                     $session->set('node_score'$bonus);
  1058.                 }
  1059.                 $nodeScore $session->get('node_score');
  1060.                 foreach ($nodeScore as $nodeScores) {
  1061.                     if (!empty($mcqNodes) && !empty($nodeScores['mcq'])) {
  1062.                         foreach ($mcqNodes as $mcqNode) {
  1063.                             if ($nodeScores['mcq'][0]->getNodeId() == $mcqNode['node'] ) {
  1064.                                 $editedNodes[$nodeScores['node']->getId()] = [
  1065.                                     'node' => $nodeScores['node'],
  1066.                                     'nodeScore' => $nodeScores['nodeScore'],
  1067.                                     'question_repetition' => $nodeScores['question_repetition'],
  1068.                                     'mcq' => 1,
  1069.                                     'maxNodeScore' => $nodeScores['maxNodeScore'],
  1070.                                     'linkScore' => $nodeScores['linkScore']
  1071.                                 ];
  1072.                             } elseif ($nodeScores['mcq'][0]->getNodeId() != $mcqNode['node']) {
  1073.                                 $editedNodes[$nodeScores['node']->getId()] = [
  1074.                                     'node' => $nodeScores['node'],
  1075.                                     'nodeScore' => $nodeScores['nodeScore'],
  1076.                                     'question_repetition' => $nodeScores['question_repetition'],
  1077.                                     'mcq' => 0,
  1078.                                     'maxNodeScore' => $nodeScores['maxNodeScore'],
  1079.                                     'linkScore' => $nodeScores['linkScore']
  1080.                                 ];
  1081.                             }
  1082.                             $session->set('node_score'$editedNodes);
  1083.                         }
  1084.                     } else {
  1085.                         if (empty($nodeScores['mcq'])) {
  1086.                             $editedNodes[$nodeScores['node']->getId()] = [
  1087.                                 'node' => $nodeScores['node'],
  1088.                                 'nodeScore' => $nodeScores['nodeScore'],
  1089.                                 'question_repetition' => $nodeScores['question_repetition'],
  1090.                                 'mcq' => 2,
  1091.                                 'maxNodeScore' => $nodeScores['maxNodeScore'],
  1092.                                 'linkScore' => $nodeScores['linkScore']
  1093.                             ];
  1094.                         } else {
  1095.                             $editedNodes[$nodeScores['node']->getId()] = [
  1096.                                 'node' => $nodeScores['node'],
  1097.                                 'nodeScore' => $nodeScores['nodeScore'],
  1098.                                 'question_repetition' => $nodeScores['question_repetition'],
  1099.                                 'mcq' => 0,
  1100.                                 'maxNodeScore' => $nodeScores['maxNodeScore'],
  1101.                                 'linkScore' => $nodeScores['linkScore']
  1102.                             ];
  1103.                         }
  1104.                         $session->set('node_score'$editedNodes);
  1105.                     }
  1106.                 }
  1107.                 $lengthHAxis count($nodeScore);
  1108.                 $hAxis "[0, ";
  1109.                 for ($i 1$i <= $lengthHAxis$i++) {
  1110.                     if ($i >= $lengthHAxis) {
  1111.                         $hAxis .= "$i";
  1112.                         $i++;
  1113.                         $hAxis .= ", $i]";
  1114.                     } else {
  1115.                         $hAxis .= "$i, ";
  1116.                     }
  1117.                 }
  1118.                 $session->set('hAxis'$hAxis);
  1119.                 $sortedTimes = [];
  1120.                 foreach ($time as $nodeTime) {
  1121.                     array_push($sortedTimes$nodeTime['seconds']);
  1122.                 }
  1123.                 $vAxisMax max($sortedTimes) * 1.25// gap mezi nejvyšší bublinou
  1124.                 $session->set('vAxisMax'$vAxisMax);
  1125.                 $lengthVAxis count($sortedTimes);
  1126.                 $vAxis "[";
  1127.                 $i 0;
  1128.                 foreach ($sortedTimes as $value) {
  1129.                     $i++;
  1130.                     if ($i >= $lengthVAxis) {
  1131.                         $vAxis .= "$value";
  1132.                         $vAxis .= "]";
  1133.                     } else {
  1134.                         $vAxis .= "$value, ";
  1135.                     }
  1136.                 }
  1137.                 $session->set('vAxis'$vAxis);
  1138.                 return $this->render('frontend/scenario_results.html.twig', [
  1139.                     'scenario' => $scenario,
  1140.                     'total_score' => $session->get('total_score'),
  1141.                     'isRestrictedScenario' => (int)$scenario->isRestricted(),
  1142.                     'max_score' => $maxScore,
  1143.                     'node_score' => $session->get('node_score'),
  1144.                     'total_time' => $totalTime,
  1145.                     'time_durations' => $time,
  1146.                     'fatal' => $fatal,
  1147.                     'hAxis' => $hAxis,
  1148.                     'vAxis' => $vAxis,
  1149.                     'vAxisMax' => $vAxisMax
  1150.                 ]);
  1151.             } else {
  1152.                 $this->addFlashMessage('warning''scenario_results_not_available', array(), 'controller');
  1153.                 return $this->redirectToRoute('scenario', [
  1154.                     'scenario' => $scenario->getId(),
  1155.                     'node' => $nodeId->encodeNodeId()
  1156.                 ]);
  1157.             }
  1158.         }
  1159.     }
  1160.     /**
  1161.      * @param Request $request
  1162.      * @param int $scenarioId
  1163.      * @return Response
  1164.      */
  1165.     #[Route(path'/scenario-results/{scenarioId}/pdf'name'scenario-results-pdf'requirements: ['scenarioId' => '\d+'], methods: ['GET'])]
  1166.     public function scenarioCertificate(Request $requestint $scenarioId): Response
  1167.     {
  1168.         $em $this->managerRegistry->getManager();
  1169.         $scenario $em->getRepository(Scenario::class)->findOneBy(array('id' => $scenarioId));
  1170.         $session $request->getSession();
  1171.         return $this->render('frontend/scenario_results_pdf.html.twig', array(
  1172.            'scenario' => $scenario,
  1173.            'total_score' => $session->get('total_score'),
  1174.            'max_score' => $session->get('max_possible_score'),
  1175.            'node_score' => $session->get('node_score'),
  1176.            'total_time' => $session->get('total_time'),
  1177.            'time_durations' => $session->get('node_time_durations'),
  1178.            'firstName' => ($this->getUser() ? $this->getUser()->getFirstName() : ''),
  1179.            'lastName' => ($this->getUser() ? $this->getUser()->getLastName() : ''),
  1180.            'date' => new \DateTime('now'),
  1181.            'hAxis' => $session->get('hAxis'),
  1182.            'vAxis' => $session->get('vAxis'),
  1183.            'vAxisMax' => $session->get('vAxisMax'),
  1184.         ));
  1185.     }
  1186.     /**
  1187.      * @param Request $request
  1188.      * @param Scenario $scenario
  1189.      * @return Response
  1190.      */
  1191.     #[Route(path'/scenario/animation'name'scenario-animation'methods: ['GET'])]
  1192.     public function scenarioAnimation(Request $request): Response
  1193.     {
  1194.         $animationId $request->get('animation'0);
  1195.         $projectName $request->get('project_name''');
  1196.         return $this->render('frontend/animation.html.twig', array(
  1197.             'animation' => $animationId,
  1198.             'projectName' => $projectName,
  1199.         ));
  1200.     }
  1201.     /**
  1202.      * @param Request $request
  1203.      * @param RestrictedAccessManager $restrictedAccessManager
  1204.      * @param $scenarioId
  1205.      * @return JsonResponse|Response
  1206.      * @throws OptimisticLockException
  1207.      */
  1208.     #[Route(path'/scenario-stat-export/{stat}/{scenario}'name'scenario-stat-export'requirements: ['stat' => '\d+''scenario' => '\d+'], methods: ['GET''POST'])]
  1209.     public function scenarioStatExport(StatManager $statManagerint $statint $scenario)
  1210.     {
  1211.         $em $this->managerRegistry->getManager();
  1212.         $scenario $em->getRepository(Scenario::class)->findOneBy(array('id' => $scenario));
  1213.         if (!$scenario) {
  1214.             $this->addFlashMessage('error''scenario_not_found', [], 'controller');
  1215.             return $this->redirectToRoute('virtual-patients');
  1216.         }
  1217.         $user $this->getUser();
  1218.         $statByNodeAttemptSum = [];
  1219.         $statDataByAttempt = [];
  1220.         $linksInfo = [];
  1221.         $nodesInfo = [];
  1222.         $isStatAccess false;
  1223.         if ($stat && $user && (in_array('ROLE_ADMIN'$user->getRoles()) || in_array('ROLE_STAT'$user->getRoles()))) {
  1224.             $data $statManager->getStatData($stat$scenario->getId());
  1225.             $statByNodeAttemptSum $data['statByNodeAttemptSum'];
  1226.             $statDataByAttempt $data['statByAttempts'];
  1227.             $nodesInfo $statManager->nodesInfo($scenario->getId());
  1228.             if (count($nodesInfo)) {
  1229.                 $linksInfo $statManager->linksInfo(array_keys($nodesInfo));
  1230.                 $statDataByAttempt $statManager->sortLinksByOrder($statDataByAttemptarray_keys($linksInfo));
  1231.             }
  1232.             $isStatAccess true;
  1233.         }
  1234.         $repository $em->getRepository(Node::class);
  1235.         $qb $repository->createQueryBuilder('n');
  1236.         $qb $qb->where(
  1237.             $qb->expr()->eq('n.scenarioId'':scenarioId'),
  1238.             $qb->expr()->eq('n.isDeleted'':isDeleted'),
  1239.             $qb->expr()->eq('n.active'':active'))
  1240.             ->setParameter('scenarioId'$scenario->getId())
  1241.             ->setParameter('isDeleted'0)
  1242.             ->setParameter('active'1)
  1243.             ->orderBy('n.nodeOrder''ASC');
  1244.         $query $qb->getQuery();
  1245.         $nodes $query->getResult();
  1246.         return $this->render('frontend/scenario_stat.html.twig', array(
  1247.             'scenario' => $scenario,
  1248.             'statData' => $statDataByAttempt,
  1249.             'statByNodeAttemptSum' => $statByNodeAttemptSum,
  1250.             'linksInfo' => $linksInfo,
  1251.             'nodesInfo' => $nodesInfo,
  1252.             'isStat' => $isStatAccess,
  1253.             'nodes' => $nodes,
  1254.             'showStatAll' => true,
  1255.         ));
  1256.     }
  1257.     /**
  1258.      * @param Request $request
  1259.      * @param RestrictedAccessManager $restrictedAccessManager
  1260.      * @param $scenarioId
  1261.      * @return JsonResponse|Response
  1262.      * @throws OptimisticLockException
  1263.      */
  1264.     #[Route(path'/scenario-stat-export-pdf/{stat}/{scenario}'name'scenario-stat-export-pdf'requirements: ['stat' => '\d+''scenario' => '\d+'], methods: ['GET''POST'])]
  1265.     public function scenarioStatExportPdf(int $statint $scenario)
  1266.     {
  1267.         return $this->generateStatPdf($stat$scenario);
  1268.     }
  1269.     public function generateStatPdf(int $statint $scenario)
  1270.     {
  1271.         return $this->pdfService->download(
  1272.             $scenario$this->pdfService->generatePdf(
  1273.                 $this->router->generate('scenario-stat-export-pdf', ['stat' => $stat'scenario' => $scenario], UrlGeneratorInterface::ABSOLUTE_URL)
  1274.             )
  1275.         );
  1276.     }
  1277.     protected function isScenarioAvailable($request$restrictedAccessManager$scenario)
  1278.     {
  1279.         if (!$scenario) {
  1280.             $this->addFlashMessage('error''scenario_not_found', [], 'controller');
  1281.             throw $this->createNotFoundException($this->trans('scenario_not_found', [], 'controller'));
  1282.         }
  1283.         elseif ($scenario->getIsDeleted() == 1) {
  1284.             $this->addFlashMessage('error''scenario_delete', array('%scenarioId%' => $scenario->getId()), 'controller');
  1285.             throw $this->createNotFoundException($this->trans('scenario_delete', [], 'controller'));
  1286.         }
  1287.         elseif ($scenario->getState() == 0) {
  1288.             $this->addFlashMessage('error''scenario_deactivated', array('%scenarioId%' => $scenario->getId()), 'controller');
  1289.             throw $this->createNotFoundException($this->trans('scenario_deactivated', [], 'controller'));
  1290.         }
  1291.         if (($user $this->getUser()) && (($user->getRoles() == ['ROLE_ADMIN']))) {
  1292.             // to admin show restricted
  1293.         } else {
  1294.             try {
  1295.                 $ip $restrictedAccessManager->getIpAddress($request);
  1296.                 $isAllowedToRestricted $restrictedAccessManager->allowedToRestrictedByIp($scenario$ip);
  1297.                 if (!$isAllowedToRestricted) {
  1298.                     $this->addFlashMessage('error''access_error.not_allowed', [], 'controller');
  1299.                     throw new AccessDeniedHttpException('Restricted scenario.');
  1300.                 }
  1301.             } catch (\Exception $e) {
  1302.                 $this->addFlashMessage('error''user_location_check_error', [], 'controller');
  1303.                 throw new AccessDeniedHttpException('Restricted scenario.');
  1304.             }
  1305.         }
  1306.     }
  1307. }