diff --git a/main/inc/lib/tracking.lib.php b/main/inc/lib/tracking.lib.php
index 462469f044..0f05b1ec03 100755
--- a/main/inc/lib/tracking.lib.php
+++ b/main/inc/lib/tracking.lib.php
@@ -121,6 +121,486 @@ class Tracking
return $parsedResult;
}
+ /**
+ * It gets table html of Lp stats used to export in pdf
+ *
+ * @param $userId
+ * @param $courseInfo
+ * @param $sessionId
+ * @param $lpId
+ * @return string
+ */
+ public static function getLpStatsContentToPdf(
+ $userId,
+ $courseInfo,
+ $sessionId,
+ $lpId,
+ $lpName
+ )
+ {
+ $hideTime = api_get_configuration_value('hide_lp_time');
+ $lpId = (int) $lpId;
+ $userId = (int) $userId;
+ $sessionId = (int) $sessionId;
+ $courseId = $courseInfo['real_id'];
+ $isAllowedToEdit = api_is_allowed_to_edit(null, true);
+ $sessionCondition = api_get_session_condition($sessionId);
+ $counter = 0;
+ $totalTime = 0;
+ $h = get_lang('h');
+ $resultDisabledExtAll = true;
+ $timeHeader = '
'.get_lang('ScormTime').' | ';
+ if ($hideTime) {
+ $timeHeader = '';
+ }
+ $output = ''.$lpName.'
';
+ $output .= '
+
+
+ | '.get_lang('ScormLessonTitle').' |
+ '.get_lang('ScormStatus').' |
+ '.get_lang('ScormScore').' |
+ '.$timeHeader.'
+
+
+
+ ';
+
+ $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
+ $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
+ $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
+ $tblQuizQuestions = Database::get_course_table(TABLE_QUIZ_QUESTION);
+ $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
+ $tblStatsExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
+ $tblStatsAttempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
+
+ // it gets the max view
+ $sql = "SELECT max(view_count) FROM $tblLpView WHERE c_id = $courseId AND lp_id = $lpId AND user_id = $userId $sessionCondition";
+ $res = Database::query($sql);
+ $view = 0;
+ $viewCondition = "";
+ if (Database::num_rows($res) > 0) {
+ $view = Database::result($res, 0, 0);
+ $viewCondition = " AND v.view_count = ".(int) $view;
+ }
+
+ $chapterTypes = learnpath::getChapterTypes();
+ $minimumAvailable = self::minimumTimeAvailable($sessionId, $courseId);
+ $timeCourse = [];
+ if ($minimumAvailable) {
+ $timeCourse = self::getCalculateTime($userId, $courseId, $sessionId);
+ }
+
+ $list = learnpath::get_flat_ordered_items_list($lpId, 0, $courseId);
+ if (is_array($list) && count($list) > 0) {
+ foreach ($list as $my_item_id) {
+ $extend_this = 1;
+ $sql = "SELECT
+ iv.status as mystatus,
+ v.view_count as mycount,
+ iv.score as myscore,
+ iv.total_time as mytime,
+ i.iid as myid,
+ i.lp_id as mylpid,
+ iv.lp_view_id as mylpviewid,
+ i.title as mytitle,
+ i.max_score as mymaxscore,
+ iv.max_score as myviewmaxscore,
+ i.item_type as item_type,
+ iv.view_count as iv_view_count,
+ iv.id as iv_id,
+ path
+ FROM $tblLpItem as i
+ INNER JOIN $tblLpItemView as iv
+ ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
+ INNER JOIN $tblLpView as v
+ ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
+ WHERE
+ v.c_id = $courseId AND
+ i.iid = $my_item_id AND
+ i.lp_id = $lpId AND
+ v.user_id = $userId AND
+ v.session_id = $sessionId
+ $viewCondition
+ ORDER BY iv.view_count";
+ $result = Database::query($sql);
+ $num = Database::num_rows($result);
+ $timeForTotal = 0;
+ $attemptResult = 0;
+ if ($timeCourse) {
+ if (isset($timeCourse['learnpath_detailed']) &&
+ isset($timeCourse['learnpath_detailed'][$lpId]) &&
+ isset($timeCourse['learnpath_detailed'][$lpId][$my_item_id])
+ ) {
+ $attemptResult = $timeCourse['learnpath_detailed'][$lpId][$my_item_id][$view];
+ }
+ }
+
+ if ($num > 0) {
+ // Not extended.
+ $row = Database::fetch_array($result, 'ASSOC');
+ $my_id = $row['myid'];
+ $my_lp_id = $row['mylpid'];
+ $my_lp_view_id = $row['mylpviewid'];
+ $lpItemPath = (int) $row['path'];
+ $resultDisabledExtAll = false;
+ if ($row['item_type'] === 'quiz') {
+ // Check results_disabled in quiz table.
+ $sql = "SELECT results_disabled
+ FROM $tblQuiz
+ WHERE iid = $lpItemPath";
+ $res_result_disabled = Database::query($sql);
+ $row_result_disabled = Database::fetch_row($res_result_disabled);
+ if (Database::num_rows($res_result_disabled) > 0 && 1 === (int) $row_result_disabled[0]) {
+ $resultDisabledExtAll = true;
+ }
+ }
+
+ // Check if there are interactions below
+ $extend_this_attempt = 0;
+ $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $courseId);
+ $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $courseId);
+ $extend_attempt_link = '';
+ if ($inter_num > 0 || $objec_num > 0) {
+ $extend_this_attempt = 1;
+ }
+
+ $oddclass = (0 == ($counter % 2)) ? 'row_odd' : 'row_even';
+ $lesson_status = $row['mystatus'];
+ $score = $row['myscore'];
+ $subtotal_time = $row['mytime'];
+ while ($tmp_row = Database::fetch_array($result)) {
+ $subtotal_time += $tmp_row['mytime'];
+ }
+
+ $title = $row['mytitle'];
+ // Selecting the exe_id from stats attempts tables in order to look the max score value.
+ $sql = 'SELECT * FROM '.$tblStatsExercises.'
+ WHERE
+ exe_exo_id="'.$row['path'].'" AND
+ exe_user_id="'.$userId.'" AND
+ orig_lp_id = "'.$lpId.'" AND
+ orig_lp_item_id = "'.$row['myid'].'" AND
+ c_id = '.$courseId.' AND
+ status <> "incomplete" AND
+ session_id = '.$sessionId.'
+ ORDER BY exe_date DESC
+ LIMIT 1';
+
+ $resultLastAttempt = Database::query($sql);
+ $num = Database::num_rows($resultLastAttempt);
+ $id_last_attempt = null;
+ if ($num > 0) {
+ while ($rowLA = Database::fetch_array($resultLastAttempt)) {
+ $id_last_attempt = $rowLA['exe_id'];
+ }
+ }
+
+ switch ($row['item_type']) {
+ case 'sco':
+ if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
+ $maxscore = $row['myviewmaxscore'];
+ } elseif ($row['myviewmaxscore'] === '') {
+ $maxscore = 0;
+ } else {
+ $maxscore = $row['mymaxscore'];
+ }
+ break;
+ case 'quiz':
+ // Get score and total time from last attempt of a exercise en lp.
+ $sql = "SELECT iid, score
+ FROM $tblLpItemView
+ WHERE
+ c_id = $courseId AND
+ lp_item_id = '".(int) $my_id."' AND
+ lp_view_id = '".(int) $my_lp_view_id."'
+ ORDER BY view_count DESC
+ LIMIT 1";
+ $res_score = Database::query($sql);
+ $row_score = Database::fetch_array($res_score);
+
+ $sql = "SELECT SUM(total_time) as total_time
+ FROM $tblLpItemView
+ WHERE
+ c_id = $courseId AND
+ lp_item_id = '".(int) $my_id."' AND
+ lp_view_id = '".(int) $my_lp_view_id."'";
+ $res_time = Database::query($sql);
+ $row_time = Database::fetch_array($res_time);
+
+ $score = 0;
+ $subtotal_time = 0;
+ if (Database::num_rows($res_score) > 0 && Database::num_rows($res_time) > 0) {
+ $score = (float) $row_score['score'];
+ $subtotal_time = (int) $row_time['total_time'];
+ }
+ // Selecting the max score from an attempt.
+ $sql = "SELECT SUM(t.ponderation) as maxscore
+ FROM (
+ SELECT DISTINCT
+ question_id, marks, ponderation
+ FROM $tblStatsAttempts as at
+ INNER JOIN $tblQuizQuestions as q
+ ON q.iid = at.question_id
+ WHERE exe_id ='$id_last_attempt'
+ ) as t";
+
+ $result = Database::query($sql);
+ $row_max_score = Database::fetch_array($result);
+ $maxscore = $row_max_score['maxscore'];
+
+ // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
+ $sql = 'SELECT SUM(exe_duration) exe_duration
+ FROM '.$tblStatsExercises.'
+ WHERE
+ exe_exo_id="'.$row['path'].'" AND
+ exe_user_id="'.$userId.'" AND
+ orig_lp_id = "'.$lpId.'" AND
+ orig_lp_item_id = "'.$row['myid'].'" AND
+ c_id = '.$courseId.' AND
+ status <> "incomplete" AND
+ session_id = '.$sessionId.'
+ ORDER BY exe_date DESC ';
+ $sumScoreResult = Database::query($sql);
+ $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
+ if (!empty($durationRow['exe_duration'])) {
+ $exeDuration = $durationRow['exe_duration'];
+ if ($exeDuration != $subtotal_time && !empty($row_score['iid']) && !empty($exeDuration)) {
+ $subtotal_time = $exeDuration;
+ // Update c_lp_item_view.total_time
+ $sqlUpdate = "UPDATE $tblLpItemView SET total_time = '$exeDuration' WHERE iid = ".$row_score['iid'];
+ Database::query($sqlUpdate);
+ }
+ }
+ break;
+ default:
+ $maxscore = $row['mymaxscore'];
+ break;
+ }
+
+ $timeForTotal = $subtotal_time;
+ $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
+ if (empty($title)) {
+ $title = learnpath::rl_get_resource_name(
+ $courseInfo['code'],
+ $lpId,
+ $row['myid']
+ );
+ }
+
+ if (in_array($row['item_type'], $chapterTypes)) {
+ $title = Security::remove_XSS($title);
+ $output .= '
+ '.$title.' |
+
';
+ } else {
+ $correct_test_link = '-';
+ $showRowspan = false;
+ if ('quiz' === $row['item_type']) {
+ $sql = 'SELECT * FROM '.$tblStatsExercises.'
+ WHERE
+ exe_exo_id="'.$row['path'].'" AND
+ exe_user_id="'.$userId.'" AND
+ orig_lp_id = "'.$lpId.'" AND
+ orig_lp_item_id = "'.$row['myid'].'" AND
+ c_id = '.$courseId.' AND
+ status <> "incomplete" AND
+ session_id = '.$sessionId.'
+ ORDER BY exe_date DESC ';
+ $resultLastAttempt = Database::query($sql);
+ $num = Database::num_rows($resultLastAttempt);
+ $showRowspan = true;
+ }
+
+ $title = Security::remove_XSS($title);
+ if ($lpId == $my_lp_id && false) {
+ $output .= '
+ | '.$title.' |
+ |
+ |
+ |
+
';
+ //$output .= '';
+ } else {
+ $output .= "";
+ $scoreItem = null;
+ if ($row['item_type'] === 'quiz') {
+ $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
+ } else {
+ $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
+ }
+ $timeRow = '| '.$time.' | ';
+ if ($hideTime) {
+ $timeRow = '';
+ }
+ $output .= '
+ '.$title.' |
+ '.learnpathitem::humanize_status($lesson_status).' |
+ '.$scoreItem.' |
+ '.$timeRow.'
+ ';
+ $output .= '
';
+ }
+ }
+
+ $counter++;
+ if ($extend_this_attempt) {
+ $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $courseId);
+ foreach ($list1 as $id => $interaction) {
+ $oddclass = 'row_even';
+ if (($counter % 2) == 0) {
+ $oddclass = 'row_odd';
+ }
+ $timeRow = ''.$interaction['time'].' | ';
+ if ($hideTime) {
+ $timeRow = '';
+ }
+
+ $output .= '
+ | '.$interaction['order_id'].' |
+ '.$interaction['id'].' |
+ '.$interaction['type'].' |
+ '.urldecode($interaction['student_response']).' |
+ '.$interaction['result'].' |
+ '.$interaction['latency'].' |
+ '.$timeRow.'
+
';
+ $counter++;
+ }
+
+ $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $courseId);
+ foreach ($list2 as $id => $interaction) {
+ $output .= '
+ | '.$interaction['order_id'].' |
+ '.$interaction['objective_id'].' |
+ '.$interaction['status'].' |
+ '.$interaction['score_raw'].' |
+ '.$interaction['score_max'].' |
+ '.$interaction['score_min'].' |
+
';
+ $counter++;
+ }
+ }
+
+ // Attempts listing by exercise.
+ if ($lpId == $my_lp_id) {
+ // Get attempts of a exercise.
+ if (!empty($lpId) && 'quiz' === $row['item_type']) {
+ $sql = "SELECT path FROM $tblLpItem
+ WHERE
+ c_id = $courseId AND
+ lp_id = '$lpId'";
+ $res_path = Database::query($sql);
+ $row_path = Database::fetch_array($res_path);
+
+ if (Database::num_rows($res_path) > 0) {
+ $sql = 'SELECT * FROM '.$tblStatsExercises.'
+ WHERE
+ exe_exo_id="'.(int) $row_path['path'].'" AND
+ status <> "incomplete" AND
+ exe_user_id="'.$userId.'" AND
+ orig_lp_id = "'.$lpId.'" AND
+ orig_lp_item_id = "'.$my_item_id.'" AND
+ c_id = '.$courseId.' AND
+ session_id = '.$sessionId.'
+ ORDER BY exe_date';
+ $res_attempts = Database::query($sql);
+ if (Database::num_rows($res_attempts) > 0) {
+ $n = 1;
+ while ($row_attempts = Database::fetch_array($res_attempts)) {
+ $my_score = $row_attempts['exe_result'];
+ $my_maxscore = $row_attempts['exe_weighting'];
+ $my_exe_id = $row_attempts['exe_id'];
+ $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
+ $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
+ $time_attemp = ' - ';
+ if ($mktime_start_date && $mktime_exe_date) {
+ $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
+ }
+ // Show only float when need it
+ if ($my_score == 0) {
+ $view_score = ExerciseLib::show_score(
+ 0,
+ $my_maxscore,
+ false
+ );
+ } else {
+ if ($my_maxscore == 0) {
+ $view_score = $my_score;
+ } else {
+ $view_score = ExerciseLib::show_score(
+ $my_score,
+ $my_maxscore,
+ false
+ );
+ }
+ }
+ $my_lesson_status = $row_attempts['status'];
+ if ($my_lesson_status == '') {
+ $my_lesson_status = learnpathitem::humanize_status('completed');
+ } elseif ($my_lesson_status == 'incomplete') {
+ $my_lesson_status = learnpathitem::humanize_status('incomplete');
+ }
+ $timeRow = ''.$time_attemp.' | ';
+ if ($hideTime) {
+ $timeRow = '';
+ }
+
+ $output .= '
+ | '.get_lang('Attempt').' '.$n.' |
+ '.$my_lesson_status.' |
+ '.$view_score.' |
+ '.$timeRow;
+ $output .= '
';
+ $n++;
+ }
+ }
+ }
+ }
+ }
+ }
+ $totalTime += $timeForTotal;
+ }
+ }
+
+ // Extend all "left green cross"
+ $totalScore = self::get_avg_student_score(
+ $userId,
+ $courseId,
+ [$lpId],
+ $sessionId,
+ false,
+ false
+ );
+
+ $totalTime = learnpathItem::getScormTimeFromParameter('js', $totalTime);
+ $totalTime = str_replace('NaN', '00'.$h.'00\'00"', $totalTime);
+
+ if (!$isAllowedToEdit && $resultDisabledExtAll) {
+ $finalScore = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
+ } else {
+ if (is_numeric($totalScore)) {
+ $finalScore = $totalScore.'%';
+ } else {
+ $finalScore = $totalScore;
+ }
+ }
+ $progress = learnpath::getProgress($lpId, $userId, $courseId, $sessionId);
+ $timeTotal = ''.$totalTime.' | ';
+ if ($hideTime) {
+ $timeTotal = '';
+ }
+
+ $output .= '
+ | '.get_lang('AccomplishedStepsTotal').' |
+ '.$progress.'% |
+ '.$finalScore.' |
+ '.$timeTotal.'
+
';
+
+ return $output;
+ }
+
/**
* @param int $user_id
* @param array $courseInfo
diff --git a/main/mySpace/myStudents.php b/main/mySpace/myStudents.php
index 3828e50c98..d991126413 100755
--- a/main/mySpace/myStudents.php
+++ b/main/mySpace/myStudents.php
@@ -270,6 +270,94 @@ switch ($action) {
Security::clear_token();
}
break;
+ case 'lp_stats_to_export_pdf':
+
+ $categoriesTempList = learnpath::getCategories($courseInfo['real_id']);
+ $categoryTest = new CLpCategory();
+ $categoryTest->setId(0);
+ $categoryTest->setName(get_lang('WithOutCategory'));
+ $categoryTest->setPosition(0);
+ $categories = [
+ $categoryTest,
+ ];
+
+ if (!empty($categoriesTempList)) {
+ $categories = array_merge($categories, $categoriesTempList);
+ }
+
+ $userEntity = api_get_user_entity($student_id);
+ $courseTable = '';
+ /** @var CLpCategory $item */
+ foreach ($categories as $item) {
+ $categoryId = $item->getId();
+ if (!learnpath::categoryIsVisibleForStudent($item, $userEntity, $courseInfo['real_id'], $sessionId)) {
+ continue;
+ }
+
+ $list = new LearnpathList(
+ $student_id,
+ $courseInfo,
+ $sessionId,
+ null,
+ false,
+ $categoryId,
+ false,
+ true
+ );
+ $flatList = $list->get_flat_list();
+ foreach ($flatList as $learnpath) {
+ $lpId = $learnpath['lp_old_id'];
+ $output = Tracking::getLpStatsContentToPdf(
+ $student_id,
+ $courseInfo,
+ $sessionId,
+ $lpId,
+ $learnpath['lp_name']
+ );
+ $courseTable .= $output;
+ }
+ }
+
+ $pdfTitle = get_lang('TestResult');
+ $sessionInfo = api_get_session_info($sessionId);
+ $studentInfo = api_get_user_info($student_id);
+ $tpl = new Template('', false, false, false, true, false, false);
+ $tpl->assign('title', $pdfTitle);
+ $tpl->assign('session_title', $sessionInfo['name']);
+ $tpl->assign('session_info', $sessionInfo);
+ $tpl->assign('table_course', $courseTable);
+
+ $content = $tpl->fetch($tpl->get_template('my_space/pdf_lp_stats.tpl'));
+
+ $params = [
+ 'pdf_title' => $pdfTitle,
+ 'session_info' => $sessionInfo,
+ 'course_info' => '',
+ 'pdf_date' => '',
+ 'student_info' => $studentInfo,
+ 'show_grade_generated_date' => true,
+ 'show_real_course_teachers' => false,
+ 'show_teacher_as_myself' => false,
+ 'orientation' => 'P',
+ ];
+ @$pdf = new PDF('A4', $params['orientation'], $params);
+ $pdf->setBackground($tpl->theme);
+ $mode = 'D';
+ $pdfName = $sessionInfo['name'].'_'.$studentInfo['complete_name'];
+ $pdf->set_footer();
+ $result = @$pdf->content_to_pdf(
+ $content,
+ '',
+ $pdfName,
+ null,
+ $mode,
+ false,
+ null,
+ false,
+ true,
+ false
+ );
+ break;
default:
break;
}
@@ -1299,6 +1387,18 @@ if (empty($details)) {
'data-title' => get_lang('CertificateOfAchievement'),
]
);
+ $sessionAction .= Display::url(
+ Display::return_icon('pdf.png', get_lang('TestResult'), [], ICON_SIZE_MEDIUM),
+ api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?'
+ .http_build_query(
+ [
+ 'action' => 'lp_stats_to_export_pdf',
+ 'student' => $student_id,
+ 'id_session' => $sId,
+ 'course' => $courseInfoItem['code'],
+ ]
+ )
+ );
}
echo $sessionAction;
} else {
diff --git a/main/template/default/my_space/pdf_lp_stats.tpl b/main/template/default/my_space/pdf_lp_stats.tpl
new file mode 100644
index 0000000000..3ca27600d1
--- /dev/null
+++ b/main/template/default/my_space/pdf_lp_stats.tpl
@@ -0,0 +1,27 @@
+
+ {{ logo }}
+
+
+{% if title %}
+
+ {{ title }}
+
+{% endif %}
+
+{% if session_title %}
+
+ {{ session_title }}
+
+{% endif %}
+
+{% if subtitle %}
+
+ {{ subtitle }}
+
+{% endif %}
+
+{% if table_course %}
+
+ {{ table_course }}
+
+{% endif %}