ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.assFormulaQuestion.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
23 
32 {
33  private array $variables;
34  private array $results;
35  private array $resultunits;
38 
39  public function __construct(
40  string $title = "",
41  string $comment = "",
42  string $author = "",
43  int $owner = -1,
44  string $question = ""
45  ) {
47  $this->variables = [];
48  $this->results = [];
49  $this->resultunits = [];
50  $this->unitrepository = new ilUnitConfigurationRepository(0);
51  $this->pass_presented_variables_repo = new PassPresentedVariablesRepo($this->db);
52  }
53 
54  public function clearVariables(): void
55  {
56  $this->variables = [];
57  }
58 
59  public function getVariables(): array
60  {
61  return $this->variables;
62  }
63 
64  public function getVariable(string $variable): ?assFormulaQuestionVariable
65  {
66  if (array_key_exists($variable, $this->variables)) {
67  return $this->variables[$variable];
68  }
69  return null;
70  }
71 
72  public function addVariable(assFormulaQuestionVariable $variable): void
73  {
74  $this->variables[$variable->getVariable()] = $variable;
75  }
76 
77  public function clearResults(): void
78  {
79  $this->results = [];
80  }
81 
82  public function getResults(): array
83  {
84  return $this->results;
85  }
86 
87  public function getResult(string $result): ?assFormulaQuestionResult
88  {
89  if (array_key_exists($result, $this->results)) {
90  return $this->results[$result];
91  }
92  return null;
93  }
94 
95  public function addResult(assFormulaQuestionResult $result): void
96  {
97  $this->results[$result->getResult()] = $result;
98  }
99 
100  public function addResultUnits(
101  ?assFormulaQuestionResult $result,
102  ?array $unit_ids
103  ): void {
104  $this->resultunits[$result->getResult()] = [];
105  if ($result === null || $unit_ids === null) {
106  return;
107  }
108  foreach ($unit_ids as $id) {
109  if (is_numeric($id) && ($id > 0)) {
110  $this->resultunits[$result->getResult()][$id] = $this->getUnitrepository()->getUnit($id);
111  }
112  }
113  }
114 
115  public function addResultUnit(
116  ?assFormulaQuestionResult $result,
118  ): void {
119  if ($result === null || $unit === null) {
120  return;
121  }
122 
123  if (!array_key_exists($result->getResult(), $this->resultunits) ||
124  !is_array($this->resultunits[$result->getResult()])) {
125  $this->resultunits[$result->getResult()] = [];
126  }
127  $this->resultunits[$result->getResult()][$unit->getId()] = $unit;
128  }
129 
130  public function getResultUnits(assFormulaQuestionResult $result): array
131  {
132  if (!isset($this->resultunits[$result->getResult()])) {
133  return [];
134  }
135 
136  $result_units = $this->resultunits[$result->getResult()];
137 
138  usort(
139  $result_units,
141  $a->getSequence() <=> $b->getSequence()
142  );
143 
144  return $result_units;
145  }
146 
147  public function getAllResultUnits(): array
148  {
149  return $this->resultunits;
150  }
151 
152  public function hasResultUnit(
153  assFormulaQuestionResult $result,
154  int $unit_id
155  ): bool {
156  if (array_key_exists($result->getResult(), $this->resultunits)
157  && array_key_exists($unit_id, $this->resultunits[$result->getResult()])) {
158  return true;
159  }
160 
161  return false;
162  }
163 
164  public function parseQuestionText(): void
165  {
166  $this->clearResults();
167  $this->clearVariables();
168  if (preg_match_all('/(\$v\d+)/im', $this->getQuestion(), $matches)) {
169  foreach ($matches[1] as $variable) {
170  $varObj = new assFormulaQuestionVariable($variable, '0.0', '0.0', null, 0);
171  $this->addVariable($varObj);
172  }
173  }
174 
175  if (preg_match_all('/(\$r\d+)/im', $this->getQuestion(), $rmatches)) {
176  foreach ($rmatches[1] as $result) {
177  $resObj = new assFormulaQuestionResult($result, null, null, 0, null, null, 1, 1, true);
178  $this->addResult($resObj);
179  }
180  }
181  }
182 
183  public function checkForDuplicateVariables(): bool
184  {
185  if (preg_match_all('/(\$v\d+)/im', $this->getQuestion(), $matches)) {
186  if ((count(array_unique($matches[1]))) != count($matches[1])) {
187  return false;
188  }
189  }
190  return true;
191  }
192 
193  public function checkForDuplicateResults(): bool
194  {
195  if (preg_match_all('/(\$r\d+)/im', $this->getQuestion(), $rmatches)) {
196  if ((count(array_unique($rmatches[1]))) != count($rmatches[1])) {
197  return false;
198  }
199  }
200  return true;
201  }
202 
207  public function fetchAllResults($questionText): array
208  {
209  $resObjects = [];
210  $matches = null;
211 
212  if (preg_match_all('/(\$r\d+)/im', $questionText, $matches)) {
213  foreach ($matches[1] as $resultKey) {
214  $resObjects[] = $this->getResult($resultKey);
215  }
216  }
217 
218  return $resObjects;
219  }
220 
224  public function fetchAllVariables(string $question_text): array
225  {
226  $var_objects = [];
227  $matches = null;
228 
229  if (preg_match_all('/(\$v\d+)/im', $question_text, $matches)) {
230  $var_objects = array_reduce(
231  $matches[1],
232  function (array $c, string $v): array {
233  $vo = $this->getVariable($v);
234  if ($vo !== null) {
235  $c[] = $vo;
236  }
237  return $c;
238  },
239  []
240  );
241  }
242 
243  return $var_objects;
244  }
245 
250  public function hasRequiredVariableSolutionValues(array $userSolution): bool
251  {
252  foreach ($this->fetchAllVariables($this->getQuestion()) as $varObj) {
253  if (!isset($userSolution[$varObj->getVariable()])) {
254  return false;
255  }
256 
257  if ($userSolution[$varObj->getVariable()] === '') {
258  return false;
259  }
260  }
261 
262  return true;
263  }
264 
266  int $active_id,
267  int $pass
268  ): array {
269  $question_id = $this->getId();
270  $values = $this->pass_presented_variables_repo->getFor(
271  $question_id,
272  $active_id,
273  $pass
274  );
275  if (is_null($values)) {
276  $values = $this->getInitialVariableSolutionValues();
277  $this->pass_presented_variables_repo->store(
278  $question_id,
279  $active_id,
280  $pass,
281  $values
282  );
283  }
284  return $values;
285  }
286 
287  public function getInitialVariableSolutionValues(): array
288  {
289  foreach ($this->fetchAllResults($this->getQuestion()) as $resObj) {
290  $resObj->findValidRandomVariables($this->getVariables(), $this->getResults());
291  }
292 
293  $variableSolutionValues = [];
294 
295  foreach ($this->fetchAllVariables($this->getQuestion()) as $varObj) {
296  $variableSolutionValues[$varObj->getVariable()] = $varObj->getValue();
297  }
298 
299  return $variableSolutionValues;
300  }
301 
302  public function saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized = true, $tstamp = 0): int
303  {
304  $init_solution_vars = $this->getVariableSolutionValuesForPass($active_id, $pass);
305  foreach ($init_solution_vars as $val1 => $val2) {
306  $this->db->manipulateF(
307  "DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s AND value1 = %s",
308  ['integer', 'integer','integer', 'text'],
309  [$active_id, $this->getId(), $pass, $val1]
310  );
311  parent::saveCurrentSolution($active_id, $pass, $val1, $val2, $authorized);
312  }
313  return parent::saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized, $tstamp);
314  }
315 
321  public function substituteVariables(array $userdata, bool $graphicalOutput = false, bool $forsolution = false, bool $result_output = false, array $correctness_icons = [])
322  {
323  if ((count($this->results) == 0) && (count($this->variables) == 0)) {
324  return false;
325  }
326 
327  $text = $this->getQuestion();
328 
329  foreach ($this->fetchAllVariables($this->getQuestion()) as $varObj) {
330  if (isset($userdata[$varObj->getVariable()]) && strlen($userdata[$varObj->getVariable()])) {
331  $varObj->setValue($userdata[$varObj->getVariable()]);
332  }
333 
334  $unit = (is_object($varObj->getUnit())) ? $varObj->getUnit()->getUnit() : "";
335 
336  $val = '';
337  if ($varObj->getValue() !== null) {
338  $val = (strlen($varObj->getValue()) > 8) ? strtoupper(sprintf("%e", $varObj->getValue())) : $varObj->getValue();
339  }
340 
341  $text = preg_replace('/\$' . substr($varObj->getVariable(), 1) . '(?![0-9]+)/', $val . ' ' . $unit . '\1', $text);
342  }
343 
344  $text = $this->purifyAndPrepareTextAreaOutput($text);
345 
346  if (preg_match_all('/(\$r\d+)/im', $this->getQuestion(), $rmatches)) {
347  foreach ($rmatches[1] as $result) {
348  $resObj = $this->getResult($result);
349  $value = "";
350  $frac_helper = '';
351  $userdata[$result]['result_type'] = $resObj->getResultType();
352  $is_frac = false;
353  if (
354  $resObj->getResultType() == assFormulaQuestionResult::RESULT_FRAC ||
355  $resObj->getResultType() == assFormulaQuestionResult::RESULT_CO_FRAC
356  ) {
357  $is_frac = true;
358  }
359  if (is_array($userdata) &&
360  isset($userdata[$result]) &&
361  isset($userdata[$result]['value'])) {
362  $input = $this->generateResultInputHTML($result, (string) $userdata[$result]['value'], $forsolution);
363  } elseif ($forsolution) {
364  $value = '';
365  if (!is_array($userdata)) {
366  $value = $resObj->calculateFormula($this->getVariables(), $this->getResults(), parent::getId());
367  $value = sprintf("%." . $resObj->getPrecision() . "f", $value);
368  }
369 
370  if ($is_frac) {
372  if (is_array($value)) {
373  $frac_helper = $value[1];
374  $value = $value[0];
375  }
376  }
377 
378  $input = $this->generateResultInputHTML($result, $value, true);
379  } else {
380  $input = $this->generateResultInputHTML($result, '', false);
381  }
382 
383  $units = "";
384  $result_units = $this->getResultUnits($resObj);
385  if (count($result_units) > 0) {
386  if ($forsolution) {
387  if (is_array($userdata)) {
388  foreach ($result_units as $unit) {
389  if (isset($userdata[$result]["unit"]) && $userdata[$result]["unit"] == $unit->getId()) {
390  $units = $unit->getUnit();
391  }
392  }
393  } else {
394  if ($resObj->getUnit()) {
395  $units = $resObj->getUnit()->getUnit();
396  }
397  }
398  } else {
399  $units = '<select name="result_' . $result . '_unit">';
400  $units .= '<option value="-1">' . $this->lng->txt("select_unit") . '</option>';
401  foreach ($result_units as $unit) {
402  $units .= '<option value="' . $unit->getId() . '"';
403  if (array_key_exists($result, $userdata) &&
404  is_array($userdata[$result]) &&
405  array_key_exists('unit', $userdata[$result])) {
406  if ($userdata[$result]["unit"] == $unit->getId()) {
407  $units .= ' selected="selected"';
408  }
409  }
410  $units .= '>' . $unit->getUnit() . '</option>';
411  }
412  $units .= '</select>';
413  }
414  } else {
415  $units = "";
416  }
417  switch ($resObj->getResultType()) {
419  $units .= ' ' . $this->lng->txt('expected_result_type') . ': ' . $this->lng->txt('result_dec');
420  break;
422  if ($frac_helper !== '') {
423  $units .= ' &asymp; ' . $frac_helper . ', ';
424  } elseif (is_array($userdata) &&
425  array_key_exists($result, $userdata) &&
426  array_key_exists('frac_helper', $userdata[$result]) &&
427  is_string($userdata[$result]["frac_helper"])) {
428  if (!str_contains($value, '/')) {
429  $units .= ' &asymp; ' . $userdata[$result]["frac_helper"] . ', ';
430  }
431  }
432  $units .= ' ' . $this->lng->txt('expected_result_type') . ': ' . $this->lng->txt('result_frac');
433  break;
435  if ($frac_helper !== '') {
436  $units .= ' &asymp; ' . $frac_helper . ', ';
437  } elseif (isset($userdata[$result]["frac_helper"]) && is_array($userdata) && $userdata[$result]["frac_helper"] !== '') {
438  if (!str_contains($value, '/')) {
439  $units .= ' &asymp; ' . $userdata[$result]["frac_helper"] . ', ';
440  }
441  }
442  $units .= ' ' . $this->lng->txt('expected_result_type') . ': ' . $this->lng->txt('result_co_frac');
443  break;
445  break;
446  }
447  $checkSign = "";
448  if ($graphicalOutput) {
449  $resunit = null;
450  $user_value = '';
451  if (is_array($userdata) && is_array($userdata[$result])) {
452  if (isset($userdata[$result]["unit"]) && $userdata[$result]["unit"] > 0) {
453  $resunit = $this->getUnitrepository()->getUnit($userdata[$result]["unit"]);
454  }
455 
456  if (isset($userdata[$result]["value"])) {
457  $user_value = $userdata[$result]["value"];
458  }
459  }
460 
461  $template = new ilTemplate("tpl.il_as_qpl_formulaquestion_output_solution_image.html", true, true, 'components/ILIAS/TestQuestionPool');
462 
463  $correctness_icon = $correctness_icons['not_correct'];
464  if ($resObj->isCorrect($this->getVariables(), $this->getResults(), $user_value, $resunit)) {
465  $correctness_icon = $correctness_icons['correct'];
466  }
467  $template->setCurrentBlock("icon_ok");
468  $template->setVariable("ICON_OK", $correctness_icon);
469  $template->parseCurrentBlock();
470 
471  $checkSign = $template->get();
472  }
473  $resultOutput = "";
474  if ($result_output) {
475  $template = new ilTemplate("tpl.il_as_qpl_formulaquestion_output_solution_result.html", true, true, 'components/ILIAS/TestQuestionPool');
476 
477  if (is_array($userdata) &&
478  array_key_exists($resObj->getResult(), $userdata) &&
479  array_key_exists('value', $userdata[$resObj->getResult()])) {
480  $found = $resObj->getResultInfo(
481  $this->getVariables(),
482  $this->getResults(),
483  $userdata[$resObj->getResult()]["value"],
484  $userdata[$resObj->getResult()]["unit"] ?? null,
485  $this->getUnitrepository()->getUnits()
486  );
487  } else {
488  $found = $resObj->getResultInfo(
489  $this->getVariables(),
490  $this->getResults(),
491  $resObj->calculateFormula($this->getVariables(), $this->getResults(), parent::getId()),
492  is_object($resObj->getUnit()) ? $resObj->getUnit()->getId() : null,
493  $this->getUnitrepository()->getUnits()
494  );
495  }
496  $resulttext = "(";
497  if ($resObj->getRatingSimple()) {
498  if ($frac_helper) {
499  $resulttext .= "n/a";
500  } else {
501  $resulttext .= $found['points'] . " " . (($found['points'] == 1) ? $this->lng->txt('point') : $this->lng->txt('points'));
502  }
503  } else {
504  $resulttext .= $this->lng->txt("rated_sign") . " " . (($found['sign']) ? $found['sign'] : 0) . " " . (($found['sign'] == 1) ? $this->lng->txt('point') : $this->lng->txt('points')) . ", ";
505  $resulttext .= $this->lng->txt("rated_value") . " " . (($found['value']) ? $found['value'] : 0) . " " . (($found['value'] == 1) ? $this->lng->txt('point') : $this->lng->txt('points')) . ", ";
506  $resulttext .= $this->lng->txt("rated_unit") . " " . (($found['unit']) ? $found['unit'] : 0) . " " . (($found['unit'] == 1) ? $this->lng->txt('point') : $this->lng->txt('points'));
507  }
508 
509  $resulttext .= ")";
510  $template->setVariable("RESULT_OUTPUT", $resulttext);
511 
512  $resultOutput = $template->get();
513  }
514  $text = preg_replace('/\$' . substr($result, 1) . '(?![0-9]+)/', $input . ' ' . $units . ' ' . $checkSign . ' ' . $resultOutput . ' ' . '\1', $text);
515  }
516  }
517  return $text;
518  }
519 
520  protected function generateResultInputHTML(string $result_key, string $result_value, bool $forsolution): string
521  {
522  if ($forsolution) {
523  return '<span class="ilc_qinput_TextInput solutionbox">'
525  . '</span>';
526  }
527  $input = '<input class="ilc_qinput_TextInput" type="text"';
528  $input .= 'spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off"';
529  $input .= 'name="result_' . $result_key . '"';
530  $input .= ' value="' . $result_value . '"/>';
531  return $input;
532  }
533 
540  public function canUseAdvancedRating($result): bool
541  {
542  $result_units = $this->getResultUnits($result);
543  $resultunit = $result->getUnit();
544  $similar_units = 0;
545  foreach ($result_units as $unit) {
546  if (is_object($resultunit)) {
547  if ($resultunit->getId() != $unit->getId()) {
548  if ($resultunit->getBaseUnit() && $unit->getBaseUnit()) {
549  if ($resultunit->getBaseUnit() == $unit->getBaseUnit()) {
550  return false;
551  }
552  }
553  if ($resultunit->getBaseUnit()) {
554  if ($resultunit->getBaseUnit() == $unit->getId()) {
555  return false;
556  }
557  }
558  if ($unit->getBaseUnit()) {
559  if ($unit->getBaseUnit() == $resultunit->getId()) {
560  return false;
561  }
562  }
563  }
564  }
565  }
566  return true;
567  }
568 
573  public function isComplete(): bool
574  {
575  if (($this->title) and ($this->author) and ($this->question) and ($this->getMaximumPoints() > 0)) {
576  return true;
577  } else {
578  return false;
579  }
580  }
581 
582  public function saveToDb(?int $original_id = null): void
583  {
585  // save variables
586  $affectedRows = $this->db->manipulateF(
587  "
588  DELETE FROM il_qpl_qst_fq_var
589  WHERE question_fi = %s",
590  ['integer'],
591  [$this->getId()]
592  );
593 
594  foreach ($this->variables as $variable) {
595  $next_id = $this->db->nextId('il_qpl_qst_fq_var');
596  $this->db->insert(
597  'il_qpl_qst_fq_var',
598  [
599  'variable_id' => ['integer', $next_id],
600  'question_fi' => ['integer', $this->getId()],
601  'variable' => ['text', $variable->getVariable()],
602  'range_min' => ['float', $variable->getRangeMin()],
603  'range_max' => ['float', $variable->getRangeMax()],
604  'unit_fi' => ['integer', (is_object($variable->getUnit()) ? (int) $variable->getUnit()->getId() : 0)],
605  'varprecision' => ['integer', (int) $variable->getPrecision()],
606  'intprecision' => ['integer', (int) $variable->getIntprecision()],
607  'range_min_txt' => ['text', $variable->getRangeMinTxt()],
608  'range_max_txt' => ['text', $variable->getRangeMaxTxt()]
609  ]
610  );
611  }
612  // save results
613  $affectedRows = $this->db->manipulateF(
614  "DELETE FROM il_qpl_qst_fq_res WHERE question_fi = %s",
615  ['integer'],
616  [$this->getId()]
617  );
618 
619  foreach ($this->results as $result) {
620  $next_id = $this->db->nextId('il_qpl_qst_fq_res');
621  if (is_object($result->getUnit())) {
622  $tmp_result_unit = $result->getUnit()->getId();
623  } else {
624  $tmp_result_unit = null;
625  }
626 
627  $formula = null;
628  if ($result->getFormula() !== null) {
629  $formula = str_replace(",", ".", $result->getFormula());
630  }
631 
632  $this->db->insert("il_qpl_qst_fq_res", [
633  "result_id" => ['integer', $next_id],
634  "question_fi" => ['integer', $this->getId()],
635  "result" => ["text", $result->getResult()],
636  "range_min" => ["float", $result->getRangeMin()],
637  "range_max" => ["float", $result->getRangeMax()],
638  "tolerance" => ["float", $result->getTolerance()],
639  "unit_fi" => ['integer', (int) $tmp_result_unit],
640  "formula" => ["clob", $formula],
641  "resprecision" => ['integer', $result->getPrecision()],
642  "rating_simple" => ['integer', ($result->getRatingSimple()) ? 1 : 0],
643  "rating_sign" => ["float", ($result->getRatingSimple()) ? 0 : $result->getRatingSign()],
644  "rating_value" => ["float", ($result->getRatingSimple()) ? 0 : $result->getRatingValue()],
645  "rating_unit" => ["float", ($result->getRatingSimple()) ? 0 : $result->getRatingUnit()],
646  "points" => ["float", $result->getPoints()],
647  "result_type" => ['integer', (int) $result->getResultType()],
648  "range_min_txt" => ["text", $result->getRangeMinTxt()],
649  "range_max_txt" => ["text", $result->getRangeMaxTxt()]
650 
651  ]);
652  }
653  // save result units
654  $affectedRows = $this->db->manipulateF(
655  "DELETE FROM il_qpl_qst_fq_res_unit WHERE question_fi = %s",
656  ['integer'],
657  [$this->getId()]
658  );
659  foreach ($this->results as $result) {
660  foreach ($this->getResultUnits($result) as $unit) {
661  $next_id = $this->db->nextId('il_qpl_qst_fq_res_unit');
662  $affectedRows = $this->db->manipulateF(
663  "INSERT INTO il_qpl_qst_fq_res_unit (result_unit_id, question_fi, result, unit_fi) VALUES (%s, %s, %s, %s)",
664  ['integer', 'integer', 'text', 'integer'],
665  [
666  $next_id,
667  $this->getId(),
668  $result->getResult(),
669  $unit->getId()
670  ]
671  );
672  }
673  }
674 
675  parent::saveToDb();
676  }
677 
678  public function loadFromDb(int $question_id): void
679  {
680  $result = $this->db->queryF(
681  "SELECT qpl_questions.* FROM qpl_questions WHERE question_id = %s",
682  ['integer'],
683  [$question_id]
684  );
685  if ($result->numRows() == 1) {
686  $data = $this->db->fetchAssoc($result);
687  $this->setId($question_id);
688  $this->setTitle((string) $data["title"]);
689  $this->setComment((string) $data["description"]);
690  $this->setPoints($data['points']);
691  $this->setOriginalId($data["original_id"]);
692  $this->setObjId($data["obj_fi"]);
693  $this->setAuthor($data["author"]);
694  $this->setOwner($data["owner"]);
695 
696  try {
697  $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
700  }
701 
702  try {
703  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
704  } catch (ilTestQuestionPoolException $e) {
705  }
706 
707  $this->unitrepository = new ilUnitConfigurationRepository($question_id);
708 
709  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc((string) $data["question_text"], 1));
710 
711  // load variables
712  $result = $this->db->queryF(
713  "SELECT * FROM il_qpl_qst_fq_var WHERE question_fi = %s",
714  ['integer'],
715  [$question_id]
716  );
717  if ($result->numRows() > 0) {
718  while ($data = $this->db->fetchAssoc($result)) {
719  $varObj = new assFormulaQuestionVariable(
720  $data['variable'],
721  $data['range_min_txt'],
722  $data['range_max_txt'],
723  $this->getUnitrepository()->getUnit($data['unit_fi']),
724  $data['varprecision'],
725  $data['intprecision']
726  );
727  $this->addVariable($varObj);
728  }
729  }
730  // load results
731  $result = $this->db->queryF(
732  "SELECT * FROM il_qpl_qst_fq_res WHERE question_fi = %s",
733  ['integer'],
734  [$question_id]
735  );
736  if ($result->numRows() > 0) {
737  while ($data = $this->db->fetchAssoc($result)) {
738  $resObj = new assFormulaQuestionResult(
739  $data['result'],
740  $data['range_min_txt'],
741  $data['range_max_txt'],
742  $data['tolerance'],
743  $this->getUnitrepository()->getUnit($data['unit_fi']),
744  $data['formula'],
745  $data['points'],
746  $data['resprecision'],
747  $data['rating_simple'] === 1,
748  $data['rating_sign'],
749  $data['rating_value'],
750  $data['rating_unit']
751  );
752  $resObj->setResultType($data['result_type']);
753  $this->addResult($resObj);
754  }
755  }
756 
757  // load result units
758  $result = $this->db->queryF(
759  "SELECT * FROM il_qpl_qst_fq_res_unit WHERE question_fi = %s",
760  ['integer'],
761  [$question_id]
762  );
763  if ($result->numRows() > 0) {
764  while ($data = $this->db->fetchAssoc($result)) {
765  $unit = $this->getUnitrepository()->getUnit($data["unit_fi"]);
766  $resObj = $this->getResult($data["result"]);
767  $this->addResultUnit($resObj, $unit);
768  }
769  }
770  }
771  parent::loadFromDb($question_id);
772  }
773 
775  \assQuestion $target
776  ): \assQuestion {
777  $this->unitrepository->cloneUnits($this->getId(), $target->getId());
778  return $target;
779  }
780 
785  public function getMaximumPoints(): float
786  {
787  $points = 0;
788  foreach ($this->results as $result) {
789  $points += $result->getPoints();
790  }
791  return $points;
792  }
793 
794  public function calculateReachedPoints(
795  int $active_id,
796  ?int $pass = null,
797  bool $authorized_solution = true
798  ): float {
799  if ($pass === null) {
800  $pass = $this->getSolutionMaxPass($active_id);
801  }
802  $solutions = $this->getSolutionValues($active_id, $pass, $authorized_solution);
803  $user_solution = [];
804  foreach ($solutions as $solution_value) {
805  if (preg_match('/^(\$v\d+)$/', $solution_value['value1'], $matches)) {
806  $user_solution[$matches[1]] = $solution_value['value2'];
807  $var_obj = $this->getVariable($solution_value['value1']);
808  $var_obj->setValue($solution_value['value2']);
809  continue;
810  }
811 
812  if (preg_match('/^(\$r\d+)$/', $solution_value['value1'], $matches)) {
813  if (!array_key_exists($matches[1], $user_solution)) {
814  $user_solution[$matches[1]] = [];
815  }
816  $user_solution[$matches[1]]['value'] = $solution_value['value2'];
817  continue;
818  }
819 
820  if (preg_match('/^(\$r\d+)_unit$/', $solution_value['value1'], $matches)) {
821  if (!array_key_exists($matches[1], $user_solution)) {
822  $user_solution[$matches[1]] = [];
823  }
824  $user_solution[$matches[1]]['unit'] = $this->unitrepository->getUnit(
825  $this->refinery->kindlyTo()->int()->transform($solution_value['value2']),
826  );
827  }
828  }
829 
830  $points = 0;
831  foreach ($this->getResults() as $result) {
832  $points += $result->getReachedPoints(
833  $this->getVariables(),
834  $this->getResults(),
835  $user_solution[$result->getResult()]['value'] ?? '',
836  $user_solution[$result->getResult()]['unit'] ?? null,
837  $this->unitrepository->getUnits()
838  );
839  }
840 
841  return (float) $points;
842  }
843 
845  {
846  $user_solution = $previewSession->getParticipantsSolution();
847 
848  $points = 0;
849  foreach ($this->getResults() as $result) {
850  $unit_id = $user_solution[$result->getResult() . '_unit'] ?? null;
851  $points += $result->getReachedPoints(
852  $this->getVariables(),
853  $this->getResults(),
854  $user_solution[$result->getResult()] ?? '',
855  $unit_id !== null ? $this->unitrepository->getUnit($unit_id) : null,
856  $this->unitrepository->getUnits()
857  );
858  }
859  return $this->ensureNonNegativePoints($points);
860  }
861 
862  protected function isValidSolutionResultValue(string $submittedValue): bool
863  {
864  $submittedValue = str_replace(',', '.', $submittedValue);
865 
866  if (is_numeric($submittedValue)) {
867  return true;
868  }
869 
870  if (preg_match('/^[-+]?\d+\/\d+$/', $submittedValue)) {
871  return true;
872  }
873 
874  return false;
875  }
876 
877  public function saveWorkingData(
878  int $active_id,
879  ?int $pass = null,
880  bool $authorized = true
881  ): bool {
882  if (is_null($pass)) {
883  $pass = ilObjTest::_getPass($active_id);
884  }
885 
886  $answer = $this->getSolutionSubmit();
887  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(
888  function () use ($answer, $active_id, $pass, $authorized) {
889  foreach ($answer as $key => $value) {
890  $matches = null;
891  if (preg_match('/^result_(\$r\d+)$/', $key, $matches) !== false) {
892  $queryResult = "SELECT solution_id FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND authorized = %s AND " . $this->db->like('value1', 'clob', $matches[1]);
893 
894  if ($this->getStep() !== null) {
895  $queryResult .= " AND step = " . $this->db->quote((int) $this->getStep(), 'integer') . " ";
896  }
897 
898  $result = $this->db->queryF(
899  $queryResult,
900  ['integer', 'integer', 'integer', 'integer'],
901  [$active_id, $pass, $this->getId(), (int) $authorized]
902  );
903  if ($result->numRows()) {
904  while ($row = $this->db->fetchAssoc($result)) {
905  $this->db->manipulateF(
906  "DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
907  ['integer', 'integer'],
908  [$row['solution_id'], (int) $authorized]
909  );
910  }
911  }
912 
913  $this->saveCurrentSolution($active_id, $pass, $matches[1], str_replace(",", ".", $value), $authorized);
914  continue;
915  }
916 
917  if (preg_match('/^result_(\$r\d+)_unit$/', $key, $matches) !== false) {
918  $queryResultUnit = "SELECT solution_id FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND authorized = %s AND " . $this->db->like('value1', 'clob', $matches[1] . "_unit");
919 
920  if ($this->getStep() !== null) {
921  $queryResultUnit .= " AND step = " . $this->db->quote((int) $this->getStep(), 'integer') . " ";
922  }
923 
924  $result = $this->db->queryF(
925  $queryResultUnit,
926  ['integer', 'integer', 'integer', 'integer'],
927  [$active_id, $pass, $this->getId(), (int) $authorized]
928  );
929  if ($result->numRows()) {
930  while ($row = $this->db->fetchAssoc($result)) {
931  $this->db->manipulateF(
932  "DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
933  ['integer', 'integer'],
934  [$row['solution_id'], (int) $authorized]
935  );
936  }
937  }
938 
939  $this->saveCurrentSolution($active_id, $pass, $matches[1] . "_unit", $value, $authorized);
940  }
941  }
942  }
943  );
944 
945  return true;
946  }
947 
951  public function lookupForExistingSolutions(int $active_id, int $pass): array
952  {
953  $return = [
954  'authorized' => false,
955  'intermediate' => false
956  ];
957 
958  $query = "
959  SELECT authorized, COUNT(*) cnt
960  FROM tst_solutions
961  WHERE active_fi = " . $this->db->quote($active_id, 'integer') . "
962  AND question_fi = " . $this->db->quote($this->getId(), 'integer') . "
963  AND pass = " . $this->db->quote($pass, 'integer') . "
964  AND value1 like '\$r%'
965  AND value2 is not null
966  AND value2 <> ''
967  ";
968 
969  if ($this->getStep() !== null) {
970  $query .= " AND step = " . $this->db->quote((int) $this->getStep(), 'integer') . " ";
971  }
972 
973  $query .= "
974  GROUP BY authorized
975  ";
976 
977  $result = $this->db->query($query);
978 
979  while ($row = $this->db->fetchAssoc($result)) {
980  if ($row['authorized']) {
981  $return['authorized'] = $row['cnt'] > 0;
982  } else {
983  $return['intermediate'] = $row['cnt'] > 0;
984  }
985  }
986  return $return;
987  }
988 
989  public function removeExistingSolutions(int $active_id, int $pass): int
990  {
991  $query = "
992  DELETE FROM tst_solutions
993  WHERE active_fi = " . $this->db->quote($active_id, 'integer') . "
994  AND question_fi = " . $this->db->quote($this->getId(), 'integer') . "
995  AND pass = " . $this->db->quote($pass, 'integer') . "
996  AND value1 like '\$r%'
997  ";
998 
999  if ($this->getStep() !== null) {
1000  $query .= " AND step = " . $this->db->quote((int) $this->getStep(), 'integer') . " ";
1001  }
1002 
1003  return $this->db->manipulate($query);
1004  }
1005 
1006  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession): void
1007  {
1008  $userSolution = $previewSession->getParticipantsSolution();
1009 
1010  foreach ($this->getSolutionSubmit() as $key => $val) {
1011  $matches = null;
1012 
1013  if (preg_match('/^result_(\$r\d+)$/', $key, $matches)) {
1014  $userSolution[$matches[1]] = $val;
1015  } elseif (preg_match('/^result_(\$r\d+)_unit$/', $key, $matches)) {
1016  $userSolution[$matches[1] . "_unit"] = $val;
1017  }
1018  }
1019 
1020  $previewSession->setParticipantsSolution($userSolution);
1021  }
1022 
1023  public function getQuestionType(): string
1024  {
1025  return "assFormulaQuestion";
1026  }
1027 
1028  public function getAdditionalTableName(): string
1029  {
1030  return "";
1031  }
1032 
1033  public function getAnswerTableName(): string
1034  {
1035  return "";
1036  }
1037 
1038  public function deleteAnswers(int $question_id): void
1039  {
1040  $affectedRows = $this->db->manipulateF(
1041  "DELETE FROM il_qpl_qst_fq_var WHERE question_fi = %s",
1042  ['integer'],
1043  [$question_id]
1044  );
1045 
1046  $affectedRows = $this->db->manipulateF(
1047  "DELETE FROM il_qpl_qst_fq_res WHERE question_fi = %s",
1048  ['integer'],
1049  [$question_id]
1050  );
1051 
1052  $affectedRows = $this->db->manipulateF(
1053  "DELETE FROM il_qpl_qst_fq_res_unit WHERE question_fi = %s",
1054  ['integer'],
1055  [$question_id]
1056  );
1057 
1058  $affectedRows = $this->db->manipulateF(
1059  "DELETE FROM il_qpl_qst_fq_ucat WHERE question_fi = %s",
1060  ['integer'],
1061  [$question_id]
1062  );
1063 
1064  $affectedRows = $this->db->manipulateF(
1065  "DELETE FROM il_qpl_qst_fq_unit WHERE question_fi = %s",
1066  ['integer'],
1067  [$question_id]
1068  );
1069  }
1070 
1071  public function getRTETextWithMediaObjects(): string
1072  {
1073  $text = parent::getRTETextWithMediaObjects();
1074  return $text;
1075  }
1076 
1077  public function getBestSolution(array $solutions): array
1078  {
1079  $user_solution = [];
1080 
1081  foreach ($solutions as $solution_value) {
1082  if (preg_match('/^(\$v\d+)$/', $solution_value['value1'], $matches)) {
1083  $user_solution[$matches[1]] = $solution_value['value2'];
1084  $varObj = $this->getVariable($matches[1]);
1085  $varObj->setValue($solution_value['value2']);
1086  } elseif (preg_match('/^(\$r\d+)$/', $solution_value['value1'], $matches)) {
1087  if (!array_key_exists($matches[1], $user_solution)) {
1088  $user_solution[$matches[1]] = [];
1089  }
1090  $user_solution[$matches[1]]['value'] = $solution_value['value2'];
1091  } elseif (preg_match('/^(\$r\d+)_unit$/', $solution_value['value1'], $matches)) {
1092  if (!array_key_exists($matches[1], $user_solution)) {
1093  $user_solution[$matches[1]] = [];
1094  }
1095  $user_solution[$matches[1]]['unit'] = $solution_value['value2'];
1096  }
1097  }
1098  foreach ($this->getResults() as $result) {
1099  $resVal = $result->calculateFormula($this->getVariables(), $this->getResults(), $this->getId(), false);
1100 
1101  if (is_object($result->getUnit())) {
1102  $user_solution[$result->getResult()]['unit'] = $result->getUnit()->getId();
1103  $user_solution[$result->getResult()]['value'] = $resVal;
1104  } elseif ($result->getUnit() === null) {
1105  $unit_factor = 1;
1106  // there is no fix result_unit, any "available unit" is accepted
1107 
1108  $available_units = $result->getAvailableResultUnits(parent::getId());
1109  $result_name = $result->getResult();
1110 
1111  $check_unit = false;
1112  if (array_key_exists($result_name, $available_units) &&
1113  $available_units[$result_name] !== null) {
1114  $check_unit = in_array($user_solution[$result_name]['unit'] ?? null, $available_units[$result_name]);
1115  }
1116 
1117  if ($check_unit == true) {
1118  //get unit-factor
1119  $unit_factor = assFormulaQuestionUnit::lookupUnitFactor($user_solution[$result_name]['unit']);
1120  }
1121 
1122  try {
1123  $user_solution[$result->getResult()]['value'] = ilMath::_div($resVal, $unit_factor, 55);
1124  } catch (ilMathDivisionByZeroException $ex) {
1125  $user_solution[$result->getResult()]['value'] = 0;
1126  }
1127  }
1128  if ($result->getResultType() == assFormulaQuestionResult::RESULT_CO_FRAC
1129  || $result->getResultType() == assFormulaQuestionResult::RESULT_FRAC) {
1131  if (is_array($value)) {
1132  $user_solution[$result->getResult()]['value'] = $value[0];
1133  $user_solution[$result->getResult()]['frac_helper'] = $value[1];
1134  } else {
1135  $user_solution[$result->getResult()]['value'] = $value;
1136  $user_solution[$result->getResult()]['frac_helper'] = null;
1137  }
1138  } else {
1139  $user_solution[$result->getResult()]['value'] = round((float) $user_solution[$result->getResult()]['value'], $result->getPrecision());
1140  }
1141  }
1142  return $user_solution;
1143  }
1144 
1145  public function setId(int $id = -1): void
1146  {
1147  parent::setId($id);
1148  $this->unitrepository->setConsumerId($this->getId());
1149  }
1150 
1151  public function setUnitrepository(\ilUnitConfigurationRepository $unitrepository): void
1152  {
1153  $this->unitrepository = $unitrepository;
1154  }
1155 
1157  {
1158  return $this->unitrepository;
1159  }
1160 
1164  protected function getSolutionSubmit(): array
1165  {
1166  $solutionSubmit = [];
1167 
1168  $post = $this->dic->http()->wrapper()->post();
1169 
1170  foreach ($this->getResults() as $index => $a) {
1171  $key = "result_$index";
1172  if ($post->has($key)) {
1173  $value = $post->retrieve(
1174  $key,
1175  $this->dic->refinery()->kindlyTo()->string()
1176  );
1177 
1178  $solutionSubmit[$key] = $value;
1179  }
1180  if ($post->has($key . "_unit")) {
1181  $value = $post->retrieve(
1182  $key . "_unit",
1183  $this->dic->refinery()->kindlyTo()->string()
1184  );
1185  $solutionSubmit[$key . "_unit"] = $value;
1186  }
1187  }
1188  return $solutionSubmit;
1189  }
1190 
1191  public function validateSolutionSubmit(): bool
1192  {
1193  foreach ($this->getSolutionSubmit() as $value) {
1194  if ($value && !$this->isValidSolutionResultValue($value)) {
1195  $this->tpl->setOnScreenMessage(
1196  'failure',
1197  $this->lng->txt("err_no_numeric_value"),
1198  true
1199  );
1200  return false;
1201  }
1202  }
1203 
1204  return true;
1205  }
1206 
1207  public function getOperators(string $expression): array
1208  {
1209  return ilOperatorsExpressionMapping::getOperatorsByExpression($expression);
1210  }
1211 
1212  public function getExpressionTypes(): array
1213  {
1214  return [
1218  ];
1219  }
1220 
1221  public function getUserQuestionResult(
1222  int $active_id,
1223  int $pass
1225  $result = new ilUserQuestionResult($this, $active_id, $pass);
1226 
1227  $maxStep = $this->lookupMaxStep($active_id, $pass);
1228  if ($maxStep > 0) {
1229  $data = $this->db->queryF(
1230  "SELECT value1, value2 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
1231  ['integer', 'integer', 'integer','integer'],
1232  [$active_id, $pass, $this->getId(), $maxStep]
1233  );
1234  } else {
1235  $data = $this->db->queryF(
1236  "SELECT value1, value2 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
1237  ['integer', 'integer', 'integer'],
1238  [$active_id, $pass, $this->getId()]
1239  );
1240  }
1241 
1242  while ($row = $this->db->fetchAssoc($data)) {
1243  if (strstr($row['value1'], '$r') && $row['value2'] != null) {
1244  $result->addKeyValue(str_replace('$r', "", $row['value1']), $row['value2']);
1245  }
1246  }
1247 
1248  $points = $this->calculateReachedPoints($active_id, $pass);
1249  $max_points = $this->getMaximumPoints();
1250 
1251  $result->setReachedPercentage(($points / $max_points) * 100);
1252 
1253  return $result;
1254  }
1255 
1264  public function getAvailableAnswerOptions($index = null)
1265  {
1266  if ($index !== null) {
1267  return $this->getResult('$r' . ($index + 1));
1268  } else {
1269  return $this->getResults();
1270  }
1271  }
1272 
1273  public function toLog(AdditionalInformationGenerator $additional_info): array
1274  {
1275  return [
1276  AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->getQuestionType(),
1277  AdditionalInformationGenerator::KEY_QUESTION_TITLE => $this->getTitleForHTMLOutput(),
1278  AdditionalInformationGenerator::KEY_QUESTION_TEXT => $this->formatSAQuestion($this->getQuestion()),
1279  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_VARIABLES => $this->buildVariablesForLog(
1280  $this->getVariables(),
1281  $additional_info->getNoneTag()
1282  ),
1283  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_RESULTS => $this->buildResultsForLog(
1284  $this->getResults(),
1285  $additional_info->getNoneTag()
1286  ),
1287  AdditionalInformationGenerator::KEY_FEEDBACK => [
1288  AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1289  AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1290  ]
1291  ];
1292  }
1293 
1298  private function buildVariablesForLog(array $variables, string $none_tag): array
1299  {
1300  return array_reduce(
1301  $variables,
1302  function (array $c, assFormulaQuestionVariable $v) use ($none_tag): array {
1303  $c[$v->getVariable()] = [
1304  AdditionalInformationGenerator::KEY_QUESTION_LOWER_LIMIT => $v->getRangeMinTxt(),
1305  AdditionalInformationGenerator::KEY_QUESTION_UPPER_LIMIT => $v->getRangeMaxTxt(),
1306  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_PRECISION => $v->getPrecision(),
1307  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_INTPRECISION => $v->getIntprecision(),
1308  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_UNIT => $v->getUnit() ?? $none_tag
1309  ];
1310  return $c;
1311  },
1312  []
1313  );
1314  }
1315 
1320  private function buildResultsForLog(array $results, string $none_tag): array
1321  {
1322  return array_reduce(
1323  $results,
1324  function (array $c, assFormulaQuestionResult $r) use ($none_tag): array {
1325  $c[$r->getResult()] = [
1326  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_RESULT_TYPE => $r->getResultType(),
1327  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_FORMULA => $r->getFormula(),
1328  AdditionalInformationGenerator::KEY_QUESTION_REACHABLE_POINTS => $r->getPoints(),
1329  AdditionalInformationGenerator::KEY_QUESTION_LOWER_LIMIT => $r->getRangeMinTxt(),
1330  AdditionalInformationGenerator::KEY_QUESTION_UPPER_LIMIT => $r->getRangeMaxTxt(),
1331  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_TOLERANCE => $r->getTolerance(),
1332  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_PRECISION => $r->getPrecision(),
1333  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_UNIT => $r->getUnit() ?? $none_tag
1334  ];
1335  return $c;
1336  },
1337  []
1338  );
1339  }
1340 
1341  protected function solutionValuesToLog(
1342  AdditionalInformationGenerator $additional_info,
1343  array $solution_values
1344  ): array {
1345  return array_reduce(
1346  $solution_values,
1347  function (array $c, array $v) use ($additional_info): array {
1348  if (str_starts_with($v['value1'], '$v')) {
1349  $var = $this->getVariable($v['value1']);
1350  if ($var === null) {
1351  $c[$v['value1']] = $additional_info->getNoneTag();
1352  return $c;
1353  }
1354  if ($var->getUnit() !== null) {
1355  $c[$v['value1']] = $v['value2'] . $var->getUnit()->getUnit();
1356  return $c;
1357  }
1358  }
1359 
1360  if (strpos($v['value1'], '_unit')) {
1361  $unit = $this->getUnitrepository()->getUnit($v['value2']);
1362  $c[$v['value1']] = $unit->getUnit() ?? $additional_info->getNoneTag();
1363  return $c;
1364  }
1365 
1366  $c[$v['value1']] = $v['value2'];
1367  return $c;
1368  },
1369  []
1370  );
1371  }
1372 
1373  public function solutionValuesToText(array $solution_values): array
1374  {
1375  ksort($solution_values);
1376  return array_reduce(
1377  $solution_values,
1378  function (array $c, array $v): array {
1379  if (!str_starts_with($v['value1'], '$r')) {
1380  return $c;
1381  }
1382  if (!strpos($v['value1'], '_unit')) {
1383  $c[$v['value1']] = "{$v['value1']} = {$v['value2']}";
1384  return $c;
1385  }
1386  $k = substr($v['value1'], 0, -5);
1387  if (array_key_exists($k, $c)) {
1388  $c[$k] .= $v['value2'];
1389  }
1390  return $c;
1391  },
1392  []
1393  );
1394  }
1395 
1396  public function getCorrectSolutionForTextOutput(int $active_id, int $pass): array
1397  {
1398  $best_solution = $this->getBestSolution($this->getSolutionValues($active_id, $pass));
1399  return array_map(
1400  function (string $v) use ($best_solution): string {
1401  $solution = "{$v} = {$best_solution[$v]['value']}";
1402  if (isset($best_solution['unit'])) {
1403  $solution .= "{$this->unitrepository->getUnit($best_solution['unit'])->getUnit()}";
1404  }
1405  return $solution;
1406  },
1407  array_keys($best_solution)
1408  );
1409  }
1410 
1411  public function getVariablesAsTextArray(int $active_id, int $pass): array
1412  {
1413  $variables = $this->getVariableSolutionValuesForPass($active_id, $pass);
1414  return array_map(
1415  function (string $v) use ($variables): string {
1416  $variable = "{$v} = {$variables[$v]}";
1417  if ($this->getVariable($v)->getUnit() !== null) {
1418  $variable .= $this->getVariable($v)->getUnit()->getUnit();
1419  }
1420  return $variable;
1421  },
1422  array_keys($variables)
1423  );
1424  }
1425 }
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
getOperators(string $expression)
Get all available operations for a specific question.
getUserQuestionResult(int $active_id, int $pass)
Get the user solution for a question by active_id and the test pass.
getSolutionValues(int $active_id, ?int $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
cloneQuestionTypeSpecificProperties(\assQuestion $target)
getBestSolution(array $solutions)
getVariablesAsTextArray(int $active_id, int $pass)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static _div($left_operand, $right_operand, int $scale=50)
substituteVariables(array $userdata, bool $graphicalOutput=false, bool $forsolution=false, bool $result_output=false, array $correctness_icons=[])
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getResultUnits(assFormulaQuestionResult $result)
saveToDb(?int $original_id=null)
setOwner(int $owner=-1)
getVariableSolutionValuesForPass(int $active_id, int $pass)
ensureNonNegativePoints(float $points)
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
Class ilUnitConfigurationRepository.
Class for single choice questions assFormulaQuestion is a class for single choice questions...
PassPresentedVariablesRepo $pass_presented_variables_repo
static prepareFormOutput($a_str, bool $a_strip=false)
$c
Definition: deliver.php:25
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
setComment(string $comment="")
lookupForExistingSolutions(int $active_id, int $pass)
getVariable(string $variable)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
purifyAndPrepareTextAreaOutput(string $content)
getCorrectSolutionForTextOutput(int $active_id, int $pass)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
fetchAllVariables(string $question_text)
removeExistingSolutions(int $active_id, int $pass)
hasRequiredVariableSolutionValues(array $userSolution)
solutionValuesToLog(AdditionalInformationGenerator $additional_info, array $solution_values)
ilUnitConfigurationRepository $unitrepository
__construct(string $title="", string $comment="", string $author="", int $owner=-1, string $question="")
Stores random-generated parts of questions in order to present the user with a fixed question during ...
canUseAdvancedRating($result)
Check if advanced rating can be used for a result.
setPoints(float $points)
addResultUnit(?assFormulaQuestionResult $result, ?assFormulaQuestionUnit $unit)
setObjId(int $obj_id=0)
hasResultUnit(assFormulaQuestionResult $result, int $unit_id)
saveQuestionDataToDb(?int $original_id=null)
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
static lookupUnitFactor(int $a_unit_id)
buildVariablesForLog(array $variables, string $none_tag)
isValidSolutionResultValue(string $submittedValue)
addResult(assFormulaQuestionResult $result)
getSolutionMaxPass(int $active_id)
toLog(AdditionalInformationGenerator $additional_info)
solutionValuesToText(array $solution_values)
__construct(Container $dic, ilPlugin $plugin)
isComplete()
Returns true, if the question is complete for use.
setOriginalId(?int $original_id)
setTitle(string $title="")
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $previewSession)
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
setLifecycle(ilAssQuestionLifecycle $lifecycle)
buildResultsForLog(array $results, string $none_tag)
addVariable(assFormulaQuestionVariable $variable)
getExpressionTypes()
Get all available expression types for a specific question.
addResultUnits(?assFormulaQuestionResult $result, ?array $unit_ids)
lookupMaxStep(int $active_id, int $pass)
setAuthor(string $author="")
$post
Definition: ltitoken.php:46
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
static convertDecimalToCoprimeFraction($decimal_value, $tolerance=1.e-9)
generateResultInputHTML(string $result_key, string $result_value, bool $forsolution)
setUnitrepository(\ilUnitConfigurationRepository $unitrepository)
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
savePreviewData(ilAssQuestionPreviewSession $previewSession)
setQuestion(string $question="")
$r