ILIAS  trunk Revision v11.0_alpha-1731-gff9cd7e2bd3
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
class.ilTestSkillEvaluation.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
24 
34 {
37  private array $questions = [];
38  private array $maxPointsByQuestion = [];
39  private array $reachedPointsByQuestion;
40  private array $skillPointAccounts;
41  private array $reachedSkillLevels;
42  private int $userId;
43  private int $activeId;
44  private int $pass;
46 
47 
48  public function __construct(
49  private ilDBInterface $db,
50  private TestLogger $logger,
51  int $test_id,
52  private int $refId,
53  private SkillProfileService $skill_profile_service,
54  private SkillPersonalService $skill_personal_service
55  ) {
56  $this->skillQuestionAssignmentList = new ilAssQuestionSkillAssignmentList($this->db);
57 
58  $this->skillLevelThresholdList = new ilTestSkillLevelThresholdList($this->db);
59  $this->skillLevelThresholdList->setTestId($test_id);
60  }
61 
62  public function getUserId(): int
63  {
64  return $this->userId;
65  }
66 
67  public function setUserId($userId)
68  {
69  $this->userId = $userId;
70  }
71 
72  public function getActiveId(): int
73  {
74  return $this->activeId;
75  }
76 
77  public function setActiveId($activeId)
78  {
79  $this->activeId = $activeId;
80  }
81 
82  public function getPass(): int
83  {
84  return $this->pass;
85  }
86 
87  public function setPass($pass)
88  {
89  $this->pass = $pass;
90  }
91 
93  {
95  }
96 
97  public function setNumRequiredBookingsForSkillTriggering(int $numRequiredBookingsForSkillTriggering): void
98  {
99  $this->numRequiredBookingsForSkillTriggering = $numRequiredBookingsForSkillTriggering;
100  }
101 
102  public function init(ilAssQuestionList $questionList)
103  {
104  $this->skillQuestionAssignmentList->setParentObjId($questionList->getParentObjId());
105  $this->skillQuestionAssignmentList->loadFromDb();
106 
107  $this->skillLevelThresholdList->loadFromDb();
108 
109  $this->initTestQuestionData($questionList);
110  }
111 
115  public function evaluate(array $test_results): void
116  {
117  $this->reset();
118 
119  $this->initTestResultData($test_results);
120 
121  $this->drawUpSkillPointAccounts();
123  }
124 
125  public function getReachedSkillLevels(): array
126  {
128  }
129 
130  private function reset()
131  {
132  $this->reachedPointsByQuestion = [];
133  $this->skillPointAccounts = [];
134  $this->reachedSkillLevels = [];
135  }
136 
137  private function initTestQuestionData(ilAssQuestionList $questionList)
138  {
139  foreach ($questionList->getQuestionDataArray() as $questionData) {
140  $this->questions[] = $questionData['question_id'];
141 
142  $this->maxPointsByQuestion[ $questionData['question_id'] ] = $questionData['points'];
143  }
144  }
145 
149  private function initTestResultData($testResults)
150  {
151  foreach ($testResults as $key => $result) {
152  if ($key === 'pass' || $key === 'test') { // note: key int 0 IS == 'pass' or 'buxtehude'
153  continue;
154  }
155 
156  $this->reachedPointsByQuestion[ $result['qid'] ] = $result['reached'];
157  }
158  }
159 
160  private function drawUpSkillPointAccounts()
161  {
162  foreach ($this->questions as $question_id) {
163  if (!$this->isAnsweredQuestion($question_id)) {
164  continue;
165  }
166 
167  $assignments = $this->skillQuestionAssignmentList->getAssignmentsByQuestionId($question_id);
168 
169  foreach ($assignments as $assignment) {
170  if ($assignment->hasEvalModeBySolution()) {
171  $reached_skill_points = $this->determineReachedSkillPointsWithSolutionCompare(
172  $assignment->getSolutionComparisonExpressionList()
173  );
174  } else {
175  $maxTestPoints = $this->maxPointsByQuestion[$question_id];
176  $reachedTestPoints = $this->reachedPointsByQuestion[$question_id];
177 
178  $reached_skill_points = $this->calculateReachedSkillPointsFromTestPoints(
179  $assignment->getSkillPoints(),
180  $maxTestPoints,
181  $reachedTestPoints
182  );
183  }
184 
186  $assignment->getSkillBaseId(),
187  $assignment->getSkillTrefId(),
188  $assignment->getMaxSkillPoints(),
189  $reached_skill_points
190  );
191  }
192  }
193  }
194 
195  private function isAnsweredQuestion($questionId): bool
196  {
197  return isset($this->reachedPointsByQuestion[$questionId]);
198  }
199 
202  ): ?int {
203  $question_provider = new ilAssLacQuestionProvider();
204  $question_provider->setQuestionId($expression_list->getQuestionId());
205 
206  foreach ($expression_list->get() as $expression) {
207  $condition_composite = (new ilAssLacConditionParser())->parse(
208  $expression->getExpression()
209  );
210 
211  $composite_evaluator = new ilAssLacCompositeEvaluator(
212  $question_provider,
213  $this->getActiveId(),
214  $this->getPass()
215  );
216  if ($composite_evaluator->evaluate($condition_composite)) {
217  return $expression->getPoints();
218  }
219  }
220 
221  return 0;
222  }
223 
224  private function calculateReachedSkillPointsFromTestPoints($skill_points, $max_test_points, $reached_test_points): float
225  {
226  if ($reached_test_points < 0) {
227  $reached_test_points = 0;
228  }
229 
230  $factor = 0;
231 
232  if ($max_test_points > 0) {
233  $factor = $reached_test_points / $max_test_points;
234  }
235 
236  return ($skill_points * $factor);
237  }
238 
239  private function bookToSkillPointAccount($skill_base_id, $skill_tref_id, $max_skill_points, $reached_skill_points): void
240  {
241  $skill_key = $skill_base_id . ':' . $skill_tref_id;
242 
243  if (!isset($this->skillPointAccounts[$skill_key])) {
244  $this->skillPointAccounts[$skill_key] = new ilTestSkillPointAccount();
245  }
246 
247  $this->skillPointAccounts[$skill_key]->addBooking($max_skill_points, $reached_skill_points);
248  }
249 
250  private function evaluateSkillPointAccounts()
251  {
252  foreach ($this->skillPointAccounts as $skill_key => $skill_point_account) {
253  if (!$this->doesNumBookingsExceedRequiredBookingsBarrier($skill_point_account)) {
254  continue;
255  }
256 
257  list($skill_base_id, $skill_tref_id) = explode(':', $skill_key);
258 
259  $skill = new ilBasicSkill((int) $skill_base_id);
260  $levels = $skill->getLevelData();
261 
262  $reached_level_id = null;
263  foreach ($levels as $level) {
264  $threshold = $this->skillLevelThresholdList->getThreshold($skill_base_id, $skill_tref_id, $level['id']);
265 
266  if (!($threshold instanceof ilTestSkillLevelThreshold) || !$threshold->getThreshold()) {
267  continue;
268  }
269 
270  if ($skill_point_account->getTotalReachedSkillPercent() < $threshold->getThreshold()) {
271  break;
272  }
273 
274  $reached_level_id = $level['id'];
275  }
276 
277  $this->reachedSkillLevels[] = [
278  'sklBaseId' => $skill_base_id, 'sklTrefId' => $skill_tref_id, 'sklLevelId' => $reached_level_id
279  ];
280  }
281  }
282 
284  {
285  return $skillPointAccount->getNumBookings() >= $this->getNumRequiredBookingsForSkillTriggering();
286  }
287 
288  public function handleSkillTriggering()
289  {
290  foreach ($this->getReachedSkillLevels() as $reachedSkillLevel) {
291  $this->invokeSkillLevelTrigger((int) $reachedSkillLevel['sklLevelId'], (int) $reachedSkillLevel['sklTrefId']);
292 
293  if ($reachedSkillLevel['sklTrefId'] > 0) {
294  $this->skill_personal_service->addPersonalSkill($this->getUserId(), (int) $reachedSkillLevel['sklTrefId']);
295  } else {
296  $this->skill_personal_service->addPersonalSkill($this->getUserId(), (int) $reachedSkillLevel['sklBaseId']);
297  }
298  }
299  //write profile completion entries if fulfilment status has changed
300  $this->skill_profile_service->writeCompletionEntryForAllProfiles($this->getUserId());
301  }
302 
303  private function invokeSkillLevelTrigger(int $skillLevelId, int $skillTrefId)
304  {
306  $skillLevelId,
307  $this->getUserId(),
308  $this->refId,
309  $skillTrefId,
311  true,
312  false,
313  (string) $this->getPass()
314  );
315 
316  $this->logger->info(
317  "refId={$this->refId} / usrId={$this->getUserId()} / levelId={$skillLevelId} / trefId={$skillTrefId}"
318  );
319  }
320 
321  public function getSkillsMatchingNumAnswersBarrier(): array
322  {
323  $skillsMatchingNumAnswersBarrier = [];
324 
325  foreach ($this->skillPointAccounts as $skillKey => $skillPointAccount) {
326  if ($this->doesNumBookingsExceedRequiredBookingsBarrier($skillPointAccount)) {
327  list($skillBaseId, $skillTrefId) = explode(':', $skillKey);
328 
329  $skillsMatchingNumAnswersBarrier[$skillKey] = [
330  'base_skill_id' => (int) $skillBaseId,
331  'tref_id' => (int) $skillTrefId
332  ];
333  }
334  }
335 
336  return $skillsMatchingNumAnswersBarrier;
337  }
338 
339  public function getSkillsInvolvedByAssignment(): array
340  {
341  $uniqueSkills = [];
342 
343  foreach ($this->skillQuestionAssignmentList->getUniqueAssignedSkills() as $skill) {
344  $skillKey = $skill['skill_base_id'] . ':' . $skill['skill_tref_id'];
345 
346  $uniqueSkills[$skillKey] = [
347  'base_skill_id' => (int) $skill['skill_base_id'],
348  'tref_id' => (int) $skill['skill_tref_id']
349  ];
350  }
351 
352  return $uniqueSkills;
353  }
354 
355  public function isAssignedSkill($skillBaseId, $skillTrefId)
356  {
357  $this->skillQuestionAssignmentList->isAssignedSkill($skillBaseId, $skillTrefId);
358  }
359 
360  public function getAssignedSkillMatchingSkillProfiles(): array
361  {
362  $matchingSkillProfiles = [];
363 
364  $usersProfiles = $this->skill_profile_service->getProfilesOfUser($this->getUserId());
365 
366  foreach ($usersProfiles as $profileData) {
367  $assignedSkillLevels = $this->skill_profile_service->getSkillLevels($profileData->getId());
368 
369  foreach ($assignedSkillLevels as $assignedSkillLevel) {
370  $skillBaseId = $assignedSkillLevel->getBaseSkillId();
371  $skillTrefId = $assignedSkillLevel->getTrefId();
372 
373  if ($this->skillQuestionAssignmentList->isAssignedSkill($skillBaseId, $skillTrefId)) {
374  $matchingSkillProfiles[$profileData->getId()] = $profileData->getTitle();
375  }
376  }
377  }
378 
379  return $matchingSkillProfiles;
380  }
381 
382  public function noProfileMatchingAssignedSkillExists(array $availableSkillProfiles): bool
383  {
384  $noProfileMatchingSkills = $this->skillQuestionAssignmentList->getUniqueAssignedSkills();
385 
386  foreach ($availableSkillProfiles as $skillProfileId => $skillProfileTitle) {
387  $profile = $this->skill_profile_service->getProfile($skillProfileId);
388  $assignedSkillLevels = $this->skill_profile_service->getSkillLevels($profile->getId());
389 
390  foreach ($assignedSkillLevels as $assignedSkillLevel) {
391  $skillBaseId = $assignedSkillLevel->getBaseSkillId();
392  $skillTrefId = $assignedSkillLevel->getTrefId();
393 
394  if ($this->skillQuestionAssignmentList->isAssignedSkill($skillBaseId, $skillTrefId)) {
395  unset($noProfileMatchingSkills["{$skillBaseId}:{$skillTrefId}"]);
396  }
397  }
398  }
399 
400  return $noProfileMatchingSkills !== [];
401  }
402 }
init(ilAssQuestionList $questionList)
setNumRequiredBookingsForSkillTriggering(int $numRequiredBookingsForSkillTriggering)
__construct(private ilDBInterface $db, private TestLogger $logger, int $test_id, private int $refId, private SkillProfileService $skill_profile_service, private SkillPersonalService $skill_personal_service)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ilAssQuestionSkillAssignmentList $skillQuestionAssignmentList
determineReachedSkillPointsWithSolutionCompare(ilAssQuestionSolutionComparisonExpressionList $expression_list)
invokeSkillLevelTrigger(int $skillLevelId, int $skillTrefId)
calculateReachedSkillPointsFromTestPoints($skill_points, $max_test_points, $reached_test_points)
$refId
Definition: xapitoken.php:58
ilTestSkillLevelThresholdList $skillLevelThresholdList
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
isAssignedSkill($skillBaseId, $skillTrefId)
bookToSkillPointAccount($skill_base_id, $skill_tref_id, $max_skill_points, $reached_skill_points)
static writeUserSkillLevelStatus(int $a_level_id, int $a_user_id, int $a_trigger_ref_id, int $a_tref_id=0, int $a_status=ilBasicSkill::ACHIEVED, bool $a_force=false, bool $a_self_eval=false, string $a_unique_identifier="", float $a_next_level_fulfilment=0.0, string $trigger_user_id="")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
initTestQuestionData(ilAssQuestionList $questionList)
doesNumBookingsExceedRequiredBookingsBarrier(ilTestSkillPointAccount $skillPointAccount)
noProfileMatchingAssignedSkillExists(array $availableSkillProfiles)
Basic Skill.