* * @var SessionInterface */ protected $session; /** * @var EntityManagerInterface */ protected $em; /** * la response est créée ou modifiée pour inserer un cookie dans le header * lors de la lecture du paquet (ce cookie est uniquement utilisé dans un mecanisme de * blocage de sécurité) * * @var Response */ private $response; public function __construct(RouterInterface $router, ParameterBagInterface $parameters, SessionInterface $session, EntityManagerInterface $em) { // Récupération des services $this->router = $router; $this->parameters = $parameters; $this->session = $session; $this->em = $em; // Définitions des params $this->packageFolder = $this->parameters->get('logipro_scorm.package_folder'); $this->contentFolder = $this->parameters->get('logipro_scorm.content_folder'); $this->contentUrlPrefix = $this->parameters->get('logipro_scorm.content_url_prefix'); } // COURS /** * Crée un cours à partir d'un package identifié par un chemin * * @param string $courseKey * @param string $zipPath * @param bool $analyse * @param string $versioningRule * * @return array */ public function createCourse(string $courseKey, string $zipPath, bool $analyse = false, string $versioningRule = Course::VERSIONING_RULE_DEFAULT) { // Création de la structure de la réponse $response = array( 'code' => 200, 'isSuccessfullyCreated' => false, 'error' => '', 'parserErrors' => array(), 'course' => array( 'courseKey' => '', 'maxVersion' => 0, 'versioningRule' => $versioningRule ), 'metadata' => array( 'isAlreadyExisting' => false, 'standard' => '', 'title' => '', 'duration' => '' ) ); // Controle sur la clef fournie (elle doit etre non null et non existante en BD) if (!empty($courseKey)) { $course = $this->requireCourse($courseKey); if ($course) { $response['code'] = 400; $response['error'] = 'La clef de cours existe déjà. Utiliser \'updateCourse\' pour mettre à jour un cours existant'; return $response; } // ZIP // Test de l'existence du fichier à uploader if (!file_exists($zipPath)) { $response['code'] = 400; $response['error'] = 'Le chemin ne pointe pas vers un paquet valide'; return $response; } // Validation du Package $validation = PackageValidator::validatePackage($zipPath); if (!$validation['validation']['status']) { $response['code'] = 400; $response['error'] = $validation['validation']['error']; return $response; } $response['metadata']['standard'] = $validation['standard']; $response['metadata']['title'] = $validation['title']; $response['metadata']['duration'] = $validation['duration']; // Récupération de la signature du zip $hash = md5_file($zipPath); // Test de l'existence de la signature $repository = $this->em->getRepository(ZipFile::class); $zipFile = $repository->findOneBy(['zipFileprint' => $hash]); $response['metadata']['isAlreadyExisting'] = !empty($zipFile); // On s'arrete ici si seule l'analyse du paquet est demandée if ($analyse) { return $response; } // si le fichier n'existe pas déjà on le crée et on le stock coté moteur if (empty($zipFile)) { $response['metadata']['isAlreadyExisting'] = false; //upload du fichier coté moteur $uploadDate = new \DateTime('NOW'); $fileName = pathinfo($zipPath)['filename']; $filePath = $this->packageFolder . '/' . $hash; if (copy($zipPath, $filePath) === false) { // Si la copie plante, on retourne une erreur $response['code'] = 400; $response['error'] = 'Erreur lors de la copie du fichier zip'; return $response; } $endUploadDate = new \DateTime('NOW'); // Création d'un objet permettant de traiter le fichier zip $zipFile = new ZipFile(); $zipFile->setZipFileName($fileName); $zipFile->setZipFileprint($hash); $zipFile->setUploadDate($uploadDate); $zipFile->setEndUploadDate($endUploadDate); $zipFile->setStandard($validation['standard']); $zipFile->setImportStatus(ZipFile::IMPORT_STATUS_PROCESSING); $this->em->persist($zipFile); $this->em->flush(); // Une fois l'objet ZipFile créé, on l'extrait $zipFile->extractZipFile($this->packageFolder, $this->contentFolder); } // COURS // Création du cours $course = new Course(); $course->setCourseKey($courseKey); $course->setVersionRule($versioningRule); $this->em->persist($course); // Métadonées du cours $response['course']['courseKey'] = $course->getCourseKey(); $response['course']['maxVersion'] = $course->getMaxVersion(); // Mapping Zip/Cours $mapping = new MapCourseZipfile(); $mapping->setCourse($course); $mapping->setZipFile($zipFile); $mapping->setVersion($response['course']['maxVersion']); $this->em->persist($mapping); $this->em->flush(); $response['isSuccessfullyCreated'] = true; } return $response; } /** * Remplace le package d'un cours * * @param string $courseKey * @param string $zipPath * @param bool $analyse * @param string $versioningRule * * @return array */ public function updateCourse(string $courseKey, string $zipPath, bool $analyse = false, string $versioningRule = Course::VERSIONING_RULE_DEFAULT) { // Création de la structure de la réponse $response = array( 'code' => 200, 'isSuccessfullyUpdated' => false, 'error' => '', 'parserErrors' => array(), 'course' => array( 'courseKey' => '', 'maxVersion' => 0, 'versioningRule' => $versioningRule ), 'metadata' => array( 'isAlreadyExisting' => false, 'standard' => '', 'title' => '', 'duration' => '' ) ); // Controle sur la clef fournie (elle doit etre non null et non existante en BD) if (!empty($courseKey)) { $course = $this->requireCourse($courseKey); if (empty($course)) { $response['code'] = 400; $response['error'] = 'La clef de cours ne fait référence à aucun cours existant'; return $response; } // ZIP // Test de l'existence du fichier à uploader if (!file_exists($zipPath)) { $response['code'] = 400; $response['error'] = 'le chemin ne pointe pas vers un paquet valide'; return $response; } // Validation du Package $validation = PackageValidator::validatePackage($zipPath); if (!$validation['validation']['status']) { return $response; } $response['metadata']['standard'] = $validation['standard']; $response['metadata']['title'] = $validation['title']; // Récupération de la signature du zip $hash = md5_file($zipPath); // Test de l'existence de la signature $repository = $this->em->getRepository(ZipFile::class); $zipFile = $repository->findOneBy(['zipFileprint' => $hash]); $response['metadata']['isAlreadyExisting'] = !empty($zipFile); // On s'arrete ici si seule l'analyse du paquet est demandée if ($analyse) { return $response; } // si le fichier n'existe pas déjà on le crée et on le stock coté moteur if (empty($zipFile)) { $response['metadata']['isAlreadyExisting'] = false; //upload du fichier coté moteur $uploadDate = new \DateTime('NOW'); $fileName = pathinfo($zipPath)['filename']; $filePath = $this->packageFolder . '/' . $hash; if (copy($zipPath, $filePath) === false) { // Si la copie plante, on retourne une erreur $response['code'] = 400; $response['error'] = 'Erreur lors de la copie du fichier zip'; return $response; } $endUploadDate = new \DateTime('NOW'); // Création d'un objet permettant de traiter le fichier zip $zipFile = new ZipFile(); $zipFile->setZipFileName($fileName); $zipFile->setZipFileprint($hash); $zipFile->setUploadDate($uploadDate); $zipFile->setEndUploadDate($endUploadDate); $zipFile->setStandard($validation['standard']); $zipFile->setImportStatus(ZipFile::IMPORT_STATUS_PROCESSING); $this->em->persist($zipFile); // Une fois l'objet ZipFile créé, on l'extrait $zipFile->extractZipFile($this->packageFolder, $this->contentFolder); } // COURS // MaJ du cours $course->setVersionRule($versioningRule); $newMaxVersion = $course->getMaxVersion(); $newMaxVersion++; $course->setMaxVersion($newMaxVersion); $this->em->persist($course); // Métadonées du cours $response['course']['courseKey'] = $course->getCourseKey(); $response['course']['maxVersion'] = $newMaxVersion; // Mapping Zip/Cours $mapping = new MapCourseZipfile(); $mapping->setCourse($course); $mapping->setZipFile($zipFile); $mapping->setVersion($newMaxVersion); $this->em->persist($mapping); $this->em->flush(); $response['isSuccessfullyUpdated'] = true; } return $response; } /** * Copie un cours à partir d'un cours existant * * @param string $orginalCourseKey * @param string $newCourseKey * @param bool $analyse * @param string $versioningRule * * @return array */ public function duplicateCourse(string $orginalCourseKey, string $newCourseKey) { // Création de la structure de la réponse $response = array( 'code' => 200, 'isSuccessfullyCreated' => false, 'error' => '', 'course' => array( 'courseKey' => '', 'maxVersion' => 0, 'versioningRule' => '' ) ); // Contrôle que la clef du cours existant est fournie if (empty($orginalCourseKey)) { $response['code'] = 400; $response['error'] = 'La clef d\'un cours existant à dupliquer est requise.'; return $response; } // Contrôle que la clef du nouveau cours est fournie if (empty($newCourseKey)) { $response['code'] = 400; $response['error'] = 'La clef du nouveau cours dupliqué est requise.'; return $response; } // Contrôle de l'existence du cours à dupliquer $originalCourse = $this->requireCourse($orginalCourseKey); if (empty($originalCourse)) { $response['code'] = 400; $response['error'] = 'Le cours que vous souhaitez dupliquer est inexistant.'; return $response; } // Contrôle de la disponibilité de la clef pour le nouveau cours $newCourse = $this->requireCourse($newCourseKey); if ($newCourse) { $response['code'] = 400; $response['error'] = 'La clef du nouveau cours existe déjà. Utilser \'updateCourse\' pour mettre à jour un cours existant.'; return $response; } // COURS $newCourse = clone $originalCourse; $newCourse->setCourseKey($newCourseKey); $newCourse->setMaxVersion(1); $this->em->persist($newCourse); // Récupération de l'objet Zip $mappingRepository = $this->em->getRepository(MapCourseZipfile::class); $originalMapping = $mappingRepository->findOneBy(array('course' => $originalCourse), array('mapCourseZipfileId' => 'desc')); $zipFile = $originalMapping->getZipFile(); // Mapping Zip/Cours $mapping = new MapCourseZipfile(); $mapping->setCourse($newCourse); $mapping->setZipFile($zipFile); $mapping->setVersion($newCourse->getMaxVersion()); $this->em->persist($mapping); $this->em->flush(); // Réponse $response['course']['courseKey'] = $newCourse->getCourseKey(); $response['course']['maxVersion'] = $newCourse->getMaxVersion(); $response['course']['versioningRule'] = $newCourse->getVersionRule(); $response['isSuccessfullyCreated'] = true; return $response; } /** * Remplace la règle de versionning d'un cours * * @param string $courseKey * @param string $versionRule * * @return string */ public function updateCourseVersionRule(string $courseKey, string $versionRule) { $course = $this->requireCourse($courseKey); if ($course) { $course->setVersionRule($versionRule); $this->em->persist($course); $this->em->flush(); return $course->getCourseKey(); } return null; } /** * Supprime une activité * * @return bool */ public function deleteCourse(string $courseKey) { $course = $this->requireCourse($courseKey); if ($course) { $this->em->remove($course); $this->em->flush(); return true; } return false; } /** * retourne le numéro de version de l'activité */ public function getCourseCurrentVersion(string $courseKey) { $course = $this->requireCourse($courseKey); if ($course) { return $course->getMaxVersion(); } return null; } /** * retourne la règle de versionnig */ public function getCourseVersionRule(string $courseKey) { $course = $this->requireCourse($courseKey); if ($course) { return $course->getVersionRule(); } return null; } public function getCourses() : array { $repository = $this->em->getRepository(Course::class); $courses = $repository->findAll(); return $courses; } // LEARNER /** * Création d'un apprenant * * @param string $learnerKey * @param string $familyName * @param string $givenName * @return int */ public function createLearner(string $learnerKey, string $familyName, string $givenName) { // Création de la structure de la réponse $response = array( 'code' => 400, 'isAlreadyExisting' => false, 'error' => '', 'learner' => array( 'learnerKey' => '', 'familyName' => '', 'givenName' => '' ) ); if (!empty($learnerKey)) { $learner = $this->requireLearner($learnerKey); if (empty($learner)) { $familyName = $familyName ?? ''; $givenName = $givenName ?? ''; $learner = new Learner(); $learner->setLearnerKey($learnerKey); $learner->setFamilyName($familyName); $learner->setGivenName($givenName); $this->em->persist($learner); $this->em->flush(); } else { $response['isAlreadyExisting'] = true; } $response['code'] = 200; $response['learner']['learnerKey'] = $learner->getLearnerKey(); $response['learner']['familyName'] = $learner->getFamilyName(); $response['learner']['givenName'] = $learner->getGivenName(); } else { $response['error'] = 'La clef d\'identification est necessaire'; } return $response; } /** * Modification d'un apprenant * * @param string $learnerKey * @param string $familyName * @param string $givenName * @return int */ public function updateLearner(string $learnerKey, string $familyName, string $givenName) { if (!empty($learnerKey)) { $learner = $this->requireLearner($learnerKey); if ($learner) { $learner->setFamilyName($familyName); $learner->setGivenName($givenName); $this->em->persist($learner); $this->em->flush(); } } return $learner->getLearnerKey(); } /** * récupération d'un apprenant * * @param string $learnerKey * * @return Learner */ public function getLearner(string $learnerKey) { $learner = $this->requireLearner($learnerKey); return $learner; } public function getLearners() { $repository = $this->em->getRepository(Learner::class); $learners = $repository->findAll(); return $learners; } /** * supprime un apprenant * * @param string $learnerKey * * @return bool */ public function deleteLearner(string $learnerKey) { $learner = $this->requireLearner($learnerKey); if ($learner) { $this->em->remove($learner); $this->em->flush(); return true; } return false; } // REGISTRATION /** * création d'une inscription * * @param string $registrationKey * @param string $courseKey * @param string $learnerKey * * @return string */ public function createRegistration(string $registrationKey, string $courseKey, string $learnerKey) { // Création de la structure de la réponse $response = array( 'code' => 400, 'isAlreadyExisting' => false, 'error' => '', 'registration' => array( 'registrationKey' => '', 'courseKey' => '', 'learnerKey' => '' ) ); if (!empty($registrationKey)) { $registration = $this->requireRegistration($registrationKey); $course = $this->requireCourse($courseKey); $learner = $this->requireLearner($learnerKey); if ($course && $learner) { if (empty($registration)) { $registration = new Registration(); $registration->setRegistrationKey($registrationKey); $registration->setCourse($course); $registration->setLearner($learner); $this->em->persist($registration); $this->em->flush(); } else { $response['isAlreadyExisting'] = true; } $response['code'] = 200; $response['registration']['registrationKey'] = $registration->getRegistrationKey(); $response['registration']['courseKey'] = $registration->getCourse(); $response['registration']['learnerKey'] = $registration->getLearner(); } else { $response['error'] = 'Le cours et l\'apprenant doivent exister'; } } else { $response['error'] = 'La clef d\'identification est necessaire'; } return $response; } /** * Renvoie les variables necessaire pour l'affichage du contenu du lecteur * dans le cadre d'une inscription (pour un apprenant et pour un cours) * * @param string $registrationKey * @param string $data * * @return array */ public function getPlayerVariables(string $registrationKey, string $data = null, Response $response = null) : array { $result = array(); // Vérification de l'existance de l'inscription $registration = $this->requireRegistration($registrationKey); if (empty($registration)) { // Erreur : une inscription valide doit être passée return null; } // Récupération du cours et des infos de version $course = $registration->getCourse(); $maxVersion = $course->getMaxVersion(); $versionRule = $course->getVersionRule(); // Récupération du registrationAttempt courant $attemptRepository = $this->em->getRepository(RegistrationAttempt::class); $currentAttempt = $attemptRepository->findOneBy(array('registration' => $registration), array('registrationAttemptId' => 'desc')); $needCreation = empty($currentAttempt); $currentVersion = $needCreation ? null : $currentAttempt->getCourseVersion(); // Vérification de l'existance d'un essai // et, si oui, de la présence d'une nouvelle version si la règle de versionning impose un restart if ($needCreation || ($versionRule == Course::VERSIONING_RULE_RESTART && $currentVersion < $maxVersion)) { $newAttempt = new RegistrationAttempt(); $newAttempt->setCourseVersion($maxVersion); $newAttempt->setRegistration($registration); $this->em->persist($newAttempt); $this->em->flush(); $currentAttempt = $newAttempt; $currentVersion = $maxVersion; if (!$needCreation) { // TODO Indiquer dans la réponse que l'on a effectué une modification de version pour l'apprenant $data = null; } } // Récupération du zip correspondant et ainsi de la version du standard utilisée $mapCourseZipRepository = $this->em->getRepository(MapCourseZipfile::class); $mapCourseZip = $mapCourseZipRepository->findOneBy(array('course' => $course, 'version' => $currentVersion)); $zipFile = $mapCourseZip->getZipFile(); $standard = $zipFile->getStandard(); // Traitement des données en fonction du standard $variables = null; switch ($standard) { case DOMSCORM2004::SCORM_2004: $playerLogic = new Scorm2004PlayerLogic($this->router, $this->parameters, $this->em); $variables = $playerLogic->getScorm2004PlayerVariables($registrationKey, $zipFile, $currentAttempt, $data); break; case DOMSCORM12::SCORM_12: default: return null; } if ($variables['result'] == 'player') { // Construction d'un URL protégé $courseKey = $zipFile->getZipFileprint(); $this->prepareContentAccess($courseKey, $response); $protection = $this->getBaseUrl($courseKey); $url = $variables['player']['content']; $url = $protection . '/' . $url; $variables['player']['content'] = $url; } else { $this->response = new Response(); } return $variables; } /** * Récupération du pourcentage de complétion d'un cours * * La complétion d'un cours est soit définie par le module lui meme * (possibilité d'avoir une pondération de la contribution de chaque item au taux de complétion) * * Soit elle correspond au rapport du nombre d'items complétés * sur le nombre d'items terminaux(consultables par l'apprenant) * * @param string $registrationKey * * @return float */ public function getCompletion(string $registrationKey) { // Vérification de l'existance de l'inscription $registration = $this->requireRegistration($registrationKey); if (empty($registration)) { return 0; } // Récupération du dernier essai moteur pour cette inscription $registrationAttemptRepository = $this->em->getRepository(RegistrationAttempt::class); $lastAttempt = $registrationAttemptRepository->findOneBy(array('registration' => $registration), array('registrationAttemptId' => 'desc')); // DETECTER AVANT LE TYPE DE SCORM POUR SAVOIR QUEL STANDARD // CODE SCORM 2004 // On récupère le track correspondant $track2004Repository = $this->em->getRepository(Scorm2004Track::class); $track2004 = $track2004Repository->findOneBy(array('registrationAttempt' => $lastAttempt)); if (empty($track2004)) { return 0; } // Dans un premier temps on détermine si la complétion peut être remontée // en se basant sur l'état de complétion de l'ensemble des items terminaux $mapTrackItemRepository = $this->em->getRepository(Scorm2004MapTrackItem::class); $attemptItemRepository = $this->em->getRepository(Scorm2004AttemptProgressInformation::class); $mapTrackItems = $mapTrackItemRepository->findBy(array('scorm2004Track' => $track2004)); if (!empty($mapTrackItems)) { $numCompletedItems = 0; $numFinalItems = 0; foreach ($mapTrackItems as $item) { // Si les items sont terminaux if ($item->getAvailableOrder() > -1) { $numFinalItems ++; $identifier = $item->getItemIdentifier(); $attemptItem = $attemptItemRepository->findOneBy(array('scorm2004Track'=> $track2004, 'item_identifier' => $identifier), array('attempt_id' => 'desc')); if (!empty($attemptItem)) { $progressStatus = $attemptItem->getProgressStatus(); $completionStatus = $attemptItem->getCompletionStatus(); if (true == $progressStatus && true == $completionStatus) { $numCompletedItems ++; } } } } if ($numFinalItems != 0) { $completion = 100.0 * ($numCompletedItems / $numFinalItems); $completion = round($completion, 2); // Si on obtient une valeur de complétion elle est retournée if ($completion > 0) { return $completion; } } } // Si aucune complétion n'a été déterminée précédement // on controle que l'on aie pas un taux de complétion présent au niveau de l'organisation $organization = $track2004->getOrganizationId(); if (empty($organization)) { return 0; } //Récupération du dernier essai SCORM sur l'organisation $lastOrganizationAttempt = $attemptItemRepository->findOneBy(array('scorm2004Track'=> $track2004, 'item_identifier' => $organization), array('attempt_id' => 'desc')); if (empty($lastOrganizationAttempt)) { return 0; } // Récupération de la valeur continue de complétion de l'essai $completionAmount = $lastOrganizationAttempt->getCompletionAmount(); if ($completionAmount > 0) { return round(100.0 * $completionAmount, 2); } // Récupération de la valeur binaire de complétion de l'essai $progressStatus = $lastOrganizationAttempt->getProgressStatus(); $completionStatus = $lastOrganizationAttempt->getCompletionStatus(); if (true == $progressStatus && true == $completionStatus) { return 100; } // Pas de complétion récupérable return 0; } /** * Récupération du score moyen d'un cours * * @param string $registrationKey */ public function getAverageScore(string $registrationKey) { // Vérification de l'existance de l'inscription $registration = $this->requireRegistration($registrationKey); if (empty($registration)) { return null; } // Récupération du dernier essai pour cette inscription $registrationAttemptRepository = $this->em->getRepository(RegistrationAttempt::class); $lastAttempt = $registrationAttemptRepository->findOneBy(array('registration' => $registration), array('registrationAttemptId' => 'desc')); // DETECTER AVANT LE TYPE DE SCORM POUR SAVOIR QUEL STANDARD // CODE SCORM 2004 // On récupère le track correspondant $track2004Repository = $this->em->getRepository(Scorm2004Track::class); $track2004 = $track2004Repository->findOneBy(array('registrationAttempt' => $lastAttempt)); if (empty($track2004)) { return null; } $mapTrackItemRepository = $this->em->getRepository(Scorm2004MapTrackItem::class); $mapTrackItems = $mapTrackItemRepository->findBy(array('scorm2004Track' => $track2004)); $averageScore = null; if (!empty($mapTrackItems)) { $scoreSum = 0; $scoreNum = 0; foreach ($mapTrackItems as $item) { $scoreMax = $item->getScoreMax(); // Si pas de score max, pas de score valide if ($scoreMax > 0) { $score = $item->getScoreRaw(); $score = $score * 100 / $scoreMax; $score = max(min($score, 100), 0); $scoreNum ++; $scoreSum += $score; } } if ($scoreNum > 0) { $averageScore = $scoreSum / $scoreNum; $averageScore = round($averageScore, 2); } } return $averageScore; } /** * Récupération des interactions d'un cours * Elles sont rangées par item * * @param string $registrationKey */ public function getInteractions(string $registrationKey) { // Vérification de l'existance de l'inscription $registration = $this->requireRegistration($registrationKey); if (empty($registration)) { return null; } // Récupération du dernier essai pour cette inscription $registrationAttemptRepository = $this->em->getRepository(RegistrationAttempt::class); $lastAttempt = $registrationAttemptRepository->findOneBy(array('registration' => $registration), array('registrationAttemptId' => 'desc')); // DETECTER AVANT LE TYPE DE SCORM POUR SAVOIR QUEL STANDARD // CODE SCORM 2004 // On récupère le track correspondant $track2004Repository = $this->em->getRepository(Scorm2004Track::class); $track2004 = $track2004Repository->findOneBy(array('registrationAttempt' => $lastAttempt)); if (empty($track2004)) { return null; } $mapTrackItemRepository = $this->em->getRepository(Scorm2004MapTrackItem::class); // Récupération des données pour chaque items $mapTrackItems = $mapTrackItemRepository->findBy(array('scorm2004Track' => $track2004)); $interactions = array(); if (!empty($mapTrackItems)) { // Récupération du DOM afin d'avoir des infos sur les items $domScorm2004 = $lastAttempt->getDOM($this->packageFolder); $attemptItemRepository = $this->em->getRepository(Scorm2004AttemptProgressInformation::class); $interactionRepository = $this->em->getRepository(Scorm2004Interaction::class); // Pour chaque item foreach ($mapTrackItems as $item) { // On récupère l'identifiant de l'item $identifier = $item->getItemIdentifier(); // On récupère le dernier AttemptProgressInfo en cours $attemptItem = $attemptItemRepository->findOneBy(array('scorm2004Track'=> $track2004, 'item_identifier' => $identifier), array('attempt_id' => 'desc')); // On récupère les interactions pour cet item et cet Attempt $itemInteractions = $interactionRepository->findBy(array('scorm2004Track' => $track2004, 'scorm2004AttemptProgressInformation' => $attemptItem, 'itemIdentifier' => $identifier), array('interaction_id' => 'asc')); // Si il existe des interactions if (!empty($itemInteractions)) { $result = array(); // On récupère le nom de l'item $result['title'] = $domScorm2004->getTitle($identifier); // On Récupère le Score sur le mapTrackItem $score = null; $scoreMax = $item->getScoreMax(); // Si pas de score max, pas de score valide if ($scoreMax > 0) { $score = $item->getScoreRaw(); $score = $score * 100 / $scoreMax; $score = max(min($score, 100), 0); } $result['score'] = $score; // On récupère le Lesson Status (si on peut / voir Dans AI quoi envoyé pour avoir un résultat neutre à l'affichage) $status = $attemptItem->getSuccessStatus(); $result['lessonStatus'] = $status; // On ajoute les objets interactions $result['interactions'] = $itemInteractions; // On ajoute ce lot d'interaction aux interactions du cours $interactions[] = $result; } } } if (count($interactions) > 0) { return $interactions; } return null; } /** * supprime une inscription * * @param string $registrationKey * * @return string */ public function deleteRegistration(string $registrationKey) { $registration = $this->requireRegistration($registrationKey); if ($registration) { $this->em->remove($registration); $this->em->flush(); return true; } return false; } /** * supprime les données d'apprentissage d'un apprenant pour une inscription * * @param string $learnerKey * * @return bool */ public function resetRegistraitonData(string $registrationKey) { $trackRepository = $this->em->getRepository(Registration::class); $registration = $this->requireRegistration($registrationKey); if ($registration) { // Récupération des Registrations Attempts $attempts = $this->getAttempts($registrationKey); // Efface les RegistrationAttempts // Les objets SCORM seront effacés en cascade foreach ($attempts as $attempt) { $this->em->remove($attempt); } // Création d'un nouveau RegistrationAttempt $course = $registration->getCourse(); $attempt = new RegistrationAttempt(); $attempt->setCourseVersion($course->getMaxVersion()); $attempt->setRegistration($registration); $this->em->persist($attempt); $this->em->flush(); return true; } return false; } public function getRegistrations() { $repository = $this->em->getRepository(Registration::class); $registrations = $repository->findAll(); return $registrations; } // ATTEMPT /** * Récupération des essais * * @param string $registrationKey * * @return string */ public function getAttempts(string $registrationKey) { $registration = $this->requireRegistration($registrationKey); if ($registration) { $attempts = $registration->getRegistrationAttempts(); return ($attempts); } return null; } /** * Récupération de l'id de l'essai en cours * * @param string $registrationKey * * @return int */ public function getCurrentAttempt(string $registrationKey) { $registration = $this->requireRegistration($registrationKey); if ($registration) { $attemptRepository = $this->em->getRepository(RegistrationAttempt::class); $currentAttempt = $attemptRepository->findCurrentAttemptByRegistration($registration); return $currentAttempt->registrationAttemptId(); } return null; } /** * Récupération de la version de l'activité pour l'essai * * @param int $attemptId * * @return int */ public function getAttemptCourseVersion(int $attemptId) { $attempt = $this->requireRegistrationAttempt($attemptId); if ($attempt) { return $attempt->getCourseVersion(); } return null; } /** * Récupération des statistiques liées à un essai * * @param int $attemptId * * @return array */ public function getAttemptStats(int $attemptId) { } // TOOLS /** * Undocumented function * * @param string $courseKey * @param Response $response * @return void */ private function prepareContentAccess(string $courseKey, Response $response = null) : void { if (!$this->session->has('scormkey')) { $scormkey = rand(); $this->session->set('scormkey', $scormkey); } else { $scormkey = $this->session->get('scormkey'); } if (null === $response) { $this->response = new Response(); } else { $this->response = $response; } $pathCookie = $this->contentUrlPrefix."/$scormkey/$courseKey/"; $md5cookiekey = md5($scormkey); $cookie = new Cookie('SCORMBundle', $md5cookiekey, 0, $pathCookie); $this->response->headers->setCookie($cookie); } /** * il FAUT utiliser la Response de l'objet ScormEngine pour bénéficier du cookie * de sécurité (sans cela blocage complet de l'acces à la ressource) * * @return Response */ public function getResponse() : Response { return $this->response; } /** * renvoi l'url de base permettant d'acceder à un fichier de la ressource * Format : * /content/nomdupaquet/clefautorisation * * Example : * /content/ * * @return string */ public function getBaseUrl($courseKey) : string { return $this->contentUrlPrefix.'/'.$this->session->get('scormkey').'/'.$courseKey; } /** * Retourne l'activité * * @param string $courseKey * @return Course */ protected function requireCourse(string $courseKey) { $repository = $this->em->getRepository(Course::class); $course = $repository->findOneBy(array('courseKey' => $courseKey)); return $course; } /** * Retourne l'apprenant * * @param string $learnerKey * @return Learner */ protected function requireLearner(string $learnerKey) { $repository = $this->em->getRepository(Learner::class); $learner = $repository->findOneBy(array('learnerKey' => $learnerKey)); return $learner; } /** * Retourne l'inscription * * @param string $registrationKey * * @return Registration */ protected function requireRegistration(string $registrationKey) { $repository = $this->em->getRepository(Registration::class); $registration = $repository->findOneBy(array('registrationKey' => $registrationKey)); return $registration; } /** * Retourne un essais relatif à une inscription * * @param int $attemptId * * @return RegistrationAttempt */ protected function requireRegistrationAttempt(int $attemptId) { $repository = $this->em->getRepository(RegistrationAttempt::class); $attempt = $repository->find($attemptId); return $attempt; } }