Merge pull request #5853 from christianbeeznest/matra-22096

Session: Add batch session export to CSV - refs BT#22096
pull/5953/head
Nicolas Ducoulombier 9 months ago committed by GitHub
commit bd524b17ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. BIN
      public/img/copy.png
  2. BIN
      public/img/icons/32/attendance_list.png
  3. BIN
      public/img/icons/32/scorms.png
  4. BIN
      public/img/icons/32/scorms_na.png
  5. BIN
      public/img/icons/32/security.png
  6. BIN
      public/img/icons/32/security_na.png
  7. BIN
      public/img/icons/32/tools.png
  8. BIN
      public/img/icons/32/tools_na.png
  9. BIN
      public/img/icons/32/user.png
  10. BIN
      public/img/icons/32/user_na.png
  11. BIN
      public/img/save_pack.png
  12. 1318
      public/main/inc/lib/TrackingCourseLog.php
  13. 44
      public/main/inc/lib/exercise.lib.php
  14. 45
      public/main/inc/lib/export.lib.inc.php
  15. 14
      public/main/inc/lib/extra_field_value.lib.php
  16. 209
      public/main/inc/lib/sessionmanager.lib.php
  17. 1335
      public/main/inc/lib/tracking.lib.php
  18. 2
      public/main/inc/lib/usergroup.lib.php
  19. 2
      public/main/my_space/myStudents.php
  20. 85
      public/main/session/session_list.php
  21. 133
      public/main/tracking/courseLog.php
  22. 2
      public/main/tracking/course_log_resources.php
  23. 17
      src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php
  24. 2
      src/CourseBundle/Repository/CQuizRepository.php

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because it is too large Load Diff

@ -3000,47 +3000,9 @@ EOT;
$repo = Container::getQuizRepository(); $repo = Container::getQuizRepository();
return $repo->findAllByCourse($course, $session, (string) $search, $active); return $repo->findAllByCourse($course, $session, (string) $search, $active)
->getQuery()
// Show courses by active status ->getResult();
/*if (true == $search_all_sessions) {
$conditions = [
'where' => [
$active_sql.' c_id = ? '.$needle_where.$timeConditions => [
$course_id,
$needle,
],
],
'order' => 'title',
];
} else {
if (empty($session_id)) {
$conditions = [
'where' => [
$active_sql.' (session_id = 0 OR session_id IS NULL) AND c_id = ? '.$needle_where.$timeConditions => [
$course_id,
$needle,
],
],
'order' => 'title',
];
} else {
$conditions = [
'where' => [
$active_sql.' (session_id = 0 OR session_id IS NULL OR session_id = ? ) AND c_id = ? '.$needle_where.$timeConditions => [
$session_id,
$course_id,
$needle,
],
],
'order' => 'title',
];
}
}
$table = Database::get_course_table(TABLE_QUIZ_TEST);
return Database::select('*', $table, $conditions);*/
} }
/** /**

@ -26,14 +26,9 @@ class Export
/** /**
* Export tabular data to CSV-file. * 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 * @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)) { if (empty($data)) {
return false; return false;
@ -55,6 +50,44 @@ class Export
return $filePath; 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. * Export tabular data to XLS-file.
*/ */

