ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilExerciseManagementCollectFilesJob.php
Go to the documentation of this file.
1<?php
8
9/* Copyright (c) 1998-2018 ILIAS open source, Extended GPL, see docs/LICENSE */
10
18{
22 private $logger = null;
28 protected $assignment;
29 protected $user_id;
30 protected $exercise_id;
32 protected $temp_dir;
33 protected $lng;
34 protected $sanitized_title; //sanitized file name/sheet title
35 protected $excel; //ilExcel
36 protected $criteria_items; //array
37 protected $title_columns;
38 protected $ass_types_with_files; //TODO will be deprecated when use the new assignment type interface
39 protected $participant_id;
40
41 const FBK_DIRECTORY = "Feedback_files";
42 const LINK_COLOR = "0,0,255";
43 const BG_COLOR = "255,255,255";
44 //Column number incremented in ilExcel
51
55 public function __construct()
56 {
57 global $DIC;
58 $this->lng = $DIC->language();
59 $this->lng->loadLanguageModule('exc');
60 //TODO will be deprecated when use the new assignment type interface
61 $this->ass_types_with_files = array(
66 );
67 $this->logger = $DIC->logger()->exc();
68 }
69
73 public function getInputTypes()
74 {
75 return
76 [
77 new SingleType(IntegerValue::class),
78 new SingleType(IntegerValue::class),
79 new SingleType(IntegerValue::class),
80 new SingleType(IntegerValue::class),
81 new SingleType(IntegerValue::class)
82 ];
83 }
84
88 public function getOutputType()
89 {
90 return new SingleType(StringValue::class);
91 }
92
93 public function isStateless()
94 {
95 return true;
96 }
97
104 public function run(array $input, Observer $observer)
105 {
106 $this->exercise_id = $input[0]->getValue();
107 $this->exercise_ref_id = $input[1]->getValue();
108 $assignment_id = $input[2]->getValue();
109 $participant_id = $input[3]->getValue();
110 $this->user_id = $input[4]->getValue();
111
112 //if we have assignment
113 if ($assignment_id > 0) {
114 $this->collectAssignmentData($assignment_id);
115 $final_directory = $this->target_directory;
116 }
117
118 if ($participant_id > 0) {
119 $this->participant_id = $participant_id;
120 $assignments = ilExAssignment::getInstancesByExercise($this->exercise_id);
121 foreach ($assignments as $assignment) {
122 $this->collectAssignmentData($assignment->getId());
123 }
124 $final_directory = $this->temp_dir . DIRECTORY_SEPARATOR . ilExSubmission::getDirectoryNameFromUserData($participant_id);
125 }
126
127 $out = new StringValue();
128 $out->setValue($final_directory);
129 return $out;
130 }
131
138 public function copyFileToSubDirectory($a_directory, $a_file)
139 {
140 $dir = $this->target_directory . "/" . $a_directory;
141
142 if (!is_dir($dir)) {
144 }
145
146 copy($a_file, $dir . "/" . basename($a_file));
147
148 /*global $DIC;
149 $fs = $DIC->filesystem();
150
151 $fs->storage()->copy($a_file, $this->temp_dir."/".basename($a_file));*/
152 }
153
158 {
159 return 30;
160 }
161
165 protected function addColumnTitles()
166 {
167 $col = 0;
168 foreach ($this->title_columns as $title) {
169 $this->excel->setCell(1, $col, $title);
170 $col++;
171 }
172 }
173
178 protected function createUniqueTempDirectory()
179 {
180 $this->temp_dir = ilUtil::ilTempnam();
181 ilUtil::makeDirParents($this->temp_dir);
182 }
183
187 protected function createTargetDirectory()
188 {
189 $path = $this->temp_dir . DIRECTORY_SEPARATOR;
190 if ($this->participant_id > 0) {
191 $user_dir = ilExSubmission::getDirectoryNameFromUserData($this->participant_id);
192 $path .= $user_dir . DIRECTORY_SEPARATOR;
193 }
194 $this->target_directory = $path . $this->sanitized_title;
195
196 ilUtil::makeDirParents($this->target_directory);
197 }
201 protected function createSubmissionsDirectory()
202 {
203 $this->logger->dump("lang key => " . $this->lng->getLangKey());
204 $this->submissions_directory = $this->target_directory . DIRECTORY_SEPARATOR . $this->lng->txt("exc_ass_submission_zip");
205 ilUtil::createDirectory($this->submissions_directory);
206 }
207
213 public function collectSubmissionFiles()
214 {
215 $members = array();
216
217 $exercise = new ilObjExercise($this->exercise_id, false);
218
219 if ($this->participant_id > 0) {
220 $exc_members_id = array($this->participant_id);
221 } else {
222 $exc_members_id = $exercise->members_obj->getMembers();
223 }
224
225 $filter = new ilExerciseMembersFilter($this->exercise_ref_id, $exc_members_id, $this->user_id);
226 $exc_members_id = $filter->filterParticipantsByAccess();
227
228 foreach ($exc_members_id as $member_id) {
229 $submission = new ilExSubmission($this->assignment, $member_id);
230 $submission->updateTutorDownloadTime();
231
232 // get member object (ilObjUser)
233 if (ilObject::_exists($member_id)) {
234 // adding file metadata
235 foreach ($submission->getFiles() as $file) {
236 $members[$file["user_id"]]["files"][$file["returned_id"]] = $file;
237 }
238
239 $tmp_obj = &ilObjectFactory::getInstanceByObjId($member_id);
240 $members[$member_id]["name"] = $tmp_obj->getFirstname() . " " . $tmp_obj->getLastname();
241 unset($tmp_obj);
242 }
243 }
244
245 ilExSubmission::downloadAllAssignmentFiles($this->assignment, $members, $this->submissions_directory);
246 }
247
253 protected function isExcelNeeded($a_ass_type, $a_has_fbk)
254 {
255 if ($a_ass_type == ilExAssignment::TYPE_TEXT) {
256 return true;
257 } elseif ($a_has_fbk && $a_ass_type != ilExAssignment::TYPE_UPLOAD_TEAM) {
258 return true;
259 }
260 return false;
261 }
262
270 protected function addCriteriaToExcel($feedback_giver, $participant_id, $row, $col)
271 {
272 $submission = new ilExSubmission($this->assignment, $participant_id);
273
274 //Possible TODO: This getPeerReviewValues doesn't return always the same array structure then the client classes have
275 //to deal with this. Use only one data structure will avoid this extra work.
276 //values can be [19] => "blablablab" or ["text"] => "blablabla"
277 $values = $submission->getPeerReview()->getPeerReviewValues($feedback_giver, $participant_id);
278
279 foreach ($this->criteria_items as $item) {
280 $col++;
281
282 //Criteria without catalog doesn't have ID nor TITLE. The criteria instance is given via "type" ilExcCriteria::getInstanceByType
283 $crit_id = $item->getId();
284 $crit_type = $item->getType();
285 $crit_title = $item->getTitle();
286 if ($crit_title == "") {
287 $crit_title = $item->getTranslatedType();
288 }
289
290 if (!in_array($crit_title, $this->title_columns)) {
291 $this->title_columns[] = $crit_title;
292 }
293 switch ($crit_type) {
294 case 'bool':
295 if ($values[$crit_id] == 1) {
296 $this->excel->setCell($row, $col, $this->lng->txt("yes"));
297 } elseif ($values[$crit_id] == -1) {
298 $this->excel->setCell($row, $col, $this->lng->txt("no"));
299 }
300 break;
301 case 'rating':
302 /*
303 * Get the rating data from the DB in the current less expensive way.
304 * assignment_id -> used in il_rating.obj_id
305 * object type as string -> used in il_rating.obj_type
306 * participant id -> il_rating.sub_obj_id
307 * "peer_" + criteria_id -> il_rating.sub_obj_type (peer or e.g. peer_12)
308 * peer id -> il_rating.user_id
309 */
310 // Possible TODO: refactor ilExAssignment->getPeerReviewCriteriaCatalogueItems somehow to avoid client
311 // classes to deal with ilExCriteria instances with persistence (by id) or instances on the fly (by type)
312 $sub_obj_type = "peer";
313 if ($crit_id) {
314 $sub_obj_type .= "_" . $crit_id;
315 }
317 $this->assignment->getId(),
318 'ass',
320 $sub_obj_type,
321 $feedback_giver
322 );
323 if ($rating_int = round((int) $rating)) {
324 $this->excel->setCell($row, $col, $rating_int);
325 }
326 break;
327 case 'text':
328 //again another check for criteria id (if instantiated via type)
329 if ($crit_id) {
330 $this->excel->setCell($row, $col, $values[$crit_id]);
331 } else {
332 $this->excel->setCell($row, $col, $values['text']);
333 }
334 break;
335 case 'file':
336 if ($crit_id) {
337 $crit_file_obj = ilExcCriteriaFile::getInstanceById($crit_id);
338 } else {
339 $crit_file_obj = ilExcCriteriaFile::getInstanceByType($crit_type);
340 }
341 $crit_file_obj->setPeerReviewContext($this->assignment, $feedback_giver, $participant_id);
342 $files = $crit_file_obj->getFiles();
343
344 $extra_crit_column = 0;
345 foreach ($files as $file) {
346 if ($extra_crit_column) {
347 $this->title_columns[] = $crit_title . "_" . $extra_crit_column;
348 }
349 $extra_crit_column++;
350 $dir = $this->getFeedbackDirectory($participant_id, $feedback_giver);
351 $this->copyFileToSubDirectory($dir, $file);
352 $this->excel->setCell($row, $col, "./" . $dir . DIRECTORY_SEPARATOR . basename($file));
353 $this->excel->addLink($row, $col, './' . $dir . DIRECTORY_SEPARATOR . basename($file));
354 $this->excel->setColors($this->excel->getCoordByColumnAndRow($col, $row), self::BG_COLOR, self::LINK_COLOR);
355 }
356 break;
357 }
358 }
359 }
360
364 protected function getFeedbackDirectory(int $participant_id, int $feedback_giver) : string
365 {
366 $dir = self::FBK_DIRECTORY . DIRECTORY_SEPARATOR .
368 "from_" . ilExSubmission::getDirectoryNameFromUserData($feedback_giver);
369 return $dir;
370 }
371
379 public function getExtraColumnsForSubmissionFiles($a_obj_id, $a_ass_id)
380 {
381 global $DIC;
382 $ilDB = $DIC->database();
383
384 $and = "";
385 if ($this->participant_id > 0) {
386 $and = " AND user_id = " . $this->participant_id;
387 }
388
389 $query = "SELECT MAX(max_num) AS max" .
390 " FROM (SELECT COUNT(user_id) AS max_num FROM exc_returned" .
391 " WHERE obj_id=" . $a_obj_id . ". AND ass_id=" . $a_ass_id . $and . " AND mimetype IS NOT NULL" .
392 " GROUP BY user_id) AS COUNTS";
393
394 $set = $ilDB->query($query);
395 $row = $ilDB->fetchAssoc($set);
396 return $row['max'];
397 }
398
406 public function addLink($a_row, $a_col, $a_submission_file)
407 {
408 $user_id = $a_submission_file['user_id'];
410
411 $filepath = './' . $this->lng->txt("exc_ass_submission_zip") . DIRECTORY_SEPARATOR . $targetdir . DIRECTORY_SEPARATOR;
412 switch ($this->assignment->getType()) {
414 $filepath .= $a_submission_file['filetitle'];
415 break;
416
418 $wsp_tree = new ilWorkspaceTree($user_id);
419 // #12939
420 if (!$wsp_tree->getRootId()) {
421 $wsp_tree->createTreeForUser($user_id);
422 }
423 $node = $wsp_tree->getNodeData((int) $a_submission_file['filetitle']);
424 $filepath .= "blog_" . $node['obj_id'] . DIRECTORY_SEPARATOR . "index.html";
425 break;
426
428 $filepath .= "prt_" . $a_submission_file['filetitle'] . DIRECTORY_SEPARATOR . "index.html";
429 break;
430
431 default:
432 $filepath = "";
433 }
434 $this->excel->addLink($a_row, $a_col, $filepath);
435 }
436
437 //TODO: Refactoring needed, long method...
438 protected function collectAssignmentData($assignment_id)
439 {
440 $ass_has_feedback = false;
441 $ass_has_criteria = false;
442
443 //assignment object
444 $this->assignment = new ilExAssignment($assignment_id);
445 $assignment_type = $this->assignment->getType();
446
447 //Sanitized title for excel file and target directory.
448 $this->sanitized_title = ilUtil::getASCIIFilename($this->assignment->getTitle());
449
450 // directories
451 if (!isset($this->temp_dir)) {
453 }
454 $this->createTargetDirectory();
455
456 //Collect submission files if needed by assignment type.
457 if (in_array($assignment_type, $this->ass_types_with_files)) {
459 $this->collectSubmissionFiles();
460 }
461
462 if ($this->assignment->getPeerReview()) {
463 $ass_has_feedback = true;
464 //obj to get the reviews in the foreach below.
465 $peer_review = new ilExPeerReview($this->assignment);
466 //default start column for revisions.
467 $first_excel_column_for_review = self::FIRST_DEFAULT_REVIEW_COLUMN;
468 }
469
470 if ($this->isExcelNeeded($assignment_type, $ass_has_feedback)) {
471 // PhpSpreadsheet object
472 $this->excel = new ilExcel();
473
474 //Excel sheet title
475 $this->excel->addSheet($this->sanitized_title);
476
477 //add common excel Columns
478 #25585
479 $this->title_columns = array(
480 $this->lng->txt('lastname'),
481 $this->lng->txt('firstname'),
482 $this->lng->txt('login'),
483 $this->lng->txt('exc_last_submission')
484 );
485 switch ($assignment_type) {
487 $this->title_columns[] = $this->lng->txt("exc_submission_text");
488 break;
490 $num_columns_submission = $this->getExtraColumnsForSubmissionFiles($this->exercise_id, $assignment_id);
491 if ($num_columns_submission > 1) {
492 for ($i = 1; $i <= $num_columns_submission; $i++) {
493 $this->title_columns[] = $this->lng->txt("exc_submission_file") . " " . $i;
494 }
495 } else {
496 $this->title_columns[] = $this->lng->txt("exc_submission_file");
497 }
498
499 $first_excel_column_for_review += $num_columns_submission - 1;
500 break;
501 default:
502 $this->title_columns[] = $this->lng->txt("exc_submission");
503 break;
504 }
505 if ($ass_has_feedback) {
506 $this->title_columns[] = $this->lng->txt("exc_peer_review_giver");
507 $this->title_columns[] = $this->lng->txt('exc_last_submission');
508 }
509
510 //criteria
511 //Notice:getPeerReviewCriteriaCatalogueItems can return just an empty instance without data.
512 if ($this->criteria_items = $this->assignment->getPeerReviewCriteriaCatalogueItems()) {
513 $ass_has_criteria = true;
514 }
515
516 if ($this->participant_id > 0) {
517 $participants = array($this->participant_id);
518 } else {
519 $participants = $this->getAssignmentMembersIds();
520 }
521
522 $filter = new ilExerciseMembersFilter($this->exercise_ref_id, $participants, $this->user_id);
523 $participants = $filter->filterParticipantsByAccess();
524
525 $row = 2;
526 // Fill the excel
527 foreach ($participants as $participant_id) {
528 $submission = new ilExSubmission($this->assignment, $participant_id);
529 $submission_files = $submission->getFiles();
530
531 if ($submission_files) {
532 $participant_name = ilObjUser::_lookupName($participant_id);
533 $this->excel->setCell($row, self::PARTICIPANT_LASTNAME_COLUMN, $participant_name['lastname']);
534 $this->excel->setCell($row, self::PARTICIPANT_FIRSTNAME_COLUMN, $participant_name['firstname']);
535 $this->excel->setCell($row, self::PARTICIPANT_LOGIN_COLUMN, $participant_name['login']);
536
537 //Get the submission Text
538 if (!in_array($assignment_type, $this->ass_types_with_files)) {
539 foreach ($submission_files as $submission_file) {
540 $this->excel->setCell($row, self::SUBMISSION_DATE_COLUMN, $submission_file['timestamp']);
541 $this->excel->setCell($row, self::FIRST_DEFAULT_SUBMIT_COLUMN, $submission_file['atext']);
542 }
543 } else {
545 foreach ($submission_files as $submission_file) {
546 $this->excel->setCell($row, self::SUBMISSION_DATE_COLUMN, $submission_file['timestamp']);
547
548 if ($assignment_type == ilExAssignment::TYPE_PORTFOLIO || $assignment_type == ilExAssignment::TYPE_BLOG) {
549 $this->excel->setCell($row, $col, $this->lng->txt("open"));
550 } else {
551 $this->excel->setCell($row, $col, $submission_file['filetitle']);
552 }
553 $this->excel->setColors($this->excel->getCoordByColumnAndRow($col, $row), self::BG_COLOR, self::LINK_COLOR);
554 $this->addLink($row, $col, $submission_file);
555 $col++; //does not affect blogs and portfolios.
556 }
557 }
558
559 if ($ass_has_feedback) {
560 if ($col < $first_excel_column_for_review) {
561 $col = $first_excel_column_for_review;
562 }
563 $reviews = $peer_review->getPeerReviewsByPeerId($participant_id);
564
565 //extra lines
566 $current_review_row = 0;
567 foreach ($reviews as $review) {
568 //not all reviews are done, we check it via date of review.
569 if ($review['tstamp']) {
570 $current_review_row++;
571 if ($current_review_row > 1) {
572 for ($i = 0; $i < $first_excel_column_for_review; $i++) {
573 $cell_to_copy = $this->excel->getCell($row, $i);
574 $this->excel->setCell($row + 1, $i, $cell_to_copy);
575 if ($i >= self::FIRST_DEFAULT_SUBMIT_COLUMN) {
576 $this->excel->setColors($this->excel->getCoordByColumnAndRow($i, $row + 1), self::BG_COLOR, self::LINK_COLOR);
577 }
578 }
579 ++$row;
580 }
581
582 $feedback_giver = $review['giver_id']; // user who made the review.
583
584 $feedback_giver_name = ilObjUser::_lookupName($feedback_giver);
585
586 $this->excel->setCell(
587 $row,
588 $col,
589 $feedback_giver_name['lastname'] . ", " . $feedback_giver_name['firstname'] . " [" . $feedback_giver_name['login'] . "]"
590 );
591
592 $this->excel->setCell($row, $col + 1, $review['tstamp']);
593
594 if ($ass_has_criteria) {
595 $this->addCriteriaToExcel($feedback_giver, $participant_id, $row, $col + 1);
596 }
597 }
598 }
599 }
600
601 $row++;
602 }
603 }
604
605 $this->addColumnTitles();
606 $this->excel->writeToFile($this->target_directory . "/" . $this->sanitized_title);
607 }
608 }
609
610 // get ONLY the members ids for this assignment
611 public function getAssignmentMembersIds()
612 {
613 global $DIC;
614
615 $ilDB = $DIC->database();
616 $members = array();
617
618 $set = $ilDB->query("SELECT usr_id" .
619 " FROM exc_mem_ass_status" .
620 " WHERE ass_id = " . $ilDB->quote($this->assignment->getId(), "integer"));
621
622 while ($rec = $ilDB->fetchAssoc($set)) {
623 $members[] = $rec['usr_id'];
624 }
625
626 return $members;
627 }
628}
An exception for terminatinating execution or to throw for unit testing.
Exercise assignment.
static getInstancesByExercise($a_exc_id)
Exercise peer review.
Exercise submission //TODO: This class has to much static methods related to delivered "files".
static downloadAllAssignmentFiles(ilExAssignment $a_ass, array $members, $to_path)
Download all submitted files of an assignment (all user)
static getDirectoryNameFromUserData($a_user_id)
static getInstanceById($a_id)
static getInstanceByType($a_type)
copyFileToSubDirectory($a_directory, $a_file)
Copy a file in the Feedback_files directory TODO use the new filesystem.
run(array $input, Observer $observer)
run the job
addLink($a_row, $a_col, $a_submission_file)
Mapping the links to use them on the excel.
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
getExpectedTimeOfTaskInSeconds()
int the amount of seconds this task usually taskes. If your task-duration scales with the the amount ...
getExtraColumnsForSubmissionFiles($a_obj_id, $a_ass_id)
Get the number of max amount of files submitted by a single user in the assignment.
createTargetDirectory()
Create the directory with the assignment title.
createSubmissionsDirectory()
Create the directory with the assignment title.
addCriteriaToExcel($feedback_giver, $participant_id, $row, $col)
Add criteria data to the excel.
Class ilExerciseMembersFilter.
Class ilObjExercise.
static _lookupName($a_user_id)
lookup user name
static getInstanceByObjId($a_obj_id, $stop_on_error=true)
get an instance of an Ilias object by object id
static _exists($a_id, $a_reference=false, $a_type=null)
checks if an object exists in object_data@access public
static getRatingForUserAndObject( $a_obj_id, $a_obj_type, $a_sub_obj_id, $a_sub_obj_type, $a_user_id, $a_category_id=null)
Get rating for a user and an object.
static ilTempnam($a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
static getASCIIFilename($a_filename)
convert utf8 to ascii filename
static makeDirParents($a_dir)
Create a new directory and all parent directories.
static createDirectory($a_dir, $a_mod=0755)
create directory
Tree handler for personal workspace.
global $DIC
Definition: goto.php:24
$i
Definition: metadata.php:24
$query
global $ilDB