ILIAS  release_8 Revision v8.24
class.ilLTIConsumerGradeServiceScores.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
31{
33 {
35 $this->id = 'Score.collection';
36 $this->template = '/{context_id}/lineitems/{item_id}/lineitem/scores';
37 $this->variables[] = 'Scores.url';
38 $this->formats[] = 'application/vnd.ims.lis.v1.scorecontainer+json';
39 $this->formats[] = 'application/vnd.ims.lis.v1.score+json';
40 $this->methods[] = 'POST';
41 }
42
47 {
48 $params = $this->parseTemplate();
49 $contextId = $params['context_id'];
50 $itemId = $params['item_id'];
51
52 ilObjLTIConsumer::getLogger()->debug("contextId: " . $contextId);
53 ilObjLTIConsumer::getLogger()->debug("objId: " . $itemId);
54 ilObjLTIConsumer::getLogger()->debug("request data: " . $response->getRequestData());
55
56 // GET is disabled by the moment, but we have the code ready
57 // for a future implementation.
58
59 //$container = empty($contentType) || ($contentType === $this->formats[0]);
60
61 $typeid = 0;
62
64 try {
65 $token = $this->checkTool(array($scope));
66 if (is_null($token)) {
67 throw new Exception('invalid request', 401);
68 }
69
70 // Bug in Moodle as tool provider, should accept only "204 No Content" but schedules grade sync task will notices a failed status if not exactly 200
71 // see: http://www.imsglobal.org/spec/lti-ags/v2p0#score-service-scope-and-allowed-http-methods
72 //$response->setCode(204); // correct
73 $returnCode = 200;
74 $returnCode = $this->checkScore($response->getRequestData(), (int) $itemId);
75 $response->setCode($returnCode); // not really correct
76 } catch (Exception $e) {
77 $response->setCode($e->getCode());
78 $response->setReason($e->getMessage());
79 }
80 }
81
82 protected function checkScore(string $requestData, int $objId): int
83 {
84 global $DIC; /* @var \ILIAS\DI\Container $DIC */
85 $score = json_decode($requestData);
86 //prüfe Userid
87 $userId = self::getUsrIdForObjectAndUsrIdent($objId, $score->userId);
88 if ($userId == null) {
89 ilObjLTIConsumer::getLogger()->debug('User not available');
90 throw new Exception('User not available', 404);
91 return 404;
92 }
93
94 if (empty($score) ||
95 !isset($score->userId) ||
96 !isset($score->gradingProgress) ||
97 !isset($score->activityProgress) ||
98 !isset($score->timestamp) ||
99 isset($score->timestamp) && !self::validate_iso8601_date($score->timestamp) ||
100 (isset($score->scoreGiven) && !is_numeric($score->scoreGiven)) ||
101 (isset($score->scoreGiven) && !isset($score->scoreMaximum)) ||
102 (isset($score->scoreMaximum) && !is_numeric($score->scoreMaximum))
103 ) {
104 ilObjLTIConsumer::getLogger()->debug('Incorrect score received');
105 ilObjLTIConsumer::getLogger()->dump($score);
106 throw new Exception('Incorrect score received', 400);
107 return 400;
108 }
109 //Achtung Ggfs. Timestamp prüfen falls schon was ankam
110 if (!isset($score->scoreMaximum)) {
111 $score->scoreMaximum = 1;
112 }
113 if (isset($score->scoreGiven)) {
114 if ($score->gradingProgress != 'FullyGraded') {
115 $score->scoreGiven = null;
116 }
117 }
118 $result = (float)$score->scoreGiven / (float)$score->scoreMaximum;
119 ilObjLTIConsumer::getLogger()->debug("result: " . $result);
120
121 $ltiObjRes = new ilLTIConsumerResultService();
122
123 $ltiObjRes->readProperties($objId);
124 // check the object status
125 if (!$ltiObjRes->isAvailable()) {
126 throw new Exception('Tool for Object not available', 404);
127 return 404;
128 }
129
130 if ($result >= $ltiObjRes->getMasteryScore()) {
132 } else {
134 }
135 $lp_percentage = (int) round(100 * $result);
136
137 $consRes = ilLTIConsumerResult::getByKeys($objId, $userId, false);
138 if (empty($consRes)) {
139 ilObjLTIConsumer::getLogger()->debug("lti_consumer_results_id not found!");
140 // throw new Exception('lti_consumer_results_id not found!', 404);
141 // return 404;
142 }
143 if (!isset($consRes->id)) {
144 $consRes->id = $DIC->database()->nextId('lti_consumer_results');
145 }
146 $DIC->database()->replace(
147 'lti_consumer_results',
148 array(
149 'id' => array('integer', $consRes->id)
150 ),
151 array(
152 'obj_id' => array('integer', $objId),
153 'usr_id' => array('integer', $userId),
154 'result' => array('float', $result)
155 )
156 );
157
158 ilLPStatus::writeStatus($objId, $userId, $lp_status, $lp_percentage, true);
159
160 $ltiTimestamp = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC3339_EXTENDED, $score->timestamp);
161 if (!$ltiTimestamp) { //moodle 4
162 $ltiTimestamp = DateTimeImmutable::createFromFormat(DateTimeInterface::ISO8601, $score->timestamp);
163 }
164 if (!$ltiTimestamp) { //for example nothing
165 $ltiTimestamp = new DateTime('now');
166 }
167 $gradeValues = [
168 'id' => array('integer', $DIC->database()->nextId('lti_consumer_grades')),
169 'obj_id' => array('integer', $objId),
170 'usr_id' => array('integer', $userId),
171 'score_given' => array('float', $score->scoreGiven),
172 'score_maximum' => array('float', $score->scoreMaximum),
173 'activity_progress' => array('text', $score->activityProgress),
174 'grading_progress' => array('text', $score->gradingProgress),
175 'lti_timestamp' => array('timestamp',$ltiTimestamp->format("Y-m-d H:i:s")),
176 'stored' => array('timestamp', date("Y-m-d H:i:s"))
177 ];
178 $DIC->database()->insert('lti_consumer_grades', $gradeValues);
179
180
181
182 return 200;
183 }
184
185 public static function validate_iso8601_date(string $date): bool
186 {
187 if (preg_match('/^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])' .
188 '(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))' .
189 '([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)' .
190 '?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/', $date) > 0) {
191 return true;
192 }
193 return false;
194 }
195
196 protected static function getUsrIdForObjectAndUsrIdent(int $objId, string $userIdent): ?int
197 {
198 global $DIC; /* @var \ILIAS\DI\Container $DIC */
199 $atExist = strpos($userIdent, '@');
200
201 $query = "SELECT usr_id FROM cmix_users WHERE obj_id = " . $DIC->database()->quote($objId, 'integer');
202
203 if ($atExist > 1) {
204 $query .= " AND usr_ident = " . $DIC->database()->quote($userIdent, 'text');
205 } else { //LTI 1.1
206 $query .= " AND" . $DIC->database()->like('usr_ident', 'text', $userIdent . '@%');
207 }
208 $res = $DIC->database()->query($query);
209
210 $usrId = null;
211 while ($row = $DIC->database()->fetchAssoc($res)) {
212 $usrId = (int) $row['usr_id'];
213 }
214
215 return $usrId;
216 }
217
218}
static writeStatus(int $a_obj_id, int $a_user_id, int $a_status, int $a_percentage=0, bool $a_force_per=false, ?int &$a_old_status=self::LP_STATUS_NOT_ATTEMPTED_NUM)
Write status for user and object.
const LP_STATUS_COMPLETED_NUM
const LP_STATUS_IN_PROGRESS_NUM
static getUsrIdForObjectAndUsrIdent(int $objId, string $userIdent)
execute(ilLTIConsumerServiceResponse $response)
Execute the request for this resource.
__construct(ilLTIConsumerServiceBase $service)
Class constructor.
const SCOPE_GRADESERVICE_SCORE
Scope for access to Score service.
array $params
Template variables parsed from the resource template.
checkTool(array $scopes=array())
Check to make sure the request is valid.
ilLTIConsumerServiceBase $service
Service associated with this resource.
parseTemplate()
Parse the template for variables.
static getByKeys(int $a_obj_id, int $a_usr_id, ?bool $a_create=false)
Get a result by object and user key.
global $DIC
Definition: feed.php:28
$scope
Definition: ltiregstart.php:53
$res
Definition: ltiservices.php:69
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$query
$response
$token
Definition: xapitoken.php:70
$objId
Definition: xapitoken.php:57