No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ScormEngine.php 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. <?php
  2. namespace Logipro\Bundle\SCORMBundle\Services;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Logipro\Bundle\SCORMBundle\Entity\Course;
  5. use Logipro\Bundle\SCORMBundle\Entity\Learner;
  6. use Logipro\Bundle\SCORMBundle\Entity\ZipFile;
  7. use Doctrine\Common\Persistence\ManagerRegistry;
  8. use Logipro\Bundle\SCORMBundle\Entity\Registration;
  9. use Logipro\Bundle\SCORMBundle\Entity\MapCourseZipfile;
  10. use Logipro\Bundle\SCORMBundle\Package\PackageValidator;
  11. use Logipro\Bundle\SCORMBundle\Entity\RegistrationAttempt;
  12. use Symfony\Component\DependencyInjection\ContainerInterface;
  13. use Symfony\Component\DependencyInjection\ContainerAwareTrait;
  14. use Logipro\Bundle\SCORMBundle\Package\Common\PackageTypeDetector;
  15. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  16. /**
  17. * Service de communication du moteur
  18. */
  19. class ScormEngine implements ContainerAwareInterface
  20. {
  21. use ContainerAwareTrait;
  22. public function __construct()
  23. {
  24. }
  25. // ACTIVITÉ
  26. /**
  27. * Crée un cours à partir d'un package identifié par un chemin
  28. *
  29. * @param string $courseKey
  30. * @param string $zipPath
  31. * @param bool $analyse
  32. * @param string $versioningRule
  33. *
  34. * @return int
  35. */
  36. public function createCourse(string $courseKey, string $zipPath, bool $analyse, string $versioningRule = Course::VERSIONING_RULE_DEFAULT)
  37. {
  38. // Création de la réponse
  39. $response = array(
  40. 'code' => 200,
  41. 'isSuccessfullyCreated' => false,
  42. 'error' => '',
  43. 'parserErrors' => array(),
  44. 'course' => array(
  45. 'courseKey' => '',
  46. 'maxVersion' => 0,
  47. 'versioningRule' => $versioningRule
  48. ),
  49. 'metadata' => array(
  50. 'isAlreadyExisting' => false,
  51. 'standard' => '',
  52. 'title' => '',
  53. 'duration' => ''
  54. )
  55. );
  56. // Controle sur la clef fournie (elle doit etre non null et non existante en BD)
  57. if (!empty($courseKey)) {
  58. $em = $this->getDoctrine()->getManager();
  59. $courseRepository = $em->getRepository(Course::class);
  60. $course = $courseRepository->findOneBy(array('courseKey' => $courseKey));
  61. if ($course) {
  62. $response['code'] = 400;
  63. $response['error'] = 'Clef de cours déjà existante';
  64. return $response;
  65. }
  66. // ZIP
  67. // Test de l'existence du fichier
  68. if (!file_exists($zipPath)) {
  69. $response['code'] = 400;
  70. $response['error'] = 'le chemin ne pointe pas vers un paquet valide';
  71. return $response;
  72. }
  73. // Validation du Package
  74. $validation = PackageValidator::validatePackage($zipPath);
  75. if (!$validation['validation']['status']) {
  76. return $response;
  77. }
  78. $response['metadata']['standard'] = $validation['standard'];
  79. $response['metadata']['title'] = $validation['title'];
  80. // Récupération de la signature du zip
  81. $hash = md5_file($zipPath);
  82. // Test de l'existence de la signature
  83. $repository = $em->getRepository(ZipFile::class);
  84. $zipFile = $repository->findOneBy(['zipFileprint' => $hash]);
  85. $response['metadata']['isAlreadyExisting'] = !empty($zipFile);
  86. // On s'arrete ici si seule l'analyse du paquet est demandée
  87. if ($analyse) {
  88. return $response;
  89. }
  90. // si le fichier n'existe pas déjà on le crée et on le stock coté moteur
  91. if (empty($zipFile)) {
  92. $response['metadata']['isAlreadyExisting'] = false;
  93. //upload du fichier coté moteur
  94. $uploadDate = new \DateTime('NOW');
  95. // A remplacer par les config
  96. $uploadPath = $this->container->getParameter('kernel.root_dir');
  97. $fileName = pathinfo($zipPath)['filename'];
  98. $filePath = $uploadPath . $fileName;
  99. if (copy($zipPath, $filePath) === false) {
  100. // Si la copie plante, on retourne une erreur
  101. $response['code'] = 400;
  102. $response['error'] = 'Erreur lors de la copie du fichier zip';
  103. return $response;
  104. }
  105. $endUploadDate = new \DateTime('NOW');
  106. // Création d'un objet permettant de traiter le fichier zip
  107. $zipFile = new ZipFile();
  108. $zipFile->setZipFileName($fileName);
  109. $zipFile->setZipFileprint($hash);
  110. $zipFile->setUploadDate($uploadDate);
  111. $zipFile->setEndUploadDate($endUploadDate);
  112. $zipFile->setStandard($validation['standard']);
  113. $zipFile->setImportStatus(ZipFile::IMPORT_STATUS_PROCESSING);
  114. $em->persist($zipFile);
  115. }
  116. // COURS
  117. // Création du cours
  118. $course = new Course($courseId);
  119. $course->setVersionRule($versioningRule);
  120. $em->persist($course);
  121. // Métadonées du cours
  122. $response['course']['courseId'] = $courseId;
  123. $response['course']['maxVersion'] = $course->getMaxVersion();
  124. // Mapping Zip/Cours
  125. $mapping = new MapCourseZipfile();
  126. $mapping->setCourse($course);
  127. $mapping->setZipFile($zipFile);
  128. $mapping->setVersion($response['course']['maxVersion']);
  129. $em->persist($mapping);
  130. $em->flush();
  131. $response['isSuccessfullyCreated'] = true;
  132. }
  133. return $response;
  134. }
  135. // COURS
  136. // Création du cours
  137. $course = new Course();
  138. $course->setVersionRule($versioningRule);
  139. $course->setCourseKey($courseKey);
  140. $em->persist($course);
  141. // Métadonées du cours
  142. $response['course']['courseId'] = $courseKey;
  143. $response['course']['maxVersion'] = $course->getMaxVersion();
  144. // Mapping Zip/Cours
  145. $mapping = new MapCourseZipfile();
  146. $mapping->setCourse($course);
  147. $mapping->setZipFile($zipFile);
  148. $mapping->setVersion($response['course']['maxVersion']);
  149. $em->persist($mapping);
  150. $em->flush();
  151. }
  152. /**
  153. * Remplace le package d'une activité
  154. */
  155. public function updateCoursePacakge()
  156. {
  157. }
  158. /**
  159. * Remplace la règle de versionning de l'activité
  160. *
  161. * @param int $courseId
  162. * @param string $versionRule
  163. *
  164. * @return int
  165. */
  166. public function updateCourseVersionRule(int $courseId, string $versionRule)
  167. {
  168. $em = $this->getDoctrine()->getManager();
  169. $course = $this->requireActvity($courseId);
  170. if ($course) {
  171. $course->setVersionRule($versionRule);
  172. $em->persist($course);
  173. $em->flush();
  174. return $course->getCourseId();
  175. }
  176. return null;
  177. }
  178. /**
  179. * Supprime une activité
  180. */
  181. public function deleteCourse(int $courseId)
  182. {
  183. $em = $this->getDoctrine()->getManager();
  184. $course = $this->requireActvity($courseId);
  185. if ($course) {
  186. $em->remove($course);
  187. $em->flush();
  188. return true;
  189. }
  190. return false;
  191. }
  192. /**
  193. * retourne le numéro de version de l'activité
  194. */
  195. public function getCourseCurrentVersion(int $courseId)
  196. {
  197. $em = $this->getDoctrine()->getManager();
  198. $course = $this->requireActvity($courseId);
  199. if ($course) {
  200. return $course->getMaxVersion();
  201. }
  202. return null;
  203. }
  204. /**
  205. * retourne la règle de versionnig
  206. */
  207. public function getCourseVersionRule()
  208. {
  209. $em = $this->getDoctrine()->getManager();
  210. $course = $this->requireActvity($courseId);
  211. if ($course) {
  212. return $course->getVersionRule();
  213. }
  214. return null;
  215. }
  216. /**
  217. * retourne le type de package de l'activité
  218. */
  219. public function getCourseType()
  220. {
  221. $em = $this->getDoctrine()->getManager();
  222. $course = $this->requireActvity($courseId);
  223. if ($course) {
  224. return $course->getType();
  225. }
  226. return null;
  227. }
  228. public function getCourses() : array
  229. {
  230. $em = $this->getDoctrine()->getManager();
  231. $repository = $em->getRepository(Course::class);
  232. $courses = $repository->findAll();
  233. return $courses;
  234. }
  235. // LEARNER
  236. /**
  237. * Création d'un apprenant
  238. *
  239. * @param string $learnerKey
  240. * @param string $familyName
  241. * @param string $givenName
  242. * @return int
  243. */
  244. public function createLearner(string $learnerKey, string $familyName, string $givenName)
  245. {
  246. if (!empty($learnerKey)) {
  247. $em = $this->getDoctrine()->getManager();
  248. $learner = new Learner();
  249. $learner->setLearnerKey($learnerKey);
  250. $learner->setFamilyName($familyName);
  251. $learner->setGivenName($givenName);
  252. $em->persist($learner);
  253. $em->flush();
  254. return $learner->getLearnerId();
  255. }
  256. return null;
  257. }
  258. /**
  259. * Modification d'un apprenant
  260. *
  261. * @param int $id
  262. * @param string $familyName
  263. * @param string $givenName
  264. * @return int
  265. */
  266. public function updateLearner(int $learnerId, string $familyName, string $givenName)
  267. {
  268. $em = $this->getDoctrine()->getManager();
  269. $learner = $this->requireLearner($learnerId);
  270. if ($learner) {
  271. $learner->setFamilyName($familyName);
  272. $learner->setGivenName($givenName);
  273. $em->persist($learner);
  274. $em->flush();
  275. }
  276. return $learner->getLearnerId();
  277. }
  278. /**
  279. * récupération d'un apprenant
  280. *
  281. * @param int $learnerId
  282. * @return Learner
  283. */
  284. public function getLearner(int $learnerId)
  285. {
  286. $em = $this->getDoctrine()->getManager();
  287. $learner = $this->requireLearner($learnerId);
  288. return $learner;
  289. }
  290. public function getLearners()
  291. {
  292. $em = $this->getDoctrine()->getManager();
  293. $repository = $em->getRepository(Learner::class);
  294. $learners = $repository->findAll();
  295. return $learners;
  296. }
  297. /**
  298. * supprime un apprenant
  299. *
  300. * @param int $learnerId
  301. * @return string
  302. */
  303. public function deleteLearner(int $learnerId)
  304. {
  305. $em = $this->getDoctrine()->getManager();
  306. $learner = $this->requireLearner($learnerId);
  307. if ($learner) {
  308. $em->remove($learner);
  309. $em->flush();
  310. return true;
  311. }
  312. return false;
  313. }
  314. // REGISTRATION
  315. /**
  316. * création d'une inscription
  317. *
  318. * @param string $registrationKey
  319. * @param int $courseId
  320. * @param int $learnerId
  321. * @return int
  322. */
  323. public function createRegistration(string $registrationKey, int $courseId, int $learnerId)
  324. {
  325. $em = $this->getDoctrine()->getManager();
  326. $course = $this->requireActvity($courseId);
  327. $learner = $this->requireLearner($learnerId);
  328. if (!empty($registrationKey) && $course && $learner) {
  329. $registration = new Registration();
  330. $registration->setRegistrationKey($registrationKey);
  331. $registration->setCourse($course);
  332. $registration->setLearner($learner);
  333. $em->persist($registration);
  334. // Création du RegistrationAttempt
  335. $attempt = new RegistrationAttempt();
  336. $attempt->setCourseVersion($course->getMaxVersion());
  337. $attempt->setRegistration($registration);
  338. $em->persist($attempt);
  339. $em->flush();
  340. return $registration->getRegistrationKey();
  341. }
  342. return null;
  343. }
  344. /**
  345. * Renvoie les variables necessaire pour l'affichage du contenu du lecteur
  346. * dans le cadre d'une inscription (pour un apprenant et pour un cours)
  347. *
  348. * @param int $registrationId
  349. * @return array
  350. */
  351. public function getPlayerVariables(int $registrationId)
  352. {
  353. }
  354. /**
  355. * supprime une inscription
  356. *
  357. * @param int $id
  358. * @return string
  359. */
  360. public function deleteRegistration(int $registrationId)
  361. {
  362. $em = $this->getDoctrine()->getManager();
  363. $registration = $this->requireRegistration($registrationId);
  364. if ($registration) {
  365. $em->remove($registration);
  366. $em->flush();
  367. return true;
  368. }
  369. return false;
  370. }
  371. public function getRegistrations()
  372. {
  373. $em = $this->getDoctrine()->getManager();
  374. $repository = $em->getRepository(Registration::class);
  375. $registrations = $repository->findAll();
  376. return $registrations;
  377. }
  378. // ATTEMPT
  379. /**
  380. * Récupération des essais
  381. *
  382. * @param int $registrationId
  383. * @return int
  384. */
  385. public function getAttempts(int $registrationId)
  386. {
  387. $em = $this->getDoctrine()->getManager();
  388. $registration = $this->requireRegistration($registrationId);
  389. if ($registration) {
  390. $attempts = $registration->getRegistrationAttempts();
  391. return ($attempts);
  392. }
  393. return null;
  394. }
  395. /**
  396. * Récupération de l'essai en cours
  397. *
  398. * @param int $registrationId
  399. * @return int
  400. */
  401. public function getCurrentAttempt(int $registrationId)
  402. {
  403. $em = $this->getDoctrine()->getManager();
  404. $registration = $this->requireRegistration($registrationId);
  405. if ($registration) {
  406. $attemptRepository = $em->getRepository(RegistrationAttempt::class);
  407. $currentAttempt = $attemptRepository->findCurrentAttemptByRegistration($registration);
  408. return $currentAttempt;
  409. }
  410. return null;
  411. }
  412. /**
  413. * Récupération de la version de l'activité pour l'essai
  414. *
  415. * @param int $attemptId
  416. * @return int
  417. */
  418. public function getAttemptCourseVersion(int $attemptId)
  419. {
  420. $em = $this->getDoctrine()->getManager();
  421. $attempt = $this->requireRegistrationAttempt($attemptId);
  422. if ($attempt) {
  423. return $attempt->getCourseVersion();
  424. }
  425. return null;
  426. }
  427. /**
  428. * Récupération des statistiques liées à un essai
  429. *
  430. * @param int $attemptId
  431. * @return array
  432. */
  433. public function getAttemptStats(int $attemptId)
  434. {
  435. }
  436. /**
  437. *
  438. * @throws \LogicException
  439. * @return ManagerRegistry
  440. */
  441. protected function getDoctrine(): ManagerRegistry
  442. {
  443. if (!$this->container->has('doctrine')) {
  444. throw new \LogicException('The DoctrineBundle is not registered in your application. Try running "composer require symfony/orm-pack".');
  445. }
  446. return $this->container->get('doctrine');
  447. }
  448. /**
  449. * Retourne l'activité
  450. *
  451. * @param int $courseId
  452. * @return Course
  453. */
  454. protected function requireActvity(int $courseId)
  455. {
  456. $em = $this->getDoctrine()->getManager();
  457. $repository = $em->getRepository(Course::class);
  458. $course = $repository->find($courseId);
  459. return $course;
  460. }
  461. /**
  462. * Retourne l'apprenant
  463. *
  464. * @param int $learnerId
  465. * @return Learner
  466. */
  467. protected function requireLearner(int $learnerId)
  468. {
  469. $em = $this->getDoctrine()->getManager();
  470. $repository = $em->getRepository(Learner::class);
  471. $learner = $repository->find($learnerId);
  472. return $learner;
  473. }
  474. /**
  475. * Retourne l'inscription
  476. *
  477. * @param int $registrationId
  478. * @return Registration
  479. */
  480. protected function requireRegistration(int $registrationId)
  481. {
  482. $em = $this->getDoctrine()->getManager();
  483. $repository = $em->getRepository(Registration::class);
  484. $registration = $repository->find($registrationId);
  485. return $registration;
  486. }
  487. /**
  488. * Retourne un essais relatif à une inscription
  489. *
  490. * @param int $attemptId
  491. * @return RegistrationAttempt
  492. */
  493. protected function requireRegistrationAttempt(int $attemptId)
  494. {
  495. $em = $this->getDoctrine()->getManager();
  496. $repository = $em->getRepository(RegistrationAttempt::class);
  497. $attempt = $repository->find($attemptId);
  498. return $attempt;
  499. }
  500. }