Internal: Fix CSV export formatting and score calculation issues - refs BT#22096

pull/5853/head
Christian Beeznest 9 months ago
parent e260348234
commit 9b1f1e6b69
  1. 45
      public/main/inc/lib/export.lib.inc.php
  2. 290
      public/main/inc/lib/sessionmanager.lib.php
  3. 19
      public/main/inc/lib/tracking.lib.php
  4. 2
      public/main/my_space/myStudents.php

@ -26,14 +26,9 @@ class Export
/**
* Export tabular data to CSV-file.
*
* @param array $data
* @param string $filename
* @param bool $writeOnly Whether to only write on disk or also send for download
* @param string $enclosure
*
* @return mixed csv raw data | false if no data to export | string file path if success in $writeOnly mode
*/
public static function arrayToCsv($data, $filename = 'export', $writeOnly = false, $enclosure = '"')
public static function arrayToCsv(array $data, string $filename = 'export', bool $writeOnly = false, string $enclosure = '"')
{
if (empty($data)) {
return false;
@ -55,6 +50,44 @@ class Export
return $filePath;
}
/**
* Converts an array of data into a CSV file and optionally sends it for download.
*
* @return string|void Returns the file path if $writeOnly is true, otherwise sends the file for download and exits.
*/
public static function arrayToCsvSimple(array $data, string $filename = 'export', bool $writeOnly = false)
{
$file = api_get_path(SYS_ARCHIVE_PATH) . uniqid('') . '.csv';
$handle = fopen($file, 'w');
if ($handle === false) {
throw new \RuntimeException("Unable to create or open the file: $file");
}
if (is_array($data)) {
foreach ($data as $row) {
$line = '';
if (is_array($row)) {
foreach ($row as $value) {
$line .= '"' . str_replace('"', '""', (string)$value) . '";';
}
}
fwrite($handle, rtrim($line, ';') . "\n");
}
}
fclose($handle);
if (!$writeOnly) {
DocumentManager::file_send_for_download($file, true, $filename . '.csv');
unlink($file);
exit;
}
return $file;
}
/**
* Export tabular data to XLS-file.
*/

@ -10232,38 +10232,9 @@ class SessionManager
*/
public static function exportSessionsAsCSV(array $selectedSessions): void
{
$csvHeaders = [];
$csvHeaders[] = get_lang('Session name');
$csvHeaders[] = get_lang('Session start date');
$csvHeaders[] = get_lang('Session end date');
$csvHeaders[] = get_lang('Course name');
$csvHeaders[] = get_lang('Official code');
if (api_sort_by_first_name()) {
$csvHeaders[] = get_lang('First name');
$csvHeaders[] = get_lang('Last name');
} else {
$csvHeaders[] = get_lang('Last name');
$csvHeaders[] = get_lang('First name');
}
$csvHeaders[] = get_lang('Login');
$csvHeaders[] = get_lang('Training time');
$csvHeaders[] = get_lang('Course progress');
$csvHeaders[] = get_lang('Exercise progress');
$csvHeaders[] = get_lang('Exercise average');
$csvHeaders[] = get_lang('Score');
$csvHeaders[] = get_lang('Score').' - '.get_lang('Best attempt');
$csvHeaders[] = get_lang('Student_publication');
$csvHeaders[] = get_lang('Messages');
$csvHeaders[] = get_lang('Classes');
$csvHeaders[] = get_lang('Registration date');
$csvHeaders[] = get_lang('FirstLogin in course');
$csvHeaders[] = get_lang('Latest login in course');
$csvHeaders[] = get_lang('Lp finalization date');
$csvHeaders[] = get_lang('Quiz finalization date');
$csvData = [];
$headersGenerated = false;
$csvHeaders = [];
foreach ($selectedSessions as $sessionId) {
$courses = SessionManager::get_course_list_by_session_id($sessionId);
@ -10271,30 +10242,24 @@ class SessionManager
if (!empty($courses)) {
foreach ($courses as $course) {
$courseCode = $course['course_code'];
$courseId = $course['id'];
$studentList = CourseManager::get_student_list_from_course_code(
$courseCode,
true,
$sessionId
);
$nbStudents = count($studentList);
$userIds = array_keys($studentList);
$csvContentInSession = TrackingCourseLog::getUserData(
null,
$nbStudents,
null,
null,
[],
false,
true,
$courseCode,
$sessionId,
true,
$userIds
);
if (!empty($csvContentInSession)) {
$csvData = array_merge($csvData, $csvContentInSession);
[$generatedHeaders, $csvContent] = self::generateSessionCourseReportData($sessionId, $courseId, $userIds);
if (!$headersGenerated) {
$csvHeaders = $generatedHeaders;
$headersGenerated = true;
}
foreach ($csvContent as $row) {
$csvData[] = $row;
}
}
}
@ -10302,8 +10267,8 @@ class SessionManager
if (!empty($csvData)) {
array_unshift($csvData, $csvHeaders);
$filename = 'export_session_courses_reports_complete_' . api_get_local_time();
Export::arrayToCsv($csvData, $filename);
$filename = 'export_session_courses_reports_complete_' . date('Y-m-d_H-i-s') . '.csv';
Export::arrayToCsvSimple($csvData, $filename);
exit;
}
}
@ -10313,161 +10278,140 @@ class SessionManager
*/
public static function exportSessionsAsZip(array $sessionList): void
{
// Create a temporary ZIP file
$tempZipFile = api_get_path(SYS_ARCHIVE_PATH) . api_get_unique_id() . '.zip';
$tempDir = dirname($tempZipFile);
// Check if the directory exists and has write permissions
if (!is_dir($tempDir) || !is_writable($tempDir)) {
exit("The directory for creating the ZIP file does not exist or lacks write permissions: $tempDir");
}
// Create a new ZIP archive
$zip = new \ZipArchive();
// Try to open the ZIP file for writing
if ($zip->open($tempZipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
exit("Unable to open the ZIP file for writing: $tempZipFile");
}
$csvList = [];
// Process each session in the list
foreach ($sessionList as $sessionItemId) {
$em = Database::getManager();
$sessionRepository = $em->getRepository(Session::class);
$session = $sessionRepository->find($sessionItemId);
$courses = SessionManager::get_course_list_by_session_id($sessionItemId);
if ($session->getNbrCourses() > 0) {
$courses = $session->getCourses();
$courseList = [];
// Collect courses for the session
foreach ($courses as $sessionRelCourse) {
$courseList[] = $sessionRelCourse->getCourse();
}
foreach ($courseList as $course) {
$courseId = $course->getId();
$courseInfo = api_get_course_info_by_id($courseId);
$addExerciseOption = api_get_configuration_value('add_exercise_best_attempt_in_report');
$sortByFirstName = api_sort_by_first_name();
$bestScoreLabel = get_lang('Score') . ' - ' . get_lang('Best attempt');
$courseCode = $courseInfo['code'];
// Prepare CSV headers
$csvHeaders = [];
$csvHeaders[] = get_lang('Official code');
if ($sortByFirstName) {
$csvHeaders[] = get_lang('First name');
$csvHeaders[] = get_lang('Last name');
} else {
$csvHeaders[] = get_lang('Last name');
$csvHeaders[] = get_lang('First name');
}
$csvHeaders[] = get_lang('Login');
$csvHeaders[] = get_lang('Training time');
$csvHeaders[] = get_lang('Course progress');
$csvHeaders[] = get_lang('Exercise progress');
$csvHeaders[] = get_lang('Exercise average');
$csvHeaders[] = get_lang('Score');
$csvHeaders[] = $bestScoreLabel;
// Include exercise results if available
$exerciseResultHeaders = [];
if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) &&
isset($addExerciseOption['courses'][$courseCode])) {
foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) {
$exercise = new Exercise();
$exercise->read($exerciseId);
if ($exercise->iid) {
$title = get_lang('Exercise') . ': ' . $exercise->get_formated_title();
$csvHeaders[] = $title;
$exerciseResultHeaders[] = $title;
}
}
}
// Add more fields to the CSV headers
$csvHeaders[] = get_lang('Student_publication');
$csvHeaders[] = get_lang('Messages');
$csvHeaders[] = get_lang('Classes');
$csvHeaders[] = get_lang('Registration date');
$csvHeaders[] = get_lang('First login in course');
$csvHeaders[] = get_lang('Latest login in course');
// Get the list of students for the course
if (!empty($courses)) {
foreach ($courses as $course) {
$courseCode = $course['course_code'];
$courseId = $course['id'];
$studentList = CourseManager::get_student_list_from_course_code($courseCode, true, $sessionItemId);
$nbStudents = count($studentList);
// Pass the necessary data as parameters instead of using globals
$userIds = array_keys($studentList);
// Get the user data for CSV content
$csvContentInSession = TrackingCourseLog::getUserData(
null,
$nbStudents,
null,
null,
[],
true,
true,
$courseCode,
$sessionItemId,
true,
$userIds
);
array_unshift($csvContentInSession, $csvHeaders);
[$csvHeaders, $csvContent] = self::generateSessionCourseReportData($sessionItemId, $courseId, $userIds);
array_unshift($csvContent, $csvHeaders);
// Get session info and dates
$sessionInfo = api_get_session_info($sessionItemId);
$sessionDates = SessionManager::parseSessionDates($session);
// Add session name and dates to the CSV content
array_unshift($csvContentInSession, [get_lang('Date'), $sessionDates['access']]);
array_unshift($csvContentInSession, [get_lang('SessionName'), Security::remove_XSS($sessionInfo['name'])]);
// Prepare CSV file information
$csvList[] = [
'session_id' => $sessionItemId,
'session_name' => $session->getTitle(),
'course_id' => $courseId,
'course_name' => $courseInfo['name'],
'path' => Export::arrayToCsv($csvContentInSession, '', true), // Generate the CSV
];
}
}
}
$courseInfo = api_get_course_info_by_id($courseId);
$csvFileName = $sessionInfo['name'] . '_' . $courseInfo['name'] . '.csv';
// Add the generated CSV files to the ZIP archive
foreach ($csvList as $csv) {
$newFileName = $csv['session_id'] . '_' . $csv['session_name'] . '-' . $csv['course_id'] . '_' . $csv['course_name'] . '.csv';
$csvFilePath = Export::arrayToCsvSimple($csvContent, $csvFileName, true);
if (file_exists($csv['path'])) {
$zip->addFile($csv['path'], $newFileName);
if ($csvFilePath && file_exists($csvFilePath)) {
$zip->addFile($csvFilePath, $csvFileName);
}
}
}
}
// Close the ZIP file
if ($zip->close() === false) {
if (!$zip->close()) {
exit("Could not close the ZIP file correctly.");
}
// Clean up the CSV files after adding them to the ZIP
foreach ($csvList as $csv) {
if (file_exists($csv['path'])) {
unlink($csv['path']);
}
}
// Send the ZIP file for download
if (file_exists($tempZipFile)) {
DocumentManager::file_send_for_download($tempZipFile, true);
unlink($tempZipFile); // Delete the temporary ZIP file after download
unlink($tempZipFile);
} else {
exit("The ZIP file was not created correctly.");
}
}
private static function generateSessionCourseReportData($sessionId, $courseId, $userIds): array
{
$em = Database::getManager();
$sessionRepository = $em->getRepository(Session::class);
$session = $sessionRepository->find($sessionId);
if (!$session instanceof Session) {
throw new \InvalidArgumentException("Invalid session object for session ID $sessionId");
}
$courseInfo = api_get_course_info_by_id($courseId);
$courseCode = $courseInfo['code'];
$csvHeaders = [
get_lang('Session name'),
get_lang('Session access dates'),
get_lang('Session display dates'),
get_lang('Course name'),
get_lang('Official code'),
get_lang('First name'),
get_lang('Last name'),
get_lang('Login'),
get_lang('Training time'),
get_lang('Course progress'),
get_lang('Exercise progress'),
get_lang('Exercise average'),
get_lang('Score'),
get_lang('Score') . ' - ' . get_lang('Best attempt'),
get_lang('Student_publication'),
get_lang('Messages'),
get_lang('Classes'),
get_lang('Registration date'),
get_lang('First login in course'),
get_lang('Latest login in course'),
];
$csvData = TrackingCourseLog::getUserData(
null,
count($userIds),
null,
null,
[],
true,
true,
$courseCode,
$sessionId,
true,
$userIds
);
$rawCsvContent = ChamiloSession::read('csv_content');
if (empty($rawCsvContent)) {
throw new \RuntimeException("No CSV content found in session for course $courseCode and session $sessionId.");
}
$csvContent = [];
foreach ($rawCsvContent as $row) {
$alignedRow = [
$row['session_name'] ?? '',
$row['session_startdate'] ?? '',
$row['session_enddate'] ?? '',
$row['course_name'] ?? '',
$row['official_code'] ?? '',
$row['firstname'] ?? '',
$row['lastname'] ?? '',
$row['username'] ?? '',
$row['time'] ?? '',
$row['average_progress'] ?? '',
$row['exercise_progress'] ?? '',
$row['exercise_average'] ?? '',
$row['student_score'] ?? '',
$row['student_score_best'] ?? '',
$row['count_assignments'] ?? '',
$row['count_messages'] ?? '',
$row['classes'] ?? '',
$row['registered_at'] ?? '',
$row['first_connection'] ?? '',
$row['last_connection'] ?? '',
];
$csvContent[] = $alignedRow;
}
return [$csvHeaders, $csvContent];
}
}

@ -2899,25 +2899,6 @@ class Tracking
}
$sessionCondition = api_get_session_condition($sessionId);
//$sessionId = (int) $sessionId;
/*if (count($lp_ids) > 0) {
$condition_session = " AND session_id = $sessionId ";
} else {
$condition_session = " WHERE session_id = $sessionId ";
}
// Check the real number of LPs corresponding to the filter in the
// database (and if no list was given, get them all)
if (empty($sessionId)) {
$sql = "SELECT DISTINCT(iid), use_max_score
FROM $lp_table
WHERE
c_id = $courseId AND
(session_id = 0 OR session_id IS NULL) $condition_lp ";
} else {
}*/
$lp_list = $use_max_score = [];
if (empty($condition_lp)) {
$repo = Container::getLpRepository();

@ -350,7 +350,7 @@ switch ($action) {
$studentId,
$course,
[],
$sId,
api_get_session_entity($sId),
false,
false,
true

Loading…
Cancel
Save