Skip to content

Commit

Permalink
Exercise: Add export attempts results to PDF - refs #3320
Browse files Browse the repository at this point in the history
  • Loading branch information
christianbeeznest committed Jan 11, 2025
1 parent 985b98a commit 0d1ca25
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 15 deletions.
4 changes: 2 additions & 2 deletions public/main/exercise/exercise.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -8072,7 +8072,7 @@ public function getExerciseAndResult($courseId, $sessionId, $quizId = [])
INNER JOIN c_quiz cq
ON cq.id = te.exe_exo_id AND te.c_id = cq.c_id
WHERE
te.id = %s AND
te.c_id = %d AND
te.session_id = %s AND
cq.id IN (%s)
ORDER BY cq.id";
Expand All @@ -8082,7 +8082,7 @@ public function getExerciseAndResult($courseId, $sessionId, $quizId = [])
$sql = "SELECT * FROM $track_exercises te
INNER JOIN c_quiz cq ON cq.id = te.exe_exo_id AND te.c_id = cq.c_id
WHERE
te.id = %s AND
te.c_id = %d AND
cq.id IN (%s)
ORDER BY cq.id";
$sql = sprintf($sql, $courseId, $ids);
Expand Down
28 changes: 27 additions & 1 deletion public/main/exercise/exercise_report.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
$exercise_id = isset($_REQUEST['exerciseId']) ? (int) $_REQUEST['exerciseId'] : (isset($_GET['id']) ? (int) $_GET['id'] : 0);
$locked = api_resource_is_locked_by_gradebook($exercise_id, LINK_EXERCISE);
$sessionId = api_get_session_id();
$action = $_REQUEST['action'] ?? null;

if (empty($exercise_id)) {
api_not_allowed(true);
Expand Down Expand Up @@ -94,6 +95,27 @@
$parameters['path'] = Security::remove_XSS($_GET['path']);
}

switch ($action) {
case 'export_all_results':
$sessionId = api_get_session_id();
$courseId = api_get_course_int_id();
ExerciseLib::exportExerciseAllResultsZip($sessionId, $courseId, $exercise_id);

break;
case 'export_pdf':
$exerciseId = (int) $_GET['exerciseId'];
$attemptId = (int) $_GET['attemptId'];
$userId = (int) $_GET['userId'];
$urlExportPdf = api_get_path(WEB_PATH).'main/exercise/exercise_show.php?'.api_get_cidreq().'&id='.$attemptId.'&action=export&export_type=result_pdf';

if (!$exerciseId || !$attemptId) {
api_not_allowed(true);
}

header('Location: '.$urlExportPdf);
exit;
}

