ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilTestEvaluationFactory.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 {
23  public function __construct(
24  protected ilDBInterface $db,
25  protected ilObjTest $test_obj
26  ) {
27  }
28 
29  protected function getPassScoringSettings(): int
30  {
31  return $this->test_obj->getPassScoring();
32  }
33 
34  protected function isRandomTest(): bool
35  {
36  return $this->test_obj->isRandomTest();
37  }
38 
39  protected function getTestQuestionCount(): int
40  {
41  return $this->test_obj->getQuestionCountWithoutReloading();
42  }
43 
44  protected function getTesttMarkSchema(): ASS_MarkSchema
45  {
46  return $this->test_obj->getMarkSchema();
47  }
48 
49  protected function getVisitTimeOfParticipant(int $active_id): array
50  {
51  return $this->test_obj->getVisitTimeOfParticipant($active_id);
52  }
53 
55  int $active_id,
56  int $pass
57  ): array {
59  }
60 
64  protected function getAccessFilteredActiveIds(): array
65  {
66  if (($participants_list = $this->test_obj->getAccessFilteredParticipantList()) !== null) {
67  return $participants_list->getAllActiveIds();
68  }
69  $participants = $this->test_obj->getTestParticipants();
70  return array_keys($participants);
71  }
72 
76  protected function queryEvaluationData(array $active_ids): array
77  {
78  $query = '
79  SELECT tst_test_result.question_fi,
80  tst_test_result.points result_points,
81  tst_test_result.answered,
82  tst_test_result.manual,
83 
84  qpl_questions.original_id,
85  qpl_questions.title questiontitle,
86  qpl_questions.points qpl_maxpoints,
87 
88  tst_active.active_id,
89  tst_active.submitted,
90  tst_active.last_finished_pass,
91  tst_pass_result.*,
92 
93  usr_data.usr_id,
94  usr_data.firstname,
95  usr_data.lastname,
96  usr_data.title,
97  usr_data.login
98 
99  FROM tst_active
100 
101  LEFT JOIN tst_pass_result ON tst_active.active_id = tst_pass_result.active_fi
102  LEFT JOIN tst_test_result ON tst_active.active_id = tst_test_result.active_fi AND tst_test_result.pass = tst_pass_result.pass
103  LEFT JOIN qpl_questions ON qpl_questions.question_id = tst_test_result.question_fi
104  LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id
105 
106  WHERE tst_active.test_fi = %s
107  AND %s
108 
109  ORDER BY tst_active.active_id ASC, tst_pass_result.pass ASC, tst_test_result.tstamp DESC
110  ';
111 
112  $result = $this->db->query(
113  sprintf(
114  $query,
115  $this->db->quote($this->test_obj->getTestId(), 'integer'),
116  $this->db->in('tst_active.active_id', $active_ids, false, 'integer'),
117  )
118  );
119  $ret = [];
120  while ($row = $this->db->fetchAssoc($result)) {
121  $ret[] = $row;
122  }
123  return $ret;
124  }
125 
127  {
128  $eval_data_rows = $this->queryEvaluationData($this->getAccessFilteredActiveIds());
129  $participants = [];
130  $current_user = null;
131  $current_attempt = null;
132 
133  foreach ($eval_data_rows as $row) {
134  if ($current_user !== $row['active_id']) {
135  $current_user = $row['active_id'];
136  $current_attempt = null;
137  $user_eval_data = $this->buildBasicUserEvaluationDataFromDB($row);
138  }
139 
140  if ($current_attempt !== $row['pass']) {
141  $current_attempt = $row['pass'];
142  $attempt = $this->buildBasicAttemptEvaluationDataFromDB($row);
143  }
144 
145  $attempt = $this->addQuestionToAttempt($attempt, $row);
146  $user_eval_data->addPass($row['pass'], $attempt);
147  $participants[$row['active_id']] = $user_eval_data;
148  }
149  return new ilTestEvaluationData($participants);
150  }
151 
153  {
154  $eval_data_rows = $this->queryEvaluationData($this->getAccessFilteredActiveIds());
155  $participants = [];
156  $current_user = null;
157  $current_attempt = null;
158 
159  foreach ($eval_data_rows as $row) {
160  if($row['pass'] === null) {
161  continue;
162  }
163 
164  if ($current_user !== $row['active_id']) {
165  $current_user = $row['active_id'];
166  $current_attempt = null;
167  $user_eval_data = $this->addVisitingTimeToUserEvalData(
169  $row['active_id']
170  );
171  }
172 
173  if ($current_attempt !== $row['pass']) {
174  $current_attempt = $row['pass'];
175  $attempt = $this->addPointsAndQuestionCountToAttempt(
177  $row
178  );
179  }
180 
181  $attempt = $this->addQuestionToAttempt($attempt, $row);
182  $user_eval_data->addPass($row['pass'], $attempt);
183  $participants[$row['active_id']] = $user_eval_data;
184  }
185 
186  $evaluation_data = $this->addQuestionsToParticipantPasses(
187  new ilTestEvaluationData($participants)
188  );
189  return $this->addMarksToParticipants($evaluation_data);
190  }
191 
193  {
194  $user_data = new ilTestEvaluationUserData($this->getPassScoringSettings());
195 
196  $user_data->setName(
197  $this->test_obj->buildName($row['usr_id'], $row['firstname'], $row['lastname'])
198  );
199 
200  if ($row['login'] !== null) {
201  $user_data->setLogin($row['login']);
202  }
203  if ($row['usr_id'] !== null) {
204  $user_data->setUserID($row['usr_id']);
205  }
206  $user_data->setSubmitted((bool) $row['submitted']);
207  $user_data->setLastFinishedPass($row['last_finished_pass']);
208  return $user_data;
209  }
210 
212  {
213  $attempt = new \ilTestEvaluationPassData();
214  $attempt->setPass($row['pass']);
215  $attempt->setReachedPoints($row['points']);
216  $attempt->setObligationsAnswered((bool) $row['obligations_answered']);
217  $attempt->setNrOfAnsweredQuestions($row['answeredquestions']);
218  $attempt->setWorkingTime($row['workingtime']);
219  $attempt->setExamId((string) $row['exam_id']);
220  $attempt->setRequestedHintsCount($row['hint_count']);
221  $attempt->setDeductedHintPoints($row['hint_points']);
222  return $attempt;
223  }
224 
226  ilTestEvaluationUserData $user_data,
227  int $active_id
229  $visitingTime = $this->getVisitTimeOfParticipant($active_id);
230  $user_data->setFirstVisit($visitingTime['firstvisit']);
231  $user_data->setLastVisit($visitingTime['lastvisit']);
232  return $user_data;
233  }
234 
236  ilTestEvaluationPassData $attempt,
237  array $row
239  if ($row['questioncount'] !== 0) {
240  $attempt->setMaxPoints($row['maxpoints']);
241  $attempt->setQuestionCount($row['questioncount']);
242  return $attempt;
243  }
244 
245  list($count, $points) = array_values(
246  $this->getQuestionCountAndPointsForPassOfParticipant($row['active_id'], $row['pass'])
247  );
248  $attempt->setMaxPoints($points);
249  $attempt->setQuestionCount($count);
250  return $attempt;
251  }
252 
253  private function addQuestionToAttempt(
254  ilTestEvaluationPassData $attempt,
255  array $row
257  if ($row['question_fi'] === null) {
258  return $attempt;
259  }
260 
261  $attempt->addAnsweredQuestion(
262  $row["question_fi"],
263  $row["qpl_maxpoints"],
264  $row["result_points"],
265  (bool) $row['answered'],
266  null,
267  $row['manual']
268  );
269  return $attempt;
270  }
271 
273  {
274  $is_random_test = $this->isRandomTest();
275 
276  foreach ($evaluation_data->getParticipantIds() as $active_id) {
277  $user_eval_data = $evaluation_data->getParticipant($active_id);
278 
279  $add_user_questions = $this->isRandomTest() ?
280  $this->getQuestionsForParticipantPassesForRandomTests($active_id, $user_eval_data, $this->getTestQuestionCount()) :
282 
283  foreach ($add_user_questions as $q) {
284 
285  $original_id = $q['original_id'];
286  $question_id = $q['question_id'];
287  $max_points = $q['max_points'];
288  $sequence = $q['sequence'];
289  $pass = $q['pass'];
290  $title = $q['title'];
291 
292  $user_eval_data->addQuestion(
293  $original_id,
294  $question_id,
295  $max_points,
296  $sequence,
297  $pass
298  );
299 
300  $evaluation_data->addQuestionTitle($question_id, $title);
301  }
302  }
303 
304  return $evaluation_data;
305  }
306 
308  int $active_id,
309  ilTestEvaluationUserData $user_eval_data,
310  int $question_count
311  ): array {
312  $ret = [];
313  for ($testpass = 0; $testpass <= $user_eval_data->getLastPass(); $testpass++) {
314  $this->db->setLimit($question_count, 0);
315  $query = "
316  SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, qpl_questions.original_id,
317  tst_test_rnd_qst.pass, qpl_questions.points, qpl_questions.title
318  FROM tst_test_rnd_qst, qpl_questions
319  WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id
320  AND tst_test_rnd_qst.pass = %s
321  AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence
322  ";
323 
324  $result = $this->db->queryF(
325  $query,
326  ['integer','integer'],
327  [$testpass, $active_id]
328  );
329 
330  if ($result->numRows()) {
331  while ($row = $this->db->fetchAssoc($result)) {
332  $tpass = array_key_exists("pass", $row) ? $row["pass"] : 0;
333 
334  if (
335  !isset($row["question_fi"], $row["points"], $row["sequence"]) ||
336  !is_numeric($row["question_fi"]) || !is_numeric($row["points"]) || !is_numeric($row["sequence"])
337  ) {
338  continue;
339  }
340 
341  $ret[] = [
342  'original_id' => (int) $row["original_id"],
343  'question_id' => (int) $row["question_fi"],
344  'max_points' => (float) $row["points"],
345  'sequence' => (int) $row["sequence"],
346  'pass' => $tpass,
347  'title' => $row["title"]
348  ];
349  }
350  }
351  }
352  return $ret;
353  }
354 
356  int $active_id
357  ): array {
358  $ret = [];
359 
360  $query = "
361  SELECT tst_test_question.sequence, tst_test_question.question_fi,
362  qpl_questions.points, qpl_questions.title, qpl_questions.original_id
363  FROM tst_test_question, tst_active, qpl_questions
364  WHERE tst_test_question.question_fi = qpl_questions.question_id
365  AND tst_active.active_id = %s
366  AND tst_active.test_fi = tst_test_question.test_fi
367  ORDER BY tst_test_question.sequence
368  ";
369 
370  $result = $this->db->queryF(
371  $query,
372  ['integer'],
373  [$active_id]
374  );
375 
376  if ($result->numRows()) {
377  $questionsbysequence = [];
378  while ($row = $this->db->fetchAssoc($result)) {
379  $questionsbysequence[$row['sequence']] = $row;
380  }
381 
382  $seqresult = $this->db->queryF(
383  "SELECT * FROM tst_sequence WHERE active_fi = %s",
384  ['integer'],
385  [$active_id]
386  );
387 
388  while ($seqrow = $this->db->fetchAssoc($seqresult)) {
389  $questionsequence = unserialize($seqrow['sequence']);
390  foreach ($questionsequence as $sidx => $seq) {
391  if (!isset($questionsbysequence[$seq])) {
392  continue;
393  }
394  $ret[] = [
395  'original_id' => $questionsbysequence[$seq]['original_id'] ?? 0,
396  'question_id' => $questionsbysequence[$seq]['question_fi'],
397  'max_points' => $questionsbysequence[$seq]['points'],
398  'sequence' => $sidx + 1,
399  'pass' => $seqrow['pass'],
400  'title' => $questionsbysequence[$seq]["title"]
401  ];
402  }
403  }
404  }
405  return $ret;
406  }
407 
409  {
410  $mark_schema = $this->getTesttMarkSchema();
411 
412  foreach ($evaluation_data->getParticipantIds() as $active_id) {
413  $user_eval_data = $evaluation_data->getParticipant($active_id);
414 
415  $percentage = $user_eval_data->getReachedPointsInPercent();
416  $mark = $mark_schema->getMatchingMark($percentage);
417 
418  if (is_object($mark)) {
419  $user_eval_data->setMark($mark->getShortName());
420  $user_eval_data->setMarkOfficial($mark->getOfficialName());
421 
422  $user_eval_data->setPassed(
423  $mark->getPassed() && $user_eval_data->areObligationsAnswered()
424  );
425  }
426  }
427 
428  return $evaluation_data;
429  }
430 
431 }
addAnsweredQuestion(int $question_id, float $max_points, float $reached_points, bool $isAnswered, ?int $sequence=null, int $manual=0)
getQuestionsForParticipantPassesForSequencedTests(int $active_id)
getQuestionsForParticipantPassesForRandomTests(int $active_id, ilTestEvaluationUserData $user_eval_data, int $question_count)
addPointsAndQuestionCountToAttempt(ilTestEvaluationPassData $attempt, array $row)
addQuestionsToParticipantPasses(ilTestEvaluationData $evaluation_data)
getQuestionCountAndPointsForPassOfParticipant(int $active_id, int $pass)
static _getQuestionCountAndPointsForPassOfParticipant($active_id, $pass)
addVisitingTimeToUserEvalData(ilTestEvaluationUserData $user_data, int $active_id)
$q
Definition: shib_logout.php:21
addMarksToParticipants(ilTestEvaluationData $evaluation_data)
addQuestionToAttempt(ilTestEvaluationPassData $attempt, array $row)
A class defining mark schemas for assessment test objects.
addQuestionTitle(int $question_id, string $question_title)
__construct(protected ilDBInterface $db, protected ilObjTest $test_obj)