Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
63.86% |
53 / 83 |
|
36.36% |
4 / 11 |
CRAP | |
0.00% |
0 / 1 |
TestSessionService | |
63.86% |
53 / 83 |
|
36.36% |
4 / 11 |
54.51 | |
0.00% |
0 / 1 |
loadSession | |
79.55% |
35 / 44 |
|
0.00% |
0 / 1 |
4.14 | |||
hasTestSession | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getTestSession | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
registerTestSession | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
getTestSessionDataById | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getTestSessionStorage | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
getRuntimeInputParameters | |
75.00% |
6 / 8 |
|
0.00% |
0 / 1 |
2.06 | |||
persist | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
deleteDeliveryExecutionData | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
accessModeChangedToWrite | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
invalidateCache | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
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) 2016 (original work) Open Assessment Technologies SA; |
19 | * |
20 | */ |
21 | |
22 | namespace oat\taoQtiTest\models; |
23 | |
24 | use common_exception_NoContent; |
25 | use oat\generis\model\OntologyAwareTrait; |
26 | use oat\oatbox\service\ConfigurableService; |
27 | use oat\taoDelivery\model\AssignmentService; |
28 | use oat\taoDelivery\model\execution\DeliveryExecution; |
29 | use oat\taoDelivery\model\execution\DeliveryExecutionInterface; |
30 | use oat\taoDelivery\model\execution\DeliveryServerService; |
31 | use oat\taoDelivery\model\execution\Delete\DeliveryExecutionDelete; |
32 | use oat\taoDelivery\model\execution\Delete\DeliveryExecutionDeleteRequest; |
33 | use oat\taoDelivery\model\RuntimeService; |
34 | use oat\taoQtiTest\models\runner\session\UserUriAware; |
35 | use qtism\data\AssessmentTest; |
36 | use qtism\runtime\storage\binary\AbstractQtiBinaryStorage; |
37 | use qtism\runtime\storage\binary\BinaryAssessmentTestSeeker; |
38 | use qtism\runtime\tests\AssessmentTestSession; |
39 | use tao_models_classes_service_ServiceCallHelper; |
40 | use taoQtiTest_helpers_TestSessionStorage; |
41 | use Throwable; |
42 | |
43 | /** |
44 | * Interface TestSessionService |
45 | * @author Aleh Hutnikau <hutnikau@1pt.com> |
46 | */ |
47 | class TestSessionService extends ConfigurableService implements DeliveryExecutionDelete |
48 | { |
49 | use OntologyAwareTrait; |
50 | |
51 | public const SERVICE_ID = 'taoQtiTest/TestSessionService'; |
52 | |
53 | public const SESSION_PROPERTY_SESSION = 'session'; |
54 | public const SESSION_PROPERTY_STORAGE = 'storage'; |
55 | public const SESSION_PROPERTY_COMPILATION = 'compilation'; |
56 | |
57 | /** |
58 | * Cache to store session instances |
59 | * @var array |
60 | */ |
61 | protected static $cache = []; |
62 | |
63 | /** |
64 | * Loads a test session into the memory cache |
65 | * @param DeliveryExecution $deliveryExecution |
66 | * @param bool $forReadingOnly |
67 | * @throws QtiTestExtractionFailedException |
68 | * @throws \common_Exception |
69 | * @throws \common_exception_Error |
70 | * @throws \common_exception_NotFound |
71 | * @throws \common_ext_ExtensionException |
72 | * @throws \oat\oatbox\service\exception\InvalidServiceManagerException |
73 | */ |
74 | protected function loadSession(DeliveryExecution $deliveryExecution, $forReadingOnly) |
75 | { |
76 | self::invalidateCache(); |
77 | |
78 | $session = null; |
79 | $sessionId = $deliveryExecution->getIdentifier(); |
80 | try { |
81 | /** @var array $inputParameters */ |
82 | $inputParameters = $this->getRuntimeInputParameters($deliveryExecution); |
83 | /** @var AssessmentTest $testDefinition */ |
84 | $testDefinition = $this->getServiceLocator()->get(QtiTestUtils::SERVICE_ID) |
85 | ->getTestDefinition($inputParameters['QtiTestCompilation']); |
86 | $testResource = new \core_kernel_classes_Resource($inputParameters['QtiTestDefinition']); |
87 | } catch (common_exception_NoContent $e) { |
88 | $sessionData = [ |
89 | self::SESSION_PROPERTY_SESSION => null, |
90 | self::SESSION_PROPERTY_STORAGE => null, |
91 | self::SESSION_PROPERTY_COMPILATION => null |
92 | ]; |
93 | self::$cache[$sessionId] = $sessionData; |
94 | return; |
95 | } |
96 | |
97 | /** @var DeliveryServerService $deliveryServerService */ |
98 | $deliveryServerService = $this->getServiceLocator()->get(DeliveryServerService::SERVICE_ID); |
99 | $resultStore = $deliveryServerService->getResultStoreWrapper($deliveryExecution); |
100 | |
101 | $sessionManager = new \taoQtiTest_helpers_SessionManager($resultStore, $testResource); |
102 | |
103 | $userId = $deliveryExecution->getUserIdentifier(); |
104 | |
105 | $config = $this->getServiceLocator()->get(\common_ext_ExtensionsManager::SERVICE_ID) |
106 | ->getExtensionById('taoQtiTest') |
107 | ->getConfig('testRunner'); |
108 | |
109 | $storageClassName = $config['test-session-storage']; |
110 | /** @var taoQtiTest_helpers_TestSessionStorage $qtiStorage */ |
111 | $qtiStorage = new $storageClassName( |
112 | $sessionManager, |
113 | new BinaryAssessmentTestSeeker($testDefinition), |
114 | $userId |
115 | ); |
116 | $this->propagate($qtiStorage); |
117 | |
118 | if ($qtiStorage->exists($sessionId)) { |
119 | $session = $qtiStorage->retrieve($testDefinition, $sessionId, $forReadingOnly); |
120 | if ($session instanceof UserUriAware) { |
121 | $session->setUserUri($userId); |
122 | } |
123 | } |
124 | |
125 | /** @var \tao_models_classes_service_FileStorage $fileStorage */ |
126 | $fileStorage = $this->getServiceLocator()->get(\tao_models_classes_service_FileStorage::SERVICE_ID); |
127 | $directoryIds = explode('|', $inputParameters['QtiTestCompilation']); |
128 | $directories = [ |
129 | 'private' => $fileStorage->getDirectoryById($directoryIds[0]), |
130 | 'public' => $fileStorage->getDirectoryById($directoryIds[1]) |
131 | ]; |
132 | |
133 | self::$cache[$sessionId] = [ |
134 | self::SESSION_PROPERTY_SESSION => $session, |
135 | self::SESSION_PROPERTY_STORAGE => $qtiStorage, |
136 | self::SESSION_PROPERTY_COMPILATION => $directories |
137 | ]; |
138 | } |
139 | |
140 | /** |
141 | * Checks if a session has been loaded |
142 | * @param $sessionId |
143 | * @return bool |
144 | */ |
145 | protected function hasTestSession($sessionId) |
146 | { |
147 | return (isset(self::$cache[$sessionId]) && isset(self::$cache[$sessionId][self::SESSION_PROPERTY_SESSION])); |
148 | } |
149 | |
150 | /** |
151 | * Gets the test session for a particular deliveryExecution |
152 | * |
153 | * @param DeliveryExecution $deliveryExecution |
154 | * @param bool $forReadingOnly |
155 | * @return \qtism\runtime\tests\AssessmentTestSession |
156 | * @throws QtiTestExtractionFailedException |
157 | * @throws \common_Exception |
158 | * @throws \common_exception_Error |
159 | * @throws \common_exception_NotFound |
160 | * @throws \common_ext_ExtensionException |
161 | * @throws \oat\oatbox\service\exception\InvalidServiceManagerException |
162 | */ |
163 | public function getTestSession(DeliveryExecution $deliveryExecution, $forReadingOnly = false) |
164 | { |
165 | $sessionId = $deliveryExecution->getIdentifier(); |
166 | if (!$this->hasTestSession($sessionId) || $this->accessModeChangedToWrite($forReadingOnly, $sessionId)) { |
167 | $this->loadSession($deliveryExecution, $forReadingOnly); |
168 | } |
169 | |
170 | return self::$cache[$sessionId][self::SESSION_PROPERTY_SESSION]; |
171 | } |
172 | |
173 | /** |
174 | * Register a test session |
175 | * |
176 | * @param AssessmentTestSession $session |
177 | * @param \taoQtiTest_helpers_TestSessionStorage $storage |
178 | * @param array $compilationDirectories |
179 | */ |
180 | public function registerTestSession( |
181 | AssessmentTestSession $session, |
182 | \taoQtiTest_helpers_TestSessionStorage $storage, |
183 | array $compilationDirectories |
184 | ) { |
185 | $sessionId = $session->getSessionId(); |
186 | self::$cache[$sessionId] = [ |
187 | self::SESSION_PROPERTY_SESSION => $session, |
188 | self::SESSION_PROPERTY_STORAGE => $storage, |
189 | self::SESSION_PROPERTY_COMPILATION => $compilationDirectories |
190 | ]; |
191 | } |
192 | |
193 | /** |
194 | * Get a test session data by identifier. |
195 | * |
196 | * Get a session by $sessionId. In case it was previously registered using the |
197 | * TestSessionService::registerTestSession method, an array with the following keys will be returned: |
198 | * |
199 | * * 'session': A qtism AssessmentTestSession object. |
200 | * * 'storage': A taoQtiTest_helpers_TestSessionStorage. |
201 | * * 'context': A RunnerServiceContext object |
202 | * (if not provided at TestSessionService::registerTestSession call time, it contains null). |
203 | * |
204 | * In case of no such session is found for $sessionId, false is returned. |
205 | * |
206 | * @param string $sessionId |
207 | * @return false|array |
208 | */ |
209 | public function getTestSessionDataById($sessionId) |
210 | { |
211 | return $this->hasTestSession($sessionId) ? self::$cache[$sessionId] : false; |
212 | } |
213 | |
214 | /** |
215 | * Gets the test session storage for a particular deliveryExecution |
216 | * |
217 | * @param DeliveryExecutionInterface $deliveryExecution |
218 | * @return taoQtiTest_helpers_TestSessionStorage|null |
219 | * @throws QtiTestExtractionFailedException |
220 | * @throws \common_Exception |
221 | * @throws \common_exception_NotFound |
222 | * @throws \common_ext_ExtensionException |
223 | * @throws \oat\oatbox\service\exception\InvalidServiceManagerException |
224 | */ |
225 | public function getTestSessionStorage(DeliveryExecutionInterface $deliveryExecution) |
226 | { |
227 | $sessionId = $deliveryExecution->getIdentifier(); |
228 | if (!$this->hasTestSession($sessionId)) { |
229 | $this->loadSession($deliveryExecution, true); |
230 | } |
231 | |
232 | return self::$cache[$sessionId][self::SESSION_PROPERTY_STORAGE]; |
233 | } |
234 | |
235 | /** |
236 | * |
237 | * @param DeliveryExecution $deliveryExecution |
238 | * @return array |
239 | * Example: |
240 | * <pre> |
241 | * array( |
242 | * 'QtiTestCompilation' => 'http://sample/first.rdf#i14369768868163155-' |
243 | * . '|http://sample/first.rdf#i1436976886612156+', |
244 | * 'QtiTestDefinition' => 'http://sample/first.rdf#i14369752345581135' |
245 | * ) |
246 | * </pre> |
247 | * @throws common_exception_NoContent |
248 | */ |
249 | public function getRuntimeInputParameters(DeliveryExecution $deliveryExecution) |
250 | { |
251 | try { |
252 | $compiledDelivery = $deliveryExecution->getDelivery(); |
253 | $runtime = $this |
254 | ->getServiceLocator() |
255 | ->get(RuntimeService::SERVICE_ID) |
256 | ->getRuntime($compiledDelivery->getUri()); |
257 | return tao_models_classes_service_ServiceCallHelper::getInputValues($runtime, []); |
258 | } catch (Throwable $exception) { |
259 | throw new common_exception_NoContent($exception->getMessage()); |
260 | } |
261 | } |
262 | |
263 | /** |
264 | * @param AssessmentTestSession $session |
265 | * @throws \qtism\runtime\storage\common\StorageException |
266 | */ |
267 | public function persist(AssessmentTestSession $session) |
268 | { |
269 | $sessionId = $session->getSessionId(); |
270 | if ($this->hasTestSession($sessionId)) { |
271 | /** @var AbstractQtiBinaryStorage $storage */ |
272 | $storage = self::$cache[$sessionId][self::SESSION_PROPERTY_STORAGE]; |
273 | $storage->persist($session); |
274 | } |
275 | } |
276 | |
277 | /** |
278 | * @inheritdoc |
279 | */ |
280 | public function deleteDeliveryExecutionData(DeliveryExecutionDeleteRequest $request) |
281 | { |
282 | $sessionId = $request->getDeliveryExecution()->getIdentifier(); |
283 | try { |
284 | $storage = $this->getTestSessionStorage($request->getDeliveryExecution(), false); |
285 | if ($storage instanceof taoQtiTest_helpers_TestSessionStorage) { |
286 | return $storage->delete($sessionId); |
287 | } |
288 | } catch (\Exception $exception) { |
289 | return false; |
290 | } |
291 | |
292 | return false; |
293 | } |
294 | |
295 | /** |
296 | * @param $forReadingOnly |
297 | * @param string $sessionId |
298 | * @return bool |
299 | */ |
300 | private function accessModeChangedToWrite($forReadingOnly, string $sessionId): bool |
301 | { |
302 | return $this->hasTestSession($sessionId) |
303 | && !$forReadingOnly |
304 | && self::$cache[$sessionId][self::SESSION_PROPERTY_SESSION]->isReadOnly(); |
305 | } |
306 | |
307 | /** |
308 | * Invalidate Cache. |
309 | * |
310 | * Invalidates the Test Session Cache. |
311 | */ |
312 | public function invalidateCache(): void |
313 | { |
314 | self::$cache = []; |
315 | } |
316 | } |