PageRenderTime 64ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/question/type/calculated/datasetitems_form.php

https://bitbucket.org/ngmares/moodle
PHP | 503 lines | 403 code | 42 blank | 58 comment | 68 complexity | cdec2b19c68729cb7e23a14c76834531 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-3.0, Apache-2.0, BSD-3-Clause
  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 the calculated question data set items.
  18. *
  19. * @package qtype
  20. * @subpackage calculated
  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. require_once($CFG->dirroot . '/question/type/edit_question_form.php');
  26. /**
  27. * Calculated question data set items editing form definition.
  28. *
  29. * @copyright 2007 Jamie Pratt me@jamiep.org
  30. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31. */
  32. class question_dataset_dependent_items_form extends question_wizard_form {
  33. /**
  34. * Question object with options and answers already loaded by get_question_options
  35. * Be careful how you use this it is needed sometimes to set up the structure of the
  36. * form in definition_inner but data is always loaded into the form with set_defaults.
  37. *
  38. * @var object
  39. */
  40. public $question;
  41. /**
  42. * Reference to question type object
  43. *
  44. * @var question_dataset_dependent_questiontype
  45. */
  46. public $qtypeobj;
  47. public $datasetdefs;
  48. public $maxnumber = -1;
  49. public $regenerate;
  50. public $noofitems;
  51. public $outsidelimit = false;
  52. public $commentanswers = array();
  53. /**
  54. * Add question-type specific form fields.
  55. *
  56. * @param MoodleQuickForm $mform the form being built.
  57. */
  58. public function __construct($submiturl, $question, $regenerate) {
  59. global $SESSION, $CFG, $DB;
  60. $this->regenerate = $regenerate;
  61. $this->question = $question;
  62. $this->qtypeobj = question_bank::get_qtype($this->question->qtype);
  63. // Validate the question category.
  64. if (!$category = $DB->get_record('question_categories',
  65. array('id' => $question->category))) {
  66. print_error('categorydoesnotexist', 'question', $returnurl);
  67. }
  68. $this->category = $category;
  69. $this->categorycontext = get_context_instance_by_id($category->contextid);
  70. //get the dataset defintions for this question
  71. if (empty($question->id)) {
  72. $this->datasetdefs = $this->qtypeobj->get_dataset_definitions(
  73. $question->id, $SESSION->calculated->definitionform->dataset);
  74. } else {
  75. if (empty($question->options)) {
  76. $this->get_question_options($question);
  77. }
  78. $this->datasetdefs = $this->qtypeobj->get_dataset_definitions(
  79. $question->id, array());
  80. }
  81. foreach ($this->datasetdefs as $datasetdef) {
  82. // Get maxnumber
  83. if ($this->maxnumber == -1 || $datasetdef->itemcount < $this->maxnumber) {
  84. $this->maxnumber = $datasetdef->itemcount;
  85. }
  86. }
  87. foreach ($this->datasetdefs as $defid => $datasetdef) {
  88. if (isset($datasetdef->id)) {
  89. $this->datasetdefs[$defid]->items =
  90. $this->qtypeobj->get_database_dataset_items($datasetdef->id);
  91. }
  92. }
  93. parent::__construct($submiturl);
  94. }
  95. protected function definition() {
  96. $labelsharedwildcard = get_string("sharedwildcard", "qtype_calculated");
  97. $mform =& $this->_form;
  98. $strquestionlabel = $this->qtypeobj->comment_header($this->question);
  99. if ($this->maxnumber != -1 ) {
  100. $this->noofitems = $this->maxnumber;
  101. } else {
  102. $this->noofitems = 0;
  103. }
  104. $label = get_string("sharedwildcards", "qtype_calculated");
  105. $html2 = $this->qtypeobj->print_dataset_definitions_category_shared(
  106. $this->question, $this->datasetdefs);
  107. $mform->addElement('static', 'listcategory', $label, $html2);
  108. //----------------------------------------------------------------------
  109. $mform->addElement('submit', 'updatedatasets',
  110. get_string('updatedatasetparam', 'qtype_calculated'));
  111. $mform->registerNoSubmitButton('updatedatasets');
  112. $mform->addElement('header', 'additemhdr',
  113. get_string('itemtoadd', 'qtype_calculated'));
  114. $idx = 1;
  115. $data = array();
  116. $j = (($this->noofitems) * count($this->datasetdefs))+1;
  117. foreach ($this->datasetdefs as $defkey => $datasetdef) {
  118. if ($datasetdef->category |= 0 ) {
  119. $name = get_string('sharedwildcard', 'qtype_calculated', $datasetdef->name);
  120. } else {
  121. $name = get_string('wildcard', 'qtype_calculated', $datasetdef->name);
  122. }
  123. $mform->addElement('text', "number[$j]", $name);
  124. $mform->setType("number[$j]", PARAM_NUMBER);
  125. $this->qtypeobj->custom_generator_tools_part($mform, $idx, $j);
  126. $idx++;
  127. $mform->addElement('hidden', "definition[$j]");
  128. $mform->setType("definition[$j]", PARAM_RAW);
  129. $mform->addElement('hidden', "itemid[$j]");
  130. $mform->setType("itemid[$j]", PARAM_RAW);
  131. $mform->addElement('static', "divider[$j]", '', '<hr />');
  132. $mform->setType("divider[$j]", PARAM_RAW);
  133. $j++;
  134. }
  135. $mform->addElement('header', 'updateanswershdr',
  136. get_string('answerstoleranceparam', 'qtype_calculated'));
  137. $mform->addElement('submit', 'updateanswers',
  138. get_string('updatetolerancesparam', 'qtype_calculated'));
  139. $mform->setAdvanced('updateanswers', true);
  140. $mform->registerNoSubmitButton('updateanswers');
  141. $answers = fullclone($this->question->options->answers);
  142. $key1 =1;
  143. foreach ($answers as $key => $answer) {
  144. if ('' === $answer->answer) {
  145. // Do nothing.
  146. } else if ('*' === $answer->answer) {
  147. $mform->addElement('static',
  148. 'answercomment[' . ($this->noofitems+$key1) . ']', $answer->answer);
  149. $mform->addElement('hidden', 'tolerance['.$key.']', '');
  150. $mform->setType('tolerance['.$key.']', PARAM_RAW);
  151. $mform->setAdvanced('tolerance['.$key.']', true);
  152. $mform->addElement('hidden', 'tolerancetype['.$key.']', '');
  153. $mform->setType('tolerancetype['.$key.']', PARAM_RAW);
  154. $mform->setAdvanced('tolerancetype['.$key.']', true);
  155. $mform->addElement('hidden', 'correctanswerlength['.$key.']', '');
  156. $mform->setType('correctanswerlength['.$key.']', PARAM_RAW);
  157. $mform->setAdvanced('correctanswerlength['.$key.']', true);
  158. $mform->addElement('hidden', 'correctanswerformat['.$key.']', '');
  159. $mform->setType('correctanswerformat['.$key.']', PARAM_RAW);
  160. $mform->setAdvanced('correctanswerformat['.$key.']', true);
  161. } else {
  162. $mform->addElement('static', 'answercomment[' . ($this->noofitems+$key1) . ']',
  163. $answer->answer);
  164. $mform->addElement('text', 'tolerance['.$key.']',
  165. get_string('tolerance', 'qtype_calculated'));
  166. $mform->setAdvanced('tolerance['.$key.']', true);
  167. $mform->addElement('select', 'tolerancetype['.$key.']',
  168. get_string('tolerancetype', 'qtype_numerical'),
  169. $this->qtypeobj->tolerance_types());
  170. $mform->setAdvanced('tolerancetype['.$key.']', true);
  171. $mform->addElement('select', 'correctanswerlength['.$key.']',
  172. get_string('correctanswershows', 'qtype_calculated'), range(0, 9));
  173. $mform->setAdvanced('correctanswerlength['.$key.']', true);
  174. $answerlengthformats = array(
  175. '1' => get_string('decimalformat', 'qtype_numerical'),
  176. '2' => get_string('significantfiguresformat', 'qtype_calculated')
  177. );
  178. $mform->addElement('select', 'correctanswerformat['.$key.']',
  179. get_string('correctanswershowsformat', 'qtype_calculated'),
  180. $answerlengthformats);
  181. $mform->setAdvanced('correctanswerformat['.$key.']', true);
  182. $mform->addElement('static', 'dividertolerance', '', '<hr />');
  183. $mform->setAdvanced('dividertolerance', true);
  184. }
  185. $key1++;
  186. }
  187. $addremoveoptions = array();
  188. $addremoveoptions['1']='1';
  189. for ($i=10; $i<=100; $i+=10) {
  190. $addremoveoptions["$i"]="$i";
  191. }
  192. $showoptions = Array();
  193. $showoptions['1']='1';
  194. $showoptions['2']='2';
  195. $showoptions['5']='5';
  196. for ($i=10; $i<=100; $i+=10) {
  197. $showoptions["$i"]="$i";
  198. }
  199. $mform->addElement('header', 'additemhdr', get_string('add', 'moodle'));
  200. $mform->closeHeaderBefore('additemhdr');
  201. if ($this->qtypeobj->supports_dataset_item_generation()) {
  202. $radiogrp = array();
  203. $radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]',
  204. null, get_string('reuseifpossible', 'qtype_calculated'), 0);
  205. $radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]',
  206. null, get_string('forceregenerationshared', 'qtype_calculated'), 1);
  207. $radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]',
  208. null, get_string('forceregenerationall', 'qtype_calculated'), 2);
  209. $mform->addGroup($radiogrp, 'forceregenerationgrp',
  210. get_string('nextitemtoadd', 'qtype_calculated'), "<br/>", false);
  211. }
  212. $mform->addElement('submit', 'getnextbutton', get_string('getnextnow', 'qtype_calculated'));
  213. $mform->addElement('static', "dividera", '', '<hr />');
  214. $addgrp = array();
  215. $addgrp[] =& $mform->createElement('submit', 'addbutton', get_string('add', 'moodle'));
  216. $addgrp[] =& $mform->createElement('select', "selectadd",
  217. get_string('additem', 'qtype_calculated'), $addremoveoptions);
  218. $addgrp[] = & $mform->createElement('static', "stat", "Items",
  219. get_string('newsetwildcardvalues', 'qtype_calculatedsimple'));
  220. $mform->addGroup($addgrp, 'addgrp', get_string('additem', 'qtype_calculated'), ' ', false);
  221. $mform->addElement('static', "divideradd", '', '');
  222. if ($this->noofitems > 0) {
  223. $mform->addElement('header', 'additemhdr', get_string('delete', 'moodle'));
  224. $deletegrp = array();
  225. $deletegrp[] = $mform->createElement('submit', 'deletebutton',
  226. get_string('delete', 'moodle'));
  227. $deletegrp[] = $mform->createElement('select', 'selectdelete',
  228. get_string('deleteitem', 'qtype_calculated')."1", $addremoveoptions);
  229. $deletegrp[] = $mform->createElement('static', "stat", "Items",
  230. get_string('setwildcardvalues', 'qtype_calculatedsimple'));
  231. $mform->addGroup($deletegrp, 'deletegrp', '', ' ', false);
  232. } else {
  233. $mform->addElement('static', 'warning', '', '<span class="error">' .
  234. get_string('youmustaddatleastoneitem', 'qtype_calculated').'</span>');
  235. }
  236. $addgrp1 = array();
  237. $addgrp1[] = $mform->createElement('submit', 'showbutton',
  238. get_string('showitems', 'qtype_calculated'));
  239. $addgrp1[] = $mform->createElement('select', "selectshow", '' , $showoptions);
  240. $addgrp1[] = $mform->createElement('static', "stat", '',
  241. get_string('setwildcardvalues', 'qtype_calculated'));
  242. $mform->addGroup($addgrp1, 'addgrp1', '', ' ', false);
  243. $mform->registerNoSubmitButton('showbutton');
  244. $mform->closeHeaderBefore('addgrp1');
  245. //----------------------------------------------------------------------
  246. $j = $this->noofitems * count($this->datasetdefs);
  247. $k = optional_param('selectshow', 1, PARAM_INT);
  248. for ($i = $this->noofitems; $i >= 1; $i--) {
  249. if ($k > 0) {
  250. $mform->addElement('header', '', "<b>" .
  251. get_string('setno', 'qtype_calculated', $i)."</b>&nbsp;&nbsp;");
  252. }
  253. foreach ($this->datasetdefs as $defkey => $datasetdef) {
  254. if ($k > 0) {
  255. if ($datasetdef->category == 0 ) {
  256. $mform->addElement('text', "number[$j]",
  257. get_string('wildcard', 'qtype_calculated', $datasetdef->name));
  258. } else {
  259. $mform->addElement('text', "number[$j]", get_string(
  260. 'sharedwildcard', 'qtype_calculated', $datasetdef->name));
  261. }
  262. } else {
  263. $mform->addElement('hidden', "number[$j]" , '');
  264. }
  265. $mform->setType("number[$j]", PARAM_NUMBER);
  266. $mform->addElement('hidden', "itemid[$j]");
  267. $mform->setType("itemid[$j]", PARAM_INT);
  268. $mform->addElement('hidden', "definition[$j]");
  269. $mform->setType("definition[$j]", PARAM_NOTAGS);
  270. $data[$datasetdef->name] =$datasetdef->items[$i]->value;
  271. $j--;
  272. }
  273. if ('' != $strquestionlabel && ($k > 0 )) {
  274. //|| $this->outsidelimit || !empty($this->numbererrors )
  275. $repeated[] = $mform->addElement('static', "answercomment[$i]", $strquestionlabel);
  276. // decode equations in question text
  277. $qtext = $this->qtypeobj->substitute_variables(
  278. $this->question->questiontext, $data);
  279. $textequations = $this->qtypeobj->find_math_equations($qtext);
  280. if ($textequations != '' && count($textequations) > 0 ) {
  281. $mform->addElement('static', "divider1[$j]", '',
  282. 'Formulas {=..} in question text');
  283. foreach ($textequations as $key => $equation) {
  284. if ($formulaerrors = qtype_calculated_find_formula_errors($equation)) {
  285. $str=$formulaerrors;
  286. } else {
  287. eval('$str = '.$equation.';');
  288. }
  289. $mform->addElement('static', "textequation", "{=$equation}", "=".$str);
  290. }
  291. }
  292. }
  293. $k--;
  294. }
  295. $mform->addElement('static', 'outsidelimit', '', '');
  296. //----------------------------------------------------------------------
  297. // Non standard name for button element needed so not using add_action_buttons
  298. if (!($this->noofitems==0) ) {
  299. $mform->addElement('submit', 'savechanges', get_string('savechanges'));
  300. $mform->closeHeaderBefore('savechanges');
  301. }
  302. $this->add_hidden_fields();
  303. $mform->addElement('hidden', 'category');
  304. $mform->setType('category', PARAM_SEQUENCE);
  305. $mform->addElement('hidden', 'wizard', 'datasetitems');
  306. $mform->setType('wizard', PARAM_ALPHA);
  307. }
  308. public function set_data($question) {
  309. $formdata = array();
  310. $fromform = new stdClass();
  311. if (isset($question->options)) {
  312. $answers = $question->options->answers;
  313. if (count($answers)) {
  314. if (optional_param('updateanswers', false, PARAM_BOOL) ||
  315. optional_param('updatedatasets', false, PARAM_BOOL)) {
  316. foreach ($answers as $key => $answer) {
  317. $fromform->tolerance[$key]= $this->_form->getElementValue(
  318. 'tolerance['.$key.']');
  319. $answer->tolerance = $fromform->tolerance[$key];
  320. $fromform->tolerancetype[$key]= $this->_form->getElementValue(
  321. 'tolerancetype['.$key.']');
  322. if (is_array($fromform->tolerancetype[$key])) {
  323. $fromform->tolerancetype[$key]= $fromform->tolerancetype[$key][0];
  324. }
  325. $answer->tolerancetype = $fromform->tolerancetype[$key];
  326. $fromform->correctanswerlength[$key]= $this->_form->getElementValue(
  327. 'correctanswerlength['.$key.']');
  328. if (is_array($fromform->correctanswerlength[$key])) {
  329. $fromform->correctanswerlength[$key] =
  330. $fromform->correctanswerlength[$key][0];
  331. }
  332. $answer->correctanswerlength = $fromform->correctanswerlength[$key];
  333. $fromform->correctanswerformat[$key] = $this->_form->getElementValue(
  334. 'correctanswerformat['.$key.']');
  335. if (is_array($fromform->correctanswerformat[$key])) {
  336. $fromform->correctanswerformat[$key] =
  337. $fromform->correctanswerformat[$key][0];
  338. }
  339. $answer->correctanswerformat = $fromform->correctanswerformat[$key];
  340. }
  341. $this->qtypeobj->save_question_calculated($question, $fromform);
  342. } else {
  343. foreach ($answers as $key => $answer) {
  344. $formdata['tolerance['.$key.']'] = $answer->tolerance;
  345. $formdata['tolerancetype['.$key.']'] = $answer->tolerancetype;
  346. $formdata['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
  347. $formdata['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
  348. }
  349. }
  350. }
  351. }
  352. //fill out all data sets and also the fields for the next item to add.
  353. $j = $this->noofitems * count($this->datasetdefs);
  354. for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--) {
  355. $data = array();
  356. foreach ($this->datasetdefs as $defid => $datasetdef) {
  357. if (isset($datasetdef->items[$itemnumber])) {
  358. $formdata["number[$j]"] = $datasetdef->items[$itemnumber]->value;
  359. $formdata["definition[$j]"] = $defid;
  360. $formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
  361. $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
  362. }
  363. $j--;
  364. }
  365. $comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $question->id,
  366. $question->questiontext, $answers, $data, $itemnumber);
  367. if ($comment->outsidelimit) {
  368. $this->outsidelimit=$comment->outsidelimit;
  369. }
  370. $totalcomment='';
  371. foreach ($question->options->answers as $key => $answer) {
  372. $totalcomment .= $comment->stranswers[$key].'<br/>';
  373. }
  374. $formdata['answercomment['.$itemnumber.']'] = $totalcomment;
  375. }
  376. $formdata['nextpageparam[forceregeneration]'] = $this->regenerate;
  377. $formdata['selectdelete'] = '1';
  378. $formdata['selectadd'] = '1';
  379. $j = $this->noofitems * count($this->datasetdefs)+1;
  380. $data = array(); // data for comment_on_datasetitems later
  381. //dataset generation dafaults
  382. if ($this->qtypeobj->supports_dataset_item_generation()) {
  383. $itemnumber = $this->noofitems+1;
  384. foreach ($this->datasetdefs as $defid => $datasetdef) {
  385. if (!optional_param('updatedatasets', false, PARAM_BOOL) &&
  386. !optional_param('updateanswers', false, PARAM_BOOL)) {
  387. $formdata["number[$j]"] = $this->qtypeobj->generate_dataset_item(
  388. $datasetdef->options);
  389. } else {
  390. $formdata["number[$j]"] = $this->_form->getElementValue("number[$j]");
  391. }
  392. $formdata["definition[$j]"] = $defid;
  393. $formdata["itemid[$j]"] = isset($datasetdef->items[$itemnumber]) ?
  394. $datasetdef->items[$itemnumber]->id : 0;
  395. $data[$datasetdef->name] = $formdata["number[$j]"];
  396. $j++;
  397. }
  398. }
  399. //existing records override generated data depending on radio element
  400. $j = $this->noofitems * count($this->datasetdefs) + 1;
  401. if (!$this->regenerate && !optional_param('updatedatasets', false, PARAM_BOOL) &&
  402. !optional_param('updateanswers', false, PARAM_BOOL)) {
  403. $idx = 1;
  404. $itemnumber = $this->noofitems + 1;
  405. foreach ($this->datasetdefs as $defid => $datasetdef) {
  406. if (isset($datasetdef->items[$itemnumber])) {
  407. $formdata["number[$j]"] = $datasetdef->items[$itemnumber]->value;
  408. $formdata["definition[$j]"] = $defid;
  409. $formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
  410. $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
  411. }
  412. $j++;
  413. }
  414. }
  415. $comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $question->id,
  416. $question->questiontext, $answers, $data, ($this->noofitems + 1));
  417. if (isset($comment->outsidelimit) && $comment->outsidelimit) {
  418. $this->outsidelimit=$comment->outsidelimit;
  419. }
  420. $key1 = 1;
  421. foreach ($question->options->answers as $key => $answer) {
  422. $formdata['answercomment['.($this->noofitems+$key1).']'] = $comment->stranswers[$key];
  423. $key1++;
  424. }
  425. if ($this->outsidelimit) {
  426. $formdata['outsidelimit']= '<span class="error">' .
  427. get_string('oneanswertrueansweroutsidelimits', 'qtype_calculated') . '</span>';
  428. }
  429. $formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $formdata);
  430. parent::set_data((object)($formdata + (array)$question));
  431. }
  432. public function validation($data, $files) {
  433. $errors = array();
  434. if (isset($data['savechanges']) && ($this->noofitems==0) ) {
  435. $errors['warning'] = get_string('warning', 'mnet');
  436. }
  437. if ($this->outsidelimit) {
  438. $errors['outsidelimits'] =
  439. get_string('oneanswertrueansweroutsidelimits', 'qtype_calculated');
  440. }
  441. $numbers = $data['number'];
  442. foreach ($numbers as $key => $number) {
  443. if (! is_numeric($number)) {
  444. if (stristr($number, ', ')) {
  445. $errors['number['.$key.']'] = get_string(
  446. 'The , cannot be used, use . as in 0.013 or 1.3e-2', 'qtype_calculated');
  447. } else {
  448. $errors['number['.$key.']'] = get_string(
  449. 'This is not a valid number', 'qtype_calculated');
  450. }
  451. } else if (stristr($number, 'x')) {
  452. $errors['number['.$key.']'] = get_string(
  453. 'Hexadecimal format (i.e. 0X12d) is not allowed', 'qtype_calculated');
  454. } else if (is_nan($number)) {
  455. $errors['number['.$key.']'] = get_string(
  456. 'is a NAN number', 'qtype_calculated');
  457. }
  458. }
  459. return $errors;
  460. }
  461. }