exerciselink.class.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Class ExerciseLink
  5. * Defines a gradebook ExerciseLink object.
  6. * @author Bert Steppé
  7. * @package chamilo.gradebook
  8. */
  9. class ExerciseLink extends AbstractLink
  10. {
  11. private $course_info = null;
  12. private $exercise_table = null;
  13. private $exercise_data = null;
  14. private $is_hp;
  15. /**
  16. * @param int $hp
  17. */
  18. public function __construct($hp = 0)
  19. {
  20. parent::__construct();
  21. $this->set_type(LINK_EXERCISE);
  22. $this->is_hp = $hp;
  23. if ($this->is_hp == 1) {
  24. $this->set_type(LINK_HOTPOTATOES);
  25. }
  26. }
  27. /**
  28. * Generate an array of exercises that a teacher hasn't created a link for.
  29. * @return array 2-dimensional array - every element contains 2 subelements (id, name)
  30. */
  31. public function get_not_created_links()
  32. {
  33. return false;
  34. if (empty($this->course_code)) {
  35. die('Error in get_not_created_links() : course code not set');
  36. }
  37. $tbl_grade_links = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
  38. $sql = 'SELECT id, title FROM '.$this->get_exercise_table().' exe
  39. WHERE id NOT IN (
  40. SELECT ref_id FROM '.$tbl_grade_links.'
  41. WHERE
  42. type = '.LINK_EXERCISE." AND
  43. course_code = '".$this->get_course_code()."'
  44. ) AND
  45. exe.c_id = ".$this->course_id;
  46. $result = Database::query($sql);
  47. $cats = array();
  48. while ($data = Database::fetch_array($result)) {
  49. $cats[] = array($data['id'], $data['title']);
  50. }
  51. return $cats;
  52. }
  53. /**
  54. * Generate an array of all exercises available.
  55. * @return array 2-dimensional array - every element contains 2 subelements (id, name)
  56. */
  57. public function get_all_links($getOnlyHotPotatoes = false)
  58. {
  59. $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
  60. $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
  61. $exerciseTable = $this->get_exercise_table();
  62. $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
  63. $documentPath = api_get_path(SYS_COURSE_PATH).$this->course_code."/document";
  64. if (empty($this->course_code)) {
  65. die('Error in get_not_created_links() : course code not set');
  66. }
  67. $session_id = api_get_session_id();
  68. if (empty($session_id)) {
  69. $session_condition = api_get_session_condition(0, true);
  70. } else {
  71. $session_condition = api_get_session_condition($session_id, true, true);
  72. }
  73. // @todo
  74. $uploadPath = null;
  75. $sql = 'SELECT id,title FROM '.$exerciseTable.'
  76. WHERE c_id = '.$this->course_id.' AND active=1 '.$session_condition;
  77. $sqlLp = "SELECT e.id, e.title
  78. FROM $exerciseTable e
  79. INNER JOIN $lpItemTable i
  80. ON (e.c_id = i.c_id AND e.id = i.path)
  81. WHERE
  82. e.c_id = $this->course_id AND
  83. active = 0 AND
  84. item_type = 'quiz'
  85. $session_condition";
  86. $sql2 = "SELECT d.path as path, d.comment as comment, ip.visibility as visibility, d.id
  87. FROM $TBL_DOCUMENT d
  88. INNER JOIN $TBL_ITEM_PROPERTY ip
  89. ON (d.id = ip.ref AND d.c_id = ip.c_id)
  90. WHERE
  91. d.c_id = $this->course_id AND
  92. ip.c_id = $this->course_id AND
  93. ip.tool = '".TOOL_DOCUMENT."' AND
  94. (d.path LIKE '%htm%') AND
  95. (d.path LIKE '%HotPotatoes_files%') AND
  96. d.path LIKE '".Database::escape_string($uploadPath.'/%/%')."' AND
  97. ip.visibility = '1'
  98. ";
  99. require_once api_get_path(SYS_CODE_PATH).'exercise/hotpotatoes.lib.php';
  100. $exerciseInLP = array();
  101. if (!$this->is_hp) {
  102. $result = Database::query($sql);
  103. $resultLp = Database::query($sqlLp);
  104. $exerciseInLP = Database::store_result($resultLp);
  105. } else {
  106. $result2 = Database::query($sql2);
  107. }
  108. $cats = array();
  109. if (isset($result)) {
  110. if (Database::num_rows($result) > 0) {
  111. while ($data = Database::fetch_array($result)) {
  112. $cats[] = array($data['id'], $data['title']);
  113. }
  114. }
  115. }
  116. $hotPotatoes = [];
  117. if (isset($result2)) {
  118. if (Database::num_rows($result2) > 0) {
  119. while ($row = Database::fetch_array($result2)) {
  120. $attribute['path'][] = $row['path'];
  121. $attribute['visibility'][] = $row['visibility'];
  122. $attribute['comment'][] = $row['comment'];
  123. $attribute['id'] = $row['id'];
  124. if (isset($attribute['path']) && is_array($attribute['path'])) {
  125. while (list($key, $path) = each($attribute['path'])) {
  126. $title = GetQuizName($path, $documentPath);
  127. if ($title == '') {
  128. $title = basename($path);
  129. }
  130. $element = array($attribute['id'], $title.'(HP)');
  131. $cats[] = $element;
  132. $hotPotatoes[] = $element;
  133. }
  134. }
  135. }
  136. }
  137. }
  138. if ($getOnlyHotPotatoes) {
  139. return $hotPotatoes;
  140. }
  141. if (!empty($exerciseInLP)) {
  142. foreach ($exerciseInLP as $exercise) {
  143. $cats[] = array(
  144. $exercise['id'],
  145. $exercise['title'].' ('.get_lang('ToolLearnpath').')'
  146. );
  147. }
  148. }
  149. return $cats;
  150. }
  151. /**
  152. * Has anyone done this exercise yet ?
  153. */
  154. public function has_results()
  155. {
  156. $tbl_stats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  157. $session_id = api_get_session_id();
  158. $course_id = api_get_course_int_id($this->get_course_code());
  159. $sql = "SELECT count(exe_id) AS number
  160. FROM $tbl_stats
  161. WHERE
  162. session_id = $session_id AND
  163. c_id = $course_id AND
  164. exe_exo_id = ".(int) $this->get_ref_id();
  165. $result = Database::query($sql);
  166. $number = Database::fetch_row($result);
  167. return ($number[0] != 0);
  168. }
  169. /**
  170. * Get the score of this exercise. Only the first attempts are taken into account.
  171. * @param int $stud_id student id (default: all students who have results -
  172. * then the average is returned)
  173. * @return array (score, max) if student is given
  174. * array (sum of scores, number of scores) otherwise
  175. * or null if no scores available
  176. */
  177. public function calc_score($stud_id = null, $type = null)
  178. {
  179. $tblStats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  180. $tblHp = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
  181. $tblDoc = Database::get_course_table(TABLE_DOCUMENT);
  182. /* the following query should be similar (in conditions) to the one used
  183. in exercise/exercise.php, look for note-query-exe-results marker*/
  184. $session_id = $this->get_session_id();
  185. $courseId = $this->getCourseId();
  186. $exercise = new Exercise($courseId);
  187. $exercise->read($this->get_ref_id());
  188. $stud_id = (int) $stud_id;
  189. if (!$this->is_hp) {
  190. if ($exercise->exercise_was_added_in_lp == false) {
  191. $sql = "SELECT * FROM $tblStats
  192. WHERE
  193. exe_exo_id = ".intval($this->get_ref_id())." AND
  194. orig_lp_id = 0 AND
  195. orig_lp_item_id = 0 AND
  196. status <> 'incomplete' AND
  197. session_id = $session_id AND
  198. c_id = $courseId
  199. ";
  200. } else {
  201. $lpId = null;
  202. if (!empty($exercise->lpList)) {
  203. // Taking only the first LP
  204. $lpId = current($exercise->lpList);
  205. $lpId = $lpId['lp_id'];
  206. }
  207. $sql = "SELECT *
  208. FROM $tblStats
  209. WHERE
  210. exe_exo_id = ".intval($this->get_ref_id())." AND
  211. orig_lp_id = $lpId AND
  212. status <> 'incomplete' AND
  213. session_id = $session_id AND
  214. c_id = $courseId ";
  215. }
  216. if (!empty($stud_id) && $type != 'ranking') {
  217. $sql .= " AND exe_user_id = $stud_id ";
  218. }
  219. $sql .= ' ORDER BY exe_id DESC';
  220. } else {
  221. $sql = "SELECT * FROM $tblHp hp
  222. INNER JOIN $tblDoc doc
  223. ON (hp.exe_name = doc.path AND doc.c_id = hp.c_id)
  224. WHERE
  225. hp.c_id = $courseId AND
  226. doc.id = ".intval($this->get_ref_id());
  227. if (!empty($stud_id)) {
  228. $sql .= " AND hp.exe_user_id = $stud_id ";
  229. }
  230. }
  231. $scores = Database::query($sql);
  232. if (isset($stud_id) && empty($type)) {
  233. // for 1 student
  234. if ($data = Database::fetch_array($scores)) {
  235. return array($data['exe_result'], $data['exe_weighting']);
  236. } else {
  237. return null;
  238. }
  239. } else {
  240. // all students -> get average
  241. // normal way of getting the info
  242. $students = array(); // user list, needed to make sure we only
  243. // take first attempts into account
  244. $student_count = 0;
  245. $sum = 0;
  246. $bestResult = 0;
  247. $weight = 0;
  248. $sumResult = 0;
  249. while ($data = Database::fetch_array($scores, 'ASSOC')) {
  250. if (!isset($students[$data['exe_user_id']])) {
  251. if ($data['exe_weighting'] != 0) {
  252. $students[$data['exe_user_id']] = $data['exe_result'];
  253. $student_count++;
  254. if ($data['exe_result'] > $bestResult) {
  255. $bestResult = $data['exe_result'];
  256. }
  257. $sum += $data['exe_result'] / $data['exe_weighting'];
  258. $sumResult += $data['exe_result'];
  259. $weight = $data['exe_weighting'];
  260. }
  261. }
  262. }
  263. if ($student_count == 0) {
  264. return null;
  265. } else {
  266. switch ($type) {
  267. case 'best':
  268. return array($bestResult, $weight);
  269. break;
  270. case 'average':
  271. $count = count($this->getStudentList());
  272. if (empty($count)) {
  273. return array(0, $weight);
  274. }
  275. return array($sumResult / $count, $weight);
  276. break;
  277. case 'ranking':
  278. return AbstractLink::getCurrentUserRanking($stud_id, $students);
  279. break;
  280. default:
  281. return array($sum, $student_count);
  282. break;
  283. }
  284. }
  285. }
  286. }
  287. /**
  288. * Get URL where to go to if the user clicks on the link.
  289. * First we go to exercise_jump.php and then to the result page.
  290. * Check this php file for more info.
  291. */
  292. public function get_link()
  293. {
  294. // Status student
  295. $user_id = api_get_user_id();
  296. $session_id = api_get_session_id();
  297. $course_code = $this->get_course_code();
  298. $courseInfo = api_get_course_info($course_code);
  299. $courseId = $courseInfo['real_id'];
  300. $status_user = api_get_status_of_user_in_course($user_id, $courseId);
  301. $data = $this->get_exercise_data();
  302. $path = isset($data['path']) ? $data['path'] : '';
  303. $url = api_get_path(WEB_CODE_PATH).'gradebook/exercise_jump.php?path='.$path.'&session_id='.$session_id.'&cidReq='.$this->get_course_code().'&gradebook=view&exerciseId='.$this->get_ref_id().'&type='.$this->get_type();
  304. if ((!api_is_allowed_to_edit() && $this->calc_score($user_id) == null) || $status_user != 1) {
  305. $url .= '&doexercise='.$this->get_ref_id();
  306. }
  307. return $url;
  308. }
  309. /**
  310. * Get name to display: same as exercise title
  311. */
  312. public function get_name()
  313. {
  314. $documentPath = api_get_path(SYS_COURSE_PATH).$this->course_code."/document";
  315. require_once api_get_path(SYS_CODE_PATH).'exercise/hotpotatoes.lib.php';
  316. $data = $this->get_exercise_data();
  317. if ($this->is_hp == 1) {
  318. if (isset($data['path'])) {
  319. $title = GetQuizName($data['path'], $documentPath);
  320. if ($title == '') {
  321. $title = basename($data['path']);
  322. }
  323. return $title;
  324. }
  325. }
  326. return $data['title'];
  327. }
  328. /**
  329. * Get description to display: same as exercise description
  330. */
  331. public function get_description()
  332. {
  333. $data = $this->get_exercise_data();
  334. return isset($data['description']) ? $data['description'] : null;
  335. }
  336. /**
  337. * Check if this still links to an exercise
  338. */
  339. public function is_valid_link()
  340. {
  341. $sql = 'SELECT count(id)
  342. FROM '.$this->get_exercise_table().'
  343. WHERE
  344. c_id = '.$this->course_id.' AND
  345. id = '.(int) $this->get_ref_id().' ';
  346. $result = Database::query($sql);
  347. $number = Database::fetch_row($result);
  348. return ($number[0] != 0);
  349. }
  350. /**
  351. * @return string
  352. */
  353. public function get_type_name()
  354. {
  355. if ($this->is_hp == 1) {
  356. return 'HotPotatoes';
  357. } else {
  358. return get_lang('Quiz');
  359. }
  360. }
  361. public function needs_name_and_description()
  362. {
  363. return false;
  364. }
  365. public function needs_max()
  366. {
  367. return false;
  368. }
  369. public function needs_results()
  370. {
  371. return false;
  372. }
  373. public function is_allowed_to_change_name() {
  374. return false;
  375. }
  376. /**
  377. * Lazy load function to get the database table of the exercise
  378. */
  379. private function get_exercise_table()
  380. {
  381. $this->exercise_table = Database::get_course_table(TABLE_QUIZ_TEST);
  382. return $this->exercise_table;
  383. }
  384. /**
  385. * Lazy load function to get the database contents of this exercise
  386. */
  387. private function get_exercise_data()
  388. {
  389. $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
  390. if ($this->is_hp == 1) {
  391. $tbl_exercise = Database::get_course_table(TABLE_DOCUMENT);
  392. } else {
  393. $tbl_exercise = $this->get_exercise_table();
  394. }
  395. $ref_id = intval($this->get_ref_id());
  396. if ($tbl_exercise == '') {
  397. return false;
  398. } elseif (!isset($this->exercise_data)) {
  399. if ($this->is_hp == 1) {
  400. $sql = "SELECT * FROM $tbl_exercise ex
  401. INNER JOIN $TBL_ITEM_PROPERTY ip
  402. ON (ip.ref = ex.id AND ip.c_id = ex.c_id)
  403. WHERE
  404. ip.c_id = $this->course_id AND
  405. ex.c_id = $this->course_id AND
  406. ip.ref = $ref_id AND
  407. ip.tool = '".TOOL_DOCUMENT."' AND
  408. ex.path LIKE '%htm%' AND
  409. ex.path LIKE '%HotPotatoes_files%' AND
  410. ip.visibility = 1";
  411. } else {
  412. $sql = 'SELECT * FROM '.$tbl_exercise.'
  413. WHERE
  414. c_id = '.$this->course_id.' AND
  415. id = '.$ref_id;
  416. }
  417. $result = Database::query($sql);
  418. $this->exercise_data = Database::fetch_array($result);
  419. }
  420. return $this->exercise_data;
  421. }
  422. /**
  423. * @return string
  424. */
  425. public function get_icon_name()
  426. {
  427. return 'exercise';
  428. }
  429. /**
  430. * @param bool $hp
  431. */
  432. public function setHp($hp)
  433. {
  434. $this->hp = $hp;
  435. }
  436. }