ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
TestScoring.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
26 
47 {
48  private bool $preserve_manual_scores = false;
49  private int $question_id = 0;
50 
54  protected array $question_cache = [];
55 
56  public function __construct(
57  private \ilObjTest $test,
58  private \ilObjUser $scorer,
59  private \ilDBInterface $db,
60  private readonly TestResultRepository $test_result_repository
61  ) {
62  }
63 
64  public function setPreserveManualScores(bool $preserve_manual_scores): void
65  {
66  $this->preserve_manual_scores = $preserve_manual_scores;
67  }
68 
69  public function getPreserveManualScores(): bool
70  {
72  }
73 
74  public function getQuestionId(): int
75  {
76  return $this->question_id;
77  }
78 
79  public function setQuestionId(int $question_id): void
80  {
81  $this->question_id = $question_id;
82  }
83 
84  public function recalculateSolutions(): array
85  {
86  $factory = new \ilTestEvaluationFactory($this->db, $this->test);
87  $participants = $factory->getCorrectionsEvaluationData()->getParticipants();
88 
89  foreach ($participants as $active_id => $userdata) {
90  if (is_object($userdata) && is_array($userdata->getPasses())) {
91  $this->recalculatePasses($userdata, $active_id);
93  $this->test->getId(),
94  $userdata->getUserID()
95  );
96  }
97  }
98 
99  return $participants;
100 
101  }
102 
103  public function recalculateSolution(int $active_id, int $pass): void
104  {
105  $user_data = $this
106  ->test
107  ->getCompleteEvaluationData()
108  ->getParticipant($active_id);
109 
110  $this->recalculatePass(
111  $user_data->getPass($pass),
112  $user_data->getUserID(),
113  $active_id,
114  $pass
115  );
116  $this->test_result_repository->updateTestResultCache($active_id);
117  }
118 
119  public function recalculatePasses(\ilTestEvaluationUserData $userdata, int $active_id): void
120  {
121  $passes = $userdata->getPasses();
122  foreach ($passes as $pass => $passdata) {
123  if (is_object($passdata)) {
124  $this->recalculatePass($passdata, $userdata->getUserID(), $active_id, $pass);
125  }
126  }
127  $this->test_result_repository->updateTestResultCache($active_id);
128  }
129 
130  public function recalculatePass(
131  \ilTestEvaluationPassData $passdata,
132  int $user_id,
133  int $active_id,
134  int $pass
135  ): void {
136  $questions = $passdata->getAnsweredQuestions();
137  foreach ($questions as $question_data) {
138  if (!$this->getQuestionId() || $this->getQuestionId() === $question_data['id']) {
139  $this->recalculateQuestionScore($user_id, $active_id, $pass, $question_data);
140  }
141  }
142  }
143 
144  private function recalculateQuestionScore(
145  int $user_id,
146  int $active_id,
147  int $pass,
148  array $questiondata
149  ): void {
150  if ($this->preserve_manual_scores === true && $questiondata['manual'] === 1) {
151  return;
152  }
153 
154  $q_id = $questiondata['id'];
155  if (!isset($this->question_cache[$q_id])) {
156  $this->question_cache[$q_id] = $this->test->createQuestionGUI('', $q_id)->getObject();
157  }
158  $question = $this->question_cache[$q_id];
159 
160  $old_points = $question->getReachedPoints($active_id, $pass);
161  $reached = $question->adjustReachedPointsByScoringOptions(
162  $question->calculateReachedPoints($active_id, $pass),
163  $active_id,
164  );
165 
166  $this->updateReachedPoints(
167  $user_id,
168  $active_id,
169  $questiondata['id'],
170  $old_points,
171  $reached,
172  $question->getMaximumPoints(),
173  $pass,
174  );
175  }
176 
182  public function updateReachedPoints(
183  int $user_id,
184  int $active_id,
185  int $question_id,
186  float $old_points,
187  float $points,
188  float $max_points,
189  int $pass,
190  bool $manual_scoring = false
191  ): void {
192  // Only update the test results if necessary
193  $has_changed = $old_points !== $points;
194  if ($has_changed && $points <= $max_points) {
195  $this->db->update(
196  'tst_test_result',
197  [
198  'points' => [\ilDBConstants::T_FLOAT, $points],
199  'tstamp' => [\ilDBConstants::T_INTEGER, time()],
200  ],
201  [
202  'active_fi' => [\ilDBConstants::T_INTEGER, $active_id],
203  'question_fi' => [\ilDBConstants::T_INTEGER, $question_id],
204  'pass' => [\ilDBConstants::T_INTEGER, $pass]
205  ]
206  );
207  }
208 
209  // Always update the pass result as the maximum points might have changed
210  $data = $this->test->getQuestionCountAndPointsForPassOfParticipant($active_id, $pass);
211  $values = [
212  'maxpoints' => [\ilDBConstants::T_FLOAT, $data['points']],
213  'tstamp' => [\ilDBConstants::T_INTEGER, time()],
214  ];
215 
216  if ($has_changed) {
217  $result = $this->db->queryF(
218  'SELECT SUM(points) reachedpoints FROM tst_test_result WHERE active_fi = %s AND pass = %s',
220  [$active_id, $pass]
221  );
222  $values['points'] = [\ilDBConstants::T_FLOAT, $result->fetchAssoc()['reachedpoints'] ?? 0.0];
223  }
224 
225  $this->db->update(
226  'tst_pass_result',
227  $values,
228  ['active_fi' => [\ilDBConstants::T_INTEGER, $active_id], 'pass' => [\ilDBConstants::T_INTEGER, $pass]]
229  );
230 
231  \ilCourseObjectiveResult::_updateObjectiveResult($user_id, $active_id, $question_id);
232  $logger = $this->test->getTestLogger();
233  if ($logger->isLoggingEnabled()) {
234  $logger->logScoringInteraction(
236  $this->test->getRefId(),
237  $question_id,
238  $this->scorer->getId(),
239  $user_id,
241  time(),
242  []
243  )
244  );
245  }
246  }
247 
251  public function calculateBestSolutionForTest(): string
252  {
253  $solution = '';
254  foreach ($this->test->getAllQuestions() as $question) {
256  $question_gui = $this->test->createQuestionGUI("", $question['question_id']);
257  $solution .= '<h1>' . $question_gui->getObject()->getTitleForHTMLOutput() . '</h1>';
258  $solution .= $question_gui->getSolutionOutput(0, null, true, true, false, false, true, false);
259  }
260 
261  return $solution;
262  }
263 
264  public function removeAllQuestionResults($question_id)
265  {
266  $query = "DELETE FROM tst_test_result WHERE question_fi = %s";
267  $this->db->manipulateF($query, ['integer'], [$question_id]);
268  }
269 
274  public function updatePassAndTestResults(array $active_ids): void
275  {
276  foreach ($active_ids as $active_id) {
277  $passSelector = new \ilTestPassesSelector($this->db, $this->test);
278  $passSelector->setActiveId($active_id);
279 
280  foreach ($passSelector->getExistingPasses() as $pass) {
281  $this->test_result_repository->updateTestAttemptResult(
282  $active_id,
283  $pass,
284  null,
285  null,
286  false
287  );
288  }
289 
290  $this->test_result_repository->updateTestResultCache($active_id);
291  }
292  }
293 
294  public function getNumManualScorings(): int
295  {
296  $query = "
297  SELECT COUNT(*) num_manual_scorings
298  FROM tst_test_result tres
299  INNER JOIN tst_active tact
300  ON tact.active_id = tres.active_fi
301  WHERE tact.test_fi = %s
302  AND tres.manual = 1
303  ";
304 
305  $types = ['integer'];
306  $values = [$this->test->getTestId()];
307 
308  if ($this->getQuestionId()) {
309  $query .= "
310  AND tres.question_fi = %s
311  ";
312 
313  $types[] = 'integer';
314  $values[] = $this->getQuestionId();
315  }
316 
317  $res = $this->db->queryF($query, $types, $values);
318 
319  while ($row = $this->db->fetchAssoc($res)) {
320  return (int) $row['num_manual_scorings'];
321  }
322 
323  return 0;
324  }
325 }
$res
Definition: ltiservices.php:66
recalculatePasses(\ilTestEvaluationUserData $userdata, int $active_id)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
__construct(private \ilObjTest $test, private \ilObjUser $scorer, private \ilDBInterface $db, private readonly TestResultRepository $test_result_repository)
Definition: TestScoring.php:56
updatePassAndTestResults(array $active_ids)
recalculateQuestionScore(int $user_id, int $active_id, int $pass, array $questiondata)
recalculateSolution(int $active_id, int $pass)
recalculatePass(\ilTestEvaluationPassData $passdata, int $user_id, int $active_id, int $pass)
static _updateObjectiveResult(int $a_user_id, int $a_active_id, int $a_question_id)
setPreserveManualScores(bool $preserve_manual_scores)
Definition: TestScoring.php:64
static _updateStatus(int $a_obj_id, int $a_usr_id, ?object $a_obj=null, bool $a_percentage=false, bool $a_force_raise=false)
updateReachedPoints(int $user_id, int $active_id, int $question_id, float $old_points, float $points, float $max_points, int $pass, bool $manual_scoring=false)
This is an optimized version of ::_setReachedPoints that only executes updates in the database if nec...