1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4
5/**
6 * @author		Björn Heyser <bheyser@databay.de>
7 * @version		$Id$
8 *
9 * @package     Modules/TestQuestionPool
10 */
11class ilAssQuestionUserSolutionAdopter
12{
13    /**
14     * @var ressource
15     */
16    protected static $preparedDeleteSolutionRecordsStatement = null;
17
18    /**
19     * @var ressource
20     */
21    protected static $preparedSelectSolutionRecordsStatement = null;
22
23    /**
24     * @var ressource
25     */
26    protected static $preparedInsertSolutionRecordStatement = null;
27
28    /**
29     * @var ressource
30     */
31    protected static $preparedDeleteResultRecordStatement = null;
32
33    /**
34     * @var ressource
35     */
36    protected static $preparedSelectResultRecordStatement = null;
37
38    /**
39     * @var ressource
40     */
41    protected static $preparedInsertResultRecordStatement = null;
42
43    /**
44     * @var ilDBInterface
45     */
46    protected $db;
47
48    /**
49     * @var ilAssQuestionProcessLockerFactory
50     */
51    protected $processLockerFactory;
52
53    /**
54     * @var integer
55     */
56    protected $userId;
57
58    /**
59     * @var integer
60     */
61    protected $activeId;
62
63    /**
64     * @var integer
65     */
66    protected $targetPass;
67
68    /**
69     * @var array
70     */
71    protected $questionIds;
72
73    /**
74     * @param ilDBInterface $db
75     * @param ilSetting $assSettings
76     * @param bool $isAssessmentLogEnabled
77     */
78    public function __construct(ilDBInterface $db, ilSetting $assSettings, $isAssessmentLogEnabled)
79    {
80        $this->db = $db;
81
82        $this->userId = null;
83        $this->activeId = null;
84        $this->targetPass = null;
85        $this->questionIds = array();
86
87        require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLockerFactory.php';
88        $this->processLockerFactory = new ilAssQuestionProcessLockerFactory($assSettings, $db);
89        $this->processLockerFactory->setAssessmentLogEnabled($isAssessmentLogEnabled);
90    }
91
92    /**
93     * @return int
94     */
95    public function getUserId()
96    {
97        return $this->userId;
98    }
99
100    /**
101     * @param int $userId
102     */
103    public function setUserId($userId)
104    {
105        $this->userId = $userId;
106    }
107
108    /**
109     * @return int
110     */
111    public function getActiveId()
112    {
113        return $this->activeId;
114    }
115
116    /**
117     * @param int $activeId
118     */
119    public function setActiveId($activeId)
120    {
121        $this->activeId = $activeId;
122    }
123
124    /**
125     * @return int
126     */
127    public function getTargetPass()
128    {
129        return $this->targetPass;
130    }
131
132    /**
133     * @param int $targetPass
134     */
135    public function setTargetPass($targetPass)
136    {
137        $this->targetPass = $targetPass;
138    }
139
140    /**
141     * @return array
142     */
143    public function getQuestionIds()
144    {
145        return $this->questionIds;
146    }
147
148    /**
149     * @param array $questionIds
150     */
151    public function setQuestionIds($questionIds)
152    {
153        $this->questionIds = $questionIds;
154    }
155
156    public function perform()
157    {
158        $this->processLockerFactory->setUserId($this->getUserId());
159
160        foreach ($this->getQuestionIds() as $questionId) {
161            $this->processLockerFactory->setQuestionId($questionId);
162            $processLocker = $this->processLockerFactory->getLocker();
163
164            $processLocker->executeUserTestResultUpdateLockOperation(function () use ($questionId) {
165                $this->adoptQuestionAnswer($questionId);
166            });
167        }
168    }
169
170    protected function adoptQuestionAnswer($questionId)
171    {
172        $this->resetTargetSolution($questionId);
173        $this->resetTargetResult($questionId);
174
175        $sourcePass = $this->adoptSourceSolution($questionId);
176
177        if ($sourcePass !== null) {
178            $this->adoptSourceResult($questionId, $sourcePass);
179        }
180    }
181
182    protected function resetTargetSolution($questionId)
183    {
184        $this->db->execute(
185            $this->getPreparedDeleteSolutionRecordsStatement(),
186            array($this->getActiveId(), $questionId, $this->getTargetPass())
187        );
188    }
189
190    protected function resetTargetResult($questionId)
191    {
192        $this->db->execute(
193            $this->getPreparedDeleteResultRecordStatement(),
194            array($this->getActiveId(), $questionId, $this->getTargetPass())
195        );
196    }
197
198    protected function adoptSourceSolution($questionId)
199    {
200        $res = $this->db->execute(
201            $this->getPreparedSelectSolutionRecordsStatement(),
202            array($this->getActiveId(), $questionId, $this->getTargetPass())
203        );
204
205        $sourcePass = null;
206
207        while ($row = $this->db->fetchAssoc($res)) {
208            if ($sourcePass === null) {
209                $sourcePass = $row['pass'];
210            } elseif ($row['pass'] < $sourcePass) {
211                break;
212            }
213
214            $solutionId = $this->db->nextId('tst_solutions');
215
216            $this->db->execute($this->getPreparedInsertSolutionRecordStatement(), array(
217                $solutionId, $this->getActiveId(), $questionId, $this->getTargetPass(), time(),
218                $row['points'], $row['value1'], $row['value2']
219            ));
220        }
221
222        return $sourcePass;
223    }
224
225    protected function adoptSourceResult($questionId, $sourcePass)
226    {
227        $res = $this->db->execute(
228            $this->getPreparedSelectResultRecordStatement(),
229            array($this->getActiveId(), $questionId, $sourcePass)
230        );
231
232        $row = $this->db->fetchAssoc($res);
233
234        $resultId = $this->db->nextId('tst_test_result');
235
236        $this->db->execute($this->getPreparedInsertResultRecordStatement(), array(
237            $resultId, $this->getActiveId(), $questionId, $this->getTargetPass(), time(),
238            $row['points'], $row['manual'], $row['hint_count'], $row['hint_points'], $row['answered']
239        ));
240    }
241
242    protected function getPreparedDeleteSolutionRecordsStatement()
243    {
244        if (self::$preparedDeleteSolutionRecordsStatement === null) {
245            self::$preparedDeleteSolutionRecordsStatement = $this->db->prepareManip(
246                "DELETE FROM tst_solutions WHERE active_fi = ? AND question_fi = ? AND pass = ?",
247                array('integer', 'integer', 'integer')
248            );
249        }
250
251        return self::$preparedDeleteSolutionRecordsStatement;
252    }
253
254    protected function getPreparedSelectSolutionRecordsStatement()
255    {
256        if (self::$preparedSelectSolutionRecordsStatement === null) {
257            $query = "
258				SELECT pass, points, value1, value2 FROM tst_solutions
259				WHERE active_fi = ? AND question_fi = ? AND pass < ? ORDER BY pass DESC
260			";
261
262            self::$preparedSelectSolutionRecordsStatement = $this->db->prepare(
263                $query,
264                array('integer', 'integer', 'integer')
265            );
266        }
267
268        return self::$preparedSelectSolutionRecordsStatement;
269    }
270
271    protected function getPreparedInsertSolutionRecordStatement()
272    {
273        if (self::$preparedInsertSolutionRecordStatement === null) {
274            $query = "
275				INSERT INTO tst_solutions (
276					solution_id, active_fi, question_fi, pass, tstamp, points, value1, value2
277				) VALUES (
278					?, ?, ?, ?, ?, ?, ?, ?
279				)
280			";
281
282            self::$preparedInsertSolutionRecordStatement = $this->db->prepareManip(
283                $query,
284                array('integer', 'integer', 'integer', 'integer', 'integer', 'integer', 'text', 'text')
285            );
286        }
287
288        return self::$preparedInsertSolutionRecordStatement;
289    }
290
291    protected function getPreparedDeleteResultRecordStatement()
292    {
293        if (self::$preparedDeleteResultRecordStatement === null) {
294            self::$preparedDeleteResultRecordStatement = $this->db->prepareManip(
295                "DELETE FROM tst_test_result WHERE active_fi = ? AND question_fi = ? AND pass = ?",
296                array('integer', 'integer', 'integer')
297            );
298        }
299
300        return self::$preparedDeleteResultRecordStatement;
301    }
302
303    protected function getPreparedSelectResultRecordStatement()
304    {
305        if (self::$preparedSelectResultRecordStatement === null) {
306            $query = "
307				SELECT points, manual, hint_count, hint_points, answered FROM tst_test_result
308				WHERE active_fi = ? AND question_fi = ? AND pass = ?
309			";
310
311            self::$preparedSelectResultRecordStatement = $this->db->prepare(
312                $query,
313                array('integer', 'integer', 'integer')
314            );
315        }
316
317        return self::$preparedSelectResultRecordStatement;
318    }
319
320    protected function getPreparedInsertResultRecordStatement()
321    {
322        if (self::$preparedInsertResultRecordStatement === null) {
323            $query = "
324				INSERT INTO tst_test_result (
325					test_result_id, active_fi, question_fi, pass, tstamp,
326					points, manual, hint_count, hint_points, answered
327				) VALUES (
328					?, ?, ?, ?, ?, ?, ?, ?, ?, ?
329				)
330			";
331
332            self::$preparedInsertResultRecordStatement = $this->db->prepareManip(
333                $query,
334                array('integer', 'integer', 'integer', 'integer', 'integer', 'integer', 'integer', 'integer', 'integer', 'integer')
335            );
336        }
337
338        return self::$preparedInsertResultRecordStatement;
339    }
340}
341