ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilTestEvaluationFactory.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
27 {
28  public function __construct(
29  protected ilDBInterface $db,
30  protected ilObjTest $test_obj
31  ) {
32  }
33 
37  private function getAccessFilteredActiveIds(): array
38  {
39  if (($participants_list = $this->test_obj->getAccessFilteredParticipantList()) !== null) {
40  return $participants_list->getAllActiveIds();
41  }
42  return array_keys($this->test_obj->getTestParticipants());
43  }
44 
48  private function retrieveEvaluationData(array $active_ids): \Generator
49  {
50  $query = '
51  SELECT tst_test_result.question_fi,
52  tst_test_result.points result_points,
53  tst_test_result.answered,
54  tst_test_result.manual,
55 
56  qpl_questions.original_id,
57  qpl_questions.title questiontitle,
58  qpl_questions.points qpl_maxpoints,
59 
60  tst_active.active_id,
61  tst_active.submitted,
62  tst_active.last_finished_pass,
63  tst_pass_result.*,
64 
65  usr_data.usr_id,
66  usr_data.firstname,
67  usr_data.lastname,
68  usr_data.title,
69  usr_data.login
70 
71  FROM tst_active
72 
73  LEFT JOIN tst_pass_result ON tst_active.active_id = tst_pass_result.active_fi
74  LEFT JOIN tst_test_result ON tst_active.active_id = tst_test_result.active_fi AND tst_test_result.pass = tst_pass_result.pass
75  LEFT JOIN qpl_questions ON qpl_questions.question_id = tst_test_result.question_fi
76  LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id
77 
78  WHERE tst_active.test_fi = %s
79  AND %s
80 
81  ORDER BY tst_active.active_id ASC, tst_pass_result.pass ASC, tst_test_result.tstamp DESC
82  ';
83 
84  $ret = [];
85  $result = $this->db->query(
86  sprintf(
87  $query,
88  $this->db->quote($this->test_obj->getTestId(), 'integer'),
89  $this->db->in('tst_active.active_id', $active_ids, false, 'integer'),
90  )
91  );
92  while ($row = $this->db->fetchAssoc($result)) {
93  yield $row;
94  }
95  }
96 
98  {
99  $participants = [];
100  $current_user = null;
101  $current_attempt = null;
102 
103  foreach ($this->retrieveEvaluationData($this->getAccessFilteredActiveIds()) as $row) {
104  $active_id = $row['active_id'];
105  $pass = $row['pass'];
106 
107  if ($current_user !== $active_id) {
108  $current_user = $active_id;
109  $current_attempt = null;
110  $user_eval_data = $this->buildBasicUserEvaluationDataFromDB($row);
111  }
112 
113  if ($current_attempt !== $pass) {
114  $current_attempt = $pass;
115  $attempt = $this->buildBasicAttemptEvaluationDataFromDB($row);
116  }
117 
118  $attempt = $this->addQuestionToAttempt($attempt, $row);
119  $user_eval_data->addPass($pass, $attempt);
120  $participants[$active_id] = $user_eval_data;
121  }
122  return new ilTestEvaluationData($participants);
123  }
124 
126  {
127  $eval_data_rows = $this->retrieveEvaluationData($this->getAccessFilteredActiveIds());
128  $participants = [];
129  $current_user = null;
130  $current_attempt = null;
131 
132  foreach ($eval_data_rows as $row) {
133  if ($row['pass'] === null) {
134  continue;
135  }
136 
137  if ($current_user !== $row['active_id']) {
138  $current_user = $row['active_id'];
139  $current_attempt = null;
140  $user_eval_data = $this->addVisitingTimeToUserEvalData(
142  $row['active_id']
143  );
144  }
145 
146  if ($row['pass'] !== null && $current_attempt !== $row['pass']) {
147  $current_attempt = $row['pass'];
148  $attempt = $this->addPointsAndQuestionCountToAttempt(
150  $row
151  );
152 
153  $start_time = $this->getFirstVisitForActiveIdAndAttempt($row['active_id'], $current_attempt);
154  if ($start_time !== null) {
155  $attempt->setStartTime($start_time);
156  }
157  $attempt->setStatusOfAttempt(
158  StatusOfAttempt::build($current_attempt, $row['last_finished_pass'], $row['finalized_by'])
159  );
160  }
161 
162  $user_eval_data->addPass($row['pass'], $this->addQuestionToAttempt($attempt, $row));
163  $participants[$row['active_id']] = $user_eval_data;
164  }
165 
166  $evaluation_data = $this->addQuestionsToParticipantPasses(
167  new ilTestEvaluationData($participants)
168  );
169  return $this->addMarksToParticipants($evaluation_data);
170  }
171 
173  {
174  $user_data = new ilTestEvaluationUserData($this->test_obj->getPassScoring());
175 
176  $user_data->setName(
177  $this->test_obj->buildName($row['usr_id'], $row['firstname'], $row['lastname'])
178  );
179 
180  if ($row['login'] !== null) {
181  $user_data->setLogin($row['login']);
182  }
183  if ($row['usr_id'] !== null) {
184  $user_data->setUserID($row['usr_id']);
185  }
186  $user_data->setSubmitted((bool) $row['submitted']);
187  $user_data->setLastFinishedPass($row['last_finished_pass']);
188  return $user_data;
189  }
190 
192  {
193  $attempt = new \ilTestEvaluationPassData();
194  $attempt->setPass($row['pass']);
195  $attempt->setReachedPoints($row['points']);
196  $attempt->setNrOfAnsweredQuestions($row['answeredquestions']);
197  $attempt->setWorkingTime($row['workingtime']);
198  $attempt->setExamId((string) $row['exam_id']);
199  return $attempt;
200  }
201 
203  ilTestEvaluationUserData $user_data,
204  int $active_id
206  $visiting_time = $this->test_obj->getVisitingTimeOfParticipant($active_id);
207  $user_data->setFirstVisit($visiting_time['first_access']);
208  $user_data->setLastVisit($visiting_time['last_access']);
209  return $user_data;
210  }
211 
213  ilTestEvaluationPassData $attempt,
214  array $row
216  if ($row['questioncount'] !== 0) {
217  $attempt->setMaxPoints($row['maxpoints']);
218  $attempt->setQuestionCount($row['questioncount']);
219  return $attempt;
220  }
221 
222  list($count, $points) = array_values(
223  $this->test_obj->getQuestionCountAndPointsForPassOfParticipant($row['active_id'], $row['pass'])
224  );
225  $attempt->setMaxPoints($points);
226  $attempt->setQuestionCount($count);
227  return $attempt;
228  }
229 
230  private function addQuestionToAttempt(
231  ilTestEvaluationPassData $attempt,
232  array $row
234  if ($row['question_fi'] === null) {
235  return $attempt;
236  }
237 
238  $attempt->addAnsweredQuestion(
239  $row["question_fi"],
240  $row["qpl_maxpoints"],
241  $row["result_points"],
242  (bool) $row['answered'],
243  null,
244  $row['manual']
245  );
246  return $attempt;
247  }
248 
250  {
251  foreach ($evaluation_data->getParticipantIds() as $active_id) {
252  $user_eval_data = $evaluation_data->getParticipant($active_id);
253  $add_user_questions = $this->test_obj->isRandomTest() ?
255  $active_id,
256  $user_eval_data,
257  $this->test_obj->getQuestionCountWithoutReloading()
258  ) :
260 
261  foreach ($add_user_questions as $q) {
262  $user_eval_data->addQuestion(
263  $q['original_id'],
264  $q['question_id'],
265  $q['max_points'],
266  $q['sequence'],
267  $q['pass']
268  );
269  $evaluation_data->addQuestionTitle($q['question_id'], $q['title']);
270  }
271  }
272  return $evaluation_data;
273  }
274 
276  int $active_id,
277  ilTestEvaluationUserData $user_eval_data,
278  int $question_count
279  ): array {
280  $ret = [];
281  for ($testpass = 0; $testpass <= $user_eval_data->getLastPass(); $testpass++) {
282  $this->db->setLimit($question_count, 0);
283  $query = '
284  SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, qpl_questions.original_id,
285  tst_test_rnd_qst.pass, qpl_questions.points, qpl_questions.title
286  FROM tst_test_rnd_qst, qpl_questions
287  WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id
288  AND tst_test_rnd_qst.pass = %s
289  AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence
290  ';
291 
292  $result = $this->db->queryF(
293  $query,
294  ['integer','integer'],
295  [$testpass, $active_id]
296  );
297 
298  if ($result->numRows()) {
299  while ($row = $this->db->fetchAssoc($result)) {
300  $tpass = array_key_exists('pass', $row) ? $row['pass'] : 0;
301 
302  if (
303  !isset($row['question_fi'], $row['points'], $row['sequence']) ||
304  !is_numeric($row['question_fi']) || !is_numeric($row['points']) || !is_numeric($row['sequence'])
305  ) {
306  continue;
307  }
308 
309  $ret[] = [
310  'original_id' => (int) $row['original_id'],
311  'question_id' => (int) $row['question_fi'],
312  'max_points' => (float) $row['points'],
313  'sequence' => (int) $row['sequence'],
314  'pass' => $tpass,
315  'title' => $row['title']
316  ];
317  }
318  }
319  }
320  return $ret;
321  }
322 
324  int $active_id
325  ): array {
326  $ret = [];
327 
328  $query = '
329  SELECT tst_test_question.sequence, tst_test_question.question_fi,
330  qpl_questions.points, qpl_questions.title, qpl_questions.original_id
331  FROM tst_test_question, tst_active, qpl_questions
332  WHERE tst_test_question.question_fi = qpl_questions.question_id
333  AND tst_active.active_id = %s
334  AND tst_active.test_fi = tst_test_question.test_fi
335  ORDER BY tst_test_question.sequence
336  ';
337 
338  $result = $this->db->queryF(
339  $query,
340  ['integer'],
341  [$active_id]
342  );
343 
344  if ($result->numRows()) {
345  $questionsbysequence = [];
346  while ($row = $this->db->fetchAssoc($result)) {
347  $questionsbysequence[$row['sequence']] = $row;
348  }
349 
350  $seqresult = $this->db->queryF(
351  'SELECT * FROM tst_sequence WHERE active_fi = %s',
352  ['integer'],
353  [$active_id]
354  );
355 
356  while ($seqrow = $this->db->fetchAssoc($seqresult)) {
357  $questionsequence = unserialize($seqrow["sequence"]);
358  foreach ($questionsequence as $sidx => $seq) {
359  if (!isset($questionsbysequence[$seq])) {
360  continue;
361  }
362  $ret[] = [
363  'original_id' => $questionsbysequence[$seq]['original_id'] ?? 0,
364  'question_id' => $questionsbysequence[$seq]['question_fi'],
365  'max_points' => $questionsbysequence[$seq]['points'],
366  'sequence' => $sidx + 1,
367  'pass' => $seqrow['pass'],
368  'title' => $questionsbysequence[$seq]["title"]
369  ];
370  }
371  }
372  }
373  return $ret;
374  }
375 
377  {
378  $mark_schema = $this->test_obj->getMarkSchema();
379 
380  foreach ($evaluation_data->getParticipantIds() as $active_id) {
381  $user_eval_data = $evaluation_data->getParticipant($active_id);
382 
383  $mark = $mark_schema->getMatchingMark(
384  $user_eval_data->getReachedPointsInPercent()
385  );
386 
387  if ($mark === null) {
388  continue;
389  }
390 
391  $user_eval_data->setMark($mark);
392  for ($i = 0; $i < $user_eval_data->getPassCount(); $i++) {
393  $pass_data = $user_eval_data->getPass($i);
394  if ($pass_data === null) {
395  continue;
396  }
397  $mark = $mark_schema->getMatchingMark(
398  $pass_data->getReachedPointsInPercent()
399  );
400  if ($mark !== null) {
401  $pass_data->setMark($mark);
402  }
403  }
404  }
405 
406  return $evaluation_data;
407  }
408 
409  public function getAllActivesPasses(): array
410  {
411  $query = '
412  SELECT active_fi, pass
413  FROM tst_active actives
414  INNER JOIN tst_pass_result passes
415  ON active_fi = active_id
416  WHERE test_fi = %s
417  ';
418 
419  $res = $this->db->queryF($query, ['integer'], [$this->test_obj->getTestId()]);
420 
421  $passes = [];
422  while ($row = $this->db->fetchAssoc($res)) {
423  if (!isset($passes[$row['active_fi']])) {
424  $passes[$row['active_fi']] = [];
425  }
426 
427  $passes[$row['active_fi']][] = $row['pass'];
428  }
429 
430  return $passes;
431  }
432 
433  public function getFirstVisitForActiveIdAndAttempt(int $active_id, int $attempt): ?string
434  {
435  $times = $this->db->fetchAssoc(
436  $this->db->queryF(
437  'SELECT MIN(started) AS first_access '
438  . 'FROM tst_times WHERE active_fi = %s AND pass = %s',
439  ['integer', 'integer'],
440  [$active_id, $attempt]
441  )
442  );
443 
444  return $times['first_access'];
445  }
446 }
$res
Definition: ltiservices.php:66
retrieveQuestionsForParticipantPassesForSequencedTests(int $active_id)
setFirstVisit(?\DateTimeImmutable $time)
addPointsAndQuestionCountToAttempt(ilTestEvaluationPassData $attempt, array $row)
addQuestionsToParticipantPasses(ilTestEvaluationData $evaluation_data)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
addAnsweredQuestion(int $question_id, float $max_points, float $reached_points, bool $is_answered, ?int $sequence=null, int $manual=0)
getFirstVisitForActiveIdAndAttempt(int $active_id, int $attempt)
retrieveQuestionsForParticipantPassesForRandomTests(int $active_id, ilTestEvaluationUserData $user_eval_data, int $question_count)
setLastVisit(?\DateTimeImmutable $time)
addVisitingTimeToUserEvalData(ilTestEvaluationUserData $user_data, int $active_id)
$q
Definition: shib_logout.php:23
addMarksToParticipants(ilTestEvaluationData $evaluation_data)
addQuestionToAttempt(ilTestEvaluationPassData $attempt, array $row)
addQuestionTitle(int $question_id, string $question_title)
__construct(protected ilDBInterface $db, protected ilObjTest $test_obj)