ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
ScoringByQuestionTableBinder.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
28 
30 {
31  private ?array $participant_data = null;
32  private array $filter_data = [];
33 
34  public function __construct(
35  private readonly Language $lng,
36  private readonly \DateTimeZone $timezone,
37  private readonly \ilTestParticipantAccessFilterFactory $participant_access_filter_factory,
38  private readonly \ilObjTest $test_obj,
39  private readonly int $question_id
40  ) {
41  }
42 
43  public function withFilterData(array $filter_data): self
44  {
45  $clone = clone $this;
46  $clone->filter_data = $filter_data;
47  return $clone;
48  }
49 
50  public function getRows(
51  DataRowBuilder $row_builder,
52  array $visible_column_ids,
53  Range $range,
54  Order $order,
55  ?array $filter_data,
56  ?array $additional_parameters
57  ): \Generator {
58  if ($this->participant_data === null) {
59  $this->participant_data = $this->getFilteredData($this->question_id);
60  }
61  $this->sortData($order);
62  $data = array_slice($this->participant_data, $range->getStart(), $range->getLength());
63  foreach ($data as $row) {
64  if (in_array($row['usr_id'], $this->test_obj->getAnonOnlyParticipantIds())) {
66  }
67  yield $row_builder->buildDataRow(
68  array_shift($row),
69  $row
70  );
71  }
72  }
73 
74  public function getTotalRowCount(
75  ?array $filter_data,
76  ?array $additional_parameters
77  ): ?int {
78  if ($this->participant_data === null) {
79  $this->participant_data = $this->getFilteredData($this->question_id);
80  }
81  return count($this->participant_data);
82  }
83 
84  public function getMaxAttempts(): int
85  {
86  return $this->test_obj->getMaxPassOfTest();
87  }
88 
89  private function getFilteredData(int $question_id): array
90  {
91  $complete_feedback = $this->test_obj->getCompleteManualFeedback($question_id);
92  $data = $this->test_obj->getCompleteEvaluationData();
93 
94  $participants = $data->getParticipants();
95  $accessible_user_ids = call_user_func(
96  $this->participant_access_filter_factory->getScoreParticipantsUserFilter($this->test_obj->getRefId()),
97  $this->buildUserIdArrayFromParticipants($participants)
98  );
99  $accessible_participants = array_filter(
100  $participants,
101  static fn(\ilTestEvaluationUserData $v): bool => in_array($v->getUserID(), $accessible_user_ids)
102  );
103 
104  return array_reduce(
105  array_keys($accessible_participants),
106  $this->getDataRowClosure($question_id, $accessible_participants, $complete_feedback),
107  []
108  );
109  }
110 
111  private function sortData(
112  Order $order
113  ): void {
114  $key = key($order->get());
115  $direction = $order->get()[$key];
116  usort(
117  $this->participant_data,
118  static function (array $a, array $b) use ($key, $direction): int {
119  $left = $a[$key] ?? null;
120  $right = $b[$key] ?? null;
121  if ($direction === 'ASC') {
122  return $left <=> $right;
123  }
124  return $right <=> $left;
125  }
126  );
127  }
128 
129  private function getDataRowClosure(
130  int $question_id,
131  array $filtered_participants,
132  array $complete_feedback
133  ): \Closure {
134  return function (
135  array $c,
136  int $active_id
137  ) use ($question_id, $filtered_participants, $complete_feedback): array {
138  $array_of_attempts = $this->buildFilteredArrayOfAttempts(
139  $question_id,
140  $active_id,
141  $filtered_participants,
142  $complete_feedback
143  );
144  return [...$c, ...$array_of_attempts];
145  };
146  }
147 
149  int $question_id,
150  int $active_id,
151  array $filtered_participants,
152  array $complete_feedback
153  ): array {
154  return array_reduce(
155  $filtered_participants[$active_id]->getPasses(),
156  function (
157  array $c,
159  ) use ($question_id, $active_id, $filtered_participants, $complete_feedback): array {
160  $question_result = $pd->getAnsweredQuestionByQuestionId($question_id);
161  $feedback_data = $complete_feedback[$active_id][$pd->getPass()][$question_id] ?? [];
162  if ($this->isFilteredAttempt($pd, $question_result, $feedback_data)) {
163  return $c;
164  }
165 
166  $current_participant = $filtered_participants[$active_id];
167 
168  $row = [
169  "{$active_id}_{$pd->getPass()}",
170  ScoringByQuestionTable::COLUMN_EXAMID => \ilObjTest::buildExamId($active_id, $pd->getPass(), $this->test_obj->getId()),
171  ScoringByQuestionTable::COLUMN_NAME => $current_participant->getName(),
173  ScoringByQuestionTable::COLUMN_POINTS_REACHED => $question_result['reached'] ?? 0.0,
174  ScoringByQuestionTable::COLUMN_POINTS_AVAILABLE => $current_participant->getQuestionByAttemptAndId($pd->getPass(), $question_id)['points'] ?? 0.0,
175  ScoringByQuestionTable::COLUMN_FEEDBACK => $feedback_data['feedback'] ?? '',
176  ScoringByQuestionTable::COLUMN_FINALIZED => isset($feedback_data['finalized_evaluation']) && $feedback_data['finalized_evaluation'] === 1,
178  'usr_id' => $current_participant->getUserId()
179  ];
180 
181  if (isset($feedback_data['finalized_tstamp'])
182  && $feedback_data['finalized_tstamp'] !== 0) {
183  $row[ScoringByQuestionTable::COLUMN_FINALIZED_ON] = (new \DateTimeImmutable(
184  '@' . $feedback_data['finalized_tstamp']
185  )
186  )->setTimezone($this->timezone);
187  }
188  $c[] = $row;
189 
190  return $c;
191  },
192  []
193  );
194  }
195 
196  private function isFilteredAttempt(
198  ?array $question_info,
199  array $feedback_data
200  ): bool {
201  if ($this->filter_data === []) {
202  return false;
203  }
204 
205  if ($this->filter_data[ScoringByQuestionTable::FILTER_FIELD_ONLY_ANSWERED] === '1'
206  && ($question_info === null || $question_info['isAnwered'] === false)
207  || $this->filter_data[ScoringByQuestionTable::COLUMN_ATTEMPT] !== ''
208  && $pd->getPass() !== (int) $this->filter_data[ScoringByQuestionTable::COLUMN_ATTEMPT]
209  || $this->filter_data[ScoringByQuestionTable::COLUMN_FINALIZED] === '1'
210  && (!isset($feedback_data['finalized_evaluation']) || $feedback_data['finalized_evaluation'] !== 1)
211  || $this->filter_data[ScoringByQuestionTable::COLUMN_FINALIZED] === '2'
212  && isset($feedback_data['finalized_evaluation']) && $feedback_data['finalized_evaluation'] === 1) {
213  return true;
214  }
215  return false;
216  }
217 
223  private function buildUserIdArrayFromParticipants(array $participants): array
224  {
225  return array_reduce(
226  $participants,
227  static function (array $c, \ilTestEvaluationUserData $v): array {
228  if ($v->getUserID() === null) {
229  return $c;
230  }
231 
232  $c[] = $v->getUserID();
233  return $c;
234  },
235  []
236  );
237  }
238 
239  private function buildFinalizedByName(array $feedback_data): string
240  {
241  if (isset($feedback_data['finalized_by_usr_id'])
242  && $feedback_data['finalized_by_usr_id'] !== '') {
243  return \ilObjUser::_lookupFullname($feedback_data['finalized_by_usr_id']);
244  }
245  return '';
246  }
247 }
getRows(DataRowBuilder $row_builder, array $visible_column_ids, Range $range, Order $order, ?array $filter_data, ?array $additional_parameters)
This is called by the table to retrieve rows; map data-records to rows using the $row_builder e...
isFilteredAttempt(\ilTestEvaluationPassData $pd, ?array $question_info, array $feedback_data)
buildFilteredArrayOfAttempts(int $question_id, int $active_id, array $filtered_participants, array $complete_feedback)
getTotalRowCount(?array $filter_data, ?array $additional_parameters)
Mainly for the purpose of pagination-support, it is important to know about the total number of recor...
$c
Definition: deliver.php:25
Both the subject and the direction need to be specified when expressing an order. ...
Definition: Order.php:28
buildDataRow(string $id, array $record)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
__construct(private readonly Language $lng, private readonly \DateTimeZone $timezone, private readonly \ilTestParticipantAccessFilterFactory $participant_access_filter_factory, private readonly \ilObjTest $test_obj, private readonly int $question_id)
global $lng
Definition: privfeed.php:31
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
static buildExamId($active_id, $pass, $test_obj_id=null)
A simple class to express a naive range of whole positive numbers.
Definition: Range.php:28
getDataRowClosure(int $question_id, array $filtered_participants, array $complete_feedback)