ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
class.ilCronFinishUnfinishedTestPasses.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
23use ILIAS\Test\Results\Data\Repository as TestResultRepository;
28
34{
35 protected readonly TestLogger $logger;
36
37 protected readonly ilLanguage $lng;
38 protected readonly ilDBInterface $db;
39 protected readonly ilObjUser $user;
41 protected int $now;
42 protected array $unfinished_passes;
43 protected array $test_ids;
44 protected array $test_ending_times;
46 protected TestResultRepository $test_result_repository;
47
48 public function __construct()
49 {
51 global $DIC;
52
53 $this->logger = TestDIC::dic()['logging.logger'];
54 $this->lng = $DIC['lng'];
55 $this->user = $DIC['ilUser'];
56 $this->lng->loadLanguageModule('assessment');
57 $this->db = $DIC->database();
58 $this->obj_data_cache = $DIC['ilObjDataCache'];
59 $this->now = time();
60 $this->unfinished_passes = [];
61 $this->test_ids = [];
62 $this->test_ending_times = [];
63
64 $this->processLockerFactory = new ilTestProcessLockerFactory(
65 new ilSetting('assessment'),
66 $this->db,
67 $this->logger
68 );
69
70 $this->test_result_repository = TestDic::dic()['results.data.repository'];
71 }
72
73 public function getId(): string
74 {
75 return 'finish_unfinished_passes';
76 }
77
78 public function getTitle(): string
79 {
80 return $this->lng->txt('finish_unfinished_passes');
81 }
82
83 public function getDescription(): string
84 {
85 return $this->lng->txt('finish_unfinished_passes_desc');
86 }
87
89 {
90 return JobScheduleType::DAILY;
91 }
92
93 public function getDefaultScheduleValue(): int
94 {
95 return 1;
96 }
97
98 public function hasAutoActivation(): bool
99 {
100 return false;
101 }
102
103 public function hasFlexibleSchedule(): bool
104 {
105 return true;
106 }
107
108 public function hasCustomSettings(): bool
109 {
110 return true;
111 }
112
113 public function run(): JobResult
114 {
115 $this->logger->info('start inf cronjob...');
116
117 $result = new JobResult();
118
120 if (count($this->unfinished_passes) > 0) {
121 $this->logger->info('found ' . count($this->unfinished_passes) . ' unfinished passes starting analyses.');
123 $this->processPasses();
124 } else {
125 $this->logger->info('No unfinished passes found.');
126 }
127
128 $result->setStatus(JobResult::STATUS_OK);
129
130 $this->logger->info(' ...finishing cronjob.');
131
132 return $result;
133 }
134
135 protected function gatherUsersWithUnfinishedPasses(): void
136 {
137 $query = '
138 SELECT tst_active.active_id,
139 tst_active.tries,
140 tst_active.user_fi usr_id,
141 tst_active.test_fi test_fi,
142 usr_data.login,
143 usr_data.lastname,
144 usr_data.firstname,
145 tst_active.submitted test_finished,
146 usr_data.matriculation,
147 usr_data.active,
148 tst_active.lastindex,
149 tst_active.last_started_pass last_started
150 FROM tst_active
151 LEFT JOIN usr_data
152 ON tst_active.user_fi = usr_data.usr_id
153 WHERE IFNULL(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass
154 ';
155 $result = $this->db->query($query);
156 while ($row = $this->db->fetchAssoc($result)) {
157 $this->unfinished_passes[] = $row;
158 $this->test_ids[] = $row['test_fi'];
159 }
160 }
161
162 protected function getTestsFinishAndProcessingTime(): void
163 {
164 $query = "SELECT test_id, obj_fi,
165 tst_set.ending_time, tst_set.ending_time_enabled, tst_set.processing_time, tst_set.enable_processing_time
166 FROM tst_tests INNER JOIN tst_test_settings AS tst_set ON tst_tests.settings_id = tst_set.id WHERE " .
167 $this->db->in('test_id', $this->test_ids, false, ilDBConstants::T_INTEGER);
168
169 $result = $this->db->query($query);
170 while ($row = $this->db->fetchAssoc($result)) {
171 $this->test_ending_times[$row['test_id']] = $row;
172 }
173 $this->logger->info('Gathered data for ' . count($this->test_ids) . ' test id(s) => (' . implode(',', $this->test_ids) . ')');
174 }
175
176 protected function processPasses(): void
177 {
178 foreach ($this->unfinished_passes as $data) {
179 $test_id = $data['test_fi'];
180 if (!array_key_exists($test_id, $this->test_ending_times)) {
181 continue;
182 }
183 if (!$this->finishPassOnEndingTime($test_id, $data['active_id'])
185 $test_id,
186 $data['usr_id'],
187 $data['active_id']
188 )
189 ) {
190 $this->logger->info('Test session with active id ('
191 . $data['active_id'] . ') can not be finished by this cron job.');
192 }
193 }
194
195 }
196
197 private function finishPassOnEndingTime(int $test_id, int $active_id): bool
198 {
199 $now = time();
200 if ($this->test_ending_times[$test_id]['ending_time_enabled'] !== 1) {
201 $this->logger->info('Test (' . $test_id . ') has no ending time.');
202 return false;
203 }
204
205 $this->logger->info('Test (' . $test_id . ') has ending time ('
206 . $this->test_ending_times[$test_id]['ending_time'] . ')');
207 $ending_time = $this->test_ending_times[$test_id]['ending_time'];
208 if ($ending_time >= $now) {
209 $this->logger->info('Test (' . $test_id . ') ending time ('
210 . $this->test_ending_times[$test_id]['ending_time']
211 . ') > now (' . $now . ') is not reached.');
212 return false;
213 }
214
215 $this->finishPassForUser($active_id, $this->test_ending_times[$test_id]['obj_fi']);
216 return true;
217 }
218
220 int $test_id,
221 int $usr_id,
222 int $active_id
223 ): bool {
224 if ($this->test_ending_times[$test_id]['enable_processing_time'] !== 1) {
225 $this->logger->info('Test (' . $test_id . ') has no processing time.');
226 return false;
227 }
228
229 $this->logger->info('Test (' . $test_id . ') has processing time (' . $this->test_ending_times[$test_id]['processing_time'] . ')');
230 $obj_id = $this->test_ending_times[$test_id]['obj_fi'];
231
232 if (!ilObject::_exists($obj_id)) {
233 $this->logger->info('Test object with id (' . $obj_id . ') does not exist.');
234 return false;
235 }
236
237 $test_obj = new ilObjTest($obj_id, false);
238 $startingTime = $test_obj->getStartingTimeOfUser($active_id);
239 $max_processing_time = $test_obj->isMaxProcessingTimeReached($startingTime, $active_id);
240 if (!$max_processing_time) {
241 $this->logger->info('Max Processing time not reached for user id ('
242 . $usr_id . ') in test with active id ('
243 . $active_id . '). Starting time: ' . $startingTime
244 . ' Processing time: ' . $test_obj->getProcessingTime() . ' / '
245 . $test_obj->getProcessingTimeInSeconds() . 's');
246 return false;
247 }
248
249 $this->logger->info('Max Processing time reached for user id ('
250 . $usr_id . ') so test with active id ('
251 . $active_id . ') will be finished.');
252 $this->finishPassForUser($active_id, $this->test_ending_times[$test_id]['obj_fi']);
253 return true;
254 }
255
256 protected function finishPassForUser($active_id, $obj_id): void
257 {
258 $processLocker = $this->processLockerFactory->withContextId((int) $active_id)->getLocker();
259
260 $test_session = new ilTestSession($this->db, $this->user);
261 $test_session->loadFromDb($active_id);
262
263 if (ilObject::_exists($obj_id)) {
264 $this->test_result_repository->updateTestAttemptResult(
265 $active_id,
266 $test_session->getPass(),
267 null,
268 $obj_id
269 );
270
272 $test_session,
273 $obj_id,
274 $this->test_result_repository
275 ))->performFinishTasks($processLocker, StatusOfAttempt::FINISHED_BY_CRONJOB);
276 $this->logger->info('Test session with active id (' . $active_id . ') and obj_id (' . $obj_id . ') is now finished.');
277 } else {
278 $this->logger->info('Test object with id (' . $obj_id . ') does not exist.');
279 }
280 }
281}
finishPassOnProcessingTime(int $test_id, int $usr_id, int $active_id)
hasAutoActivation()
Is to be activated on "installation", does only work for ILIAS core cron jobs.
language handling
User class.
class ilObjectDataCache
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
ILIAS Setting Class.
Class ilTestPassFinishTasks.
Test session handler.
Interface ilDBInterface.
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
if(!file_exists('../ilias.ini.php'))
global $DIC
Definition: shib_login.php:26