ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilCertificateMigrationJob.php
Go to the documentation of this file.
1<?php
2/*
3 +----------------------------------------------------------------------------+
4 | ILIAS open source |
5 +----------------------------------------------------------------------------+
6 | Copyright (c) 1998-2001 ILIAS open source, University of Cologne |
7 | |
8 | This program is free software; you can redistribute it and/or |
9 | modify it under the terms of the GNU General Public License |
10 | as published by the Free Software Foundation; either version 2 |
11 | of the License, or (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21 +----------------------------------------------------------------------------+
22*/
23
30
36{
38 protected $db_table;
39
41 protected $user_id;
42
44 protected $tree;
45
47 protected $db;
48
50 protected $event_handler;
51
53 protected $task_factory;
54
56 private $logger;
57
58 private $types = [
59 'test',
60 'scorm',
61 'exercise',
62 'course'
63 ];
64
71 public function run(array $input, Observer $observer) : \ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\IntegerValue
72 {
73 global $DIC;
74
75 $this->user_id = $input[0]->getValue();
76 $user = new ilObjUser($this->user_id);
77
78 $this->tree = $DIC->repositoryTree();
79 $this->db = $DIC->database();
81 $this->event_handler = $DIC['ilAppEventHandler'];
82 $this->task_factory = $DIC->backgroundTasks()->taskFactory();
83 $this->logger = $DIC->logger()->cert();
84
85 $certificates = [];
86 $output = new IntegerValue();
87
88 $this->logger->debug('Started at ' . ($st_time = date('d.m.Y H:i:s')) . ' for user with id: ' . $this->user_id);
89
90 $task_informations = $this->getTaskInformations();
91 if (empty($task_informations)) {
92 $this->initTask();
93 }
94 if ($task_informations['state'] === \ilCertificateMigrationJobDefinitions::CERT_MIGRATION_STATE_RUNNING) {
95 $this->logger->info('Parallel execution protection. Stopped task for user ' . $this->user_id . ', because it is already running.');
97 return $output;
98 }
99 if ($task_informations['lock'] == true) {
100 // we should never get here if no fatal error occures
101 $this->logger->info('Parallel execution protection. Stopped task for user ' . $this->user_id . ', because it is locked.');
103 return $output;
104 }
105
106 $this->updateTask([
107 'lock' => true,
109 ]);
110
111 $found_items = 0;
112
113 try {
114 // collect all data
115 $this->logger->info('Start collection certificate data for user: ' . $this->user_id);
116
117 foreach ($this->types as $type) {
118 if ($type === 'scorm') {
120 } elseif ($type === 'test') {
122 } elseif ($type === 'exercise') {
124 } elseif ($type === 'course') {
126 }
127
128 $found_items += count($certificates[$type]);
129 $observer->heartbeat();
130 }
131
132 $this->updateTask(['found_items' => $found_items]);
133 $this->logger->debug('Found overall ' . $found_items . ' items for user with id: ' . $this->user_id);
134 $this->logger->info('Finished collecting certificate data for user: ' . $this->user_id);
135
136 $processed_items = 0;
137
138 $template_repository = new ilCertificateTemplateRepository($this->db);
139 $repository = new ilUserCertificateRepository($this->db);
140 $value_replacement = new ilCertificateValueReplacement();
141
142 $this->logger->info('Start migrating certificates for user: ' . $user->getId());
143
144 foreach ($this->types as $type) {
145 $this->logger->info(sprintf('Start preparing "%s" certificates', $type));
146
147 switch ($type) {
148 case 'test':
149 $class = ilTestPlaceholderValues::class;
150 break;
151
152 case 'scorm':
153 $class = ilScormPlaceholderValues::class;
154 break;
155
156 case 'exercise':
157 $class = ilExercisePlaceholderValues::class;
158 break;
159
160 case 'course':
161 $class = ilCoursePlaceholderValues::class;
162 break;
163 }
164 $placeholder_values = new $class();
165
166 foreach ($certificates[$type] as $certificate_id) {
167 if (\ilCertificate::isObjectActive($certificate_id)) {
168 try {
169 switch (true) {
170 case $type == 'course':
171 $acquireDate = new ilDateTime(
172 ilCourseParticipants::getDateTimeOfPassed($certificate_id, $user->getId()),
174 );
175 $acquireTimestamp = $acquireDate->get(IL_CAL_UNIX);
176 break;
178 $test = new ilObjTest($certificate_id, false);
179 $acquireTimestamp = $test
180 ->getTestResult($test->getActiveIdOfUser($user->getId()))['test']['result_tstamp'];
181 break;
182 default:
183 $acquireDate = new ilDateTime(
184 ilLPStatus::_lookupStatusChanged($certificate_id, $user->getId()),
186 );
187 $acquireTimestamp = $acquireDate->get(IL_CAL_UNIX);
188 }
189 } catch (Exception $exception) {
190 $this->logger->warning(sprintf('Unable to gather aquired timestamp for certificate %s', $certificate_id));
191 $acquireTimestamp = null;
192 }
193 if ($acquireTimestamp === null || 0 === (int) $acquireTimestamp) {
194 $acquireTimestamp = time();
195 }
196 $template = null;
197 try {
198 $template = $template_repository->fetchFirstCreatedTemplate($certificate_id);
199 } catch (Exception $exception) {
200 $this->logger->warning(sprintf('Unable to gather template for certificate %s. Skipped.', $certificate_id));
201 }
202 if ($template) {
204 $template->getId(),
205 $certificate_id,
206 $type,
207 $user->getId(),
208 $user->getFullname(),
209 $acquireTimestamp,
210 $value_replacement->replace(
211 $placeholder_values->getPlaceholderValues($user->getId(), $certificate_id),
212 $template->getCertificateContent()
213 ) ?? '',
214 json_encode($placeholder_values->getPlaceholderValues($user->getId(), $certificate_id)) ?? '',
215 null,
216 $template->getVersion(),
218 true,
219 $template->getBackgroundImagePath() ?? '',
220 $template->getThumbnailImagePath() ?? ''
221 );
222 $repository->save($certificate);
223 $processed_items++;
224 }
225 }
226 $this->updateTask([
227 'processed_items' => $processed_items,
228 'progress' => ($processed_items / $found_items) * 100
229 ]);
230
231 $observer->heartbeat();
232 }
233 $this->logger->info(sprintf('Finished preparing "%s" certificates', $type));
234 }
235
236 $this->logger->info('Finished migrating certificates for user: ' . $user->getId());
237 } catch (\Exception $e) {
238 $this->logger->error($e->getMessage(), 'error');
239 $this->updateTask([
240 'lock' => false,
241 'processed_items' => $processed_items,
243 ]);
244 $output->setValue((int) $e->getCode());
245 return $output;
246 }
247
249
250 $this->logger->debug('Finished at ' . ($f_time = date('d.m.Y H:i:s')) . ' after ' . (strtotime($f_time) - strtotime($st_time)) . ' seconds');
251 $this->updateTask([
252 'lock' => false,
253 'finished_ts' => $f_time,
254 'processed_items' => $processed_items,
256 ]);
257
258 $user->writePref('cert_migr_finished', 1);
259
260 return $output;
261 }
262
267 public function isStateless() : bool
268 {
269 return true;
270 }
271
278 public function getExpectedTimeOfTaskInSeconds() : int
279 {
280 return 100;
281 }
282
286 public function getInputTypes() : array
287 {
288 return [
289 new SingleType(IntegerValue::class),
290 ];
291 }
292
296 public function getOutputType() : SingleType
297 {
298 return new SingleType(IntegerValue::class);
299 }
300
304 public function getTaskInformations() : array
305 {
306 global $DIC;
307
308 $db = $DIC->database();
309
310 $result = $db->queryF(
311 'SELECT * FROM ' . $this->db_table . ' WHERE usr_id = %s',
312 ['integer'],
313 [$this->user_id]
314 );
315 if ($result->numRows() == 1) {
316 $data = $db->fetchAssoc($result);
317 return $data;
318 }
319 return [];
320 }
321
325 protected function initTask()
326 {
327 $this->logger->debug('Insert new entry for user with id: ' . $this->user_id);
328 $this->db->insert($this->db_table, [
329 'id' => ['integer', $this->db->nextId($this->db_table)],
330 'usr_id' => ['integer', $this->user_id],
331 'lock' => ['integer', false],
332 'found_items' => ['integer', 0],
333 'processed_items' => ['integer', 0],
334 'migrated_items' => ['integer', 0],
335 'progress' => ['integer', 0],
337 'started_ts' => ['integer', strtotime('now')],
338 'finished_ts' => null,
339 ]);
340 }
341
346 protected function updateTask(array $data)
347 {
348 $updata = [];
349 if (array_key_exists('lock', $data)) {
350 $updata['lock'] = ['integer', $data['lock']];
351 }
352 if (array_key_exists('found_items', $data)) {
353 $updata['found_items'] = ['integer', $data['found_items']];
354 }
355 if (array_key_exists('processed_items', $data)) {
356 $updata['processed_items'] = ['integer', $data['processed_items']];
357 }
358 if (array_key_exists('migrated_items', $data)) {
359 $updata['migrated_items'] = ['integer', $data['migrated_items']];
360 }
361 if (array_key_exists('progress', $data)) {
362 $updata['progress'] = ['integer', $data['progress']];
363 }
364 if (array_key_exists('state', $data)) {
365 $updata['state'] = ['text', $data['state']];
366 }
367 if (array_key_exists('finished_ts', $data)) {
368 if (is_string($data['finished_ts'])) {
369 $data['finished_ts'] = strtotime($data['finished_ts']);
370 }
371 $updata['finished_ts'] = ['integer', $data['finished_ts']];
372 }
373 $this->logger->debug('Update data: ' . json_encode($updata));
374 if (!empty($updata)) {
375 $this->logger->debug('Update entry for user with id: ' . $this->user_id);
376 $this->db->update($this->db_table, $updata, ['usr_id' => ['integer', $this->user_id] ]);
377 }
378 }
379
384 protected function getScormCertificates() : array
385 {
386 $this->logger->info('Trying to get scorm certificates for user: ' . $this->user_id);
387
388 $data = array();
389
392 $obj_ids = array();
393 $root = $this->tree->getNodeData($this->tree->getRootId());
394 foreach ($this->tree->getSubTree($root, true, "sahs") as $node) {
395 $obj_ids[] = $node["obj_id"];
396 }
397 if ($obj_ids) {
398 foreach (\ilCertificate::areObjectsActive($obj_ids) as $objectId => $active) {
399 if ($active) {
401
402 $object = \ilObjectFactory::getInstanceByObjId($objectId, false);
403 if (!$object || !($object instanceof \ilObjSAHSLearningModule)) {
404 $this->logger->debug('Found inconsistent object, skipped migration: ' . $objectId);
405 continue;
406 }
407 $object->setSubType($type);
408
409 $lpdata = $completed = false;
410 if ($lp_active) {
411 $completed = ilLPStatus::_hasUserCompleted($objectId, $this->user_id);
412 $lpdata = true;
413 }
414
415 if (!$lpdata) {
416 switch ($type) {
417 case 'scorm':
419 $objectId,
420 $this->user_id
421 );
422 break;
423
424 case 'scorm2004':
426 $objectId,
427 $this->user_id
428 );
429 break;
430 }
431 }
432
433 if (!$completed) {
434 $this->logger->info(sprintf(
435 'User did not complete SCORM object with obj_id %s, ignoring object for migration ...',
436 $objectId
437 ));
438 continue;
439 }
440
441 $this->logger->info(sprintf(
442 'User completed SCORM object with obj_id %s, considering object for migration ...',
443 $objectId
444 ));
445
446 $data[] = $objectId;
447 }
448 }
449 }
450 }
451
452 $this->logger->info('Got ' . count($data) . ' scorm certificates for user: ' . $this->user_id);
453 return $data;
454 }
455
460 protected function getTestCertificates() : array
461 {
462 $this->logger->info('Trying to get test certificates for user: ' . $this->user_id);
463
464 $data = array();
465
466 foreach (\ilObjTest::getTestObjIdsWithActiveForUserId($this->user_id) as $objectId) {
467 $object = \ilObjectFactory::getInstanceByObjId($objectId, false);
468 if (!$object || !($object instanceof \ilObjTest)) {
469 $this->logger->debug('Found inconsistent object, skipped migration: ' . $objectId);
470 continue;
471 }
472
473 $session = new \ilTestSessionFactory($object);
474 $session = $session->getSession(null);
475 if ($object->canShowCertificate($session, $session->getUserId(), $session->getActiveId())) {
476 $data[] = $objectId;
477 }
478 }
479
480 $this->logger->info('Got ' . count($data) . ' test certificates for user: ' . $this->user_id);
481 return $data;
482 }
483
488 protected function getExerciseCertificates() : array
489 {
490 $this->logger->info('Trying to get exercise certificates for user: ' . $this->user_id);
491
492 $data = array();
493
494 foreach (\ilObjExercise::_lookupFinishedUserExercises($this->user_id) as $objectId => $passed) {
495 $object = \ilObjectFactory::getInstanceByObjId($objectId, false);
496 if (!$object || !($object instanceof \ilObjExercise)) {
497 $this->logger->debug('Found inconsistent object, skipped migration: ' . $objectId);
498 continue;
499 }
500
501 $adapter = new \ilExerciseCertificateAdapter($object);
502 if ($adapter->hasUserCertificate($this->user_id)) {
503 $data[] = $objectId;
504 }
505 }
506
507 $this->logger->info('Got ' . count($data) . ' exercise certificates for user: ' . $this->user_id);
508 return $data;
509 }
510
515 protected function getCourseCertificates() : array
516 {
517 $this->logger->info('Trying to get course certificates for user: ' . $this->user_id);
518
519 $data = array();
520
521 $obj_ids = \ilCourseParticipants::_getMembershipByType($this->user_id, "crs");
522 if ($obj_ids) {
523 \ilCourseCertificateAdapter::_preloadListData([$this->user_id], $obj_ids);
524
525 foreach ($obj_ids as $objectId) {
526 if (\ilCourseCertificateAdapter::_hasUserCertificate($this->user_id, $objectId)) {
527 $object = \ilObjectFactory::getInstanceByObjId($objectId, false);
528 if (!$object || !($object instanceof \ilObjCourse)) {
529 $this->logger->debug('Found inconsistent object, skipped migration: ' . $objectId);
530 continue;
531 }
532
533 $data[] = $objectId;
534 }
535 }
536 }
537
538 $this->logger->info('Got ' . count($data) . ' course certificates for user: ' . $this->user_id);
539 return $data;
540 }
541}
$result
$test
Definition: Utf8Test.php:84
$f_time
Definition: buildRTE.php:82
An exception for terminatinating execution or to throw for unit testing.
const IL_CAL_UNIX
const IL_CAL_DATETIME
Class ilCertificateMigrationJob.
getScormCertificates()
Get certificates for scorm objects.
getTestCertificates()
Get certificates for test objects.
getExerciseCertificates()
Get certificates for exercise objects.
run(array $input, Observer $observer)
getCourseCertificates()
Get certificates for course objects.
static isObjectActive($a_obj_id)
static _preloadListData($a_usr_ids, $a_obj_ids)
Get certificate/passed status for all given objects and users.
static _hasUserCertificate($a_usr_id, $a_obj_id)
Check if user has certificate for course.
static getDateTimeOfPassed($a_obj_id, $a_usr_id)
@classDescription Date and time handling
static _hasUserCompleted($a_obj_id, $a_user_id)
Lookup user object completion.
static _lookupStatusChanged($a_obj_id, $a_user_id)
Lookup status changed.
Class ilObjCourse.
Class ilObjExercise.
static _lookupFinishedUserExercises($a_user_id)
Get all exercises for user.
Class ilObjSCORMLearningModule.
static _lookupSubType($a_obj_id)
lookup subtype id (scorm, )
static _getCourseCompletionForUser($a_id, $a_user)
Get the completion of a SCORM module for a given user.
static _getCourseCompletionForUser($a_id, $a_user)
Get the completion of a SCORM module for a given user.
static getTestObjIdsWithActiveForUserId($userId)
static _enabledLearningProgress()
check wether learing progress is enabled or not
static getInstanceByObjId($a_obj_id, $stop_on_error=true)
get an instance of an Ilias object by object id
static _getMembershipByType($a_usr_id, $a_type, $a_only_member_role=false)
get membership by type Get course or group membership
$template
if(@file_exists(dirname(__FILE__).'/lang/eng.php')) $certificate
Definition: example_052.php:77
const ILIAS_VERSION_NUMERIC
heartbeat()
I'm still alive! If your calculation takes a really long time don't forget to use the heartbeat.
$certificates
Definition: metarefresh.php:39
$user
Definition: migrateto20.php:57
Class BaseForm.
$type
$session
$root
Definition: sabredav.php:45
global $DIC
Definition: saml.php:7
$data
Definition: bench.php:6