1<?php 2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4/** 5 * Class ilTestScoring 6 * 7 * This class holds a mechanism to get the scoring for 8 * - a test, 9 * - a user in a test, 10 * - a pass in a users passes in a test, or 11 * - a question in a pass in a users passes in a test. 12 * 13 * Warning: 14 * Please use carefully, this is one of the classes that may cause funny spikes on your servers load graph on large 15 * datasets in the test. 16 * 17 * @author Maximilian Becker <mbecker@databay.de> 18 * 19 * @version $Id$ 20 * 21 * @ingroup ModulesTest 22 */ 23class ilTestScoring 24{ 25 /** @var ilObjTest $test */ 26 protected $test; 27 28 /** @var bool $preserve_manual_scores */ 29 protected $preserve_manual_scores; 30 31 private $recalculatedPasses; 32 33 /** 34 * @var int 35 */ 36 protected $questionId = 0; 37 38 public function __construct(ilObjTest $test) 39 { 40 $this->test = $test; 41 $this->preserve_manual_scores = false; 42 43 $this->recalculatedPasses = array(); 44 } 45 46 /** 47 * @param boolean $preserve_manual_scores 48 */ 49 public function setPreserveManualScores($preserve_manual_scores) 50 { 51 $this->preserve_manual_scores = $preserve_manual_scores; 52 } 53 54 /** 55 * @return boolean 56 */ 57 public function getPreserveManualScores() 58 { 59 return $this->preserve_manual_scores; 60 } 61 62 /** 63 * @return int 64 */ 65 public function getQuestionId() 66 { 67 return $this->questionId; 68 } 69 70 /** 71 * @param int $questionId 72 */ 73 public function setQuestionId(int $questionId) 74 { 75 $this->questionId = $questionId; 76 } 77 78 public function recalculateSolutions() 79 { 80 $participants = $this->test->getCompleteEvaluationData(false)->getParticipants(); 81 if (is_array($participants)) { 82 require_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 83 foreach ($participants as $active_id => $userdata) { 84 if (is_object($userdata) && is_array($userdata->getPasses())) { 85 $this->recalculatePasses($userdata, $active_id); 86 } 87 assQuestion::_updateTestResultCache($active_id); 88 } 89 } 90 } 91 92 /** 93 * Updates passed status of the Test 94 * 95 * @param $active_id 96 * @param $pass 97 */ 98 public function recalculateSolution($active_id, $pass) 99 { 100 $user_data = $this 101 ->test 102 ->getCompleteEvaluationData(false) 103 ->getParticipant($active_id) 104 ->getPass($pass); 105 106 $this->recalculatePass($user_data, $active_id, $pass); 107 assQuestion::_updateTestResultCache($active_id); 108 } 109 110 /** 111 * @param $userdata 112 * @param $active_id 113 */ 114 public function recalculatePasses($userdata, $active_id) 115 { 116 $passes = $userdata->getPasses(); 117 foreach ($passes as $pass => $passdata) { 118 if (is_object($passdata)) { 119 $this->recalculatePass($passdata, $active_id, $pass); 120 $this->addRecalculatedPassByActive($active_id, $pass); 121 } 122 } 123 } 124 125 /** 126 * @param $passdata 127 * @param $active_id 128 * @param $pass 129 */ 130 public function recalculatePass($passdata, $active_id, $pass) 131 { 132 $questions = $passdata->getAnsweredQuestions(); 133 if (is_array($questions)) { 134 foreach ($questions as $questiondata) { 135 if ($this->getQuestionId() && $this->getQuestionId() != $questiondata['id']) { 136 continue; 137 } 138 139 $question_gui = $this->test->createQuestionGUI("", $questiondata['id']); 140 $this->recalculateQuestionScore($question_gui, $active_id, $pass, $questiondata); 141 } 142 } 143 } 144 145 /** 146 * @param $question_gui 147 * @param $active_id 148 * @param $pass 149 * @param $questiondata 150 */ 151 public function recalculateQuestionScore($question_gui, $active_id, $pass, $questiondata) 152 { 153 /** @var assQuestion $question_gui */ 154 if (is_object($question_gui)) { 155 $reached = $question_gui->object->calculateReachedPoints($active_id, $pass); 156 $actual_reached = $question_gui->object->adjustReachedPointsByScoringOptions($reached, $active_id, $pass); 157 158 if ($this->preserve_manual_scores == true && $questiondata['manual'] == '1') { 159 // Do we need processing here? 160 } else { 161 assQuestion::setForcePassResultUpdateEnabled(true); 162 163 assQuestion::_setReachedPoints( 164 $active_id, 165 $questiondata['id'], 166 $actual_reached, 167 $question_gui->object->getMaximumPoints(), 168 $pass, 169 false, 170 true 171 ); 172 173 assQuestion::setForcePassResultUpdateEnabled(false); 174 } 175 } 176 } 177 178 /** 179 * @return string HTML with the best solution output. 180 */ 181 public function calculateBestSolutionForTest() 182 { 183 $solution = ''; 184 foreach ($this->test->getAllQuestions() as $question) { 185 /** @var AssQuestionGUI $question_gui */ 186 $question_gui = $this->test->createQuestionGUI("", $question['question_id']); 187 $solution .= $question_gui->getSolutionOutput(0, null, true, true, false, false, true, false); 188 } 189 190 return $solution; 191 } 192 193 public function resetRecalculatedPassesByActives() 194 { 195 $this->recalculatedPasses = array(); 196 } 197 198 public function getRecalculatedPassesByActives() 199 { 200 return $this->recalculatedPasses; 201 } 202 203 public function addRecalculatedPassByActive($activeId, $pass) 204 { 205 if (!is_array($this->recalculatedPasses[$activeId])) { 206 $this->recalculatedPasses[$activeId] = array(); 207 } 208 209 $this->recalculatedPasses[$activeId][] = $pass; 210 } 211 212 public function removeAllQuestionResults($questionId) 213 { 214 global $DIC; /* @var ILIAS\DI\Container $DIC */ 215 216 $query = "DELETE FROM tst_test_result WHERE question_fi = %s"; 217 $DIC->database()->manipulateF($query, array('integer'), array($questionId)); 218 } 219 220 public function updatePassAndTestResults($activeIds) 221 { 222 global $DIC; /* @var ILIAS\DI\Container $DIC */ 223 224 foreach ($activeIds as $activeId) { 225 $passSelector = new ilTestPassesSelector($DIC->database(), $this->test); 226 $passSelector->setActiveId($activeId); 227 228 foreach ($passSelector->getExistingPasses() as $pass) { 229 assQuestion::_updateTestPassResults($activeId, $pass, $this->test->areObligationsEnabled()); 230 } 231 232 assQuestion::_updateTestResultCache($activeId); 233 } 234 } 235 236 /** 237 * @return int 238 */ 239 public function getNumManualScorings() 240 { 241 global $DIC; /* @var ILIAS\DI\Container $DIC */ 242 243 $query = " 244 SELECT COUNT(*) num_manual_scorings 245 FROM tst_test_result tres 246 247 INNER JOIN tst_active tact 248 ON tact.active_id = tres.active_fi 249 AND tact.test_fi = %s 250 251 WHERE tres.manual = 1 252 "; 253 254 $types = array('integer'); 255 $values = array($this->test->getTestId()); 256 257 if ($this->getQuestionId()) { 258 $query .= " 259 AND tres.question_fi = %s 260 "; 261 262 $types[] = 'integer'; 263 $values[] = $this->getQuestionId(); 264 } 265 266 $res = $DIC->database()->queryF($query, $types, $values); 267 268 while ($row = $DIC->database()->fetchAssoc($res)) { 269 return (int) $row['num_manual_scorings']; 270 } 271 272 return 0; 273 } 274} 275