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