Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
61.64% |
45 / 73 |
|
42.11% |
8 / 19 |
CRAP | |
0.00% |
0 / 1 |
taoQtiTest_helpers_TestSessionStorage | |
61.64% |
45 / 73 |
|
42.11% |
8 / 19 |
99.23 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getLastError | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setLastError | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUserUri | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setUserUri | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
retrieve | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
retrieveSessionInReadMode | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
retrieveSessionInWriteMode | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
instantiate | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
persist | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
lockSession | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
2.02 | |||
getRetrievalStream | |
84.62% |
11 / 13 |
|
0.00% |
0 / 1 |
4.06 | |||
persistStream | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
exists | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
2.15 | |||
delete | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
createBinaryStreamAccess | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getServiceLocator | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
sessionExists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
assessModeChangedToWrite | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | /** |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; under version 2 |
7 | * of the License (non-upgradable). |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | * |
18 | * Copyright (c) 2013-2016 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); |
19 | * |
20 | */ |
21 | |
22 | use oat\tao\model\state\StateStorage; |
23 | use qtism\common\storage\IStream; |
24 | use qtism\runtime\tests\AbstractSessionManager; |
25 | use qtism\common\storage\MemoryStream; |
26 | use qtism\runtime\storage\binary\BinaryAssessmentTestSeeker; |
27 | use qtism\runtime\storage\binary\AbstractQtiBinaryStorage; |
28 | use qtism\runtime\storage\common\StorageException; |
29 | use qtism\data\AssessmentTest; |
30 | use qtism\runtime\tests\AssessmentTestSession; |
31 | use qtism\runtime\storage\binary\QtiBinaryStreamAccess; |
32 | use oat\taoQtiTest\models\files\QtiFlysystemFileManager; |
33 | use oat\oatbox\service\ServiceManager; |
34 | use oat\oatbox\service\ServiceManagerAwareInterface; |
35 | use oat\oatbox\service\ServiceManagerAwareTrait; |
36 | |
37 | /** |
38 | * A QtiSm AssessmentTestSession Storage Service implementation for TAO. |
39 | * |
40 | * It is able to retrieve test sessions related to a given user and a given |
41 | * test definition. |
42 | * |
43 | * @author Jérôme Bogaerts <jerome@taotesting.com> |
44 | * |
45 | */ |
46 | class taoQtiTest_helpers_TestSessionStorage extends AbstractQtiBinaryStorage implements ServiceManagerAwareInterface |
47 | { |
48 | use ServiceManagerAwareTrait; |
49 | use oat\oatbox\mutex\LockTrait; |
50 | |
51 | /** |
52 | * The last recorded error. |
53 | * |
54 | * @var integer |
55 | */ |
56 | private $lastError = -1; |
57 | |
58 | /** |
59 | * The URI (Uniform Resource Identifier) of the user the Test Session belongs to. |
60 | * |
61 | * @var string |
62 | */ |
63 | private $userUri; |
64 | |
65 | /** |
66 | * @var AssessmentTestSession |
67 | */ |
68 | private static $session; |
69 | |
70 | /** |
71 | * Create a new TestSessionStorage object. |
72 | * |
73 | * @param AbstractSessionManager $manager The session manager to be used to create new AssessmentTestSession and |
74 | * AssessmentItemSession objects. |
75 | * @param BinaryAssessmentTestSeeker $seeker The seeker making able the storage engine to index AssessmentTest's |
76 | * components. |
77 | * @param string $userUri The URI (Uniform Resource Identifier) of the user the Test Session belongs to. |
78 | */ |
79 | public function __construct(AbstractSessionManager $manager, BinaryAssessmentTestSeeker $seeker, $userUri) |
80 | { |
81 | parent::__construct($manager, $seeker); |
82 | $this->setUserUri($userUri); |
83 | } |
84 | |
85 | /** |
86 | * Get the last retrieved error. -1 means |
87 | * no error. |
88 | * |
89 | * @return integer |
90 | */ |
91 | public function getLastError() |
92 | { |
93 | return $this->lastError; |
94 | } |
95 | |
96 | /** |
97 | * Set the last retrieved error. -1 means |
98 | * no error. |
99 | * |
100 | * @param integer $lastError |
101 | */ |
102 | public function setLastError($lastError) |
103 | { |
104 | $this->lastError = $lastError; |
105 | } |
106 | |
107 | /** |
108 | * Get the URI (Uniform Resource Identifier) of the user the Test Session belongs to. |
109 | * |
110 | * @return string |
111 | */ |
112 | public function getUserUri() |
113 | { |
114 | return $this->userUri; |
115 | } |
116 | |
117 | /** |
118 | * Set the URI (Uniform Resource Identifier) of the user the Test Session belongs to. |
119 | * |
120 | * @param string $userUri |
121 | */ |
122 | public function setUserUri($userUri) |
123 | { |
124 | $this->userUri = $userUri; |
125 | } |
126 | |
127 | /** |
128 | * @param AssessmentTest $test |
129 | * @param string $sessionId |
130 | * @param bool $forReadingOnly |
131 | * @return AssessmentTestSession |
132 | * @throws StorageException |
133 | */ |
134 | public function retrieve(AssessmentTest $test, $sessionId, $forReadingOnly = false) |
135 | { |
136 | if ($forReadingOnly === false) { |
137 | return $this->retrieveSessionInWriteMode($test, $sessionId); |
138 | } else { |
139 | return $this->retrieveSessionInReadMode($test, $sessionId); |
140 | } |
141 | } |
142 | |
143 | /** |
144 | * @param AssessmentTest $test |
145 | * @param string $sessionId |
146 | * @return taoQtiTest_helpers_TestSession |
147 | * @throws StorageException |
148 | */ |
149 | private function retrieveSessionInReadMode(AssessmentTest $test, string $sessionId): taoQtiTest_helpers_TestSession |
150 | { |
151 | if (!$this->sessionExists($sessionId)) { |
152 | $this->setLastError(-1); |
153 | self::$session = parent::retrieve($test, $sessionId); |
154 | self::$session->setReadOnly(true); |
155 | } |
156 | |
157 | return self::$session; |
158 | } |
159 | |
160 | /** |
161 | * @param AssessmentTest $test |
162 | * @param string $sessionId |
163 | * @return taoQtiTest_helpers_TestSession |
164 | * @throws StorageException |
165 | */ |
166 | private function retrieveSessionInWriteMode(AssessmentTest $test, string $sessionId): taoQtiTest_helpers_TestSession |
167 | { |
168 | if ($this->sessionExists($sessionId) && self::$session->isLocked()) { |
169 | return self::$session; |
170 | } |
171 | |
172 | $this->setLastError(-1); |
173 | self::$session = parent::retrieve($test, $sessionId); |
174 | $this->lockSession(self::$session); |
175 | |
176 | return self::$session; |
177 | } |
178 | |
179 | /** |
180 | * @param AssessmentTest $test |
181 | * @param string $sessionId |
182 | * @return AssessmentTestSession |
183 | * @throws StorageException |
184 | */ |
185 | public function instantiate(AssessmentTest $test, $sessionId = '') |
186 | { |
187 | $session = parent::instantiate($test, $sessionId); |
188 | $this->lockSession($session); |
189 | return $session; |
190 | } |
191 | |
192 | /** |
193 | * @param AssessmentTestSession $assessmentTestSession |
194 | * @throws StorageException |
195 | */ |
196 | public function persist(AssessmentTestSession $assessmentTestSession) |
197 | { |
198 | if ($assessmentTestSession->isReadOnly()) { |
199 | throw new StorageException( |
200 | 'Readonly test session cannot be stored. Test session id: ' . $assessmentTestSession->getSessionId(), |
201 | StorageException::PERSITANCE |
202 | ); |
203 | } |
204 | parent::persist($assessmentTestSession); |
205 | } |
206 | |
207 | /** |
208 | * @param AssessmentTestSession $session |
209 | */ |
210 | private function lockSession(AssessmentTestSession $session) |
211 | { |
212 | if ($session->isLocked()) { |
213 | return; |
214 | } |
215 | |
216 | $lock = $this->createLock('AssessmentTestSession_' . $session->getSessionId(), 30); |
217 | $lock->acquire(true); |
218 | $session->setReadOnly(false); |
219 | $session->setLock($lock); |
220 | } |
221 | |
222 | protected function getRetrievalStream($sessionId) |
223 | { |
224 | |
225 | $storageService = $this->getServiceLocator()->get(tao_models_classes_service_StateStorage::SERVICE_ID); |
226 | $userUri = $this->getUserUri(); |
227 | |
228 | if (is_null($userUri) === true) { |
229 | $msg = "Could not retrieve current user URI."; |
230 | throw new StorageException($msg, StorageException::RETRIEVAL); |
231 | } |
232 | |
233 | $data = $storageService->get($userUri, $sessionId); |
234 | |
235 | $stateEmpty = (empty($data) === true); |
236 | $stream = new MemoryStream(($stateEmpty === true) ? '' : $data); |
237 | $stream->open(); |
238 | |
239 | if ($stateEmpty === false) { |
240 | // Consume additional error (short signed integer). |
241 | $this->setLastError($stream->read(2)); |
242 | } |
243 | |
244 | $stream->close(); |
245 | return $stream; |
246 | } |
247 | |
248 | protected function persistStream(AssessmentTestSession $assessmentTestSession, MemoryStream $stream) |
249 | { |
250 | /** @var tao_models_classes_service_StateStorage $storageService */ |
251 | $storageService = $this->getServiceLocator()->get(tao_models_classes_service_StateStorage::SERVICE_ID); |
252 | ; |
253 | $userUri = $this->getUserUri(); |
254 | |
255 | if (is_null($userUri) === true) { |
256 | $msg = "Could not retrieve current user URI."; |
257 | throw new StorageException($msg, StorageException::RETRIEVAL); |
258 | } |
259 | |
260 | $data = $this->getLastError() . $stream->getBinary(); |
261 | if (!$storageService->set($userUri, $assessmentTestSession->getSessionId(), $data)) { |
262 | throw new StorageException('Can\'t write into storage at ' . static::class); |
263 | } |
264 | } |
265 | |
266 | public function exists($sessionId) |
267 | { |
268 | $storageService = $this->getServiceLocator()->get(tao_models_classes_service_StateStorage::SERVICE_ID); |
269 | $userUri = $this->getUserUri(); |
270 | |
271 | if (is_null($userUri) === true) { |
272 | $msg = "Could not retrieve current user URI."; |
273 | throw new StorageException($msg, StorageException::RETRIEVAL); |
274 | } |
275 | |
276 | return $storageService->has($userUri, $sessionId); |
277 | } |
278 | |
279 | /** |
280 | * @param string $sessionId |
281 | * @return bool |
282 | */ |
283 | public function delete($sessionId) |
284 | { |
285 | /** @var StateStorage $storageService */ |
286 | $storageService = ServiceManager::getServiceManager()->get(StateStorage::SERVICE_ID); |
287 | |
288 | return $storageService->del($this->getUserUri(), $sessionId); |
289 | } |
290 | |
291 | protected function createBinaryStreamAccess(IStream $stream) |
292 | { |
293 | return new QtiBinaryStreamAccess( |
294 | $stream, |
295 | $this->getServiceLocator()->get(QtiFlysystemFileManager::SERVICE_ID) |
296 | ); |
297 | } |
298 | |
299 | public function getServiceLocator() |
300 | { |
301 | if ($this->serviceLocator === null) { |
302 | return ServiceManager::getServiceManager(); |
303 | } |
304 | return $this->serviceLocator; |
305 | } |
306 | |
307 | /** |
308 | * @param string $sessionId |
309 | * @return bool |
310 | */ |
311 | private function sessionExists(string $sessionId): bool |
312 | { |
313 | return self::$session && self::$session->getSessionId() === $sessionId; |
314 | } |
315 | |
316 | /** |
317 | * @param $forReadingOnly |
318 | * @return bool |
319 | */ |
320 | private function assessModeChangedToWrite($forReadingOnly): bool |
321 | { |
322 | return !$forReadingOnly && self::$session->isReadOnly(); |
323 | } |
324 | } |