ILIAS  trunk Revision v11.0_alpha-1723-g8e69f309bab
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
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'] = $solution_value['value2'];
825  }
826  }
827 
828  $points = 0;
829  foreach ($this->getResults() as $result) {
830  $points += $result->getReachedPoints(
831  $this->getVariables(),
832  $this->getResults(),
833  $user_solution[$result->getResult()]['value'] ?? '',
834  $user_solution[$result->getResult()]['unit'] ?? '',
835  $this->unitrepository->getUnits()
836  );
837  }
838 
839  return (float) $points;
840  }
841 
843  {
844  $user_solution = $previewSession->getParticipantsSolution();
845 
846  $points = 0;
847  foreach ($this->getResults() as $result) {
848  $v = isset($user_solution[$result->getResult()]) ? $user_solution[$result->getResult()] : null;
849  $u = isset($user_solution[$result->getResult() . '_unit']) ? $user_solution[$result->getResult() . '_unit'] : null;
850 
851  $points += $result->getReachedPoints(
852  $this->getVariables(),
853  $this->getResults(),
854  $v,
855  $u,
856  $this->unitrepository->getUnits()
857  );
858  }
859 
860  $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $points);
861 
862  return $this->ensureNonNegativePoints($reachedPoints);
863  }
864 
865  protected function isValidSolutionResultValue(string $submittedValue): bool
866  {
867  $submittedValue = str_replace(',', '.', $submittedValue);
868 
869  if (is_numeric($submittedValue)) {
870  return true;
871  }
872 
873  if (preg_match('/^[-+]?\d+\/\d+$/', $submittedValue)) {
874  return true;
875  }
876 
877  return false;
878  }
879 
880  public function saveWorkingData(
881  int $active_id,
882  ?int $pass = null,
883  bool $authorized = true
884  ): bool {
885  if (is_null($pass)) {
886  $pass = ilObjTest::_getPass($active_id);
887  }
888 
889  $answer = $this->getSolutionSubmit();
890  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(
891  function () use ($answer, $active_id, $pass, $authorized) {
892  foreach ($answer as $key => $value) {
893  $matches = null;
894  if (preg_match('/^result_(\$r\d+)$/', $key, $matches) !== false) {
895  $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]);
896 
897  if ($this->getStep() !== null) {
898  $queryResult .= " AND step = " . $this->db->quote((int) $this->getStep(), 'integer') . " ";
899  }
900 
901  $result = $this->db->queryF(
902  $queryResult,
903  ['integer', 'integer', 'integer', 'integer'],
904  [$active_id, $pass, $this->getId(), (int) $authorized]
905  );
906  if ($result->numRows()) {
907  while ($row = $this->db->fetchAssoc($result)) {
908  $this->db->manipulateF(
909  "DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
910  ['integer', 'integer'],
911  [$row['solution_id'], (int) $authorized]
912  );
913  }
914  }
915 
916  $this->saveCurrentSolution($active_id, $pass, $matches[1], str_replace(",", ".", $value), $authorized);
917  continue;
918  }
919 
920  if (preg_match('/^result_(\$r\d+)_unit$/', $key, $matches) !== false) {
921  $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");
922 
923  if ($this->getStep() !== null) {
924  $queryResultUnit .= " AND step = " . $this->db->quote((int) $this->getStep(), 'integer') . " ";
925  }
926 
927  $result = $this->db->queryF(
928  $queryResultUnit,
929  ['integer', 'integer', 'integer', 'integer'],
930  [$active_id, $pass, $this->getId(), (int) $authorized]
931  );
932  if ($result->numRows()) {
933  while ($row = $this->db->fetchAssoc($result)) {
934  $this->db->manipulateF(
935  "DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
936  ['integer', 'integer'],
937  [$row['solution_id'], (int) $authorized]
938  );
939  }
940  }
941 
942  $this->saveCurrentSolution($active_id, $pass, $matches[1] . "_unit", $value, $authorized);
943  }
944  }
945  }
946  );
947 
948  return true;
949  }
950 
954  public function lookupForExistingSolutions(int $active_id, int $pass): array
955  {
956  $return = [
957  'authorized' => false,
958  'intermediate' => false
959  ];
960 
961  $query = "
962  SELECT authorized, COUNT(*) cnt
963  FROM tst_solutions
964  WHERE active_fi = " . $this->db->quote($active_id, 'integer') . "
965  AND question_fi = " . $this->db->quote($this->getId(), 'integer') . "
966  AND pass = " . $this->db->quote($pass, 'integer') . "
967  AND value1 like '\$r%'
968  AND value2 is not null
969  AND value2 <> ''
970  ";
971 
972  if ($this->getStep() !== null) {
973  $query .= " AND step = " . $this->db->quote((int) $this->getStep(), 'integer') . " ";
974  }
975 
976  $query .= "
977  GROUP BY authorized
978  ";
979 
980  $result = $this->db->query($query);
981 
982  while ($row = $this->db->fetchAssoc($result)) {
983  if ($row['authorized']) {
984  $return['authorized'] = $row['cnt'] > 0;
985  } else {
986  $return['intermediate'] = $row['cnt'] > 0;
987  }
988  }
989  return $return;
990  }
991 
992  public function removeExistingSolutions(int $active_id, int $pass): int
993  {
994  $query = "
995  DELETE FROM tst_solutions
996  WHERE active_fi = " . $this->db->quote($active_id, 'integer') . "
997  AND question_fi = " . $this->db->quote($this->getId(), 'integer') . "
998  AND pass = " . $this->db->quote($pass, 'integer') . "
999  AND value1 like '\$r%'
1000  ";
1001 
1002  if ($this->getStep() !== null) {
1003  $query .= " AND step = " . $this->db->quote((int) $this->getStep(), 'integer') . " ";
1004  }
1005 
1006  return $this->db->manipulate($query);
1007  }
1008 
1009  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession): void
1010  {
1011  $userSolution = $previewSession->getParticipantsSolution();
1012 
1013  foreach ($this->getSolutionSubmit() as $key => $val) {
1014  $matches = null;
1015 
1016  if (preg_match('/^result_(\$r\d+)$/', $key, $matches)) {
1017  $userSolution[$matches[1]] = $val;
1018  } elseif (preg_match('/^result_(\$r\d+)_unit$/', $key, $matches)) {
1019  $userSolution[$matches[1] . "_unit"] = $val;
1020  }
1021  }
1022 
1023  $previewSession->setParticipantsSolution($userSolution);
1024  }
1025 
1026  public function getQuestionType(): string
1027  {
1028  return "assFormulaQuestion";
1029  }
1030 
1031  public function getAdditionalTableName(): string
1032  {
1033  return "";
1034  }
1035 
1036  public function getAnswerTableName(): string
1037  {
1038  return "";
1039  }
1040 
1041  public function deleteAnswers(int $question_id): void
1042  {
1043  $affectedRows = $this->db->manipulateF(
1044  "DELETE FROM il_qpl_qst_fq_var WHERE question_fi = %s",
1045  ['integer'],
1046  [$question_id]
1047  );
1048 
1049  $affectedRows = $this->db->manipulateF(
1050  "DELETE FROM il_qpl_qst_fq_res WHERE question_fi = %s",
1051  ['integer'],
1052  [$question_id]
1053  );
1054 
1055  $affectedRows = $this->db->manipulateF(
1056  "DELETE FROM il_qpl_qst_fq_res_unit WHERE question_fi = %s",
1057  ['integer'],
1058  [$question_id]
1059  );
1060 
1061  $affectedRows = $this->db->manipulateF(
1062  "DELETE FROM il_qpl_qst_fq_ucat WHERE question_fi = %s",
1063  ['integer'],
1064  [$question_id]
1065  );
1066 
1067  $affectedRows = $this->db->manipulateF(
1068  "DELETE FROM il_qpl_qst_fq_unit WHERE question_fi = %s",
1069  ['integer'],
1070  [$question_id]
1071  );
1072  }
1073 
1074  public function getRTETextWithMediaObjects(): string
1075  {
1076  $text = parent::getRTETextWithMediaObjects();
1077  return $text;
1078  }
1079 
1080  public function getBestSolution(array $solutions): array
1081  {
1082  $user_solution = [];
1083 
1084  foreach ($solutions as $solution_value) {
1085  if (preg_match('/^(\$v\d+)$/', $solution_value['value1'], $matches)) {
1086  $user_solution[$matches[1]] = $solution_value['value2'];
1087  $varObj = $this->getVariable($matches[1]);
1088  $varObj->setValue($solution_value['value2']);
1089  } elseif (preg_match('/^(\$r\d+)$/', $solution_value['value1'], $matches)) {
1090  if (!array_key_exists($matches[1], $user_solution)) {
1091  $user_solution[$matches[1]] = [];
1092  }
1093  $user_solution[$matches[1]]['value'] = $solution_value['value2'];
1094  } elseif (preg_match('/^(\$r\d+)_unit$/', $solution_value['value1'], $matches)) {
1095  if (!array_key_exists($matches[1], $user_solution)) {
1096  $user_solution[$matches[1]] = [];
1097  }
1098  $user_solution[$matches[1]]['unit'] = $solution_value['value2'];
1099  }
1100  }
1101  foreach ($this->getResults() as $result) {
1102  $resVal = $result->calculateFormula($this->getVariables(), $this->getResults(), $this->getId(), false);
1103 
1104  if (is_object($result->getUnit())) {
1105  $user_solution[$result->getResult()]['unit'] = $result->getUnit()->getId();
1106  $user_solution[$result->getResult()]['value'] = $resVal;
1107  } elseif ($result->getUnit() === null) {
1108  $unit_factor = 1;
1109  // there is no fix result_unit, any "available unit" is accepted
1110 
1111  $available_units = $result->getAvailableResultUnits(parent::getId());
1112  $result_name = $result->getResult();
1113 
1114  $check_unit = false;
1115  if (array_key_exists($result_name, $available_units) &&
1116  $available_units[$result_name] !== null) {
1117  $check_unit = in_array($user_solution[$result_name]['unit'], $available_units[$result_name]);
1118  }
1119 
1120  if ($check_unit == true) {
1121  //get unit-factor
1122  $unit_factor = assFormulaQuestionUnit::lookupUnitFactor($user_solution[$result_name]['unit']);
1123  }
1124 
1125  try {
1126  $user_solution[$result->getResult()]['value'] = ilMath::_div($resVal, $unit_factor, 55);
1127  } catch (ilMathDivisionByZeroException $ex) {
1128  $user_solution[$result->getResult()]['value'] = 0;
1129  }
1130  }
1131  if ($result->getResultType() == assFormulaQuestionResult::RESULT_CO_FRAC
1132  || $result->getResultType() == assFormulaQuestionResult::RESULT_FRAC) {
1134  if (is_array($value)) {
1135  $user_solution[$result->getResult()]['value'] = $value[0];
1136  $user_solution[$result->getResult()]['frac_helper'] = $value[1];
1137  } else {
1138  $user_solution[$result->getResult()]['value'] = $value;
1139  $user_solution[$result->getResult()]['frac_helper'] = null;
1140  }
1141  } else {
1142  $user_solution[$result->getResult()]['value'] = round((float) $user_solution[$result->getResult()]['value'], $result->getPrecision());
1143  }
1144  }
1145  return $user_solution;
1146  }
1147 
1148  public function setId(int $id = -1): void
1149  {
1150  parent::setId($id);
1151  $this->unitrepository->setConsumerId($this->getId());
1152  }
1153 
1154  public function setUnitrepository(\ilUnitConfigurationRepository $unitrepository): void
1155  {
1156  $this->unitrepository = $unitrepository;
1157  }
1158 
1160  {
1161  return $this->unitrepository;
1162  }
1163 
1167  protected function getSolutionSubmit(): array
1168  {
1169  $solutionSubmit = [];
1170 
1171  $post = $this->dic->http()->wrapper()->post();
1172 
1173  foreach ($this->getResults() as $index => $a) {
1174  $key = "result_$index";
1175  if ($post->has($key)) {
1176  $value = $post->retrieve(
1177  $key,
1178  $this->dic->refinery()->kindlyTo()->string()
1179  );
1180 
1181  $solutionSubmit[$key] = $value;
1182  }
1183  if ($post->has($key . "_unit")) {
1184  $value = $post->retrieve(
1185  $key . "_unit",
1186  $this->dic->refinery()->kindlyTo()->string()
1187  );
1188  $solutionSubmit[$key . "_unit"] = $value;
1189  }
1190  }
1191  return $solutionSubmit;
1192  }
1193 
1194  public function validateSolutionSubmit(): bool
1195  {
1196  foreach ($this->getSolutionSubmit() as $value) {
1197  if ($value && !$this->isValidSolutionResultValue($value)) {
1198  $this->tpl->setOnScreenMessage(
1199  'failure',
1200  $this->lng->txt("err_no_numeric_value"),
1201  true
1202  );
1203  return false;
1204  }
1205  }
1206 
1207  return true;
1208  }
1209 
1210  public function getOperators(string $expression): array
1211  {
1212  return ilOperatorsExpressionMapping::getOperatorsByExpression($expression);
1213  }
1214 
1215  public function getExpressionTypes(): array
1216  {
1217  return [
1221  ];
1222  }
1223 
1224  public function getUserQuestionResult(
1225  int $active_id,
1226  int $pass
1228  $result = new ilUserQuestionResult($this, $active_id, $pass);
1229 
1230  $maxStep = $this->lookupMaxStep($active_id, $pass);
1231  if ($maxStep > 0) {
1232  $data = $this->db->queryF(
1233  "SELECT value1, value2 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
1234  ['integer', 'integer', 'integer','integer'],
1235  [$active_id, $pass, $this->getId(), $maxStep]
1236  );
1237  } else {
1238  $data = $this->db->queryF(
1239  "SELECT value1, value2 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
1240  ['integer', 'integer', 'integer'],
1241  [$active_id, $pass, $this->getId()]
1242  );
1243  }
1244 
1245  while ($row = $this->db->fetchAssoc($data)) {
1246  if (strstr($row['value1'], '$r') && $row['value2'] != null) {
1247  $result->addKeyValue(str_replace('$r', "", $row['value1']), $row['value2']);
1248  }
1249  }
1250 
1251  $points = $this->calculateReachedPoints($active_id, $pass);
1252  $max_points = $this->getMaximumPoints();
1253 
1254  $result->setReachedPercentage(($points / $max_points) * 100);
1255 
1256  return $result;
1257  }
1258 
1267  public function getAvailableAnswerOptions($index = null)
1268  {
1269  if ($index !== null) {
1270  return $this->getResult('$r' . ($index + 1));
1271  } else {
1272  return $this->getResults();
1273  }
1274  }
1275 
1276  public function toLog(AdditionalInformationGenerator $additional_info): array
1277  {
1278  return [
1279  AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->getQuestionType(),
1280  AdditionalInformationGenerator::KEY_QUESTION_TITLE => $this->getTitleForHTMLOutput(),
1281  AdditionalInformationGenerator::KEY_QUESTION_TEXT => $this->formatSAQuestion($this->getQuestion()),
1282  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_VARIABLES => $this->buildVariablesForLog(
1283  $this->getVariables(),
1284  $additional_info->getNoneTag()
1285  ),
1286  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_RESULTS => $this->buildResultsForLog(
1287  $this->getResults(),
1288  $additional_info->getNoneTag()
1289  ),
1290  AdditionalInformationGenerator::KEY_FEEDBACK => [
1291  AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1292  AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1293  ]
1294  ];
1295  }
1296 
1301  private function buildVariablesForLog(array $variables, string $none_tag): array
1302  {
1303  return array_reduce(
1304  $variables,
1305  function (array $c, assFormulaQuestionVariable $v) use ($none_tag): array {
1306  $c[$v->getVariable()] = [
1307  AdditionalInformationGenerator::KEY_QUESTION_LOWER_LIMIT => $v->getRangeMinTxt(),
1308  AdditionalInformationGenerator::KEY_QUESTION_UPPER_LIMIT => $v->getRangeMaxTxt(),
1309  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_PRECISION => $v->getPrecision(),
1310  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_INTPRECISION => $v->getIntprecision(),
1311  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_UNIT => $v->getUnit() ?? $none_tag
1312  ];
1313  return $c;
1314  },
1315  []
1316  );
1317  }
1318 
1323  private function buildResultsForLog(array $results, string $none_tag): array
1324  {
1325  return array_reduce(
1326  $results,
1327  function (array $c, assFormulaQuestionResult $r) use ($none_tag): array {
1328  $c[$r->getResult()] = [
1329  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_RESULT_TYPE => $r->getResultType(),
1330  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_FORMULA => $r->getFormula(),
1331  AdditionalInformationGenerator::KEY_QUESTION_REACHABLE_POINTS => $r->getPoints(),
1332  AdditionalInformationGenerator::KEY_QUESTION_LOWER_LIMIT => $r->getRangeMinTxt(),
1333  AdditionalInformationGenerator::KEY_QUESTION_UPPER_LIMIT => $r->getRangeMaxTxt(),
1334  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_TOLERANCE => $r->getTolerance(),
1335  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_PRECISION => $r->getPrecision(),
1336  AdditionalInformationGenerator::KEY_QUESTION_FORMULA_UNIT => $r->getUnit() ?? $none_tag
1337  ];
1338  return $c;
1339  },
1340  []
1341  );
1342  }
1343 
1344  protected function solutionValuesToLog(
1345  AdditionalInformationGenerator $additional_info,
1346  array $solution_values
1347  ): array {
1348  return array_reduce(
1349  $solution_values,
1350  function (array $c, array $v) use ($additional_info): array {
1351  if (str_starts_with($v['value1'], '$v')) {
1352  $var = $this->getVariable($v['value1']);
1353  if ($var === null) {
1354  $c[$v['value1']] = $additional_info->getNoneTag();
1355  return $c;
1356  }
1357  if ($var->getUnit() !== null) {
1358  $c[$v['value1']] = $v['value2'] . $var->getUnit()->getUnit();
1359  return $c;
1360  }
1361  }
1362 
1363  if (strpos($v['value1'], '_unit')) {
1364  $unit = $this->getUnitrepository()->getUnit($v['value2']);
1365  $c[$v['value1']] = $unit->getUnit() ?? $additional_info->getNoneTag();
1366  return $c;
1367  }
1368 
1369  $c[$v['value1']] = $v['value2'];
1370  return $c;
1371  },
1372  []
1373  );
1374  }
1375 
1376  public function solutionValuesToText(array $solution_values): array
1377  {
1378  ksort($solution_values);
1379  return array_reduce(
1380  $solution_values,
1381  function (array $c, array $v): array {
1382  if (!str_starts_with($v['value1'], '$r')) {
1383  return $c;
1384  }
1385  if (!strpos($v['value1'], '_unit')) {
1386  $c[$v['value1']] = "{$v['value1']} = {$v['value2']}";
1387  return $c;
1388  }
1389  $k = substr($v['value1'], 0, -5);
1390  if (array_key_exists($k, $c)) {
1391  $c[$k] .= $v['value2'];
1392  }
1393  return $c;
1394  },
1395  []
1396  );
1397  }
1398 
1399  public function getCorrectSolutionForTextOutput(int $active_id, int $pass): array
1400  {
1401  $best_solution = $this->getBestSolution($this->getSolutionValues($active_id, $pass));
1402  return array_map(
1403  function (string $v) use ($best_solution): string {
1404  $solution = "{$v} = {$best_solution[$v]['value']}";
1405  if (isset($best_solution['unit'])) {
1406  $solution .= "{$this->unitrepository->getUnit($best_solution['unit'])->getUnit()}";
1407  }
1408  return $solution;
1409  },
1410  array_keys($best_solution)
1411  );
1412  }
1413 
1414  public function getVariablesAsTextArray(int $active_id, int $pass): array
1415  {
1416  $variables = $this->getVariableSolutionValuesForPass($active_id, $pass);
1417  return array_map(
1418  function (string $v) use ($variables): string {
1419  $variable = "{$v} = {$variables[$v]}";
1420  if ($this->getVariable($v)->getUnit() !== null) {
1421  $variable .= $this->getVariable($v)->getUnit()->getUnit();
1422  }
1423  return $variable;
1424  },
1425  array_keys($variables)
1426  );
1427  }
1428 }
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)
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $preview_session, $reached_points)
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