ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilStudyProgrammeUserTable.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
27 {
28  public const OPTION_ALL = -1;
29  public const VALIDITY_OPTION_VALID = 1;
30  public const VALIDITY_OPTION_INVALID = 3;
31  public const OPTION_USR_ACTIVE = 1;
32  public const OPTION_USR_INACTIVE = 2;
33 
34  public const PRG_COLS = [
35  ['name', 'name', false, true, true],
36  ['login', 'login', false, true, true],
37  ['prg_status', 'prg_status', false, true, true],
38  ['prg_completion_date', 'prg_completion_date', true, true, true],
39  ['prg_completion_by', 'prg_completion_by', true, true, true],
40  ['points', 'prg_points_reachable', false, true, false],
41  ['points_required', 'prg_points_required', false, false, true],
42  ['points_current', 'prg_points_current', false, false, true],
43  ['prg_custom_plan', 'prg_custom_plan', true, true, true],
44  ['prg_belongs_to', 'prg_belongs_to', true, true, true],
45  ['prg_assign_date', 'prg_assign_date', false, true, true],
46  ['prg_assigned_by', 'prg_assigned_by', true, true, true],
47  ['prg_deadline', 'prg_deadline', true, true, true],
48  ['prg_expiry_date', 'prg_expiry_date', true, true, true],
49  ['prg_validity', 'prg_validity', true, true, true]
50  ];
51 
52  private const ORDER_MAPPING = [
53  'prg_status' => 'status',
54  'prg_custom_plan' => 'custom_plan',
55  'prg_belongs_to' => 'belongs_to',
56  'prg_validity' => 'validity',
57  'prg_orgus' => 'orgus',
58  'prg_completion_by' => 'completion_by',
59  'prg_completion_date' => 'completion_date',
60  'prg_assign_date' => 'assign_date',
61  'prg_assigned_by' => 'assigned_by',
62  'prg_deadline' => 'deadline',
63  'prg_expiry_date' => 'expiry_date',
64  'pgs_id' => 'prgrs_id'
65  ];
66 
68 
69  public function __construct(
70  protected ilDBInterface $db,
71  protected ilExportFieldsInfo $export_fields_info,
72  protected ilPRGAssignmentDBRepository $assignment_repo,
73  protected ilLanguage $lng,
74  protected ilPRGPermissionsHelper $permissions,
75  protected ilCertificateDownloadValidator $cert_validator
76  ) {
77  $this->lng->loadLanguageModule("prg");
78  $this->user_ids_viewer_may_read_learning_progress_of = $this->permissions->getUserIdsSusceptibleTo(
80  );
81  }
82 
83  protected function getUserDataColumns(int $prg_id): array
84  {
85  $cols = [];
86  $user_data_cols = $this->export_fields_info->getSelectableFieldsInfo($prg_id);
87  foreach ($user_data_cols as $k => $column_definition) {
88  $cols[$k] = [$k, $column_definition['txt'], true, true, true];
89  }
90  return $cols;
91  }
92 
93  protected function getPrgColumns(): array
94  {
95  $cols = [];
96  foreach (self::PRG_COLS as $k) {
97  $k[1] = $this->lng->txt($k[1]);
98  $cols[$k[0]] = $k;
99  }
100 
101  return $cols;
102  }
103 
104  public function getColumns(
105  int $prg_id,
106  bool $add_active_column = false,
107  bool $add_cert_column = false
108  ): array {
109  $prg_cols = $this->getPrgColumns();
110  $prg_cols_pre = array_slice($prg_cols, 0, 2);
111  $prg_cols_post = array_slice($prg_cols, 2);
112 
113  $columns = array_merge(
114  $prg_cols_pre,
115  $this->getUserDataColumns($prg_id),
116  $prg_cols_post
117  );
118 
119  if ($add_active_column) {
120  $columns["active"] = ["active", $this->lng->txt("active"), true, true, true];
121  }
122  if ($add_cert_column) {
123  $columns["cert_relevance"] = ["cert_relevance", $this->lng->txt("cert_relevance"), true, true, true];
124  }
125  return $columns;
126  }
127 
128 
129  public function countFetchData(int $prg_id, ?array $valid_user_ids, ilPRGAssignmentFilter $custom_filters): int
130  {
131  return $this->assignment_repo->countAllForNodeIsContained($prg_id, $valid_user_ids, $custom_filters);
132  }
133 
138  public function fetchData(
139  int $prg_id,
140  ?array $valid_user_ids,
141  Order $order,
142  ?ilPRGAssignmentFilter $custom_filters = null,
143  ?int $limit = null,
144  ?int $offset = null
145  ): array {
146  $data = $this->assignment_repo->getAllForNodeIsContained(
147  $prg_id,
148  $valid_user_ids,
149  $custom_filters
150  );
151 
152  $root_assignemnts = array_filter(
153  $data,
154  fn($ass) => $ass->getRootId() === $prg_id
155  );
156  $root_usr_ids = array_map(fn($r) => $r->getUserid(), $root_assignemnts);
157  $cert_ass_ids = $this->assignment_repo->getCertificateRelevantAssignmentIds(
158  $prg_id,
159  ...$root_usr_ids
160  );
161 
162  $rows = array_map(fn($ass) => $this->toRow($ass, $prg_id, $cert_ass_ids), $data);
163  $rows = $this->postOrder($rows, $order);
164  if ($limit) {
165  $offset = $offset ?? 0;
166  $rows = array_slice($rows, $offset, $limit);
167  }
168  return $rows;
169  }
170 
171  public function fetchSingleUserRootAssignments(int $usr_id): array
172  {
173  $data = $this->assignment_repo->getForUser($usr_id);
174  $row = array_map(fn($ass) => $this->toRow($ass, $ass->getRootId(), []), $data);
175  return $row;
176  }
177 
178 
179  protected $skip_perm_check_on_user = false;
180  public function disablePermissionCheck($flag = false): void
181  {
182  $this->skip_perm_check_on_user = $flag;
183  }
184 
185  protected function includeLearningProgress(int $usr_id): bool
186  {
187  return $this->skip_perm_check_on_user
188  || in_array($usr_id, $this->user_ids_viewer_may_read_learning_progress_of);
189  }
190 
191  protected function toRow(ilPRGAssignment $ass, int $node_id, array $cert_ass_ids): ilStudyProgrammeUserTableRow
192  {
193  $pgs = $ass->getProgressForNode($node_id);
194  $row = new ilStudyProgrammeUserTableRow(
195  $ass->getId(),
196  $ass->getUserId(),
197  $node_id,
198  $ass->getRootId() === $node_id,
199  $ass->getUserInformation()
200  );
201 
202  $show_lp = $this->includeLearningProgress($ass->getUserId());
203 
204  $prg_node = ilObjStudyProgramme::getInstanceByObjId($node_id);
205  $points_reachable = (string) $pgs->getPossiblePointsOfRelevantChildren();
206  if ($prg_node->getLPMode() === ilStudyProgrammeSettings::MODE_LP_COMPLETED) {
207  $points_reachable = (string) $pgs->getAmountOfPoints();
208  }
209 
210  $prg_lifecycle_status = $prg_node->getStatus();
211  $row = $row
212  ->withUserActiveRaw($ass->getUserInformation()->isActive())
213  ->withUserActive($this->activeToRepresent($ass->getUserInformation()->isActive()))
214  ->withFirstname($ass->getUserInformation()->getFirstname())
215  ->withLastname($ass->getUserInformation()->getLastname())
216  ->withLogin($ass->getUserInformation()->getLogin())
217  ->withOrgUs($ass->getUserInformation()->getOrguRepresentation())
218  ->withGender($this->lng->txt('gender_' . $ass->getUserInformation()->getGender()))
219  ->withStatus($show_lp ? $this->statusToRepresent($pgs->getStatus()) : '')
220  ->withStatusRaw($pgs->getStatus())
221  ->withCompletionDate(
222  $show_lp && $pgs->getCompletionDate() ? $pgs->getCompletionDate()->format($this->getUserDateFormat()) : ''
223  )
224  ->withCompletionBy(
225  $show_lp && $pgs->getCompletionBy() ? $this->completionByToRepresent($pgs) : ''
226  )
227  ->withCompletionByObjIds(
228  $show_lp && $pgs->getCompletionBy() ? $this->completionByToCollection($pgs) : null
229  )
230  ->withPointsReachable($points_reachable)
231  ->withPointsRequired((string) $pgs->getAmountOfPoints())
232  ->withPointsCurrent($show_lp ? (string) $pgs->getCurrentAmountOfPoints() : '')
233  ->withCustomPlan($this->boolToRepresent($pgs->hasIndividualModifications()))
234  ->withBelongsTo($this::lookupTitle($ass->getRootId()))
235  ->withAssignmentDate($pgs->getAssignmentDate()->format($this->getUserDateFormat()))
236  ->withAssignmentBy(
238  $ass->isManuallyAssigned(),
239  $ass->getLastChangeBy()
240  )
241  )
242  ->withDeadline(
243  $show_lp && $pgs->getDeadline() && !$pgs->isSuccessful() ? $pgs->getDeadline()->format($this->getUserDateFormat()) : ''
244  )
245  ->withExpiryDate(
246  $show_lp && $pgs->getValidityOfQualification() ? $pgs->getValidityOfQualification()->format($this->getUserDateFormat()) : ''
247  )
248  ->withValidity($show_lp ? $this->validToRepresent($pgs) : '')
249  ->withRestartDate($ass->getRestartDate() ? $ass->getRestartDate()->format($this->getUserDateFormat()) : '')
250  ->withNodeLifecycleStatus($prg_lifecycle_status)
251  ->withCertificateRelevance(
252  in_array($ass->getId(), $cert_ass_ids)
253  && $this->cert_validator->isCertificateDownloadable($ass->getUserId(), $ass->getRootId())
254  )
255  ;
256  return $row;
257  }
258 
259  protected function getUserDateFormat(): string
260  {
261  return ilCalendarUtil::getUserDateFormat(0, true);
262  }
263 
267  public function statusToRepresent($a_status): string
268  {
269  if ($a_status == ilPRGProgress::STATUS_IN_PROGRESS) {
270  return $this->lng->txt("prg_status_in_progress");
271  }
272  if ($a_status == ilPRGProgress::STATUS_COMPLETED) {
273  return $this->lng->txt("prg_status_completed");
274  }
275  if ($a_status == ilPRGProgress::STATUS_ACCREDITED) {
276  return $this->lng->txt("prg_status_accredited");
277  }
278  if ($a_status == ilPRGProgress::STATUS_NOT_RELEVANT) {
279  return $this->lng->txt("prg_status_not_relevant");
280  }
281  if ($a_status == ilPRGProgress::STATUS_FAILED) {
282  return $this->lng->txt("prg_status_failed");
283  }
284  throw new ilException("Unknown status: '$a_status'");
285  }
286 
287  public function boolToRepresent(bool $value): string
288  {
289  return ($value) ? $this->lng->txt("yes") : $this->lng->txt("no");
290  }
291 
292  public function validToRepresent(ilPRGProgress $pgs): string
293  {
294  if (!$pgs->isSuccessful()) {
295  return '-';
296  }
297  return $pgs->isInvalidated() ? $this->lng->txt("prg_not_valid") : $this->lng->txt("prg_still_valid");
298  }
299 
300  public function activeToRepresent(bool $value): string
301  {
302  return $value ? $this->lng->txt('active') : $this->lng->txt('inactive');
303  }
304 
305  public function assignmentSourceToRepresent(bool $manually, int $assignment_src): string
306  {
308  $srcs[ilPRGAssignment::AUTO_ASSIGNED_BY_RESTART] = 'restarted';
309  if ($manually || ! array_key_exists($assignment_src, $srcs)) {
310  return $this::lookupTitle($assignment_src);
311  }
312  return implode(' ', [
313  $this->lng->txt('prg_autoassignment'),
314  $this->lng->txt($srcs[$assignment_src])
315  ]);
316  }
317 
318  public function completionByToRepresent(ilPRGProgress $progress): string
319  {
320  $completion_by = $progress->getCompletionBy();
321  if ($completion_by !== ilPRGProgress::COMPLETED_BY_SUBNODES) {
322  return $this::lookupTitle($completion_by);
323  }
324 
325  $out = array_map(
326  fn(int $node_obj_id): string => self::lookupTitle($node_obj_id),
327  $this->completionByToCollection($progress)
328  );
329 
330  return implode(', ', $out);
331  }
332 
333  protected function completionByToCollection(ilPRGProgress $progress): array
334  {
335  $completion_by = $progress->getCompletionBy();
336  if ($completion_by !== ilPRGProgress::COMPLETED_BY_SUBNODES) {
337  return [$completion_by];
338  }
339  $successful_subnodes = array_filter(
340  $progress->getSubnodes(),
341  static fn(ilPRGProgress $pgs): bool => $pgs->isSuccessful()
342  );
343  return array_map(
344  static fn(ilPRGProgress $pgs): int => $pgs->getNodeId(),
345  $successful_subnodes
346  );
347  }
348 
349  public static function lookupTitle(int $obj_id): string
350  {
351  $type = ilObject::_lookupType($obj_id);
352  switch ($type) {
353  case 'usr':
354  return ilObject::_lookupTitle($obj_id);
355  case 'prg':
356  $title = ilObject::_lookupTitle($obj_id);
358  return sprintf('(%s)', $title);
359  }
360  return $title;
361  case 'crs':
362  $title = ilObject::_lookupTitle($obj_id);
363  $refs = ilObject::_getAllReferences($obj_id);
364  $target_ref_id = array_shift($refs) ?? null;
365  if ($target_ref_id === null || ilObject::_isInTrash($target_ref_id)) {
366  return sprintf('(%s)', $title);
367  }
368  return $title;
369  }
370 
371  if ($del = ilObjectDataDeletionLog::get($obj_id)) {
372  return sprintf('(%s)', $del['title']);
373  }
374  return 'object id ' . $obj_id;
375  }
376 
377  protected function postOrder(array $list, \ILIAS\Data\Order $order): array
378  {
379  [$aspect, $direction] = $order->join('', function ($i, $k, $v) {
380  return [$k, $v];
381  });
382 
383  if (array_key_exists($aspect, self::ORDER_MAPPING)) {
384  $aspect = self::ORDER_MAPPING[$aspect];
385  }
386 
387  $user_date_format = $this->getUserDateFormat();
388  usort($list, static function (ilStudyProgrammeUserTableRow $a, ilStudyProgrammeUserTableRow $b) use ($aspect, $user_date_format): int {
389  $a = $a->toArray();
390  $b = $b->toArray();
391 
392  if (is_numeric($a[$aspect])) {
393  return $a[$aspect] <=> $b[$aspect];
394  }
395  if (is_bool($a[$aspect])) {
396  return (int) $a[$aspect] <=> (int) $b[$aspect];
397  }
398 
399  if (in_array($aspect, [
400  'completion_date',
401  'deadline',
402  'assign_date',
403  'expiry_date',
404  ])) {
405  return \DateTimeImmutable::createFromFormat($user_date_format, $a[$aspect])
406  <=> \DateTimeImmutable::createFromFormat($user_date_format, $b[$aspect]);
407  }
408  return strcmp($a[$aspect], $b[$aspect]);
409  });
410 
411  if ($direction === $order::DESC) {
412  $list = array_reverse($list);
413  }
414  return $list;
415  }
416 }
ilStudyProgrammeUserTable provides a flattened list of progresses at a programme-node.
fetchData(int $prg_id, ?array $valid_user_ids, Order $order, ?ilPRGAssignmentFilter $custom_filters=null, ?int $limit=null, ?int $offset=null)
getColumns(int $prg_id, bool $add_active_column=false, bool $add_cert_column=false)
Interface Observer Contains several chained tasks and infos about them.
static _getAllReferences(int $id)
get all reference ids for object ID
completionByToRepresent(ilPRGProgress $progress)
static getUserDateFormat(int $a_add_time=0, bool $a_for_parsing=false)
Parse current user setting into date/time format.
assignmentSourceToRepresent(bool $manually, int $assignment_src)
Both the subject and the direction need to be specified when expressing an order. ...
Definition: Order.php:28
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
toRow(ilPRGAssignment $ass, int $node_id, array $cert_ass_ids)
A Progress is the status of a user on a single node of an assignment; it is unique by assignment_id:u...
static _lookupTitle(int $obj_id)
Validates if an active certificate is stored in the database and can be downloaded by the user...
static _isInTrash(int $ref_id)
$out
Definition: buildRTE.php:24
completionByToCollection(ilPRGProgress $progress)
countFetchData(int $prg_id, ?array $valid_user_ids, ilPRGAssignmentFilter $custom_filters)
Both role and OrgU-based permissions are relevant in many places of the PRG.
static getInstanceByObjId(int $obj_id)
ilStudyProgrammeUserTable provides a flattened list of progresses at a programme-node.
Assignments are relations of users to a PRG; They hold progress-information for (sub-)nodes of the PR...
global $lng
Definition: privfeed.php:31
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
static getRefIdFor(int $obj_id)
__construct(protected ilDBInterface $db, protected ilExportFieldsInfo $export_fields_info, protected ilPRGAssignmentDBRepository $assignment_repo, protected ilLanguage $lng, protected ilPRGPermissionsHelper $permissions, protected ilCertificateDownloadValidator $cert_validator)
getProgressForNode(int $node_id)
Assignments are relations of users to a PRG; They hold progress-information for (sub-)nodes of the PR...
static _lookupType(int $id, bool $reference=false)
postOrder(array $list, \ILIAS\Data\Order $order)
$r