ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilTestPassesSelector.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
30{
31 private ?int $active_id = null;
32 private ?int $last_finished_pass = null;
33 private ?array $passes = null;
34 private array $test_passed_once_cache = [];
35
36 public function __construct(
37 private ilDBInterface $db,
38 private ilObjTest $test_obj
39 ) {
40 }
41
42 public function getActiveId(): ?int
43 {
44 return $this->active_id;
45 }
46
47 public function setActiveId(?int $active_id): void
48 {
49 $this->active_id = $active_id;
50 }
51
52 public function getLastFinishedPass(): ?int
53 {
55 }
56
57 public function setLastFinishedPass(?int $last_finished_pass): void
58 {
59 $this->last_finished_pass = $last_finished_pass === null ? -1 : $last_finished_pass;
60 }
61
62 private function passesLoaded(): bool
63 {
64 return is_array($this->passes);
65 }
66
67 private function ensureLoadedPasses()
68 {
69 if (!$this->passesLoaded()) {
70 $this->loadPasses();
71 }
72 }
73 private function loadPasses(): void
74 {
75 $query = '
76 SELECT tst_pass_result.* FROM tst_pass_result
77 WHERE tst_pass_result.active_fi = %s
78 ORDER BY tst_pass_result.pass
79 ';
80
81 $res = $this->db->queryF(
82 $query,
83 ['integer'],
84 [$this->getActiveId()]
85 );
86
87 $this->passes = [];
88
89 while ($row = $this->db->fetchAssoc($res)) {
90 $this->passes[$row['pass']] = $row;
91 }
92 }
93
94 private function getLazyLoadedPasses(): array
95 {
96 $this->ensureLoadedPasses();
97 return $this->passes;
98 }
99
100 public function loadLastFinishedPass(): void
101 {
102 $query = 'SELECT last_finished_pass FROM tst_active WHERE active_id = %s';
103
104 $res = $this->db->queryF(
105 $query,
106 ['integer'],
107 [$this->getActiveId()]
108 );
109
110 while ($row = $this->db->fetchAssoc($res)) {
111 $this->setLastFinishedPass($row['last_finished_pass']);
112 }
113 }
114
115 public function getExistingPasses(): array
116 {
117 return array_keys($this->getLazyLoadedPasses());
118 }
119
120 public function hasExistingPasses(): bool
121 {
122 return $this->getExistingPasses() !== [];
123 }
124
125 public function getNumExistingPasses(): int
126 {
127 return count($this->getExistingPasses());
128 }
129
130 public function openPassExists(): bool
131 {
132 return count($this->getExistingPasses()) > count($this->getClosedPasses());
133 }
134
135 public function getClosedPasses(): array
136 {
137 $existing_passes = $this->getExistingPasses();
138 return $this->fetchClosedPasses($existing_passes);
139 }
140
141 public function getReportablePasses(): array
142 {
143 $existing_passes = $this->getExistingPasses();
144 return $this->fetchReportablePasses($existing_passes);
145 }
146
147 public function hasReportablePasses(): bool
148 {
149 return (bool) count($this->getReportablePasses());
150 }
151
152 private function fetchReportablePasses(array $existing_passes): array
153 {
154 $last_pass = $this->fetchLastPass($existing_passes);
155
156 $reportable_passes = [];
157
158 foreach ($existing_passes as $pass) {
159 if ($this->isReportableAttempt($last_pass, $pass)) {
160 $reportable_passes[] = $pass;
161 }
162 }
163
164 return $reportable_passes;
165 }
166
167 private function fetchClosedPasses(array $existing_passes): array
168 {
169 $closed_passes = [];
170
171 foreach ($existing_passes as $pass) {
172 if ($this->isClosedPass($pass)) {
173 $closed_passes[] = $pass;
174 }
175 }
176
177 return $closed_passes;
178 }
179
180 private function fetchLastPass(array $existing_passes): ?int
181 {
182 $last_pass = null;
183
184 foreach ($existing_passes as $pass) {
185 if ($last_pass === null || $pass > $last_pass) {
186 $last_pass = $pass;
187 }
188 }
189
190 return $last_pass;
191 }
192
193 private function isReportableAttempt(int $last_attempt, int $attempt): bool
194 {
195 switch ($this->test_obj->getScoreSettings()->getResultSummarySettings()->getScoreReporting()) {
196 case ScoreReportingTypes::SCORE_REPORTING_IMMIDIATLY:
197 return true;
198
199 case ScoreReportingTypes::SCORE_REPORTING_DATE:
200 return $this->isReportingDateReached();
201
202 case ScoreReportingTypes::SCORE_REPORTING_FINISHED:
203 if ($attempt < $last_attempt) {
204 return true;
205 }
206
207 return $this->isClosedPass($attempt);
208
209 case ScoreReportingTypes::SCORE_REPORTING_AFTER_PASSED:
210 if (!$this->hasTestPassedOnce($this->getActiveId())) {
211 return false;
212 }
213
214 return $this->isClosedPass($attempt);
215
216 default:
217 return false;
218 }
219 }
220
222 {
223 if ($this->getLastFinishedPass() === null) {
224 throw new ilTestException('invalid object state: last finished pass was not set!');
225 }
226 }
227
228 private function isClosedPass(int $pass): bool
229 {
231
232 if ($pass <= $this->getLastFinishedPass()) {
233 return true;
234 }
235
236 if ($this->isProcessingTimeReached($pass)) {
237 return true;
238 }
239
240 return false;
241 }
242
243 private function isReportingDateReached(): bool
244 {
245 $reporting_date = $this->test_obj->getScoreSettings()->getResultSummarySettings()->getReportingDate();
246 return $reporting_date <= new DateTimeImmutable('now', new DateTimeZone('UTC'));
247 }
248
249 private function isProcessingTimeReached(int $pass): bool
250 {
251 if (!$this->test_obj->getEnableProcessingTime()) {
252 return false;
253 }
254
255 $startingTime = $this->test_obj->getStartingTimeOfUser($this->getActiveId(), $pass);
256
257 if ($startingTime === false) {
258 return false;
259 }
260
261 return $this->test_obj->isMaxProcessingTimeReached($startingTime, $this->getActiveId());
262 }
263
265 {
266 $last_finished_pass = $this->getLastFinishedPass();
267 if ($last_finished_pass === null || $last_finished_pass === -1) {
268 return null;
269 }
270
271 $passes = $this->getLazyLoadedPasses();
272 if (!isset($passes[$last_finished_pass])) {
273 return null;
274 }
275 return $passes[$last_finished_pass]['tstamp'];
276 }
277
278 public function hasTestPassedOnce(int $active_id): bool
279 {
280 if (!isset($this->test_passed_once_cache[$active_id])) {
281 $this->test_passed_once_cache[$active_id] = false;
282
283 $res = $this->db->queryF(
284 'SELECT passed_once FROM tst_result_cache WHERE active_fi = %s',
285 ['integer'],
286 [$active_id]
287 );
288
289 while ($row = $this->db->fetchAssoc($res)) {
290 $this->test_passed_once_cache[$active_id] = (bool) $row['passed_once'];
291 }
292 }
293
294 return $this->test_passed_once_cache[$active_id];
295 }
296}
Base Exception for all Exceptions relating to Modules/Test.
isReportableAttempt(int $last_attempt, int $attempt)
fetchReportablePasses(array $existing_passes)
setLastFinishedPass(?int $last_finished_pass)
__construct(private ilDBInterface $db, private ilObjTest $test_obj)
fetchClosedPasses(array $existing_passes)
fetchLastPass(array $existing_passes)
Interface ilDBInterface.
$res
Definition: ltiservices.php:69