ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilTestScoring.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
41 {
42  private bool $preserve_manual_scores = false;
43  private array $recalculated_passes = [];
44  private int $question_id = 0;
45 
46  protected ilLanguage $lng;
47 
51  protected array $question_cache = [];
52 
56  protected array $participants = [];
57 
58  protected string $initiator_name;
59  protected int $initiator_id;
60 
61  public function __construct(
62  private ilObjTest $test,
63  private ilDBInterface $db
64  ) {
65  global $DIC;
66  $this->lng = $DIC->language();
67  $this->initiator_name = $DIC->user()->getFullname() . " (" . $DIC->user()->getLogin() . ")";
68  $this->initiator_id = $DIC->user()->getId();
69  }
70 
71  public function setPreserveManualScores(bool $preserve_manual_scores): void
72  {
73  $this->preserve_manual_scores = $preserve_manual_scores;
74  }
75 
76  public function getPreserveManualScores(): bool
77  {
79  }
80 
81  public function getQuestionId(): int
82  {
83  return $this->question_id;
84  }
85 
86  public function setQuestionId(int $question_id): void
87  {
88  $this->question_id = $question_id;
89  }
90 
94  public function recalculateSolutions(): array
95  {
96  $factory = new ilTestEvaluationFactory($this->db, $this->test);
97  $this->participants = $factory->getCorrectionsEvaluationData()->getParticipants();
98 
99  foreach ($this->participants as $active_id => $userdata) {
100  if (is_object($userdata) && is_array($userdata->getPasses())) {
101  $this->recalculatePasses($userdata, $active_id);
103  $this->test->getId(),
104  $userdata->getUserID()
105  );
106  }
107  }
108 
109  return $this->participants;
110  }
111 
112  public function recalculateSolution(int $active_id, int $pass): void
113  {
114  $user_data = $this
115  ->test
116  ->getCompleteEvaluationData(false)
117  ->getParticipant($active_id)
118  ->getPass($pass);
119 
120  $this->recalculatePass($user_data, $active_id, $pass);
121  $this->test->updateTestResultCache($active_id);
122  }
123 
124  public function recalculatePasses(ilTestEvaluationUserData $userdata, int $active_id): void
125  {
126  $passes = $userdata->getPasses();
127  foreach ($passes as $pass => $passdata) {
128  if (is_object($passdata)) {
129  $this->recalculatePass($passdata, $active_id, $pass);
130  $this->addRecalculatedPassByActive($active_id, $pass);
131  }
132  }
133  $this->test->updateTestResultCache($active_id);
134  }
135 
136  public function recalculatePass(
137  ilTestEvaluationPassData $passdata,
138  int $active_id,
139  int $pass
140  ) {
141  $questions = $passdata->getAnsweredQuestions();
142  foreach ($questions as $question_data) {
143  if (!$this->getQuestionId() || $this->getQuestionId() === $question_data['id']) {
144  $this->recalculateQuestionScore($question_data['id'], $active_id, $pass, $question_data);
145  }
146  }
147  }
148 
149  public function recalculateQuestionScore(int $q_id, int $active_id, int $pass, array $questiondata): void
150  {
151  if (!isset($this->question_cache[$q_id])) {
152  $this->question_cache[$q_id] = $this->test->createQuestionGUI("", $q_id)->object;
153  }
154  $question = $this->question_cache[$q_id];
155 
156  $old_points = $question->getReachedPoints($active_id, $pass);
157  $reached = $question->adjustReachedPointsByScoringOptions(
158  $question->calculateReachedPoints($active_id, $pass),
159  $active_id,
160  $pass
161  );
162 
163  if ($this->preserve_manual_scores && $questiondata['manual'] == '1') {
164  return;
165  }
166 
167  $this->updateReachedPoints(
168  $active_id,
169  $questiondata['id'],
170  $old_points,
171  $reached,
172  $question->getMaximumPoints(),
173  $pass,
174  );
175  }
176 
184  public function updateReachedPoints(int $active_id, int $question_id, float $old_points, float $points, float $max_points, int $pass): void
185  {
186  // Only update the test results if necessary
187  $has_changed = $old_points !== $points;
188  if ($has_changed && $points <= $max_points) {
189  $this->db->update(
190  'tst_test_result',
191  [
192  'points' => ['float', $points],
193  'tstamp' => ['integer', time()],
194  ],
195  [
196  'active_fi' => ['integer', $active_id],
197  'question_fi' => ['integer', $question_id],
198  'pass' => ['integer', $pass]
199  ]
200  );
201  }
202 
203  // Always update the pass result as the maximum points might have changed
205  $values = [
206  'maxpoints' => ['float', $data['points']],
207  'tstamp' => ['integer', time()],
208  ];
209 
210  if ($has_changed) {
211  $result = $this->db->queryF(
212  'SELECT SUM(points) reachedpoints FROM tst_test_result WHERE active_fi = %s AND pass = %s',
213  ['integer', 'integer'],
214  [$active_id, $pass]
215  );
216  $values['points'] = ['float', $result->fetchAssoc()['reachedpoints'] ?? 0.0];
217  }
218 
219  $this->db->update(
220  'tst_pass_result',
221  $values,
222  ['active_fi' => ['integer', $active_id], 'pass' => ['integer', $pass]]
223  );
224 
227  $msg = $this->lng->txtlng('assessment', 'log_answer_changed_points', ilObjAssessmentFolder::_getLogLanguage());
228  $msg = sprintf(
229  $msg,
230  isset($this->participants[$active_id]) && $this->participants[$active_id] instanceof ilTestEvaluationUserData ?
231  $this->participants[$active_id]->getName() :
232  '',
233  $old_points,
234  $points,
235  $this->initiator_name
236  );
238  $this->initiator_id,
239  $this->test->getId(),
240  $msg,
242  );
243  }
244  }
245 
249  public function calculateBestSolutionForTest(): string
250  {
251  $solution = '';
252  foreach ($this->test->getAllQuestions() as $question) {
254  $question_gui = $this->test->createQuestionGUI("", $question['question_id']);
255  $solution .= '<h1>' . $question_gui->object->getTitleForHTMLOutput() . '</h1>';
256  $solution .= $question_gui->getSolutionOutput(0, null, true, true, false, false, true, false);
257  }
258 
259  return $solution;
260  }
261 
263  {
264  $this->recalculated_passes = [];
265  }
266 
267  public function getRecalculatedPassesByActives(): array
268  {
270  }
271 
272  public function addRecalculatedPassByActive(int $active_id, int $pass): void
273  {
274  if (! array_key_exists($active_id, $this->recalculated_passes)
275  || !is_array($this->recalculated_passes[$active_id])
276  ) {
277  $this->recalculated_passes[$active_id] = [];
278  }
279 
280  $this->recalculated_passes[$active_id][] = $pass;
281  }
282 
283  public function removeAllQuestionResults($question_id)
284  {
285  $query = "DELETE FROM tst_test_result WHERE question_fi = %s";
286  $this->db->manipulateF($query, array('integer'), array($question_id));
287  }
288 
293  public function updatePassAndTestResults(array $active_ids): void
294  {
295  foreach ($active_ids as $active_id) {
296  $passSelector = new ilTestPassesSelector($this->db, $this->test);
297  $passSelector->setActiveId($active_id);
298 
299  foreach ($passSelector->getExistingPasses() as $pass) {
300  $this->test->updateTestPassResults($active_id, $pass, $this->test->areObligationsEnabled());
301  }
302 
303  $this->test->updateTestResultCache($active_id);
304  }
305  }
306 
307  public function getNumManualScorings(): int
308  {
309  $query = "
310  SELECT COUNT(*) num_manual_scorings
311  FROM tst_test_result tres
312 
313  INNER JOIN tst_active tact
314  ON tact.active_id = tres.active_fi
315  AND tact.test_fi = %s
316 
317  WHERE tres.manual = 1
318  ";
319 
320  $types = array('integer');
321  $values = array($this->test->getTestId());
322 
323  if ($this->getQuestionId()) {
324  $query .= "
325  AND tres.question_fi = %s
326  ";
327 
328  $types[] = 'integer';
329  $values[] = $this->getQuestionId();
330  }
331 
332  $res = $this->db->queryF($query, $types, $values);
333 
334  while ($row = $this->db->fetchAssoc($res)) {
335  return (int) $row['num_manual_scorings'];
336  }
337 
338  return 0;
339  }
340 }
$res
Definition: ltiservices.php:69
removeAllQuestionResults($question_id)
static _addLog( $user_id, $object_id, $logtext, $question_id=0, $original_id=0, $test_only=false, $test_ref_id=0)
Add an assessment log entry.
updateReachedPoints(int $active_id, int $question_id, float $old_points, float $points, float $max_points, int $pass)
This is an optimized version of ::_setReachedPoints that only executes updates in the database if nec...
setQuestionId(int $question_id)
setPreserveManualScores(bool $preserve_manual_scores)
recalculatePasses(ilTestEvaluationUserData $userdata, int $active_id)
global $DIC
Definition: feed.php:28
static _getUserIdFromActiveId(int $active_id)
static _getQuestionCountAndPointsForPassOfParticipant($active_id, $pass)
recalculateQuestionScore(int $q_id, int $active_id, int $pass, array $questiondata)
Class ilTestScoring.
updatePassAndTestResults(array $active_ids)
recalculateSolution(int $active_id, int $pass)
addRecalculatedPassByActive(int $active_id, int $pass)
recalculatePass(ilTestEvaluationPassData $passdata, int $active_id, int $pass)
static _updateObjectiveResult(int $a_user_id, int $a_active_id, int $a_question_id)
__construct(private ilObjTest $test, private ilDBInterface $db)
static _updateStatus(int $a_obj_id, int $a_usr_id, ?object $a_obj=null, bool $a_percentage=false, bool $a_force_raise=false)