ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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 );
68
69 $this->test_result_repository = TestDic::dic()['results.data.repository'];
70 }
71
72 public function getId(): string
73 {
74 return 'finish_unfinished_passes';
75 }
76
77 public function getTitle(): string
78 {
79 return $this->lng->txt('finish_unfinished_passes');
80 }
81
82 public function getDescription(): string
83 {
84 return $this->lng->txt('finish_unfinished_passes_desc');
85 }
86
88 {
89 return JobScheduleType::DAILY;
90 }
91
92 public function getDefaultScheduleValue(): int
93 {
94 return 1;
95 }
96
97 public function hasAutoActivation(): bool
98 {
99 return false;
100 }
101
102 public function hasFlexibleSchedule(): bool
103 {
104 return true;
105 }
106
107 public function hasCustomSettings(): bool
108 {
109 return true;
110 }
111
112 public function run(): JobResult
113 {
114 $this->logger->info('start inf cronjob...');
115
116 $result = new JobResult();
117
119 if (count($this->unfinished_passes) > 0) {
120 $this->logger->info('found ' . count($this->unfinished_passes) . ' unfinished passes starting analyses.');
122 $this->processPasses();
123 } else {
124 $this->logger->info('No unfinished passes found.');
125 }
126
127 $result->setStatus(JobResult::STATUS_OK);
128
129 $this->logger->info(' ...finishing cronjob.');
130
131 return $result;
132 }
133
134 protected function gatherUsersWithUnfinishedPasses(): void
135 {
136 $query = '
137 SELECT tst_active.active_id,
138 tst_active.tries,
139 tst_active.user_fi usr_id,
140 tst_active.test_fi test_fi,
141 usr_data.login,
142 usr_data.lastname,
143 usr_data.firstname,
144 tst_active.submitted test_finished,
145 usr_data.matriculation,
146 usr_data.active,
147 tst_active.lastindex,
148 tst_active.last_started_pass last_started
149 FROM tst_active
150 LEFT JOIN usr_data
151 ON tst_active.user_fi = usr_data.usr_id
152 WHERE IFNULL(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass
153 ';
154 $result = $this->db->query($query);
155 while ($row = $this->db->fetchAssoc($result)) {
156 $this->unfinished_passes[] = $row;
157 $this->test_ids[] = $row['test_fi'];
158 }
159 }
160
161 protected function getTestsFinishAndProcessingTime(): void
162 {
163 $query = 'SELECT test_id, obj_fi, ending_time, ending_time_enabled, processing_time, enable_processing_time FROM tst_tests WHERE ' .
164 $this->db->in('test_id', $this->test_ids, false, 'integer');
165 $result = $this->db->query($query);
166 while ($row = $this->db->fetchAssoc($result)) {
167 $this->test_ending_times[$row['test_id']] = $row;
168 }
169 $this->logger->info('Gathered data for ' . count($this->test_ids) . ' test id(s) => (' . implode(',', $this->test_ids) . ')');
170 }
171
172 protected function processPasses(): void
173 {
174 foreach ($this->unfinished_passes as $data) {
175 $test_id = $data['test_fi'];
176 if (!array_key_exists($test_id, $this->test_ending_times)) {
177 continue;
178 }
179 if (!$this->finishPassOnEndingTime($test_id, $data['active_id'])
181 $test_id,
182 $data['usr_id'],
183 $data['active_id']
184 )
185 ) {
186 $this->logger->info('Test session with active id ('
187 . $data['active_id'] . ') can not be finished by this cron job.');
188 }
189 }
190
191 }
192
193 private function finishPassOnEndingTime(int $test_id, int $active_id): bool
194 {
195 $now = time();
196 if ($this->test_ending_times[$test_id]['ending_time_enabled'] !== 1) {
197 $this->logger->info('Test (' . $test_id . ') has no ending time.');
198 return false;
199 }
200
201 $this->logger->info('Test (' . $test_id . ') has ending time ('
202 . $this->test_ending_times[$test_id]['ending_time'] . ')');
203 $ending_time = $this->test_ending_times[$test_id]['ending_time'];
204 if ($ending_time >= $now) {
205 $this->logger->info('Test (' . $test_id . ') ending time ('
206 . $this->test_ending_times[$test_id]['ending_time']
207 . ') > now (' . $now . ') is not reached.');
208 return false;
209 }
210
211 $this->finishPassForUser($active_id, $this->test_ending_times[$test_id]['obj_fi']);
212 return true;
213 }
214
216 int $test_id,
217 int $usr_id,
218 int $active_id
219 ): bool {
220 if ($this->test_ending_times[$test_id]['enable_processing_time'] !== 1) {
221 $this->logger->info('Test (' . $test_id . ') has no processing time.');
222 return false;
223 }
224
225 $this->logger->info('Test (' . $test_id . ') has processing time (' . $this->test_ending_times[$test_id]['processing_time'] . ')');
226 $obj_id = $this->test_ending_times[$test_id]['obj_fi'];
227
228 if (!ilObject::_exists($obj_id)) {
229 $this->logger->info('Test object with id (' . $obj_id . ') does not exist.');
230 return false;
231 }
232
233 $test_obj = new ilObjTest($obj_id, false);
234 $startingTime = $test_obj->getStartingTimeOfUser($active_id);
235 $max_processing_time = $test_obj->isMaxProcessingTimeReached($startingTime, $active_id);
236 if (!$max_processing_time) {
237 $this->logger->info('Max Processing time not reached for user id ('
238 . $usr_id . ') in test with active id ('
239 . $active_id . '). Starting time: ' . $startingTime
240 . ' Processing time: ' . $test_obj->getProcessingTime() . ' / '
241 . $test_obj->getProcessingTimeInSeconds() . 's');
242 return false;
243 }
244
245 $this->logger->info('Max Processing time reached for user id ('
246 . $usr_id . ') so test with active id ('
247 . $active_id . ') will be finished.');
248 $this->finishPassForUser($active_id, $this->test_ending_times[$test_id]['obj_fi']);
249 return true;
250 }
251
252 protected function finishPassForUser($active_id, $obj_id): void
253 {
254 $processLocker = $this->processLockerFactory->withContextId((int) $active_id)->getLocker();
255
256 $test_session = new ilTestSession($this->db, $this->user);
257 $test_session->loadFromDb($active_id);
258
259 if (ilObject::_exists($obj_id)) {
260 $this->test_result_repository->updateTestAttemptResult(
261 $active_id,
262 $test_session->getPass(),
263 null,
264 $obj_id
265 );
266
268 $test_session,
269 $obj_id,
270 $this->test_result_repository
271 ))->performFinishTasks($processLocker, StatusOfAttempt::FINISHED_BY_CRONJOB);
272 $this->logger->info('Test session with active id (' . $active_id . ') and obj_id (' . $obj_id . ') is now finished.');
273 } else {
274 $this->logger->info('Test object with id (' . $obj_id . ') does not exist.');
275 }
276 }
277}
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