events.lib.php 73 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189
  1. <?php
  2. /* See license terms in /license.txt */
  3. //use Chamilo\UserBundle\Entity\User;
  4. use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
  5. use ChamiloSession as Session;
  6. /**
  7. * Class Event
  8. * Functions of this library are used to record informations when some kind
  9. * of event occur. Each event has his own types of informations then each event
  10. * use its own function.
  11. */
  12. class Event
  13. {
  14. /**
  15. * @author Sebastien Piraux <piraux_seb@hotmail.com> old code
  16. * @author Julio Montoya
  17. *
  18. * @param int $userId
  19. *
  20. * @return bool
  21. * @desc Record information for login event when an user identifies himself with username & password
  22. */
  23. public static function eventLogin($userId)
  24. {
  25. $userInfo = api_get_user_info($userId);
  26. $userId = (int) $userId;
  27. if (empty($userInfo)) {
  28. return false;
  29. }
  30. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
  31. $reallyNow = api_get_utc_datetime();
  32. $userIp = Database::escape_string(api_get_real_ip());
  33. $sql = "INSERT INTO $table (login_user_id, user_ip, login_date, logout_date) VALUES
  34. ($userId,
  35. '$userIp',
  36. '$reallyNow',
  37. '$reallyNow'
  38. )";
  39. Database::query($sql);
  40. // Auto subscribe
  41. $user_status = $userInfo['status'] == SESSIONADMIN ? 'sessionadmin' : $userInfo['status'] == COURSEMANAGER ? 'teacher' : $userInfo['status'] == DRH ? 'DRH' : 'student';
  42. $autoSubscribe = api_get_setting($user_status.'_autosubscribe');
  43. if ($autoSubscribe) {
  44. $autoSubscribe = explode('|', $autoSubscribe);
  45. foreach ($autoSubscribe as $code) {
  46. if (CourseManager::course_exists($code)) {
  47. CourseManager::subscribeUser($userId, $code);
  48. }
  49. }
  50. }
  51. return true;
  52. }
  53. /**
  54. * @param int $sessionId
  55. *
  56. * @return bool
  57. */
  58. public static function isSessionLogNeedToBeSave($sessionId)
  59. {
  60. if (!empty($sessionId)) {
  61. $visibility = api_get_session_visibility($sessionId);
  62. if (!empty($visibility) && $visibility != SESSION_AVAILABLE) {
  63. $extraFieldValue = new ExtraFieldValue('session');
  64. $value = $extraFieldValue->get_values_by_handler_and_field_variable(
  65. $sessionId,
  66. 'disable_log_after_session_ends'
  67. );
  68. if (!empty($value) && isset($value['value']) && (int) $value['value'] == 1) {
  69. return false;
  70. }
  71. }
  72. }
  73. return true;
  74. }
  75. /**
  76. * @author Sebastien Piraux <piraux_seb@hotmail.com>
  77. * @desc Record information for access event for courses
  78. *
  79. * @return bool
  80. */
  81. public static function accessCourse()
  82. {
  83. if (Session::read('login_as')) {
  84. return false;
  85. }
  86. $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
  87. // For "what's new" notification
  88. $TABLETRACK_LASTACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
  89. $sessionId = api_get_session_id();
  90. $now = api_get_utc_datetime();
  91. $courseId = api_get_course_int_id();
  92. $userId = api_get_user_id();
  93. $ip = Database::escape_string(api_get_real_ip());
  94. if (self::isSessionLogNeedToBeSave($sessionId) === false) {
  95. return false;
  96. }
  97. if ($userId) {
  98. $userId = $userId;
  99. } else {
  100. $userId = '0'; // no one
  101. }
  102. $sql = "INSERT INTO $TABLETRACK_ACCESS (user_ip, access_user_id, c_id, access_date, access_session_id)
  103. VALUES ('$ip', $userId, $courseId, '$now', $sessionId)";
  104. Database::query($sql);
  105. // added for "what's new" notification
  106. $sql = "UPDATE $TABLETRACK_LASTACCESS SET access_date = '$now'
  107. WHERE
  108. access_user_id = $userId AND
  109. c_id = $courseId AND
  110. access_tool IS NULL AND
  111. access_session_id = $sessionId";
  112. $result = Database::query($sql);
  113. if (Database::affected_rows($result) == 0) {
  114. $sql = "INSERT INTO $TABLETRACK_LASTACCESS (access_user_id, c_id, access_date, access_session_id)
  115. VALUES ($userId, $courseId, '$now', $sessionId)";
  116. Database::query($sql);
  117. }
  118. return true;
  119. }
  120. /**
  121. * @param string $tool name of the tool
  122. *
  123. * @author Sebastien Piraux <piraux_seb@hotmail.com>
  124. * @desc Record information for access event for tools
  125. *
  126. * $tool can take this values :
  127. * Links, Calendar, Document, Announcements,
  128. * Group, Video, Works, Users, Exercises, Course Desc
  129. * ...
  130. * Values can be added if new modules are created (15char max)
  131. * I encourage to use $nameTool as $tool when calling this function
  132. *
  133. * Functionality for "what's new" notification is added by Toon Van Hoecke
  134. *
  135. * @return bool
  136. */
  137. public static function event_access_tool($tool)
  138. {
  139. if (Session::read('login_as')) {
  140. return false;
  141. }
  142. $tool = Database::escape_string($tool);
  143. if (empty($tool)) {
  144. return false;
  145. }
  146. $courseInfo = api_get_course_info();
  147. $sessionId = api_get_session_id();
  148. $reallyNow = api_get_utc_datetime();
  149. $userId = api_get_user_id();
  150. if (empty($courseInfo)) {
  151. return false;
  152. }
  153. if (self::isSessionLogNeedToBeSave($sessionId) === false) {
  154. return false;
  155. }
  156. $courseId = $courseInfo['real_id'];
  157. $tableAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
  158. //for "what's new" notification
  159. $tableLastAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
  160. // record information
  161. // only if user comes from the course $_cid
  162. //if( eregi($_configuration['root_web'].$_cid,$_SERVER['HTTP_REFERER'] ) )
  163. //$pos = strpos($_SERVER['HTTP_REFERER'],$_configuration['root_web'].$_cid);
  164. $coursePath = isset($courseInfo['path']) ? $courseInfo['path'] : null;
  165. $pos = isset($_SERVER['HTTP_REFERER']) ? strpos(strtolower($_SERVER['HTTP_REFERER']), strtolower(api_get_path(WEB_COURSE_PATH).$coursePath)) : false;
  166. // added for "what's new" notification
  167. $pos2 = isset($_SERVER['HTTP_REFERER']) ? strpos(strtolower($_SERVER['HTTP_REFERER']), strtolower(api_get_path(WEB_PATH)."index")) : false;
  168. // end "what's new" notification
  169. if ($pos !== false || $pos2 !== false) {
  170. $params = [
  171. 'access_user_id' => $userId,
  172. 'c_id' => $courseId,
  173. 'access_tool' => $tool,
  174. 'access_date' => $reallyNow,
  175. 'access_session_id' => $sessionId,
  176. 'user_ip' => Database::escape_string(api_get_real_ip()),
  177. ];
  178. Database::insert($tableAccess, $params);
  179. }
  180. // "what's new" notification
  181. $sql = "UPDATE $tableLastAccess
  182. SET access_date = '$reallyNow'
  183. WHERE
  184. access_user_id = $userId AND
  185. c_id = $courseId AND
  186. access_tool = '$tool' AND
  187. access_session_id = $sessionId";
  188. $result = Database::query($sql);
  189. if (Database::affected_rows($result) == 0) {
  190. $params = [
  191. 'access_user_id' => $userId,
  192. 'c_id' => $courseId,
  193. 'access_tool' => $tool,
  194. 'access_date' => $reallyNow,
  195. 'access_session_id' => $sessionId,
  196. ];
  197. Database::insert($tableLastAccess, $params);
  198. }
  199. return true;
  200. }
  201. /**
  202. * Record information for download event (when an user click to d/l a
  203. * document) it will be used in a redirection page.
  204. *
  205. * @param string $documentUrl
  206. *
  207. * @return int
  208. *
  209. * @author Sebastien Piraux <piraux_seb@hotmail.com>
  210. * @author Evie Embrechts (bug fixed: The user id is put in single quotes)
  211. */
  212. public static function event_download($documentUrl)
  213. {
  214. if (Session::read('login_as')) {
  215. return false;
  216. }
  217. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
  218. $documentUrl = Database::escape_string($documentUrl);
  219. $reallyNow = api_get_utc_datetime();
  220. $userId = api_get_user_id();
  221. $courseId = api_get_course_int_id();
  222. $sessionId = api_get_session_id();
  223. $sql = "INSERT INTO $table (
  224. down_user_id,
  225. c_id,
  226. down_doc_path,
  227. down_date,
  228. down_session_id
  229. )
  230. VALUES (
  231. $userId,
  232. $courseId,
  233. '$documentUrl',
  234. '$reallyNow',
  235. $sessionId
  236. )";
  237. Database::query($sql);
  238. return 1;
  239. }
  240. /**
  241. * @param int $documentId of document (id in mainDb.document table)
  242. *
  243. * @author Sebastien Piraux <piraux_seb@hotmail.com>
  244. * @desc Record information for upload event
  245. * used in the works tool to record informations when
  246. * an user upload 1 work
  247. *
  248. * @return int
  249. */
  250. public static function event_upload($documentId)
  251. {
  252. if (Session::read('login_as')) {
  253. return false;
  254. }
  255. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_UPLOADS);
  256. $courseId = api_get_course_int_id();
  257. $reallyNow = api_get_utc_datetime();
  258. $userId = api_get_user_id();
  259. $documentId = (int) $documentId;
  260. $sessionId = api_get_session_id();
  261. $sql = "INSERT INTO $table
  262. ( upload_user_id,
  263. c_id,
  264. upload_work_id,
  265. upload_date,
  266. upload_session_id
  267. )
  268. VALUES (
  269. $userId,
  270. $courseId,
  271. $documentId,
  272. '$reallyNow',
  273. $sessionId
  274. )";
  275. Database::query($sql);
  276. return 1;
  277. }
  278. /**
  279. * Record information for link event (when an user click on an added link)
  280. * it will be used in a redirection page.
  281. *
  282. * @param int $linkId (id in c_link table)
  283. *
  284. * @return int
  285. *
  286. * @author Sebastien Piraux <piraux_seb@hotmail.com>
  287. */
  288. public static function event_link($linkId)
  289. {
  290. if (Session::read('login_as')) {
  291. return false;
  292. }
  293. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
  294. $reallyNow = api_get_utc_datetime();
  295. $userId = api_get_user_id();
  296. $courseId = api_get_course_int_id();
  297. $linkId = (int) $linkId;
  298. $sessionId = api_get_session_id();
  299. $sql = "INSERT INTO ".$table."
  300. ( links_user_id,
  301. c_id,
  302. links_link_id,
  303. links_date,
  304. links_session_id
  305. ) VALUES (
  306. $userId,
  307. $courseId,
  308. $linkId,
  309. '$reallyNow',
  310. $sessionId
  311. )";
  312. Database::query($sql);
  313. return 1;
  314. }
  315. /**
  316. * Update the TRACK_E_EXERCICES exercises.
  317. *
  318. * @param int exeid id of the attempt
  319. * @param int exo_id exercise id
  320. * @param mixed result score
  321. * @param int weighting ( higher score )
  322. * @param int duration ( duration of the attempt in seconds )
  323. * @param int session_id
  324. * @param int learnpath_id (id of the learnpath)
  325. * @param int learnpath_item_id (id of the learnpath_item)
  326. *
  327. * @return bool
  328. *
  329. * @author Sebastien Piraux <piraux_seb@hotmail.com>
  330. * @author Julio Montoya Armas <gugli100@gmail.com> Reworked 2010
  331. * @desc Record result of user when an exercise was done
  332. */
  333. public static function updateEventExercise(
  334. $exeId,
  335. $exoId,
  336. $score,
  337. $weighting,
  338. $sessionId,
  339. $learnpathId = 0,
  340. $learnpathItemId = 0,
  341. $learnpathItemViewId = 0,
  342. $duration = 0,
  343. $questionsList = [],
  344. $status = '',
  345. $remindList = [],
  346. $endDate = null
  347. ) {
  348. if (empty($exeId)) {
  349. return false;
  350. }
  351. /*
  352. * Code commented due BT#8423 do not change the score to 0.
  353. *
  354. * Validation in case of fraud with actived control time
  355. if (!ExerciseLib::exercise_time_control_is_valid($exo_id, $learnpath_id, $learnpath_item_id)) {
  356. $score = 0;
  357. }
  358. */
  359. if (!isset($status) || empty($status)) {
  360. $status = '';
  361. } else {
  362. $status = Database::escape_string($status);
  363. }
  364. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  365. if (!empty($questionsList)) {
  366. $questionsList = array_map('intval', $questionsList);
  367. }
  368. if (!empty($remindList)) {
  369. $remindList = array_map('intval', $remindList);
  370. $remindList = array_filter($remindList);
  371. $remindList = implode(",", $remindList);
  372. } else {
  373. $remindList = '';
  374. }
  375. if (empty($endDate)) {
  376. $endDate = api_get_utc_datetime();
  377. }
  378. $exoId = (int) $exoId;
  379. $sessionId = (int) $sessionId;
  380. $learnpathId = (int) $learnpathId;
  381. $learnpathItemId = (int) $learnpathItemId;
  382. $learnpathItemViewId = (int) $learnpathItemViewId;
  383. $duration = (int) $duration;
  384. $exeId = (int) $exeId;
  385. $score = Database::escape_string($score);
  386. $weighting = Database::escape_string($weighting);
  387. $questions = implode(',', $questionsList);
  388. $userIp = Database::escape_string(api_get_real_ip());
  389. $sql = "UPDATE $table SET
  390. exe_exo_id = $exoId,
  391. score = '$score',
  392. max_score = '$weighting',
  393. session_id = $sessionId,
  394. orig_lp_id = $learnpathId,
  395. orig_lp_item_id = $learnpathItemId,
  396. orig_lp_item_view_id = $learnpathItemViewId,
  397. exe_duration = $duration,
  398. exe_date = '$endDate',
  399. status = '$status',
  400. questions_to_check = '$remindList',
  401. data_tracking = '$questions',
  402. user_ip = '$userIp'
  403. WHERE exe_id = $exeId";
  404. Database::query($sql);
  405. //Deleting control time session track
  406. //ExerciseLib::exercise_time_control_delete($exo_id);
  407. return true;
  408. }
  409. /**
  410. * Record an event for this attempt at answering an exercise.
  411. *
  412. * @param float Score achieved
  413. * @param string Answer given
  414. * @param int Question ID
  415. * @param int Exercise attempt ID a.k.a exe_id (from track_e_exercise)
  416. * @param int Position
  417. * @param int Exercise ID (from c_quiz)
  418. * @param bool update results?
  419. * @param $fileName string Filename (for audio answers - using nanogong)
  420. * @param int User ID The user who's going to get this score. Default value of null means "get from context".
  421. * @param int Course ID (from the "id" column of course table). Default value of null means "get from context".
  422. * @param int Session ID (from the session table). Default value of null means "get from context".
  423. * @param int Learnpath ID (from c_lp table). Default value of null means "get from context".
  424. * @param int Learnpath item ID (from the c_lp_item table). Default value of null means "get from context".
  425. *
  426. * @return bool Result of the insert query
  427. */
  428. public static function saveQuestionAttempt(
  429. $score,
  430. $answer,
  431. $question_id,
  432. $exe_id,
  433. $position,
  434. $exercise_id = 0,
  435. $updateResults = false,
  436. $fileName = null,
  437. $user_id = null,
  438. $course_id = null,
  439. $session_id = null,
  440. $learnpath_id = null,
  441. $learnpath_item_id = null
  442. ) {
  443. global $debug;
  444. $question_id = Database::escape_string($question_id);
  445. $exe_id = Database::escape_string($exe_id);
  446. $position = Database::escape_string($position);
  447. $now = api_get_utc_datetime();
  448. $course_id = (int) $course_id;
  449. // check user_id or get from context
  450. if (empty($user_id)) {
  451. $user_id = api_get_user_id();
  452. // anonymous
  453. if (empty($user_id)) {
  454. $user_id = api_get_anonymous_id();
  455. }
  456. }
  457. // check course_id or get from context
  458. if (empty($course_id)) {
  459. $course_id = api_get_course_int_id();
  460. }
  461. // check session_id or get from context
  462. $session_id = (int) $session_id;
  463. if (empty($session_id)) {
  464. $session_id = api_get_session_id();
  465. }
  466. // check learnpath_id or get from context
  467. if (empty($learnpath_id)) {
  468. global $learnpath_id;
  469. }
  470. // check learnpath_item_id or get from context
  471. if (empty($learnpath_item_id)) {
  472. global $learnpath_item_id;
  473. }
  474. $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  475. if ($debug) {
  476. error_log("----- entering saveQuestionAttempt() function ------");
  477. error_log("answer: $answer");
  478. error_log("score: $score");
  479. error_log("question_id : $question_id");
  480. error_log("position: $position");
  481. }
  482. //Validation in case of fraud with active control time
  483. if (!ExerciseLib::exercise_time_control_is_valid($exercise_id, $learnpath_id, $learnpath_item_id)) {
  484. if ($debug) {
  485. error_log("exercise_time_control_is_valid is false");
  486. }
  487. $score = 0;
  488. $answer = 0;
  489. }
  490. if (!empty($question_id) && !empty($exe_id) && !empty($user_id)) {
  491. if (is_null($answer)) {
  492. $answer = '';
  493. }
  494. if (is_null($score)) {
  495. $score = 0;
  496. }
  497. $attempt = [
  498. 'user_id' => $user_id,
  499. 'question_id' => $question_id,
  500. 'answer' => $answer,
  501. 'marks' => $score,
  502. 'c_id' => $course_id,
  503. 'session_id' => $session_id,
  504. 'position' => $position,
  505. 'tms' => $now,
  506. 'filename' => !empty($fileName) ? basename($fileName) : $fileName,
  507. 'teacher_comment' => '',
  508. ];
  509. // Check if attempt exists.
  510. $sql = "SELECT exe_id FROM $TBL_TRACK_ATTEMPT
  511. WHERE
  512. c_id = $course_id AND
  513. session_id = $session_id AND
  514. exe_id = $exe_id AND
  515. user_id = $user_id AND
  516. question_id = $question_id AND
  517. position = $position";
  518. $result = Database::query($sql);
  519. if (Database::num_rows($result)) {
  520. if ($debug) {
  521. error_log("Attempt already exist: exe_id: $exe_id - user_id:$user_id - question_id:$question_id");
  522. }
  523. if ($updateResults == false) {
  524. //The attempt already exist do not update use update_event_exercise() instead
  525. return false;
  526. }
  527. } else {
  528. $attempt['exe_id'] = $exe_id;
  529. }
  530. if ($debug) {
  531. error_log("updateResults : $updateResults");
  532. error_log("Saving question attempt: ");
  533. error_log($sql);
  534. }
  535. $recording_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
  536. if ($updateResults == false) {
  537. $attempt_id = Database::insert($TBL_TRACK_ATTEMPT, $attempt);
  538. if ($debug) {
  539. error_log("Insert attempt with id #$attempt_id");
  540. }
  541. if (defined('ENABLED_LIVE_EXERCISE_TRACKING')) {
  542. if ($debug) {
  543. error_log("Saving e attempt recording ");
  544. }
  545. $attempt_recording = [
  546. 'exe_id' => $attempt_id,
  547. 'question_id' => $question_id,
  548. 'marks' => $score,
  549. 'insert_date' => $now,
  550. 'author' => '',
  551. 'session_id' => $session_id,
  552. ];
  553. Database::insert($recording_table, $attempt_recording);
  554. }
  555. } else {
  556. Database::update(
  557. $TBL_TRACK_ATTEMPT,
  558. $attempt,
  559. [
  560. 'exe_id = ? AND question_id = ? AND user_id = ? ' => [
  561. $exe_id,
  562. $question_id,
  563. $user_id,
  564. ],
  565. ]
  566. );
  567. if (defined('ENABLED_LIVE_EXERCISE_TRACKING')) {
  568. $attempt_recording = [
  569. 'exe_id' => $exe_id,
  570. 'question_id' => $question_id,
  571. 'marks' => $score,
  572. 'insert_date' => $now,
  573. 'author' => '',
  574. 'session_id' => $session_id,
  575. ];
  576. Database::update(
  577. $recording_table,
  578. $attempt_recording,
  579. [
  580. 'exe_id = ? AND question_id = ? AND session_id = ? ' => [
  581. $exe_id,
  582. $question_id,
  583. $session_id,
  584. ],
  585. ]
  586. );
  587. }
  588. $attempt_id = $exe_id;
  589. }
  590. return $attempt_id;
  591. } else {
  592. return false;
  593. }
  594. }
  595. /**
  596. * Record an hotspot spot for this attempt at answering an hotspot question.
  597. *
  598. * @param int $exeId
  599. * @param int $questionId Question ID
  600. * @param int $answerId Answer ID
  601. * @param int $correct
  602. * @param string $coords Coordinates of this point (e.g. 123;324)
  603. * @param bool $updateResults
  604. * @param int $exerciseId
  605. *
  606. * @return bool Result of the insert query
  607. *
  608. * @uses \Course code and user_id from global scope $_cid and $_user
  609. */
  610. public static function saveExerciseAttemptHotspot(
  611. $exeId,
  612. $questionId,
  613. $answerId,
  614. $correct,
  615. $coords,
  616. $updateResults = false,
  617. $exerciseId = 0
  618. ) {
  619. $debug = false;
  620. global $safe_lp_id, $safe_lp_item_id;
  621. if ($updateResults == false) {
  622. // Validation in case of fraud with activated control time
  623. if (!ExerciseLib::exercise_time_control_is_valid($exerciseId, $safe_lp_id, $safe_lp_item_id)) {
  624. if ($debug) {
  625. error_log('Attempt is fraud');
  626. }
  627. $correct = 0;
  628. }
  629. }
  630. if (empty($exeId)) {
  631. if ($debug) {
  632. error_log('exe id is empty');
  633. }
  634. return false;
  635. }
  636. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
  637. if ($updateResults) {
  638. if ($debug) {
  639. error_log("Insert hotspot results: exeId: $exeId correct: $correct");
  640. }
  641. $params = [
  642. 'hotspot_correct' => $correct,
  643. 'hotspot_coordinate' => $coords,
  644. ];
  645. Database::update(
  646. $table,
  647. $params,
  648. [
  649. 'hotspot_user_id = ? AND hotspot_exe_id = ? AND hotspot_question_id = ? AND hotspot_answer_id = ? ' => [
  650. api_get_user_id(),
  651. $exeId,
  652. $questionId,
  653. $answerId,
  654. ],
  655. ]
  656. );
  657. } else {
  658. if ($debug) {
  659. error_log("Insert hotspot results: exeId: $exeId correct: $correct");
  660. }
  661. return Database::insert(
  662. $table,
  663. [
  664. 'hotspot_user_id' => api_get_user_id(),
  665. 'c_id' => api_get_course_int_id(),
  666. 'hotspot_exe_id' => $exeId,
  667. 'hotspot_question_id' => $questionId,
  668. 'hotspot_answer_id' => $answerId,
  669. 'hotspot_correct' => $correct,
  670. 'hotspot_coordinate' => $coords,
  671. ]
  672. );
  673. }
  674. }
  675. /**
  676. * Records information for common (or admin) events (in the track_e_default table).
  677. *
  678. * @author Yannick Warnier <yannick.warnier@beeznest.com>
  679. *
  680. * @param string $event_type Type of event
  681. * @param string $event_value_type Type of value
  682. * @param mixed $event_value Value (string, or array in the case of user info)
  683. * @param string $datetime Datetime (UTC) (defaults to null)
  684. * @param int $user_id User ID (defaults to null)
  685. * @param int $course_id Course ID (defaults to null)
  686. * @param int $sessionId Session ID
  687. *
  688. * @return bool
  689. * @assert ('','','') === false
  690. */
  691. public static function addEvent(
  692. $event_type,
  693. $event_value_type,
  694. $event_value,
  695. $datetime = null,
  696. $user_id = null,
  697. $course_id = null,
  698. $sessionId = 0
  699. ) {
  700. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
  701. if (empty($event_type)) {
  702. return false;
  703. }
  704. $event_type = Database::escape_string($event_type);
  705. $event_value_type = Database::escape_string($event_value_type);
  706. if (!empty($course_id)) {
  707. $course_id = (int) $course_id;
  708. } else {
  709. $course_id = api_get_course_int_id();
  710. }
  711. if (!empty($sessionId)) {
  712. $sessionId = (int) $sessionId;
  713. } else {
  714. $sessionId = api_get_session_id();
  715. }
  716. //Clean the user_info
  717. if ($event_value_type == LOG_USER_OBJECT) {
  718. if (is_array($event_value)) {
  719. unset($event_value['complete_name']);
  720. unset($event_value['complete_name_with_username']);
  721. unset($event_value['firstName']);
  722. unset($event_value['lastName']);
  723. unset($event_value['avatar_small']);
  724. unset($event_value['avatar']);
  725. unset($event_value['mail']);
  726. unset($event_value['password']);
  727. unset($event_value['last_login']);
  728. unset($event_value['picture_uri']);
  729. $event_value = serialize($event_value);
  730. }
  731. }
  732. // If event is an array then the $event_value_type should finish with
  733. // the suffix _array for example LOG_WORK_DATA = work_data_array
  734. if (is_array($event_value)) {
  735. $event_value = serialize($event_value);
  736. }
  737. $event_value = Database::escape_string($event_value);
  738. $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
  739. if (!isset($datetime)) {
  740. $datetime = api_get_utc_datetime();
  741. }
  742. $datetime = Database::escape_string($datetime);
  743. if (!isset($user_id)) {
  744. $user_id = api_get_user_id();
  745. }
  746. $params = [
  747. 'default_user_id' => $user_id,
  748. 'c_id' => $course_id,
  749. 'default_date' => $datetime,
  750. 'default_event_type' => $event_type,
  751. 'default_value_type' => $event_value_type,
  752. 'default_value' => $event_value,
  753. 'session_id' => $sessionId,
  754. ];
  755. Database::insert($table, $params);
  756. return true;
  757. }
  758. /**
  759. * Gets the last attempt of an exercise based in the exe_id.
  760. *
  761. * @param int $exeId
  762. *
  763. * @return mixed
  764. */
  765. public static function getLastAttemptDateOfExercise($exeId)
  766. {
  767. $exeId = (int) $exeId;
  768. $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  769. $sql = "SELECT max(tms) as last_attempt_date
  770. FROM $track_attempts
  771. WHERE exe_id = $exeId";
  772. $rs_last_attempt = Database::query($sql);
  773. $row_last_attempt = Database::fetch_array($rs_last_attempt);
  774. $date = $row_last_attempt['last_attempt_date']; //Get the date of last attempt
  775. return $date;
  776. }
  777. /**
  778. * Gets the last attempt of an exercise based in the exe_id.
  779. *
  780. * @param int $exeId
  781. *
  782. * @return mixed
  783. */
  784. public static function getLatestQuestionIdFromAttempt($exeId)
  785. {
  786. $exeId = (int) $exeId;
  787. $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  788. $sql = "SELECT question_id FROM $track_attempts
  789. WHERE exe_id = $exeId
  790. ORDER BY tms DESC
  791. LIMIT 1";
  792. $result = Database::query($sql);
  793. if (Database::num_rows($result)) {
  794. $row = Database::fetch_array($result);
  795. return $row['question_id'];
  796. } else {
  797. return false;
  798. }
  799. }
  800. /**
  801. * Gets how many attempts exists by user, exercise, learning path.
  802. *
  803. * @param int user id
  804. * @param int exercise id
  805. * @param int lp id
  806. * @param int lp item id
  807. * @param int lp item view id
  808. *
  809. * @return int
  810. */
  811. public static function get_attempt_count(
  812. $user_id,
  813. $exerciseId,
  814. $lp_id,
  815. $lp_item_id,
  816. $lp_item_view_id
  817. ) {
  818. $stat_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  819. $user_id = (int) $user_id;
  820. $exerciseId = (int) $exerciseId;
  821. $lp_id = (int) $lp_id;
  822. $lp_item_id = (int) $lp_item_id;
  823. $lp_item_view_id = (int) $lp_item_view_id;
  824. $courseId = api_get_course_int_id();
  825. $sessionId = api_get_session_id();
  826. $sql = "SELECT count(*) as count
  827. FROM $stat_table
  828. WHERE
  829. exe_exo_id = $exerciseId AND
  830. exe_user_id = $user_id AND
  831. status != 'incomplete' AND
  832. orig_lp_id = $lp_id AND
  833. orig_lp_item_id = $lp_item_id AND
  834. orig_lp_item_view_id = $lp_item_view_id AND
  835. c_id = $courseId AND
  836. session_id = $sessionId";
  837. $query = Database::query($sql);
  838. if (Database::num_rows($query) > 0) {
  839. $attempt = Database::fetch_array($query, 'ASSOC');
  840. return (int) $attempt['count'];
  841. }
  842. return 0;
  843. }
  844. /**
  845. * @param $user_id
  846. * @param $exerciseId
  847. * @param $lp_id
  848. * @param $lp_item_id
  849. *
  850. * @return int
  851. */
  852. public static function get_attempt_count_not_finished(
  853. $user_id,
  854. $exerciseId,
  855. $lp_id,
  856. $lp_item_id
  857. ) {
  858. $stat_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  859. $user_id = (int) $user_id;
  860. $exerciseId = (int) $exerciseId;
  861. $lp_id = (int) $lp_id;
  862. $lp_item_id = (int) $lp_item_id;
  863. //$lp_item_view_id = (int) $lp_item_view_id;
  864. $courseId = api_get_course_int_id();
  865. $sessionId = api_get_session_id();
  866. $sql = "SELECT count(*) as count
  867. FROM $stat_table
  868. WHERE
  869. exe_exo_id = $exerciseId AND
  870. exe_user_id = $user_id AND
  871. status != 'incomplete' AND
  872. orig_lp_id = $lp_id AND
  873. orig_lp_item_id = $lp_item_id AND
  874. c_id = $courseId AND
  875. session_id = $sessionId";
  876. $query = Database::query($sql);
  877. if (Database::num_rows($query) > 0) {
  878. $attempt = Database::fetch_array($query, 'ASSOC');
  879. return $attempt['count'];
  880. } else {
  881. return 0;
  882. }
  883. }
  884. /**
  885. * @param int $user_id
  886. * @param int $lp_id
  887. * @param array $course
  888. * @param int $session_id
  889. *
  890. * @return bool
  891. */
  892. public static function delete_student_lp_events(
  893. $user_id,
  894. $lp_id,
  895. $course,
  896. $session_id
  897. ) {
  898. $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
  899. $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
  900. $lpInteraction = Database::get_course_table(TABLE_LP_IV_INTERACTION);
  901. $lpObjective = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
  902. if (empty($course)) {
  903. return false;
  904. }
  905. $course_id = $course['real_id'];
  906. $user_id = (int) $user_id;
  907. $lp_id = (int) $lp_id;
  908. $session_id = (int) $session_id;
  909. if (empty($course_id)) {
  910. $course_id = api_get_course_int_id();
  911. }
  912. $track_e_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  913. $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  914. $recording_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
  915. // Make sure we have the exact lp_view_id
  916. $sql = "SELECT id FROM $lp_view_table
  917. WHERE
  918. c_id = $course_id AND
  919. user_id = $user_id AND
  920. lp_id = $lp_id AND
  921. session_id = $session_id";
  922. $result = Database::query($sql);
  923. if (Database::num_rows($result)) {
  924. $view = Database::fetch_array($result, 'ASSOC');
  925. $lp_view_id = $view['id'];
  926. $sql = "DELETE FROM $lp_item_view_table
  927. WHERE c_id = $course_id AND lp_view_id = $lp_view_id";
  928. Database::query($sql);
  929. $sql = "DELETE FROM $lpInteraction
  930. WHERE c_id = $course_id AND lp_iv_id = $lp_view_id";
  931. Database::query($sql);
  932. $sql = "DELETE FROM $lpObjective
  933. WHERE c_id = $course_id AND lp_iv_id = $lp_view_id";
  934. Database::query($sql);
  935. }
  936. if (api_get_configuration_value('lp_minimum_time')) {
  937. $sql = "DELETE FROM track_e_access_complete
  938. WHERE
  939. tool = 'learnpath' AND
  940. c_id = $course_id AND
  941. tool_id = $lp_id AND
  942. user_id = $user_id AND
  943. session_id = $session_id
  944. ";
  945. Database::query($sql);
  946. }
  947. $sql = "DELETE FROM $lp_view_table
  948. WHERE
  949. c_id = $course_id AND
  950. user_id = $user_id AND
  951. lp_id= $lp_id AND
  952. session_id = $session_id
  953. ";
  954. Database::query($sql);
  955. $sql = "SELECT exe_id FROM $track_e_exercises
  956. WHERE
  957. exe_user_id = $user_id AND
  958. session_id = $session_id AND
  959. c_id = $course_id AND
  960. orig_lp_id = $lp_id";
  961. $result = Database::query($sql);
  962. $exe_list = [];
  963. while ($row = Database::fetch_array($result, 'ASSOC')) {
  964. $exe_list[] = $row['exe_id'];
  965. }
  966. if (!empty($exe_list) && is_array($exe_list) && count($exe_list) > 0) {
  967. $exeListString = implode(',', $exe_list);
  968. $sql = "DELETE FROM $track_e_exercises
  969. WHERE exe_id IN ($exeListString)";
  970. Database::query($sql);
  971. $sql = "DELETE FROM $track_attempts
  972. WHERE exe_id IN ($exeListString)";
  973. Database::query($sql);
  974. $sql = "DELETE FROM $recording_table
  975. WHERE exe_id IN ($exeListString)";
  976. Database::query($sql);
  977. }
  978. self::addEvent(
  979. LOG_LP_ATTEMPT_DELETE,
  980. LOG_LP_ID,
  981. $lp_id,
  982. null,
  983. null,
  984. $course_id,
  985. $session_id
  986. );
  987. return true;
  988. }
  989. /**
  990. * Delete all exercise attempts (included in LP or not).
  991. *
  992. * @param int user id
  993. * @param int exercise id
  994. * @param int $course_id
  995. * @param int session id
  996. */
  997. public static function delete_all_incomplete_attempts(
  998. $user_id,
  999. $exercise_id,
  1000. $course_id,
  1001. $session_id = 0
  1002. ) {
  1003. $user_id = (int) $user_id;
  1004. $exercise_id = (int) $exercise_id;
  1005. $course_id = (int) $course_id;
  1006. $session_id = (int) $session_id;
  1007. if (!empty($user_id) && !empty($exercise_id) && !empty($course_id)) {
  1008. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1009. $sql = "DELETE FROM $table
  1010. WHERE
  1011. exe_user_id = $user_id AND
  1012. exe_exo_id = $exercise_id AND
  1013. c_id = $course_id AND
  1014. session_id = $session_id AND
  1015. status = 'incomplete' ";
  1016. Database::query($sql);
  1017. self::addEvent(
  1018. LOG_EXERCISE_RESULT_DELETE,
  1019. LOG_EXERCISE_AND_USER_ID,
  1020. $exercise_id.'-'.$user_id,
  1021. null,
  1022. null,
  1023. $course_id,
  1024. $session_id
  1025. );
  1026. }
  1027. }
  1028. /**
  1029. * Gets all exercise results (NO Exercises in LPs ) from a given exercise id, course, session.
  1030. *
  1031. * @param int $exercise_id
  1032. * @param int $courseId
  1033. * @param int $session_id
  1034. *
  1035. * @return array with the results
  1036. */
  1037. public static function get_all_exercise_results(
  1038. $exercise_id,
  1039. $courseId,
  1040. $session_id = 0,
  1041. $load_question_list = true,
  1042. $user_id = null
  1043. ) {
  1044. $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1045. $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1046. $courseId = (int) $courseId;
  1047. $exercise_id = (int) $exercise_id;
  1048. $session_id = (int) $session_id;
  1049. $user_condition = null;
  1050. if (!empty($user_id)) {
  1051. $user_id = (int) $user_id;
  1052. $user_condition = "AND exe_user_id = $user_id ";
  1053. }
  1054. $sql = "SELECT * FROM $TABLETRACK_EXERCICES
  1055. WHERE
  1056. status = '' AND
  1057. c_id = $courseId AND
  1058. exe_exo_id = $exercise_id AND
  1059. session_id = $session_id AND
  1060. orig_lp_id =0 AND
  1061. orig_lp_item_id = 0
  1062. $user_condition
  1063. ORDER BY exe_id";
  1064. $res = Database::query($sql);
  1065. $list = [];
  1066. while ($row = Database::fetch_array($res, 'ASSOC')) {
  1067. $list[$row['exe_id']] = $row;
  1068. if ($load_question_list) {
  1069. $sql = "SELECT * FROM $TBL_TRACK_ATTEMPT
  1070. WHERE exe_id = {$row['exe_id']}";
  1071. $res_question = Database::query($sql);
  1072. while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
  1073. $list[$row['exe_id']]['question_list'][$row_q['question_id']] = $row_q;
  1074. }
  1075. }
  1076. }
  1077. return $list;
  1078. }
  1079. /**
  1080. * Gets all exercise results (NO Exercises in LPs ) from a given exercise id, course, session.
  1081. *
  1082. * @param int $courseId
  1083. * @param int $session_id
  1084. * @param bool $get_count
  1085. *
  1086. * @return array with the results
  1087. */
  1088. public static function get_all_exercise_results_by_course(
  1089. $courseId,
  1090. $session_id = 0,
  1091. $get_count = true
  1092. ) {
  1093. $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1094. $courseId = (int) $courseId;
  1095. $session_id = (int) $session_id;
  1096. $select = '*';
  1097. if ($get_count) {
  1098. $select = 'count(*) as count';
  1099. }
  1100. $sql = "SELECT $select FROM $table_track_exercises
  1101. WHERE status = '' AND
  1102. c_id = $courseId AND
  1103. session_id = $session_id AND
  1104. orig_lp_id = 0 AND
  1105. orig_lp_item_id = 0
  1106. ORDER BY exe_id";
  1107. $res = Database::query($sql);
  1108. if ($get_count) {
  1109. $row = Database::fetch_array($res, 'ASSOC');
  1110. return $row['count'];
  1111. } else {
  1112. $list = [];
  1113. while ($row = Database::fetch_array($res, 'ASSOC')) {
  1114. $list[$row['exe_id']] = $row;
  1115. }
  1116. return $list;
  1117. }
  1118. }
  1119. /**
  1120. * Gets all exercise results (NO Exercises in LPs) from a given exercise id, course, session.
  1121. *
  1122. * @param int $user_id
  1123. * @param int $courseId
  1124. * @param int $session_id
  1125. *
  1126. * @return array with the results
  1127. */
  1128. public static function get_all_exercise_results_by_user(
  1129. $user_id,
  1130. $courseId,
  1131. $session_id = 0
  1132. ) {
  1133. $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1134. $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1135. $courseId = (int) $courseId;
  1136. $session_id = (int) $session_id;
  1137. $user_id = (int) $user_id;
  1138. $sql = "SELECT * FROM $table_track_exercises
  1139. WHERE
  1140. status = '' AND
  1141. exe_user_id = $user_id AND
  1142. c_id = $courseId AND
  1143. session_id = $session_id AND
  1144. orig_lp_id = 0 AND
  1145. orig_lp_item_id = 0
  1146. ORDER by exe_id";
  1147. $res = Database::query($sql);
  1148. $list = [];
  1149. while ($row = Database::fetch_array($res, 'ASSOC')) {
  1150. $list[$row['exe_id']] = $row;
  1151. $sql = "SELECT * FROM $table_track_attempt
  1152. WHERE exe_id = {$row['exe_id']}";
  1153. $res_question = Database::query($sql);
  1154. while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
  1155. $list[$row['exe_id']]['question_list'][$row_q['question_id']] = $row_q;
  1156. }
  1157. }
  1158. return $list;
  1159. }
  1160. /**
  1161. * Gets exercise results (NO Exercises in LPs) from a given exercise id, course, session.
  1162. *
  1163. * @param int $exe_id attempt id
  1164. * @param string $status
  1165. *
  1166. * @return array with the results
  1167. */
  1168. public static function get_exercise_results_by_attempt($exe_id, $status = null)
  1169. {
  1170. $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1171. $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1172. $table_track_attempt_recording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
  1173. $exe_id = (int) $exe_id;
  1174. $status = Database::escape_string($status);
  1175. $sql = "SELECT * FROM $table_track_exercises
  1176. WHERE status = '$status' AND exe_id = $exe_id";
  1177. $res = Database::query($sql);
  1178. $list = [];
  1179. if (Database::num_rows($res)) {
  1180. $row = Database::fetch_array($res, 'ASSOC');
  1181. //Checking if this attempt was revised by a teacher
  1182. $sql_revised = "SELECT exe_id FROM $table_track_attempt_recording
  1183. WHERE author != '' AND exe_id = $exe_id
  1184. LIMIT 1";
  1185. $res_revised = Database::query($sql_revised);
  1186. $row['attempt_revised'] = 0;
  1187. if (Database::num_rows($res_revised) > 0) {
  1188. $row['attempt_revised'] = 1;
  1189. }
  1190. $list[$exe_id] = $row;
  1191. $sql = "SELECT * FROM $table_track_attempt
  1192. WHERE exe_id = $exe_id
  1193. ORDER BY tms ASC";
  1194. $res_question = Database::query($sql);
  1195. while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
  1196. $list[$exe_id]['question_list'][$row_q['question_id']] = $row_q;
  1197. }
  1198. }
  1199. return $list;
  1200. }
  1201. /**
  1202. * Gets exercise results (NO Exercises in LPs) from a given user, exercise id, course, session, lp_id, lp_item_id.
  1203. *
  1204. * @param int user id
  1205. * @param int exercise id
  1206. * @param string course code
  1207. * @param int session id
  1208. * @param int lp id
  1209. * @param int lp item id
  1210. * @param string order asc or desc
  1211. *
  1212. * @return array with the results
  1213. */
  1214. public static function getExerciseResultsByUser(
  1215. $user_id,
  1216. $exercise_id,
  1217. $courseId,
  1218. $session_id = 0,
  1219. $lp_id = 0,
  1220. $lp_item_id = 0,
  1221. $order = null
  1222. ) {
  1223. $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1224. $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1225. $table_track_attempt_recording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
  1226. $courseId = (int) $courseId;
  1227. $exercise_id = (int) $exercise_id;
  1228. $session_id = (int) $session_id;
  1229. $user_id = (int) $user_id;
  1230. $lp_id = (int) $lp_id;
  1231. $lp_item_id = (int) $lp_item_id;
  1232. if (!in_array(strtolower($order), ['asc', 'desc'])) {
  1233. $order = 'asc';
  1234. }
  1235. $sql = "SELECT * FROM $table_track_exercises
  1236. WHERE
  1237. status = '' AND
  1238. exe_user_id = $user_id AND
  1239. c_id = $courseId AND
  1240. exe_exo_id = $exercise_id AND
  1241. session_id = $session_id AND
  1242. orig_lp_id = $lp_id AND
  1243. orig_lp_item_id = $lp_item_id
  1244. ORDER by exe_id $order ";
  1245. $res = Database::query($sql);
  1246. $list = [];
  1247. while ($row = Database::fetch_array($res, 'ASSOC')) {
  1248. // Checking if this attempt was revised by a teacher
  1249. $exeId = $row['exe_id'];
  1250. $sql = "SELECT exe_id FROM $table_track_attempt_recording
  1251. WHERE author != '' AND exe_id = $exeId
  1252. LIMIT 1";
  1253. $res_revised = Database::query($sql);
  1254. $row['attempt_revised'] = 0;
  1255. if (Database::num_rows($res_revised) > 0) {
  1256. $row['attempt_revised'] = 1;
  1257. }
  1258. $list[$row['exe_id']] = $row;
  1259. $sql = "SELECT * FROM $table_track_attempt
  1260. WHERE exe_id = $exeId";
  1261. $res_question = Database::query($sql);
  1262. while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
  1263. $list[$row['exe_id']]['question_list'][$row_q['question_id']][] = $row_q;
  1264. }
  1265. }
  1266. return $list;
  1267. }
  1268. /**
  1269. * Count exercise attempts (NO Exercises in LPs ) from a given exercise id, course, session.
  1270. *
  1271. * @param int $user_id
  1272. * @param int $exercise_id
  1273. * @param int $courseId
  1274. * @param int $session_id
  1275. *
  1276. * @return array with the results
  1277. */
  1278. public static function count_exercise_attempts_by_user(
  1279. $user_id,
  1280. $exercise_id,
  1281. $courseId,
  1282. $session_id = 0
  1283. ) {
  1284. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1285. $courseId = (int) $courseId;
  1286. $exercise_id = (int) $exercise_id;
  1287. $session_id = (int) $session_id;
  1288. $user_id = (int) $user_id;
  1289. $sql = "SELECT count(*) as count
  1290. FROM $table
  1291. WHERE status = '' AND
  1292. exe_user_id = $user_id AND
  1293. c_id = $courseId AND
  1294. exe_exo_id = $exercise_id AND
  1295. session_id = $session_id AND
  1296. orig_lp_id =0 AND
  1297. orig_lp_item_id = 0
  1298. ORDER BY exe_id";
  1299. $res = Database::query($sql);
  1300. $result = 0;
  1301. if (Database::num_rows($res) > 0) {
  1302. $row = Database::fetch_array($res, 'ASSOC');
  1303. $result = $row['count'];
  1304. }
  1305. return $result;
  1306. }
  1307. /**
  1308. * Gets all exercise BEST results attempts (NO Exercises in LPs)
  1309. * from a given exercise id, course, session per user.
  1310. *
  1311. * @param int $exercise_id
  1312. * @param int $courseId
  1313. * @param int $session_id
  1314. * @param int $userId
  1315. *
  1316. * @return array with the results
  1317. *
  1318. * @todo rename this function
  1319. */
  1320. public static function get_best_exercise_results_by_user(
  1321. $exercise_id,
  1322. $courseId,
  1323. $session_id = 0,
  1324. $userId = 0
  1325. ) {
  1326. $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1327. $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1328. $courseId = (int) $courseId;
  1329. $exercise_id = (int) $exercise_id;
  1330. $session_id = (int) $session_id;
  1331. $sql = "SELECT * FROM $table_track_exercises
  1332. WHERE
  1333. status = '' AND
  1334. c_id = $courseId AND
  1335. exe_exo_id = $exercise_id AND
  1336. session_id = $session_id AND
  1337. orig_lp_id = 0 AND
  1338. orig_lp_item_id = 0";
  1339. if (!empty($userId)) {
  1340. $userId = (int) $userId;
  1341. $sql .= " AND exe_user_id = $userId ";
  1342. }
  1343. $sql .= ' ORDER BY exe_id';
  1344. $res = Database::query($sql);
  1345. $list = [];
  1346. while ($row = Database::fetch_array($res, 'ASSOC')) {
  1347. $list[$row['exe_id']] = $row;
  1348. $exeId = $row['exe_id'];
  1349. $sql = "SELECT * FROM $table_track_attempt
  1350. WHERE exe_id = $exeId";
  1351. $res_question = Database::query($sql);
  1352. while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
  1353. $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
  1354. }
  1355. }
  1356. // Getting the best results of every student
  1357. $best_score_return = [];
  1358. foreach ($list as $student_result) {
  1359. $user_id = $student_result['exe_user_id'];
  1360. $current_best_score[$user_id] = $student_result['score'];
  1361. if (!isset($best_score_return[$user_id]['score'])) {
  1362. $best_score_return[$user_id] = $student_result;
  1363. }
  1364. if ($current_best_score[$user_id] > $best_score_return[$user_id]['score']) {
  1365. $best_score_return[$user_id] = $student_result;
  1366. }
  1367. }
  1368. return $best_score_return;
  1369. }
  1370. /**
  1371. * Get the last best result from all attempts in exercises per user (out of learning paths).
  1372. *
  1373. * @param int $user_id
  1374. * @param int $exercise_id
  1375. * @param int $courseId
  1376. * @param int $session_id
  1377. *
  1378. * @return array
  1379. */
  1380. public static function get_best_attempt_exercise_results_per_user(
  1381. $user_id,
  1382. $exercise_id,
  1383. $courseId,
  1384. $session_id = 0
  1385. ) {
  1386. $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1387. $courseId = (int) $courseId;
  1388. $exercise_id = (int) $exercise_id;
  1389. $session_id = (int) $session_id;
  1390. $user_id = (int) $user_id;
  1391. $sql = "SELECT * FROM $table_track_exercises
  1392. WHERE
  1393. status = '' AND
  1394. c_id = $courseId AND
  1395. exe_exo_id = $exercise_id AND
  1396. session_id = $session_id AND
  1397. exe_user_id = $user_id AND
  1398. orig_lp_id = 0 AND
  1399. orig_lp_item_id = 0
  1400. ORDER BY exe_id";
  1401. $res = Database::query($sql);
  1402. $list = [];
  1403. while ($row = Database::fetch_array($res, 'ASSOC')) {
  1404. $list[$row['exe_id']] = $row;
  1405. }
  1406. //Getting the best results of every student
  1407. $best_score_return = [];
  1408. $best_score_return['score'] = 0;
  1409. foreach ($list as $result) {
  1410. $current_best_score = $result;
  1411. if ($current_best_score['score'] > $best_score_return['score']) {
  1412. $best_score_return = $result;
  1413. }
  1414. }
  1415. if (!isset($best_score_return['max_score'])) {
  1416. $best_score_return = [];
  1417. }
  1418. return $best_score_return;
  1419. }
  1420. /**
  1421. * @param int $exercise_id
  1422. * @param int $courseId
  1423. * @param int $session_id
  1424. *
  1425. * @return mixed
  1426. */
  1427. public static function count_exercise_result_not_validated(
  1428. $exercise_id,
  1429. $courseId,
  1430. $session_id = 0
  1431. ) {
  1432. $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1433. $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
  1434. $courseId = (int) $courseId;
  1435. $session_id = (int) $session_id;
  1436. $exercise_id = (int) $exercise_id;
  1437. $sql = "SELECT count(e.exe_id) as count
  1438. FROM $table_track_exercises e
  1439. LEFT JOIN $table_track_attempt a
  1440. ON e.exe_id = a.exe_id
  1441. WHERE
  1442. exe_exo_id = $exercise_id AND
  1443. c_id = $courseId AND
  1444. e.session_id = $session_id AND
  1445. orig_lp_id = 0 AND
  1446. marks IS NULL AND
  1447. status = '' AND
  1448. orig_lp_item_id = 0
  1449. ORDER BY e.exe_id";
  1450. $res = Database::query($sql);
  1451. $row = Database::fetch_array($res, 'ASSOC');
  1452. return $row['count'];
  1453. }
  1454. /**
  1455. * Gets all exercise events from a Learning Path within a Course nd Session.
  1456. *
  1457. * @param int $exercise_id
  1458. * @param int $courseId
  1459. * @param int $session_id
  1460. *
  1461. * @return array
  1462. */
  1463. public static function get_all_exercise_event_from_lp(
  1464. $exercise_id,
  1465. $courseId,
  1466. $session_id = 0
  1467. ) {
  1468. $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1469. $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1470. $courseId = (int) $courseId;
  1471. $exercise_id = (int) $exercise_id;
  1472. $session_id = (int) $session_id;
  1473. $sql = "SELECT * FROM $table_track_exercises
  1474. WHERE
  1475. status = '' AND
  1476. c_id = $courseId AND
  1477. exe_exo_id = $exercise_id AND
  1478. session_id = $session_id AND
  1479. orig_lp_id !=0 AND
  1480. orig_lp_item_id != 0";
  1481. $res = Database::query($sql);
  1482. $list = [];
  1483. while ($row = Database::fetch_array($res, 'ASSOC')) {
  1484. $exeId = $row['exe_id'];
  1485. $list[$exeId] = $row;
  1486. $sql = "SELECT * FROM $table_track_attempt
  1487. WHERE exe_id = $exeId";
  1488. $res_question = Database::query($sql);
  1489. while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
  1490. $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
  1491. }
  1492. }
  1493. return $list;
  1494. }
  1495. /**
  1496. * Get a list of all the exercises in a given learning path.
  1497. *
  1498. * @param int $lp_id
  1499. * @param int $course_id This parameter is probably deprecated as lp_id now is a global iid
  1500. *
  1501. * @return array
  1502. */
  1503. public static function get_all_exercises_from_lp($lp_id, $course_id)
  1504. {
  1505. $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
  1506. $course_id = (int) $course_id;
  1507. $lp_id = (int) $lp_id;
  1508. $sql = "SELECT * FROM $lp_item_table
  1509. WHERE
  1510. c_id = $course_id AND
  1511. lp_id = $lp_id AND
  1512. item_type = 'quiz'
  1513. ORDER BY parent_item_id, display_order";
  1514. $res = Database::query($sql);
  1515. $list = [];
  1516. while ($row = Database::fetch_array($res, 'ASSOC')) {
  1517. $list[] = $row;
  1518. }
  1519. return $list;
  1520. }
  1521. /**
  1522. * This function gets the comments of an exercise.
  1523. *
  1524. * @param int $exe_id
  1525. * @param int $question_id
  1526. *
  1527. * @return string the comment
  1528. */
  1529. public static function get_comments($exe_id, $question_id)
  1530. {
  1531. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1532. $exe_id = (int) $exe_id;
  1533. $question_id = (int) $question_id;
  1534. $sql = "SELECT teacher_comment
  1535. FROM $table
  1536. WHERE
  1537. exe_id = $exe_id AND
  1538. question_id = $question_id
  1539. ORDER by question_id";
  1540. $sqlres = Database::query($sql);
  1541. $comm = strval(Database::result($sqlres, 0, 'teacher_comment'));
  1542. $comm = trim($comm);
  1543. return $comm;
  1544. }
  1545. /**
  1546. * @param int $exeId
  1547. *
  1548. * @return array
  1549. */
  1550. public static function getAllExerciseEventByExeId($exeId)
  1551. {
  1552. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1553. $exeId = (int) $exeId;
  1554. $sql = "SELECT * FROM $table
  1555. WHERE exe_id = $exeId
  1556. ORDER BY position";
  1557. $res_question = Database::query($sql);
  1558. $list = [];
  1559. if (Database::num_rows($res_question)) {
  1560. while ($row = Database::fetch_array($res_question, 'ASSOC')) {
  1561. $list[$row['question_id']][] = $row;
  1562. }
  1563. }
  1564. return $list;
  1565. }
  1566. /**
  1567. * @param int $exeId
  1568. * @param int $user_id
  1569. * @param int $courseId
  1570. * @param int $session_id
  1571. * @param int $question_id
  1572. */
  1573. public static function delete_attempt(
  1574. $exeId,
  1575. $user_id,
  1576. $courseId,
  1577. $session_id,
  1578. $question_id
  1579. ) {
  1580. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1581. $exeId = (int) $exeId;
  1582. $user_id = (int) $user_id;
  1583. $courseId = (int) $courseId;
  1584. $session_id = (int) $session_id;
  1585. $question_id = (int) $question_id;
  1586. $sql = "DELETE FROM $table
  1587. WHERE
  1588. exe_id = $exeId AND
  1589. user_id = $user_id AND
  1590. c_id = $courseId AND
  1591. session_id = $session_id AND
  1592. question_id = $question_id ";
  1593. Database::query($sql);
  1594. self::addEvent(
  1595. LOG_QUESTION_RESULT_DELETE,
  1596. LOG_EXERCISE_ATTEMPT_QUESTION_ID,
  1597. $exeId.'-'.$question_id,
  1598. null,
  1599. null,
  1600. $courseId,
  1601. $session_id
  1602. );
  1603. }
  1604. /**
  1605. * @param $exeId
  1606. * @param $user_id
  1607. * @param int $courseId
  1608. * @param $question_id
  1609. * @param int $sessionId
  1610. */
  1611. public static function delete_attempt_hotspot(
  1612. $exeId,
  1613. $user_id,
  1614. $courseId,
  1615. $question_id,
  1616. $sessionId = null
  1617. ) {
  1618. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
  1619. $exeId = (int) $exeId;
  1620. $user_id = (int) $user_id;
  1621. $courseId = (int) $courseId;
  1622. $question_id = (int) $question_id;
  1623. if (!isset($sessionId)) {
  1624. $sessionId = api_get_session_id();
  1625. }
  1626. $sql = "DELETE FROM $table
  1627. WHERE
  1628. hotspot_exe_id = $exeId AND
  1629. hotspot_user_id = $user_id AND
  1630. c_id = $courseId AND
  1631. hotspot_question_id = $question_id ";
  1632. Database::query($sql);
  1633. self::addEvent(
  1634. LOG_QUESTION_RESULT_DELETE,
  1635. LOG_EXERCISE_ATTEMPT_QUESTION_ID,
  1636. $exeId.'-'.$question_id,
  1637. null,
  1638. null,
  1639. $courseId,
  1640. $sessionId
  1641. );
  1642. }
  1643. /**
  1644. * Registers in track_e_course_access when user logs in for the first time to a course.
  1645. *
  1646. * @param int $courseId ID of the course
  1647. * @param int $user_id ID of the user
  1648. * @param int $sessionId ID of the session (if any)
  1649. *
  1650. * @return bool
  1651. */
  1652. public static function eventCourseLogin($courseId, $user_id, $sessionId)
  1653. {
  1654. if (Session::read('login_as')) {
  1655. return false;
  1656. }
  1657. $sessionId = (int) $sessionId;
  1658. if (self::isSessionLogNeedToBeSave($sessionId) === false) {
  1659. return false;
  1660. }
  1661. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
  1662. $loginDate = $logoutDate = api_get_utc_datetime();
  1663. // $counter represents the number of time this record has been refreshed
  1664. $counter = 1;
  1665. $courseId = (int) $courseId;
  1666. $user_id = (int) $user_id;
  1667. $ip = Database::escape_string(api_get_real_ip());
  1668. $sql = "INSERT INTO $table(c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
  1669. VALUES($courseId, '$ip', $user_id, '$loginDate', '$logoutDate', $counter, $sessionId)";
  1670. $courseAccessId = Database::query($sql);
  1671. if ($courseAccessId) {
  1672. // Course catalog stats modifications see #4191
  1673. CourseManager::update_course_ranking(
  1674. null,
  1675. null,
  1676. null,
  1677. null,
  1678. true,
  1679. false
  1680. );
  1681. return true;
  1682. }
  1683. }
  1684. /**
  1685. * Updates the user - course - session every X minutes
  1686. * In order to avoid.
  1687. *
  1688. * @param int $courseId
  1689. * @param int $userId
  1690. * @param int $sessionId
  1691. * @param int $minutes
  1692. *
  1693. * @return bool
  1694. */
  1695. public static function eventCourseLoginUpdate(
  1696. $courseId,
  1697. $userId,
  1698. $sessionId,
  1699. $minutes = 5
  1700. ) {
  1701. if (Session::read('login_as')) {
  1702. return false;
  1703. }
  1704. if (empty($courseId) || empty($userId)) {
  1705. return false;
  1706. }
  1707. $sessionId = (int) $sessionId;
  1708. if (self::isSessionLogNeedToBeSave($sessionId) === false) {
  1709. return false;
  1710. }
  1711. $courseId = (int) $courseId;
  1712. $userId = (int) $userId;
  1713. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
  1714. $sql = "SELECT course_access_id, logout_course_date
  1715. FROM $table
  1716. WHERE
  1717. c_id = $courseId AND
  1718. session_id = $sessionId AND
  1719. user_id = $userId
  1720. ORDER BY login_course_date DESC
  1721. LIMIT 1";
  1722. $result = Database::query($sql);
  1723. // Save every 5 minutes by default
  1724. $seconds = $minutes * 60;
  1725. $maxSeconds = 3600; // Only update if max diff is one hour
  1726. if (Database::num_rows($result)) {
  1727. $row = Database::fetch_array($result);
  1728. $id = $row['course_access_id'];
  1729. $logout = $row['logout_course_date'];
  1730. $now = time();
  1731. $logout = api_strtotime($logout, 'UTC');
  1732. if ($now - $logout > $seconds &&
  1733. $now - $logout < $maxSeconds
  1734. ) {
  1735. $now = api_get_utc_datetime();
  1736. $sql = "UPDATE $table SET
  1737. logout_course_date = '$now',
  1738. counter = counter + 1
  1739. WHERE course_access_id = $id";
  1740. Database::query($sql);
  1741. }
  1742. return true;
  1743. }
  1744. return false;
  1745. }
  1746. /**
  1747. * Register the logout of the course (usually when logging out of the platform)
  1748. * from the track_e_course_access table.
  1749. *
  1750. * @param array $logoutInfo Information stored by local.inc.php
  1751. * before new context ['uid'=> x, 'cid'=>y, 'sid'=>z]
  1752. *
  1753. * @return bool
  1754. */
  1755. public static function courseLogout($logoutInfo)
  1756. {
  1757. if (Session::read('login_as')) {
  1758. return false;
  1759. }
  1760. if (empty($logoutInfo['uid']) || empty($logoutInfo['cid'])) {
  1761. return false;
  1762. }
  1763. $sessionLifetime = api_get_configuration_value('session_lifetime');
  1764. /*
  1765. * When $_configuration['session_lifetime'] is larger than ~100 hours
  1766. * (in order to let users take exercises with no problems)
  1767. * the function Tracking::get_time_spent_on_the_course() returns larger values (200h) due the condition:
  1768. * login_course_date > now() - INTERVAL $session_lifetime SECOND
  1769. */
  1770. if (empty($sessionLifetime) || $sessionLifetime > 86400) {
  1771. $sessionLifetime = 3600; // 1 hour
  1772. }
  1773. if (!empty($logoutInfo) && !empty($logoutInfo['cid'])) {
  1774. $sessionId = 0;
  1775. if (!empty($logoutInfo['sid'])) {
  1776. $sessionId = (int) $logoutInfo['sid'];
  1777. }
  1778. if (self::isSessionLogNeedToBeSave($sessionId) === false) {
  1779. return false;
  1780. }
  1781. $tableCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
  1782. $userId = (int) $logoutInfo['uid'];
  1783. $courseId = (int) $logoutInfo['cid'];
  1784. $currentDate = api_get_utc_datetime();
  1785. // UTC time
  1786. $diff = time() - $sessionLifetime;
  1787. $time = api_get_utc_datetime($diff);
  1788. $sql = "SELECT course_access_id, logout_course_date
  1789. FROM $tableCourseAccess
  1790. WHERE
  1791. user_id = $userId AND
  1792. c_id = $courseId AND
  1793. session_id = $sessionId AND
  1794. login_course_date > '$time'
  1795. ORDER BY login_course_date DESC
  1796. LIMIT 1";
  1797. $result = Database::query($sql);
  1798. $insert = false;
  1799. if (Database::num_rows($result) > 0) {
  1800. $row = Database::fetch_array($result, 'ASSOC');
  1801. $courseAccessId = $row['course_access_id'];
  1802. $sql = "UPDATE $tableCourseAccess SET
  1803. logout_course_date = '$currentDate',
  1804. counter = counter + 1
  1805. WHERE course_access_id = $courseAccessId";
  1806. Database::query($sql);
  1807. } else {
  1808. $insert = true;
  1809. }
  1810. if ($insert) {
  1811. $ip = Database::escape_string(api_get_real_ip());
  1812. $sql = "INSERT INTO $tableCourseAccess (c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
  1813. VALUES ($courseId, '$ip', $userId, '$currentDate', '$currentDate', 1, $sessionId)";
  1814. Database::query($sql);
  1815. }
  1816. return true;
  1817. }
  1818. }
  1819. /**
  1820. * Register a "fake" time spent on the platform, for example to match the
  1821. * estimated time he took to author an assignment/work, see configuration
  1822. * setting considered_working_time.
  1823. * This assumes there is already some connection of the student to the
  1824. * course, otherwise he wouldn't be able to upload an assignment.
  1825. * This works by creating a new record, copy of the current one, then
  1826. * updating the current one to be just the considered_working_time and
  1827. * end at the same second as the user connected to the course.
  1828. *
  1829. * @param int $courseId The course in which to add the time
  1830. * @param int $userId The user for whom to add the time
  1831. * @param int $sessionId The session in which to add the time (if any)
  1832. * @param string $virtualTime The amount of time to be added,
  1833. * in a hh:mm:ss format. If int, we consider it is expressed in hours.
  1834. *
  1835. * @return true on successful insertion, false otherwise
  1836. */
  1837. public static function eventAddVirtualCourseTime(
  1838. $courseId,
  1839. $userId,
  1840. $sessionId,
  1841. $virtualTime = ''
  1842. ) {
  1843. $courseId = (int) $courseId;
  1844. $userId = (int) $userId;
  1845. $sessionId = (int) $sessionId;
  1846. $logoutDate = api_get_utc_datetime();
  1847. $loginDate = ChamiloApi::addOrSubTimeToDateTime(
  1848. $virtualTime,
  1849. $logoutDate,
  1850. false
  1851. );
  1852. $params = [
  1853. 'login_course_date' => $loginDate,
  1854. 'logout_course_date' => $logoutDate,
  1855. 'session_id' => $sessionId,
  1856. 'user_id' => $userId,
  1857. 'counter' => 0,
  1858. 'c_id' => $courseId,
  1859. 'user_ip' => api_get_real_ip(),
  1860. ];
  1861. $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
  1862. Database::insert($courseTrackingTable, $params);
  1863. return true;
  1864. }
  1865. /**
  1866. * Removes a "fake" time spent on the platform, for example to match the
  1867. * estimated time he took to author an assignment/work, see configuration
  1868. * setting considered_working_time.
  1869. * This method should be called when something that generated a fake
  1870. * time record is removed. Given the database link is weak (no real
  1871. * relationship kept between the deleted item and this record), this
  1872. * method just looks for the latest record that has the same time as the
  1873. * item's fake time, is in the past and in this course+session. If such a
  1874. * record cannot be found, it doesn't do anything.
  1875. * The IP address is not considered a useful filter here.
  1876. *
  1877. * @param int $courseId The course in which to add the time
  1878. * @param int $userId The user for whom to add the time
  1879. * @param int $sessionId The session in which to add the time (if any)
  1880. * @param string $virtualTime The amount of time to be added, in a hh:mm:ss format. If int, we consider it is expressed in hours.
  1881. *
  1882. * @return true on successful removal, false otherwise
  1883. */
  1884. public static function eventRemoveVirtualCourseTime(
  1885. $courseId,
  1886. $userId,
  1887. $sessionId = 0,
  1888. $virtualTime = ''
  1889. ) {
  1890. if (empty($virtualTime)) {
  1891. return false;
  1892. }
  1893. $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
  1894. $courseId = (int) $courseId;
  1895. $userId = (int) $userId;
  1896. $sessionId = (int) $sessionId;
  1897. // Change $virtualTime format from hh:mm:ss to hhmmss which is the
  1898. // format returned by SQL for a subtraction of two datetime values
  1899. // @todo make sure this is portable between DBMSes
  1900. if (preg_match('/:/', $virtualTime)) {
  1901. list($h, $m, $s) = preg_split('/:/', $virtualTime);
  1902. $virtualTime = $h * 3600 + $m * 60 + $s;
  1903. } else {
  1904. $virtualTime *= 3600;
  1905. }
  1906. // Get the current latest course connection register. We need that
  1907. // record to re-use the data and create a new record.
  1908. $sql = "SELECT course_access_id
  1909. FROM $courseTrackingTable
  1910. WHERE
  1911. user_id = $userId AND
  1912. c_id = $courseId AND
  1913. session_id = $sessionId AND
  1914. counter = 0 AND
  1915. (UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) = '$virtualTime'
  1916. ORDER BY login_course_date DESC LIMIT 0,1";
  1917. $result = Database::query($sql);
  1918. // Ignore if we didn't find any course connection record in the last
  1919. // hour. In this case it wouldn't be right to add a "fake" time record.
  1920. if (Database::num_rows($result) > 0) {
  1921. // Found the latest connection
  1922. $row = Database::fetch_row($result);
  1923. $courseAccessId = $row[0];
  1924. $sql = "DELETE FROM $courseTrackingTable
  1925. WHERE course_access_id = $courseAccessId";
  1926. $result = Database::query($sql);
  1927. return $result;
  1928. }
  1929. return false;
  1930. }
  1931. /**
  1932. * Register the logout of the course (usually when logging out of the platform)
  1933. * from the track_e_access_complete table.
  1934. *
  1935. * @param array $logInfo Information stored by local.inc.php
  1936. *
  1937. * @return bool
  1938. */
  1939. public static function registerLog($logInfo)
  1940. {
  1941. $sessionId = api_get_session_id();
  1942. $courseId = api_get_course_int_id();
  1943. if (!Tracking::minimumTimeAvailable($sessionId, $courseId)) {
  1944. return false;
  1945. }
  1946. if (self::isSessionLogNeedToBeSave($sessionId) === false) {
  1947. return false;
  1948. }
  1949. $loginAs = (int) Session::read('login_as') === true;
  1950. $logInfo['user_id'] = api_get_user_id();
  1951. $logInfo['date_reg'] = api_get_utc_datetime();
  1952. $logInfo['tool'] = !empty($logInfo['tool']) ? $logInfo['tool'] : '';
  1953. $logInfo['tool_id'] = !empty($logInfo['tool_id']) ? (int) $logInfo['tool_id'] : 0;
  1954. $logInfo['tool_id_detail'] = !empty($logInfo['tool_id_detail']) ? (int) $logInfo['tool_id_detail'] : 0;
  1955. $logInfo['action'] = !empty($logInfo['action']) ? $logInfo['action'] : '';
  1956. $logInfo['action_details'] = !empty($logInfo['action_details']) ? $logInfo['action_details'] : '';
  1957. $logInfo['ip_user'] = api_get_real_ip();
  1958. $logInfo['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
  1959. $logInfo['session_id'] = $sessionId;
  1960. $logInfo['c_id'] = $courseId;
  1961. $logInfo['ch_sid'] = session_id();
  1962. $logInfo['login_as'] = $loginAs;
  1963. $logInfo['info'] = !empty($logInfo['info']) ? $logInfo['info'] : '';
  1964. $logInfo['url'] = $_SERVER['REQUEST_URI'];
  1965. $logInfo['current_id'] = Session::read('last_id', 0);
  1966. $id = Database::insert('track_e_access_complete', $logInfo);
  1967. if ($id && empty($logInfo['current_id'])) {
  1968. Session::write('last_id', $id);
  1969. }
  1970. return true;
  1971. }
  1972. }