Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 50 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
QtiTestListenerService | |
0.00% |
0 / 50 |
|
0.00% |
0 / 7 |
306 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
sessionStateChanged | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
executionStateChanged | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
logStateEvent | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
archiveState | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
42 | |||
dispatchOffload | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getQueueDispatcher | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
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) 2017-2022 (original work) Open Assessment Technologies SA; |
19 | */ |
20 | |
21 | namespace oat\taoQtiTest\models; |
22 | |
23 | use oat\oatbox\service\ConfigurableService; |
24 | use oat\tao\model\taskQueue\QueueDispatcherInterface; |
25 | use oat\taoDelivery\model\execution\DeliveryExecution; |
26 | use oat\taoDelivery\models\classes\execution\event\DeliveryExecutionState; |
27 | use oat\taoQtiTest\models\classes\tasks\QtiStateOffload\AbstractQtiStateManipulationTask; |
28 | use oat\taoQtiTest\models\classes\tasks\QtiStateOffload\StateOffloadTask; |
29 | use oat\taoQtiTest\models\event\AfterAssessmentTestSessionClosedEvent; |
30 | use oat\taoQtiTest\models\event\QtiTestStateChangeEvent; |
31 | use oat\taoQtiTest\models\runner\communicator\TestStateChannel; |
32 | use oat\taoQtiTest\models\runner\ExtendedState; |
33 | use oat\taoQtiTest\models\runner\QtiRunnerMessageService; |
34 | use oat\taoQtiTest\models\runner\time\QtiTimeStorage; |
35 | use qtism\runtime\tests\AssessmentTestSession; |
36 | |
37 | /** |
38 | * Class QtiTestListenerService |
39 | * @package oat\taoQtiTest\models |
40 | */ |
41 | class QtiTestListenerService extends ConfigurableService |
42 | { |
43 | public const SERVICE_ID = 'taoQtiTest/QtiTestListenerService'; |
44 | |
45 | public const OPTION_ARCHIVE_ENABLED = 'archive-enabled'; |
46 | |
47 | public const OPTION_ARCHIVE_EXCLUDE = 'archive-exclude'; |
48 | |
49 | /** |
50 | * @var string Constant to turn off test state archiving. |
51 | */ |
52 | public const ARCHIVE_EXCLUDE_TEST = 'archive-exclude-test'; |
53 | |
54 | /** |
55 | * @var string Constant to turn off item state archiving. |
56 | */ |
57 | public const ARCHIVE_EXCLUDE_ITEMS = 'archive-exclude-items'; |
58 | |
59 | /** |
60 | * @var string Constant to turn off extended test state archiving. |
61 | */ |
62 | public const ARCHIVE_EXCLUDE_EXTRA = 'archive-exclude-extra'; |
63 | |
64 | public function __construct($options = []) |
65 | { |
66 | parent::__construct($options); |
67 | |
68 | $archiveExcludeOption = $this->getOption(self::OPTION_ARCHIVE_EXCLUDE); |
69 | |
70 | if (is_null($archiveExcludeOption) || !is_array($archiveExcludeOption)) { |
71 | $this->setOption(self::OPTION_ARCHIVE_EXCLUDE, []); |
72 | } |
73 | } |
74 | |
75 | /** |
76 | * |
77 | * @param QtiTestStateChangeEvent $event |
78 | */ |
79 | public function sessionStateChanged(QtiTestStateChangeEvent $event) |
80 | { |
81 | $sessionMemento = $event->getSessionMemento(); |
82 | $this->logStateEvent($sessionMemento->getSession()); |
83 | } |
84 | |
85 | /** |
86 | * |
87 | * @param DeliveryExecutionState $event |
88 | */ |
89 | public function executionStateChanged(DeliveryExecutionState $event) |
90 | { |
91 | if ($event->getPreviousState() == DeliveryExecution::STATE_ACTIVE) { |
92 | $testSessionService = $this->getServiceManager()->get(TestSessionService::SERVICE_ID); |
93 | $session = $testSessionService->getTestSession($event->getDeliveryExecution()); |
94 | if ($session) { |
95 | $this->logStateEvent($session); |
96 | } |
97 | } |
98 | } |
99 | |
100 | /** |
101 | * Logs the event with its related message to be dispatched to the client through the communication channel. |
102 | * @param AssessmentTestSession $session |
103 | */ |
104 | protected function logStateEvent(AssessmentTestSession $session) |
105 | { |
106 | $route = $session->getRoute(); |
107 | if ($route->valid()) { |
108 | $messageService = $this->getServiceManager()->get(QtiRunnerMessageService::SERVICE_ID); |
109 | $stateService = $this->getServiceManager()->get(ExtendedStateService::SERVICE_ID); |
110 | |
111 | $data = [ |
112 | 'state' => $session->getState(), |
113 | 'message' => $messageService->getStateMessage($session), |
114 | ]; |
115 | |
116 | $stateService->addEvent($session->getSessionId(), TestStateChannel::CHANNEL_NAME, $data); |
117 | } |
118 | } |
119 | |
120 | /** |
121 | * Archive Test States |
122 | * |
123 | * This method archives the Test States (Test + Items) related to the DeliveryExecution throwing $event. |
124 | * Please note that it is only relevant for the new QTI Test Runner. |
125 | * |
126 | * @param AfterAssessmentTestSessionClosedEvent $event |
127 | */ |
128 | public function archiveState(AfterAssessmentTestSessionClosedEvent $event) |
129 | { |
130 | if (!$this->getOption(self::OPTION_ARCHIVE_ENABLED)) { |
131 | \common_Logger::d('Item State archive is not enabled'); |
132 | return; |
133 | } |
134 | |
135 | $archivingExclusions = $this->getOption(self::OPTION_ARCHIVE_EXCLUDE); |
136 | |
137 | // Retrieve the Test Session Id (Item State Identifiers are based on it). |
138 | $sessionId = $event->getSession()->getSessionId(); |
139 | $userId = $event->getUserId(); |
140 | |
141 | if (!in_array(self::ARCHIVE_EXCLUDE_ITEMS, $archivingExclusions)) { |
142 | $itemRefs = $event->getSession()->getRoute()->getAssessmentItemRefs(); |
143 | foreach ($itemRefs as $itemRef) { |
144 | $this->dispatchOffload($userId, $sessionId . $itemRef->getIdentifier(), 'Item'); |
145 | } |
146 | } |
147 | |
148 | if (!in_array(self::ARCHIVE_EXCLUDE_TEST, $archivingExclusions)) { |
149 | $this->dispatchOffload($userId, $sessionId, 'Test'); |
150 | $this->dispatchOffload( |
151 | $userId, |
152 | QtiTimeStorage::getStorageKeyFromTestSessionId($sessionId), |
153 | 'Test Timeline' |
154 | ); |
155 | } |
156 | |
157 | if (!in_array(self::ARCHIVE_EXCLUDE_EXTRA, $archivingExclusions)) { |
158 | $this->dispatchOffload( |
159 | $userId, |
160 | ExtendedState::getStorageKeyFromTestSessionId($sessionId), |
161 | 'Extended State' |
162 | ); |
163 | } |
164 | } |
165 | |
166 | private function dispatchOffload(string $userId, string $callId, string $stateLabel): void |
167 | { |
168 | $this->getQueueDispatcher()->createTask(new StateOffloadTask(), [ |
169 | AbstractQtiStateManipulationTask::PARAM_USER_ID_KEY => $userId, |
170 | AbstractQtiStateManipulationTask::PARAM_CALL_ID_KEY => $callId, |
171 | AbstractQtiStateManipulationTask::PARAM_STATE_LABEL_KEY => $stateLabel |
172 | ]); |
173 | } |
174 | |
175 | private function getQueueDispatcher(): QueueDispatcherInterface |
176 | { |
177 | return $this->getServiceManager()->getContainer()->get(QueueDispatcherInterface::SERVICE_ID); |
178 | } |
179 | } |