testcategory.class.php 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * @author hubert.borderiou & jmontoya
  5. */
  6. class Testcategory
  7. {
  8. public $id;
  9. public $name; //why using name?? Use title as in the db!
  10. public $title;
  11. public $description;
  12. public $parent_id;
  13. public $parent_path;
  14. public $category_array_tree;
  15. public $type;
  16. public $course_id;
  17. public $c_id; // from db
  18. public $root;
  19. public $visibility;
  20. /**
  21. * Constructor of the class Category
  22. * @author - Hubert Borderiou
  23. * If you give an in_id and no in_name, you get info concerning the category of id=in_id
  24. * otherwise, you've got an category objet avec your in_id, in_name, in_descr
  25. * @todo fix this function
  26. *
  27. * @param int $in_id
  28. * @param string $in_name
  29. * @param string $in_description
  30. * @param int $parent_id
  31. * @param string $type Posible values: all, simple, global
  32. * @param int $course_id
  33. * @param int visibility
  34. */
  35. public function Testcategory($in_id = 0, $in_name = '', $in_description = "", $parent_id = 0, $type = 'simple', $course_id = null, $visibility = 1)
  36. {
  37. if ($in_id != 0 && $in_name == "") {
  38. $tmpobj = new Testcategory();
  39. $tmpobj->getCategory($in_id);
  40. $this->id = $tmpobj->id;
  41. $this->name = $tmpobj->name;
  42. $this->title = $tmpobj->name;
  43. $this->description = $tmpobj->description;
  44. $this->parent_id = $tmpobj->parent_id;
  45. $this->parent_path = $this->name;
  46. $this->c_id = $tmpobj->c_id;
  47. $this->root = $tmpobj->root;
  48. $this->visibility = $tmpobj->visibility;
  49. if (!empty($tmpobj->parent_id)) {
  50. $category = new Testcategory($tmpobj->parent_id);
  51. $this->parent_path = $category->parent_path.' > '.$this->name;
  52. }
  53. } else {
  54. $this->id = $in_id;
  55. $this->name = $in_name;
  56. $this->description = $in_description;
  57. $this->parent_id = $parent_id;
  58. $this->visibility = $visibility;
  59. }
  60. $this->type = $type;
  61. if (!empty($course_id)) {
  62. $this->course_id = $course_id;
  63. } else {
  64. $this->course_id = api_get_course_int_id();
  65. }
  66. }
  67. /**
  68. * Return the Testcategory object with id=in_id
  69. * @param int $id
  70. * @return bool
  71. * @assert () === false
  72. */
  73. public function getCategory($id)
  74. {
  75. if (empty($id)) {
  76. return false;
  77. }
  78. $t_cattable = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  79. $id = intval($id);
  80. $sql = "SELECT * FROM $t_cattable WHERE iid = $id ";
  81. $res = Database::query($sql);
  82. $numRows = Database::num_rows($res);
  83. if ($numRows > 0) {
  84. $row = Database::fetch_array($res);
  85. $this->id = $row['iid'];
  86. $this->title = $this->name = $row['title'];
  87. $this->description = $row['description'];
  88. $this->parent_id = $row['parent_id'];
  89. $this->c_id = $row['c_id'];
  90. $this->root = $row['root'];
  91. $this->visibility = $row['visibility'];
  92. } else {
  93. return false;
  94. }
  95. }
  96. /**
  97. * Add Testcategory in the database if name doesn't already exists
  98. *
  99. */
  100. public function addCategoryInBDD()
  101. {
  102. $t_cattable = Database :: get_course_table(TABLE_QUIZ_CATEGORY);
  103. $v_name = Database::escape_string($this->name);
  104. $parent_id = intval($this->parent_id);
  105. $course_id = $this->course_id;
  106. $courseCondition = " AND c_id = $course_id ";
  107. if ($this->type == 'global') {
  108. $course_id = '';
  109. $courseCondition = null;
  110. }
  111. // Only admins can add global categories
  112. if ($this->type == 'global' && empty($course_id) && (!api_is_platform_admin() && !api_is_question_manager())) {
  113. return false;
  114. }
  115. // Check if name already exists.
  116. $sql = "SELECT count(*) AS nb FROM $t_cattable WHERE title = '$v_name' $courseCondition";
  117. $result = Database::query($sql);
  118. $data = Database::fetch_array($result);
  119. // lets add in BD if not the same name
  120. if ($data['nb'] <= 0) {
  121. // @todo inject the app in the class
  122. global $app;
  123. $category = new ChamiloLMS\Entity\CQuizCategory();
  124. $category->setTitle($this->name);
  125. $category->setDescription($this->description);
  126. if (!empty($parent_id)) {
  127. $parent = $app['orm.ems']['db_write']->find('\Entity\CQuizCategory', $parent_id);
  128. if ($parent) {
  129. $category->setParent($parent);
  130. }
  131. }
  132. $category->setCId($course_id);
  133. $app['orm.ems']['db_write']->persist($category);
  134. $app['orm.ems']['db_write']->flush();
  135. if ($category->getIid()) {
  136. return $category->getIid();
  137. } else {
  138. return false;
  139. }
  140. } else {
  141. return false;
  142. }
  143. }
  144. /**
  145. * Modify category name or description of category with id=in_id
  146. */
  147. public function modifyCategory()
  148. {
  149. // @todo inject the app in the class
  150. global $app;
  151. $category = $app['orm.ems']['db_write']->find('\Entity\CQuizCategory', $this->id);
  152. if (!$category) {
  153. return false;
  154. }
  155. $courseId = $category->getCId();
  156. //Only admins can delete global categories
  157. if (empty($courseId) && !api_is_platform_admin()) {
  158. return false;
  159. }
  160. $category->setTitle($this->name);
  161. $category->setDescription($this->description);
  162. $category->setVisibility($this->visibility);
  163. if (!empty($this->parent_id)) {
  164. foreach ($this->parent_id as $parentId) {
  165. if ($this->id == $parentId) {
  166. continue;
  167. }
  168. $parent = $app['orm.ems']['db_write']->find('\Entity\CQuizCategory', $parentId);
  169. if ($parent) {
  170. $category->setParent($parent);
  171. }
  172. break;
  173. }
  174. } else {
  175. $category->setParent(null);
  176. }
  177. $app['orm.ems']['db_write']->persist($category);
  178. $app['orm.ems']['db_write']->flush();
  179. if ($category->getIid()) {
  180. return $category->getIid();
  181. } else {
  182. return false;
  183. }
  184. }
  185. /**
  186. * Removes the category with id=in_id from the database if no question use this category
  187. * @todo I'm removing the $in_id parameter because it seems that you're using $this->id instead of $in_id after confirmation delete this
  188. * jmontoya
  189. */
  190. public function removeCategory()
  191. {
  192. global $app;
  193. $category = $app['orm.ems']['db_write']->find('\Entity\CQuizCategory', $this->id);
  194. if (!$category) {
  195. return false;
  196. }
  197. //Only admins can delete global categories
  198. $courseId = $category->getCId();
  199. //Only admins can delete global categories
  200. if (empty($courseId) && !api_is_platform_admin() || api_is_question_manager()) {
  201. return false;
  202. }
  203. $repo = $app['orm.ems']['db_write']->getRepository('ChamiloLMS\Entity\CQuizCategory');
  204. $repo->removeFromTree($category);
  205. // clear cached nodes
  206. $app['orm.ems']['db_write']->clear();
  207. return true;
  208. }
  209. /**
  210. * @param string $title
  211. * @param int $course_id
  212. * @return bool
  213. */
  214. public function get_category_by_title($title , $course_id = 0)
  215. {
  216. $table = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  217. $course_id = intval($course_id);
  218. $title = Database::escape_string($title);
  219. $sql = "SELECT * FROM $table WHERE title = '$title' AND c_id IN ('0', '$course_id')LIMIT 1";
  220. $result = Database::query($sql);
  221. if (Database::num_rows($result)) {
  222. $result = Database::store_result($result, 'ASSOC');
  223. return $result[0];
  224. }
  225. return false;
  226. }
  227. /**
  228. *
  229. * Get categories by title for json calls
  230. * @param string $tag
  231. * @return array
  232. * @assert() === false
  233. */
  234. public function get_categories_by_keyword($tag)
  235. {
  236. if (empty($tag)) {
  237. return false;
  238. }
  239. $table = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  240. $sql = "SELECT iid, title, c_id FROM $table WHERE 1=1 ";
  241. $tag = Database::escape_string($tag);
  242. $where_condition = array();
  243. if (!empty($tag)) {
  244. $condition = ' LIKE "%'.$tag.'%"';
  245. $where_condition = array(
  246. "title $condition",
  247. );
  248. $where_condition = ' AND ('.implode(' OR ', $where_condition).') ';
  249. }
  250. switch ($this->type) {
  251. case 'simple':
  252. $course_condition = " AND c_id = '".api_get_course_int_id()."' ";
  253. break;
  254. case 'global':
  255. $course_condition = " AND c_id = '0' ";
  256. break;
  257. case 'all':
  258. $course_condition = " AND c_id IN ('0', '".api_get_course_int_id()."')";
  259. break;
  260. }
  261. $where_condition .= $course_condition;
  262. $order_clause = " ORDER BY title";
  263. $sql .= $where_condition.$order_clause;
  264. $result = Database::query($sql);
  265. if (Database::num_rows($result)) {
  266. return Database::store_result($result, 'ASSOC');
  267. }
  268. return false;
  269. }
  270. /**
  271. * Gets the number of question of category id=in_id
  272. * @todo I'm removing the $in_id parameter because it seems that you're using $this->id instead of $in_id after confirmation delete this
  273. * jmontoya
  274. */
  275. //public function getCategoryQuestionsNumber($in_id) {
  276. public function getCategoryQuestionsNumber()
  277. {
  278. $t_reltable = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
  279. $in_id = Database::escape_string($this->id);
  280. $sql = "SELECT count(*) AS nb FROM $t_reltable WHERE category_id = $in_id";
  281. $res = Database::query($sql);
  282. if (Database::num_rows($res)) {
  283. $row = Database::fetch_array($res);
  284. return $row['nb'];
  285. }
  286. return 0;
  287. }
  288. public function display($in_color="#E0EBF5")
  289. {
  290. echo "<textarea style='background-color:$in_color; width:60%; height:100px;'>";
  291. print_r($this);
  292. echo "</textarea>";
  293. }
  294. /**
  295. * return an array of all Category objects in the database
  296. * If in_field=="" Return an array of all category objects in the database
  297. * Otherwise, return an array of all in_field value in the database (in_field = id or name or description)
  298. */
  299. public static function getCategoryListInfo($in_field = "", $courseId = null)
  300. {
  301. $courseId = intval($courseId);
  302. $t_cattable = Database :: get_course_table(TABLE_QUIZ_CATEGORY);
  303. $in_field = Database::escape_string($in_field);
  304. $tabres = array();
  305. if ($in_field=="") {
  306. $sql = "SELECT * FROM $t_cattable WHERE c_id = $courseId ORDER BY title ASC";
  307. $res = Database::query($sql);
  308. while ($row = Database::fetch_array($res)) {
  309. $tmpcat = new Testcategory($row['iid'], $row['title'], $row['description'], $row['parent_id']);
  310. $tabres[] = $tmpcat;
  311. }
  312. } else {
  313. $sql = "SELECT $in_field FROM $t_cattable WHERE c_id = $courseId
  314. ORDER BY $in_field ASC";
  315. $res = Database::query($sql);
  316. while ($row = Database::fetch_array($res)) {
  317. $tabres[] = $row[$in_field];
  318. }
  319. }
  320. return $tabres;
  321. }
  322. /**
  323. Return the testcategory id for question with question_id = $in_questionid
  324. In this version, a question has only 1 testcategory.
  325. Return the testcategory id, 0 if none
  326. * @assert () === false
  327. */
  328. public static function getCategoryForQuestion($question_id, $courseId = null)
  329. {
  330. $result = array();
  331. if (empty($courseId)) {
  332. $courseId = api_get_course_int_id();
  333. }
  334. $courseId = intval($courseId);
  335. $categoryTable = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
  336. $question_id = Database::escape_string($question_id);
  337. $sql = "SELECT category_id FROM $categoryTable WHERE question_id = '$question_id' AND c_id = $courseId";
  338. $res = Database::query($sql);
  339. if (Database::num_rows($res) > 0) {
  340. while ($row = Database::fetch_array($res, 'ASSOC')) {
  341. $result[] = $row['category_id'];
  342. }
  343. }
  344. return $result;
  345. }
  346. public static function getCategoryForQuestionWithCategoryData($question_id, $courseId = null) {
  347. $result = array(); // result
  348. if (empty($courseId)) {
  349. $courseId = api_get_course_int_id();
  350. }
  351. $courseId = intval($courseId);
  352. $t_cattable = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
  353. $table_category = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  354. $question_id = Database::escape_string($question_id);
  355. $sql = "SELECT * FROM $t_cattable qc INNER JOIN $table_category c ON (category_id = c.iid) WHERE question_id = '$question_id' AND qc.c_id = $courseId";
  356. $res = Database::query($sql);
  357. if (Database::num_rows($res) > 0) {
  358. while ($row = Database::fetch_array($res, 'ASSOC')) {
  359. $result[] = $row;
  360. }
  361. }
  362. return $result;
  363. }
  364. /**
  365. *
  366. * @param int $question_id
  367. * @param int $course_id
  368. * @param bool $display_into_labels
  369. * @param bool $categoryMinusOne shows category - 1 see BT#6540
  370. * @return string
  371. */
  372. public static function getCategoryNamesForQuestion($question_id, $course_id = null, $display_into_labels = true, $categoryMinusOne = false)
  373. {
  374. if (empty($course_id) || $course_id == "") {
  375. $course_id = api_get_course_int_id();
  376. }
  377. $t_cattable = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
  378. $table_category = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  379. $question_id = intval($question_id);
  380. $course_id = intval($course_id);
  381. $sql = "SELECT c.title, c.iid FROM $t_cattable qc INNER JOIN $table_category c
  382. ON (qc.category_id = c.iid AND qc.c_id = $course_id)
  383. WHERE question_id = '$question_id' ";
  384. $res = Database::query($sql);
  385. $result = array();
  386. if (Database::num_rows($res) > 0) {
  387. while ($row = Database::fetch_array($res)) {
  388. $cat = new Testcategory($row['iid']);
  389. if ($categoryMinusOne) {
  390. $catParts = explode('>', $cat->parent_path);
  391. if (isset($catParts[0])) {
  392. unset($catParts[0]);
  393. $cat->parent_path = implode('>', $catParts);
  394. }
  395. }
  396. $result[] = array('title' => $cat->parent_path);
  397. }
  398. }
  399. if ($display_into_labels) {
  400. $html = self::draw_category_label($result, 'header');
  401. return $html;
  402. }
  403. return $result;
  404. }
  405. /**
  406. * true if question id has a category
  407. * @assert () === false
  408. * @assert (null) === false
  409. * @assert (-1) === false
  410. */
  411. public static function isQuestionHasCategory($question_id)
  412. {
  413. $category_list = Testcategory::getCategoryForQuestion($question_id);
  414. if (!empty($category_list)) {
  415. return true;
  416. }
  417. return false;
  418. }
  419. /**
  420. * @todo fix this
  421. Return the category name for question with question_id = $in_questionid
  422. In this version, a question has only 1 category.
  423. Return the category id, "" if none
  424. */
  425. public static function getCategoryNameForQuestion($catid, $course_id = null)
  426. {
  427. if (empty($course_id) || $course_id == "") {
  428. $course_id = api_get_course_int_id();
  429. }
  430. $course_id = intval($course_id);
  431. $result = array();
  432. $t_cattable = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  433. $catid = Database::escape_string($catid);
  434. $sql = "SELECT title FROM $t_cattable WHERE iid = '$catid' AND c_id = $course_id";
  435. $res = Database::query($sql);
  436. $data = Database::fetch_array($res);
  437. if (Database::num_rows($res) > 0) {
  438. $result = $data['title'];
  439. }
  440. return $result;
  441. }
  442. /**
  443. * return the list of different categories ID for a test
  444. * @param int exercise id
  445. * @param bool group category
  446. * @return array of category id (integer)
  447. * @author hubert.borderiou 07-04-2011, Julio Montoya
  448. */
  449. public static function getListOfCategoriesIDForTest($exercise_id, $grouped_by_category = true)
  450. {
  451. // parcourir les questions d'un test, recup les categories uniques dans un tableau
  452. $categories_in_exercise = array();
  453. $exercise = new Exercise();
  454. $exercise->read($exercise_id, false);
  455. $categories_in_exercise = $exercise->getQuestionWithCategories();
  456. $categories = array();
  457. if (!empty($categories_in_exercise)) {
  458. foreach($categories_in_exercise as $category) {
  459. $category['id'] = $category['iid'];
  460. $categories[$category['iid']] = $category;
  461. }
  462. }
  463. return $categories;
  464. /*
  465. // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ???
  466. foreach ($question_list as $question_id) {
  467. $category_list = Testcategory::getCategoryForQuestion($question_id);
  468. if (!empty($category_list)) {
  469. $categories_in_exercise = array_merge($categories_in_exercise, $category_list);
  470. }
  471. }
  472. if (!empty($categories_in_exercise)) {
  473. $categories_in_exercise = array_unique(array_filter($categories_in_exercise));
  474. }
  475. return $categories_in_exercise;*/
  476. }
  477. public static function getListOfCategoriesIDForTestObject(Exercise $exercise_obj)
  478. {
  479. // parcourir les questions d'un test, recup les categories uniques dans un tableau
  480. $categories_in_exercise = array();
  481. // $question_list = $exercise_obj->getQuestionList();
  482. $question_list = $exercise_obj->getQuestionOrderedListByName();
  483. // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ???
  484. foreach ($question_list as $questionInfo) {
  485. $question_id = $questionInfo['question_id'];
  486. $category_list = Testcategory::getCategoryForQuestion($question_id);
  487. if (!empty($category_list)) {
  488. $categories_in_exercise = array_merge($categories_in_exercise, $category_list);
  489. }
  490. }
  491. if (!empty($categories_in_exercise)) {
  492. $categories_in_exercise = array_unique(array_filter($categories_in_exercise));
  493. }
  494. return $categories_in_exercise;
  495. }
  496. /**
  497. * Return the list of differents categories NAME for a test
  498. * @param int exercise id
  499. * @param bool
  500. * @return array of string
  501. *
  502. * @author function rewrote by jmontoya
  503. */
  504. public static function getListOfCategoriesNameForTest($exercise_id, $grouped_by_category = true)
  505. {
  506. $result = array();
  507. $categories = self::getListOfCategoriesIDForTest($exercise_id, $grouped_by_category);
  508. foreach ($categories as $catInfo) {
  509. $categoryId = $catInfo['iid'];
  510. ///$cat = new Testcategory($cat_id);
  511. if (!empty($categoryId)) {
  512. $result[$categoryId] = array(
  513. 'title' => $catInfo['title'],
  514. 'parent_id' => $catInfo['parent_id'],
  515. 'c_id' => $catInfo['c_id']
  516. );
  517. }
  518. }
  519. return $result;
  520. }
  521. /**
  522. * @param Exercise $exercise_obj
  523. * @return array
  524. */
  525. public static function getListOfCategoriesForTest(Exercise $exercise_obj) {
  526. $result = array();
  527. $categories = self::getListOfCategoriesIDForTestObject($exercise_obj);
  528. foreach ($categories as $cat_id) {
  529. $cat = new Testcategory($cat_id);
  530. $cat = (array)$cat;
  531. $cat['iid'] = $cat['id'];
  532. $cat['title'] = $cat['name'];
  533. $result[$cat['id']] = $cat;
  534. }
  535. return $result;
  536. }
  537. /**
  538. * return the number of differents categories for a test
  539. * input : test_id
  540. * return : integer
  541. * hubert.borderiou 07-04-2011
  542. */
  543. public static function getNumberOfCategoriesForTest($exercise_id) {
  544. return count(Testcategory::getListOfCategoriesIDForTest($exercise_id));
  545. }
  546. /**
  547. * return the number of question of a category id in a test
  548. * input : test_id, category_id
  549. * return : integer
  550. * hubert.borderiou 07-04-2011
  551. */
  552. public static function getNumberOfQuestionsInCategoryForTest($exercise_id, $category_id)
  553. {
  554. $number_questions_in_category = 0;
  555. $exercise = new Exercise();
  556. $exercise->read($exercise_id);
  557. $question_list = $exercise->getQuestionList();
  558. // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ? ? ?
  559. foreach ($question_list as $question_id) {
  560. $category_in_question = Testcategory::getCategoryForQuestion($question_id);
  561. if (in_array($category_id, $category_in_question)) {
  562. $number_questions_in_category++;
  563. }
  564. }
  565. return $number_questions_in_category;
  566. }
  567. /**
  568. * return the number of question for a test using random by category
  569. * input : test_id, number of random question (min 1)
  570. * hubert.borderiou 07-04-2011
  571. * question witout categories are not counted
  572. */
  573. public static function getNumberOfQuestionRandomByCategory($exercise_id, $in_nbrandom)
  574. {
  575. $nbquestionresult = 0;
  576. $list_categories = Testcategory::getListOfCategoriesIDForTest($exercise_id);
  577. if (!empty($list_categories)) {
  578. foreach ($list_categories as $category_item) {
  579. if ($category_item > 0) {
  580. // 0 = no category for this question
  581. $nbQuestionInThisCat = Testcategory::getNumberOfQuestionsInCategoryForTest($exercise_id, $category_item);
  582. if ($nbQuestionInThisCat > $in_nbrandom) {
  583. $nbquestionresult += $in_nbrandom;
  584. } else {
  585. $nbquestionresult += $nbQuestionInThisCat;
  586. }
  587. }
  588. }
  589. }
  590. return $nbquestionresult;
  591. }
  592. /**
  593. * Return an array (id=>name)
  594. * tabresult[0] = get_lang('NoCategory');
  595. *
  596. */
  597. static function getCategoriesIdAndName($in_courseid = 0)
  598. {
  599. $tabcatobject = Testcategory::getCategoryListInfo("", $in_courseid);
  600. $tabresult = array("0"=>get_lang('NoCategorySelected'));
  601. for ($i=0; $i < count($tabcatobject); $i++) {
  602. $tabresult[$tabcatobject[$i]->id] = $tabcatobject[$i]->name;
  603. }
  604. return $tabresult;
  605. }
  606. /**
  607. * Returns an array of question ids for each category
  608. * $categories[1][30] = 10, array with category id = 1 and question_id = 10
  609. * A question has "n" categories
  610. * @param int exercise
  611. * @param array check question list
  612. * @param string order by
  613. * @return array
  614. */
  615. static function getQuestionsByCat(
  616. $exerciseId,
  617. $check_in_question_list = array(),
  618. $categoriesAddedInExercise = array()
  619. ) {
  620. $tableQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION);
  621. $TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  622. $TBL_QUESTION_REL_CATEGORY = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
  623. $categoryTable = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  624. $exerciseId = intval($exerciseId);
  625. $sql = "SELECT DISTINCT qrc.question_id, qrc.category_id
  626. FROM $TBL_QUESTION_REL_CATEGORY qrc INNER JOIN $TBL_EXERCICE_QUESTION eq
  627. ON (eq.question_id = qrc.question_id)
  628. INNER JOIN $categoryTable c
  629. ON (c.iid = qrc.category_id)
  630. INNER JOIN $tableQuestion q
  631. ON (q.iid = qrc.question_id )
  632. WHERE exercice_id = $exerciseId AND
  633. qrc.c_id = ".api_get_course_int_id()."
  634. ";
  635. $res = Database::query($sql);
  636. $categories = array();
  637. while ($data = Database::fetch_array($res)) {
  638. if (!empty($check_in_question_list)) {
  639. if (!in_array($data['question_id'], $check_in_question_list)) {
  640. continue;
  641. }
  642. }
  643. if (!isset($categories[$data['category_id']]) OR !is_array($categories[$data['category_id']])) {
  644. $categories[$data['category_id']] = array();
  645. }
  646. $categories[$data['category_id']][] = $data['question_id'];
  647. }
  648. $newCategoryList = array();
  649. foreach ($categoriesAddedInExercise as $category) {
  650. $categoryId = $category['category_id'];
  651. if (isset($categories[$categoryId])) {
  652. $newCategoryList[$categoryId] = $categories[$categoryId];
  653. }
  654. }
  655. return $newCategoryList;
  656. }
  657. /**
  658. * Return an array of X elements of an array
  659. * @param array $array
  660. * @param int $numberOfElements
  661. * @param bool $randomize
  662. * @return array
  663. */
  664. public static function getNElementsFromArray($array, $numberOfElements, $randomize)
  665. {
  666. if (empty($numberOfElements)) {
  667. return array();
  668. }
  669. if (!empty($array)) {
  670. if ($randomize) {
  671. shuffle($array);
  672. }
  673. if ($numberOfElements < count($array)) {
  674. $array = array_slice($array, 0, $numberOfElements);
  675. }
  676. }
  677. return $array;
  678. }
  679. /**
  680. * Display signs [+] and/or (>0) after question title if question has options
  681. * scoreAlwaysPositive and/or uncheckedMayScore
  682. */
  683. public function displayQuestionOption($in_objQuestion) {
  684. if ($in_objQuestion->type == MULTIPLE_ANSWER && $in_objQuestion->scoreAlwaysPositive) {
  685. echo "<span style='font-size:75%'> (>0)</span>";
  686. }
  687. if ($in_objQuestion->type == MULTIPLE_ANSWER && $in_objQuestion->uncheckedMayScore) {
  688. echo "<span style='font-size:75%'> [+]</span>";
  689. }
  690. }
  691. /**
  692. * key of $array are the categopy id (0 for not in a category)
  693. * value is the array of question id of this category
  694. * Sort question by Category
  695. */
  696. public static function sortCategoriesQuestionByName($array) {
  697. $tabResult = array();
  698. $category_array = array();
  699. // tab of category name
  700. while (list($cat_id, $tabquestion) = each($array)) {
  701. $cat = new Testcategory($cat_id);
  702. $category_array[$cat_id] = $cat->title;
  703. }
  704. reset($array);
  705. // sort table by value, keeping keys as they are
  706. asort($category_array);
  707. // keys of $tabCatName are keys order for $in_tab
  708. while (list($key, $val) = each($category_array)) {
  709. $tabResult[$key] = $array[$key];
  710. }
  711. return $tabResult;
  712. }
  713. /**
  714. * return total score for test exe_id for all question in the category $in_cat_id for user
  715. * If no question for this category, return ""
  716. */
  717. public static function getCatScoreForExeidForUserid($in_cat_id, $in_exe_id, $in_user_id) {
  718. $tbl_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  719. $tbl_question_rel_category = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
  720. $in_cat_id = intval($in_cat_id);
  721. $in_exe_id = intval($in_exe_id);
  722. $in_user_id = intval($in_user_id);
  723. $query = "SELECT DISTINCT marks, exe_id, user_id, ta.question_id, category_id
  724. FROM $tbl_track_attempt ta , $tbl_question_rel_category qrc
  725. WHERE ta.question_id = qrc.question_id AND qrc.category_id=$in_cat_id AND exe_id = $in_exe_id AND user_id = $in_user_id";
  726. $res = Database::query($query);
  727. $totalcatscore = "";
  728. while ($data = Database::fetch_array($res)) {
  729. $totalcatscore += $data['marks'];
  730. }
  731. return $totalcatscore;
  732. }
  733. /**
  734. * return the number max of question in a category
  735. * count the number of questions in all categories, and return the max
  736. * @author - hubert borderiou
  737. */
  738. public static function getNumberMaxQuestionByCat($in_testid)
  739. {
  740. $res_num_max = 0;
  741. // foreach question
  742. $tabcatid = Testcategory::getListOfCategoriesIDForTest($in_testid);
  743. for ($i=0; $i < count($tabcatid); $i++) {
  744. if ($tabcatid[$i] > 0) { // 0 = no category for this question
  745. $nbQuestionInThisCat = Testcategory::getNumberOfQuestionsInCategoryForTest($in_testid, $tabcatid[$i]);
  746. if ($nbQuestionInThisCat > $res_num_max) {
  747. $res_num_max = $nbQuestionInThisCat;
  748. }
  749. }
  750. }
  751. return $res_num_max;
  752. }
  753. /**
  754. * @param int $course_id
  755. * @return array
  756. */
  757. public static function getCategoryListName($course_id = null)
  758. {
  759. $category_list = self::getCategoryListInfo(null, $course_id);
  760. $category_name_list = array();
  761. if (!empty($category_list)) {
  762. foreach ($category_list as $category) {
  763. $category_name_list[$category->id] = $category->name;
  764. }
  765. }
  766. return $category_name_list;
  767. }
  768. /**
  769. * @param array $category_list
  770. * @param array $all_categories
  771. * @return null|string
  772. */
  773. public static function return_category_labels($category_list, $all_categories)
  774. {
  775. $category_list_to_render = array();
  776. foreach ($category_list as $category_id) {
  777. $category_name = null;
  778. if (!isset($all_categories[$category_id])) {
  779. $category_name = get_lang('Untitled');
  780. $parentId = null;
  781. $cId = null;
  782. } else {
  783. $parentId = $all_categories[$category_id]['parent_id'];
  784. $cId = $all_categories[$category_id]['c_id'];
  785. $category_name = $all_categories[$category_id]['title'];
  786. }
  787. $category_list_to_render[] = array(
  788. 'title' => $category_name,
  789. 'parent_id' => $parentId,
  790. 'c_id' => $cId
  791. );
  792. }
  793. $html = self::draw_category_label($category_list_to_render, 'label');
  794. return $html;
  795. }
  796. /**
  797. * @param array $category_list
  798. * @param string $type
  799. * @return null|string
  800. */
  801. public static function draw_category_label($category_list, $type = 'label') {
  802. $new_category_list = array();
  803. foreach ($category_list as $category) {
  804. $category_name = $category['title'];
  805. $category_name_cut = Text::cut($category['title'], 20);
  806. switch ($type) {
  807. case 'label':
  808. // Global cat
  809. /*
  810. $parentId = isset($category['parent_id']) && !empty($category['parent_id']) ? $category['parent_id'] : null;
  811. if (empty($parentId)) {
  812. $new_category_list[] = Display::label($category_name, 'info');
  813. } else {
  814. // Local cat
  815. $new_category_list[] = Display::label($category_name, 'success');
  816. }*/
  817. $courseId = isset($category['c_id']) && !empty($category['c_id']) ? $category['c_id'] : null;
  818. if (empty($courseId)) {
  819. $new_category_list[] = Display::label($category_name_cut, 'info', $category_name);
  820. } else {
  821. // Local cat
  822. $new_category_list[] = Display::label($category_name_cut, 'success', $category_name);
  823. }
  824. break;
  825. case 'header':
  826. $new_category_list[] = $category_name;
  827. break;
  828. }
  829. }
  830. $html = null;
  831. if (!empty($new_category_list)) {
  832. switch ($type) {
  833. case 'label':
  834. $html = implode(' ', $new_category_list);
  835. break;
  836. case 'header':
  837. $html = Display::page_subheader3(get_lang('Category').': '.implode(', ', $new_category_list));
  838. break;
  839. }
  840. }
  841. return $html;
  842. }
  843. /**
  844. * Returns a category summary report
  845. *
  846. * @param int exercise id
  847. * @param array prefilled array with the category_id, score, and weight example: array(1 => array('score' => '10', 'total' => 20));
  848. * @param bool $categoryMinusOne shows category - 1 see BT#6540
  849. * @return string
  850. */
  851. public static function get_stats_table_by_attempt($exercise_id, $category_list = array(), $categoryMinusOne = false)
  852. {
  853. global $app;
  854. if (empty($category_list)) {
  855. return null;
  856. }
  857. $category_name_list = Testcategory::getListOfCategoriesNameForTest($exercise_id, false);
  858. $table = new HTML_Table(array('class' => 'data_table'));
  859. $table->setHeaderContents(0, 0, get_lang('Categories'));
  860. $table->setHeaderContents(0, 1, get_lang('AbsoluteScore'));
  861. $table->setHeaderContents(0, 2, get_lang('RelativeScore'));
  862. $row = 1;
  863. $none_category = array();
  864. if (isset($category_list['none'])) {
  865. $none_category = $category_list['none'];
  866. unset($category_list['none']);
  867. }
  868. $total = array();
  869. if (isset($category_list['total'])) {
  870. $total = $category_list['total'];
  871. unset($category_list['total']);
  872. }
  873. $em = $app['orm.em'];
  874. $repo = $em->getRepository('ChamiloLMS\Entity\CQuizCategory');
  875. $redefineCategoryList = array();
  876. if (!empty($category_list) && count($category_list) > 1) {
  877. $globalCategoryScore = array();
  878. foreach ($category_list as $category_id => $category_item) {
  879. $cat = $em->find('ChamiloLMS\Entity\CQuizCategory', $category_id);
  880. $path = $repo->getPath($cat);
  881. $categoryName = $category_name_list[$category_id];
  882. $index = 0;
  883. if ($categoryMinusOne) {
  884. $index = 1;
  885. }
  886. if (isset($path[$index])) {
  887. $category_id = $path[$index]->getIid();
  888. $categoryName = $path[$index]->getTitle();
  889. }
  890. if (!isset($globalCategoryScore[$category_id])) {
  891. $globalCategoryScore[$category_id] = array();
  892. $globalCategoryScore[$category_id]['score'] = 0;
  893. $globalCategoryScore[$category_id]['total'] = 0;
  894. $globalCategoryScore[$category_id]['title'] = '';
  895. }
  896. $globalCategoryScore[$category_id]['score'] += $category_item['score'];
  897. $globalCategoryScore[$category_id]['total'] += $category_item['total'];
  898. $globalCategoryScore[$category_id]['title'] = $categoryName;
  899. }
  900. foreach ($globalCategoryScore as $category_item) {
  901. $table->setCellContents($row, 0, $category_item['title']);
  902. $table->setCellContents($row, 1, ExerciseLib::show_score($category_item['score'], $category_item['total'], false));
  903. $table->setCellContents($row, 2, ExerciseLib::show_score($category_item['score'], $category_item['total'], true, false, true));
  904. $class = 'class="row_odd"';
  905. if ($row % 2) {
  906. $class = 'class="row_even"';
  907. }
  908. $table->setRowAttributes($row, $class, true);
  909. $row++;
  910. }
  911. if (!empty($none_category)) {
  912. $table->setCellContents($row, 0, get_lang('None'));
  913. $table->setCellContents($row, 1, ExerciseLib::show_score($none_category['score'], $none_category['total'], false));
  914. $table->setCellContents($row, 2, ExerciseLib::show_score($none_category['score'], $none_category['total'], true, false, true));
  915. $row++;
  916. }
  917. if (!empty($total)) {
  918. $table->setCellContents($row, 0, get_lang('Total'));
  919. $table->setCellContents($row, 1, ExerciseLib::show_score($total['score'], $total['total'], false));
  920. $table->setCellContents($row, 2, ExerciseLib::show_score($total['score'], $total['total'], true, false, true));
  921. $table->setRowAttributes($row, 'class="row_total"', true);
  922. }
  923. return $table->toHtml();
  924. }
  925. return null;
  926. }
  927. /**
  928. * @return array
  929. */
  930. function get_all_categories() {
  931. $table = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  932. $sql = "SELECT * FROM $table ORDER BY title ASC";
  933. $res = Database::query($sql);
  934. while ($row = Database::fetch_array($res,'ASSOC')) {
  935. $array[] = $row;
  936. }
  937. return $array;
  938. }
  939. /**
  940. * @param int $exercise_id
  941. * @param int $course_id
  942. * @param string $order
  943. * @return array
  944. */
  945. public function getCategoryExerciseTree(
  946. $exercise_id,
  947. $course_id,
  948. $order = null,
  949. $shuffle = false,
  950. $excludeCategoryWithNoQuestions = true
  951. ) {
  952. $table = Database::get_course_table(TABLE_QUIZ_REL_CATEGORY);
  953. $table_category = Database::get_course_table(TABLE_QUIZ_CATEGORY);
  954. $sql = "SELECT * FROM $table qc INNER JOIN $table_category c ON (category_id = c.iid)
  955. WHERE exercise_id = {$exercise_id} ";
  956. if (!empty($order)) {
  957. $sql .= "ORDER BY $order";
  958. }
  959. $categories = array();
  960. $result = Database::query($sql);
  961. if (Database::num_rows($result)) {
  962. while ($row = Database::fetch_array($result, 'ASSOC')) {
  963. if ($excludeCategoryWithNoQuestions) {
  964. if ($row['count_questions'] == 0) {
  965. continue;
  966. }
  967. }
  968. $categories[$row['category_id']] = $row;
  969. }
  970. }
  971. if ($shuffle) {
  972. ArrayClass::shuffle_assoc($categories);
  973. }
  974. return $categories;
  975. }
  976. /**
  977. * Returns the category form.
  978. * @param Exercise $exercise_obj
  979. * @return string
  980. */
  981. public function returnCategoryForm(Exercise $exercise_obj)
  982. {
  983. $categories = $this->getListOfCategoriesForTest($exercise_obj);
  984. $saved_categories = $exercise_obj->get_categories_in_exercise();
  985. $return = null;
  986. if (!empty($categories)) {
  987. $nbQuestionsTotal = $exercise_obj->getNumberQuestionExerciseCategory();
  988. $exercise_obj->setCategoriesGrouping(true);
  989. $real_question_count = count($exercise_obj->getQuestionList());
  990. $warning = null;
  991. if ($nbQuestionsTotal != $real_question_count) {
  992. $warning = Display::return_message(get_lang('CheckThatYouHaveEnoughQuestionsInYourCategories'), 'warning');
  993. }
  994. $return .= $warning;
  995. $return .= '<table class="data_table">';
  996. $return .= '<tr>';
  997. $return .= '<th height="24">' . get_lang('Categories') . '</th>';
  998. $return .= '<th width="70" height="24">' . get_lang('Number') . '</th></tr>';
  999. foreach($categories as $category) {
  1000. $cat_id = $category['iid'];
  1001. $return .= '<tr>';
  1002. $return .= '<td>';
  1003. $return .= Display::div($category['parent_path']);
  1004. $return .= '</td>';
  1005. $return .= '<td>';
  1006. $value = isset($saved_categories) && isset($saved_categories[$cat_id]) ? $saved_categories[$cat_id]['count_questions'] : -1;
  1007. $return .= '<input name="category['.$cat_id.']" value="' .$value.'" />';
  1008. $return .= '</td>';
  1009. $return .= '</tr>';
  1010. }
  1011. $return .= '</table>';
  1012. $return .= get_lang('ZeroMeansNoQuestionWillBeSelectedMinusOneMeansThatAllQuestionsWillBeSelected');
  1013. return $return;
  1014. }
  1015. }
  1016. /**
  1017. * Sorts an array
  1018. * @param $array
  1019. * @return mixed
  1020. */
  1021. public function sort_tree_array($array)
  1022. {
  1023. foreach ($array as $key => $row) {
  1024. $parent[$key] = $row['parent_id'];
  1025. }
  1026. if (count($array) > 0) {
  1027. array_multisort($parent, SORT_ASC, $array);
  1028. }
  1029. return $array;
  1030. }
  1031. public function getForm(& $form, $action = 'new') {
  1032. switch($action) {
  1033. case 'new':
  1034. $header = get_lang('AddACategory');
  1035. $submit = get_lang('AddTestCategory');
  1036. break;
  1037. case 'edit':
  1038. $header = get_lang('EditCategory');
  1039. $submit = get_lang('ModifyCategory');
  1040. break;
  1041. }
  1042. // settting the form elements
  1043. $form->addElement('header', $header);
  1044. $form->addElement('hidden', 'category_id');
  1045. $form->addElement('text', 'category_name', get_lang('CategoryName'), array('class' => 'span6'));
  1046. $form->add_html_editor('category_description', get_lang('CategoryDescription'), false, false, array('ToolbarSet' => 'test_category', 'Width' => '90%', 'Height' => '200'));
  1047. $category_parent_list = array();
  1048. $options = array(
  1049. '1' => get_lang('Visible'),
  1050. '0' => get_lang('Hidden')
  1051. );
  1052. $form->addElement('select', 'visibility', get_lang('Visibility'), $options);
  1053. $script = null;
  1054. if (!empty($this->parent_id)) {
  1055. $parent_cat = new Testcategory($this->parent_id);
  1056. $category_parent_list = array($parent_cat->id => $parent_cat->name);
  1057. $script .= '<script>$(function() { $("#parent_id").trigger("addItem",[{"title": "'.$parent_cat->name.'", "value": "'.$parent_cat->id.'"}]); });</script>';
  1058. }
  1059. $form->addElement('html', $script);
  1060. $form->addElement('select', 'parent_id', get_lang('Parent'), $category_parent_list, array('id' => 'parent_id'));
  1061. $form->addElement('style_submit_button', 'SubmitNote', $submit, 'class="add"');
  1062. // setting the defaults
  1063. $defaults = array();
  1064. $defaults["category_id"] = $this->id;
  1065. $defaults["category_name"] = $this->name;
  1066. $defaults["category_description"] = $this->description;
  1067. $defaults["parent_id"] = $this->parent_id;
  1068. $defaults["visibility"] = $this->visibility;
  1069. $form->setDefaults($defaults);
  1070. // setting the rules
  1071. $form->addRule('category_name', get_lang('ThisFieldIsRequired'), 'required');
  1072. }
  1073. }