ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
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 $max_points_by_question = [];
40  private array $skill_point_accounts;
41  private array $reached_skill_levels;
42  private int $user_id;
43  private int $active_id;
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->skill_question_assignment_list = new ilAssQuestionSkillAssignmentList($this->db);
57 
58  $this->skill_level_threshold_list = new ilTestSkillLevelThresholdList($this->db);
59  $this->skill_level_threshold_list->setTestId($test_id);
60  }
61 
62  public function getUserId(): int
63  {
64  return $this->user_id;
65  }
66 
67  public function setUserId(int $user_id): void
68  {
69  $this->user_id = $user_id;
70  }
71 
72  public function getActiveId(): int
73  {
74  return $this->active_id;
75  }
76 
77  public function setActiveId(int $active_id): void
78  {
79  $this->active_id = $active_id;
80  }
81 
82  public function getPass(): int
83  {
84  return $this->pass;
85  }
86 
87  public function setPass($pass): void
88  {
89  $this->pass = $pass;
90  }
91 
93  {
95  }
96 
97  public function setNumRequiredBookingsForSkillTriggering(int $num_required_bookings_for_skill_triggering): void
98  {
99  $this->num_required_bookings_for_skill_triggering = $num_required_bookings_for_skill_triggering;
100  }
101 
102  public function init(ilAssQuestionList $question_list): void
103  {
104  $this->skill_question_assignment_list->setParentObjId($question_list->getParentObjId());
105  $this->skill_question_assignment_list->loadFromDb();
106 
107  $this->skill_level_threshold_list->loadFromDb();
108 
109  $this->initTestQuestionData($question_list);
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(): void
131  {
132  $this->reached_points_by_question = [];
133  $this->skill_point_accounts = [];
134  $this->reached_skill_levels = [];
135  }
136 
137  private function initTestQuestionData(ilAssQuestionList $question_list): void
138  {
139  foreach ($question_list->getQuestionDataArray() as $question_data) {
140  $this->questions[] = $question_data['question_id'];
141  $this->max_points_by_question[ $question_data['question_id'] ] = $question_data['points'];
142  }
143  }
144 
145  private function initTestResultData(array $test_results): void
146  {
147  foreach ($test_results as $key => $result) {
148  if ($key === 'pass' || $key === 'test') { // note: key int 0 IS == 'pass' or 'buxtehude'
149  continue;
150  }
151 
152  $this->reached_points_by_question[ $result['qid'] ] = $result['reached'];
153  }
154  }
155 
156  private function drawUpSkillPointAccounts(): void
157  {
158  foreach ($this->questions as $question_id) {
159  if (!$this->isAnsweredQuestion($question_id)) {
160  continue;
161  }
162 
163  $assignments = $this->skill_question_assignment_list->getAssignmentsByQuestionId($question_id);
164 
165  foreach ($assignments as $assignment) {
166  if ($assignment->hasEvalModeBySolution()) {
167  $reached_skill_points = $this->determineReachedSkillPointsWithSolutionCompare(
168  $assignment->getSolutionComparisonExpressionList()
169  );
170  } else {
171  $max_test_points = $this->max_points_by_question[$question_id];
172  $reached_test_points = $this->reached_points_by_question[$question_id];
173  $reached_skill_points = $this->calculateReachedSkillPointsFromTestPoints(
174  $assignment->getSkillPoints(),
175  $max_test_points,
176  $reached_test_points
177  );
178  }
179 
181  $assignment->getSkillBaseId(),
182  $assignment->getSkillTrefId(),
183  $assignment->getMaxSkillPoints(),
184  $reached_skill_points
185  );
186  }
187  }
188  }
189 
190  private function isAnsweredQuestion(int $question_id): bool
191  {
192  return isset($this->reached_points_by_question[$question_id]);
193  }
194 
197  ): ?int {
198  $question_provider = new ilAssLacQuestionProvider();
199  $question_provider->setQuestionId($expression_list->getQuestionId());
200 
201  foreach ($expression_list->get() as $expression) {
202  $condition_composite = (new ilAssLacConditionParser())->parse(
203  $expression->getExpression()
204  );
205 
206  $composite_evaluator = new ilAssLacCompositeEvaluator(
207  $question_provider,
208  $this->getActiveId(),
209  $this->getPass()
210  );
211  if ($composite_evaluator->evaluate($condition_composite)) {
212  return $expression->getPoints();
213  }
214  }
215 
216  return 0;
217  }
218 
220  int $skill_points,
221  float $max_test_points,
222  float $reached_test_points
223  ): float {
224  if ($reached_test_points < 0) {
225  $reached_test_points = 0;
226  }
227 
228  $factor = 0;
229 
230  if ($max_test_points > 0) {
231  $factor = $reached_test_points / $max_test_points;
232  }
233 
234  return ($skill_points * $factor);
235  }
236 
237  private function bookToSkillPointAccount(
238  int $skill_base_id,
239  int $skill_tref_id,
240  int $max_skill_points,
241  float $reached_skill_points
242  ): void {
243  $skill_key = $skill_base_id . ':' . $skill_tref_id;
244 
245  if (!isset($this->skill_point_accounts[$skill_key])) {
246  $this->skill_point_accounts[$skill_key] = new ilTestSkillPointAccount();
247  }
248 
249  $this->skill_point_accounts[$skill_key]->addBooking($max_skill_points, $reached_skill_points);
250  }
251 
252  private function evaluateSkillPointAccounts(): void
253  {
254  foreach ($this->skill_point_accounts as $skill_key => $skill_point_account) {
255  if (!$this->doesNumBookingsExceedRequiredBookingsBarrier($skill_point_account)) {
256  continue;
257  }
258 
259  list($skill_base_id, $skill_tref_id) = explode(':', $skill_key);
260 
261  $skill = new ilBasicSkill((int) $skill_base_id);
262  $levels = $skill->getLevelData();
263 
264  $reached_level_id = null;
265  foreach ($levels as $level) {
266  $threshold = $this->skill_level_threshold_list->getThreshold($skill_base_id, $skill_tref_id, $level['id']);
267 
268  if (!($threshold instanceof ilTestSkillLevelThreshold) || $threshold->getThreshold() === null) {
269  continue;
270  }
271 
272  if ($skill_point_account->getTotalReachedSkillPercent() < $threshold->getThreshold()) {
273  break;
274  }
275 
276  $reached_level_id = $level['id'];
277  }
278 
279  $this->reached_skill_levels[] = [
280  'sklBaseId' => $skill_base_id, 'sklTrefId' => $skill_tref_id, 'sklLevelId' => $reached_level_id
281  ];
282  }
283  }
284 
286  {
287  return $skillPointAccount->getNumBookings() >= $this->getNumRequiredBookingsForSkillTriggering();
288  }
289 
290  public function handleSkillTriggering(): void
291  {
292  foreach ($this->getReachedSkillLevels() as $reached_skill_level) {
293  $this->invokeSkillLevelTrigger((int) $reached_skill_level['sklLevelId'], (int) $reached_skill_level['sklTrefId']);
294 
295  if ($reached_skill_level['sklTrefId'] > 0) {
296  $this->skill_personal_service->addPersonalSkill($this->getUserId(), (int) $reached_skill_level['sklTrefId']);
297  } else {
298  $this->skill_personal_service->addPersonalSkill($this->getUserId(), (int) $reached_skill_level['sklBaseId']);
299  }
300  }
301  //write profile completion entries if fulfilment status has changed
302  $this->skill_profile_service->writeCompletionEntryForAllProfiles($this->getUserId());
303  }
304 
305  private function invokeSkillLevelTrigger(int $skill_level_id, int $skill_tref_id): void
306  {
308  $skill_level_id,
309  $this->getUserId(),
310  $this->refId,
311  $skill_tref_id,
313  true,
314  false,
315  (string) $this->getPass()
316  );
317 
318  $this->logger->info(
319  "refId={$this->refId} / usrId={$this->getUserId()} / levelId={$skill_level_id} / trefId={$skill_tref_id}"
320  );
321  }
322 
323  public function getSkillsMatchingNumAnswersBarrier(): array
324  {
325  $skills_matching_num_answers_barrier = [];
326 
327  foreach ($this->skill_point_accounts as $skillKey => $skillPointAccount) {
328  if ($this->doesNumBookingsExceedRequiredBookingsBarrier($skillPointAccount)) {
329  list($skillBaseId, $skillTrefId) = explode(':', $skillKey);
330 
331  $skills_matching_num_answers_barrier[$skillKey] = [
332  'base_skill_id' => (int) $skillBaseId,
333  'tref_id' => (int) $skillTrefId
334  ];
335  }
336  }
337 
338  return $skills_matching_num_answers_barrier;
339  }
340 
341  public function getSkillsInvolvedByAssignment(): array
342  {
343  $unique_skills = [];
344 
345  foreach ($this->skill_question_assignment_list->getUniqueAssignedSkills() as $skill) {
346  $skillKey = $skill['skill_base_id'] . ':' . $skill['skill_tref_id'];
347 
348  $unique_skills[$skillKey] = [
349  'base_skill_id' => (int) $skill['skill_base_id'],
350  'tref_id' => (int) $skill['skill_tref_id']
351  ];
352  }
353 
354  return $unique_skills;
355  }
356 
357  public function isAssignedSkill($skill_base_id, $skill_tref_id): void
358  {
359  $this->skill_question_assignment_list->isAssignedSkill($skill_base_id, $skill_tref_id);
360  }
361 
362  public function getAssignedSkillMatchingSkillProfiles(): array
363  {
364  $matching_skill_profiles = [];
365  $users_profiles = $this->skill_profile_service->getProfilesOfUser($this->getUserId());
366  foreach ($users_profiles as $profile_data) {
367  $assigned_skill_levels = $this->skill_profile_service->getSkillLevels($profile_data->getId());
368 
369  foreach ($assigned_skill_levels as $assigned_skill_level) {
370  $skill_base_id = $assigned_skill_level->getBaseSkillId();
371  $skill_tref_id = $assigned_skill_level->getTrefId();
372 
373  if ($this->skill_question_assignment_list->isAssignedSkill($skill_base_id, $skill_tref_id)) {
374  $matching_skill_profiles[$profile_data->getId()] = $profile_data->getTitle();
375  }
376  }
377  }
378 
379  return $matching_skill_profiles;
380  }
381 
382  public function noProfileMatchingAssignedSkillExists(array $available_skill_profiles): bool
383  {
384  $no_profile_matching_skills = $this->skill_question_assignment_list->getUniqueAssignedSkills();
385 
386  foreach (array_keys($available_skill_profiles) as $skill_profile_id) {
387  $assigned_skill_levels = $this->skill_profile_service->getSkillLevels(
388  $this->skill_profile_service->getProfile($skill_profile_id)->getId()
389  );
390 
391  foreach ($assigned_skill_levels as $assigned_skill_level) {
392  $skill_base_id = $assigned_skill_level->getBaseSkillId();
393  $skill_tref_id = $assigned_skill_level->getTrefId();
394 
395  if ($this->skill_question_assignment_list->isAssignedSkill($skill_base_id, $skill_tref_id)) {
396  unset($no_profile_matching_skills["{$skill_base_id}:{$skill_tref_id}"]);
397  }
398  }
399  }
400 
401  return $no_profile_matching_skills !== [];
402  }
403 }
initTestQuestionData(ilAssQuestionList $question_list)
__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 $skill_question_assignment_list
determineReachedSkillPointsWithSolutionCompare(ilAssQuestionSolutionComparisonExpressionList $expression_list)
$refId
Definition: xapitoken.php:58
invokeSkillLevelTrigger(int $skill_level_id, int $skill_tref_id)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
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="")
ilTestSkillLevelThresholdList $skill_level_threshold_list
bookToSkillPointAccount(int $skill_base_id, int $skill_tref_id, int $max_skill_points, float $reached_skill_points)
init(ilAssQuestionList $question_list)
calculateReachedSkillPointsFromTestPoints(int $skill_points, float $max_test_points, float $reached_test_points)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
doesNumBookingsExceedRequiredBookingsBarrier(ilTestSkillPointAccount $skillPointAccount)
Basic Skill.
noProfileMatchingAssignedSkillExists(array $available_skill_profiles)
initTestResultData(array $test_results)
setNumRequiredBookingsForSkillTriggering(int $num_required_bookings_for_skill_triggering)
isAssignedSkill($skill_base_id, $skill_tref_id)