MultipleAnswerTrueFalseDegreeCertainty.php 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use ChamiloSession as Session;
  4. /**
  5. * Class MultipleAnswerTrueFalseDegreeCertainty
  6. * This class allows to instantiate an object of type MULTIPLE_ANSWER
  7. * (MULTIPLE CHOICE, MULTIPLE ANSWER), extending the class question.
  8. *
  9. * @package chamilo.exercise
  10. */
  11. class MultipleAnswerTrueFalseDegreeCertainty extends Question
  12. {
  13. const LEVEL_DARKGREEN = 1;
  14. const LEVEL_LIGHTGREEN = 2;
  15. const LEVEL_WHITE = 3;
  16. const LEVEL_LIGHTRED = 4;
  17. const LEVEL_DARKRED = 5;
  18. public static $typePicture = 'mccert.png';
  19. public static $explanationLangVar = 'MultipleAnswerTrueFalseDegreeCertainty';
  20. public $optionsTitle;
  21. public $options;
  22. /**
  23. * Constructor.
  24. */
  25. public function __construct()
  26. {
  27. parent::__construct();
  28. $this->type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY;
  29. $this->isContent = $this->getIsContent();
  30. $this->optionsTitle = [1 => 'Answers', 2 => 'DegreeOfCertaintyThatMyAnswerIsCorrect'];
  31. $this->options = [
  32. 1 => 'True',
  33. 2 => 'False',
  34. 3 => '50%',
  35. 4 => '60%',
  36. 5 => '70%',
  37. 6 => '80%',
  38. 7 => '90%',
  39. 8 => '100%',
  40. ];
  41. }
  42. /**
  43. * Redefines Question::createAnswersForm: creates the HTML form to answer the question.
  44. *
  45. * @uses \globals $text and $class, defined in the calling script
  46. *
  47. * @param FormValidator $form
  48. *
  49. * @throws Exception
  50. * @throws HTML_QuickForm_Error
  51. */
  52. public function createAnswersForm($form)
  53. {
  54. global $text;
  55. $nbAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : 4;
  56. // The previous default value was 2. See task #1759.
  57. $nbAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0));
  58. $courseId = api_get_course_int_id();
  59. $objEx = Session::read('objExercise');
  60. $renderer = &$form->defaultRenderer();
  61. $defaults = [];
  62. $html = '<table class="data_table"><tr style="text-align: center;"><th>'
  63. .get_lang('Number')
  64. .'</th><th>'
  65. .get_lang('True')
  66. .'</th><th>'
  67. .get_lang('False')
  68. .'</th><th>'
  69. .get_lang('Answer')
  70. .'</th>';
  71. // show column comment when feedback is enable
  72. if ($objEx->getFeedbackType() != EXERCISE_FEEDBACK_TYPE_EXAM) {
  73. $html .= '<th>'.get_lang('Comment').'</th>';
  74. }
  75. $html .= '</tr>';
  76. $form->addElement('label', get_lang('Answers').'<br /> <img src="../img/fill_field.png">', $html);
  77. $correct = 0;
  78. $answer = null;
  79. if (!empty($this->id)) {
  80. $answer = new Answer($this->id);
  81. $answer->read();
  82. if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) {
  83. $nbAnswers = $answer->nbrAnswers;
  84. }
  85. }
  86. $form->addElement('hidden', 'nb_answers');
  87. $boxesNames = [];
  88. if ($nbAnswers < 1) {
  89. $nbAnswers = 1;
  90. echo Display::return_message(get_lang('YouHaveToCreateAtLeastOneAnswer'));
  91. }
  92. // Can be more options
  93. $optionData = Question::readQuestionOption($this->id, $courseId);
  94. for ($i = 1; $i <= $nbAnswers; $i++) {
  95. $renderer->setElementTemplate(
  96. '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
  97. 'correct['.$i.']'
  98. );
  99. $renderer->setElementTemplate(
  100. '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
  101. 'counter['.$i.']'
  102. );
  103. $renderer->setElementTemplate(
  104. '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
  105. 'answer['.$i.']'
  106. );
  107. $renderer->setElementTemplate(
  108. '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
  109. 'comment['.$i.']'
  110. );
  111. $answerNumber = $form->addElement('text', 'counter['.$i.']', null, 'value="'.$i.'"');
  112. $answerNumber->freeze();
  113. $defaults['answer['.$i.']'] = '';
  114. $defaults['comment['.$i.']'] = '';
  115. $defaults['correct['.$i.']'] = '';
  116. if (is_object($answer)) {
  117. $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : '';
  118. if (isset($_POST['answer']) && isset($_POST['answer'][$i])) {
  119. $defaults['answer['.$i.']'] = Security::remove_XSS($_POST['answer'][$i]);
  120. }
  121. $defaults['comment['.$i.']'] = isset($answer->comment[$i]) ? $answer->comment[$i] : '';
  122. if (isset($_POST['comment']) && isset($_POST['comment'][$i])) {
  123. $defaults['comment['.$i.']'] = Security::remove_XSS($_POST['comment'][$i]);
  124. }
  125. $defaults['weighting['.$i.']'] = isset($answer->weighting[$i]) ? float_format($answer->weighting[$i], 1) : '';
  126. $correct = isset($answer->correct[$i]) ? $answer->correct[$i] : '';
  127. $defaults['correct['.$i.']'] = $correct;
  128. if (isset($_POST['correct']) && isset($_POST['correct'][$i])) {
  129. $defaults['correct['.$i.']'] = Security::remove_XSS($_POST['correct'][$i]);
  130. }
  131. $j = 1;
  132. if (!empty($optionData)) {
  133. foreach ($optionData as $id => $data) {
  134. $form->addElement('radio', 'correct['.$i.']', null, null, $id);
  135. $j++;
  136. if ($j == 3) {
  137. break;
  138. }
  139. }
  140. }
  141. } else {
  142. $form->addElement('radio', 'correct['.$i.']', null, null, 1);
  143. $form->addElement('radio', 'correct['.$i.']', null, null, 2);
  144. }
  145. $boxesNames[] = 'correct['.$i.']';
  146. $form->addElement(
  147. 'html_editor',
  148. 'answer['.$i.']',
  149. null,
  150. ['style' => 'vertical-align:middle;'],
  151. ['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100']
  152. );
  153. $form->addRule('answer['.$i.']', get_lang('ThisFieldIsRequired'), 'required');
  154. // show comment when feedback is enable
  155. if ($objEx->getFeedbackType() != EXERCISE_FEEDBACK_TYPE_EXAM) {
  156. $form->addElement(
  157. 'html_editor',
  158. 'comment['.$i.']',
  159. null,
  160. ['style' => 'vertical-align:middle;'],
  161. ['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100']
  162. );
  163. }
  164. $form->addElement('html', '</tr>');
  165. }
  166. $form->addElement('html', '</table>');
  167. $form->addElement('html', '<br />');
  168. // 3 scores
  169. $form->addElement('text', 'option[1]', get_lang('Correct'), ['class' => 'span1', 'value' => '1']);
  170. $form->addElement('text', 'option[2]', get_lang('Wrong'), ['class' => 'span1', 'value' => '-0.5']);
  171. $form->addElement('hidden', 'option[3]', 0);
  172. $form->addRule('option[1]', get_lang('ThisFieldIsRequired'), 'required');
  173. $form->addRule('option[2]', get_lang('ThisFieldIsRequired'), 'required');
  174. $form->addElement('html', '</tr><table>');
  175. $form->addElement('hidden', 'options_count', 3);
  176. $form->addElement('html', '</table><br /><br />');
  177. //Extra values True, false, Dont known
  178. if (!empty($this->extra)) {
  179. $scores = explode(':', $this->extra);
  180. if (!empty($scores)) {
  181. for ($i = 1; $i <= 3; $i++) {
  182. $defaults['option['.$i.']'] = $scores[$i - 1];
  183. }
  184. }
  185. }
  186. if ($objEx->edit_exercise_in_lp === true ||
  187. (empty($this->exerciseList) && empty($objEx->id))
  188. ) {
  189. $form->addElement('submit', 'lessAnswers', get_lang('LessAnswer'), 'class="btn btn-danger minus"');
  190. $form->addElement('submit', 'moreAnswers', get_lang('PlusAnswer'), 'class="btn btn-primary plus"');
  191. //$text and $class defined in calling script
  192. $form->addElement('submit', 'submitQuestion', $text, 'class = "btn btn-primary"');
  193. }
  194. $renderer->setElementTemplate('{element}&nbsp;', 'lessAnswers');
  195. $renderer->setElementTemplate('{element}&nbsp;', 'submitQuestion');
  196. $renderer->setElementTemplate('{element}&nbsp;', 'moreAnswers');
  197. $form->addElement('html', '</div></div>');
  198. $defaults['correct'] = $correct;
  199. if (!empty($this->id)) {
  200. $form->setDefaults($defaults);
  201. } else {
  202. $form->setDefaults($defaults);
  203. }
  204. $form->setConstants(['nb_answers' => $nbAnswers]);
  205. }
  206. /**
  207. * abstract function which creates the form to create / edit the answers of the question.
  208. *
  209. * @param FormValidator $form
  210. * @param Exercise $exercise
  211. */
  212. public function processAnswersCreation($form, $exercise)
  213. {
  214. $questionWeighting = 0;
  215. $objAnswer = new Answer($this->id);
  216. $nbAnswers = $form->getSubmitValue('nb_answers');
  217. $courseId = api_get_course_int_id();
  218. $correct = [];
  219. $options = Question::readQuestionOption($this->id, $courseId);
  220. if (!empty($options)) {
  221. foreach ($options as $optionData) {
  222. $id = $optionData['id'];
  223. unset($optionData['id']);
  224. Question::updateQuestionOption($id, $optionData, $courseId);
  225. }
  226. } else {
  227. for ($i = 1; $i <= 8; $i++) {
  228. $lastId = Question::saveQuestionOption($this->id, $this->options[$i], $courseId, $i);
  229. $correct[$i] = $lastId;
  230. }
  231. }
  232. /* Getting quiz_question_options (true, false, doubt) because
  233. it's possible that there are more options in the future */
  234. $newOptions = Question::readQuestionOption($this->id, $courseId);
  235. $sortedByPosition = [];
  236. foreach ($newOptions as $item) {
  237. $sortedByPosition[$item['position']] = $item;
  238. }
  239. /* Saving quiz_question.extra values that has the correct scores of
  240. the true, false, doubt options registered in this format
  241. XX:YY:ZZZ where XX is a float score value. */
  242. $extraValues = [];
  243. for ($i = 1; $i <= 3; $i++) {
  244. $score = trim($form->getSubmitValue('option['.$i.']'));
  245. $extraValues[] = $score;
  246. }
  247. $this->setExtra(implode(':', $extraValues));
  248. for ($i = 1; $i <= $nbAnswers; $i++) {
  249. $answer = trim($form->getSubmitValue('answer['.$i.']'));
  250. $comment = trim($form->getSubmitValue('comment['.$i.']'));
  251. $goodAnswer = trim($form->getSubmitValue('correct['.$i.']'));
  252. if (empty($options)) {
  253. // If this is the first time that the question is created then change
  254. // the default values from the form 1 and 2 by the correct "option id" registered
  255. $goodAnswer = $sortedByPosition[$goodAnswer]['id'];
  256. }
  257. $questionWeighting += $extraValues[0]; //By default 0 has the correct answers
  258. $objAnswer->createAnswer($answer, $goodAnswer, $comment, '', $i);
  259. }
  260. // saves the answers into the data base
  261. $objAnswer->save();
  262. // sets the total weighting of the question
  263. $this->updateWeighting($questionWeighting);
  264. $this->save($exercise);
  265. }
  266. /**
  267. * {@inheritdoc}
  268. */
  269. public function return_header(Exercise $exercise, $counter = null, $score = [])
  270. {
  271. $header = parent::return_header($exercise, $counter, $score);
  272. $header .= '<table class="'.$this->question_table_class.'"><tr>';
  273. $header .= '<th>'.get_lang('Choice').'</th>';
  274. if ($exercise->showExpectedChoiceColumn()) {
  275. $header .= '<th>'.get_lang('ExpectedChoice').'</th>';
  276. }
  277. $header .= '<th>'
  278. .get_lang('Answer')
  279. .'</th><th colspan="2" style="text-align:center;">'
  280. .get_lang('YourDegreeOfCertainty')
  281. .'</th>'
  282. ;
  283. if ($exercise->getFeedbackType() != EXERCISE_FEEDBACK_TYPE_EXAM) {
  284. $header .= '<th>'.get_lang('Comment').'</th>';
  285. } else {
  286. $header .= '<th>&nbsp;</th>';
  287. }
  288. $header .= '</tr>';
  289. return $header;
  290. }
  291. /**
  292. * Get color code, status, label and description for the current answer.
  293. *
  294. * @param string $studentAnswer
  295. * @param string $expectedAnswer
  296. * @param int $studentDegreeChoicePosition
  297. *
  298. * @return array An array with indexes 'color', 'background-color', 'status', 'label' and 'description'
  299. */
  300. public function getResponseDegreeInfo($studentAnswer, $expectedAnswer, $studentDegreeChoicePosition)
  301. {
  302. $result = [];
  303. if ($studentDegreeChoicePosition == 3) {
  304. $result = [
  305. 'color' => '#000000',
  306. 'background-color' => '#F6BA2A',
  307. 'status' => self::LEVEL_WHITE,
  308. 'label' => get_lang('DegreeOfCertaintyDeclaredIgnorance'),
  309. 'description' => get_lang('DegreeOfCertaintyDeclaredIgnoranceDescription'),
  310. ];
  311. } else {
  312. $checkResult = $studentAnswer == $expectedAnswer ? true : false;
  313. if ($checkResult) {
  314. if ($studentDegreeChoicePosition >= 6) {
  315. $result = [
  316. 'color' => '#FFFFFF',
  317. 'background-color' => '#1E9C55',
  318. 'status' => self::LEVEL_DARKGREEN,
  319. 'label' => get_lang('DegreeOfCertaintyVerySure'),
  320. 'description' => get_lang('DegreeOfCertaintyVerySureDescription'),
  321. ];
  322. } elseif ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) {
  323. $result = [
  324. 'color' => '#000000',
  325. 'background-color' => '#B1E183',
  326. 'status' => self::LEVEL_LIGHTGREEN,
  327. 'label' => get_lang('DegreeOfCertaintyPrettySure'),
  328. 'description' => get_lang('DegreeOfCertaintyPrettySureDescription'),
  329. ];
  330. }
  331. } else {
  332. if ($studentDegreeChoicePosition >= 6) {
  333. $result = [
  334. 'color' => '#FFFFFF',
  335. 'background-color' => '#ED4040',
  336. 'status' => self::LEVEL_DARKRED,
  337. 'label' => get_lang('DegreeOfCertaintyVeryUnsure'),
  338. 'description' => get_lang('DegreeOfCertaintyVeryUnsureDescription'),
  339. ];
  340. } elseif ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) {
  341. $result = [
  342. 'color' => '#000000',
  343. 'background-color' => '#F79B88',
  344. 'status' => self::LEVEL_LIGHTRED,
  345. 'label' => get_lang('DegreeOfCertaintyUnsure'),
  346. 'description' => get_lang('DegreeOfCertaintyUnsureDescription'),
  347. ];
  348. }
  349. }
  350. }
  351. return $result;
  352. }
  353. /**
  354. * Method to show the code color and his meaning for the test result.
  355. */
  356. public static function showColorCodes()
  357. {
  358. ?>
  359. <table class="fc-border-separate" cellspacing="0" style="width:600px;
  360. margin: auto; border: 3px solid #A39E9E;" >
  361. <tr style="border-bottom: 1px solid #A39E9E;">
  362. <td style="width:15%; height:30px; background-color: #088A08; border-right: 1px solid #A39E9E;">
  363. &nbsp;
  364. </td>
  365. <td style="padding-left:10px;">
  366. <b><?php echo get_lang('DegreeOfCertaintyVerySure'); ?> :</b>
  367. <?php echo get_lang('DegreeOfCertaintyVerySureDescription'); ?>
  368. </td>
  369. </tr>
  370. <tr style="border-bottom: 1px solid #A39E9E;">
  371. <td style="width:15%; height:30px; background-color: #A9F5A9; border-right: 1px solid #A39E9E;">
  372. &nbsp;
  373. </td>
  374. <td style="padding-left:10px;">
  375. <b><?php echo get_lang('DegreeOfCertaintyPrettySure'); ?> :</b>
  376. <?php echo get_lang('DegreeOfCertaintyPrettySureDescription'); ?>
  377. </td>
  378. </tr>
  379. <tr style="border: 1px solid #A39E9E;">
  380. <td style="width:15%; height:30px; background-color: #FFFFFF; border-right: 1px solid #A39E9E;">
  381. &nbsp;
  382. </td>
  383. <td style="padding-left:10px;">
  384. <b><?php echo get_lang('DegreeOfCertaintyDeclaredIgnorance'); ?> :</b>
  385. <?php echo get_lang('DegreeOfCertaintyDeclaredIgnoranceDescription'); ?>
  386. </td>
  387. </tr>
  388. <tr style="border: 1px solid #A39E9E;">
  389. <td style="width:15%; height:30px; background-color: #F6CECE; border-right: 1px solid #A39E9E;">
  390. &nbsp;
  391. </td>
  392. <td style="padding-left:10px;">
  393. <b><?php echo get_lang('DegreeOfCertaintyUnsure'); ?> :</b>
  394. <?php echo get_lang('DegreeOfCertaintyUnsureDescription'); ?>
  395. </td>
  396. </tr>
  397. <tr style="border-bottom: 1px solid #A39E9E;">
  398. <td style="width:15%; height:30px; background-color: #FE2E2E; border-right: 1px solid #A39E9E;">
  399. &nbsp;
  400. </td>
  401. <td style="padding-left:10px;">
  402. <b><?php echo get_lang('DegreeOfCertaintyVeryUnsure'); ?> :</b>
  403. <?php echo get_lang('DegreeOfCertaintyVeryUnsureDescription'); ?>
  404. </td>
  405. </tr>
  406. </table><br/>
  407. <?php
  408. }
  409. /**
  410. * Display basic bar charts of results by category of questions.
  411. *
  412. * @param array $scoreListAll
  413. * @param string $title The block title
  414. * @param int $sizeRatio
  415. *
  416. * @return string The HTML/CSS code for the charts block
  417. */
  418. public static function displayDegreeChartByCategory($scoreListAll, $title, $sizeRatio = 1)
  419. {
  420. $maxHeight = 0;
  421. $groupCategoriesByBracket = false;
  422. if ($groupCategoriesByBracket) {
  423. $scoreList = [];
  424. $categoryPrefixList = [];
  425. // categoryPrefix['Math'] = firstCategoryId for this prefix
  426. // rebuild $scoreList factorizing data with category prefix
  427. foreach ($scoreListAll as $categoryId => $scoreListForCategory) {
  428. $objCategory = new Testcategory();
  429. $objCategoryNum = $objCategory->getCategory($categoryId);
  430. preg_match("/^\[([^]]+)\]/", $objCategoryNum->name, $matches);
  431. if (count($matches) > 1) {
  432. // check if we have already see this prefix
  433. if (array_key_exists($matches[1], $categoryPrefixList)) {
  434. // add the result color for this entry
  435. $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_DARKGREEN] +=
  436. $scoreListForCategory[self::LEVEL_DARKGREEN];
  437. $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_LIGHTGREEN] +=
  438. $scoreListForCategory[self::LEVEL_LIGHTGREEN];
  439. $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_WHITE] +=
  440. $scoreListForCategory[self::LEVEL_WHITE];
  441. $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_LIGHTRED] +=
  442. $scoreListForCategory[self::LEVEL_LIGHTRED];
  443. $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_DARKRED] +=
  444. $scoreListForCategory[self::LEVEL_DARKRED];
  445. } else {
  446. $categoryPrefixList[$matches[1]] = $categoryId;
  447. $scoreList[$categoryId] = $scoreListAll[$categoryId];
  448. }
  449. } else {
  450. // doesn't match the prefix '[math] Math category'
  451. $scoreList[$categoryId] = $scoreListAll[$categoryId];
  452. }
  453. }
  454. } else {
  455. $scoreList = $scoreListAll;
  456. }
  457. // get the max height of item to have each table the same height if displayed side by side
  458. $testCategory = new TestCategory();
  459. foreach ($scoreList as $categoryId => $scoreListForCategory) {
  460. $category = $testCategory->getCategory($categoryId);
  461. if ($category) {
  462. $categoryQuestionName = $category->name;
  463. }
  464. list($noValue, $height) = self::displayDegreeChartChildren(
  465. $scoreListForCategory,
  466. 300,
  467. '',
  468. 1,
  469. 0,
  470. false,
  471. true,
  472. 0
  473. );
  474. if ($height > $maxHeight) {
  475. $maxHeight = $height;
  476. }
  477. }
  478. if (count($scoreList) > 1) {
  479. $boxWidth = $sizeRatio * 300 * 2 + 54;
  480. } else {
  481. $boxWidth = $sizeRatio * 300 + 54;
  482. }
  483. $html = '<div class="row-chart">';
  484. $html .= '<h4 class="chart-title">'.$title.'</h4>';
  485. $legendTitle = [
  486. 'DegreeOfCertaintyVeryUnsure',
  487. 'DegreeOfCertaintyUnsure',
  488. 'DegreeOfCertaintyDeclaredIgnorance',
  489. 'DegreeOfCertaintyPrettySure',
  490. 'DegreeOfCertaintyVerySure',
  491. ];
  492. $html .= '<ul class="chart-legend">';
  493. foreach ($legendTitle as $i => $item) {
  494. $html .= '<li><i class="fa fa-square square_color'.$i.'" aria-hidden="true"></i> '.get_lang($item).'</li>';
  495. }
  496. $html .= '</ul>';
  497. // get the html of items
  498. $i = 0;
  499. $testCategory = new Testcategory();
  500. foreach ($scoreList as $categoryId => $scoreListForCategory) {
  501. $category = $testCategory->getCategory($categoryId);
  502. $categoryQuestionName = '';
  503. if ($category) {
  504. $categoryQuestionName = $category->name;
  505. }
  506. if ($categoryQuestionName === '') {
  507. $categoryName = get_lang('WithoutCategory');
  508. } else {
  509. $categoryName = $categoryQuestionName;
  510. }
  511. $html .= '<div class="col-md-4">';
  512. $html .= self::displayDegreeChartChildren(
  513. $scoreListForCategory,
  514. 300,
  515. $categoryName,
  516. 1,
  517. $maxHeight,
  518. false,
  519. false,
  520. $groupCategoriesByBracket
  521. );
  522. $html .= '</div>';
  523. if ($i == 2) {
  524. $html .= '<div style="clear:both; height: 10px;">&nbsp;</div>';
  525. $i = 0;
  526. } else {
  527. $i++;
  528. }
  529. }
  530. $html .= '</div>';
  531. return $html.'<div style="clear:both; height: 10px;" >&nbsp;</div>';
  532. }
  533. /**
  534. * Return HTML code for the $scoreList of MultipleAnswerTrueFalseDegreeCertainty questions.
  535. *
  536. * @param $scoreList
  537. * @param $widthTable
  538. * @param string $title
  539. * @param int $sizeRatio
  540. * @param int $minHeight
  541. * @param bool $displayExplanationText
  542. * @param bool $returnHeight
  543. * @param bool $groupCategoriesByBracket
  544. * @param int $numberOfQuestions
  545. *
  546. * @return array|string
  547. */
  548. public static function displayDegreeChart(
  549. $scoreList,
  550. $widthTable,
  551. $title = '',
  552. $sizeRatio = 1,
  553. $minHeight = 0,
  554. $displayExplanationText = true,
  555. $returnHeight = false,
  556. $groupCategoriesByBracket = false,
  557. $numberOfQuestions = 0
  558. ) {
  559. $topAndBottomMargin = 10;
  560. $colorList = [
  561. self::LEVEL_DARKRED,
  562. self::LEVEL_LIGHTRED,
  563. self::LEVEL_WHITE,
  564. self::LEVEL_LIGHTGREEN,
  565. self::LEVEL_DARKGREEN,
  566. ];
  567. // get total attempt number
  568. $highterColorHeight = 0;
  569. foreach ($scoreList as $color => $number) {
  570. if ($number > $highterColorHeight) {
  571. $highterColorHeight = $number;
  572. }
  573. }
  574. $totalAttemptNumber = $numberOfQuestions;
  575. $verticalLineHeight = $highterColorHeight * $sizeRatio * 2 + 122 + $topAndBottomMargin * 2;
  576. if ($verticalLineHeight < $minHeight) {
  577. $minHeightCorrection = $minHeight - $verticalLineHeight;
  578. $verticalLineHeight += $minHeightCorrection;
  579. }
  580. // draw chart
  581. $html = '';
  582. if ($groupCategoriesByBracket) {
  583. $title = api_preg_replace("/[^]]*$/", '', $title);
  584. $title = ucfirst(api_preg_replace("/[\[\]]/", '', $title));
  585. }
  586. $titleDisplay = (strpos($title, "ensemble") > 0) ?
  587. $title."<br/>($totalAttemptNumber questions)" :
  588. $title;
  589. $textSize = (
  590. strpos($title, 'ensemble') > 0 ||
  591. strpos($title, 'votre dernier résultat à ce test') > 0
  592. ) ? 100 : 80;
  593. $html .= '<div class="row-chart">';
  594. $html .= '<h4 class="chart-title">'.$titleDisplay.'</h4>';
  595. $nbResponsesInc = 0;
  596. if (isset($scoreList[4])) {
  597. $nbResponsesInc += (int) $scoreList[4];
  598. }
  599. if (isset($scoreList[5])) {
  600. $nbResponsesInc += (int) $scoreList[5];
  601. }
  602. $nbResponsesIng = isset($scoreList[3]) ? $scoreList[3] : 0;
  603. $nbResponsesCor = 0;
  604. if (isset($scoreList[1])) {
  605. $nbResponsesCor += (int) $scoreList[1];
  606. }
  607. if (isset($scoreList[2])) {
  608. $nbResponsesCor += (int) $scoreList[2];
  609. }
  610. $IncorrectAnswers = sprintf(get_lang('IncorrectAnswersX'), $nbResponsesInc);
  611. $IgnoranceAnswers = sprintf(get_lang('IgnoranceAnswersX'), $nbResponsesIng);
  612. $CorrectAnswers = sprintf(get_lang('CorrectAnswersX'), $nbResponsesCor);
  613. $html .= '<div class="chart-grid">';
  614. $explainHistoList = null;
  615. if ($displayExplanationText) {
  616. // Display of histogram text
  617. $explainHistoList = [
  618. 'DegreeOfCertaintyVeryUnsure',
  619. 'DegreeOfCertaintyUnsure',
  620. 'DegreeOfCertaintyDeclaredIgnorance',
  621. 'DegreeOfCertaintyPrettySure',
  622. 'DegreeOfCertaintyVerySure',
  623. ];
  624. }
  625. foreach ($colorList as $i => $color) {
  626. if (array_key_exists($color, $scoreList)) {
  627. $scoreOnBottom = $scoreList[$color]; // height of the colored area on the bottom
  628. } else {
  629. $scoreOnBottom = 0;
  630. }
  631. $sizeBar = ($scoreOnBottom * $sizeRatio * 2).'px;';
  632. if ($i == 0) {
  633. $html .= '<div class="item">';
  634. $html .= '<div class="panel-certaint" style="min-height:'.$verticalLineHeight.'px; position: relative;">';
  635. $html .= '<div class="answers-title">'.$IncorrectAnswers.'</div>';
  636. $html .= '<ul class="certaint-list-two">';
  637. } elseif ($i == 3) {
  638. $html .= '<div class="item">';
  639. $html .= '<div class="panel-certaint" style="height:'.$verticalLineHeight.'px; position: relative;">';
  640. $html .= '<div class="answers-title">'.$CorrectAnswers.'</div>';
  641. $html .= '<ul class="certaint-list-two">';
  642. } elseif ($i == 2) {
  643. $html .= '<div class="item">';
  644. $html .= '<div class="panel-certaint" style="height:'.$verticalLineHeight.'px; position: relative;">';
  645. $html .= '<div class="answers-title">'.$IgnoranceAnswers.'</div>';
  646. $html .= '<ul class="certaint-list">';
  647. }
  648. $html .= '<li>';
  649. $html .= '<div class="certaint-score">';
  650. $html .= $scoreOnBottom;
  651. $html .= '</div>';
  652. $html .= '<div class="levelbar_'.$color.'" style="height:'.$sizeBar.'">&nbsp;</div>';
  653. $html .= '<div class="certaint-text">'.get_lang($explainHistoList[$i]).'</div>';
  654. $html .= '</li>';
  655. if ($i == 1 || $i == 2 || $i == 4) {
  656. $html .= '</ul>';
  657. $html .= '</div>';
  658. $html .= '</div>';
  659. }
  660. }
  661. $html .= '</div>';
  662. $html .= '</div>';
  663. if ($returnHeight) {
  664. return [$html, $verticalLineHeight];
  665. } else {
  666. return $html;
  667. }
  668. }
  669. /**
  670. * Return HTML code for the $scoreList of MultipleAnswerTrueFalseDegreeCertainty questions.
  671. *
  672. * @param $scoreList
  673. * @param $widthTable
  674. * @param string $title
  675. * @param int $sizeRatio
  676. * @param int $minHeight
  677. * @param bool $displayExplanationText
  678. * @param bool $returnHeight
  679. * @param bool $groupCategoriesByBracket
  680. * @param int $numberOfQuestions
  681. *
  682. * @return array|string
  683. */
  684. public static function displayDegreeChartChildren(
  685. $scoreList,
  686. $widthTable,
  687. $title = '',
  688. $sizeRatio = 1,
  689. $minHeight = 0,
  690. $displayExplanationText = true,
  691. $returnHeight = false,
  692. $groupCategoriesByBracket = false,
  693. $numberOfQuestions = 0
  694. ) {
  695. $topAndBottomMargin = 10;
  696. $colorList = [
  697. self::LEVEL_DARKRED,
  698. self::LEVEL_LIGHTRED,
  699. self::LEVEL_WHITE,
  700. self::LEVEL_LIGHTGREEN,
  701. self::LEVEL_DARKGREEN,
  702. ];
  703. // get total attempt number
  704. $highterColorHeight = 0;
  705. foreach ($scoreList as $color => $number) {
  706. if ($number > $highterColorHeight) {
  707. $highterColorHeight = $number;
  708. }
  709. }
  710. $totalAttemptNumber = $numberOfQuestions;
  711. $verticalLineHeight = $highterColorHeight * $sizeRatio * 2 + 122 + $topAndBottomMargin * 2;
  712. if ($verticalLineHeight < $minHeight) {
  713. $minHeightCorrection = $minHeight - $verticalLineHeight;
  714. $verticalLineHeight += $minHeightCorrection;
  715. }
  716. // draw chart
  717. $html = '';
  718. if ($groupCategoriesByBracket) {
  719. $title = api_preg_replace("/[^]]*$/", '', $title);
  720. $title = ucfirst(api_preg_replace("/[\[\]]/", '', $title));
  721. }
  722. $textSize = 80;
  723. $classGlobalChart = '';
  724. if ($displayExplanationText) {
  725. // global chart
  726. $classGlobalChart = 'globalChart';
  727. }
  728. $html .= '<table class="certaintyTable" style="height :'.$verticalLineHeight.'px; margin-bottom: 10px;" >';
  729. $html .= '<tr><th colspan="5" class="'.$classGlobalChart.'">'
  730. .$title
  731. .'</th><tr>'
  732. ;
  733. $nbResponsesInc = 0;
  734. if (isset($scoreList[4])) {
  735. $nbResponsesInc += (int) $scoreList[4];
  736. }
  737. if (isset($scoreList[5])) {
  738. $nbResponsesInc += (int) $scoreList[5];
  739. }
  740. $nbResponsesIng = isset($scoreList[3]) ? $scoreList[3] : 0;
  741. $nbResponsesCor = 0;
  742. if (isset($scoreList[1])) {
  743. $nbResponsesCor += (int) $scoreList[1];
  744. }
  745. if (isset($scoreList[2])) {
  746. $nbResponsesCor += (int) $scoreList[2];
  747. }
  748. $colWidth = $widthTable / 5;
  749. $html .= '<tr>
  750. <td class="firstLine borderRight '.$classGlobalChart.'"
  751. colspan="2"
  752. style="width:'.($colWidth * 2).'px; line-height: 15px; font-size:'.$textSize.'%;">'.
  753. sprintf(get_lang('IncorrectAnswersX'), $nbResponsesInc).'
  754. </td>
  755. <td class="firstLine borderRight '.$classGlobalChart.'"
  756. style="width:'.$colWidth.'px; line-height: 15px; font-size :'.$textSize.'%;">'.
  757. sprintf(get_lang('IgnoranceAnswersX'), $nbResponsesIng).'
  758. </td>
  759. <td class="firstLine '.$classGlobalChart.'"
  760. colspan="2"
  761. style="width:'.($colWidth * 2).'px; line-height: 15px; font-size:'.$textSize.'%;">'.
  762. sprintf(get_lang('CorrectAnswersX'), $nbResponsesCor).'
  763. </td>
  764. </tr>';
  765. $html .= '<tr>';
  766. foreach ($colorList as $i => $color) {
  767. if (array_key_exists($color, $scoreList)) {
  768. $scoreOnBottom = $scoreList[$color]; // height of the colored area on the bottom
  769. } else {
  770. $scoreOnBottom = 0;
  771. }
  772. $sizeOnBottom = $scoreOnBottom * $sizeRatio * 2;
  773. if ($i == 1 || $i == 2) {
  774. $html .= '<td width="'
  775. .$colWidth
  776. .'px" style="border-right: 1px dotted #7FC5FF; vertical-align: bottom;font-size: '
  777. .$textSize
  778. .'%;">'
  779. ;
  780. } else {
  781. $html .= '<td width="'
  782. .$colWidth
  783. .'px" style="vertical-align: bottom;font-size: '
  784. .$textSize
  785. .'%;">'
  786. ;
  787. }
  788. $html .= '<div class="certaint-score">'
  789. .$scoreOnBottom
  790. .'</div><div class="levelbar_'
  791. .$color
  792. .'" style="height: '
  793. .$sizeOnBottom
  794. .'px;">&nbsp;</div>'
  795. ;
  796. $html .= '</td>';
  797. }
  798. $html .= '</tr>';
  799. if ($displayExplanationText) {
  800. // Display of histogram text
  801. $explainHistoList = [
  802. 'DegreeOfCertaintyVeryUnsure',
  803. 'DegreeOfCertaintyUnsure',
  804. 'DegreeOfCertaintyDeclaredIgnorance',
  805. 'DegreeOfCertaintyPrettySure',
  806. 'DegreeOfCertaintyVerySure',
  807. ];
  808. $html .= '<tr>';
  809. $i = 0;
  810. foreach ($explainHistoList as $explain) {
  811. if ($i == 1 || $i == 2) {
  812. $class = 'borderRight';
  813. } else {
  814. $class = '';
  815. }
  816. $html .= '<td class="firstLine '
  817. .$class
  818. .' '
  819. .$classGlobalChart
  820. .'" style="width="'
  821. .$colWidth
  822. .'px; font-size:'
  823. .$textSize
  824. .'%;">'
  825. ;
  826. $html .= get_lang($explain);
  827. $html .= '</td>';
  828. $i++;
  829. }
  830. $html .= '</tr>';
  831. }
  832. $html .= '</table></center>';
  833. if ($returnHeight) {
  834. return [$html, $verticalLineHeight];
  835. } else {
  836. return $html;
  837. }
  838. }
  839. /**
  840. * return previous attempt id for this test for student, 0 if no previous attempt.
  841. *
  842. * @param $exeId
  843. *
  844. * @return int
  845. */
  846. public static function getPreviousAttemptId($exeId)
  847. {
  848. $tblTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  849. $exeId = (int) $exeId;
  850. $sql = "SELECT * FROM $tblTrackEExercise
  851. WHERE exe_id = ".$exeId;
  852. $res = Database::query($sql);
  853. if (empty(Database::num_rows($res))) {
  854. // if we cannot find the exe_id
  855. return 0;
  856. }
  857. $data = Database::fetch_assoc($res);
  858. $courseCode = $data['c_id'];
  859. $exerciseId = $data['exe_exo_id'];
  860. $userId = $data['exe_user_id'];
  861. $attemptDate = $data['exe_date'];
  862. if ($attemptDate == '0000-00-00 00:00:00') {
  863. // incomplete attempt, close it before continue
  864. return 0;
  865. }
  866. // look for previous attempt
  867. $exerciseId = (int) $exerciseId;
  868. $userId = (int) $userId;
  869. $sql = "SELECT *
  870. FROM $tblTrackEExercise
  871. WHERE c_id = '$courseCode'
  872. AND exe_exo_id = $exerciseId
  873. AND exe_user_id = $userId
  874. AND status = ''
  875. AND exe_date > '0000-00-00 00:00:00'
  876. AND exe_date < '$attemptDate'
  877. ORDER BY exe_date DESC";
  878. $res = Database::query($sql);
  879. if (Database::num_rows($res) == 0) {
  880. // no previous attempt
  881. return 0;
  882. }
  883. $data = Database::fetch_assoc($res);
  884. return $data['exe_id'];
  885. }
  886. /**
  887. * return an array of number of answer color for exe attempt
  888. * for question type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY
  889. * e.g.
  890. * [LEVEL_DARKGREEN => 3, LEVEL_LIGHTGREEN => 0, LEVEL_WHITE => 5, LEVEL_LIGHTRED => 12, LEVEL_DARKTRED => 0].
  891. *
  892. * @param $exeId
  893. *
  894. * @return array
  895. */
  896. public static function getColorNumberListForAttempt($exeId)
  897. {
  898. $result = [
  899. self::LEVEL_DARKGREEN => 0,
  900. self::LEVEL_LIGHTGREEN => 0,
  901. self::LEVEL_WHITE => 0,
  902. self::LEVEL_LIGHTRED => 0,
  903. self::LEVEL_DARKRED => 0,
  904. ];
  905. $attemptInfoList = self::getExerciseAttemptInfo($exeId);
  906. foreach ($attemptInfoList as $attemptInfo) {
  907. $oQuestion = new MultipleAnswerTrueFalseDegreeCertainty();
  908. $oQuestion->read($attemptInfo['question_id']);
  909. if ($oQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) {
  910. $answerColor = self::getAnswerColor($exeId, $attemptInfo['question_id'], $attemptInfo['position']);
  911. if ($answerColor) {
  912. $result[$answerColor]++;
  913. }
  914. }
  915. }
  916. return $result;
  917. }
  918. /**
  919. * return an array of number of color for question type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY
  920. * for each question category.
  921. *
  922. * e.g.
  923. * [
  924. * (categoryId=)5 => [LEVEL_DARKGREEN => 3, LEVEL_WHITE => 5, LEVEL_LIGHTRED => 12]
  925. * (categoryId=)2 => [LEVEL_DARKGREEN => 8, LEVEL_LIGHTRED => 2, LEVEL_DARKTRED => 8]
  926. * (categoryId=)0 => [LEVEL_DARKGREEN => 1,
  927. * LEVEL_LIGHTGREEN => 2,
  928. * LEVEL_WHITE => 6,
  929. * LEVEL_LIGHTRED => 1,
  930. * LEVEL_DARKTRED => 9]
  931. * ]
  932. *
  933. * @param int $exeId
  934. *
  935. * @return array
  936. */
  937. public static function getColorNumberListForAttemptByCategory($exeId)
  938. {
  939. $result = [];
  940. $attemptInfoList = self::getExerciseAttemptInfo($exeId);
  941. foreach ($attemptInfoList as $attemptInfo) {
  942. $oQuestion = new MultipleAnswerTrueFalseDegreeCertainty();
  943. $oQuestion->read($attemptInfo['question_id']);
  944. if ($oQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) {
  945. $questionCategory = Testcategory::getCategoryForQuestion($attemptInfo['question_id']);
  946. if (!array_key_exists($questionCategory, $result)) {
  947. $result[$questionCategory] = [];
  948. }
  949. $answerColor = self::getAnswerColor($exeId, $attemptInfo['question_id'], $attemptInfo['position']);
  950. if ($answerColor && isset($result[$questionCategory])) {
  951. if (!isset($result[$questionCategory][$answerColor])) {
  952. $result[$questionCategory][$answerColor] = 0;
  953. }
  954. $result[$questionCategory][$answerColor]++;
  955. }
  956. }
  957. }
  958. return $result;
  959. }
  960. /**
  961. * Return true if answer of $exeId, $questionId, $position is correct, otherwise return false.
  962. *
  963. * @param $exeId
  964. * @param $questionId
  965. * @param $position
  966. *
  967. * @return int
  968. */
  969. public static function getAnswerColor($exeId, $questionId, $position)
  970. {
  971. $attemptInfoList = self::getExerciseAttemptInfo($exeId, $questionId, $position);
  972. if (count($attemptInfoList) != 1) {
  973. // havent got the answer
  974. return 0;
  975. }
  976. $answerCodes = $attemptInfoList[0]['answer'];
  977. // student answer
  978. $splitAnswer = preg_split("/:/", $answerCodes);
  979. // get correct answer option id
  980. $correctAnswerOptionId = self::getCorrectAnswerOptionId($splitAnswer[0]);
  981. if ($correctAnswerOptionId == 0) {
  982. // error returning the correct answer option id
  983. return 0;
  984. }
  985. // get student answer option id
  986. $studentAnswerOptionId = isset($splitAnswer[1]) ? $splitAnswer[1] : null;
  987. // we got the correct answer option id, let's compare ti with the student answer
  988. $percentage = null;
  989. if (isset($splitAnswer[2])) {
  990. $percentage = self::getPercentagePosition($splitAnswer[2]);
  991. }
  992. if ($studentAnswerOptionId == $correctAnswerOptionId) {
  993. // yeah, student got correct answer
  994. switch ($percentage) {
  995. case 3:
  996. return self::LEVEL_WHITE;
  997. case 4:
  998. case 5:
  999. return self::LEVEL_LIGHTGREEN;
  1000. case 6:
  1001. case 7:
  1002. case 8:
  1003. return self::LEVEL_DARKGREEN;
  1004. default:
  1005. return 0;
  1006. }
  1007. } else {
  1008. // bummer, wrong answer dude
  1009. switch ($percentage) {
  1010. case 3:
  1011. return self::LEVEL_WHITE;
  1012. case 4:
  1013. case 5:
  1014. return self::LEVEL_LIGHTRED;
  1015. case 6:
  1016. case 7:
  1017. case 8:
  1018. return self::LEVEL_DARKRED;
  1019. default:
  1020. return 0;
  1021. }
  1022. }
  1023. }
  1024. /**
  1025. * Return the position of certitude %age choose by student.
  1026. *
  1027. * @param $optionId
  1028. *
  1029. * @return int
  1030. */
  1031. public static function getPercentagePosition($optionId)
  1032. {
  1033. $tblAnswerOption = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION);
  1034. $courseId = api_get_course_int_id();
  1035. $optionId = (int) $optionId;
  1036. $sql = "SELECT position
  1037. FROM $tblAnswerOption
  1038. WHERE c_id = $courseId AND id = $optionId";
  1039. $res = Database::query($sql);
  1040. if (Database::num_rows($res) == 0) {
  1041. return 0;
  1042. }
  1043. $data = Database::fetch_assoc($res);
  1044. return $data['position'];
  1045. }
  1046. /**
  1047. * return the correct id from c_quiz_question_option for question idAuto.
  1048. *
  1049. * @param $idAuto
  1050. *
  1051. * @return int
  1052. */
  1053. public static function getCorrectAnswerOptionId($idAuto)
  1054. {
  1055. $tblAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
  1056. $courseId = api_get_course_int_id();
  1057. $idAuto = (int) $idAuto;
  1058. $sql = "SELECT correct FROM $tblAnswer
  1059. WHERE c_id = $courseId AND id_auto = $idAuto";
  1060. $res = Database::query($sql);
  1061. $data = Database::fetch_assoc($res);
  1062. if (Database::num_rows($res) > 0) {
  1063. return $data['correct'];
  1064. } else {
  1065. return 0;
  1066. }
  1067. }
  1068. /**
  1069. * return an array of exe info from track_e_attempt.
  1070. *
  1071. * @param int $exeId
  1072. * @param int $questionId
  1073. * @param int $position
  1074. *
  1075. * @return array
  1076. */
  1077. public static function getExerciseAttemptInfo($exeId, $questionId = -1, $position = -1)
  1078. {
  1079. $result = [];
  1080. $and = '';
  1081. $questionId = (int) $questionId;
  1082. $position = (int) $position;
  1083. $exeId = (int) $exeId;
  1084. if ($questionId >= 0) {
  1085. $and .= " AND question_id = $questionId";
  1086. }
  1087. if ($position >= 0) {
  1088. $and .= " AND position = $position";
  1089. }
  1090. $tblExeAttempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
  1091. $cId = api_get_course_int_id();
  1092. $sql = "SELECT * FROM $tblExeAttempt
  1093. WHERE c_id = $cId AND exe_id = $exeId $and";
  1094. $res = Database::query($sql);
  1095. while ($data = Database::fetch_assoc($res)) {
  1096. $result[] = $data;
  1097. }
  1098. return $result;
  1099. }
  1100. /**
  1101. * @param int $exeId
  1102. *
  1103. * @return int
  1104. */
  1105. public static function getNumberOfQuestionsForExeId($exeId)
  1106. {
  1107. $tableTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
  1108. $exeId = (int) $exeId;
  1109. $sql = "SELECT exe_exo_id
  1110. FROM $tableTrackEExercise
  1111. WHERE exe_id=".$exeId;
  1112. $res = Database::query($sql);
  1113. $data = Database::fetch_assoc($res);
  1114. if ($data) {
  1115. $exerciseId = $data['exe_exo_id'];
  1116. $objectExercise = new Exercise();
  1117. $objectExercise->read($exerciseId);
  1118. return $objectExercise->getQuestionCount();
  1119. }
  1120. return 0;
  1121. }
  1122. /**
  1123. * Display student chart results for these question types.
  1124. *
  1125. * @param int $exeId
  1126. * @param Exercise $objExercice
  1127. *
  1128. * @return string
  1129. */
  1130. public static function displayStudentsChartResults($exeId, $objExercice)
  1131. {
  1132. $numberOfQuestions = self::getNumberOfQuestionsForExeId($exeId);
  1133. $globalScoreList = self::getColorNumberListForAttempt($exeId);
  1134. $html = self::displayDegreeChart(
  1135. $globalScoreList,
  1136. 600,
  1137. get_lang('YourOverallResultForTheTest'),
  1138. 2,
  1139. 0,
  1140. true,
  1141. false,
  1142. false,
  1143. $numberOfQuestions
  1144. );
  1145. $html .= '<br/>';
  1146. $previousAttemptId = self::getPreviousAttemptId($exeId);
  1147. if ($previousAttemptId > 0) {
  1148. $previousAttemptScoreList = self::getColorNumberListForAttempt(
  1149. $previousAttemptId
  1150. );
  1151. $html .= self::displayDegreeChart(
  1152. $previousAttemptScoreList,
  1153. 600,
  1154. get_lang('ForComparisonYourLastResultToThisTest'),
  1155. 2
  1156. );
  1157. $html .= '<br/>';
  1158. }
  1159. $list = self::getColorNumberListForAttemptByCategory($exeId);
  1160. $html .= self::displayDegreeChartByCategory(
  1161. $list,
  1162. get_lang('YourResultsByDiscipline'),
  1163. 1,
  1164. $objExercice
  1165. );
  1166. $html .= '<br/>';
  1167. return $html;
  1168. }
  1169. /**
  1170. * send mail to student with degre certainty result test.
  1171. *
  1172. * @param int $userId
  1173. * @param Exercise $objExercise
  1174. * @param int $exeId
  1175. */
  1176. public static function sendQuestionCertaintyNotification($userId, $objExercise, $exeId)
  1177. {
  1178. $userInfo = api_get_user_info($userId);
  1179. $recipientName = api_get_person_name($userInfo['firstname'],
  1180. $userInfo['lastname'],
  1181. null,
  1182. PERSON_NAME_EMAIL_ADDRESS
  1183. );
  1184. $subject = "[".get_lang('DoNotReply')."] "
  1185. .html_entity_decode(get_lang('ResultAccomplishedTest')." \"".$objExercise->title."\"");
  1186. // message sended to the student
  1187. $message = get_lang('Dear').' '.$recipientName.",<br /><br />";
  1188. $exerciseLink = "<a href='".api_get_path(WEB_CODE_PATH)."/exercise/result.php?show_headers=1&"
  1189. .api_get_cidreq()
  1190. ."&id=$exeId'>";
  1191. $exerciseTitle = $objExercise->title;
  1192. $message .= sprintf(
  1193. get_lang('MessageQuestionCertainty'),
  1194. $exerciseTitle,
  1195. api_get_path(WEB_PATH),
  1196. $exerciseLink
  1197. );
  1198. // show histogram
  1199. $message .= self::displayStudentsChartResults($exeId, $objExercise);
  1200. $message .= get_lang('KindRegards');
  1201. $message = api_preg_replace("/\\\n/", '', $message);
  1202. MessageManager::send_message_simple($userId, $subject, $message);
  1203. }
  1204. }