|
|
@ -10232,38 +10232,9 @@ class SessionManager |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static function exportSessionsAsCSV(array $selectedSessions): void |
|
|
|
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 = []; |
|
|
|
$csvData = []; |
|
|
|
|
|
|
|
$headersGenerated = false; |
|
|
|
|
|
|
|
$csvHeaders = []; |
|
|
|
|
|
|
|
|
|
|
|
foreach ($selectedSessions as $sessionId) { |
|
|
|
foreach ($selectedSessions as $sessionId) { |
|
|
|
$courses = SessionManager::get_course_list_by_session_id($sessionId); |
|
|
|
$courses = SessionManager::get_course_list_by_session_id($sessionId); |
|
|
@ -10271,30 +10242,24 @@ class SessionManager |
|
|
|
if (!empty($courses)) { |
|
|
|
if (!empty($courses)) { |
|
|
|
foreach ($courses as $course) { |
|
|
|
foreach ($courses as $course) { |
|
|
|
$courseCode = $course['course_code']; |
|
|
|
$courseCode = $course['course_code']; |
|
|
|
|
|
|
|
$courseId = $course['id']; |
|
|
|
$studentList = CourseManager::get_student_list_from_course_code( |
|
|
|
$studentList = CourseManager::get_student_list_from_course_code( |
|
|
|
$courseCode, |
|
|
|
$courseCode, |
|
|
|
true, |
|
|
|
true, |
|
|
|
$sessionId |
|
|
|
$sessionId |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
$nbStudents = count($studentList); |
|
|
|
|
|
|
|
$userIds = array_keys($studentList); |
|
|
|
$userIds = array_keys($studentList); |
|
|
|
$csvContentInSession = TrackingCourseLog::getUserData( |
|
|
|
|
|
|
|
null, |
|
|
|
|
|
|
|
$nbStudents, |
|
|
|
|
|
|
|
null, |
|
|
|
|
|
|
|
null, |
|
|
|
|
|
|
|
[], |
|
|
|
|
|
|
|
false, |
|
|
|
|
|
|
|
true, |
|
|
|
|
|
|
|
$courseCode, |
|
|
|
|
|
|
|
$sessionId, |
|
|
|
|
|
|
|
true, |
|
|
|
|
|
|
|
$userIds |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($csvContentInSession)) { |
|
|
|
[$generatedHeaders, $csvContent] = self::generateSessionCourseReportData($sessionId, $courseId, $userIds); |
|
|
|
$csvData = array_merge($csvData, $csvContentInSession); |
|
|
|
|
|
|
|
|
|
|
|
if (!$headersGenerated) { |
|
|
|
|
|
|
|
$csvHeaders = $generatedHeaders; |
|
|
|
|
|
|
|
$headersGenerated = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($csvContent as $row) { |
|
|
|
|
|
|
|
$csvData[] = $row; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -10302,8 +10267,8 @@ class SessionManager |
|
|
|
|
|
|
|
|
|
|
|
if (!empty($csvData)) { |
|
|
|
if (!empty($csvData)) { |
|
|
|
array_unshift($csvData, $csvHeaders); |
|
|
|
array_unshift($csvData, $csvHeaders); |
|
|
|
$filename = 'export_session_courses_reports_complete_' . api_get_local_time(); |
|
|
|
$filename = 'export_session_courses_reports_complete_' . date('Y-m-d_H-i-s') . '.csv'; |
|
|
|
Export::arrayToCsv($csvData, $filename); |
|
|
|
Export::arrayToCsvSimple($csvData, $filename); |
|
|
|
exit; |
|
|
|
exit; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -10313,161 +10278,140 @@ class SessionManager |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static function exportSessionsAsZip(array $sessionList): void |
|
|
|
public static function exportSessionsAsZip(array $sessionList): void |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Create a temporary ZIP file |
|
|
|
|
|
|
|
$tempZipFile = api_get_path(SYS_ARCHIVE_PATH) . api_get_unique_id() . '.zip'; |
|
|
|
$tempZipFile = api_get_path(SYS_ARCHIVE_PATH) . api_get_unique_id() . '.zip'; |
|
|
|
$tempDir = dirname($tempZipFile); |
|
|
|
$tempDir = dirname($tempZipFile); |
|
|
|
|
|
|
|
|
|
|
|
// Check if the directory exists and has write permissions |
|
|
|
|
|
|
|
if (!is_dir($tempDir) || !is_writable($tempDir)) { |
|
|
|
if (!is_dir($tempDir) || !is_writable($tempDir)) { |
|
|
|
exit("The directory for creating the ZIP file does not exist or lacks write permissions: $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(); |
|
|
|
$zip = new \ZipArchive(); |
|
|
|
|
|
|
|
|
|
|
|
// Try to open the ZIP file for writing |
|
|
|
|
|
|
|
if ($zip->open($tempZipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { |
|
|
|
if ($zip->open($tempZipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { |
|
|
|
exit("Unable to open the ZIP file for writing: $tempZipFile"); |
|
|
|
exit("Unable to open the ZIP file for writing: $tempZipFile"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$csvList = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process each session in the list |
|
|
|
|
|
|
|
foreach ($sessionList as $sessionItemId) { |
|
|
|
foreach ($sessionList as $sessionItemId) { |
|
|
|
$em = Database::getManager(); |
|
|
|
$courses = SessionManager::get_course_list_by_session_id($sessionItemId); |
|
|
|
$sessionRepository = $em->getRepository(Session::class); |
|
|
|
|
|
|
|
$session = $sessionRepository->find($sessionItemId); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($session->getNbrCourses() > 0) { |
|
|
|
if (!empty($courses)) { |
|
|
|
$courses = $session->getCourses(); |
|
|
|
foreach ($courses as $course) { |
|
|
|
$courseList = []; |
|
|
|
$courseCode = $course['course_code']; |
|
|
|
|
|
|
|
$courseId = $course['id']; |
|
|
|
// 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 |
|
|
|
|
|
|
|
$studentList = CourseManager::get_student_list_from_course_code($courseCode, true, $sessionItemId); |
|
|
|
$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); |
|
|
|
$userIds = array_keys($studentList); |
|
|
|
|
|
|
|
|
|
|
|
// Get the user data for CSV content |
|
|
|
[$csvHeaders, $csvContent] = self::generateSessionCourseReportData($sessionItemId, $courseId, $userIds); |
|
|
|
$csvContentInSession = TrackingCourseLog::getUserData( |
|
|
|
array_unshift($csvContent, $csvHeaders); |
|
|
|
null, |
|
|
|
|
|
|
|
$nbStudents, |
|
|
|
|
|
|
|
null, |
|
|
|
|
|
|
|
null, |
|
|
|
|
|
|
|
[], |
|
|
|
|
|
|
|
true, |
|
|
|
|
|
|
|
true, |
|
|
|
|
|
|
|
$courseCode, |
|
|
|
|
|
|
|
$sessionItemId, |
|
|
|
|
|
|
|
true, |
|
|
|
|
|
|
|
$userIds |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
array_unshift($csvContentInSession, $csvHeaders); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get session info and dates |
|
|
|
|
|
|
|
$sessionInfo = api_get_session_info($sessionItemId); |
|
|
|
$sessionInfo = api_get_session_info($sessionItemId); |
|
|
|
$sessionDates = SessionManager::parseSessionDates($session); |
|
|
|
$courseInfo = api_get_course_info_by_id($courseId); |
|
|
|
|
|
|
|
$csvFileName = $sessionInfo['name'] . '_' . $courseInfo['name'] . '.csv'; |
|
|
|
// 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 |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add the generated CSV files to the ZIP archive |
|
|
|
$csvFilePath = Export::arrayToCsvSimple($csvContent, $csvFileName, true); |
|
|
|
foreach ($csvList as $csv) { |
|
|
|
|
|
|
|
$newFileName = $csv['session_id'] . '_' . $csv['session_name'] . '-' . $csv['course_id'] . '_' . $csv['course_name'] . '.csv'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (file_exists($csv['path'])) { |
|
|
|
if ($csvFilePath && file_exists($csvFilePath)) { |
|
|
|
$zip->addFile($csv['path'], $newFileName); |
|
|
|
$zip->addFile($csvFilePath, $csvFileName); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Close the ZIP file |
|
|
|
if (!$zip->close()) { |
|
|
|
if ($zip->close() === false) { |
|
|
|
|
|
|
|
exit("Could not close the ZIP file correctly."); |
|
|
|
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)) { |
|
|
|
if (file_exists($tempZipFile)) { |
|
|
|
DocumentManager::file_send_for_download($tempZipFile, true); |
|
|
|
DocumentManager::file_send_for_download($tempZipFile, true); |
|
|
|
unlink($tempZipFile); // Delete the temporary ZIP file after download |
|
|
|
unlink($tempZipFile); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
exit("The ZIP file was not created correctly."); |
|
|
|
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]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|