ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilTestEvaluationFactory.php
Go to the documentation of this file.
1<?php
2
19declare(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
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(
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() ?
254 $this->retrieveQuestionsForParticipantPassesForRandomTests(
255 $active_id,
256 $user_eval_data,
257 $this->test_obj->getQuestionCountWithoutReloading()
258 ) :
259 $this->retrieveQuestionsForParticipantPassesForSequencedTests($active_id);
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}
addQuestionTitle(int $question_id, string $question_title)
addQuestionToAttempt(ilTestEvaluationPassData $attempt, array $row)
retrieveQuestionsForParticipantPassesForRandomTests(int $active_id, ilTestEvaluationUserData $user_eval_data, int $question_count)
addQuestionsToParticipantPasses(ilTestEvaluationData $evaluation_data)
addMarksToParticipants(ilTestEvaluationData $evaluation_data)
__construct(protected ilDBInterface $db, protected ilObjTest $test_obj)
getFirstVisitForActiveIdAndAttempt(int $active_id, int $attempt)
addVisitingTimeToUserEvalData(ilTestEvaluationUserData $user_data, int $active_id)
addPointsAndQuestionCountToAttempt(ilTestEvaluationPassData $attempt, array $row)
retrieveQuestionsForParticipantPassesForSequencedTests(int $active_id)
addAnsweredQuestion(int $question_id, float $max_points, float $reached_points, bool $is_answered, ?int $sequence=null, int $manual=0)
setFirstVisit(?\DateTimeImmutable $time)
setLastVisit(?\DateTimeImmutable $time)
Interface ilDBInterface.
$res
Definition: ltiservices.php:69
if(!file_exists('../ilias.ini.php'))
$q
Definition: shib_logout.php:23