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/Test 10 */ 11class ilTestPassesSelector 12{ 13 protected $db; 14 15 protected $testOBJ; 16 17 private $activeId; 18 19 private $lastFinishedPass = null; 20 21 private $passes = null; 22 23 private $testPassedOnceCache = array(); 24 25 public function __construct(ilDBInterface $db, ilObjTest $testOBJ) 26 { 27 $this->db = $db; 28 $this->testOBJ = $testOBJ; 29 } 30 31 public function getActiveId() 32 { 33 return $this->activeId; 34 } 35 36 public function setActiveId($activeId) 37 { 38 $this->activeId = $activeId; 39 } 40 41 public function getLastFinishedPass() 42 { 43 return $this->lastFinishedPass; 44 } 45 46 public function setLastFinishedPass($lastFinishedPass) 47 { 48 $lastFinishedPass = $lastFinishedPass === null ? -1 : $lastFinishedPass; 49 $this->lastFinishedPass = $lastFinishedPass; 50 } 51 52 private function passesLoaded() 53 { 54 return is_array($this->passes); 55 } 56 57 private function ensureLoadedPasses() 58 { 59 if (!$this->passesLoaded()) { 60 $this->loadPasses(); 61 } 62 } 63 private function loadPasses() 64 { 65 $query = " 66 SELECT DISTINCT tst_pass_result.* FROM tst_pass_result 67 LEFT JOIN tst_test_result 68 ON tst_pass_result.pass = tst_test_result.pass 69 AND tst_pass_result.active_fi = tst_test_result.active_fi 70 WHERE tst_pass_result.active_fi = %s 71 ORDER BY tst_pass_result.pass 72 "; 73 74 $res = $this->db->queryF( 75 $query, 76 array('integer'), 77 array($this->getActiveId()) 78 ); 79 80 $this->passes = array(); 81 82 while ($row = $this->db->fetchAssoc($res)) { 83 $this->passes[$row['pass']] = $row; 84 } 85 } 86 87 private function getLazyLoadedPasses() 88 { 89 $this->ensureLoadedPasses(); 90 return $this->passes; 91 } 92 93 public function loadLastFinishedPass() 94 { 95 $query = " 96 SELECT last_finished_pass FROM tst_active WHERE active_id = %s 97 "; 98 99 $res = $this->db->queryF( 100 $query, 101 array('integer'), 102 array($this->getActiveId()) 103 ); 104 105 while ($row = $this->db->fetchAssoc($res)) { 106 $this->setLastFinishedPass($row['last_finished_pass']); 107 } 108 } 109 110 public function getExistingPasses() 111 { 112 return array_keys($this->getLazyLoadedPasses()); 113 } 114 115 public function hasExistingPasses() 116 { 117 return (bool) count($this->getExistingPasses()); 118 } 119 120 public function getNumExistingPasses() 121 { 122 return count($this->getExistingPasses()); 123 } 124 125 public function openPassExists() 126 { 127 return count($this->getExistingPasses()) > count($this->getClosedPasses()); 128 } 129 130 public function getClosedPasses() 131 { 132 $existingPasses = $this->getExistingPasses(); 133 $closedPasses = $this->fetchClosedPasses($existingPasses); 134 135 return $closedPasses; 136 } 137 138 public function getReportablePasses() 139 { 140 $existingPasses = $this->getExistingPasses(); 141 142 $reportablePasses = $this->fetchReportablePasses($existingPasses); 143 144 return $reportablePasses; 145 } 146 147 public function hasReportablePasses() 148 { 149 return (bool) count($this->getReportablePasses()); 150 } 151 152 private function fetchReportablePasses($existingPasses) 153 { 154 $lastPass = $this->fetchLastPass($existingPasses); 155 156 $reportablePasses = array(); 157 158 foreach ($existingPasses as $pass) { 159 if ($this->isReportablePass($lastPass, $pass)) { 160 $reportablePasses[] = $pass; 161 } 162 } 163 164 return $reportablePasses; 165 } 166 167 private function fetchClosedPasses($existingPasses) 168 { 169 $closedPasses = array(); 170 171 foreach ($existingPasses as $pass) { 172 if ($this->isClosedPass($pass)) { 173 $closedPasses[] = $pass; 174 } 175 } 176 177 return $closedPasses; 178 } 179 180 private function fetchLastPass($existingPasses) 181 { 182 $lastPass = null; 183 184 foreach ($existingPasses as $pass) { 185 if ($lastPass === null || $pass > $lastPass) { 186 $lastPass = $pass; 187 } 188 } 189 190 return $lastPass; 191 } 192 193 private function isReportablePass($lastPass, $pass) 194 { 195 switch ($this->testOBJ->getScoreReporting()) { 196 case ilObjTest::SCORE_REPORTING_IMMIDIATLY: 197 198 return true; 199 200 case ilObjTest::SCORE_REPORTING_DATE: 201 202 return $this->isReportingDateReached(); 203 204 case ilObjTest::SCORE_REPORTING_FINISHED: 205 206 if ($pass < $lastPass) { 207 return true; 208 } 209 210 return $this->isClosedPass($pass); 211 212 case ilObjTest::SCORE_REPORTING_AFTER_PASSED: 213 214 if (!$this->hasTestPassedOnce($this->getActiveId())) { 215 return false; 216 } 217 218 return $this->isClosedPass($pass); 219 } 220 221 return false; 222 } 223 224 private function checkLastFinishedPassInitialised() 225 { 226 if ($this->getLastFinishedPass() === null) { 227 throw new ilTestException('invalid object state: last finished pass was not set!'); 228 } 229 } 230 231 private function isClosedPass($pass) 232 { 233 $this->checkLastFinishedPassInitialised(); 234 235 if ($pass <= $this->getLastFinishedPass()) { 236 return true; 237 } 238 239 if ($this->isProcessingTimeReached($pass)) { 240 return true; 241 } 242 243 return false; 244 } 245 246 private function isReportingDateReached() 247 { 248 $reg = '/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/'; 249 $date = $this->testOBJ->getReportingDate(); 250 $matches = null; 251 252 if (!preg_match($reg, $date, $matches)) { 253 return false; 254 } 255 256 $repTS = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 257 258 return time() >= $repTS; 259 } 260 261 private function isProcessingTimeReached($pass) 262 { 263 if (!$this->testOBJ->getEnableProcessingTime()) { 264 return false; 265 } 266 267 $startingTime = $this->testOBJ->getStartingTimeOfUser($this->getActiveId(), $pass); 268 269 if ($startingTime === false) { 270 return false; 271 } 272 273 return $this->testOBJ->isMaxProcessingTimeReached($startingTime, $this->getActiveId()); 274 } 275 276 /** 277 * @return int timestamp 278 */ 279 public function getLastFinishedPassTimestamp() 280 { 281 if ($this->getLastFinishedPass() === null) { 282 return null; 283 } 284 285 $passes = $this->getLazyLoadedPasses(); 286 return $passes[$this->getLastFinishedPass()]['tstamp']; 287 } 288 289 public function hasTestPassedOnce($activeId) 290 { 291 global $DIC; /* @var ILIAS\DI\Container $DIC */ 292 293 if (!isset($this->testPassedOnceCache[$activeId])) { 294 $this->testPassedOnceCache[$activeId] = false; 295 296 $res = $DIC->database()->queryF( 297 "SELECT passed_once FROM tst_result_cache WHERE active_fi = %s", 298 array('integer'), 299 array($activeId) 300 ); 301 302 while ($row = $DIC->database()->fetchAssoc($res)) { 303 $this->testPassedOnceCache[$activeId] = (bool) $row['passed_once']; 304 } 305 } 306 307 return $this->testPassedOnceCache[$activeId]; 308 } 309} 310