Merge branch 'chart' into 1.11.x

pull/2729/head
Yannick Warnier 7 years ago
commit 070246802b
  1. 158
      app/Resources/public/css/base.css
  2. 2
      main/course_progress/index.php
  3. 2
      main/inc/lib/sortable_table.class.php
  4. 3
      main/lang/english/trad4all.inc.php
  5. 6
      main/lang/french/trad4all.inc.php
  6. 3
      main/lang/spanish/trad4all.inc.php
  7. 229
      main/template/default/tracking/tracking_course_log.tpl
  8. 120
      main/tracking/courseLog.php

@ -2905,54 +2905,6 @@ form .formw .freeze {
}
/* header cell in data table in tools */
.data_table {
margin-bottom: 18px;
width: 100%;
border: 1px solid #DDDDDD;
}
.data_table td {
font-size: 12px;
}
.data_table th, .data_table td {
border-top: 1px solid #DDDDDD;
line-height: 18px;
padding: 10px;
}
.data_table th {
padding: 8px 10px;
font-weight: normal;
background-color: #EEEEEE;
border-bottom: 1px solid #DDDDDD;
color: #333;
}
.data_table th a {
color: #333;
}
.data_table_no_border {
border-collapse: collapse;
border-spacing: 0;
font-size: 12px;
margin-bottom: 15px;
margin-top: 8px;
text-align: left;
width: 100%;
}
.data_table_no_border td {
line-height: normal;
padding: 6px;
text-align: left;
vertical-align: middle;
}
.data_table tr.row_odd {
background-color: #F9F9F9;
}
.attendance-users-table .data_table tr.row_odd {
height: 64px;
@ -8207,6 +8159,116 @@ ul#toolnavbox-two li a.btn {
/**** END LP COLLAPSE CATEGORY ****/
/**** TRACKING ****/
.tracking .panel-body{
padding: 5px;
}
.tracking .tracking-icon{
display: block;
float: left;
height: 70px;
width: 50px;
text-align: center;
line-height: 70px;
font-size: 35px;
}
.tracking-box-title{
font-size: 14px;
margin-bottom: 20px;
}
.tracking .tracking-info{
padding: 5px 10px;
margin-left: 50px;
}
.tracking .tracking-info .tracking-text{
display: block;
font-size: 14px;
}
.tracking .tracking-info .tracking-number{
display: block;
font-weight: bold;
font-size: 2em;
}
.tracking-top-student .list-top{
margin: 0;
padding: 0;
list-style: none;
}
.tracking-top-student .list-top li{
display: inline-block;
width: 100%;
}
.tracking-top-student .list-top .avatar{
display: block;
float: left;
height: 55px;
width: 50px;
text-align: center;
line-height: 60px;
}
.tracking-top-student .list-top .info{
padding: 5px 10px;
margin-left: 60px;
}
.tracking-top-student .list-top .info .name{
font-size: 14px;
margin: 0;
padding: 0;
}
.tracking-top-student .list-top .info .progress{
height: 15px;
margin-top: 5px;
margin-bottom: 5px;
border: 1px solid #1e88e5;
background: #ffffff;
text-shadow: none;
}
.tracking-top-student .list-top .info .progress-bar{
line-height: 14px;
background-color: #1e88e5;
}
.tracking-top-student .list-top .avatar .round {
line-height: 39px;
color: #ffffff;
width: 40px;
height: 40px;
display: inline-block;
font-weight: 400;
text-align: center;
border-radius: 100%;
background: transparent;
font-size: 25px;
}
.tracking-top-student .list-top .avatar .round.trophy{
background: #ffb22b;
}
.tracking-top-student .list-top .avatar .round img{
border-radius: 100%;
}
.tracking-box-legend{
font-size: 10px;
color: #828282;
}
.tracking-student{
background-color: #725890;
border-color: #725890;
color: #ffffff;
}
.tracking-lessons{
background-color: #4199b0;
border-color: #4199b0;
color: #ffffff;
}
.tracking-exercise{
background-color: #ab4643;
border-color: #ab4643;
color: #ffffff;
}
.tracking-certificate{
background-color: #dd853d;
border-color: #dd853d;
color: #ffffff;
}
/* CSS Responsive */
@media (min-width: 1025px) and (max-width: 1200px) {
.sidebar-scorm {

@ -75,6 +75,7 @@ $thematic = new Thematic();
// thematic controller object
$thematic_controller = new ThematicController();
$thematic_data = [];
if (!empty($thematic_id)) {
// thematic data by id
@ -180,6 +181,7 @@ function check_per_custom_date(obj) {
$thematicControl = Session::read('thematic_control');
if ($action == 'thematic_list') {
$interbreadcrumb[] = ['url' => '#', 'name' => get_lang('ThematicControl')];
}

@ -130,7 +130,7 @@ class SortableTable extends HTML_Table
$table_id = $table_name.uniqid();
}
$this->table_id = $table_id;
parent::__construct(['class' => 'data_table table', 'id' => $table_id]);
parent::__construct(['class' => 'table table-bordered data_table', 'id' => $table_id]);
$this->table_name = $table_name;
$this->additional_parameters = [];
$this->param_prefix = $table_name.'_';

@ -8307,4 +8307,7 @@ $CourseHoursDuration = "Course duration (h)";
$AnnouncementWillBeSentTo = "Announcement will be sent to";
$FrmReadOutTextIntro = "You need attach a audio file according to the text, clicking on the %s icon.";
$CreateReadOutText = "Create read-out text";
$OutstandingStudents = "Outstanding students";
$PercentileScoresDistribution = "Percentile scores distribution";
$ProgressObtainedFromLPProgressAndTestsAverage = "Note: This progress is obtained through a combination of progress in the learning paths and average scores in the tests";
?>

@ -8237,4 +8237,10 @@ $CareDetailView = "Vue détaillée de vie étudiante";
$MoreDataAvailableInTheDatabaseButTrunkedForEfficiencyReasons = "Plus de données disponibles mais tronquées pour des raisons d'efficacité.";
$SendAnnouncementCopyToMyself = "M'envoyer une copie par e-mail.";
$CourseHoursDuration = "Durée du cours (h)";
$AnnouncementWillBeSentTo = "L'annonce sera envoyée à";
$FrmReadOutTextIntro = "Il est nécessaire d'ajouter un fichier audio correspondant au texte, en cliquant sur l'icône %s.";
$CreateReadOutText = "Créer texte lu";
$OutstandingStudents = "Apprenants distingués";
$PercentileScoresDistribution = "Distribution des scores en pourcents";
$ProgressObtainedFromLPProgressAndTestsAverage = "Note: Le progrès ci-dessus est obtenu par une combinaison du progrès dans les parcours et du score dans les exercices";
?>

@ -8332,4 +8332,7 @@ $CourseHoursDuration = "Duración del curso (h)";
$AnnouncementWillBeSentTo = "El anuncio será enviado a";
$FrmReadOutTextIntro = "Necesita adjuntar un archivo de audio de acuerdo al texto, haciendo click en el ícono %s.";
$CreateReadOutText = "Crear texto leído";
$OutstandingStudents = "Estudiantes destacados";
$PercentileScoresDistribution = "Distribución porcentual de notas";
$ProgressObtainedFromLPProgressAndTestsAverage = "Nota: Este progreso es obtenido a través de una combinación del progreso en las lecciones y de las notas promedias en los ejercicios";
?>

@ -0,0 +1,229 @@
<!-- tracking course log -->
<script type="text/javascript">
window.onload = function() {
var scoreStudent = document.getElementById("chart-score").getContext('2d');
var lastAccess = document.getElementById("chart-access").getContext('2d');
var jsonfile = {{ json_time_student }};
var labels = [];
var times = [];
Object.keys(jsonfile).forEach(function(key) {
//Names
labels.push(jsonfile[key].fullname);
//Time plataform total
times.push(jsonfile[key].lasttime);
});
var myChartAccess = new Chart(lastAccess,{
type: 'line',
data: {
labels: labels,
datasets: [{
data: times,
borderColor: "#3ba557",
backgroundColor: "#3ba557",
borderWidth: 1,
fill: false,
label: '{{ "Minutes"|get_lang }}',
}]
},
options: {
legend:{
display: false
},
scales: {
xAxes:[{
position: "bottom",
scaleLabel: {
display: true,
labelString: '{{ "Students"|get_lang }}',
},
ticks: {
display: false
}
}],
yAxes: [{
position: "left",
scaleLabel: {
display: true,
labelString: '{{ "Minutes"|get_lang }}',
}
}]
}
}
});
var myChartScore = new Chart(scoreStudent, {
type: 'bar',
data: {
labels: ["0-9%", "10-19%", "20-29%", "30-39%", "40-49%", "50-59%", "60-69%", "70-79%", "80-89%", "90-100%"],
datasets: [{
label: '{{ "NumberOfUsers"|get_lang }}',
data: {{ score_distribution }},
backgroundColor: {{ chart_colors }},
borderColor: {{ chart_colors }},
borderWidth: 1,
fill: false
}]
},
options: {
legend:{
display: false
},
scales: {
yAxes: [{
position: "left",
scaleLabel: {
display: true,
labelString: '{{ "NumberOfUsers"|get_lang }}',
},
ticks: {
display: true,
min: 0,
stepSize: 1
}
}],
xAxes:[{
position: "bottom",
scaleLabel: {
display: true,
labelString: "{{ 'PercentileScoresDistribution'|get_lang }}",
},
gridLines: {
display: true
},
ticks: {
display: false,
}
}],
}
}
});
};
</script>
<div class="tracking-course-summary">
<div class="row">
<div class="col-lg-3 col-sm-3">
<div class="panel panel-default tracking tracking-student">
<div class="panel-body">
<span class="tracking-icon">
<i class="fa fa-graduation-cap" aria-hidden="true"></i>
</span>
<div class="tracking-info">
<div class="tracking-text"> {{ "NumberOfUsers"|get_lang }}</div>
<div class="tracking-number">
{{ number_students }}
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-3">
<div class="panel panel-default tracking tracking-lessons">
<div class="panel-body">
<span class="tracking-icon">
<i class="fa fa-book" aria-hidden="true"></i>
</span>
<div class="tracking-info">
<div class="tracking-text"> {{ "CourseProgress"|get_lang }}</div>
<div class="tracking-number">
{{ students_completed_lp }}/{{ number_students }}
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-3">
<div class="panel panel-default tracking tracking-exercise">
<div class="panel-body">
<span class="tracking-icon">
<i class="fa fa-heartbeat" aria-hidden="true"></i>
</span>
<div class="tracking-info">
<div class="tracking-text"> {{ "ExerciseAverage"|get_lang }}</div>
<div class="tracking-number">
{{ students_test_score }}%
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-3">
<div class="panel panel-default tracking tracking-certificate">
<div class="panel-body">
<span class="tracking-icon">
<i class="fa fa-id-card-o" aria-hidden="true"></i>
</span>
<div class="tracking-info">
<div class="tracking-text"> {{ "CountCertificates"|get_lang }}</div>
<div class="tracking-number">
{{ certificate_count }}/{{ number_students }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<div class="tracking-chart">
<h4 class="tracking-box-title">{{ 'PercentileScoresDistribution'|get_lang }}</h4>
<canvas id="chart-score"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default tracking-top-student">
<div class="panel-body">
<h4 class="tracking-box-title">{{ 'OutstandingStudents'|get_lang }}</h4>
<ul class="list-top">
{% for student in top_students %}
{% set counter = counter + 1 %}
{% if counter <= 3 %}
<li>
<div class="avatar">
<span class="round">
<img title="{{ student.fullname }}" alt="{{ student.fullname }}" src="{{ student.avatar.dir }}{{ student.avatar.file }}" width="40px">
</span>
</div>
<div class="info">
<h3 class="name">{{ student.fullname }}</h3>
<div class="progress">
<div class="progress-bar progress-bar-success progress-bar-striped" role="progressbar" aria-valuenow="{{ student.score }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ student.score }}%;">
{{ student.score }}%
</div>
</div>
</div>
</li>
{% endif %}
{% endfor %}
</ul>
<span class="tracking-box-legend">{{ 'ProgressObtainedFromLPProgressAndTestsAverage'|get_lang }}</span>
</div>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<h4 class="tracking-box-title">{{ "TotalTimeSpentInTheCourse"|get_lang }}</h4>
<canvas id="chart-access"></canvas>
</div>
</div>
</div>
</div>
</div>

@ -2,21 +2,26 @@
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
/**
* @package chamilo.tracking
*/
require_once __DIR__.'/../inc/global.inc.php';
$current_course_tool = TOOL_TRACKING;
$courseInfo = api_get_course_info(api_get_course_id());
$courseCode = $courseInfo['code'];
$courseId = api_get_course_id();
$courseInfo = api_get_course_info($courseId);
//keep course_code form as it is loaded (global) by the table's get_user_data
$course_code = $courseCode = $courseInfo['code'];
$from_myspace = false;
$from = isset($_GET['from']) ? $_GET['from'] : null;
// Starting the output buffering when we are exporting the information.
$export_csv = isset($_GET['export']) && $_GET['export'] == 'csv' ? true : false;
$session_id = isset($_REQUEST['id_session']) ? (int) $_REQUEST['id_session'] : 0;
$session_id = $sessionId = api_get_session_id();
$htmlHeadXtra[] = api_get_js('chartjs/Chart.min.js');
$htmlHeadXtra[] = ' ';
$this_section = SECTION_COURSES;
if ($from == 'myspace') {
@ -65,7 +70,7 @@ if (api_is_drh()) {
// then check if he has also been given access to the corresponding courses
$coursesFollowedList = CourseManager::get_courses_followed_by_drh(api_get_user_id());
$coursesFollowedList = array_keys($coursesFollowedList);
if (!in_array(api_get_course_id(), $coursesFollowedList)) {
if (!in_array($courseId, $coursesFollowedList)) {
api_not_allowed(true);
exit;
}
@ -136,8 +141,6 @@ $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);
$sessionId = api_get_session_id();
// Breadcrumbs.
if (isset($_GET['origin']) && $_GET['origin'] == 'resume_session') {
$interbreadcrumb[] = [
@ -157,22 +160,28 @@ if (isset($_GET['origin']) && $_GET['origin'] == 'resume_session') {
$view = isset($_REQUEST['view']) ? $_REQUEST['view'] : '';
$nameTools = get_lang('Tracking');
//Template Tracking
$tpl = new Template($nameTools);
// getting all the students of the course
if (empty($session_id)) {
// Registered students in a course outside session.
$a_students = CourseManager::get_student_list_from_course_code(
api_get_course_id()
$courseId
);
} else {
// Registered students in session.
$a_students = CourseManager::get_student_list_from_course_code(
api_get_course_id(),
$courseId,
true,
$sessionId
);
}
$nbStudents = count($a_students);
$user_ids = array_keys($a_students);
$extra_info = [];
$userProfileInfo = [];
// Getting all the additional information of an additional profile field.
@ -243,6 +252,8 @@ echo Display::toolbarAction(
);
$course_name = get_lang('Course').' '.$courseInfo['name'];
if ($session_id) {
$titleSession = Display::return_icon(
'session.png',
@ -330,6 +341,83 @@ if ($showReporting) {
}
}
// Show the charts part only if there are students subscribed to this course/session
if ($nbStudents > 0) {
$usersTracking = TrackingCourseLog::get_user_data(null, $nbStudents, null, 'DESC');
$numberStudentsCompletedLP = 0;
$averageStudentsTestScore = 0;
$scoresDistribution = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
$userScoreList = [];
$listStudentIds = [];
$timeStudent = [];
$certificateCount = 0;
$category = Category::load(
null,
null,
$course_code,
null,
null,
$session_id
);
foreach ($usersTracking as $userTracking) {
$userId = UserManager::get_user_id_from_username($userTracking[3]);
if ($userTracking[5] === '100%') {
$numberStudentsCompletedLP++;
}
$averageStudentTestScore = substr($userTracking[7], 0, -1);
$averageStudentsTestScore += $averageStudentTestScore;
if ($averageStudentTestScore === '100') {
$reducedAverage = 9;
} else {
$reducedAverage = floor($averageStudentTestScore / 10);
}
$scoresDistribution[$reducedAverage]++;
$scoreStudent = substr($userTracking[5], 0, -1) + substr($userTracking[7], 0, -1);
list($hours, $minutes, $seconds) = preg_split('/:/', $userTracking[4]);
$myTime = round((3600 * $hours + 60 * $minutes + $seconds) / 60);
$certificate = false;
if (isset($category[0]) && $category[0]->is_certificate_available($userId)) {
$certificate = true;
$certificateCount++;
}
$listStudent = [
'id' => $userId,
'fullname' => $userTracking[2] . ', ' . $userTracking[1],
'score' => floor($scoreStudent / 2),
'lasttime' => $myTime,
'avatar' => UserManager::get_user_picture_path_by_id($userId),
'certicate' => $certificate
];
$listStudentIds[] = $userId;
$userScoreList[] = $listStudent;
}
uasort($userScoreList, 'sort_by_order');
$averageStudentsTestScore = round($averageStudentsTestScore / $nbStudents);
$colors = ChamiloApi::getColorPalette(true, true, 10);
$tpl->assign('chart_colors', json_encode($colors));
$tpl->assign('certificate_count', $certificateCount);
$tpl->assign('score_distribution', json_encode($scoresDistribution));
$tpl->assign('json_time_student', json_encode($userScoreList));
$tpl->assign('students_test_score', $averageStudentsTestScore);
$tpl->assign('students_completed_lp', $numberStudentsCompletedLP);
$tpl->assign('number_students', $nbStudents);
$tpl->assign('top_students', $userScoreList);
$trackingSummaryLayout = $tpl->get_template("tracking/tracking_course_log.tpl");
$content = $tpl->fetch($trackingSummaryLayout);
echo $content;
}
$html .= Display::page_subheader2(get_lang('StudentList'));
// PERSON_NAME_DATA_EXPORT is buggy
@ -386,12 +474,13 @@ if (count($a_students) > 0) {
$all_datas = [];
$course_code = $_course['id'];
$user_ids = array_keys($a_students);
$table = new SortableTable(
'users_tracking',
['TrackingCourseLog', 'get_number_of_users'],
['TrackingCourseLog', 'get_user_data'],
function () use ($usersTracking) {
return $usersTracking;
},
1
);
@ -400,6 +489,8 @@ if (count($a_students) > 0) {
$parameters['from'] = isset($_GET['myspace']) ? Security::remove_XSS($_GET['myspace']) : null;
$table->set_additional_parameters($parameters);
$headers = [];
// tab of header texts
$table->set_header(0, get_lang('OfficialCode'), true);
@ -559,3 +650,10 @@ if ($export_csv) {
exit;
}
Display::display_footer();
function sort_by_order($a, $b)
{
return $a['score'] <= $b['score'];
}

Loading…
Cancel
Save