1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4/**
5* Test results import parser
6*
7* @author Helmut Schottmüller <ilias@aurealis.de>
8* @version $Id$
9* @ingroup ModulesTest
10*/
11include_once("./Services/Xml/classes/class.ilSaxParser.php");
12
13class ilTestResultsImportParser extends ilSaxParser
14{
15    private $tst_obj;
16    private $table;
17    private $active_id_mapping;
18    private $question_id_mapping;
19    private $user_criteria_field;
20    private $user_criteria_type;
21    private $user_criteria_checked = false;
22
23    protected $src_pool_def_id_mapping;
24
25    /**
26    * Constructor
27    */
28    public function __construct($a_xml_file, &$test_object)
29    {
30        parent::__construct($a_xml_file, true);
31        $this->tst_obj = &$test_object;
32        $this->table = '';
33        $this->active_id_mapping = array();
34        $this->question_id_mapping = array();
35        $this->user_criteria_checked = false;
36        $this->src_pool_def_id_mapping = array();
37    }
38
39    /**
40     * @return array
41     */
42    public function getQuestionIdMapping()
43    {
44        return $this->question_id_mapping;
45    }
46
47    /**
48     * @param array $question_id_mapping
49     */
50    public function setQuestionIdMapping($question_id_mapping)
51    {
52        $this->question_id_mapping = $question_id_mapping;
53    }
54
55    /**
56     * @return array
57     */
58    public function getSrcPoolDefIdMapping()
59    {
60        return $this->src_pool_def_id_mapping;
61    }
62
63    /**
64     * @param array $src_pool_def_id_mapping
65     */
66    public function setSrcPoolDefIdMapping($src_pool_def_id_mapping)
67    {
68        $this->src_pool_def_id_mapping = $src_pool_def_id_mapping;
69    }
70
71    /**
72    * set event handler
73    * should be overwritten by inherited class
74    * @access	private
75    */
76    public function setHandlers($a_xml_parser)
77    {
78        xml_set_object($a_xml_parser, $this);
79        xml_set_element_handler($a_xml_parser, 'handlerBeginTag', 'handlerEndTag');
80        xml_set_character_data_handler($a_xml_parser, 'handlerParseCharacterData');
81    }
82
83    /**
84    * handler for begin of element parser
85    */
86    public function handlerBeginTag($a_xml_parser, $a_name, $a_attribs)
87    {
88        global $DIC;
89        $ilDB = $DIC['ilDB'];
90
91        $this->sametag = false;
92        $this->characterbuffer = "";
93        $this->depth[$a_xml_parser]++;
94        $this->path[$this->depth[$a_xml_parser]] = strtolower($a_name);
95        $this->qti_element = $a_name;
96
97        switch (strtolower($a_name)) {
98            case "results":
99                break;
100            case "row":
101                switch ($this->table) {
102                    case 'tst_active':
103                        if (!$this->user_criteria_checked) {
104                            $this->user_criteria_checked = true;
105                            if ($ilDB->tableColumnExists('usr_data', $a_attribs['user_criteria'])) {
106                                $analyzer = new ilDBAnalyzer();
107                                $info = $analyzer->getFieldInformation('usr_data');
108                                $this->user_criteria_field = $a_attribs['user_criteria'];
109                                $this->user_criteria_type = $info[$a_attribs['user_criteria']]['type'];
110                            }
111                        }
112                        $usr_id = ANONYMOUS_USER_ID;
113                        if (strlen($this->user_criteria_field)) {
114                            $result = $ilDB->queryF(
115                                "SELECT usr_id FROM usr_data WHERE " . $this->user_criteria_field . " =  %s",
116                                array($this->user_criteria_type),
117                                array($a_attribs[$this->user_criteria_field])
118                            );
119                            if ($result->numRows()) {
120                                $row = $ilDB->fetchAssoc($result);
121                                $usr_id = $row['usr_id'];
122                            }
123                        }
124                        $next_id = $ilDB->nextId('tst_active');
125
126                        $ilDB->insert('tst_active', array(
127                            'active_id' => array('integer', $next_id),
128                            'user_fi' => array('integer', $usr_id),
129                            'anonymous_id' => array('text', strlen($a_attribs['anonymous_id']) ? $a_attribs['anonymous_id'] : null),
130                            'test_fi' => array('integer', $this->tst_obj->getTestId()),
131                            'lastindex' => array('integer', $a_attribs['lastindex']),
132                            'tries' => array('integer', $a_attribs['tries']),
133                            'submitted' => array('integer', $a_attribs['submitted']),
134                            'submittimestamp' => array('timestamp', strlen($a_attribs['submittimestamp']) ? $a_attribs['submittimestamp'] : null),
135                            'tstamp' => array('integer', $a_attribs['tstamp']),
136                            'importname' => array('text', $a_attribs['fullname']),
137                            'last_finished_pass' => array('integer', $this->fetchLastFinishedPass($a_attribs)),
138                            'last_started_pass' => array('integer', $this->fetchLastStartedPass($a_attribs)),
139                            'answerstatusfilter' => array('integer', $this->fetchAttribute($a_attribs, 'answer_status_filter')),
140                            'objective_container' => array('integer', $this->fetchAttribute($a_attribs, 'objective_container'))
141                        ));
142                        $this->active_id_mapping[$a_attribs['active_id']] = $next_id;
143                        break;
144                    case 'tst_test_rnd_qst':
145                        $nextId = $ilDB->nextId('tst_test_rnd_qst');
146                        $newActiveId = $this->active_id_mapping[$a_attribs['active_fi']];
147                        $newQuestionId = $this->question_id_mapping[$a_attribs['question_fi']];
148                        $newSrcPoolDefId = $this->src_pool_def_id_mapping[$a_attribs['src_pool_def_fi']];
149                        $ilDB->insert('tst_test_rnd_qst', array(
150                            'test_random_question_id' => array('integer', $nextId),
151                            'active_fi' => array('integer', $newActiveId),
152                            'question_fi' => array('integer', $newQuestionId),
153                            'sequence' => array('integer', $a_attribs['sequence']),
154                            'pass' => array('integer', $a_attribs['pass']),
155                            'tstamp' => array('integer', $a_attribs['tstamp']),
156                            'src_pool_def_fi' => array('integer', $newSrcPoolDefId)
157                        ));
158                        break;
159                    case 'tst_pass_result':
160                        $affectedRows = $ilDB->manipulateF(
161                            "INSERT INTO tst_pass_result (active_fi, pass, points, maxpoints, questioncount, answeredquestions, workingtime, tstamp) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
162                            array(
163                                'integer',
164                                'integer',
165                                'float',
166                                'float',
167                                'integer',
168                                'integer',
169                                'integer',
170                                'integer'
171                            ),
172                            array(
173                                $this->active_id_mapping[$a_attribs['active_fi']],
174                                strlen($a_attribs['pass']) ? $a_attribs['pass'] : 0,
175                                ($a_attribs["points"]) ? $a_attribs["points"] : 0,
176                                ($a_attribs["maxpoints"]) ? $a_attribs["maxpoints"] : 0,
177                                $a_attribs["questioncount"],
178                                $a_attribs["answeredquestions"],
179                                ($a_attribs["workingtime"]) ? $a_attribs["workingtime"] : 0,
180                                $a_attribs["tstamp"]
181                            )
182                        );
183                        break;
184                    case 'tst_result_cache':
185                        $affectedRows = $ilDB->manipulateF(
186                            "INSERT INTO tst_result_cache (active_fi, pass, max_points, reached_points, mark_short, mark_official, passed, failed, tstamp) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)",
187                            array(
188                                'integer',
189                                'integer',
190                                'float',
191                                'float',
192                                'text',
193                                'text',
194                                'integer',
195                                'integer',
196                                'integer'
197                            ),
198                            array(
199                                $this->active_id_mapping[$a_attribs['active_fi']],
200                                strlen($a_attribs['pass']) ? $a_attribs['pass'] : 0,
201                                ($a_attribs["max_points"]) ? $a_attribs["max_points"] : 0,
202                                ($a_attribs["reached_points"]) ? $a_attribs["reached_points"] : 0,
203                                strlen($a_attribs["mark_short"]) ? $a_attribs["mark_short"] : " ",
204                                strlen($a_attribs["mark_official"]) ? $a_attribs["mark_official"] : " ",
205                                ($a_attribs["passed"]) ? 1 : 0,
206                                ($a_attribs["failed"]) ? 1 : 0,
207                                $a_attribs["tstamp"]
208                            )
209                        );
210                        break;
211                    case 'tst_sequence':
212                        $affectedRows = $ilDB->insert("tst_sequence", array(
213                            "active_fi" => array("integer", $this->active_id_mapping[$a_attribs['active_fi']]),
214                            "pass" => array("integer", $a_attribs['pass']),
215                            "sequence" => array("clob", $a_attribs['sequence']),
216                            "postponed" => array("text", (strlen($a_attribs['postponed'])) ? $a_attribs['postponed'] : null),
217                            "hidden" => array("text", (strlen($a_attribs['hidden'])) ? $a_attribs['hidden'] : null),
218                            "tstamp" => array("integer", $a_attribs['tstamp'])
219                        ));
220                        break;
221                    case 'tst_solutions':
222                        $next_id = $ilDB->nextId('tst_solutions');
223                        $affectedRows = $ilDB->insert("tst_solutions", array(
224                            "solution_id" => array("integer", $next_id),
225                            "active_fi" => array("integer", $this->active_id_mapping[$a_attribs['active_fi']]),
226                            "question_fi" => array("integer", $this->question_id_mapping[$a_attribs['question_fi']]),
227                            "value1" => array("clob", (strlen($a_attribs['value1'])) ? $a_attribs['value1'] : null),
228                            "value2" => array("clob", (strlen($a_attribs['value2'])) ? $a_attribs['value2'] : null),
229                            "pass" => array("integer", $a_attribs['pass']),
230                            "tstamp" => array("integer", $a_attribs['tstamp'])
231                        ));
232                        break;
233                    case 'tst_test_result':
234                        $next_id = $ilDB->nextId('tst_test_result');
235                        $affectedRows = $ilDB->manipulateF(
236                            "INSERT INTO tst_test_result (test_result_id, active_fi, question_fi, points, pass, manual, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s)",
237                            array('integer', 'integer','integer', 'float', 'integer', 'integer','integer'),
238                            array($next_id, $this->active_id_mapping[$a_attribs['active_fi']], $this->question_id_mapping[$a_attribs['question_fi']], $a_attribs['points'], $a_attribs['pass'], (strlen($a_attribs['manual'])) ? $a_attribs['manual'] : 0, $a_attribs['tstamp'])
239                        );
240                        break;
241                    case 'tst_times':
242                        $next_id = $ilDB->nextId('tst_times');
243                        $affectedRows = $ilDB->manipulateF(
244                            "INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
245                            array('integer', 'integer', 'timestamp', 'timestamp', 'integer', 'integer'),
246                            array($next_id, $this->active_id_mapping[$a_attribs['active_fi']], $a_attribs['started'], $a_attribs['finished'], $a_attribs['pass'], $a_attribs['tstamp'])
247                        );
248                        break;
249                }
250                break;
251            default:
252                $this->table = $a_name;
253                break;
254        }
255    }
256
257    /**
258    * handler for end of element
259    */
260    public function handlerEndTag($a_xml_parser, $a_name)
261    {
262        switch (strtolower($a_name)) {
263            case "tst_active":
264                global $DIC;
265                $ilLog = $DIC['ilLog'];
266                $ilLog->write("active id mapping: " . print_r($this->active_id_mapping, true));
267                break;
268            case "tst_test_question":
269                global $DIC;
270                $ilLog = $DIC['ilLog'];
271                $ilLog->write("question id mapping: " . print_r($this->question_id_mapping, true));
272                break;
273        }
274    }
275
276    /**
277      * handler for character data
278      */
279    public function handlerParseCharacterData($a_xml_parser, $a_data)
280    {
281        // do nothing
282    }
283
284    private function fetchAttribute($attributes, $name)
285    {
286        if (isset($attributes[$name])) {
287            return $attributes[$name];
288        }
289
290        return null;
291    }
292
293    private function fetchLastFinishedPass($attribs)
294    {
295        if (isset($attribs['last_finished_pass'])) {
296            return $attribs['last_finished_pass'];
297        }
298
299        if ($attribs['tries'] > 0) {
300            return $attribs['tries'] - 1;
301        }
302
303        return null;
304    }
305
306    private function fetchLastStartedPass($attribs)
307    {
308        if (isset($attribs['last_started_pass'])) {
309            return $attribs['last_started_pass'];
310        }
311
312        if ($attribs['tries'] > 0) {
313            return $attribs['tries'] - 1;
314        }
315
316        return null;
317    }
318}
319