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
24/**
25* @author Stefan Meyer <meyer@leifos.com>
26*
27* @version $Id$
28*
29* @package ilias-tracking
30*
31*/
32
33include_once './Services/Tracking/classes/class.ilLPStatus.php';
34
35class ilLPStatusTestPassed extends ilLPStatus
36{
37    public function __construct($a_obj_id)
38    {
39        global $DIC;
40
41        $ilDB = $DIC['ilDB'];
42
43        parent::__construct($a_obj_id);
44        $this->db = $ilDB;
45    }
46
47    public static function _getInProgress($a_obj_id)
48    {
49        global $DIC;
50
51        $ilBench = $DIC['ilBench'];
52
53        $ilBench->start('LearningProgress', '9182_LPStatusTestPassed_inProgress');
54        $userIds = self::getUserIdsByResultArrayStatus($a_obj_id, 'in_progress');
55        $ilBench->stop('LearningProgress', '9182_LPStatusTestPassed_inProgress');
56
57        return $userIds;
58    }
59
60    public static function _getCompleted($a_obj_id)
61    {
62        global $DIC;
63
64        $ilBench = $DIC['ilBench'];
65
66        $ilBench->start('LearningProgress', '9183_LPStatusTestPassed_completed');
67        $userIds = self::getUserIdsByResultArrayStatus($a_obj_id, 'passed');
68        $ilBench->stop('LearningProgress', '9183_LPStatusTestPassed_completed');
69
70        return $userIds;
71    }
72
73    public static function _getNotAttempted($a_obj_id)
74    {
75        return self::getUserIdsByResultArrayStatus($a_obj_id, 'not_attempted');
76    }
77
78    public static function _getFailed($a_obj_id)
79    {
80        return self::getUserIdsByResultArrayStatus($a_obj_id, 'failed');
81    }
82
83    private static function getUserIdsByResultArrayStatus($objId, $resultArrayStatus)
84    {
85        $status_info = ilLPStatusWrapper::_getStatusInfo($objId);
86
87        $user_ids = array();
88
89        foreach ($status_info['results'] as $user_data) {
90            if ($user_data[$resultArrayStatus]) {
91                $user_ids[] = $user_data['user_id'];
92            }
93        }
94
95        return $user_ids;
96    }
97
98    public static function _getStatusInfo($a_obj_id)
99    {
100        include_once './Modules/Test/classes/class.ilObjTestAccess.php';
101        $status_info['results'] = ilObjTestAccess::_getPassedUsers($a_obj_id);
102        return $status_info;
103    }
104
105    /**
106     * Determine status.
107     *
108     * Behaviour of "old" 4.0 learning progress:
109     *
110     * Setting "Multiple Pass Scoring": Score the last pass
111     * - Test not started: No entry
112     * - First question opened: Icon/Text: Failed, Score 0%
113     * - First question answered (correct, points enough for passing): Icon/Text: Completed, Score 66%
114     * - No change after successfully finishing the pass. (100%)
115     * - 2nd Pass, first question opened: Still Completed/Completed
116     * - First question answered (incorrect, success possible): Icon/Text Failed, Score 33%
117     * - Second question answered (correct): Icon/Text completed
118     * - 3rd pass, like 2nd, but two times wrong answer: Icon/Text: Failed
119     *
120     * Setting "Multiple Pass Scoring": Score the best pass
121     * - Test not started: No entry
122     * - First question opened: Icon/Text: Failed, Score 0%
123     * - First question answered (correct, points enough for passing): Icon/Text: Completed, Score 66%
124     * - No change after successfully finishing the pass. (100%)
125     * - 2nd Pass, first question opened: Still Completed/Completed
126     * - First question answered (incorrect, success possible): Still Completed/Completed
127     *
128     * Due to this behaviour in 4.0 we do not have a "in progress" status. During the test
129     * the status is "failed" unless the score is enough to pass the test, which makes the
130     * learning progress status "completed".
131     *
132     * @param	integer		object id
133     * @param	integer		user id
134     * @param	object		object (optional depends on object type)
135     * @return	integer		status
136     */
137    public function determineStatus($a_obj_id, $a_user_id, $a_obj = null)
138    {
139        global $DIC;
140
141        $ilDB = $DIC['ilDB'];
142
143        $status = self::LP_STATUS_NOT_ATTEMPTED_NUM;
144        require_once 'Modules/Test/classes/class.ilObjTestAccess.php';
145        $res = $ilDB->query("
146			SELECT tst_active.active_id, tst_active.tries, count(tst_sequence.active_fi) " . $ilDB->quoteIdentifier("sequences") . ", tst_active.last_finished_pass,
147				CASE WHEN
148					(tst_tests.nr_of_tries - 1) = tst_active.last_finished_pass
149				THEN '1'
150				ELSE '0'
151				END is_last_pass
152			FROM tst_active
153			LEFT JOIN tst_sequence
154			ON tst_sequence.active_fi = tst_active.active_id
155			LEFT JOIN tst_tests
156			ON tst_tests.test_id = tst_active.test_fi
157			WHERE tst_active.user_fi = {$ilDB->quote($a_user_id, "integer")}
158			AND tst_active.test_fi = {$ilDB->quote(ilObjTestAccess::_getTestIDFromObjectID($a_obj_id))}
159			GROUP BY tst_active.active_id, tst_active.tries, is_last_pass
160		");
161
162        if ($rec = $ilDB->fetchAssoc($res)) {
163            if ($rec['sequences'] > 0) {
164                require_once 'Modules/Test/classes/class.ilObjTest.php';
165
166                $test_obj = new ilObjTest($a_obj_id, false);
167                $is_passed = ilObjTestAccess::_isPassed($a_user_id, $a_obj_id);
168
169                if ($test_obj->getPassScoring() == SCORE_LAST_PASS) {
170                    $is_finished = false;
171                    if ($rec['last_finished_pass'] != null && $rec['sequences'] - 1 == $rec['last_finished_pass']) {
172                        $is_finished = true;
173                    }
174                    $status = $this->determineStatusForScoreLastPassTests($is_finished, $is_passed);
175                } elseif ($test_obj->getPassScoring() == SCORE_BEST_PASS) {
176                    $status = self::LP_STATUS_IN_PROGRESS_NUM;
177
178                    if ($rec['last_finished_pass'] != null) {
179                        $status = $this->determineLpStatus($is_passed);
180                    }
181
182                    if (!$rec['is_last_pass'] && $status == self::LP_STATUS_FAILED_NUM) {
183                        $status = self::LP_STATUS_IN_PROGRESS_NUM;
184                    }
185                }
186            }
187        }
188
189        return $status;
190    }
191
192    /**
193     * @param $is_finished
194     * @param $passed
195     * @return int
196     */
197    protected function determineStatusForScoreLastPassTests($is_finished, $passed)
198    {
199        $status = self::LP_STATUS_IN_PROGRESS_NUM;
200
201        if ($is_finished) {
202            $status = $this->determineLpStatus($passed);
203        }
204
205        return $status;
206    }
207
208    /**
209     * @param $passed
210     * @return int
211     */
212    protected function determineLpStatus($passed)
213    {
214        $status = self::LP_STATUS_FAILED_NUM;
215
216        if ($passed) {
217            $status = self::LP_STATUS_COMPLETED_NUM;
218        }
219
220        return $status;
221    }
222
223    /**
224     * Determine percentage
225     *
226     * @param	integer		object id
227     * @param	integer		user id
228     * @param	object		object (optional depends on object type)
229     * @return	integer		percentage
230     */
231    public function determinePercentage($a_obj_id, $a_user_id, $a_obj = null)
232    {
233        global $DIC;
234
235        $ilDB = $DIC['ilDB'];
236
237        $set = $ilDB->query("SELECT tst_result_cache.*, tst_active.user_fi FROM " .
238                     "tst_result_cache JOIN tst_active ON (tst_active.active_id = tst_result_cache.active_fi)" .
239                     " JOIN tst_tests ON (tst_tests.test_id = tst_active.test_fi) " .
240                     " WHERE tst_tests.obj_fi = " . $ilDB->quote($a_obj_id, "integer") .
241                     " AND tst_active.user_fi = " . $ilDB->quote($a_user_id, "integer"));
242        $per = 0;
243        if ($rec = $ilDB->fetchAssoc($set)) {
244            if ($rec["max_points"] > 0) {
245                $per = min(100, 100 / $rec["max_points"] * $rec["reached_points"]);
246            } else {
247                // According to mantis #12305
248                $per = 0;
249            }
250        }
251        return (int) $per;
252    }
253}
254