ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilExerciseManagementCollectFilesJob.php
Go to the documentation of this file.
1<?php
2
28
33{
34 public const FBK_DIRECTORY = "Feedback_files";
35 public const LINK_COLOR = "0,0,255";
36 public const BG_COLOR = "255,255,255";
37 //Column number incremented in ilExcel
40 public const PARTICIPANT_LOGIN_COLUMN = 2;
41 public const SUBMISSION_DATE_COLUMN = 3;
44 protected \ILIAS\Exercise\PeerReview\DomainService $peer_review;
46 protected \ILIAS\Exercise\Team\TeamManager $team;
47
48 protected ilLogger $logger;
49 protected string $target_directory = "";
50 protected string $submissions_directory = "";
52 protected int $user_id = 0;
53 protected int $exercise_id = 0;
54 protected int $exercise_ref_id = 0;
55 protected ?string $temp_dir = null;
56 protected ilLanguage $lng;
57 protected string $sanitized_title = ""; //sanitized file name/sheet title
58 protected ilExcel $excel;
59 protected array $criteria_items = [];
60 protected array $title_columns = [];
61 protected array $ass_types_with_files = []; //TODO will be deprecated when use the new assignment type interface
62 protected int $participant_id = 0;
63 protected ?array $selected_participants = null;
64
68 public function __construct()
69 {
70 global $DIC;
71 $this->lng = $DIC->language();
72 $this->lng->loadLanguageModule('exc');
73 //TODO will be deprecated when use the new assignment type interface
74 $this->ass_types_with_files = array(
79 );
81 $this->logger = $DIC->logger()->exc();
82 $this->domain = $DIC->exercise()->internal()->domain();
83 $this->peer_review = $this->domain->peerReview();
84 $this->team = $DIC->exercise()->internal()->domain()->team();
85 }
86
90 public function getInputTypes(): array
91 {
92 return
93 [
94 new SingleType(IntegerValue::class),
95 new SingleType(IntegerValue::class),
96 new SingleType(IntegerValue::class),
97 new SingleType(IntegerValue::class),
98 new SingleType(IntegerValue::class),
99 new SingleType(StringValue::class),
100 ];
101 }
102
103 public function getOutputType(): Type
104 {
105 return new SingleType(StringValue::class);
106 }
107
108 public function isStateless(): bool
109 {
110 return true;
111 }
112
121 public function run(
122 array $input,
123 Observer $observer
124 ): Value {
125 $this->exercise_id = $input[0]->getValue();
126 $this->exercise_ref_id = $input[1]->getValue();
127 $assignment_id = $input[2]->getValue();
128 $participant_id = $input[3]->getValue();
129 $this->user_id = $input[4]->getValue();
130 $selected_participants = $input[5]->getValue();
131 $this->logger->debug("Collect files. assignment id: " . $assignment_id . ", selected participants: " . $selected_participants);
132 if (trim($selected_participants) === "") {
133 $this->selected_participants = null;
134 } else {
135 $this->selected_participants = explode(",", $selected_participants);
136 }
137 $final_directory = "";
138
139 //if we have assignment
140 if ($assignment_id > 0) {
141 $this->collectAssignmentData($assignment_id);
142 $final_directory = $this->target_directory;
143 }
144
145 if ($participant_id > 0) {
146 $this->participant_id = $participant_id;
147 $assignments = ilExAssignment::getInstancesByExercise($this->exercise_id);
148 foreach ($assignments as $assignment) {
149 $this->collectAssignmentData($assignment->getId());
150 }
151 $final_directory = $this->temp_dir . DIRECTORY_SEPARATOR . ilExSubmission::getDirectoryNameFromUserData($participant_id);
152 }
153
154 $out = new StringValue();
155 $out->setValue($final_directory);
156 return $out;
157 }
158
163 public function copyFileToSubDirectory(string $a_directory, \ILIAS\Exercise\PeerReview\Criteria\CriteriaFile $file): void
164 {
165 $crit_file_manager = $this->peer_review->criteriaFile($this->assignment->getId());
166 $dir = $this->target_directory . "/" . $a_directory;
167
168 if (!is_dir($dir)) {
170 }
171
172 $f = fopen($dir . "/" . basename($file->getTitle()), 'wb');
173 fwrite($f, $crit_file_manager->getStream($file->getRid())->getContents());
174 fclose($f);
175
176 /*global $DIC;
177 $fs = $DIC->filesystem();
178
179 $fs->storage()->copy($a_file, $this->temp_dir."/".basename($a_file));*/
180 }
181
183 {
184 return 30;
185 }
186
191 protected function addColumnTitles(): void
192 {
193 $col = 0;
194 foreach ($this->title_columns as $title) {
195 $this->excel->setCell(1, $col, $title);
196 $col++;
197 }
198 }
199
204 protected function createUniqueTempDirectory(): void
205 {
206 $this->temp_dir = ilFileUtils::ilTempnam();
207 ilFileUtils::makeDirParents($this->temp_dir);
208 }
209
213 protected function createTargetDirectory(): void
214 {
215 $path = $this->temp_dir . DIRECTORY_SEPARATOR;
216 if ($this->participant_id > 0) {
217 $user_dir = ilExSubmission::getDirectoryNameFromUserData($this->participant_id);
218 $path .= $user_dir . DIRECTORY_SEPARATOR;
219 }
220 $this->target_directory = $path . $this->sanitized_title;
221 ilFileUtils::makeDirParents($this->target_directory);
222 }
223
227 protected function createSubmissionsDirectory(): void
228 {
229 $this->logger->dump("lang key => " . $this->lng->getLangKey());
230 $this->submissions_directory = $this->target_directory . DIRECTORY_SEPARATOR . $this->lng->txt("exc_ass_submission_zip");
231 ilFileUtils::createDirectory($this->submissions_directory);
232 }
233
242 public function collectSubmissionFiles(): void
243 {
244 $members = array();
245
246 $exercise = new ilObjExercise($this->exercise_id, false);
247
248 if ($this->participant_id > 0) {
249 $exc_members_id = array($this->participant_id);
250 } else {
251 $exc_members_id = $exercise->members_obj->getMembers();
252 }
253
254 $filter = new ilExerciseMembersFilter($this->exercise_ref_id, $exc_members_id, $this->user_id);
255 $exc_members_id = $filter->filterParticipantsByAccess();
256
257 $user_ids = [];
258 foreach ($exc_members_id as $member_id) {
259 if (!is_null($this->selected_participants) && !in_array($member_id, $this->selected_participants)) {
260 continue;
261 }
262 $user_ids[] = $member_id;
263 }
264
265 $this->domain->submission($this->assignment->getId())
266 ->copySubmissionsToDir(
267 $user_ids,
268 $this->submissions_directory
269 );
270 }
271
272 protected function isExcelNeeded(int $a_ass_type, bool $a_has_fbk): bool
273 {
274 if ($a_ass_type == ilExAssignment::TYPE_TEXT || $a_ass_type == ilExAssignment::TYPE_UPLOAD
275 || $a_ass_type == ilExAssignment::TYPE_UPLOAD_TEAM) {
276 return true;
277 } elseif ($a_has_fbk && $a_ass_type != ilExAssignment::TYPE_UPLOAD_TEAM) {
278 return true;
279 }
280 return false;
281 }
282
287 protected function addCriteriaToExcel(
288 int $feedback_giver,
289 int $participant_id,
290 int $row,
291 int $col
292 ): void {
293 $submission = new ilExSubmission($this->assignment, $participant_id);
294
295 //Possible TODO: This getPeerReviewValues doesn't return always the same array structure then the client classes have
296 //to deal with this. Use only one data structure will avoid this extra work.
297 //values can be [19] => "blablablab" or ["text"] => "blablabla"
298 $values = $submission->getPeerReview()->getPeerReviewValues($feedback_giver, $participant_id);
299
300 foreach ($this->criteria_items as $item) {
301 $col++;
302
303 //Criteria without catalog doesn't have ID nor TITLE. The criteria instance is given via "type" ilExcCriteria::getInstanceByType
304 $crit_id = $item->getId();
305 $crit_type = $item->getType();
306 $crit_title = $item->getTitle();
307 if ($crit_title == "") {
308 $crit_title = $item->getTranslatedType();
309 }
310
311 if (!in_array($crit_title, $this->title_columns)) {
312 $this->title_columns[] = $crit_title;
313 }
314 switch ($crit_type) {
315 case 'bool':
316 if ($values[$crit_id] == 1) {
317 $this->excel->setCell($row, $col, $this->lng->txt("yes"));
318 } elseif ($values[$crit_id] == -1) {
319 $this->excel->setCell($row, $col, $this->lng->txt("no"));
320 }
321 break;
322 case 'rating':
323 /*
324 * Get the rating data from the DB in the current less expensive way.
325 * assignment_id -> used in il_rating.obj_id
326 * object type as string -> used in il_rating.obj_type
327 * participant id -> il_rating.sub_obj_id
328 * "peer_" + criteria_id -> il_rating.sub_obj_type (peer or e.g. peer_12)
329 * peer id -> il_rating.user_id
330 */
331 // Possible TODO: refactor ilExAssignment->getPeerReviewCriteriaCatalogueItems somehow to avoid client
332 // classes to deal with ilExCriteria instances with persistence (by id) or instances on the fly (by type)
333 $sub_obj_type = "peer";
334 if ($crit_id) {
335 $sub_obj_type .= "_" . $crit_id;
336 }
338 $this->assignment->getId(),
339 'ass',
340 $participant_id,
341 $sub_obj_type,
342 $feedback_giver
343 );
344 if ($rating_int = round((int) $rating)) {
345 $this->excel->setCell($row, $col, $rating_int);
346 }
347 break;
348 case 'text':
349 //again another check for criteria id (if instantiated via type)
350 if ($crit_id) {
351 $this->excel->setCell($row, $col, $values[$crit_id]);
352 } else {
353 $this->excel->setCell($row, $col, $values['text']);
354 }
355 break;
356 case 'file':
357 if ($crit_id) {
359 $crit_file_obj = ilExcCriteriaFile::getInstanceById($crit_id);
360 } else {
361 $crit_file_obj = ilExcCriteriaFile::getInstanceByType($crit_type);
362 }
363 $crit_file_obj->setPeerReviewContext($this->assignment, $feedback_giver, $participant_id);
364 $files = $crit_file_obj->getFiles();
365
366 $extra_crit_column = 0;
367 foreach ($files as $file) {
368 if ($extra_crit_column !== 0) {
369 $this->title_columns[] = $crit_title . "_" . $extra_crit_column;
370 }
371 $extra_crit_column++;
372 $dir = $this->getFeedbackDirectory($participant_id, $feedback_giver);
373 $this->copyFileToSubDirectory($dir, $file);
374 $this->excel->setCell($row, $col, "./" . $dir . DIRECTORY_SEPARATOR . basename($file->getTitle()));
375 $this->excel->addLink($row, $col, './' . $dir . DIRECTORY_SEPARATOR . basename($file->getTitle()));
376 $this->excel->setColors($this->excel->getCoordByColumnAndRow($col, $row), self::BG_COLOR, self::LINK_COLOR);
377 }
378 break;
379 }
380 }
381 }
382
386 protected function getFeedbackDirectory(int $participant_id, int $feedback_giver): string
387 {
388 $dir = self::FBK_DIRECTORY . DIRECTORY_SEPARATOR .
389 "to_" . ilExSubmission::getDirectoryNameFromUserData($participant_id) . DIRECTORY_SEPARATOR .
390 "from_" . ilExSubmission::getDirectoryNameFromUserData($feedback_giver);
391 return $dir;
392 }
393
398 public function getExtraColumnsForSubmissionFiles(int $a_obj_id, int $a_ass_id): int
399 {
400 $subm = $this->domain->submission($a_ass_id);
401 return $subm->getMaxAmountOfSubmittedFiles(
402 $this->participant_id
403 );
404 }
405
406 // Mapping the links to use them on the excel.
407 public function addLink(
408 int $a_row,
409 int $a_col,
410 Submission $sub
411 ): void {
412 $user_id = $sub->getUserId();
414
415 $filepath = './' . $this->lng->txt("exc_ass_submission_zip") . DIRECTORY_SEPARATOR . $targetdir . DIRECTORY_SEPARATOR;
416 switch ($this->assignment->getType()) {
418 $filepath .= $sub->getTitle();
419 break;
420
422 $wsp_tree = new ilWorkspaceTree($user_id);
423 // #12939
424 if (!$wsp_tree->getRootId()) {
425 $wsp_tree->createTreeForUser($user_id);
426 }
427 $node = $wsp_tree->getNodeData((int) $sub->getTitle());
428 $filepath .= "blog_" . $node['obj_id'] . DIRECTORY_SEPARATOR . "index.html";
429 break;
430
432 $filepath .= "prt_" . $sub->getTitle() . DIRECTORY_SEPARATOR . "index.html";
433 break;
434
435 default:
436 $filepath = "";
437 }
438 $this->excel->addLink($a_row, $a_col, $filepath);
439 }
440
450 protected function collectAssignmentData(int $assignment_id): void
451 {
452 $ass_has_feedback = false;
453 $ass_has_criteria = false;
454
455 //assignment object
456 $this->assignment = new ilExAssignment($assignment_id);
457 $assignment_type = $this->assignment->getType();
458
459 //Sanitized title for excel file and target directory.
460 $this->sanitized_title = ilFileUtils::getASCIIFilename($this->assignment->getTitle());
461
462 // directories
463 if (!isset($this->temp_dir)) {
464 $this->createUniqueTempDirectory();
465 }
466 $this->createTargetDirectory();
467
468 //Collect submission files if needed by assignment type.
469 if (in_array($assignment_type, $this->ass_types_with_files)) {
470 $this->createSubmissionsDirectory();
471 $this->collectSubmissionFiles();
472 }
473
474 $first_excel_column_for_review = 0;
475 $col = 0;
476 $peer_review = null;
477 if ($this->assignment->getPeerReview()) {
478 $ass_has_feedback = true;
479 //obj to get the reviews in the foreach below.
480 $peer_review = new ilExPeerReview($this->assignment);
481 //default start column for revisions.
482 $first_excel_column_for_review = self::FIRST_DEFAULT_REVIEW_COLUMN;
483 }
484
485 if ($this->isExcelNeeded($assignment_type, $ass_has_feedback)) {
486 // PhpSpreadsheet object
487 $this->excel = new ilExcel();
488
489 //Excel sheet title
490 $this->excel->addSheet($this->sanitized_title);
491
492 //add common excel Columns
493 #25585
494 $this->title_columns = array(
495 $this->lng->txt('lastname'),
496 $this->lng->txt('firstname'),
497 $this->lng->txt('login'),
498 $this->lng->txt('exc_last_submission')
499 );
500 switch ($assignment_type) {
502 $this->title_columns[] = $this->lng->txt("exc_submission_text");
503 break;
506 if ($assignment_type === ilExAssignment::TYPE_UPLOAD_TEAM) {
507 $first_excel_column_for_review++;
508 $this->title_columns[] = $this->lng->txt("exc_team");
509 }
510 $num_columns_submission = $this->getExtraColumnsForSubmissionFiles($this->exercise_id, $assignment_id);
511 if ($num_columns_submission > 1) {
512 for ($i = 1; $i <= $num_columns_submission; $i++) {
513 $this->title_columns[] = $this->lng->txt("exc_submission_file") . " " . $i;
514 }
515 } else {
516 $this->title_columns[] = $this->lng->txt("exc_submission_file");
517 }
518
519 $first_excel_column_for_review += $num_columns_submission - 1;
520 break;
521 default:
522 $this->title_columns[] = $this->lng->txt("exc_submission");
523 break;
524 }
525 if ($ass_has_feedback) {
526 $this->title_columns[] = $this->lng->txt("exc_peer_review_giver");
527 $this->title_columns[] = $this->lng->txt('exc_last_submission');
528 }
529
530 //criteria
531 //Notice:getPeerReviewCriteriaCatalogueItems can return just an empty instance without data.
532 if ($this->criteria_items = $this->assignment->getPeerReviewCriteriaCatalogueItems()) {
533 $ass_has_criteria = true;
534 }
535
536 if ($this->participant_id > 0) {
537 $participants = array($this->participant_id);
538 } else {
539 $participants = $this->getAssignmentMembersIds();
540 }
541
542 $filter = new ilExerciseMembersFilter($this->exercise_ref_id, $participants, $this->user_id);
543 $participants = $filter->filterParticipantsByAccess();
544
545 $row = 2;
546 // Fill the excel
547 foreach ($participants as $participant_id) {
548 if (!is_null($this->selected_participants) && !in_array($participant_id, $this->selected_participants)) {
549 continue;
550 }
551
552 $submissions = $this->domain->submission($this->assignment->getId())
553 ->getSubmissionsOfUser($participant_id);
554
555 if ($submissions->current()) {
556 $participant_name = ilObjUser::_lookupName($participant_id);
557 $this->excel->setCell($row, self::PARTICIPANT_LASTNAME_COLUMN, $participant_name['lastname']);
558 $this->excel->setCell($row, self::PARTICIPANT_FIRSTNAME_COLUMN, $participant_name['firstname']);
559 $this->excel->setCell($row, self::PARTICIPANT_LOGIN_COLUMN, $participant_name['login']);
560
561 //Get the submission Text
562 if (!in_array($assignment_type, $this->ass_types_with_files)) {
563 foreach ($submissions as $sub) {
564 $this->excel->setCell($row, self::SUBMISSION_DATE_COLUMN, $sub->getTimestamp());
565 $this->excel->setCell($row, self::FIRST_DEFAULT_SUBMIT_COLUMN, $sub->getText());
566 }
567 } else {
568 $col = self::FIRST_DEFAULT_SUBMIT_COLUMN;
569 if ($assignment_type === ilExAssignment::TYPE_UPLOAD_TEAM) {
570 $team_id = $this->team->getTeamForMember($this->assignment->getId(), $participant_id);
571 $this->excel->setCell($row, $col, (string) $team_id);
572 $col++;
573 }
574 foreach ($submissions as $sub) {
575 $this->excel->setCell($row, self::SUBMISSION_DATE_COLUMN, $sub->getTimestamp());
576
577 if ($assignment_type == ilExAssignment::TYPE_PORTFOLIO || $assignment_type == ilExAssignment::TYPE_BLOG) {
578 $this->excel->setCell($row, $col, $this->lng->txt("open"));
579 } else {
580 $this->excel->setCell($row, $col, $sub->getTitle());
581 }
582 $this->excel->setColors($this->excel->getCoordByColumnAndRow($col, $row), self::BG_COLOR, self::LINK_COLOR);
583 if ($assignment_type != ilExAssignment::TYPE_UPLOAD_TEAM) {
584 $this->addLink($row, $col, $sub);
585 }
586 $col++; //does not affect blogs and portfolios.
587 }
588 }
589
590 if ($ass_has_feedback) {
591 if ($col < $first_excel_column_for_review) {
592 $col = $first_excel_column_for_review;
593 }
594 $reviews = [];
595 if ($peer_review !== null) {
596 $reviews = $peer_review->getPeerReviewsByPeerId($participant_id);
597 }
598
599 //extra lines
600 $current_review_row = 0;
601 foreach ($reviews as $review) {
602 //not all reviews are done, we check it via date of review.
603 if ($review['tstamp']) {
604 $current_review_row++;
605 if ($current_review_row > 1) {
606 for ($i = 0; $i < $first_excel_column_for_review; $i++) {
607 $cell_to_copy = $this->excel->getCell($row, $i);
608 $this->excel->setCell($row + 1, $i, $cell_to_copy);
609 if ($i >= self::FIRST_DEFAULT_SUBMIT_COLUMN) {
610 $this->excel->setColors($this->excel->getCoordByColumnAndRow($i, $row + 1), self::BG_COLOR, self::LINK_COLOR);
611 }
612 }
613 ++$row;
614 }
615
616 $feedback_giver = $review['giver_id']; // user who made the review.
617
618 $feedback_giver_name = ilObjUser::_lookupName($feedback_giver);
619
620 $this->excel->setCell(
621 $row,
622 $col,
623 $feedback_giver_name['lastname'] . ", " . $feedback_giver_name['firstname'] . " [" . $feedback_giver_name['login'] . "]"
624 );
625
626 $this->excel->setCell($row, $col + 1, $review['tstamp']);
627
628 if ($ass_has_criteria) {
629 $this->addCriteriaToExcel($feedback_giver, $participant_id, $row, $col + 1);
630 }
631 }
632 }
633 }
634
635 $row++;
636 }
637 }
638
639 $this->addColumnTitles();
640 $this->excel->writeToFile($this->target_directory . "/" . $this->sanitized_title);
641 }
642 }
643
648 public function getAssignmentMembersIds(): array
649 {
650 global $DIC;
651
652 $ilDB = $DIC->database();
653 $members = array();
654
655 $set = $ilDB->query("SELECT usr_id" .
656 " FROM exc_mem_ass_status" .
657 " WHERE ass_id = " . $ilDB->quote($this->assignment->getId(), "integer"));
658
659 while ($rec = $ilDB->fetchAssoc($set)) {
660 if (!\ilObjUser::userExists([(int) $rec['usr_id']])) {
661 continue;
662 }
663 $members[] = $rec['usr_id'];
664 }
665
666 return $members;
667 }
668}
$out
Definition: buildRTE.php:24
Exercise assignment.
static getInstancesByExercise(int $a_exc_id)
const TYPE_UPLOAD
direct checks against const should be avoided, use type objects instead
Exercise peer review.
Exercise submission //TODO: This class has many static methods related to delivered "files".
static getDirectoryNameFromUserData(int $a_user_id)
static getInstanceById(int $a_id)
static getInstanceByType(string $a_type)
run(array $input, Observer $observer)
run the job
ILIAS Exercise PeerReview DomainService $peer_review
getExtraColumnsForSubmissionFiles(int $a_obj_id, int $a_ass_id)
Get the number of max amount of files submitted by a single user in the assignment.
collectSubmissionFiles()
Store the zip file which contains all submission files in the target directory.
getFeedbackDirectory(int $participant_id, int $feedback_giver)
see also bug https://mantis.ilias.de/view.php?id=30999
copyFileToSubDirectory(string $a_directory, \ILIAS\Exercise\PeerReview\Criteria\CriteriaFile $file)
Copy a file in the Feedback_files directory TODO use the new filesystem.
getAssignmentMembersIds()
get ONLY the members ids for this assignment
collectAssignmentData(int $assignment_id)
write assignment data to excel file
createTargetDirectory()
Create the directory with the assignment title.
createSubmissionsDirectory()
Create the directory with the assignment title.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
static getASCIIFilename(string $a_filename)
static ilTempnam(?string $a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
static createDirectory(string $a_dir, int $a_mod=0755)
create directory
language handling
Component logger with individual log levels by component id.
Class ilObjExercise.
static userExists(array $a_usr_ids=[])
static _lookupName(int $a_user_id)
static getRatingForUserAndObject(int $a_obj_id, string $a_obj_type, int $a_sub_obj_id, string $a_sub_obj_type, int $a_user_id, ?int $a_category_id=null)
Get rating for a user and an object.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$path
Definition: ltiservices.php:30
getValue()
Get the value that is displayed in the input client side.
Definition: Group.php:49
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
global $DIC
Definition: shib_login.php:26