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