answer.class.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * This class allows to instantiate an object of type Answer
  5. * 5 arrays are created to receive the attributes of each answer belonging to a specified question
  6. * @package chamilo.exercise
  7. * @author Olivier Brouckaert
  8. * @version $Id: answer.class.php 21172 2009-06-01 20:58:05Z darkvela $
  9. */
  10. /**
  11. * Code
  12. */
  13. /**
  14. * Answer class
  15. * @package chamilo.exercise
  16. */
  17. class Answer
  18. {
  19. public $questionId;
  20. // these are arrays
  21. public $answer;
  22. public $correct;
  23. public $comment;
  24. public $weighting;
  25. public $position;
  26. public $hotspot_coordinates;
  27. public $hotspot_type;
  28. public $destination;
  29. // these arrays are used to save temporarily new answers
  30. // then they are moved into the arrays above or deleted in the event of cancellation
  31. public $new_answer;
  32. public $new_correct;
  33. public $new_comment;
  34. public $new_weighting;
  35. public $new_position;
  36. public $new_hotspot_coordinates;
  37. public $new_hotspot_type;
  38. public $nbrAnswers;
  39. public $new_nbrAnswers;
  40. public $new_destination; // id of the next question if feedback option is set to Directfeedback
  41. public $course; //Course information
  42. /**
  43. * constructor of the class
  44. *
  45. * @author Olivier Brouckaert
  46. * @param integer Question ID that answers belong to
  47. */
  48. function Answer($questionId, $course_id = null)
  49. {
  50. $this->questionId = intval($questionId);
  51. $this->answer = array();
  52. $this->correct = array();
  53. $this->comment = array();
  54. $this->weighting = array();
  55. $this->position = array();
  56. $this->hotspot_coordinates = array();
  57. $this->hotspot_type = array();
  58. $this->destination = array();
  59. // clears $new_* arrays
  60. $this->cancel();
  61. if (!empty($course_id)) {
  62. $course_info = api_get_course_info_by_id($course_id);
  63. } else {
  64. $course_info = api_get_course_info();
  65. }
  66. $this->course = $course_info;
  67. $this->course_id = $course_info['real_id'];
  68. // fills arrays
  69. $objExercise = new Exercise($this->course_id);
  70. $exerciseId = isset($_REQUEST['exerciseId']) ? $_REQUEST['exerciseId'] : null;
  71. if ($exerciseId) {
  72. $objExercise->read($exerciseId);
  73. if ($objExercise->random_answers == '1') {
  74. $this->readOrderedBy('rand()', ''); // randomize answers
  75. } else {
  76. $this->read(); // natural order
  77. }
  78. } else {
  79. $this->read();
  80. }
  81. }
  82. /**
  83. * Clears $new_* arrays
  84. *
  85. * @author - Olivier Brouckaert
  86. */
  87. function cancel()
  88. {
  89. $this->new_answer = array();
  90. $this->new_correct = array();
  91. $this->new_comment = array();
  92. $this->new_weighting = array();
  93. $this->new_position = array();
  94. $this->new_hotspot_coordinates = array();
  95. $this->new_hotspot_type = array();
  96. $this->new_nbrAnswers = 0;
  97. $this->new_destination = array();
  98. }
  99. /**
  100. * Reads answer informations from the data base
  101. *
  102. * @author - Olivier Brouckaert
  103. */
  104. function read()
  105. {
  106. $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
  107. $questionId = $this->questionId;
  108. $sql = "SELECT id, id_auto, answer,correct,comment,ponderation, position, hotspot_coordinates, hotspot_type, destination FROM
  109. $TBL_ANSWER WHERE c_id = {$this->course_id} AND question_id ='".$questionId."' ORDER BY position";
  110. $result = Database::query($sql);
  111. $i = 1;
  112. // while a record is found
  113. while ($object = Database::fetch_object($result)) {
  114. $this->id[$i] = $object->id;
  115. $this->answer[$i] = $object->answer;
  116. $this->correct[$i] = $object->correct;
  117. $this->comment[$i] = $object->comment;
  118. $this->weighting[$i] = $object->ponderation;
  119. $this->position[$i] = $object->position;
  120. $this->hotspot_coordinates[$i] = $object->hotspot_coordinates;
  121. $this->hotspot_type[$i] = $object->hotspot_type;
  122. $this->destination[$i] = $object->destination;
  123. $this->autoId[$i] = $object->id_auto;
  124. $i++;
  125. }
  126. $this->nbrAnswers = $i - 1;
  127. }
  128. /**
  129. * reads answer informations from the data base ordered by parameter
  130. * @param string Field we want to order by
  131. * @param string DESC or ASC
  132. * @author Frederic Vauthier
  133. */
  134. function readOrderedBy($field, $order = 'ASC')
  135. {
  136. $field = Database::escape_string($field);
  137. if (empty($field)) {
  138. $field = 'position';
  139. }
  140. if ($order != 'ASC' && $order != 'DESC') {
  141. $order = 'ASC';
  142. }
  143. $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
  144. $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_QUESTION);
  145. $questionId = intval($this->questionId);
  146. $sql = "SELECT type FROM $TBL_QUIZ WHERE c_id = {$this->course_id} AND id = $questionId";
  147. $result_question = Database::query($sql);
  148. $question_type = Database::fetch_array($result_question);
  149. $sql = "SELECT answer,correct,comment,ponderation,position, hotspot_coordinates, hotspot_type, destination, id_auto ".
  150. "FROM $TBL_ANSWER WHERE c_id = {$this->course_id} AND question_id='".$questionId."' ".
  151. "ORDER BY $field $order";
  152. $result = Database::query($sql);
  153. $i = 1;
  154. // while a record is found
  155. $doubt_data = null;
  156. while ($object = Database::fetch_object($result)) {
  157. if ($question_type['type'] == UNIQUE_ANSWER_NO_OPTION && $object->position == 666) {
  158. $doubt_data = $object;
  159. continue;
  160. }
  161. $this->answer[$i] = $object->answer;
  162. $this->correct[$i] = $object->correct;
  163. $this->comment[$i] = $object->comment;
  164. $this->weighting[$i] = $object->ponderation;
  165. $this->position[$i] = $object->position;
  166. $this->destination[$i] = $object->destination;
  167. $this->autoId[$i] = $object->id_auto;
  168. $i++;
  169. }
  170. if ($question_type['type'] == UNIQUE_ANSWER_NO_OPTION && !empty($doubt_data)) {
  171. $this->answer[$i] = $doubt_data->answer;
  172. $this->correct[$i] = $doubt_data->correct;
  173. $this->comment[$i] = $doubt_data->comment;
  174. $this->weighting[$i] = $doubt_data->ponderation;
  175. $this->position[$i] = $doubt_data->position;
  176. $this->destination[$i] = $doubt_data->destination;
  177. $this->autoId[$i] = $doubt_data->id_auto;
  178. $i++;
  179. }
  180. $this->nbrAnswers = $i - 1;
  181. }
  182. /**
  183. * returns the autoincrement id identificator
  184. *
  185. * @author - Juan Carlos Ra�a
  186. * @return - integer - answer num
  187. */
  188. function selectAutoId($id)
  189. {
  190. return $this->autoId[$id];
  191. }
  192. /**
  193. * returns the number of answers in this question
  194. *
  195. * @author - Olivier Brouckaert
  196. * @return - integer - number of answers
  197. */
  198. function selectNbrAnswers()
  199. {
  200. return $this->nbrAnswers;
  201. }
  202. /**
  203. * returns the question ID which the answers belong to
  204. *
  205. * @author - Olivier Brouckaert
  206. * @return - integer - the question ID
  207. */
  208. function selectQuestionId()
  209. {
  210. return $this->questionId;
  211. }
  212. /**
  213. * returns the question ID of the destination question
  214. *
  215. * @author - Julio Montoya
  216. * @return - integer - the question ID
  217. */
  218. function selectDestination($id)
  219. {
  220. return $this->destination[$id];
  221. }
  222. /**
  223. * returns the answer title
  224. *
  225. * @author - Olivier Brouckaert
  226. * @param - integer $id - answer ID
  227. * @return - string - answer title
  228. */
  229. function selectAnswer($id)
  230. {
  231. return $this->answer[$id];
  232. }
  233. /**
  234. * return array answer by id else return a bool
  235. */
  236. function selectAnswerByAutoId($auto_id)
  237. {
  238. $TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
  239. $auto_id = intval($auto_id);
  240. $sql = "SELECT id, answer FROM $TBL_ANSWER WHERE c_id = {$this->course_id} AND id_auto='$auto_id'";
  241. $rs = Database::query($sql);
  242. if (Database::num_rows($rs) > 0) {
  243. $row = Database::fetch_array($rs);
  244. return $row;
  245. }
  246. return false;
  247. }
  248. /**
  249. * returns the answer title from an answer's position
  250. *
  251. * @author - Yannick Warnier
  252. * @param - integer $id - answer ID
  253. * @return - bool - answer title
  254. */
  255. function selectAnswerIdByPosition($pos)
  256. {
  257. foreach ($this->position as $k => $v) {
  258. if ($v != $pos) {
  259. continue;
  260. }
  261. return $k;
  262. }
  263. return false;
  264. }
  265. /**
  266. * Returns a list of answers
  267. * @author Yannick Warnier <ywarnier@beeznest.org>
  268. * @return array List of answers where each answer is an array of (id, answer, comment, grade) and grade=weighting
  269. */
  270. function getAnswersList($decode = false)
  271. {
  272. $list = array();
  273. for ($i = 1; $i <= $this->nbrAnswers; $i++) {
  274. if (!empty($this->answer[$i])) {
  275. //Avoid problems when parsing elements with accents
  276. if ($decode) {
  277. $this->answer[$i] = api_html_entity_decode(
  278. $this->answer[$i],
  279. ENT_QUOTES,
  280. api_get_system_encoding()
  281. );
  282. $this->comment[$i] = api_html_entity_decode(
  283. $this->comment[$i],
  284. ENT_QUOTES,
  285. api_get_system_encoding()
  286. );
  287. }
  288. $list[] = array(
  289. 'id' => $i,
  290. 'answer' => $this->answer[$i],
  291. 'comment' => $this->comment[$i],
  292. 'grade' => $this->weighting[$i],
  293. 'hotspot_coord' => $this->hotspot_coordinates[$i],
  294. 'hotspot_type' => $this->hotspot_type[$i],
  295. 'correct' => $this->correct[$i],
  296. 'destination' => $this->destination[$i]
  297. );
  298. }
  299. }
  300. return $list;
  301. }
  302. /**
  303. * Returns a list of grades
  304. * @author Yannick Warnier <ywarnier@beeznest.org>
  305. * @return array List of grades where grade=weighting (?)
  306. */
  307. function getGradesList()
  308. {
  309. $list = array();
  310. for ($i = 0; $i < $this->nbrAnswers; $i++) {
  311. if (!empty($this->answer[$i])) {
  312. $list[$i] = $this->weighting[$i];
  313. }
  314. }
  315. return $list;
  316. }
  317. /**
  318. * Returns the question type
  319. * @author Yannick Warnier <ywarnier@beeznest.org>
  320. * @return integer The type of the question this answer is bound to
  321. */
  322. function getQuestionType()
  323. {
  324. $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
  325. $sql = "SELECT type FROM $TBL_QUESTIONS WHERE c_id = {$this->course_id} AND id = '".$this->questionId."'";
  326. $res = Database::query($sql);
  327. if (Database::num_rows($res) <= 0) {
  328. return null;
  329. }
  330. $row = Database::fetch_array($res);
  331. return $row['type'];
  332. }
  333. /**
  334. * tells if answer is correct or not
  335. *
  336. * @author - Olivier Brouckaert
  337. * @param - integer $id - answer ID
  338. * @return - integer - 0 if bad answer, not 0 if good answer
  339. */
  340. function isCorrect($id)
  341. {
  342. return $this->correct[$id];
  343. }
  344. /**
  345. * returns answer comment
  346. *
  347. * @author - Olivier Brouckaert
  348. * @param - integer $id - answer ID
  349. * @return - string - answer comment
  350. */
  351. function selectComment($id)
  352. {
  353. return $this->comment[$id];
  354. }
  355. /**
  356. * returns answer weighting
  357. *
  358. * @author - Olivier Brouckaert
  359. * @param - integer $id - answer ID
  360. * @return - integer - answer weighting
  361. */
  362. function selectWeighting($id)
  363. {
  364. return $this->weighting[$id];
  365. }
  366. /**
  367. * returns answer position
  368. *
  369. * @author - Olivier Brouckaert
  370. * @param - integer $id - answer ID
  371. * @return - integer - answer position
  372. */
  373. function selectPosition($id)
  374. {
  375. return $this->position[$id];
  376. }
  377. /**
  378. * returns answer hotspot coordinates
  379. *
  380. * @author Olivier Brouckaert
  381. * @param integer Answer ID
  382. * @return integer Answer position
  383. */
  384. function selectHotspotCoordinates($id)
  385. {
  386. return $this->hotspot_coordinates[$id];
  387. }
  388. /**
  389. * returns answer hotspot type
  390. *
  391. * @author Toon Keppens
  392. * @param integer Answer ID
  393. * @return integer Answer position
  394. */
  395. function selectHotspotType($id)
  396. {
  397. return $this->hotspot_type[$id];
  398. }
  399. /**
  400. * creates a new answer
  401. *
  402. * @author Olivier Brouckaert
  403. * @param string answer title
  404. * @param integer 0 if bad answer, not 0 if good answer
  405. * @param string answer comment
  406. * @param integer answer weighting
  407. * @param integer answer position
  408. * @param coordinates Coordinates for hotspot exercises (optional)
  409. * @param integer Type for hotspot exercises (optional)
  410. */
  411. function createAnswer(
  412. $answer,
  413. $correct,
  414. $comment,
  415. $weighting,
  416. $position,
  417. $new_hotspot_coordinates = null,
  418. $new_hotspot_type = null,
  419. $destination = ''
  420. ) {
  421. $this->new_nbrAnswers++;
  422. $id = $this->new_nbrAnswers;
  423. $this->new_answer[$id] = $answer;
  424. $this->new_correct[$id] = $correct;
  425. $this->new_comment[$id] = $comment;
  426. $this->new_weighting[$id] = $weighting;
  427. $this->new_position[$id] = $position;
  428. $this->new_hotspot_coordinates[$id] = $new_hotspot_coordinates;
  429. $this->new_hotspot_type[$id] = $new_hotspot_type;
  430. $this->new_destination[$id] = $destination;
  431. }
  432. /**
  433. * updates an answer
  434. *
  435. * @author Toon Keppens
  436. * @param string Answer title
  437. * @param string Answer comment
  438. * @param integer Answer weighting
  439. * @param integer Answer position
  440. */
  441. function updateAnswers($answer, $comment, $weighting, $position, $destination)
  442. {
  443. $TBL_REPONSES = Database :: get_course_table(TABLE_QUIZ_ANSWER);
  444. $questionId = $this->questionId;
  445. $sql = "UPDATE $TBL_REPONSES SET
  446. answer = '".Database::escape_string($answer)."',
  447. comment = '".Database::escape_string($comment)."',
  448. ponderation = '".Database::escape_string($weighting)."',
  449. position = '".Database::escape_string($position)."',
  450. destination = '".Database::escape_string($destination)."'
  451. WHERE c_id = {$this->course_id} AND id = '".Database::escape_string($position)."'
  452. AND question_id = '".Database::escape_string($questionId)."'";
  453. Database::query($sql);
  454. }
  455. /**
  456. * Records answers into the data base
  457. *
  458. * @author - Olivier Brouckaert
  459. */
  460. function save()
  461. {
  462. $TBL_REPONSES = Database :: get_course_table(TABLE_QUIZ_ANSWER);
  463. $questionId = intval($this->questionId);
  464. // removes old answers before inserting of new ones
  465. $sql = "DELETE FROM $TBL_REPONSES WHERE c_id = {$this->course_id} AND question_id = '".($questionId)."'";
  466. Database::query($sql);
  467. $c_id = $this->course['real_id'];
  468. // inserts new answers into data base
  469. $sql = "INSERT INTO $TBL_REPONSES (c_id, id, question_id, answer, correct, comment, ponderation, position, hotspot_coordinates,hotspot_type, destination) VALUES ";
  470. for ($i = 1; $i <= $this->new_nbrAnswers; $i++) {
  471. $answer = Database::escape_string($this->new_answer[$i]);
  472. $correct = Database::escape_string($this->new_correct[$i]);
  473. $comment = Database::escape_string($this->new_comment[$i]);
  474. $weighting = Database::escape_string($this->new_weighting[$i]);
  475. $position = Database::escape_string($this->new_position[$i]);
  476. $hotspot_coordinates = Database::escape_string($this->new_hotspot_coordinates[$i]);
  477. $hotspot_type = Database::escape_string($this->new_hotspot_type[$i]);
  478. $destination = Database::escape_string($this->new_destination[$i]);
  479. $sql .= "($c_id, '$i','$questionId','$answer','$correct','$comment','$weighting','$position','$hotspot_coordinates','$hotspot_type','$destination'),";
  480. }
  481. $sql = api_substr($sql, 0, -1);
  482. Database::query($sql);
  483. // moves $new_* arrays
  484. $this->answer = $this->new_answer;
  485. $this->correct = $this->new_correct;
  486. $this->comment = $this->new_comment;
  487. $this->weighting = $this->new_weighting;
  488. $this->position = $this->new_position;
  489. $this->hotspot_coordinates = $this->new_hotspot_coordinates;
  490. $this->hotspot_type = $this->new_hotspot_type;
  491. $this->nbrAnswers = $this->new_nbrAnswers;
  492. $this->destination = $this->new_destination;
  493. // clears $new_* arrays
  494. $this->cancel();
  495. }
  496. /**
  497. * Duplicates answers by copying them into another question
  498. *
  499. * @author Olivier Brouckaert
  500. * @param int question id
  501. * @param array destination course info (result of the function api_get_course_info() )
  502. */
  503. function duplicate($newQuestionId, $course_info = null)
  504. {
  505. if (empty($course_info)) {
  506. $course_info = $this->course;
  507. } else {
  508. $course_info = $course_info;
  509. }
  510. $TBL_REPONSES = Database :: get_course_table(TABLE_QUIZ_ANSWER);
  511. if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE || self::getQuestionType(
  512. ) == MULTIPLE_ANSWER_TRUE_FALSE
  513. ) {
  514. //Selecting origin options
  515. $origin_options = Question::readQuestionOption($this->selectQuestionId(), $this->course['real_id']);
  516. if (!empty($origin_options)) {
  517. foreach ($origin_options as $item) {
  518. $new_option_list[] = $item['id'];
  519. }
  520. }
  521. $destination_options = Question::readQuestionOption($newQuestionId, $course_info['real_id']);
  522. $i = 0;
  523. $fixed_list = array();
  524. if (!empty($destination_options)) {
  525. foreach ($destination_options as $item) {
  526. $fixed_list[$new_option_list[$i]] = $item['id'];
  527. $i++;
  528. }
  529. }
  530. }
  531. // if at least one answer
  532. if ($this->nbrAnswers) {
  533. // inserts new answers into data base
  534. $sql = "INSERT INTO $TBL_REPONSES (c_id, id,question_id,answer,correct,comment, ponderation,position,hotspot_coordinates,hotspot_type,destination) VALUES";
  535. $c_id = $course_info['real_id'];
  536. for ($i = 1; $i <= $this->nbrAnswers; $i++) {
  537. if ($this->course['id'] != $course_info['id']) {
  538. $this->answer[$i] = DocumentManager::replace_urls_inside_content_html_from_copy_course(
  539. $this->answer[$i],
  540. $this->course['id'],
  541. $course_info['id']
  542. );
  543. $this->comment[$i] = DocumentManager::replace_urls_inside_content_html_from_copy_course(
  544. $this->comment[$i],
  545. $this->course['id'],
  546. $course_info['id']
  547. );
  548. }
  549. $answer = Database::escape_string($this->answer[$i]);
  550. $correct = Database::escape_string($this->correct[$i]);
  551. if (self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE || self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE
  552. ) {
  553. $correct = $fixed_list[intval($correct)];
  554. }
  555. $comment = Database::escape_string($this->comment[$i]);
  556. $weighting = Database::escape_string($this->weighting[$i]);
  557. $position = Database::escape_string($this->position[$i]);
  558. $hotspot_coordinates = Database::escape_string($this->hotspot_coordinates[$i]);
  559. $hotspot_type = Database::escape_string($this->hotspot_type[$i]);
  560. $destination = Database::escape_string($this->destination[$i]);
  561. $sql .= "($c_id, '$i','$newQuestionId','$answer','$correct','$comment',"."'$weighting','$position','$hotspot_coordinates','$hotspot_type','$destination'),";
  562. }
  563. $sql = api_substr($sql, 0, -1);
  564. Database::query($sql);
  565. }
  566. }
  567. function getJs() {
  568. //if ($this->questionId == 2)
  569. echo '<script>jsPlumb.ready(function() {
  570. if ($("#drag'.$this->questionId.'_question").length > 0) {
  571. jsPlumbDemo.init('.$this->questionId.');
  572. }
  573. });</script>';
  574. }
  575. }