ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilXapiStatementEvaluation.php
Go to the documentation of this file.
1<?php
2
3/* Copyright (c) 1998-2019 ILIAS open source, Extended GPL, see docs/LICENSE */
4
5
16{
22 protected $resultStatusByXapiVerbMap = array(
23 ilCmiXapiVerbList::COMPLETED => "completed",
24 ilCmiXapiVerbList::PASSED => "passed",
25 ilCmiXapiVerbList::FAILED => "failed",
27 );
28
30 ilCmiXapiVerbList::PROGRESSED => "progressed",
31 ilCmiXapiVerbList::EXPERIENCED => "experienced"
32 );
33
37 protected $object;
38
42 protected $log;
43
50 {
51 $this->log = $log;
52 $this->object = $object;
53
54 $objLP = ilObjectLP::getInstance($this->object->getId());
55 $this->lpMode = $objLP->getCurrentMode();
56 }
57
59 {
60 foreach ($report->getStatements() as $xapiStatement) {
61 #$this->log->debug(
62 # "handle statement:\n".json_encode($xapiStatement, JSON_PRETTY_PRINT)
63 #);
64
65 // ensure json decoded non assoc
66 $xapiStatement = json_decode(json_encode($xapiStatement));
67 $cmixUser = $this->getCmixUser($xapiStatement);
68
69 $this->evaluateStatement($xapiStatement, $cmixUser->getUsrId());
70
71 $this->log->debug('update lp for object (' . $this->object->getId() . ')');
72 if ($cmixUser->getUsrId() != ANONYMOUS_USER_ID) {
73 ilLPStatusWrapper::_updateStatus($this->object->getId(), $cmixUser->getUsrId());
74 }
75 }
76 }
77
78 public function getCmixUser($xapiStatement)
79 {
80 $cmixUser = null;
81 if ($this->object->getContentType() == ilObjCmiXapi::CONT_TYPE_CMI5) {
83 $this->object->getId(),
84 $xapiStatement->actor->account->name
85 );
86 } else {
88 $this->object->getId(),
89 str_replace('mailto:', '', $xapiStatement->actor->mbox)
90 );
91 }
92 return $cmixUser;
93 }
94
95 public function evaluateStatement($xapiStatement, $usrId)
96 {
97 global $DIC;
98 $xapiVerb = $this->getXapiVerb($xapiStatement);
99
100 if ($this->isValidXapiStatement($xapiStatement)) {
101 // result status and if exists scaled score
102 if ($this->hasResultStatusRelevantXapiVerb($xapiVerb)) {
103 if (!$this->isValidObject($xapiStatement)) {
104 return;
105 }
106 $userResult = $this->getUserResult($usrId);
107
108 $oldResultStatus = $userResult->getStatus();
109 $newResultStatus = $this->getResultStatusForXapiVerb($xapiVerb);
110
111 // this is for both xapi and cmi5
112 if ($this->isResultStatusToBeReplaced($oldResultStatus, $newResultStatus)) {
113 $this->log->debug("isResultStatusToBeReplaced: true");
114 $userResult->setStatus($newResultStatus);
115 }
116
117 if ($this->hasXapiScore($xapiStatement)) {
118 $xapiScore = $this->getXapiScore($xapiStatement);
119 $this->log->debug("Score: " . $xapiScore);
120 $userResult->setScore((float) $xapiScore);
121 }
122 $userResult->save();
123
124 // only cmi5
125 if ($this->object->getContentType() == ilObjCmiXapi::CONT_TYPE_CMI5) {
126 if (($xapiVerb == ilCmiXapiVerbList::COMPLETED || $xapiVerb == ilCmiXapiVerbList::PASSED) && $this->isLpModeInterestedInResultStatus($newResultStatus, false)) {
127 // it is possible to check against authToken usrId!
128 $cmixUser = $this->getCmixUser($xapiStatement);
129 $cmixUser->setSatisfied(true);
130 $cmixUser->save();
131 $this->sendSatisfiedStatement($cmixUser);
132 }
133 }
134 }
135 // result progress (i think only cmi5 relevant)
136 if ($this->hasResultProgressRelevantXapiVerb($xapiVerb)) {
137 $userResult = $this->getUserResult($usrId);
138 $progressedScore = $this->getProgressedScore($xapiStatement);
139 if ($progressedScore !== false && (float) $progressedScore > 0) {
140 $userResult->setScore((float) ($progressedScore / 100));
141 $userResult->save();
142 }
143 }
144 }
145 }
146
147 protected function isValidXapiStatement($xapiStatement)
148 {
149 if (!isset($xapiStatement->actor)) {
150 return false;
151 }
152
153 if (!isset($xapiStatement->verb) || !isset($xapiStatement->verb->id)) {
154 return false;
155 }
156
157 if (!isset($xapiStatement->object) || !isset($xapiStatement->object->id)) {
158 return false;
159 }
160
161 return true;
162 }
163
167 protected function isValidObject($xapiStatement)
168 {
169 if ($xapiStatement->object->id != $this->object->getActivityId()) {
170 $this->log->debug($xapiStatement->object->id . " != " . $this->object->getActivityId());
171 return false;
172 }
173 return true;
174 }
175
176
177 protected function getXapiVerb($xapiStatement)
178 {
179 return $xapiStatement->verb->id;
180 }
181
182 protected function getResultStatusForXapiVerb($xapiVerb)
183 {
184 return $this->resultStatusByXapiVerbMap[$xapiVerb];
185 }
186
187 protected function hasResultStatusRelevantXapiVerb($xapiVerb)
188 {
189 return isset($this->resultStatusByXapiVerbMap[$xapiVerb]);
190 }
191
192 protected function getResultProgressForXapiVerb($xapiVerb)
193 {
194 return $this->resultProgressByXapiVerbMap[$xapiVerb];
195 }
196
197 protected function hasResultProgressRelevantXapiVerb($xapiVerb)
198 {
199 return isset($this->resultProgressByXapiVerbMap[$xapiVerb]);
200 }
201
202 protected function hasXapiScore($xapiStatement)
203 {
204 if (!isset($xapiStatement->result)) {
205 return false;
206 }
207
208 if (!isset($xapiStatement->result->score)) {
209 return false;
210 }
211
212 if (!isset($xapiStatement->result->score->scaled)) {
213 return false;
214 }
215
216 return true;
217 }
218
219 protected function getXapiScore($xapiStatement)
220 {
221 return $xapiStatement->result->score->scaled;
222 }
223
224 protected function getProgressedScore($xapiStatement)
225 {
226 if (!isset($xapiStatement->result)) {
227 return false;
228 }
229
230 if (!isset($xapiStatement->result->extensions)) {
231 return false;
232 }
233
234 if (!isset($xapiStatement->result->extensions->{'https://w3id.org/xapi/cmi5/result/extensions/progress'})) {
235 return false;
236 }
237 return $xapiStatement->result->extensions->{'https://w3id.org/xapi/cmi5/result/extensions/progress'};
238 }
239
240 protected function getUserResult($usrId)
241 {
242 try {
243 $result = ilCmiXapiResult::getInstanceByObjIdAndUsrId($this->object->getId(), $usrId);
244 } catch (ilCmiXapiException $e) {
246 $result->setObjId($this->object->getId());
247 $result->setUsrId($usrId);
248 }
249
250 return $result;
251 }
252
253 protected function isResultStatusToBeReplaced($oldResultStatus, $newResultStatus)
254 {
255 if (!$this->isLpModeInterestedInResultStatus($newResultStatus)) {
256 $this->log->debug("isLpModeInterestedInResultStatus: false");
257 return false;
258 }
259
260 if (!$this->doesNewResultStatusDominateOldOne($oldResultStatus, $newResultStatus)) {
261 $this->log->debug("doesNewResultStatusDominateOldOne: false");
262 return false;
263 }
264
265 if ($this->needsAvoidFailedEvaluation($oldResultStatus, $newResultStatus)) {
266 $this->log->debug("needsAvoidFailedEvaluation: false");
267 return false;
268 }
269
270 return true;
271 }
272
273 protected function isLpModeInterestedInResultStatus($resultStatus, $deactivated = true)
274 {
275 if ($this->lpMode == ilLPObjSettings::LP_MODE_DEACTIVATED) {
276 return $deactivated;
277 }
278
279 switch ($resultStatus) {
280 case 'failed':
281
282 return in_array($this->lpMode, [
286 ]);
287
288 case 'passed':
289
290 return in_array($this->lpMode, [
295 ]);
296
297 case 'completed':
298
299 return in_array($this->lpMode, [
304 ]);
305 }
306
307 return false;
308 }
309
310 protected function doesNewResultStatusDominateOldOne($oldResultStatus, $newResultStatus)
311 {
312 if ($oldResultStatus == '') {
313 return true;
314 }
315
316 if (in_array($newResultStatus, ['passed', 'failed'])) {
317 return true;
318 }
319
320 if (!in_array($oldResultStatus, ['passed', 'failed'])) {
321 return true;
322 }
323
324 return false;
325 }
326
327 protected function needsAvoidFailedEvaluation($oldResultStatus, $newResultStatus)
328 {
329 if (!$this->object->isKeepLpStatusEnabled()) {
330 return false;
331 }
332
333 if ($newResultStatus != 'failed') {
334 return false;
335 }
336
337 return $oldResultStatus == 'completed' || $oldResultStatus == 'passed';
338 }
339
340 protected function sendSatisfiedStatement($cmixUser)
341 {
342 global $DIC;
343
344 $lrsType = $this->object->getLrsType();
345 $defaultLrs = $lrsType->getLrsEndpoint();
346 //$fallbackLrs = $lrsType->getLrsFallbackEndpoint();
347 $defaultBasicAuth = $lrsType->getBasicAuth();
348 //$fallbackBasicAuth = $lrsType->getFallbackBasicAuth();
349 $defaultHeaders = [
350 'X-Experience-API-Version' => '1.0.3',
351 'Authorization' => $defaultBasicAuth,
352 'Content-Type' => 'application/json;charset=utf-8',
353 'Cache-Control' => 'no-cache, no-store, must-revalidate'
354 ];
355 /*
356 $fallbackHeaders = [
357 'X-Experience-API-Version' => '1.0.3',
358 'Authorization' => $fallbackBasicAuth,
359 'Content-Type' => 'application/json;charset=utf-8',
360 'Cache-Control' => 'no-cache, no-store, must-revalidate'
361 ];
362 */
363 $satisfiedStatement = $this->object->getSatisfiedStatement($cmixUser);
364 $satisfiedStatementParams = [];
365 $satisfiedStatementParams['statementId'] = $satisfiedStatement['id'];
366 $defaultStatementsUrl = $defaultLrs . "/statements";
367 $defaultSatisfiedStatementUrl = $defaultStatementsUrl . '?' . ilCmiXapiAbstractRequest::buildQuery($satisfiedStatementParams);
368
369 $client = new GuzzleHttp\Client();
370 $req_opts = array(
371 GuzzleHttp\RequestOptions::VERIFY => true,
372 GuzzleHttp\RequestOptions::CONNECT_TIMEOUT => 10,
373 GuzzleHttp\RequestOptions::HTTP_ERRORS => false
374 );
375
376 $defaultSatisfiedStatementRequest = new GuzzleHttp\Psr7\Request(
377 'PUT',
378 $defaultSatisfiedStatementUrl,
379 $defaultHeaders,
380 json_encode($satisfiedStatement)
381 );
382 $promises = array();
383 $promises['defaultSatisfiedStatement'] = $client->sendAsync($defaultSatisfiedStatementRequest, $req_opts);
384 try {
385 $responses = GuzzleHttp\Promise\Utils::settle($promises)->wait();
386 $body = '';
387 ilCmiXapiAbstractRequest::checkResponse($responses['defaultSatisfiedStatement'], $body, [204]);
388 } catch (Exception $e) {
389 $this->log->error('error:' . $e->getMessage());
390 }
391 }
392}
$result
An exception for terminatinating execution or to throw for unit testing.
static buildQuery(array $params, $encoding=PHP_QUERY_RFC3986)
static checkResponse($response, &$body, $allowedStatus=[200, 204])
static getInstanceByObjIdAndUsrId($objId, $usrId)
static getInstanceByObjectIdAndUsrIdent($objId, $usrIdent)
const LP_MODE_CMIX_COMPL_OR_PASSED_WITH_FAILED
static _updateStatus($a_obj_id, $a_usr_id, $a_obj=null, $a_percentage=false, $a_force_raise=false)
Update status.
Component logger with individual log levels by component id.
static getInstance($a_obj_id)
isResultStatusToBeReplaced($oldResultStatus, $newResultStatus)
__construct(ilLogger $log, ilObjCmiXapi $object)
ilXapiStatementEvaluation constructor.
isLpModeInterestedInResultStatus($resultStatus, $deactivated=true)
evaluateReport(ilCmiXapiStatementsReport $report)
needsAvoidFailedEvaluation($oldResultStatus, $newResultStatus)
doesNewResultStatusDominateOldOne($oldResultStatus, $newResultStatus)
const ANONYMOUS_USER_ID
Definition: constants.php:25
if($_SERVER['argc']< 4) $client
Definition: cron.php:12
global $DIC
Definition: goto.php:24