if (!empty($_REQUEST['export_report']) && '1' == $_REQUEST['export_report']) {
if (api_is_platform_admin() || api_is_course_admin() ||
api_is_course_tutor() || api_is_session_general_coach()
Expand Down Expand Up @@ -431,9 +453,13 @@
$actions .= '<a id="export_opener" href="'.api_get_self().'?export_report=1&exerciseId='.$exercise_id.'" >'.
Display::getMdiIcon('content-save', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Export')).'</a>';
$actions .= Display::url(
Display::getMdiIcon(ActionIcon::REFRESH, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('RecalculateResults')),
Display::getMdiIcon(ActionIcon::REFRESH, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Recalculate Results')),
api_get_path(WEB_CODE_PATH).'exercise/recalculate_all.php?'.api_get_cidreq()."&exercise=$exercise_id"
);
$actions .= Display::url(
Display::getMdiIcon(ActionIcon::EXPORT_PDF, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Export all attempts')),
api_get_self().'?'.api_get_cidreq().'&action=export_all_results&exerciseId='.$exercise_id
);

// clean result before a selected date icon
if ($allowClean) {
Expand Down
40 changes: 31 additions & 9 deletions public/main/exercise/exercise_show.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
$currentUserId = api_get_user_id();
$printHeaders = 'learnpath' === $origin;
$id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0; //exe id
$exportTypeAllResults = ('export' === $_GET['action'] && (in_array($_GET['export_type'], ['all_results', 'result_pdf'])));

if (empty($id)) {
api_not_allowed(true);
Expand All @@ -39,15 +40,17 @@
$learnpath_item_id = $track_exercise_info['orig_lp_item_id'];
$lp_item_view_id = $track_exercise_info['orig_lp_item_view_id'];
$isBossOfStudent = false;
if (api_is_student_boss()) {
// Check if boss has access to user info.
if (UserManager::userIsBossOfStudent($currentUserId, $student_id)) {
$isBossOfStudent = true;
if (!$exportTypeAllResults) {
if (api_is_student_boss()) {
// Check if boss has access to user info.
if (UserManager::userIsBossOfStudent($currentUserId, $student_id)) {
$isBossOfStudent = true;
} else {
api_not_allowed($printHeaders);
}
} else {
api_not_allowed($printHeaders);
api_protect_course_script($printHeaders, false, true);
}
} else {
api_protect_course_script($printHeaders, false, true);
}

// Database table definitions
Expand Down Expand Up @@ -80,6 +83,7 @@
if (empty($questionList)) {
$questionList = Session::read('questionList');
}
/* @var Exercise $objExercise */
if (empty($objExercise)) {
$objExercise = Session::read('objExercise');
}
Expand All @@ -93,7 +97,8 @@
api_is_course_tutor() ||
api_is_session_admin() ||
api_is_drh() ||
api_is_student_boss();
api_is_student_boss() ||
$exportTypeAllResults;

if (!empty($sessionId) && !$is_allowedToEdit) {
if (api_is_course_session_coach(
Expand Down Expand Up @@ -973,7 +978,24 @@ class="exercise_mark_select"
'orientation' => 'P',
];
$pdf = new PDF('A4', $params['orientation'], $params);
$pdf->html_to_pdf_with_template($content, false, false, true);
if ('all_results' === $_GET['export_type']) {
$sessionId = api_get_session_id();
$courseId = api_get_course_int_id();
$exportName = 'S'.$sessionId.'-C'.$courseId.'-T'.$exercise_id;
$baseDir = api_get_path(SYS_ARCHIVE_PATH);
$folderName = 'pdfexport-'.$exportName;
$exportFolderPath = $baseDir.$folderName;
if (!is_dir($exportFolderPath)) {
@mkdir($exportFolderPath);
}
$pdfFileName = $user_info['firstname'].' '.$user_info['lastname'].'-attemptId'.$id.'.pdf';
$pdfFileName = api_replace_dangerous_char($pdfFileName);
$fileNameToSave = $exportFolderPath.'/'.$pdfFileName;
$pdf->html_to_pdf_with_template($content, true, false, true, [], 'F', $fileNameToSave);
} else {
$pdf->html_to_pdf_with_template($content, false, false, true);
}

exit;
}

Expand Down
126 changes: 126 additions & 0 deletions public/main/inc/lib/exercise.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -2351,6 +2351,13 @@ public static function get_exam_results_data(
]
);

$exportPdfUrl = api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.
api_get_cidreq().'&exerciseId='.$exercise_id.'&action=export_pdf&attemptId='.$id.'&userId='.(int) $results[$i]['exe_user_id'];
$actions .= '<a href="'.$exportPdfUrl.'" target="_blank">'
.Display::getMdiIcon(ActionIcon::EXPORT_PDF, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Export to PDF'))
.'</a>';


$filterByUser = isset($_GET['filter_by_user']) ? (int) $_GET['filter_by_user'] : 0;
$delete_link = '<a
href="exercise_report.php?'.api_get_cidreq().'&filter_by_user='.$filterByUser.'&filter='.$filter.'&exerciseId='.$exercise_id.'&delete=delete&did='.$id.'"
Expand Down Expand Up @@ -6024,4 +6031,123 @@ public static function scorePassed($score, $total)

return $scorePassed;
}

/**
* Export all results of *one* exercise to a ZIP file containing individual PDFs.
*
* @return false|void
* @throws Exception
*/
public static function exportExerciseAllResultsZip(
int $sessionId,
int $courseId,
int $exerciseId,
array $filterDates = [],
string $mainPath = ''
) {
$objExerciseTmp = new Exercise($courseId);
$exeResults = $objExerciseTmp->getExerciseAndResult(
$courseId,
$sessionId,
$exerciseId
);

$exportOk = false;
if (!empty($exeResults)) {
$exportName = 'S'.$sessionId.'-C'.$courseId.'-T'.$exerciseId;
$baseDir = api_get_path(SYS_ARCHIVE_PATH);
$folderName = 'pdfexport-'.$exportName;
$exportFolderPath = $baseDir.$folderName;

// 1. Cleans the export folder if it exists.
if (is_dir($exportFolderPath)) {
rmdirr($exportFolderPath);
}

// 2. Create the pdfs inside a new export folder path.
foreach ($exeResults as $exeResult) {
$exeId = (int) $exeResult['exe_id'];
self::saveFileExerciseResultPdf($exeId, $courseId, $sessionId);
}

// 3. 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 \ZipArchive();
if ($zip->open($exportFilePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === true) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($exportFolderPath),
RecursiveIteratorIterator::LEAVES_ONLY
);

foreach ($files as $name => $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($exportFolderPath) + 1);
$zip->addFile($filePath, $relativePath);
}
}

$zip->close();
} else {
throw new Exception('Failed to create ZIP file');
}

rmdirr($exportFolderPath);

if (!empty($mainPath) && file_exists($exportFilePath)) {
@rename($exportFilePath, $mainPath.'/'.$exportName.'.zip');
} else {
DocumentManager::file_send_for_download($exportFilePath, true, $exportName.'.zip');
exit;
}
}
}

if (empty($mainPath) && !$exportOk) {
Display::addFlash(
Display::return_message(
get_lang('ExportExerciseNoResult'),
'warning',
false
)
);
}

return false;
}

/**
* Generates and saves a PDF file for a specific exercise attempt result.
*/
public static function saveFileExerciseResultPdf(
int $exeId,
int $courseId,
int $sessionId
): void
{
$cidReq = 'cid='.$courseId.'&sid='.$sessionId.'&gid=0&gradebook=0';
$url = api_get_path(WEB_PATH).'main/exercise/exercise_show.php?'.$cidReq.'&id='.$exeId.'&action=export&export_type=all_results';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_COOKIE, session_id());
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$result = curl_exec($ch);

if (false === $result) {
error_log('saveFileExerciseResultPdf error: '.curl_error($ch));
}

curl_close($ch);
}
}
8 changes: 5 additions & 3 deletions public/main/inc/lib/pdf.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ public function html_to_pdf_with_template(
$saveToFile = false,
$returnHtml = false,
$addDefaultCss = false,
$extraRows = []
$extraRows = [],
$outputMode = 'D',
$fileToSave = null
) {
if (empty($this->template)) {
$tpl = new Template('', false, false, false, false, true, false);
Expand Down Expand Up @@ -170,9 +172,9 @@ public function html_to_pdf_with_template(
$css,
$this->params['filename'],
$this->params['course_code'],
'D',
$outputMode,
$saveToFile,
null,
$fileToSave,
$returnHtml,
$addDefaultCss
);
Expand Down

0 comments on commit 0d1ca25

Please sign in to comment.