testcategory.class.php 41 KB

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