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. 34
      public/main/inc/lib/extra_field_value.lib.php
  16. 209
      public/main/inc/lib/sessionmanager.lib.php
  17. 1339
      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. 4
      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();
return $repo->findAllByCourse($course, $session, (string) $search, $active);
// Show courses by active status
/*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);*/
return $repo->findAllByCourse($course, $session, (string) $search, $active)
->getQuery()
->getResult();
}
/**

@ -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.
*/

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

@ -8533,7 +8533,7 @@ class SessionManager
[
'name' => 'title',
'index' => 's.title',
'width' => '160',
'width' => '300',
'align' => 'left',
'search' => 'true',
'searchoptions' => ['sopt' => $operators],
@ -8549,6 +8549,7 @@ class SessionManager
'name' => 'display_start_date',
'index' => 'display_start_date',
'align' => 'left',
'width' => '200',
'search' => 'true',
'searchoptions' => [
'dataInit' => 'date_pick_today',
@ -8559,6 +8560,7 @@ class SessionManager
'name' => 'display_end_date',
'index' => 'display_end_date',
'align' => 'left',
'width' => '200',
'search' => 'true',
'searchoptions' => [
'dataInit' => 'date_pick_one_month',
@ -8579,6 +8581,7 @@ class SessionManager
'name' => 'users',
'index' => 'users',
'align' => 'left',
'width' => '100',
'search' => 'false',
];
@ -8603,6 +8606,7 @@ class SessionManager
'name' => 'status',
'index' => 'status',
'align' => 'left',
'width' => '120',
'search' => 'true',
'stype' => 'select',
// for the bottom bar
@ -8633,7 +8637,7 @@ class SessionManager
[
'name' => 'title',
'index' => 's.title',
'width' => '160',
'width' => '300',
'align' => 'left',
'search' => 'true',
'searchoptions' => ['sopt' => $operators],
@ -8649,6 +8653,7 @@ class SessionManager
'name' => 'display_start_date',
'index' => 'display_start_date',
'align' => 'left',
'width' => '200',
'search' => 'true',
'searchoptions' => [
'dataInit' => 'date_pick_today',
@ -8659,6 +8664,7 @@ class SessionManager
'name' => 'display_end_date',
'index' => 'display_end_date',
'align' => 'left',
'width' => '200',
'search' => 'true',
'searchoptions' => [
'dataInit' => 'date_pick_one_month',
@ -8679,6 +8685,7 @@ class SessionManager
'name' => 'users',
'index' => 'users',
'align' => 'left',
'width' => '100',
'search' => 'false',
];
@ -8722,7 +8729,7 @@ class SessionManager
[
'name' => 'title',
'index' => 's.title',
'width' => '200',
'width' => '300',
'align' => 'left',
'search' => 'true',
'searchoptions' => ['sopt' => $operators],
@ -8731,12 +8738,14 @@ class SessionManager
'name' => 'display_start_date',
'index' => 'display_start_date',
'align' => 'left',
'width' => '200',
'search' => 'true',
'searchoptions' => ['dataInit' => 'date_pick_today', 'sopt' => $date_operators],
],
[
'name' => 'display_end_date',
'index' => 'display_end_date',
'width' => '200',
'align' => 'left',
'search' => 'true',
'searchoptions' => ['dataInit' => 'date_pick_one_month', 'sopt' => $date_operators],
@ -8801,7 +8810,7 @@ class SessionManager
[
'name' => 'title',
'index' => 's.title',
'width' => '160',
'width' => '300',
'align' => 'left',
'search' => 'true',
'searchoptions' => ['sopt' => $operators],
@ -8817,6 +8826,7 @@ class SessionManager
'name' => 'display_start_date',
'index' => 'display_start_date',
'align' => 'left',
'width' => '200',
'search' => 'true',
'searchoptions' => [
'dataInit' => 'date_pick_today',
@ -8827,6 +8837,7 @@ class SessionManager
'name' => 'display_end_date',
'index' => 'display_end_date',
'align' => 'left',
'width' => '200',
'search' => 'true',
'searchoptions' => [
'dataInit' => 'date_pick_one_month',
@ -8847,6 +8858,7 @@ class SessionManager
'name' => 'users',
'index' => 'users',
'align' => 'left',
'width' => '100',
'search' => 'false',
];
@ -8871,6 +8883,7 @@ class SessionManager
'name' => 'status',
'index' => 'status',
'align' => 'left',
'width' => '120',
'search' => 'true',
'stype' => 'select',
// for the bottom bar
@ -10213,4 +10226,192 @@ class SessionManager
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
{
public const SOCIAL_CLASS = 1;
public const NORMAL_CLASS = 0;
public $columns = [
'id',
'title',

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

@ -18,9 +18,21 @@ $htmlHeadXtra[] = api_get_jqgrid_js();
$action = $_REQUEST['action'] ?? null;
$idChecked = $_REQUEST['idChecked'] ?? null;
$idMultiple = $_REQUEST['id'] ?? null;
$listType = isset($_REQUEST['list_type']) ? Security::remove_XSS($_REQUEST['list_type']) : SessionManager::getDefaultSessionTab();
$copySessionContent = isset($_REQUEST['copy_session_content']) ? true : false;
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':
$sessionInfo = api_get_session_info($idChecked);
if ($sessionInfo) {
@ -37,7 +49,6 @@ switch ($action) {
}
header('Location: '.$url);
exit();
break;
case 'copy':
$result = SessionManager::copy($idChecked);
if ($result) {
@ -50,6 +61,32 @@ switch ($action) {
$url = 'session_list.php?list_type='.$listType;
}
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;
}
@ -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';
$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>
@ -302,12 +344,47 @@ $orderUrl = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=order';
grid.jqGrid('sortableRows', options);
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}, // add options
{reloadAfterSubmit:false},// del options
{reloadAfterSubmit:true, url: '<?php echo $deleteUrl; ?>' }, // del options
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
// Create the searching dialog.

@ -17,6 +17,7 @@ $course = api_get_course_entity();
if (null === $course) {
api_not_allowed(true);
}
$courseInfo = api_get_course_info();
$sessionId = api_get_session_id();
$is_allowedToTrack = Tracking::isAllowToTrack($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
$courseCode = $course->getCode();
$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
$sortByFirstName = api_sort_by_first_name();
$from_myspace = false;
$from = $_GET['from'] ?? null;
$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.
$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)) {
Session::write('id_session', $sessionId);
}
@ -93,6 +116,13 @@ if (!empty($columnsToHideFromSetting) && isset($columnsToHideFromSetting['column
}
$columnsToHide = json_encode($columnsToHide);
$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
$js = "<script>
@ -129,6 +159,10 @@ $js = "<script>
foldup(id);
});
}
$('#download-csv').on('click', function (e) {
e.preventDefault();
location.href = '".$exportInactiveUsers.'&csv=1&since='."'+$('#reminder_form_since').val();
});
})
</script>";
$htmlHeadXtra[] = $js;
@ -141,6 +175,7 @@ $TABLECOURSE = Database::get_main_table(TABLE_MAIN_COURSE);
$table_user = Database::get_main_table(TABLE_MAIN_USER);
$TABLEQUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
$userEditionExtraFieldToCheck = 'true' === api_get_setting('platform.user_edition_extra_field_to_check');
// Breadcrumbs.
if ('resume_session' === $origin) {
$interbreadcrumb[] = [
@ -195,6 +230,31 @@ if (isset($_GET['additional_profile_field'])) {
Session::write('additional_user_profile_info', $userProfileInfo);
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');
$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();">'.
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 = '';
if (isset($_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->get_all();
$bestScoreLabel = get_lang('Score').' - '.get_lang('BestAttempt');
// 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
$formClass = new FormValidator(
'classes',
@ -456,12 +511,18 @@ if ($nbStudents > 0) {
if (false === $hideReports) {
$conditions['course_id'] = $courseId;
$conditions['include_invited_users'] = false;
$usersTracking = TrackingCourseLog::get_user_data(
$usersTracking = TrackingCourseLog::getUserData(
0,
$nbStudents,
$trackingColumn,
$trackingDirection,
$conditions
$conditions,
true,
false,
null,
(int) $sessionId,
$export_csv,
$user_ids
);
$userRepo = Container::getUserRepository();
foreach ($usersTracking as $userTracking) {
@ -474,7 +535,7 @@ if ($nbStudents > 0) {
$numberStudentsCompletedLP++;
}
$averageStudentTestScore = substr($userTracking[7], 0, -1);
$averageStudentsTestScore += $averageStudentTestScore;
$averageStudentsTestScore .= $averageStudentTestScore;
if ('100' === $averageStudentTestScore) {
$reducedAverage = 9;
@ -574,11 +635,11 @@ if ($nbStudents > 0) {
$el->setSelected(7);
$form->addElement('hidden', 'action', 'add');
$form->addElement('hidden', 'remindallinactives', 'true');
$form->addElement('hidden', 'cidReq', $course->getCode());
$form->addElement('hidden', 'id_session', api_get_session_id());
$form->addElement('hidden', 'cid', api_get_course_int_id());
$form->addElement('hidden', 'sid', api_get_session_id());
$form->addButtonSend(get_lang('Notify'));
$extraFieldSelect = TrackingCourseLog::display_additional_profile_fields();
$extraFieldSelect = TrackingCourseLog::displayAdditionalProfileFields();
if (!empty($extraFieldSelect)) {
$html .= $extraFieldSelect;
}
@ -612,7 +673,7 @@ if ($nbStudents > 0) {
$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['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');
$table->set_header($headerCounter++, get_lang('Latest access in course'), false);
$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;
if ('true' === api_get_setting('show_email_addresses')) {
$table->set_header($counter, get_lang('Email'), false);
@ -732,6 +798,15 @@ if ($nbStudents > 0) {
$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);
$headers['Details'] = get_lang('Details');
@ -742,7 +817,7 @@ if ($nbStudents > 0) {
$parameters[$key] = $value;
}
}
$parameters['cidReq'] = $courseCode;
$parameters['cid'] = api_get_course_int_id();
$parameters['sid'] = $sessionId;
$table->set_additional_parameters($parameters);
// 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('Exercise average'));
/*$exerciseList = ExerciseLib::get_all_exercises(
$courseInfo,
$sessionId,
false,
null,
false,
3
);*/
$exerciseList = [];
$session = api_get_session_entity($sessionId);
$qb = Container::getQuizRepository()->findAllByCourse($course, $session, null, 2, false);
/** @var CQuiz[] $exercises */
@ -946,7 +1013,8 @@ if (!empty($groupList)) {
$totalBestScoreAverageNotInLP = 0;
$bestScoreAverageNotInLP = 0;
if (!empty($exercises)) {
foreach ($exercises as $exerciseData) {
foreach ($exercises as $i => $exerciseData) {
$exerciseList[$i]['iid'] = $exerciseData->getIid();
foreach ($studentIdList as $userId) {
$results = Event::get_best_exercise_results_by_user(
$exerciseData->getIid(),
@ -975,6 +1043,13 @@ if (!empty($groupList)) {
2
).' %';
}
$bestScoreAverageNotInLP = (string) TrackingCourseLog::calcBestScoreAverageNotInLP(
$exerciseList,
$studentIdList,
(int) $courseInfo['real_id'],
$sessionId,
true
);
}
$row = 1;
@ -1022,6 +1097,8 @@ if ($export_csv) {
$csv_headers[] = get_lang('First access to 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'])) {
foreach ($_GET['additional_profile_field'] as $fieldId) {
@ -1030,7 +1107,7 @@ if ($export_csv) {
}
ob_end_clean();
$csvContentInSession = Session::read('csv_content');
$csvContentInSession = Session::read('csv_content', []);
// Adding headers before the content.
array_unshift($csvContentInSession, $csv_headers);

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

@ -32,7 +32,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo
$resourceNodeRepo = $this->container->get(ResourceNodeRepository::class);
$q = $this->entityManager->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c');
$updateConfigurations = [
/*$updateConfigurations = [
['table' => 'c_tool_intro', 'field' => 'intro_text'],
['table' => 'c_course_description', 'field' => 'content'],
['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_question', 'fields' => ['survey_question', 'survey_question_comment']],
['table' => 'c_survey_question_option', 'field' => 'option_text'],
];
];*/
/** @var Course $course */
foreach ($q->toIterable() as $course) {
@ -59,9 +59,9 @@ final class Version20230913162700 extends AbstractMigrationChamilo
continue;
}
foreach ($updateConfigurations as $config) {
/* foreach ($updateConfigurations as $config) {
$this->updateContent($config, $courseDirectory, $courseId, $documentRepo);
}
}*/
$this->updateHtmlContent($courseDirectory, $courseId, $documentRepo, $resourceNodeRepo);
}
@ -155,6 +155,13 @@ final class Version20230913162700 extends AbstractMigrationChamilo
$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'";
$result = $this->connection->executeQuery($sql);
$documents = $result->fetchAllAssociative();
@ -170,7 +177,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo
$contentText = str_replace($matches[0][$index], $replacement, $contentText);
}
}
}
}*/
}
return $contentText;

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

Loading…
Cancel
Save