@ -220,6 +220,10 @@ class ExtraFieldValue extends Model
$tags = []; $tags = [];
foreach ($tagValues as $tagValue) { foreach ($tagValues as $tagValue) {
if (is_array($tagValue)) {
$tagValue = reset($tagValue);
}
if (empty($tagValue)) { if (empty($tagValue)) {
continue; continue;
} }
@ -338,8 +342,17 @@ class ExtraFieldValue extends Model
break; break;
case ExtraField::FIELD_TYPE_DATE: case ExtraField::FIELD_TYPE_DATE:
if (is_array($value)) {
if (empty($value)) {
break;
}
$value = reset($value);
}
if (is_string($value) && !empty($value)) {
$d = DateTime::createFromFormat('Y-m-d', $value); $d = DateTime::createFromFormat('Y-m-d', $value);
$valid = $d && $d->format('Y-m-d') === $value; $valid = $d && $d->format('Y-m-d') === $value;
if ($valid) { if ($valid) {
$newParams = [ $newParams = [
'item_id' => $params['item_id'], 'item_id' => $params['item_id'],
@ -349,6 +362,7 @@ class ExtraFieldValue extends Model
]; ];
$this->save($newParams, $showQuery); $this->save($newParams, $showQuery);
} }
}
break; break;
case ExtraField::FIELD_TYPE_DATETIME: case ExtraField::FIELD_TYPE_DATETIME:
$d = DateTime::createFromFormat('Y-m-d H:i', $value); $d = DateTime::createFromFormat('Y-m-d H:i', $value);

@ -8533,7 +8533,7 @@ class SessionManager
[ [
'name' => 'title', 'name' => 'title',
'index' => 's.title', 'index' => 's.title',
'width' => '160', 'width' => '300',
'align' => 'left', 'align' => 'left',
'search' => 'true', 'search' => 'true',
'searchoptions' => ['sopt' => $operators], 'searchoptions' => ['sopt' => $operators],
@ -8549,6 +8549,7 @@ class SessionManager
'name' => 'display_start_date', 'name' => 'display_start_date',
'index' => 'display_start_date', 'index' => 'display_start_date',
'align' => 'left', 'align' => 'left',
'width' => '200',
'search' => 'true', 'search' => 'true',
'searchoptions' => [ 'searchoptions' => [
'dataInit' => 'date_pick_today', 'dataInit' => 'date_pick_today',
@ -8559,6 +8560,7 @@ class SessionManager
'name' => 'display_end_date', 'name' => 'display_end_date',
'index' => 'display_end_date', 'index' => 'display_end_date',
'align' => 'left', 'align' => 'left',
'width' => '200',
'search' => 'true', 'search' => 'true',
'searchoptions' => [ 'searchoptions' => [
'dataInit' => 'date_pick_one_month', 'dataInit' => 'date_pick_one_month',
@ -8579,6 +8581,7 @@ class SessionManager
'name' => 'users', 'name' => 'users',
'index' => 'users', 'index' => 'users',
'align' => 'left', 'align' => 'left',
'width' => '100',
'search' => 'false', 'search' => 'false',
]; ];
@ -8603,6 +8606,7 @@ class SessionManager
'name' => 'status', 'name' => 'status',
'index' => 'status', 'index' => 'status',
'align' => 'left', 'align' => 'left',
'width' => '120',
'search' => 'true', 'search' => 'true',
'stype' => 'select', 'stype' => 'select',
// for the bottom bar // for the bottom bar
@ -8633,7 +8637,7 @@ class SessionManager
[ [
'name' => 'title', 'name' => 'title',
'index' => 's.title', 'index' => 's.title',
'width' => '160', 'width' => '300',
'align' => 'left', 'align' => 'left',
'search' => 'true', 'search' => 'true',
'searchoptions' => ['sopt' => $operators], 'searchoptions' => ['sopt' => $operators],
@ -8649,6 +8653,7 @@ class SessionManager
'name' => 'display_start_date', 'name' => 'display_start_date',
'index' => 'display_start_date', 'index' => 'display_start_date',
'align' => 'left', 'align' => 'left',
'width' => '200',
'search' => 'true', 'search' => 'true',
'searchoptions' => [ 'searchoptions' => [
'dataInit' => 'date_pick_today', 'dataInit' => 'date_pick_today',
@ -8659,6 +8664,7 @@ class SessionManager
'name' => 'display_end_date', 'name' => 'display_end_date',
'index' => 'display_end_date', 'index' => 'display_end_date',
'align' => 'left', 'align' => 'left',
'width' => '200',
'search' => 'true', 'search' => 'true',
'searchoptions' => [ 'searchoptions' => [
'dataInit' => 'date_pick_one_month', 'dataInit' => 'date_pick_one_month',
@ -8679,6 +8685,7 @@ class SessionManager
'name' => 'users', 'name' => 'users',
'index' => 'users', 'index' => 'users',
'align' => 'left', 'align' => 'left',
'width' => '100',
'search' => 'false', 'search' => 'false',
]; ];
@ -8722,7 +8729,7 @@ class SessionManager
[ [
'name' => 'title', 'name' => 'title',
'index' => 's.title', 'index' => 's.title',
'width' => '200', 'width' => '300',
'align' => 'left', 'align' => 'left',
'search' => 'true', 'search' => 'true',
'searchoptions' => ['sopt' => $operators], 'searchoptions' => ['sopt' => $operators],
@ -8731,12 +8738,14 @@ class SessionManager
'name' => 'display_start_date', 'name' => 'display_start_date',
'index' => 'display_start_date', 'index' => 'display_start_date',
'align' => 'left', 'align' => 'left',
'width' => '200',
'search' => 'true', 'search' => 'true',
'searchoptions' => ['dataInit' => 'date_pick_today', 'sopt' => $date_operators], 'searchoptions' => ['dataInit' => 'date_pick_today', 'sopt' => $date_operators],
], ],
[ [
'name' => 'display_end_date', 'name' => 'display_end_date',
'index' => 'display_end_date', 'index' => 'display_end_date',
'width' => '200',
'align' => 'left', 'align' => 'left',
'search' => 'true', 'search' => 'true',
'searchoptions' => ['dataInit' => 'date_pick_one_month', 'sopt' => $date_operators], 'searchoptions' => ['dataInit' => 'date_pick_one_month', 'sopt' => $date_operators],
@ -8801,7 +8810,7 @@ class SessionManager
[ [
'name' => 'title', 'name' => 'title',
'index' => 's.title', 'index' => 's.title',
'width' => '160', 'width' => '300',
'align' => 'left', 'align' => 'left',
'search' => 'true', 'search' => 'true',
'searchoptions' => ['sopt' => $operators], 'searchoptions' => ['sopt' => $operators],
@ -8817,6 +8826,7 @@ class SessionManager
'name' => 'display_start_date', 'name' => 'display_start_date',
'index' => 'display_start_date', 'index' => 'display_start_date',
'align' => 'left', 'align' => 'left',
'width' => '200',
'search' => 'true', 'search' => 'true',
'searchoptions' => [ 'searchoptions' => [
'dataInit' => 'date_pick_today', 'dataInit' => 'date_pick_today',
@ -8827,6 +8837,7 @@ class SessionManager
'name' => 'display_end_date', 'name' => 'display_end_date',
'index' => 'display_end_date', 'index' => 'display_end_date',
'align' => 'left', 'align' => 'left',
'width' => '200',
'search' => 'true', 'search' => 'true',
'searchoptions' => [ 'searchoptions' => [
'dataInit' => 'date_pick_one_month', 'dataInit' => 'date_pick_one_month',
@ -8847,6 +8858,7 @@ class SessionManager
'name' => 'users', 'name' => 'users',
'index' => 'users', 'index' => 'users',
'align' => 'left', 'align' => 'left',
'width' => '100',
'search' => 'false', 'search' => 'false',
]; ];
@ -8871,6 +8883,7 @@ class SessionManager
'name' => 'status', 'name' => 'status',
'index' => 'status', 'index' => 'status',
'align' => 'left', 'align' => 'left',
'width' => '120',
'search' => 'true', 'search' => 'true',
'stype' => 'select', 'stype' => 'select',
// for the bottom bar // for the bottom bar
@ -10213,4 +10226,192 @@ class SessionManager
return $users; return $users;
} }
/**
* Method to export sessions data as CSV
*/
public static function exportSessionsAsCSV(array $selectedSessions): void
{
$csvData = [];
$headersGenerated = false;
$csvHeaders = [];
foreach ($selectedSessions as $sessionId) {
$courses = SessionManager::get_course_list_by_session_id($sessionId);
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
);
$userIds = array_keys($studentList);
[$generatedHeaders, $csvContent] = self::generateSessionCourseReportData($sessionId, $courseId, $userIds);
if (!$headersGenerated) {
$csvHeaders = $generatedHeaders;
$headersGenerated = true;
}
foreach ($csvContent as $row) {
$csvData[] = $row;
}
}
}
}
if (!empty($csvData)) {
array_unshift($csvData, $csvHeaders);
$filename = 'export_session_courses_reports_complete_' . date('Y-m-d_H-i-s') . '.csv';
Export::arrayToCsvSimple($csvData, $filename);
exit;
}
}
/**
* Exports session data as a ZIP file with CSVs and sends it for download.
*/
public static function exportSessionsAsZip(array $sessionList): void
{
$tempZipFile = api_get_path(SYS_ARCHIVE_PATH) . api_get_unique_id() . '.zip';
$tempDir = dirname($tempZipFile);
if (!is_dir($tempDir) || !is_writable($tempDir)) {
exit("The directory for creating the ZIP file does not exist or lacks write permissions: $tempDir");
}
$zip = new \ZipArchive();
if ($zip->open($tempZipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
exit("Unable to open the ZIP file for writing: $tempZipFile");
}
foreach ($sessionList as $sessionItemId) {
$courses = SessionManager::get_course_list_by_session_id($sessionItemId);
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);
$userIds = array_keys($studentList);
[$csvHeaders, $csvContent] = self::generateSessionCourseReportData($sessionItemId, $courseId, $userIds);
array_unshift($csvContent, $csvHeaders);
$sessionInfo = api_get_session_info($sessionItemId);
$courseInfo = api_get_course_info_by_id($courseId);
$csvFileName = $sessionInfo['name'] . '_' . $courseInfo['name'] . '.csv';
$csvFilePath = Export::arrayToCsvSimple($csvContent, $csvFileName, true);
if ($csvFilePath && file_exists($csvFilePath)) {
$zip->addFile($csvFilePath, $csvFileName);
}
}
}
}
if (!$zip->close()) {
exit("Could not close the ZIP file correctly.");
}
if (file_exists($tempZipFile)) {
DocumentManager::file_send_for_download($tempZipFile, true);
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];
}
} }

File diff suppressed because it is too large Load Diff

@ -18,6 +18,8 @@ use Chamilo\CoreBundle\Component\Utils\ToolIcon;
*/ */
class UserGroupModel extends Model class UserGroupModel extends Model
{ {
public const SOCIAL_CLASS = 1;
public const NORMAL_CLASS = 0;
public $columns = [ public $columns = [
'id', 'id',
'title', 'title',

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

@ -18,9 +18,21 @@ $htmlHeadXtra[] = api_get_jqgrid_js();
$action = $_REQUEST['action'] ?? null; $action = $_REQUEST['action'] ?? null;
$idChecked = $_REQUEST['idChecked'] ?? null; $idChecked = $_REQUEST['idChecked'] ?? null;
$idMultiple = $_REQUEST['id'] ?? null;
$listType = isset($_REQUEST['list_type']) ? Security::remove_XSS($_REQUEST['list_type']) : SessionManager::getDefaultSessionTab(); $listType = isset($_REQUEST['list_type']) ? Security::remove_XSS($_REQUEST['list_type']) : SessionManager::getDefaultSessionTab();
$copySessionContent = isset($_REQUEST['copy_session_content']) ? true : false;
switch ($action) { switch ($action) {
case 'delete_multiple':
$sessionList = explode(',', $idMultiple);
foreach ($sessionList as $id) {
$sessionInfo = api_get_session_info($id);
if ($sessionInfo) {
$response = SessionManager::delete($id);
}
}
echo 1;
exit;
case 'delete': case 'delete':
$sessionInfo = api_get_session_info($idChecked); $sessionInfo = api_get_session_info($idChecked);
if ($sessionInfo) { if ($sessionInfo) {
@ -37,7 +49,6 @@ switch ($action) {
} }
header('Location: '.$url); header('Location: '.$url);
exit(); exit();
break;
case 'copy': case 'copy':
$result = SessionManager::copy($idChecked); $result = SessionManager::copy($idChecked);
if ($result) { if ($result) {
@ -50,6 +61,32 @@ switch ($action) {
$url = 'session_list.php?list_type='.$listType; $url = 'session_list.php?list_type='.$listType;
} }
header('Location: '.$url); header('Location: '.$url);
exit;
case 'copy_multiple':
$sessionList = explode(',', $idMultiple);
foreach ($sessionList as $id) {
$sessionIdCopied = SessionManager::copy($id);
if ($sessionIdCopied) {
$sessionInfo = api_get_session_info($sessionIdCopied);
Display::addFlash(Display::return_message(get_lang('ItemCopied').' - '.$sessionInfo['name']));
} else {
Display::addFlash(Display::return_message(get_lang('ThereWasAnError'), 'error'));
}
}
$url = 'session_list.php';
if ('custom' !== $listType) {
$url = 'session_list.php?list_type='.$listType;
}
header('Location: '.$url);
exit;
case 'export_csv':
$selectedSessions = explode(',', $idMultiple);
SessionManager::exportSessionsAsCSV($selectedSessions);
break;
case 'export_multiple':
$sessionList = explode(',', $idMultiple);
SessionManager::exportSessionsAsZip($sessionList);
break; break;
} }
@ -163,6 +200,11 @@ $action_links = 'function action_formatter(cellvalue, options, rowObject) {
$urlAjaxExtraField = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?1=1'; $urlAjaxExtraField = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?1=1';
$orderUrl = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=order'; $orderUrl = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=order';
$deleteUrl = api_get_self().'?list_type='.$listType.'&action=delete_multiple';
$copyUrl = api_get_self().'?list_type='.$listType.'&action=copy_multiple';
$exportUrl = api_get_self().'?list_type='.$listType.'&action=export_multiple';
$exportCsvUrl = api_get_self().'?list_type='.$listType.'&action=export_csv';
$extra_params['multiselect'] = true;
?> ?>
<script> <script>
@ -302,12 +344,47 @@ $orderUrl = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=order';
grid.jqGrid('sortableRows', options); grid.jqGrid('sortableRows', options);
grid.jqGrid('navGrid','#sessions_pager', grid.jqGrid('navGrid','#sessions_pager',
{edit:false,add:false,del:false}, {edit:false,add:false,del:true},
{height:280,reloadAfterSubmit:false}, // edit options {height:280,reloadAfterSubmit:false}, // edit options
{height:280,reloadAfterSubmit:false}, // add options {height:280,reloadAfterSubmit:false}, // add options
{reloadAfterSubmit:false},// del options {reloadAfterSubmit:true, url: '<?php echo $deleteUrl; ?>' }, // del options
prmSearch prmSearch
); ).navButtonAdd('#sessions_pager',{
caption:"<?php echo addslashes(Display::return_icon('copy.png', get_lang('Copy'))); ?>",
buttonicon:"ui-icon ui-icon-plus",
onClickButton: function(a) {
var list = $("#sessions").jqGrid('getGridParam', 'selarrrow');
if (list.length) {
window.location.replace('<?php echo $copyUrl; ?>&id='+list.join(','));
} else {
alert("<?php echo addslashes(get_lang('Select an option')); ?>");
}
}
}).navButtonAdd('#sessions_pager',{
caption:"<?php echo addslashes(Display::return_icon('save_pack.png', get_lang('Export courses reports'))); ?>",
buttonicon:"ui-icon ui-icon-plus",
onClickButton: function(a) {
var list = $("#sessions").jqGrid('getGridParam', 'selarrrow');
if (list.length) {
window.location.replace('<?php echo $exportUrl; ?>&id='+list.join(','));
} else {
alert("<?php echo addslashes(get_lang('Select an option')); ?>");
}
},
position:"last"
}).navButtonAdd('#sessions_pager',{
caption:"<?php echo addslashes(Display::return_icon('export_csv.png', get_lang('Export courses reports complete'))); ?>",
buttonicon:"ui-icon ui-icon-plus",
onClickButton: function(a) {
var list = $("#sessions").jqGrid('getGridParam', 'selarrrow');
if (list.length) {
window.location.replace('<?php echo $exportCsvUrl; ?>&id='+list.join(','));
} else {
alert("<?php echo addslashes(get_lang('Select an option')); ?>");
}
},
position:"last"
});
<?php <?php
// Create the searching dialog. // Create the searching dialog.

@ -17,6 +17,7 @@ $course = api_get_course_entity();
if (null === $course) { if (null === $course) {
api_not_allowed(true); api_not_allowed(true);
} }
$courseInfo = api_get_course_info();
$sessionId = api_get_session_id(); $sessionId = api_get_session_id();
$is_allowedToTrack = Tracking::isAllowToTrack($sessionId); $is_allowedToTrack = Tracking::isAllowToTrack($sessionId);
$session = api_get_session_entity($sessionId); $session = api_get_session_entity($sessionId);
@ -28,12 +29,23 @@ if (!$is_allowedToTrack) {
//keep course_code form as it is loaded (global) by the table's get_user_data //keep course_code form as it is loaded (global) by the table's get_user_data
$courseCode = $course->getCode(); $courseCode = $course->getCode();
$courseId = $course->getId(); $courseId = $course->getId();
$parameters['cid'] = isset($_GET['cid']) ? (int) $_GET['cid'] : '';
$parameters['id_session'] = $sessionId;
$parameters['from'] = isset($_GET['myspace']) ? Security::remove_XSS($_GET['myspace']) : null;
$parameters['user_active'] = isset($_REQUEST['user_active']) && is_numeric($_REQUEST['user_active']) ? (int) $_REQUEST['user_active'] : null;
// PERSON_NAME_DATA_EXPORT is buggy // PERSON_NAME_DATA_EXPORT is buggy
$sortByFirstName = api_sort_by_first_name(); $sortByFirstName = api_sort_by_first_name();
$from_myspace = false; $from_myspace = false;
$from = $_GET['from'] ?? null; $from = $_GET['from'] ?? null;
$origin = api_get_origin(); $origin = api_get_origin();
$lpShowMaxProgress = 'true' === api_get_setting('lp.lp_show_max_progress_instead_of_average');
if ('true' === api_get_setting('lp.lp_show_max_progress_or_average_enable_course_level_redefinition')) {
$lpShowProgressCourseSetting = api_get_course_setting('lp_show_max_or_average_progress');
if (in_array($lpShowProgressCourseSetting, ['max', 'average'])) {
$lpShowMaxProgress = ('max' === $lpShowProgressCourseSetting);
}
}
// Starting the output buffering when we are exporting the information. // Starting the output buffering when we are exporting the information.
$export_csv = isset($_GET['export']) && 'csv' === $_GET['export']; $export_csv = isset($_GET['export']) && 'csv' === $_GET['export'];
@ -80,7 +92,18 @@ if (api_is_drh()) {
} }
} }
if ($export_csv) { $additionalParams = '';
if (isset($_GET['additional_profile_field'])) {
foreach ($_GET['additional_profile_field'] as $fieldId) {
$additionalParams .= '&additional_profile_field[]='.(int) $fieldId;
}
}
if (isset($parameters['user_active'])) {
$additionalParams .= '&user_active='.(int) $parameters['user_active'];
}
if ($export_csv || isset($_GET['csv'])) {
if (!empty($sessionId)) { if (!empty($sessionId)) {
Session::write('id_session', $sessionId); Session::write('id_session', $sessionId);
} }
@ -93,6 +116,13 @@ if (!empty($columnsToHideFromSetting) && isset($columnsToHideFromSetting['column
} }
$columnsToHide = json_encode($columnsToHide); $columnsToHide = json_encode($columnsToHide);
$csv_content = []; $csv_content = [];
$visibleIcon = Display::return_icon(
'visible.png',
get_lang('HideColumn'),
['align' => 'absmiddle', 'hspace' => '3px']
);
$exportInactiveUsers = api_get_path(WEB_CODE_PATH).'tracking/courseLog.php?'.api_get_cidreq().'&'.$additionalParams;
// Scripts for reporting array hide/show columns // Scripts for reporting array hide/show columns
$js = "<script> $js = "<script>
@ -129,6 +159,10 @@ $js = "<script>
foldup(id); foldup(id);
}); });
} }
$('#download-csv').on('click', function (e) {
e.preventDefault();
location.href = '".$exportInactiveUsers.'&csv=1&since='."'+$('#reminder_form_since').val();
});
}) })
</script>"; </script>";
$htmlHeadXtra[] = $js; $htmlHeadXtra[] = $js;
@ -141,6 +175,7 @@ $TABLECOURSE = Database::get_main_table(TABLE_MAIN_COURSE);
$table_user = Database::get_main_table(TABLE_MAIN_USER); $table_user = Database::get_main_table(TABLE_MAIN_USER);
$TABLEQUIZ = Database::get_course_table(TABLE_QUIZ_TEST); $TABLEQUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
$userEditionExtraFieldToCheck = 'true' === api_get_setting('platform.user_edition_extra_field_to_check');
// Breadcrumbs. // Breadcrumbs.
if ('resume_session' === $origin) { if ('resume_session' === $origin) {
$interbreadcrumb[] = [ $interbreadcrumb[] = [
@ -195,6 +230,31 @@ if (isset($_GET['additional_profile_field'])) {
Session::write('additional_user_profile_info', $userProfileInfo); Session::write('additional_user_profile_info', $userProfileInfo);
Session::write('extra_field_info', $extra_info); Session::write('extra_field_info', $extra_info);
$defaultExtraFields = [];
$defaultExtraFieldsFromSettings = [];
$defaultExtraFieldsFromSettings = api_get_setting('course.course_log_default_extra_fields', true);
if (!empty($defaultExtraFieldsFromSettings) && isset($defaultExtraFieldsFromSettings['extra_fields'])) {
$defaultExtraFields = $defaultExtraFieldsFromSettings['extra_fields'];
$defaultExtraInfo = [];
$defaultUserProfileInfo = [];
foreach ($defaultExtraFields as $fieldName) {
$extraFieldInfo = UserManager::get_extra_field_information_by_name($fieldName);
if (!empty($extraFieldInfo)) {
// Fetching only the user that are loaded NOT ALL user in the portal.
$defaultUserProfileInfo[$extraFieldInfo['id']] = TrackingCourseLog::getAdditionalProfileInformationOfFieldByUser(
$extraFieldInfo['id'],
$user_ids
);
$defaultExtraInfo[$extraFieldInfo['id']] = $extraFieldInfo;
}
}
Session::write('default_additional_user_profile_info', $defaultUserProfileInfo);
Session::write('default_extra_field_info', $defaultExtraInfo);
}
Display::display_header($nameTools, 'Tracking'); Display::display_header($nameTools, 'Tracking');
$actionsLeft = TrackingCourseLog::actionsLeft('users', $sessionId, false); $actionsLeft = TrackingCourseLog::actionsLeft('users', $sessionId, false);
@ -202,13 +262,6 @@ $actionsLeft = TrackingCourseLog::actionsLeft('users', $sessionId, false);
$actionsRight = '<a href="javascript: void(0);" onclick="javascript: window.print();">'. $actionsRight = '<a href="javascript: void(0);" onclick="javascript: window.print();">'.
Display::getMdiIcon(ActionIcon::PRINT, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Print')).'</a>'; Display::getMdiIcon(ActionIcon::PRINT, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Print')).'</a>';
$additionalParams = '';
if (isset($_GET['additional_profile_field'])) {
foreach ($_GET['additional_profile_field'] as $fieldId) {
$additionalParams .= '&additional_profile_field[]='.(int) $fieldId;
}
}
$users_tracking_per_page = ''; $users_tracking_per_page = '';
if (isset($_GET['users_tracking_per_page'])) { if (isset($_GET['users_tracking_per_page'])) {
$users_tracking_per_page = '&users_tracking_per_page='.intval($_GET['users_tracking_per_page']); $users_tracking_per_page = '&users_tracking_per_page='.intval($_GET['users_tracking_per_page']);
@ -326,8 +379,10 @@ $class = new UserGroupModel();
//$classes = $class->getUserGroupInCourse($options); //$classes = $class->getUserGroupInCourse($options);
$classes = $class->get_all(); $classes = $class->get_all();
$bestScoreLabel = get_lang('Score').' - '.get_lang('BestAttempt');
// Show the charts part only if there are students subscribed to this course/session // Show the charts part only if there are students subscribed to this course/session
if ($nbStudents > 0) { if ($nbStudents > 0 || isset($parameters['user_active'])) {
// Classes // Classes
$formClass = new FormValidator( $formClass = new FormValidator(
'classes', 'classes',
@ -456,12 +511,18 @@ if ($nbStudents > 0) {
if (false === $hideReports) { if (false === $hideReports) {
$conditions['course_id'] = $courseId; $conditions['course_id'] = $courseId;
$conditions['include_invited_users'] = false; $conditions['include_invited_users'] = false;
$usersTracking = TrackingCourseLog::get_user_data( $usersTracking = TrackingCourseLog::getUserData(
0, 0,
$nbStudents, $nbStudents,
$trackingColumn, $trackingColumn,
$trackingDirection, $trackingDirection,
$conditions $conditions,
true,
false,
null,
(int) $sessionId,
$export_csv,
$user_ids
); );
$userRepo = Container::getUserRepository(); $userRepo = Container::getUserRepository();
foreach ($usersTracking as $userTracking) { foreach ($usersTracking as $userTracking) {
@ -474,7 +535,7 @@ if ($nbStudents > 0) {
$numberStudentsCompletedLP++; $numberStudentsCompletedLP++;
} }
$averageStudentTestScore = substr($userTracking[7], 0, -1); $averageStudentTestScore = substr($userTracking[7], 0, -1);
$averageStudentsTestScore += $averageStudentTestScore; $averageStudentsTestScore .= $averageStudentTestScore;
if ('100' === $averageStudentTestScore) { if ('100' === $averageStudentTestScore) {
$reducedAverage = 9; $reducedAverage = 9;
@ -574,11 +635,11 @@ if ($nbStudents > 0) {
$el->setSelected(7); $el->setSelected(7);
$form->addElement('hidden', 'action', 'add'); $form->addElement('hidden', 'action', 'add');
$form->addElement('hidden', 'remindallinactives', 'true'); $form->addElement('hidden', 'remindallinactives', 'true');
$form->addElement('hidden', 'cidReq', $course->getCode()); $form->addElement('hidden', 'cid', api_get_course_int_id());
$form->addElement('hidden', 'id_session', api_get_session_id()); $form->addElement('hidden', 'sid', api_get_session_id());
$form->addButtonSend(get_lang('Notify')); $form->addButtonSend(get_lang('Notify'));
$extraFieldSelect = TrackingCourseLog::display_additional_profile_fields(); $extraFieldSelect = TrackingCourseLog::displayAdditionalProfileFields();
if (!empty($extraFieldSelect)) { if (!empty($extraFieldSelect)) {
$html .= $extraFieldSelect; $html .= $extraFieldSelect;
} }
@ -612,7 +673,7 @@ if ($nbStudents > 0) {
$table->setDataFunctionParams($conditions); $table->setDataFunctionParams($conditions);
} }
$parameters['cidReq'] = isset($_GET['cidReq']) ? Security::remove_XSS($_GET['cidReq']) : ''; $parameters['cid'] = isset($_GET['cid']) ? Security::remove_XSS($_GET['cid']) : '';
$parameters['sid'] = $sessionId; $parameters['sid'] = $sessionId;
$parameters['from'] = isset($_GET['myspace']) ? Security::remove_XSS($_GET['myspace']) : null; $parameters['from'] = isset($_GET['myspace']) ? Security::remove_XSS($_GET['myspace']) : null;
@ -718,6 +779,11 @@ if ($nbStudents > 0) {
$headers['first_login'] = get_lang('First access to course'); $headers['first_login'] = get_lang('First access to course');
$table->set_header($headerCounter++, get_lang('Latest access in course'), false); $table->set_header($headerCounter++, get_lang('Latest access in course'), false);
$headers['latest_login'] = get_lang('Latest access in course'); $headers['latest_login'] = get_lang('Latest access in course');
$table->set_header($headerCounter++, get_lang('Lp Finalization Date'), false);
$headers['lp_finalization_date'] = get_lang('Lp Finalization Date');
$table->set_header($headerCounter++, get_lang('Quiz Finalization Date'), false);
$headers['quiz_finalization_date'] = get_lang('Quiz Finalization Date');
$counter = $headerCounter; $counter = $headerCounter;
if ('true' === api_get_setting('show_email_addresses')) { if ('true' === api_get_setting('show_email_addresses')) {
$table->set_header($counter, get_lang('Email'), false); $table->set_header($counter, get_lang('Email'), false);
@ -732,6 +798,15 @@ if ($nbStudents > 0) {
$parameters['additional_profile_field'] = $fieldId; $parameters['additional_profile_field'] = $fieldId;
} }
} }
if (isset($defaultExtraFields)) {
if (!empty($defaultExtraInfo)) {
foreach ($defaultExtraInfo as $field) {
$table->set_header($counter, $field['display_text'], false);
$headers[$field['variable']] = $field['display_text'];
$counter++;
}
}
}
$table->set_header($counter, get_lang('Details'), false); $table->set_header($counter, get_lang('Details'), false);
$headers['Details'] = get_lang('Details'); $headers['Details'] = get_lang('Details');
@ -742,7 +817,7 @@ if ($nbStudents > 0) {
$parameters[$key] = $value; $parameters[$key] = $value;
} }
} }
$parameters['cidReq'] = $courseCode; $parameters['cid'] = api_get_course_int_id();
$parameters['sid'] = $sessionId; $parameters['sid'] = $sessionId;
$table->set_additional_parameters($parameters); $table->set_additional_parameters($parameters);
// display buttons to un hide hidden columns // display buttons to un hide hidden columns
@ -783,15 +858,7 @@ $groupTable->setHeaderContents(0, $column++, get_lang('Average time in the cours
$groupTable->setHeaderContents(0, $column++, get_lang('Course progress')); $groupTable->setHeaderContents(0, $column++, get_lang('Course progress'));
$groupTable->setHeaderContents(0, $column++, get_lang('Exercise average')); $groupTable->setHeaderContents(0, $column++, get_lang('Exercise average'));
/*$exerciseList = ExerciseLib::get_all_exercises( $exerciseList = [];
$courseInfo,
$sessionId,
false,
null,
false,
3
);*/
$session = api_get_session_entity($sessionId); $session = api_get_session_entity($sessionId);
$qb = Container::getQuizRepository()->findAllByCourse($course, $session, null, 2, false); $qb = Container::getQuizRepository()->findAllByCourse($course, $session, null, 2, false);
/** @var CQuiz[] $exercises */ /** @var CQuiz[] $exercises */
@ -946,7 +1013,8 @@ if (!empty($groupList)) {
$totalBestScoreAverageNotInLP = 0; $totalBestScoreAverageNotInLP = 0;
$bestScoreAverageNotInLP = 0; $bestScoreAverageNotInLP = 0;
if (!empty($exercises)) { if (!empty($exercises)) {
foreach ($exercises as $exerciseData) { foreach ($exercises as $i => $exerciseData) {
$exerciseList[$i]['iid'] = $exerciseData->getIid();
foreach ($studentIdList as $userId) { foreach ($studentIdList as $userId) {
$results = Event::get_best_exercise_results_by_user( $results = Event::get_best_exercise_results_by_user(
$exerciseData->getIid(), $exerciseData->getIid(),
@ -975,6 +1043,13 @@ if (!empty($groupList)) {
2 2
).' %'; ).' %';
} }
$bestScoreAverageNotInLP = (string) TrackingCourseLog::calcBestScoreAverageNotInLP(
$exerciseList,
$studentIdList,
(int) $courseInfo['real_id'],
$sessionId,
true
);
} }
$row = 1; $row = 1;
@ -1022,6 +1097,8 @@ if ($export_csv) {
$csv_headers[] = get_lang('First access to course'); $csv_headers[] = get_lang('First access to course');
$csv_headers[] = get_lang('Latest access in course'); $csv_headers[] = get_lang('Latest access in course');
$csv_headers[] = get_lang('Lp Finalization Date');
$csv_headers[] = get_lang('Quiz Finalization Date');
if (isset($_GET['additional_profile_field'])) { if (isset($_GET['additional_profile_field'])) {
foreach ($_GET['additional_profile_field'] as $fieldId) { foreach ($_GET['additional_profile_field'] as $fieldId) {
@ -1030,7 +1107,7 @@ if ($export_csv) {
} }
ob_end_clean(); ob_end_clean();
$csvContentInSession = Session::read('csv_content'); $csvContentInSession = Session::read('csv_content', []);
// Adding headers before the content. // Adding headers before the content.
array_unshift($csvContentInSession, $csv_headers); array_unshift($csvContentInSession, $csv_headers);

@ -28,7 +28,7 @@ if (!$is_allowedToTrack) {
} }
if ($export_csv || $exportXls) { if ($export_csv || $exportXls) {
$csvData = TrackingCourseLog::get_item_resources_data(0, 0, '', ''); $csvData = TrackingCourseLog::getItemResourcesData(0, 0, '', '');
array_walk( array_walk(
$csvData, $csvData,
function (&$item) { function (&$item) {

@ -32,7 +32,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo
$resourceNodeRepo = $this->container->get(ResourceNodeRepository::class); $resourceNodeRepo = $this->container->get(ResourceNodeRepository::class);
$q = $this->entityManager->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c'); $q = $this->entityManager->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c');
$updateConfigurations = [ /*$updateConfigurations = [
['table' => 'c_tool_intro', 'field' => 'intro_text'], ['table' => 'c_tool_intro', 'field' => 'intro_text'],
['table' => 'c_course_description', 'field' => 'content'], ['table' => 'c_course_description', 'field' => 'content'],
['table' => 'c_quiz', 'fields' => ['description', 'text_when_finished']], ['table' => 'c_quiz', 'fields' => ['description', 'text_when_finished']],
@ -48,7 +48,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo
['table' => 'c_survey', 'fields' => ['title', 'subtitle']], ['table' => 'c_survey', 'fields' => ['title', 'subtitle']],
['table' => 'c_survey_question', 'fields' => ['survey_question', 'survey_question_comment']], ['table' => 'c_survey_question', 'fields' => ['survey_question', 'survey_question_comment']],
['table' => 'c_survey_question_option', 'field' => 'option_text'], ['table' => 'c_survey_question_option', 'field' => 'option_text'],
]; ];*/
/** @var Course $course */ /** @var Course $course */
foreach ($q->toIterable() as $course) { foreach ($q->toIterable() as $course) {
@ -59,9 +59,9 @@ final class Version20230913162700 extends AbstractMigrationChamilo
continue; continue;
} }
foreach ($updateConfigurations as $config) { /* foreach ($updateConfigurations as $config) {
$this->updateContent($config, $courseDirectory, $courseId, $documentRepo); $this->updateContent($config, $courseDirectory, $courseId, $documentRepo);
} }*/
$this->updateHtmlContent($courseDirectory, $courseId, $documentRepo, $resourceNodeRepo); $this->updateHtmlContent($courseDirectory, $courseId, $documentRepo, $resourceNodeRepo);
} }
@ -155,6 +155,13 @@ final class Version20230913162700 extends AbstractMigrationChamilo
$documentPath = str_replace('/courses/'.$courseDirectory.'/document/', '/', $videoPath); $documentPath = str_replace('/courses/'.$courseDirectory.'/document/', '/', $videoPath);
error_log('Debugging Replace URLs:');
error_log('Full URL: ' . $fullUrl);
error_log('Video Path: ' . $videoPath);
error_log('Actual Course Directory: ' . $actualCourseDirectory);
error_log('Processed Document Path: ' . $documentPath);
/*
$sql = "SELECT iid, path, resource_node_id FROM c_document WHERE c_id = $courseId AND path LIKE '$documentPath'"; $sql = "SELECT iid, path, resource_node_id FROM c_document WHERE c_id = $courseId AND path LIKE '$documentPath'";
$result = $this->connection->executeQuery($sql); $result = $this->connection->executeQuery($sql);
$documents = $result->fetchAllAssociative(); $documents = $result->fetchAllAssociative();
@ -170,7 +177,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo
$contentText = str_replace($matches[0][$index], $replacement, $contentText); $contentText = str_replace($matches[0][$index], $replacement, $contentText);
} }
} }
} }*/
} }
return $contentText; return $contentText;

@ -40,7 +40,9 @@ final class CQuizRepository extends ResourceRepository implements ResourceWithLi
$this->addCategoryQueryBuilder($categoryId, $qb); $this->addCategoryQueryBuilder($categoryId, $qb);
$this->addActiveQueryBuilder($active, $qb); $this->addActiveQueryBuilder($active, $qb);
$this->addNotDeletedQueryBuilder($qb); $this->addNotDeletedQueryBuilder($qb);
if (!empty($title)) {
$this->addTitleQueryBuilder($title, $qb); $this->addTitleQueryBuilder($title, $qb);
}
return $qb; return $qb;
} }

Loading…
Cancel
Save