ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
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 ilLPStatusWrapper::_updateStatus($this->object->getId(), $cmixUser->getUsrId());
73 }
74 }
75
76 public function getCmixUser($xapiStatement)
77 {
78 $cmixUser = null;
79 if ($this->object->getContentType() == ilObjCmiXapi::CONT_TYPE_CMI5)
80 {
82 $this->object->getId(),
83 $xapiStatement->actor->account->name
84 );
85 }
86 else
87 {
89 $this->object->getId(),
90 str_replace('mailto:', '', $xapiStatement->actor->mbox)
91 );
92 }
93 return $cmixUser;
94 }
95
96 public function evaluateStatement($xapiStatement, $usrId)
97 {
98 global $DIC;
99 $xapiVerb = $this->getXapiVerb($xapiStatement);
100
101 if ($this->isValidXapiStatement($xapiStatement))
102 {
103 // result status and if exists scaled score
104 if ($this->hasResultStatusRelevantXapiVerb($xapiVerb))
105 {
106 if (!$this->isValidObject($xapiStatement))
107 {
108 return;
109 }
110 $userResult = $this->getUserResult($usrId);
111
112 $oldResultStatus = $userResult->getStatus();
113 $newResultStatus = $this->getResultStatusForXapiVerb($xapiVerb);
114
115 // this is for both xapi and cmi5
116 if ($this->isResultStatusToBeReplaced($oldResultStatus, $newResultStatus)) {
117 $this->log->debug("isResultStatusToBeReplaced: true");
118 $userResult->setStatus($newResultStatus);
119 }
120
121 if ($this->hasXapiScore($xapiStatement)) {
122 $xapiScore = $this->getXapiScore($xapiStatement);
123 $this->log->debug("Score: " . $xapiScore);
124 $userResult->setScore((float) $xapiScore);
125 }
126 $userResult->save();
127
128 // only cmi5
129 if ($this->object->getContentType() == ilObjCmiXapi::CONT_TYPE_CMI5)
130 {
131 if (($xapiVerb == ilCmiXapiVerbList::COMPLETED || $xapiVerb == ilCmiXapiVerbList::PASSED) && $this->isLpModeInterestedInResultStatus($newResultStatus,false))
132 {
133 // it is possible to check against authToken usrId!
134 $cmixUser = $this->getCmixUser($xapiStatement);
135 $cmixUser->setSatisfied(true);
136 $cmixUser->save();
137 $this->sendSatisfiedStatement($cmixUser);
138 }
139 }
140 }
141 // result progress (i think only cmi5 relevant)
142 if ($this->hasResultProgressRelevantXapiVerb($xapiVerb))
143 {
144 $userResult = $this->getUserResult($usrId);
145 $progressedScore = $this->getProgressedScore($xapiStatement);
146 if ($progressedScore !== false && (float) $progressedScore > 0)
147 {
148 $userResult->setScore((float) ($progressedScore / 100));
149 $userResult->save();
150 }
151 }
152 }
153 }
154
155 protected function isValidXapiStatement($xapiStatement)
156 {
157 if (!isset($xapiStatement->actor)) {
158 return false;
159 }
160
161 if (!isset($xapiStatement->verb) || !isset($xapiStatement->verb->id)) {
162 return false;
163 }
164
165 if (!isset($xapiStatement->object) || !isset($xapiStatement->object->id)) {
166 return false;
167 }
168
169 return true;
170 }
171
175 protected function isValidObject($xapiStatement)
176 {
177 if ($xapiStatement->object->id != $this->object->getActivityId())
178 {
179 $this->log->debug($xapiStatement->object->id . " != " . $this->object->getActivityId());
180 return false;
181 }
182 return true;
183 }
184
185
186 protected function getXapiVerb($xapiStatement)
187 {
188 return $xapiStatement->verb->id;
189 }
190
191 protected function getResultStatusForXapiVerb($xapiVerb)
192 {
193 return $this->resultStatusByXapiVerbMap[$xapiVerb];
194 }
195
196 protected function hasResultStatusRelevantXapiVerb($xapiVerb)
197 {
198 return isset($this->resultStatusByXapiVerbMap[$xapiVerb]);
199 }
200
201 protected function getResultProgressForXapiVerb($xapiVerb)
202 {
203 return $this->resultProgressByXapiVerbMap[$xapiVerb];
204 }
205
206 protected function hasResultProgressRelevantXapiVerb($xapiVerb)
207 {
208 return isset($this->resultProgressByXapiVerbMap[$xapiVerb]);
209 }
210
211 protected function hasXapiScore($xapiStatement)
212 {
213 if (!isset($xapiStatement->result)) {
214 return false;
215 }
216
217 if (!isset($xapiStatement->result->score)) {
218 return false;
219 }
220
221 if (!isset($xapiStatement->result->score->scaled)) {
222 return false;
223 }
224
225 return true;
226 }
227
228 protected function getXapiScore($xapiStatement)
229 {
230 return $xapiStatement->result->score->scaled;
231 }
232
233 protected function getProgressedScore($xapiStatement)
234 {
235 if (!isset($xapiStatement->result)) {
236 return false;
237 }
238
239 if (!isset($xapiStatement->result->extensions)) {
240 return false;
241 }
242
243 if (!isset($xapiStatement->result->extensions->{'https://w3id.org/xapi/cmi5/result/extensions/progress'})) {
244 return false;
245 }
246 return $xapiStatement->result->extensions->{'https://w3id.org/xapi/cmi5/result/extensions/progress'};
247 }
248
249 protected function getUserResult($usrId)
250 {
251 try {
252 $result = ilCmiXapiResult::getInstanceByObjIdAndUsrId($this->object->getId(), $usrId);
253 } catch (ilCmiXapiException $e) {
255 $result->setObjId($this->object->getId());
256 $result->setUsrId($usrId);
257 }
258
259 return $result;
260 }
261
262 protected function isResultStatusToBeReplaced($oldResultStatus, $newResultStatus)
263 {
264 if (!$this->isLpModeInterestedInResultStatus($newResultStatus)) {
265 $this->log->debug("isLpModeInterestedInResultStatus: false");
266 return false;
267 }
268
269 if (!$this->doesNewResultStatusDominateOldOne($oldResultStatus, $newResultStatus)) {
270 $this->log->debug("doesNewResultStatusDominateOldOne: false");
271 return false;
272 }
273
274 if ($this->needsAvoidFailedEvaluation($oldResultStatus, $newResultStatus)) {
275 $this->log->debug("needsAvoidFailedEvaluation: false");
276 return false;
277 }
278
279 return true;
280 }
281
282 protected function isLpModeInterestedInResultStatus($resultStatus, $deactivated=true)
283 {
284 if ($this->lpMode == ilLPObjSettings::LP_MODE_DEACTIVATED) {
285 return $deactivated;
286 }
287
288 switch ($resultStatus) {
289 case 'failed':
290
291 return in_array($this->lpMode, [
295 ]);
296
297 case 'passed':
298
299 return in_array($this->lpMode, [
304 ]);
305
306 case 'completed':
307
308 return in_array($this->lpMode, [
313 ]);
314 }
315
316 return false;
317 }
318
319 protected function doesNewResultStatusDominateOldOne($oldResultStatus, $newResultStatus)
320 {
321 if ($oldResultStatus == '' ) {
322 return true;
323 }
324
325 if (in_array($newResultStatus, ['passed', 'failed'])) {
326 return true;
327 }
328
329 if (!in_array($oldResultStatus, ['passed', 'failed'])) {
330 return true;
331 }
332
333 return false;
334 }
335
336 protected function needsAvoidFailedEvaluation($oldResultStatus, $newResultStatus)
337 {
338 if (!$this->object->isKeepLpStatusEnabled()) {
339 return false;
340 }
341
342 if ($newResultStatus != 'failed') {
343 return false;
344 }
345
346 return $oldResultStatus == 'completed' || $oldResultStatus == 'passed';
347 }
348
349 protected function sendSatisfiedStatement($cmixUser)
350 {
351 global $DIC;
352
353 $lrsType = $this->object->getLrsType();
354 $defaultLrs = $lrsType->getLrsEndpoint();
355 //$fallbackLrs = $lrsType->getLrsFallbackEndpoint();
356 $defaultBasicAuth = $lrsType->getBasicAuth();
357 //$fallbackBasicAuth = $lrsType->getFallbackBasicAuth();
358 $defaultHeaders = [
359 'X-Experience-API-Version' => '1.0.3',
360 'Authorization' => $defaultBasicAuth,
361 'Content-Type' => 'application/json;charset=utf-8',
362 'Cache-Control' => 'no-cache, no-store, must-revalidate'
363 ];
364 /*
365 $fallbackHeaders = [
366 'X-Experience-API-Version' => '1.0.3',
367 'Authorization' => $fallbackBasicAuth,
368 'Content-Type' => 'application/json;charset=utf-8',
369 'Cache-Control' => 'no-cache, no-store, must-revalidate'
370 ];
371 */
372 $satisfiedStatement = $this->object->getSatisfiedStatement($cmixUser);
373 $satisfiedStatementParams = [];
374 $satisfiedStatementParams['statementId'] = $satisfiedStatement['id'];
375 $defaultStatementsUrl = $defaultLrs . "/statements";
376 $defaultSatisfiedStatementUrl = $defaultStatementsUrl . '?' . ilCmiXapiAbstractRequest::buildQuery($satisfiedStatementParams);
377
378 $client = new GuzzleHttp\Client();
379 $req_opts = array(
380 GuzzleHttp\RequestOptions::VERIFY => true,
381 GuzzleHttp\RequestOptions::CONNECT_TIMEOUT => 10,
382 GuzzleHttp\RequestOptions::HTTP_ERRORS => false
383 );
384
385 $defaultSatisfiedStatementRequest = new GuzzleHttp\Psr7\Request(
386 'PUT',
387 $defaultSatisfiedStatementUrl,
388 $defaultHeaders,
389 json_encode($satisfiedStatement)
390 );
391 $promises = array();
392 $promises['defaultSatisfiedStatement'] = $client->sendAsync($defaultSatisfiedStatementRequest, $req_opts);
393 try
394 {
395 $responses = GuzzleHttp\Promise\Utils::settle($promises)->wait();
396 $body = '';
397 ilCmiXapiAbstractRequest::checkResponse($responses['defaultSatisfiedStatement'],$body,[204]);
398 }
399 catch(Exception $e)
400 {
401 $this->log->error('error:' . $e->getMessage());
402 }
403 }
404}
$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)
if($_SERVER['argc']< 4) $client
Definition: cron.php:12
$DIC
Definition: xapitoken.php:46