ILIAS  release_8 Revision v8.24
class.ilExerciseManagementCollectFilesJob.php
Go to the documentation of this file.
1<?php
2
26
31{
32 public const FBK_DIRECTORY = "Feedback_files";
33 public const LINK_COLOR = "0,0,255";
34 public const BG_COLOR = "255,255,255";
35 //Column number incremented in ilExcel
38 public const PARTICIPANT_LOGIN_COLUMN = 2;
39 public const SUBMISSION_DATE_COLUMN = 3;
42
43 protected ilLogger $logger;
44 protected string $target_directory = "";
45 protected string $submissions_directory = "";
47 protected int $user_id = 0;
48 protected int $exercise_id = 0;
49 protected int $exercise_ref_id = 0;
50 protected ?string $temp_dir = null;
51 protected ilLanguage $lng;
52 protected string $sanitized_title = ""; //sanitized file name/sheet title
53 protected ilExcel $excel;
54 protected array $criteria_items = [];
55 protected array $title_columns = [];
56 protected array $ass_types_with_files = []; //TODO will be deprecated when use the new assignment type interface
57 protected int $participant_id = 0;
58
62 public function __construct()
63 {
64 global $DIC;
65 $this->lng = $DIC->language();
66 $this->lng->loadLanguageModule('exc');
67 //TODO will be deprecated when use the new assignment type interface
68 $this->ass_types_with_files = array(
73 );
75 $this->logger = $DIC->logger()->exc();
76 }
77
81 public function getInputTypes(): array
82 {
83 return
84 [
85 new SingleType(IntegerValue::class),
86 new SingleType(IntegerValue::class),
87 new SingleType(IntegerValue::class),
88 new SingleType(IntegerValue::class),
89 new SingleType(IntegerValue::class)
90 ];
91 }
92
93 public function getOutputType(): Type
94 {
95 return new SingleType(StringValue::class);
96 }
97
98 public function isStateless(): bool
99 {
100 return true;
101 }
102
111 public function run(
112 array $input,
113 Observer $observer
114 ): Value {
115 $this->exercise_id = $input[0]->getValue();
116 $this->exercise_ref_id = $input[1]->getValue();
117 $assignment_id = $input[2]->getValue();
118 $participant_id = $input[3]->getValue();
119 $this->user_id = $input[4]->getValue();
120 $final_directory = "";
121
122 //if we have assignment
123 if ($assignment_id > 0) {
124 $this->collectAssignmentData($assignment_id);
125 $final_directory = $this->target_directory;
126 }
127
128 if ($participant_id > 0) {
129 $this->participant_id = $participant_id;
130 $assignments = ilExAssignment::getInstancesByExercise($this->exercise_id);
131 foreach ($assignments as $assignment) {
132 $this->collectAssignmentData($assignment->getId());
133 }
134 $final_directory = $this->temp_dir . DIRECTORY_SEPARATOR . ilExSubmission::getDirectoryNameFromUserData($participant_id);
135 }
136
137 $out = new StringValue();
138 $out->setValue($final_directory);
139 return $out;
140 }
141
146 public function copyFileToSubDirectory(string $a_directory, string $a_file): void
147 {
148 $dir = $this->target_directory . "/" . $a_directory;
149
150 if (!is_dir($dir)) {
152 }
153
154 copy($a_file, $dir . "/" . basename($a_file));
155
156 /*global $DIC;
157 $fs = $DIC->filesystem();
158
159 $fs->storage()->copy($a_file, $this->temp_dir."/".basename($a_file));*/
160 }
161
163 {
164 return 30;
165 }
166
171 protected function addColumnTitles(): void
172 {
173 $col = 0;
174 foreach ($this->title_columns as $title) {
175 $this->excel->setCell(1, $col, $title);
176 $col++;
177 }
178 }
179
184 protected function createUniqueTempDirectory(): void
185 {
186 $this->temp_dir = ilFileUtils::ilTempnam();
187 ilFileUtils::makeDirParents($this->temp_dir);
188 }
189
193 protected function createTargetDirectory(): void
194 {
195 $path = $this->temp_dir . DIRECTORY_SEPARATOR;
196 if ($this->participant_id > 0) {
197 $user_dir = ilExSubmission::getDirectoryNameFromUserData($this->participant_id);
198 $path .= $user_dir . DIRECTORY_SEPARATOR;
199 }
200 $this->target_directory = $path . $this->sanitized_title;
201 ilFileUtils::makeDirParents($this->target_directory);
202 }
203
207 protected function createSubmissionsDirectory(): void
208 {
209 $this->logger->dump("lang key => " . $this->lng->getLangKey());
210 $this->submissions_directory = $this->target_directory . DIRECTORY_SEPARATOR . $this->lng->txt("exc_ass_submission_zip");
211 ilFileUtils::createDirectory($this->submissions_directory);
212 }
213
222 public function collectSubmissionFiles(): void
223 {
224 $members = array();
225
226 $exercise = new ilObjExercise($this->exercise_id, false);
227
228 if ($this->participant_id > 0) {
229 $exc_members_id = array($this->participant_id);
230 } else {
231 $exc_members_id = $exercise->members_obj->getMembers();
232 }
233
234 $filter = new ilExerciseMembersFilter($this->exercise_ref_id, $exc_members_id, $this->user_id);
235 $exc_members_id = $filter->filterParticipantsByAccess();
236
237 foreach ($exc_members_id as $member_id) {
238 $submission = new ilExSubmission($this->assignment, $member_id);
239 $submission->updateTutorDownloadTime();
240
241 // get member object (ilObjUser)
242 if (ilObject::_exists($member_id)) {
243 // adding file metadata
244 foreach ($submission->getFiles() as $file) {
245 $members[$file["user_id"]]["files"][$file["returned_id"]] = $file;
246 }
247
249 $tmp_obj = ilObjectFactory::getInstanceByObjId($member_id);
250 $members[$member_id]["name"] = $tmp_obj->getFirstname() . " " . $tmp_obj->getLastname();
251 unset($tmp_obj);
252 }
253 }
254 ilExSubmission::downloadAllAssignmentFiles($this->assignment, $members, $this->submissions_directory);
255 }
256
257 protected function isExcelNeeded(int $a_ass_type, bool $a_has_fbk): bool
258 {
259 if ($a_ass_type == ilExAssignment::TYPE_TEXT || $a_ass_type == ilExAssignment::TYPE_UPLOAD
260 || $a_ass_type == ilExAssignment::TYPE_UPLOAD_TEAM) {
261 return true;
262 } elseif ($a_has_fbk && $a_ass_type != ilExAssignment::TYPE_UPLOAD_TEAM) {
263 return true;
264 }
265 return false;
266 }
267
272 protected function addCriteriaToExcel(
273 int $feedback_giver,
274 int $participant_id,
275 int $row,
276 int $col
277 ): void {
278 $submission = new ilExSubmission($this->assignment, $participant_id);
279
280 //Possible TODO: This getPeerReviewValues doesn't return always the same array structure then the client classes have
281 //to deal with this. Use only one data structure will avoid this extra work.
282 //values can be [19] => "blablablab" or ["text"] => "blablabla"
283 $values = $submission->getPeerReview()->getPeerReviewValues($feedback_giver, $participant_id);
284
285 foreach ($this->criteria_items as $item) {
286 $col++;
287
288 //Criteria without catalog doesn't have ID nor TITLE. The criteria instance is given via "type" ilExcCriteria::getInstanceByType
289 $crit_id = $item->getId();
290 $crit_type = $item->getType();
291 $crit_title = $item->getTitle();
292 if ($crit_title == "") {
293 $crit_title = $item->getTranslatedType();
294 }
295
296 if (!in_array($crit_title, $this->title_columns)) {
297 $this->title_columns[] = $crit_title;
298 }
299 switch ($crit_type) {
300 case 'bool':
301 if ($values[$crit_id] == 1) {
302 $this->excel->setCell($row, $col, $this->lng->txt("yes"));
303 } elseif ($values[$crit_id] == -1) {
304 $this->excel->setCell($row, $col, $this->lng->txt("no"));
305 }
306 break;
307 case 'rating':
308 /*
309 * Get the rating data from the DB in the current less expensive way.
310 * assignment_id -> used in il_rating.obj_id
311 * object type as string -> used in il_rating.obj_type
312 * participant id -> il_rating.sub_obj_id
313 * "peer_" + criteria_id -> il_rating.sub_obj_type (peer or e.g. peer_12)
314 * peer id -> il_rating.user_id
315 */
316 // Possible TODO: refactor ilExAssignment->getPeerReviewCriteriaCatalogueItems somehow to avoid client
317 // classes to deal with ilExCriteria instances with persistence (by id) or instances on the fly (by type)
318 $sub_obj_type = "peer";
319 if ($crit_id) {
320 $sub_obj_type .= "_" . $crit_id;
321 }
323 $this->assignment->getId(),
324 'ass',
325 $participant_id,
326 $sub_obj_type,
327 $feedback_giver
328 );
329 if ($rating_int = round((int) $rating)) {
330 $this->excel->setCell($row, $col, $rating_int);
331 }
332 break;
333 case 'text':
334 //again another check for criteria id (if instantiated via type)
335 if ($crit_id) {
336 $this->excel->setCell($row, $col, $values[$crit_id]);
337 } else {
338 $this->excel->setCell($row, $col, $values['text']);
339 }
340 break;
341 case 'file':
342 if ($crit_id) {
344 $crit_file_obj = ilExcCriteriaFile::getInstanceById($crit_id);
345 } else {
346 $crit_file_obj = ilExcCriteriaFile::getInstanceByType($crit_type);
347 }
348 $crit_file_obj->setPeerReviewContext($this->assignment, $feedback_giver, $participant_id);
349 $files = $crit_file_obj->getFiles();
350
351 $extra_crit_column = 0;
352 foreach ($files as $file) {
353 if ($extra_crit_column !== 0) {
354 $this->title_columns[] = $crit_title . "_" . $extra_crit_column;
355 }
356 $extra_crit_column++;
357 $dir = $this->getFeedbackDirectory($participant_id, $feedback_giver);
358 $this->copyFileToSubDirectory($dir, $file);
359 $this->excel->setCell($row, $col, "./" . $dir . DIRECTORY_SEPARATOR . basename($file));
360 $this->excel->addLink($row, $col, './' . $dir . DIRECTORY_SEPARATOR . basename($file));
361 $this->excel->setColors($this->excel->getCoordByColumnAndRow($col, $row), self::BG_COLOR, self::LINK_COLOR);
362 }
363 break;
364 }
365 }
366 }
367
371 protected function getFeedbackDirectory(int $participant_id, int $feedback_giver): string
372 {
373 $dir = self::FBK_DIRECTORY . DIRECTORY_SEPARATOR .
374 "to_" . ilExSubmission::getDirectoryNameFromUserData($participant_id) . DIRECTORY_SEPARATOR .
375 "from_" . ilExSubmission::getDirectoryNameFromUserData($feedback_giver);
376 return $dir;
377 }
378
383 public function getExtraColumnsForSubmissionFiles(int $a_obj_id, int $a_ass_id): int
384 {
385 global $DIC;
386 $ilDB = $DIC->database();
387
388 $and = "";
389 if ($this->participant_id > 0) {
390 $and = " AND user_id = " . $this->participant_id;
391 }
392
393 $query = "SELECT MAX(max_num) AS max" .
394 " FROM (SELECT COUNT(user_id) AS max_num FROM exc_returned" .
395 " WHERE obj_id=" . $a_obj_id . ". AND ass_id=" . $a_ass_id . $and . " AND mimetype IS NOT NULL" .
396 " GROUP BY user_id) AS COUNTS";
397
398 $set = $ilDB->query($query);
399 $row = $ilDB->fetchAssoc($set);
400 return (int) $row['max'];
401 }
402
403 // Mapping the links to use them on the excel.
404 public function addLink(
405 int $a_row,
406 int $a_col,
407 array $a_submission_file
408 ): void {
409 $user_id = $a_submission_file['user_id'];
411
412 $filepath = './' . $this->lng->txt("exc_ass_submission_zip") . DIRECTORY_SEPARATOR . $targetdir . DIRECTORY_SEPARATOR;
413 switch ($this->assignment->getType()) {
415 $filepath .= $a_submission_file['filetitle'];
416 break;
417
419 $wsp_tree = new ilWorkspaceTree($user_id);
420 // #12939
421 if (!$wsp_tree->getRootId()) {
422 $wsp_tree->createTreeForUser($user_id);
423 }
424 $node = $wsp_tree->getNodeData((int) $a_submission_file['filetitle']);
425 $filepath .= "blog_" . $node['obj_id'] . DIRECTORY_SEPARATOR . "index.html";
426 break;
427
429 $filepath .= "prt_" . $a_submission_file['filetitle'] . DIRECTORY_SEPARATOR . "index.html";
430 break;
431
432 default:
433 $filepath = "";
434 }
435 $this->excel->addLink($a_row, $a_col, $filepath);
436 }
437
447 protected function collectAssignmentData(int $assignment_id): void
448 {
449 $ass_has_feedback = false;
450 $ass_has_criteria = false;
451
452 //assignment object
453 $this->assignment = new ilExAssignment($assignment_id);
454 $assignment_type = $this->assignment->getType();
455
456 //Sanitized title for excel file and target directory.
457 $this->sanitized_title = ilFileUtils::getASCIIFilename($this->assignment->getTitle());
458
459 // directories
460 if (!isset($this->temp_dir)) {
461 $this->createUniqueTempDirectory();
462 }
463 $this->createTargetDirectory();
464
465 //Collect submission files if needed by assignment type.
466 if (in_array($assignment_type, $this->ass_types_with_files)) {
467 $this->createSubmissionsDirectory();
468 $this->collectSubmissionFiles();
469 }
470
471 $first_excel_column_for_review = 0;
472 $col = 0;
473 $peer_review = null;
474 if ($this->assignment->getPeerReview()) {
475 $ass_has_feedback = true;
476 //obj to get the reviews in the foreach below.
477 $peer_review = new ilExPeerReview($this->assignment);
478 //default start column for revisions.
479 $first_excel_column_for_review = self::FIRST_DEFAULT_REVIEW_COLUMN;
480 }
481
482 if ($this->isExcelNeeded($assignment_type, $ass_has_feedback)) {
483 // PhpSpreadsheet object
484 $this->excel = new ilExcel();
485
486 //Excel sheet title
487 $this->excel->addSheet($this->sanitized_title);
488
489 //add common excel Columns
490 #25585
491 $this->title_columns = array(
492 $this->lng->txt('lastname'),
493 $this->lng->txt('firstname'),
494 $this->lng->txt('login'),
495 $this->lng->txt('exc_last_submission')
496 );
497 switch ($assignment_type) {
499 $this->title_columns[] = $this->lng->txt("exc_submission_text");
500 break;
503 if ($assignment_type === ilExAssignment::TYPE_UPLOAD_TEAM) {
504 $first_excel_column_for_review++;
505 $this->title_columns[] = $this->lng->txt("exc_team");
506 }
507 $num_columns_submission = $this->getExtraColumnsForSubmissionFiles($this->exercise_id, $assignment_id);
508 if ($num_columns_submission > 1) {
509 for ($i = 1; $i <= $num_columns_submission; $i++) {
510 $this->title_columns[] = $this->lng->txt("exc_submission_file") . " " . $i;
511 }
512 } else {
513 $this->title_columns[] = $this->lng->txt("exc_submission_file");
514 }
515
516 $first_excel_column_for_review += $num_columns_submission - 1;
517 break;
518 default:
519 $this->title_columns[] = $this->lng->txt("exc_submission");
520 break;
521 }
522 if ($ass_has_feedback) {
523 $this->title_columns[] = $this->lng->txt("exc_peer_review_giver");
524 $this->title_columns[] = $this->lng->txt('exc_last_submission');
525 }
526
527 //criteria
528 //Notice:getPeerReviewCriteriaCatalogueItems can return just an empty instance without data.
529 if ($this->criteria_items = $this->assignment->getPeerReviewCriteriaCatalogueItems()) {
530 $ass_has_criteria = true;
531 }
532
533 if ($this->participant_id > 0) {
534 $participants = array($this->participant_id);
535 } else {
536 $participants = $this->getAssignmentMembersIds();
537 }
538
539 $filter = new ilExerciseMembersFilter($this->exercise_ref_id, $participants, $this->user_id);
540 $participants = $filter->filterParticipantsByAccess();
541
542 $row = 2;
543 // Fill the excel
544 foreach ($participants as $participant_id) {
545 $submission = new ilExSubmission($this->assignment, $participant_id);
546 $submission_files = $submission->getFiles();
547
548 if ($submission_files !== []) {
549 $participant_name = ilObjUser::_lookupName($participant_id);
550 $this->excel->setCell($row, self::PARTICIPANT_LASTNAME_COLUMN, $participant_name['lastname']);
551 $this->excel->setCell($row, self::PARTICIPANT_FIRSTNAME_COLUMN, $participant_name['firstname']);
552 $this->excel->setCell($row, self::PARTICIPANT_LOGIN_COLUMN, $participant_name['login']);
553
554 //Get the submission Text
555 if (!in_array($assignment_type, $this->ass_types_with_files)) {
556 foreach ($submission_files as $submission_file) {
557 $this->excel->setCell($row, self::SUBMISSION_DATE_COLUMN, $submission_file['timestamp']);
558 $this->excel->setCell($row, self::FIRST_DEFAULT_SUBMIT_COLUMN, $submission_file['atext']);
559 }
560 } else {
561 $col = self::FIRST_DEFAULT_SUBMIT_COLUMN;
562 if ($assignment_type === ilExAssignment::TYPE_UPLOAD_TEAM) {
563 $team_id = ilExAssignmentTeam::getTeamId($this->assignment->getId(), $participant_id, false);
564 $this->excel->setCell($row, $col, (string) $team_id);
565 $col++;
566 }
567 foreach ($submission_files as $submission_file) {
568 $this->excel->setCell($row, self::SUBMISSION_DATE_COLUMN, $submission_file['timestamp']);
569
570 if ($assignment_type == ilExAssignment::TYPE_PORTFOLIO || $assignment_type == ilExAssignment::TYPE_BLOG) {
571 $this->excel->setCell($row, $col, $this->lng->txt("open"));
572 } else {
573 $this->excel->setCell($row, $col, $submission_file['filetitle']);
574 }
575 $this->excel->setColors($this->excel->getCoordByColumnAndRow($col, $row), self::BG_COLOR, self::LINK_COLOR);
576 if ($assignment_type != ilExAssignment::TYPE_UPLOAD_TEAM) {
577 $this->addLink($row, $col, $submission_file);
578 }
579 $col++; //does not affect blogs and portfolios.
580 }
581 }
582
583 if ($ass_has_feedback) {
584 if ($col < $first_excel_column_for_review) {
585 $col = $first_excel_column_for_review;
586 }
587 $reviews = [];
588 if ($peer_review !== null) {
589 $reviews = $peer_review->getPeerReviewsByPeerId($participant_id);
590 }
591
592 //extra lines
593 $current_review_row = 0;
594 foreach ($reviews as $review) {
595 //not all reviews are done, we check it via date of review.
596 if ($review['tstamp']) {
597 $current_review_row++;
598 if ($current_review_row > 1) {
599 for ($i = 0; $i < $first_excel_column_for_review; $i++) {
600 $cell_to_copy = $this->excel->getCell($row, $i);
601 $this->excel->setCell($row + 1, $i, $cell_to_copy);
602 if ($i >= self::FIRST_DEFAULT_SUBMIT_COLUMN) {
603 $this->excel->setColors($this->excel->getCoordByColumnAndRow($i, $row + 1), self::BG_COLOR, self::LINK_COLOR);
604 }
605 }
606 ++$row;
607 }
608
609 $feedback_giver = $review['giver_id']; // user who made the review.
610
611 $feedback_giver_name = ilObjUser::_lookupName($feedback_giver);
612
613 $this->excel->setCell(
614 $row,
615 $col,
616 $feedback_giver_name['lastname'] . ", " . $feedback_giver_name['firstname'] . " [" . $feedback_giver_name['login'] . "]"
617 );
618
619 $this->excel->setCell($row, $col + 1, $review['tstamp']);
620
621 if ($ass_has_criteria) {
622 $this->addCriteriaToExcel($feedback_giver, $participant_id, $row, $col + 1);
623 }
624 }
625 }
626 }
627
628 $row++;
629 }
630 }
631
632 $this->addColumnTitles();
633 $this->excel->writeToFile($this->target_directory . "/" . $this->sanitized_title);
634 }
635 }
636
641 public function getAssignmentMembersIds(): array
642 {
643 global $DIC;
644
645 $ilDB = $DIC->database();
646 $members = array();
647
648 $set = $ilDB->query("SELECT usr_id" .
649 " FROM exc_mem_ass_status" .
650 " WHERE ass_id = " . $ilDB->quote($this->assignment->getId(), "integer"));
651
652 while ($rec = $ilDB->fetchAssoc($set)) {
653 if (!\ilObjUser::userExists([(int) $rec['usr_id']])) {
654 continue;
655 }
656 $members[] = $rec['usr_id'];
657 }
658
659 return $members;
660 }
661}
$out
Definition: buildRTE.php:24
static getTeamId(int $a_assignment_id, int $a_user_id, bool $a_create_on_demand=false)
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.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static downloadAllAssignmentFiles(ilExAssignment $a_ass, array $members, string $to_path)
Download all submitted files of an assignment (all user)
static getDirectoryNameFromUserData(int $a_user_id)
static getInstanceById(int $a_id)
static getInstanceByType(string $a_type)
copyFileToSubDirectory(string $a_directory, string $a_file)
Copy a file in the Feedback_files directory TODO use the new filesystem.
run(array $input, Observer $observer)
run the job
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.
getFeedbackDirectory(int $participant_id, int $feedback_giver)
see also bug https://mantis.ilias.de/view.php?id=30999
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.
addLink(int $a_row, int $a_col, array $a_submission_file)
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 _lookupName(int $a_user_id)
lookup user name
static userExists(array $a_usr_ids=array())
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
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...
global $DIC
Definition: feed.php:28
$path
Definition: ltiservices.php:32
$i
Definition: metadata.php:41
getValue()
Get the value that is displayed in the input client side.
Definition: Group.php:47
$query