diff --git a/main/admin/export_exercise_results.php b/main/admin/export_exercise_results.php index 38bdde5953..ceaf5705dd 100644 --- a/main/admin/export_exercise_results.php +++ b/main/admin/export_exercise_results.php @@ -144,6 +144,18 @@ $form ) ->setSelected($exerciseId); +$form->addDateTimePicker('start_date', get_lang('StartDate')); +$form->addDateTimePicker('end_date', get_lang('EndDate')); +$form->addRule('start_date', get_lang('InvalidDate'), 'datetime'); +$form->addRule('end_date', get_lang('InvalidDate'), 'datetime'); + +$form->addRule( + ['start_date', 'end_date'], + get_lang('StartDateShouldBeBeforeEndDate'), + 'date_compare', + 'lte' +); + $form->addHidden('course_id_changed', '0'); $form->addHidden('exercise_id_changed', '0'); $form->addButtonExport(get_lang('Export'), 'name'); @@ -155,7 +167,11 @@ if ($form->validate()) { $sessionId = (int) $values['session_id']; $courseId = (int) $values['selected_course']; $exerciseId = (int) $values['exerciseId']; - ExerciseLib::exportExerciseAllResultsZip($sessionId, $courseId, $exerciseId); + $filterDates = [ + 'start_date' => (!empty($values['start_date']) ? $values['start_date'] : ''), + 'end_date' => (!empty($values['end_date']) ? $values['end_date'] : ''), + ]; + ExerciseLib::exportExerciseAllResultsZip($sessionId, $courseId, $exerciseId, $filterDates); } } diff --git a/main/exercise/exercise.class.php b/main/exercise/exercise.class.php index a6aff7e0e2..7c022f2f4b 100755 --- a/main/exercise/exercise.class.php +++ b/main/exercise/exercise.class.php @@ -8420,13 +8420,16 @@ class Exercise } /** + * Get array of exercise details and user results * @param int $courseId * @param int $sessionId * @param array $quizId + * @param bool $checkOnlyActiveUsers + * @param array $filterDates Limit the results exported to those within this range ('start_date' to 'end_date') * * @return array exercises */ - public function getExerciseAndResult($courseId, $sessionId, $quizId = [], $status = null) + public function getExerciseAndResult($courseId, $sessionId, $quizId = [], $checkOnlyActiveUsers = false, $filterDates = []) { if (empty($quizId)) { return []; @@ -8438,16 +8441,30 @@ class Exercise $ids = is_array($quizId) ? $quizId : [$quizId]; $ids = array_map('intval', $ids); $ids = implode(',', $ids); - $track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); + $trackExcercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); + $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST); + $tblUser = Database::get_main_table(TABLE_MAIN_USER); $condition = ''; - if (isset($status)) { - $condition .= " AND te.status = '$status' "; + $innerJoinUser = ''; + if ($checkOnlyActiveUsers) { + $condition .= " AND te.status = '' "; + $innerJoinUser .= " INNER JOIN $tblUser u ON u.user_id = te. exe_user_id"; + } + + if (!empty($filterDates)) { + if (!empty($filterDates['start_date'])) { + $condition .= " AND te.exe_date >= '".Database::escape_string($filterDates['start_date'])."' "; + } + if (!empty($filterDates['end_date'])) { + $condition .= " AND te.exe_date <= '".Database::escape_string($filterDates['end_date'])."' "; + } } if (0 != $sessionId) { - $sql = "SELECT * FROM $track_exercises te - INNER JOIN c_quiz cq ON cq.iid = te.exe_exo_id + $sql = "SELECT * FROM $trackExcercises te + INNER JOIN $tblQuiz cq ON cq.iid = te.exe_exo_id + $innerJoinUser WHERE te.c_id = %s AND te.session_id = %s AND @@ -8457,8 +8474,9 @@ class Exercise $sql = sprintf($sql, $courseId, $sessionId, $ids); } else { - $sql = "SELECT * FROM $track_exercises te - INNER JOIN c_quiz cq ON cq.iid = te.exe_exo_id + $sql = "SELECT * FROM $trackExcercises te + INNER JOIN $tblQuiz cq ON cq.iid = te.exe_exo_id + $innerJoinUser WHERE te.c_id = %s AND cq.iid IN (%s) diff --git a/main/exercise/exercise.php b/main/exercise/exercise.php index f5efa530ea..33848f68bc 100644 --- a/main/exercise/exercise.php +++ b/main/exercise/exercise.php @@ -660,6 +660,11 @@ if ($is_allowedToEdit && $origin !== 'learnpath') { ); } + $actionsLeft .= Display::url( + Display::return_icon('export_pdf.png', get_lang('ExportAllExercisesAllResults'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&action=export_all_exercises_results' + ); + if ($limitTeacherAccess) { if (api_is_platform_admin()) { $actionsLeft .= $cleanAll; diff --git a/main/exercise/exercise_report.php b/main/exercise/exercise_report.php index 32e34b12af..a35918690b 100755 --- a/main/exercise/exercise_report.php +++ b/main/exercise/exercise_report.php @@ -58,9 +58,12 @@ $course_id = api_get_course_int_id(); $exercise_id = isset($_REQUEST['exerciseId']) ? (int) $_REQUEST['exerciseId'] : 0; $locked = api_resource_is_locked_by_gradebook($exercise_id, LINK_EXERCISE); $sessionId = api_get_session_id(); +$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null; -if (empty($exercise_id)) { - api_not_allowed(true); +if ('export_all_exercises_results' !== $action) { + if (empty($exercise_id)) { + api_not_allowed(true); + } } $blockPage = true; @@ -149,8 +152,13 @@ if (!empty($_REQUEST['export_report']) && $_REQUEST['export_report'] == '1') { $objExerciseTmp = new Exercise(); $exerciseExists = $objExerciseTmp->read($exercise_id); -$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : null; switch ($action) { + case 'export_all_exercises_results': + $sessionId = api_get_session_id(); + $courseId = api_get_course_int_id(); + ExerciseLib::exportAllExercisesResultsZip($sessionId, $courseId); + + break; case 'export_all_results': $sessionId = api_get_session_id(); $courseId = api_get_course_int_id(); diff --git a/main/inc/lib/exercise.lib.php b/main/inc/lib/exercise.lib.php index b18445ce2b..8034c000ab 100644 --- a/main/inc/lib/exercise.lib.php +++ b/main/inc/lib/exercise.lib.php @@ -7190,17 +7190,75 @@ EOT; curl_close($ch); } + public static function exportAllExercisesResultsZip( + int $sessionId, + int $courseId, + $filterDates = [] + ) { + $exercises = self::get_all_exercises_for_course_id( + null, + $sessionId, + $courseId, + true + ); + + $exportOk = false; + if (!empty($exercises)) { + $exportName = 'S'.$sessionId.'-C'.$courseId.'-ALL'; + $baseDir = api_get_path(SYS_ARCHIVE_PATH); + $folderName = 'pdfexport-'.$exportName; + $exportFolderPath = $baseDir.$folderName; + + if (!is_dir($exportFolderPath)) { + @mkdir($exportFolderPath); + } + + foreach ($exercises as $exercise) { + $exerciseId = $exercise['iid']; + self::exportExerciseAllResultsZip($sessionId, $courseId, $exerciseId, [], $exportFolderPath); + } + + // If export folder is not empty will be zipped. + $isFolderPathEmpty = (file_exists($exportFolderPath) && 2 == count(scandir($exportFolderPath))); + if (is_dir($exportFolderPath) && !$isFolderPathEmpty) { + $exportOk = true; + $exportFilePath = $baseDir.$exportName.'.zip'; + $zip = new \PclZip($exportFilePath); + $zip->create($exportFolderPath, PCLZIP_OPT_REMOVE_PATH, $exportFolderPath); + rmdirr($exportFolderPath); + + DocumentManager::file_send_for_download($exportFilePath, true, $exportName.'.zip'); + exit; + } + } + + if (!$exportOk) { + Display::addFlash( + Display::return_message( + get_lang('ExportExerciseNoResult'), + 'warning', + false + ) + ); + } + + return false; + } + public static function exportExerciseAllResultsZip( int $sessionId, int $courseId, - int $exerciseId + int $exerciseId, + $filterDates = [], + string $mainPath = '' ) { $objExerciseTmp = new Exercise($courseId); $exeResults = $objExerciseTmp->getExerciseAndResult( $courseId, $sessionId, $exerciseId, - '' + true, + $filterDates ); $exportOk = false; @@ -7232,8 +7290,12 @@ EOT; $zip->create($exportFolderPath, PCLZIP_OPT_REMOVE_PATH, $exportFolderPath); rmdirr($exportFolderPath); - DocumentManager::file_send_for_download($exportFilePath, true, $exportName.'.zip'); - exit; + if (!empty($mainPath) && file_exists($exportFilePath)) { + @rename($exportFilePath, $mainPath.'/'.$exportName.'.zip'); + } else { + DocumentManager::file_send_for_download($exportFilePath, true, $exportName.'.zip'); + exit; + } } } diff --git a/main/lang/english/trad4all.inc.php b/main/lang/english/trad4all.inc.php index a9da2dc982..669b133277 100644 --- a/main/lang/english/trad4all.inc.php +++ b/main/lang/english/trad4all.inc.php @@ -9024,4 +9024,5 @@ $AddEmailPicture = "Add picture for headers"; $AddEmailPictureComment = "This picture will be used in the course header and in the e-mails sent and PDFs generated from this course. The picture will have a ratio of 25:7. You can crop the picture you upload."; $DeleteEmailPicture = "Delete picture for headers"; $AddPictureComment = "The final picture must be in a 16:9 ratio, but you can crop the picture you upload."; +$ExportAllExercisesAllResults = "Export all results of all tests"; ?> \ No newline at end of file diff --git a/main/lang/french/trad4all.inc.php b/main/lang/french/trad4all.inc.php index 7017d3e55d..ad8b92610a 100644 --- a/main/lang/french/trad4all.inc.php +++ b/main/lang/french/trad4all.inc.php @@ -8958,4 +8958,5 @@ $AddEmailPicture = "Ajouter une image pour les en-têtes"; $AddEmailPictureComment = "L'image sera utilisée dans les en-têtes de ce cours mais également des e-mails envoyés et des PDFs générés depuis ce cours. L'image doit avoir un ratio de 25:7. Vous pouvez recouper l'image au moment de l'envoi."; $DeleteEmailPicture = "Supprimer l'image d'en-tête"; $AddPictureComment = "L'image finale doit avoir un format 16:9, mais vous pouvez la recouper lors de l'envoi."; +$ExportAllExercisesAllResults = "Exporter tous les résultats de tous les exercices"; ?> \ No newline at end of file diff --git a/main/lang/spanish/trad4all.inc.php b/main/lang/spanish/trad4all.inc.php index fa3977b737..1d530b9919 100644 --- a/main/lang/spanish/trad4all.inc.php +++ b/main/lang/spanish/trad4all.inc.php @@ -9049,4 +9049,5 @@ $AddEmailPicture = "Añadir imagen de cabecera"; $AddEmailPictureComment = "Esta imagen será usada en la cabecera de este curso y la de los e-mails enviados desde este curso, pero también en los PDFs generados desde este curso. La imagen final tiene que tener una proporción de 25:7, pero la puede recortar al momento de subirla."; $DeleteEmailPicture = "Eliminar la imagen de cabecera"; $AddPictureComment = "La imagen final tiene que tener una proporción de 16:9, pero la puede recortar durante la subida."; +$ExportAllExercisesAllResults = "Exportar todos los resultados de todos los ejercicios"; ?> \ No newline at end of file