PageRenderTime 37ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/question/type/calculatedmulti/edit_calculatedmulti_form.php

https://bitbucket.org/kudutest1/moodlegit
PHP | 351 lines | 267 code | 41 blank | 43 comment | 51 complexity | 65fc92e7a142d463fa5dab2e0d798918 MD5 | raw file
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Defines the editing form for calculated multiple-choice questions.
  18. *
  19. * @package qtype
  20. * @subpackage calculatedmulti
  21. * @copyright 2007 Jamie Pratt me@jamiep.org
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. defined('MOODLE_INTERNAL') || die();
  25. /**
  26. * Calculated multiple-choice question editing form.
  27. *
  28. * @copyright 2007 Jamie Pratt me@jamiep.org
  29. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30. */
  31. class qtype_calculatedmulti_edit_form extends question_edit_form {
  32. /**
  33. * Handle to the question type for this question.
  34. *
  35. * @var question_calculatedmulti_qtype
  36. */
  37. public $qtypeobj;
  38. public $questiondisplay;
  39. public $initialname = '';
  40. public $reload = false;
  41. public function __construct($submiturl, $question, $category,
  42. $contexts, $formeditable = true) {
  43. $this->question = $question;
  44. $this->qtypeobj = question_bank::get_qtype('calculatedmulti');
  45. $this->reload = optional_param('reload', false, PARAM_BOOL);
  46. if (!$this->reload) {
  47. // Use database data as this is first pass.
  48. if (isset($this->question->id)) {
  49. // Remove prefix #{..}# if exists.
  50. $this->initialname = $question->name;
  51. $regs= array();
  52. if (preg_match('~#\{([^[:space:]]*)#~', $question->name , $regs)) {
  53. $question->name = str_replace($regs[0], '', $question->name);
  54. };
  55. }
  56. }
  57. parent::__construct($submiturl, $question, $category, $contexts, $formeditable);
  58. }
  59. public function get_per_answer_fields($mform, $label, $gradeoptions,
  60. &$repeatedoptions, &$answersoption) {
  61. $repeated = array();
  62. $answeroptions = array();
  63. $answeroptions[] = $mform->createElement('text', 'answer',
  64. $label, array('size' => 50));
  65. $answeroptions[] = $mform->createElement('select', 'fraction',
  66. get_string('grade'), $gradeoptions);
  67. $repeated[] = $mform->createElement('group', 'answeroptions',
  68. $label, $answeroptions, null, false);
  69. // Added answeroptions help button in definition_inner() after called to add_per_answer_fields.
  70. $repeatedoptions['answer']['type'] = PARAM_RAW;
  71. $repeatedoptions['fraction']['default'] = 0;
  72. $answersoption = 'answers';
  73. $mform->setType('answer', PARAM_NOTAGS);
  74. $repeated[] = $mform->createElement('hidden', 'tolerance');
  75. $repeated[] = $mform->createElement('hidden', 'tolerancetype', 1);
  76. $repeatedoptions['tolerance']['type'] = PARAM_FLOAT;
  77. $repeatedoptions['tolerance']['default'] = 0.01;
  78. $repeatedoptions['tolerancetype']['type'] = PARAM_INT;
  79. // Create display group.
  80. $answerdisplay = array();
  81. $answerdisplay[] = $mform->createElement('select', 'correctanswerlength',
  82. get_string('answerdisplay', 'qtype_calculated'), range(0, 9));
  83. $repeatedoptions['correctanswerlength']['default'] = 2;
  84. $answerlengthformats = array(
  85. '1' => get_string('decimalformat', 'qtype_numerical'),
  86. '2' => get_string('significantfiguresformat', 'qtype_calculated')
  87. );
  88. $answerdisplay[] = $mform->createElement('select', 'correctanswerformat',
  89. get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
  90. $repeated[] = $mform->createElement('group', 'answerdisplay',
  91. get_string('answerdisplay', 'qtype_calculated'), $answerdisplay, null, false);
  92. // Add feedback.
  93. $repeated[] = $mform->createElement('editor', 'feedback',
  94. get_string('feedback', 'question'), null, $this->editoroptions);
  95. return $repeated;
  96. }
  97. protected function definition_inner($mform) {
  98. $label = get_string('sharedwildcards', 'qtype_calculated');
  99. $mform->addElement('hidden', 'initialcategory', 1);
  100. $mform->addElement('hidden', 'reload', 1);
  101. $mform->setType('initialcategory', PARAM_INT);
  102. $mform->setType('reload', PARAM_BOOL);
  103. $html2 = '';
  104. $mform->insertElementBefore(
  105. $mform->createElement('static', 'listcategory', $label, $html2), 'name');
  106. if (isset($this->question->id)) {
  107. $mform->insertElementBefore($mform->createElement('static', 'initialname',
  108. get_string('questionstoredname', 'qtype_calculated'),
  109. $this->initialname), 'name');
  110. };
  111. $addfieldsname = 'updatecategory';
  112. $addstring = get_string('updatecategory', 'qtype_calculated');
  113. $mform->registerNoSubmitButton($addfieldsname);
  114. $this->editasmultichoice = 1;
  115. $mform->insertElementBefore(
  116. $mform->createElement('submit', $addfieldsname, $addstring), 'listcategory');
  117. $mform->registerNoSubmitButton('createoptionbutton');
  118. $mform->addElement('hidden', 'multichoice', $this->editasmultichoice);
  119. $mform->setType('multichoice', PARAM_INT);
  120. $menu = array(get_string('answersingleno', 'qtype_multichoice'),
  121. get_string('answersingleyes', 'qtype_multichoice'));
  122. $mform->addElement('select', 'single',
  123. get_string('answerhowmany', 'qtype_multichoice'), $menu);
  124. $mform->setDefault('single', 1);
  125. $mform->addElement('advcheckbox', 'shuffleanswers',
  126. get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0, 1));
  127. $mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice');
  128. $mform->setDefault('shuffleanswers', 1);
  129. $numberingoptions = question_bank::get_qtype('multichoice')->get_numbering_styles();
  130. $mform->addElement('select', 'answernumbering',
  131. get_string('answernumbering', 'qtype_multichoice'), $numberingoptions);
  132. $mform->setDefault('answernumbering', 'abc');
  133. $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
  134. question_bank::fraction_options_full(), max(5, QUESTION_NUMANS_START));
  135. $mform->addHelpButton('answeroptions[0]', 'answeroptions', 'qtype_calculatedmulti');
  136. $repeated = array();
  137. $nounits = optional_param('nounits', 1, PARAM_INT);
  138. $mform->addElement('hidden', 'nounits', $nounits);
  139. $mform->setType('nounits', PARAM_INT);
  140. $mform->setConstants(array('nounits'=>$nounits));
  141. for ($i = 0; $i < $nounits; $i++) {
  142. $mform->addElement('hidden', 'unit'."[$i]",
  143. optional_param("unit[$i]", '', PARAM_NOTAGS));
  144. $mform->setType('unit'."[$i]", PARAM_NOTAGS);
  145. $mform->addElement('hidden', 'multiplier'."[$i]",
  146. optional_param("multiplier[$i]", '', PARAM_FLOAT));
  147. $mform->setType("multiplier[$i]", PARAM_FLOAT);
  148. }
  149. $this->add_combined_feedback_fields(true);
  150. $mform->disabledIf('shownumcorrect', 'single', 'eq', 1);
  151. $this->add_interactive_settings(true, true);
  152. // Hidden elements.
  153. $mform->addElement('hidden', 'synchronize', '');
  154. $mform->setType('synchronize', PARAM_INT);
  155. if (isset($this->question->options) && isset($this->question->options->synchronize)) {
  156. $mform->setDefault('synchronize', $this->question->options->synchronize);
  157. } else {
  158. $mform->setDefault('synchronize', 0);
  159. }
  160. $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
  161. $mform->setType('wizard', PARAM_ALPHA);
  162. }
  163. public function data_preprocessing($question) {
  164. $question = parent::data_preprocessing($question);
  165. $question = $this->data_preprocessing_answers($question, false);
  166. $question = $this->data_preprocessing_combined_feedback($question, true);
  167. $question = $this->data_preprocessing_hints($question, true, true);
  168. if (isset($question->options)) {
  169. $question->synchronize = $question->options->synchronize;
  170. $question->single = $question->options->single;
  171. $question->answernumbering = $question->options->answernumbering;
  172. $question->shuffleanswers = $question->options->shuffleanswers;
  173. }
  174. return $question;
  175. }
  176. protected function data_preprocessing_answers($question, $withanswerfiles = false) {
  177. $question = parent::data_preprocessing_answers($question, $withanswerfiles);
  178. if (empty($question->options->answers)) {
  179. return $question;
  180. }
  181. $key = 0;
  182. foreach ($question->options->answers as $answer) {
  183. // See comment in the parent method about this hack.
  184. unset($this->_form->_defaultValues["tolerance[$key]"]);
  185. unset($this->_form->_defaultValues["tolerancetype[$key]"]);
  186. unset($this->_form->_defaultValues["correctanswerlength[$key]"]);
  187. unset($this->_form->_defaultValues["correctanswerformat[$key]"]);
  188. $question->tolerance[$key] = $answer->tolerance;
  189. $question->tolerancetype[$key] = $answer->tolerancetype;
  190. $question->correctanswerlength[$key] = $answer->correctanswerlength;
  191. $question->correctanswerformat[$key] = $answer->correctanswerformat;
  192. $key++;
  193. }
  194. return $question;
  195. }
  196. public function validation($data, $files) {
  197. $errors = parent::validation($data, $files);
  198. // Verifying for errors in {=...} in question text.
  199. $qtext = '';
  200. $qtextremaining = $data['questiontext']['text'];
  201. $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
  202. foreach ($possibledatasets as $name => $value) {
  203. $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
  204. }
  205. while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
  206. $qtextsplits = explode($regs1[0], $qtextremaining, 2);
  207. $qtext = $qtext.$qtextsplits[0];
  208. $qtextremaining = $qtextsplits[1];
  209. if (!empty($regs1[1]) && $formulaerrors =
  210. qtype_calculated_find_formula_errors($regs1[1])) {
  211. if (!isset($errors['questiontext'])) {
  212. $errors['questiontext'] = $formulaerrors.':'.$regs1[1];
  213. } else {
  214. $errors['questiontext'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
  215. }
  216. }
  217. }
  218. $answers = $data['answer'];
  219. $answercount = 0;
  220. $maxgrade = false;
  221. $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
  222. $mandatorydatasets = array();
  223. foreach ($answers as $key => $answer) {
  224. $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
  225. }
  226. if (count($mandatorydatasets) == 0) {
  227. foreach ($answers as $key => $answer) {
  228. $errors['answeroptions['.$key.']'] =
  229. get_string('atleastonewildcard', 'qtype_calculated');
  230. }
  231. }
  232. $totalfraction = 0;
  233. $maxfraction = -1;
  234. foreach ($answers as $key => $answer) {
  235. $trimmedanswer = trim($answer);
  236. $fraction = (float) $data['fraction'][$key];
  237. if (empty($trimmedanswer) && $trimmedanswer != '0' && empty($fraction)) {
  238. continue;
  239. }
  240. if (empty($trimmedanswer)) {
  241. $errors['answeroptions['.$key.']'] = get_string('errgradesetanswerblank', 'qtype_multichoice');
  242. }
  243. if ($trimmedanswer != '' || $answercount == 0) {
  244. // Verifying for errors in {=...} in answer text.
  245. $qanswer = '';
  246. $qanswerremaining = $trimmedanswer;
  247. $possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer);
  248. foreach ($possibledatasets as $name => $value) {
  249. $qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining);
  250. }
  251. while (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) {
  252. $qanswersplits = explode($regs1[0], $qanswerremaining, 2);
  253. $qanswer = $qanswer . $qanswersplits[0];
  254. $qanswerremaining = $qanswersplits[1];
  255. if (!empty($regs1[1]) && $formulaerrors =
  256. qtype_calculated_find_formula_errors($regs1[1])) {
  257. if (!isset($errors['answeroptions['.$key.']'])) {
  258. $errors['answeroptions['.$key.']'] = $formulaerrors.':'.$regs1[1];
  259. } else {
  260. $errors['answeroptions['.$key.']'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
  261. }
  262. }
  263. }
  264. }
  265. if ($trimmedanswer != '') {
  266. if ('2' == $data['correctanswerformat'][$key] &&
  267. '0' == $data['correctanswerlength'][$key]) {
  268. $errors['correctanswerlength['.$key.']'] =
  269. get_string('zerosignificantfiguresnotallowed', 'qtype_calculated');
  270. }
  271. if (!is_numeric($data['tolerance'][$key])) {
  272. $errors['tolerance['.$key.']'] =
  273. get_string('xmustbenumeric', 'qtype_numerical',
  274. get_string('acceptederror', 'qtype_numerical'));
  275. }
  276. if ($data['fraction'][$key] > 0) {
  277. $totalfraction += $data['fraction'][$key];
  278. }
  279. if ($data['fraction'][$key] > $maxfraction) {
  280. $maxfraction = $data['fraction'][$key];
  281. }
  282. $answercount++;
  283. }
  284. }
  285. if ($answercount == 0) {
  286. $errors['answeroptions[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
  287. $errors['answeroptions[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
  288. } else if ($answercount == 1) {
  289. $errors['answeroptions[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
  290. }
  291. // Perform sanity checks on fractional grades.
  292. if ($data['single']== 1 ) {
  293. if ($maxfraction != 1) {
  294. $errors['answeroptions[0]'] = get_string('errfractionsnomax', 'qtype_multichoice',
  295. $maxfraction * 100);
  296. }
  297. } else {
  298. $totalfraction = round($totalfraction, 2);
  299. if ($totalfraction != 1) {
  300. $totalfraction = $totalfraction * 100;
  301. $errors['answeroptions[0]'] =
  302. get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
  303. }
  304. }
  305. return $errors;
  306. }
  307. public function qtype() {
  308. return 'calculatedmulti';
  309. }
  310. }