ILIAS  release_8 Revision v8.24
class.ilLPObjectStatisticsLPTableGUI.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=0);
4
28{
29 protected array $types = array("min", "avg", "max");
34 );
35 protected bool $is_chart = false;
36 protected bool $is_details = false;
37 protected array $chart_data = array();
38 protected ?array $preselected;
39 protected array $status_map = [];
40
44 public function __construct(
45 ?object $a_parent_obj,
46 string $a_parent_cmd,
47 array $a_preselect = null,
48 bool $a_is_chart = false,
49 bool $a_is_details = false
50 ) {
51 $this->preselected = $a_preselect;
52 $this->is_chart = $a_is_chart;
53 $this->is_details = $a_is_details;
54
55 $this->setId("lpobjstatlptbl");
56 parent::__construct($a_parent_obj, $a_parent_cmd);
57 }
58
59 public function init(): void
60 {
61 if (!$this->is_details) {
62 $this->setShowRowsSelector(true);
63 $this->addColumn("", "", "1%", true);
64 $this->addColumn($this->lng->txt("trac_title"), "title");
65 $all_columns = $this->getSelectableColumns();
66 foreach ($this->getSelectedColumns() as $col_name => $col_info) {
67 $column_definition = $all_columns[$col_name];
68 $this->addColumn(
69 $column_definition['txt'],
70 $column_definition['sortable'] ? $column_definition['field'] : '',
71 $column_definition['width']
72 );
73 }
74 } else {
75 $this->setLimit(20);
76 $this->setShowRowsSelector(false); // see #35492
77 $this->addColumn($this->lng->txt("trac_figure"));
78 }
79 $this->initFilter();
80
81 if (strpos($this->filter["yearmonth"], "-") === false) {
82 foreach ($this->getMonthsYear(
83 $this->filter["yearmonth"]
84 ) as $num => $caption) {
85 if ($this->is_details) {
86 $this->addColumn($caption); // see #35492
87 } else {
88 $this->addColumn($caption, "month_" . $num);
89 }
90 }
91 } else {
92 foreach ($this->types as $type) {
93 if ($type != "avg") {
94 $caption = " " . $this->lng->txt(
95 "trac_object_stat_lp_" . $type
96 );
97 } else {
98 $caption = " &#216;";
99 }
100 $this->addColumn(
101 $this->lng->txt("trac_members_short") . $caption,
102 "mem_cnt_" . $type
103 );
104 }
105
107
108 foreach ($this->status as $status) {
109 $icon = $icons->renderIconForStatus($status);
110
111 foreach ($this->types as $type) {
112 if ($type != "avg") {
113 $caption = $icon . " " . $this->lng->txt(
114 "trac_object_stat_lp_" . $type
115 );
116 } else {
117 $caption = $icon . " &#216;";
118 }
119 $this->addColumn($caption, $status . "_" . $type);
120 }
121 }
122 }
123
124 if (!$this->is_details) {
125 $this->setTitle($this->lng->txt("trac_object_stat_lp"));
126
127 // $this->setSelectAllCheckbox("item_id");
128 $this->addMultiCommand(
129 "showLearningProgressGraph",
130 $this->lng->txt("trac_show_graph")
131 );
132 $this->setResetCommand("resetLearningProgressFilter");
133 $this->setFilterCommand("applyLearningProgressFilter");
134 }
135
136 $this->setFormAction(
137 $this->ctrl->getFormAction(
138 $this->getParentObject(),
139 $this->getParentCmd()
140 )
141 );
142 $this->setRowTemplate(
143 "tpl.lp_object_statistics_lp_row.html",
144 "Services/Tracking"
145 );
146 $this->setEnableHeader(true);
147 $this->setEnableNumInfo(true);
148 $this->setEnableTitle(true);
149 $this->setDefaultOrderField("title");
150 $this->setDefaultOrderDirection("asc");
151
152 $this->status_map = array(ilLPStatus::LP_STATUS_NOT_ATTEMPTED_NUM => "not_attempted",
156 );
157 }
158
159 public function loadItems(): void
160 {
161 if ($this->is_details) {
162 $this->getDetailItems($this->preselected[0]);
163 } else {
165 $this->getItems();
166 }
167 }
168 public function getSelectableColumns(): array
169 {
170 if ($this->is_details) {
171 return [];
172 }
173 $columns = [];
174 $columns['obj_id'] = [
175 'field' => 'obj_id',
176 'txt' => $this->lng->txt('object_id'),
177 'default' => false,
178 'optional' => true,
179 'sortable' => true,
180 'width' => '5%'
181 ];
182 $columns['reference_ids'] = [
183 'field' => 'reference_ids',
184 'txt' => $this->lng->txt('trac_reference_ids_column'),
185 'default' => false,
186 'optional' => true,
187 'sortable' => true,
188 'width' => '5%'
189 ];
190 $columns['paths'] = [
191 'field' => 'paths',
192 'txt' => $this->lng->txt('trac_paths'),
193 'default' => false,
194 'optional' => true,
195 'sortable' => false,
196 'width' => '25%'
197 ];
198 return $columns;
199 }
200
201
202 public function numericOrdering(string $a_field): bool
203 {
204 $alphabetic_ordering = [
205 'title'
206 ];
207 if (!in_array($a_field, $alphabetic_ordering)) {
208 return true;
209 }
210 return false;
211 }
212
213 protected function isForwardingToFormDispatcher(): bool
214 {
215 return true;
216 }
217
218
222 public function initFilter(): void
223 {
224 $this->setDisableFilterHiding(true);
225
226 // object type selection
227 $this->filter["type"] = "crs";
228
229 // title/description
230 $ti = new ilTextInputGUI(
231 $this->lng->txt("trac_title_description"),
232 "query"
233 );
234 $ti->setMaxLength(64);
235 $ti->setSize(20);
236 $this->addFilterItem($ti);
237 $ti->readFromSession();
238 $this->filter["query"] = $ti->getValue();
239
240 // year/month
241 $si = new ilSelectInputGUI(
242 $this->lng->txt("year") . " / " . $this->lng->txt("month"),
243 "yearmonth"
244 );
245 $si->setOptions($this->getMonthsFilter());
246 $this->addFilterItem($si);
247 $si->readFromSession();
248 if (!$si->getValue()) {
249 $si->setValue(date("Y-m"));
250 }
251 $this->filter["yearmonth"] = $si->getValue();
252
253 if (!strpos($this->filter["yearmonth"], "-")) {
254 $si = new ilSelectInputGUI(
255 $this->lng->txt("trac_figure"),
256 "figure"
257 );
258 $options = array(
259 "mem_cnt_max" => $this->lng->txt(
260 "members"
261 ) . " " . $this->lng->txt("trac_object_stat_lp_max"),
262 "mem_cnt_avg" => $this->lng->txt("members") . " &#216;",
263 // we are using the db column names here (not the lp constants)!
266 ) . " " . $this->lng->txt("trac_object_stat_lp_max"),
269 ) . " &#216;"
270 );
271 $si->setOptions($options);
272 $this->addFilterItem($si);
273 $si->readFromSession();
274 if (!$si->getValue()) {
275 $si->setValue("mem_cnt_max");
276 }
277 $this->filter["measure"] = $si->getValue();
278 }
279
280 $this->filter = $this->initRepositoryFilter($this->filter);
281
282 if ($this->is_details) {
283 $this->filters = array();
284 }
285 }
286
287 public function getItems()
288 {
289 $data = array();
290 $all_status = array_merge(array("mem_cnt"), $this->status);
291
292 $objects = $this->searchObjects(
293 $this->getCurrentFilter(true),
294 "read",
295 null,
296 false
297 );
298 if ($objects) {
299 $objects = array_keys($objects);
300
301 $yearmonth = explode("-", $this->filter["yearmonth"]);
302 if (sizeof($yearmonth) == 1) {
304 $objects,
305 $yearmonth[0]
306 ) as $item) {
307 $obj_id = $item["obj_id"];
308 if (!isset($data[$obj_id])) {
309 $data[$obj_id]["obj_id"] = $obj_id;
310 $data[$obj_id]["title"] = ilObject::_lookupTitle(
311 $obj_id
312 );
313 $data[$obj_id]['reference_ids'] = $this->findReferencesForObjId($obj_id);
314 }
315 $measure_type = substr($this->filter["measure"], -3);
316 $measure_field = substr($this->filter["measure"], 0, -4);
317 $value = $item[$measure_field . "_" . $measure_type];
318 $idx = $item["yyyy"] . "-" . str_pad(
319 $item["mm"],
320 2,
321 "0",
322 STR_PAD_LEFT
323 );
324 $data[$obj_id]["month_" . $idx] = $value;
325 }
326
327 if ($this->is_chart) {
328 // get data for single days (used in chart display)
329 foreach (array_keys(
330 $this->getMonthsYear($yearmonth[0])
331 ) as $num) {
332 $num_string = explode('-', $num);
333 $num = (int) array_pop($num_string);
335 $objects,
336 $yearmonth[0],
337 $num,
338 true
339 ) as $item) {
340 $idx = $yearmonth[0] .
341 "-" . str_pad(
342 (string) $num,
343 2,
344 "0",
345 STR_PAD_LEFT
346 ) .
347 "-" . str_pad(
348 $item["dd"],
349 2,
350 "0",
351 STR_PAD_LEFT
352 );
353 $this->chart_data[$item["obj_id"]][$idx] = $item;
354 }
355 }
356 }
357 } else {
358 // get data aggregated for month
360 $objects,
361 $yearmonth[0],
362 (int) $yearmonth[1]
363 ) as $item) {
364 $obj_id = $item["obj_id"];
365 if (!isset($data[$obj_id])) {
366 $data[$obj_id]["obj_id"] = $obj_id;
367 $data[$obj_id]["title"] = ilObject::_lookupTitle(
368 $obj_id
369 );
370 $data[$obj_id]['reference_ids'] = $this->findReferencesForObjId($obj_id);
371 $this->initRow($data[$obj_id]);
372 }
373
374 foreach ($all_status as $status) {
375 // status-id to field name
376 if (is_numeric($status)) {
377 $field = $this->status_map[$status];
378 } else {
379 $field = $status;
380 }
381
382 // aggregated fields
383 foreach ($this->types as $type) {
384 $value = $item[$field . "_" . $type];
385 $data[$obj_id][$status . "_" . $type] = $value;
386 }
387 }
388 }
389
390 if ($this->is_chart) {
391 // get data for single days (used in chart display)
393 $objects,
394 $yearmonth[0],
395 (int) $yearmonth[1],
396 true
397 ) as $item) {
398 $this->chart_data[$item["obj_id"]][$item["dd"]] = $item;
399 }
400 }
401 }
402
403 // add objects with no usage data
404 foreach ($objects as $obj_id) {
405 if (!isset($data[$obj_id])) {
406 $data[$obj_id]["obj_id"] = $obj_id;
407 $data[$obj_id]["title"] = ilObject::_lookupTitle($obj_id);
408 $data[$obj_id]['reference_ids'] = $this->findReferencesForObjId($obj_id);
409 }
410 }
411 }
412 $this->setData($data);
413 }
414
415 protected function getDetailItems(int $a_obj_id): void
416 {
417 $data = array();
418 $all_status = array_merge(array("mem_cnt"), $this->status);
419
421 array($a_obj_id),
422 $this->filter["yearmonth"]
423 ) as $item) {
424 $month = "month_" . $item["yyyy"] . "-" . str_pad(
425 $item["mm"],
426 2,
427 "0",
428 STR_PAD_LEFT
429 );
430
431 foreach ($all_status as $status) {
432 // status-id to field name
433 if ($status != "mem_cnt") {
434 $field = $this->status_map[$status];
435 } else {
436 $field = $status;
437 }
438 // aggregated fields
439 foreach ($this->types as $type) {
440 $value = $item[$field . "_" . $type];
441 $idx = $item["yyyy"] . "-" . str_pad(
442 $item["mm"],
443 2,
444 "0",
445 STR_PAD_LEFT
446 );
447 $data[$status . "_" . $type]["month_" . $idx] = $value;
448 }
449 }
450 }
451
453
454 // add captions
455 foreach (array_keys($data) as $figure) {
456 $status = substr($figure, 0, -4);
457 $type = substr($figure, -3);
458
459 if ($status != "mem_cnt") {
461 (int) $status
462 );
463 $icon = $icons->renderIconForStatus((int) $status);
464 $text = $icon . " " . $text;
465 } else {
466 $text = $this->lng->txt("members");
467 }
468 if ($type != "avg") {
469 $caption = $text . " " . $this->lng->txt(
470 "trac_object_stat_lp_" . $type
471 );
472 } else {
473 $caption = $text . " &#216;";
474 }
475 $data[$figure]["figure"] = $caption;
476 }
477
478 $this->setData($data);
479 }
480
481 protected function initRow(array &$a_row): void
482 {
483 foreach ($this->types as $type) {
484 $a_row["mem_cnt_" . $type] = null;
485 }
486 foreach ($this->status as $status) {
487 foreach ($this->types as $type) {
488 $a_row[$status . "_" . $type] = null;
489 }
490 }
491 }
492
496 protected function fillRow(array $a_set): void
497 {
498 global $DIC;
499
500 $ilCtrl = $DIC['ilCtrl'];
501
502 if (!$this->is_details) {
503 $type = ilObject::_lookupType($a_set["obj_id"]);
504
505 // ajax details layer link
506 if (strpos($this->filter["yearmonth"], "-") === false) {
507 $this->ctrl->setParameter(
508 $this->parent_obj,
509 "item_id",
510 $a_set["obj_id"]
511 );
512 $url = $this->ctrl->getLinkTarget(
513 $this->parent_obj,
514 "showLearningProgressDetails"
515 );
516 $a_set["title"] .= " (<a href=\"#\" onclick=\"ilObjStat.showLPDetails(event, '" . $url . "');\">Details</a>)";
517 $this->ctrl->setParameter($this->parent_obj, "item_id", "");
518 }
519
520 // optional columns before parsing outer "checkbox" block
521 if ($this->isColumnSelected('obj_id')) {
522 $this->tpl->setVariable('OBJ_ID_COL_VALUE', (string) $a_set['obj_id']);
523 }
524 if ($this->isColumnSelected('reference_ids')) {
525 $this->tpl->setVariable('REF_IDS', implode(', ', $a_set['reference_ids']));
526 }
527 if ($this->isColumnSelected('paths')) {
528 $paths = [];
529 foreach ($a_set['reference_ids'] as $reference_id) {
530 $path_gui = new ilPathGUI();
531 $path_gui->enableTextOnly(false);
532 $path_gui->enableHideLeaf(false);
533 $path_gui->setUseImages(true);
534 $paths[] = $path_gui->getPath(ROOT_FOLDER_ID, $reference_id);
535 }
536 $this->tpl->setVariable('PATHS', implode('<br />', $paths));
537 }
538 $this->tpl->setCurrentBlock("checkbox");
539 $this->tpl->setVariable("OBJ_ID", $a_set["obj_id"]);
540 $this->tpl->setVariable(
541 "ICON_SRC",
542 ilObject::_getIcon(0, "tiny", $type)
543 );
544 $this->tpl->setVariable("ICON_ALT", $this->lng->txt($type));
545 $this->tpl->setVariable("TITLE_TEXT", $a_set["title"]);
546 if ($this->preselected && in_array(
547 $a_set["obj_id"],
548 $this->preselected
549 )) {
550 $this->tpl->setVariable(
551 "CHECKBOX_STATE",
552 " checked=\"checked\""
553 );
554 }
555 $this->tpl->parseCurrentBlock();
556 } else {
557 $this->tpl->setCurrentBlock("details");
558 $this->tpl->setVariable("TXT_FIGURE", $a_set["figure"]);
559 $this->tpl->parseCurrentBlock();
560 }
561
562 $this->tpl->setCurrentBlock("item");
563
564 if (strpos($this->filter["yearmonth"], "-") === false) {
565 foreach (array_keys(
566 $this->getMonthsYear($this->filter["yearmonth"])
567 ) as $num) {
568 $value = $this->anonymizeValue((int) ($a_set["month_" . $num] ?? 0));
569 $this->tpl->setVariable("ITEM_VALUE", $value);
570 $this->tpl->parseCurrentBlock();
571 }
572 } else {
573 foreach ($this->types as $type) {
574 $this->tpl->setVariable(
575 "ITEM_VALUE",
576 $this->anonymizeValue(
577 (int) ($a_set["mem_cnt_" . $type] ?? 0)
578 )
579 );
580 $this->tpl->parseCurrentBlock();
581 }
582 foreach ($this->status as $status) {
583 foreach ($this->types as $type) {
584 $this->tpl->setVariable(
585 "ITEM_VALUE",
586 $this->anonymizeValue(
587 (int) ($a_set[$status . "_" . $type] ?? 0)
588 )
589 );
590 $this->tpl->parseCurrentBlock();
591 }
592 }
593 }
594 }
595
596 public function getGraph(array $a_graph_items): string
597 {
598 $a_graph_items = array(array_pop($a_graph_items));
599
601 $chart->setSize("700", "500");
602
603 $legend = new ilChartLegend();
604 $chart->setLegend($legend);
605
606 // needed for correct stacking
607 $custom_order = array(
608 ilLPStatus::LP_STATUS_IN_PROGRESS_NUM => array("#f7d408",
609 "#fffa00"
610 ),
611 ilLPStatus::LP_STATUS_FAILED_NUM => array("#cf0202", "#f15b5b"),
612 ilLPStatus::LP_STATUS_COMPLETED_NUM => array("#17aa0e", "#6ce148"),
614 "#c4c4c4"
615 )
616 );
617
618 $chart->setColors(array());
619
620 $max_value = 0;
621 foreach ($this->chart_data as $object_id => $days) {
622 if (in_array($object_id, $a_graph_items)) {
623 $series = array();
624 foreach ($custom_order as $status => $colors) {
625 $series[$status] = $chart->getDataInstance(
627 );
628 $series[$status]->setLabel(
630 );
631 $chart_colors[] = $colors[0];
632 }
633 $chart->setColors($chart_colors);
634
635 if (strpos($this->filter["yearmonth"], "-") === false) {
636 $x_axis = $this->lng->txt("month");
637
638 $counter = 0;
639 foreach (array_keys(
640 $this->getMonthsYear(
641 $this->filter["yearmonth"]
642 )
643 ) as $month) {
644 for ($loop = 1; $loop < 32; $loop++) {
645 $item_day = $month . "-" . str_pad(
646 (string) $loop,
647 2,
648 "0",
649 STR_PAD_LEFT
650 );
651 foreach (array_keys($custom_order) as $status) {
652 if (isset($days[$item_day])) {
653 // as there is only 1 entry per day, avg == sum
654 $value = (int) $days[$item_day][$this->status_map[$status] . "_avg"];
655 } else {
656 $value = 0;
657 }
658 $max_value = max($max_value, $value);
659 $value = $this->anonymizeValue($value, true);
660 $series[$status]->addPoint($counter, $value);
661 }
662 $counter++;
663 }
664 }
665 } else {
666 $x_axis = $this->lng->txt("day");
667 for ($loop = 1; $loop < 32; $loop++) {
668 foreach (array_keys($custom_order) as $status) {
669 if (isset($days[$loop])) {
670 // as there is only 1 entry per day, avg == sum
671 $value = (int) $days[$loop][$this->status_map[$status] . "_avg"];
672 } else {
673 $value = 0;
674 }
675 $max_value = max($max_value, $value);
676 $value = $this->anonymizeValue($value, true);
677 $series[$status]->addPoint($loop, $value);
678 }
679 }
680 }
681
682 foreach (array_keys($custom_order) as $status) {
683 $chart->addData($series[$status]);
684 }
685 }
686 }
687
688 $value_ticks = $this->buildValueScale($max_value, true);
689
690 $labels = array();
691 if (strpos($this->filter["yearmonth"], "-") === false) {
692 $counter = 0;
693 foreach ($this->getMonthsYear(
694 $this->filter["yearmonth"],
695 true
696 ) as $caption) {
697 $labels[$counter] = $caption;
698 $counter += 31;
699 }
700 } else {
701 for ($loop = 1; $loop < 32; $loop++) {
702 $labels[$loop] = $loop . ".";
703 }
704 }
705 $chart->setTicks($labels, $value_ticks, true);
706
707 return $chart->getHTML();
708 }
709
710 protected function initLearningProgressDetailsLayer(): void
711 {
712 global $DIC;
713
714 $tpl = $DIC['tpl'];
715
718
719 $tpl->addJavascript("./Services/Tracking/js/ilObjStat.js");
720 }
721}
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getInstanceByType(int $a_type, string $a_id)
const TYPE_GRID
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(?object $a_parent_obj, string $a_parent_cmd, array $a_preselect=null, bool $a_is_chart=false, bool $a_is_details=false)
Constructor.
numericOrdering(string $a_field)
Should this field be sorted numeric?
static getInstance(int $variant=ilLPStatusIcons::ICON_VARIANT_DEFAULT, ?\ILIAS\UI\Renderer $renderer=null, ?\ILIAS\UI\Factory $factory=null)
const LP_STATUS_COMPLETED_NUM
const LP_STATUS_IN_PROGRESS_NUM
const LP_STATUS_NOT_ATTEMPTED_NUM
const LP_STATUS_FAILED_NUM
TableGUI class for learning progress.
getMonthsYear($a_year=null, $a_short=false)
anonymizeValue($a_value, bool $a_force_number=false)
initRepositoryFilter(array $filter)
findReferencesForObjId(int $a_obj_id)
getCurrentFilter(bool $as_query=false)
getMonthsFilter($a_short=false)
buildValueScale(int $a_max_value, bool $a_anonymize=false, bool $a_format_seconds=false)
searchObjects(array $filter, string $permission, ?array $preset_obj_ids=null, bool $a_check_lp_activation=true)
Search objects that match current filters.
static _getStatusText(int $a_status, ?ilLanguage $a_lng=null)
Get status alt text.
static _lookupType(int $id, bool $reference=false)
static _getIcon(int $obj_id=0, string $size="big", string $type="", bool $offline=false)
Get icon for repository item.
static _lookupTitle(int $obj_id)
Creates a path for a start and endnode.
This class represents a selection list property in a property form.
isColumnSelected(string $col)
setShowRowsSelector(bool $a_value)
Toggle rows-per-page selector.
setFilterCommand(string $a_val, string $a_caption="")
setLimit(int $a_limit=0, int $a_default_limit=0)
set max.
setTitle(string $a_title, string $a_icon="", string $a_icon_alt="")
setEnableNumInfo(bool $a_val)
addFilterItem(ilTableFilterItem $a_input_item, bool $a_optional=false)
setEnableTitle(bool $a_enabletitle)
addMultiCommand(string $a_cmd, string $a_text)
setFormAction(string $a_form_action, bool $a_multipart=false)
addColumn(string $a_text, string $a_sort_field="", string $a_width="", bool $a_is_checkbox_action_column=false, string $a_class="", string $a_tooltip="", bool $a_tooltip_with_html=false)
setEnableHeader(bool $a_enableheader)
setDefaultOrderField(string $a_defaultorderfield)
setDisableFilterHiding(bool $a_val=true)
setRowTemplate(string $a_template, string $a_template_dir="")
Set row template.
setId(string $a_val)
setDefaultOrderDirection(string $a_defaultorderdirection)
setResetCommand(string $a_val, string $a_caption="")
setData(array $a_data)
Set table data.
This class represents a text property in a property form.
static getObjectLPStatistics(array $a_obj_ids, int $a_year, int $a_month=null, bool $a_group_by_day=false)
static initOverlay(?ilGlobalTemplateInterface $a_main_tpl=null)
Init YUI Overlay module used in Modules/Test, Services/TermsOfService, Services/Tracking,...
static initjQuery(ilGlobalTemplateInterface $a_tpl=null)
inits and adds the jQuery JS-File to the global or a passed template
const ROOT_FOLDER_ID
Definition: constants.php:32
global $DIC
Definition: feed.php:28
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$type
$url