1<?php
2require_once 'Services/Cron/classes/class.ilCronJob.php';
3require_once 'Services/Cron/classes/class.ilCronJobResult.php';
4require_once 'Modules/Test/classes/class.ilObjTest.php';
5require_once 'Modules/Test/classes/class.ilTestPassFinishTasks.php';
6require_once 'Services/Logging/classes/public/class.ilLoggerFactory.php';
7
8/* Copyright (c) 1998-2016 ILIAS open source, Extended GPL, see docs/LICENSE */
9
10/**
11 * Class ilCronFinishUnfinishedTestPasses
12 * @author Guido Vollbach <gvollbach@databay.de>
13 */
14class ilCronFinishUnfinishedTestPasses extends ilCronJob
15{
16
17    /**
18     * @var ilLogger
19     */
20    protected $log;
21
22    /**
23     * @var $lng ilLanguage
24     */
25    protected $lng;
26
27    /**
28     * @var $ilDB ilDB
29     */
30    protected $db;
31
32    /**
33     * @var $ilObjDataCache ilObjectDataCache
34     */
35    protected $obj_data_cache;
36
37    /**
38     * @var int
39     */
40    protected $now;
41
42    protected $unfinished_passes;
43
44    protected $test_ids;
45
46    protected $test_ending_times;
47
48    /**
49     * @var ilTestProcessLockerFactory
50     */
51    protected $processLockerFactory;
52
53    /**
54     * Constructor
55     */
56    public function __construct()
57    {
58        /**
59         * @var $ilDB ilDB
60         * @var $ilObjDataCache ilObjectDataCache
61         */
62
63        global $DIC;
64        $ilObjDataCache = $DIC['ilObjDataCache'];
65        $lng = $DIC['lng'];
66        $ilDB = $DIC['ilDB'];
67
68        global $DIC; /* @var ILIAS\DI\Container $DIC */
69
70        $this->log = ilLoggerFactory::getLogger('tst');
71        $this->lng = $lng;
72        $this->lng->loadLanguageModule('assessment');
73        $this->db = $ilDB;
74        $this->obj_data_cache = $ilObjDataCache;
75        $this->now = time();
76        $this->unfinished_passes = array();
77        $this->test_ids = array();
78        $this->test_ending_times = array();
79
80        require_once 'Modules/Test/classes/class.ilTestProcessLockerFactory.php';
81        $this->processLockerFactory = new ilTestProcessLockerFactory(
82            new ilSetting('assessment'),
83            $DIC->database()
84        );
85    }
86
87    public function getId()
88    {
89        return 'finish_unfinished_passes';
90    }
91
92    public function getTitle()
93    {
94        global $DIC;
95        $lng = $DIC['lng'];
96
97        return $lng->txt("finish_unfinished_passes");
98    }
99
100    public function getDescription()
101    {
102        global $DIC;
103        $lng = $DIC['lng'];
104
105        return $lng->txt("finish_unfinished_passes_desc");
106    }
107
108    public function getDefaultScheduleType()
109    {
110        return self::SCHEDULE_TYPE_DAILY;
111    }
112
113    public function getDefaultScheduleValue()
114    {
115        return;
116    }
117
118    public function hasAutoActivation()
119    {
120        return false;
121    }
122
123    public function hasFlexibleSchedule()
124    {
125        return true;
126    }
127
128    public function hasCustomSettings()
129    {
130        return true;
131    }
132
133    public function run()
134    {
135        $this->log->info('start inf cronjob...');
136
137        $result = new ilCronJobResult();
138
139        $this->gatherUsersWithUnfinishedPasses();
140        if (count($this->unfinished_passes) > 0) {
141            $this->log->info('found ' . count($this->unfinished_passes) . ' unfinished passes starting analyses.');
142            $this->getTestsFinishAndProcessingTime();
143            $this->processPasses();
144        } else {
145            $this->log->info('No unfinished passes found.');
146        }
147
148        $result->setStatus(ilCronJobResult::STATUS_OK);
149
150        $this->log->info(' ...finishing cronjob.');
151
152        return $result;
153    }
154
155    protected function gatherUsersWithUnfinishedPasses()
156    {
157        $query = "SELECT	tst_active.active_id,
158						tst_active.tries,
159						tst_active.user_fi usr_id,
160						tst_active.test_fi test_fi,
161						usr_data.login,
162						usr_data.lastname,
163						usr_data.firstname,
164						tst_active.submitted test_finished,
165						usr_data.matriculation,
166						usr_data.active,
167						tst_active.lastindex,
168						tst_active.last_started_pass last_started
169				FROM tst_active
170				LEFT JOIN usr_data
171				ON tst_active.user_fi = usr_data.usr_id
172				WHERE IFNULL(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass
173			";
174        $result = $this->db->query($query);
175        while ($row = $this->db->fetchAssoc($result)) {
176            $this->unfinished_passes[] = $row;
177            $this->test_ids[] = $row['test_fi'];
178        }
179    }
180
181    protected function getTestsFinishAndProcessingTime()
182    {
183        $query = 'SELECT test_id, obj_fi, ending_time, ending_time_enabled, processing_time, enable_processing_time FROM tst_tests WHERE ' .
184                    $this->db->in('test_id', $this->test_ids, false, 'integer');
185        $result = $this->db->query($query);
186        while ($row = $this->db->fetchAssoc($result)) {
187            $this->test_ending_times[$row['test_id']] = $row;
188        }
189        $this->log->info('Gathered data for ' . count($this->test_ids) . ' test id(s) => (' . implode(',', $this->test_ids) . ')');
190    }
191
192    protected function processPasses()
193    {
194        $now = time();
195        foreach ($this->unfinished_passes as $key => $data) {
196            $test_id = $data['test_fi'];
197            $can_not_be_finished = true;
198            if (array_key_exists($test_id, $this->test_ending_times)) {
199                if ($this->test_ending_times[$test_id]['ending_time_enabled'] == 1) {
200                    $this->log->info('Test (' . $test_id . ') has ending time (' . $this->test_ending_times[$test_id]['ending_time'] . ')');
201                    $ending_time = $this->test_ending_times[$test_id]['ending_time'];
202                    if ($ending_time < $now) {
203                        $this->finishPassForUser($data['active_id'], $this->test_ending_times[$test_id]['obj_fi']);
204                        $can_not_be_finished = false;
205                    } else {
206                        $this->log->info('Test (' . $test_id . ') ending time (' . $this->test_ending_times[$test_id]['ending_time'] . ') > now (' . $now . ') is not reached.');
207                    }
208                } else {
209                    $this->log->info('Test (' . $test_id . ') has no ending time.');
210                }
211                if ($this->test_ending_times[$test_id]['enable_processing_time'] == 1) {
212                    $this->log->info('Test (' . $test_id . ') has processing time (' . $this->test_ending_times[$test_id]['processing_time'] . ')');
213                    $obj_id = $this->test_ending_times[$test_id]['obj_fi'];
214                    $test_obj = new ilObjTest($obj_id, false);
215                    $startingTime = $test_obj->getStartingTimeOfUser($data['active_id'], $data['last_started_pass']);
216                    $max_processing_time = $test_obj->isMaxProcessingTimeReached($startingTime, $data['active_id']);
217                    if ($max_processing_time) {
218                        $this->log->info('Max Processing time reached for user id (' . $data['usr_id'] . ') so test with active id (' . $data['active_id'] . ') will be finished.');
219                        $this->finishPassForUser($data['active_id'], $this->test_ending_times[$test_id]['obj_fi']);
220                        $can_not_be_finished = false;
221                    }
222                } else {
223                    $this->log->info('Test (' . $test_id . ') has no processing time.');
224                }
225
226                if ($can_not_be_finished) {
227                    $this->log->info('Test session with active id (' . $data['active_id'] . ') can not be finished by this cron job.');
228                }
229            }
230        }
231    }
232
233    protected function finishPassForUser($active_id, $obj_id)
234    {
235        $this->processLockerFactory->setActiveId($active_id);
236        $processLocker = $this->processLockerFactory->getLocker();
237
238        $pass_finisher = new ilTestPassFinishTasks($active_id, $obj_id);
239        $pass_finisher->performFinishTasks($processLocker);
240
241        $this->log->info('Test session with active id (' . $active_id . ') and obj_id (' . $obj_id . ') is now finished.');
242    }
243}
244