From 619363db82da1f9ac9c62c5fc6a971a754ca4e32 Mon Sep 17 00:00:00 2001 From: Myriam Delaruelle <Myriam Delaruelle@bdn-un-mdelarue.ad.univ-lorraine.fr> Date: Wed, 20 Jan 2021 15:28:08 +0100 Subject: [PATCH] template suivi - refonte table suivi --- renderer.php | 4 + styles.css | 135 ++++++++++++++- suivi_unit.php | 273 +++++++++++++++++++++++++++---- templates/course-header.mustache | 7 +- templates/suivi-table.html | 31 ++++ templates/suivi-table.mustache | 31 ++++ templates/suivi.mustache | 76 +++++++++ view/view_suivi_unit3.php | 24 +-- 8 files changed, 524 insertions(+), 57 deletions(-) create mode 100644 templates/suivi-table.html create mode 100644 templates/suivi-table.mustache create mode 100644 templates/suivi.mustache diff --git a/renderer.php b/renderer.php index c42167b..a00c6e0 100644 --- a/renderer.php +++ b/renderer.php @@ -1268,4 +1268,8 @@ class format_iena_renderer extends format_topics_renderer{ $this->get_iena_sections($course); } } + + public function display_completion($data){ + echo $this->render_from_template('format_iena/suivi', $data); + } } diff --git a/styles.css b/styles.css index 8ab2e49..dd89a64 100644 --- a/styles.css +++ b/styles.css @@ -451,7 +451,7 @@ ul.nav.navbar-nav.ml-auto { } /* Course module name in a bubble */ -.iena-h-prog-mod-item span { +.iena-h-prog-mod-item span.popover-module { display: none; position: absolute; top: -3rem; @@ -467,10 +467,10 @@ ul.nav.navbar-nav.ml-auto { text-align: center; overflow: hidden; } -.iena-h-prog-mod-item:hover span { +.iena-h-prog-mod-item:hover span.popover-module { display: inline; } -.iena-h-prog-mod-item span:before { +.iena-h-prog-mod-item span.popover-module:before { content:''; width:100%; height:100%; @@ -507,4 +507,131 @@ ul.nav.navbar-nav.ml-auto { #days-text{ margin:0 20px 0 5px; -} \ No newline at end of file +} + + + + + +/*################################# CSS TABLEAU DE SUIVI ##########################################*/ +/* Permet la rotation des entêtes du tableau */ +th.th-rotate { + /* Something you can count on */ + height: 140px; + white-space: nowrap; +} + +th.th-rotate > div { + transform: + /* Magic Numbers */ + translate(17px, 49px) + /* 45 is really 360 - 45 */ + rotate(315deg); + width: 30px; +} +th.th-rotate > div > span { + border-bottom: 1px solid #ccc; + padding: 5px 10px; +} +/* Change la couleur une ligne sur deux dans le tableau */ +#table-body tr:nth-child(even) { background: #fafafa; } +#table-body tr:nth-child(odd) { background: #eee; } + +#table-body tr{ + border-bottom: 1px solid white; +} + + +/* Colorie la cellule en fonction de l'achèvement */ + +/*.state-0 { background-color: rgba(0, 143, 132, 0.23); } +.state-1 { background-color: #009085; } +.state-2 { background-color: #009085; } +.state-3 { background-color: rgba(214, 141, 1, 0.4); }*/ + +.state-0 { background-color: #CECECE; } +.state-1 { background-color: #40ACA3; } +.state-2 { background-color: #40ACA3; } +.state-3 { background-color: #F3A41C}; + + +.state-0 span.icon-progress, .state-1 span.icon-progress, .state-2 span.icon-progress, .state-3 span.icon-progress{ + content: ""; + display: block; +} + +.state-1 span.icon-progress::after{ + content: "\f1db"; + font-family: FontAwesome; width: 100%; display: inline-block; +} + +.state-2 span.icon-progress::after{ + content: "\f05d"; + font-family: FontAwesome; width: 100%; display: inline-block; text-align: center; +} +.state-3 span.icon-progress::after{ + content: "\f1db"; + font-family: FontAwesome; width: 100%; display: inline-block; text-align: center; +} + + +#first-column-head { + vertical-align: bottom; + padding-bottom: 0.5rem; +} + +.pointer-help { + cursor: help; +} + +.stud_perc { + min-width: 45px; + display: inline-block; +} + +#table-body td { border-right: 1px solid #eee; } + +/* Scroll de la table */ +#table-body { + display: block; + /*overflow-y:scroll;*/ + overflow: auto; + margin-right: 10rem; + height: 300px; + height: 50vh; +} + +#suivi { + margin-bottom: 1rem; +} + +#suivi thead { + display: block; + /*overflow-x:scroll;*/ + overflow:auto; + height: 143px; +} + +#suivi thead tr th:nth-child(1) { + width:20em; + min-width:20em; +} +#suivi thead tr th { + min-width:32px; +} +#suivi tbody tr th { + width:20em; + min-width:20em; + word-break: break-word; +} +#suivi tbody tr td { + min-width:32px; +} + +#suivi input[type="checkbox"] { + margin: 5px; + font-size: 1rem; +} + +.mb1 { margin-bottom: 1rem; } +.mt1 { margin-top: 1rem; } \ No newline at end of file diff --git a/suivi_unit.php b/suivi_unit.php index ed45ff9..8332776 100644 --- a/suivi_unit.php +++ b/suivi_unit.php @@ -40,6 +40,9 @@ global $COURSE, $DB, $USER; // ]; // csv_export_writer::download_array("machin.csv", $arr); + + + // Defines the id of the course with a get parameter $courseID = required_param('courseid', PARAM_INT); // Define the url of the view @@ -50,10 +53,16 @@ $PAGE->set_url($url); // Getting DB information (*) of the course $course = $DB->get_record('course', array('id' => $courseID), '*', MUST_EXIST); + require_login($course, false, NULL); $PAGE->set_title($course->fullname); $PAGE->set_heading($course->fullname . " – Suivi des étudiants"); + +require_once($CFG->libdir . '/completionlib.php'); +$completion = new completion_info($course); +$context = context_course::instance($COURSE->id); + // if (!has_capability('moodle/course:sectionvisibility', $context = context_course::instance($courseID), $USER->id)) { if (!has_capability('course/iena:suivi', $context = context_course::instance($courseID), $USER->id)) { $link = $CFG->wwwroot . '/course/view.php?id=' . $courseID; @@ -62,6 +71,234 @@ if (!has_capability('course/iena:suivi', $context = context_course::instance($co } echo $OUTPUT->header(); +/*$filters = [ + "all" => "Tous", + "=100" => "100%", + "=0" => "0%", + "<100" => "<100%", + "<50" => "<50%", + "<25" => "<25%", + ">50" => ">=50%", + ">25" => ">=25%", + ">0" => ">0%", +];*/ + +$filters=[ + "0"=>array( + "value"=>"all", + "name"=>"Tous", + ), + "1"=>array( + "value"=>"=100%", + "name"=>"100%", + ), + "2"=>array( + "value"=>"=0", + "name"=>"0", + ), + "3"=>array( + "value"=>"<100", + "name"=>"<100%", + ), + "4"=>array( + "value"=>"<50", + "name"=>"<50%", + ), + "5"=>array( + "value"=>"<25", + "name"=>"<25%", + ), + "6"=>array( + "value"=>">50", + "name"=>">=50%", + ), + "7"=>array( + "value"=>">25", + "name"=>">=25%", + ), + "8"=>array( + "value"=>"0", + "name"=>">0%", + ), +]; + + + +/* Groupe du GET provenant du sélecteur de la page du cours, ou premier groupe de l'utilisateur, ou groupe 0 (tous les groupes). */ +if(isset($_GET['groupid'])){ + $active_group_id = $_GET['groupid']; +} +else{ + $active_group_id=NULL; +} + +// @TODO à supprimer si pas utilisé dans le JS (mettre dans la clause IF pour éviter un appel inutile si le GET existe) +$current_user_groups_ids=array(); +/* Si pas de groupe dans le get ou groupe 0 (tous) mais pas le droit => premier groupe existant du user ou groupe 0 (tous) */ +if ($active_group_id == NULL || ($active_group_id == 0 && !has_capability('course/iena:suivi_edit', $context, $USER->id)) ) { + if ( count($current_user_groups_ids) == 0 ) { + $active_group_id = 0; + } else { + $current_user_groups_ids = groups_get_user_groups($COURSE->id, $USER->id)[0]; + $active_group_id = $current_user_groups_ids[0]; + } +} + +/* Groupes du cours avec id, nom et liste des id utilisateur de tous les membres (3 clefs d'un tableau de groupes : id, name, member) */ +if ( $active_group_id == 0 ) { + //On récupère les ids des membres + $groups = groups_get_all_groups($COURSE->id, 0, 0, 'g.*', true); +} else { + //On ne récupère pas les ids des membres + $groups = groups_get_all_groups($COURSE->id, 0, 0, 'g.*', false); +} + +$current_user_groups = []; +$active_group_name = ""; +foreach ($groups as $group) { + /* Récupération du nom du group actif au passage */ + if ( $group->id == $active_group_id ) { + $active_group_name = $group->name; + $group->selected='selected'; + } + foreach ($current_user_groups_ids as $ugi) { + if ( $group->id == $ugi ) { + $current_user_groups[] = $group; + } + } +} + +/* Liste de tous les utilisateurs avec : +- informations personnelles (tous les utilisateurs du cours) +- liste des activités avec état d'achèvement (si achevées, sinon vide) */ +$progress = $completion->get_progress_all( + '', + array(), + $active_group_id, + 'u.lastname ASC, u.firstname ASC', + '', + '', + $context +); + +//Formate l'achèvement d'activités pour le tableau de suivi. Pour chaque étudiant, on aura dans le bon ordre la liste des modules et le completionstate +function format_progress($progress, $modules){ + + $students=array(); + foreach ($progress as $prog_info) { + $progress_student=new StdClass(); + $progress_student->name=$prog_info->firstname." ".$prog_info->lastname; + $progress_student->id=$prog_info->id; + $progress_student->email=$prog_info->email; + $progress_student->progress=array(); + foreach($modules as $key=>$module){ + + $module_progress=new StdClass(); + + if(!isset($prog_info->progress[$module->id])){ + + $module_progress->completionstate='0'; + } + else{ + $module_progress->completionstate=$prog_info->progress[$module->id]->completionstate; + } + $module_progress->namemodule=$module->name; + + $progress_student->progress[$key]=$module_progress; + } + + $students[]=$progress_student; + } + return $students; + +} + +/* Ajoute les groupes de l'utilisateur pour l'afficher dans l'export */ +/* Si le user a le droit d'afficher pour tous les groupes ou qu'il n'y en a pas, on récupère le groupe de l'utilisateur pour qu'il puisse être téléchargé dans le tableau blobal intergroupe. */ +if ( $active_group_id == 0 ) { + foreach ($progress as $prog) { + + + $prog->groups = ""; + foreach ($groups as $group) { + if ( in_array($prog->id, $group->members) ) { + $prog->groups .= $group->name . " "; + } + } + } +} + +/* Liste de toutes les activités du cours (sauf en attente de suppression). La liste est épurée et constitue un tableau d'objet. */ +$activities = $completion->get_activities(); +$modules = []; +foreach ($activities as $activity) { + $module = new StdClass(); + $module->id = $activity->id; + $module->name = $activity->name; + $displayname = format_string($activity->name, true, array('context' => $activity->context)); + $module->displayname = strlen($displayname) > 20 ? mb_substr($displayname, 0, 19, 'UTF-8').'…' : $displayname; + $module->section = $activity->section; + $modules[] = $module; +} + +// Liste des sections du cours +$modinfo = get_fast_modinfo($COURSE->id); +$sections_info_all = $modinfo->get_section_info_all(); +$sections = []; + +// Section du get acquise depuis le clic sur l'indicateur dans la page du cours, section depuis laquelle on a cliqué sur le bouton pour voir le suivi. + +if(isset($_GET['sectionid'])){ + $active_section_id = $_GET['sectionid']; +} +else{ + $active_section_id=0; +} + + +foreach ($sections_info_all as $key => $section_info) { + $section = new StdClass(); + $section->id = $section_info->id; + $section->name = $section_info->name === NULL || $section_info->name === '' ? 'Section ' . $key : $section_info->name; + if($section->id==$active_section_id){ + $section->selected="selected"; + } + + $sections[] = $section; + +} + + +// $active_section_name = "Toutes les sections"; +// foreach ($sections as $section) { +// if ($section->id == $active_section_id) { +// $active_section_name = $section->name; +// } +// } + +// Filtre actif si il existe (si la page est rechargée pour un filtre de groupe p.ex.) +if (isset($_GET['filter'])) { + $filter = $_GET['filter']; +} else { + $filter = 'all'; +} + +$data=array(); +$data['filters']=$filters; +$data["sections"]=$sections; +$data["groups"]=array_values($groups); +$data["modules"]=$modules; + + +ini_set('xdebug.var_display_max_depth', 99); + + + +$data["students"]=format_progress($progress, $modules); + + + + // If a post is sent trought the page if ($_POST) { require_once("$CFG->libdir/formslib.php"); @@ -71,35 +308,13 @@ if ($_POST) { echo $view->get_content($usersID); } else { - // $view_param_indicateur = new view_param_indicateur(); - // $sections = $view_param_indicateur->get_course_sections_modules(); - // $Tab_id_modules = array(); - // $i = 0; - - // foreach ($sections as $section) { - // foreach ($section->ressources as $mod) { - // $Tab_id_modules[$i] = $mod->id; - // $i++; - // } - // } - - // foreach ($sections as $section) { - // foreach ($Tab_id_modules as $id_module) { - // $verif_db = $DB->get_record('format_iena_settings', array('cmid' => $id_module, 'sectionid' => $section->id), '*'); - - // if ($verif_db == false) { - // $format_iena_setting_data = new stdClass(); - // $format_iena_setting_data->cmid = $id_module; - // $format_iena_setting_data->hide = 0; - // $format_iena_setting_data->courseid = $COURSE->id; - // $format_iena_setting_data->sectionid = $section->id; - // $DB->insert_record('format_iena_settings', $format_iena_setting_data, false); - // } - // } - // } - - // require_once('view/view_suivi_unit.php'); - require_once('view/view_suivi_unit3.php'); + //require_once('view/view_suivi_unit3.php'); + + $renderer = $PAGE->get_renderer('format_iena'); + $renderer->display_completion($data); } + + + echo $OUTPUT->footer(); \ No newline at end of file diff --git a/templates/course-header.mustache b/templates/course-header.mustache index 8b40b49..31d931c 100644 --- a/templates/course-header.mustache +++ b/templates/course-header.mustache @@ -26,7 +26,12 @@ </div> {{#modules}} - <a href="{{url}}" class="iena-h-prog-mod-item iena-g-prog-{{completion}}"><span>{{name}}</span></a> + <!-- <a href="{{url}}" class="iena-h-prog-mod-item iena-g-prog-{{completion}}"><span>{{name}}</span></a> --> + + <a href="{{url}}" class="iena-h-prog-mod-item state-{{completion}}"> + <!--<span class="icon-progress"></span>--> + <span class="popover-module">{{name}}</span> + </a> {{/modules}} </div> diff --git a/templates/suivi-table.html b/templates/suivi-table.html new file mode 100644 index 0000000..6cefc45 --- /dev/null +++ b/templates/suivi-table.html @@ -0,0 +1,31 @@ +<table id="suivi"> + <thead> + <tr id="modules"> + <th id="first-column-head">Etudiants</th> + {{#modules}} + <th class="rotate" data-fullname="{{name}}" data-section="{{section}}"> + <div> + <span> + {{displayname}} + </span> + </div> + </th> + {{/modules}} + </tr> + </thead> + <tbody id="table-body"> + {{#students}} + <tr data-userid="{{id}}" data-percent={{percent}}> + <th> + <input type="checkbox" name="checkstudent"> + <span class="stud_perc">{{percent}}</span> + <a href="{{report_link}}" target="_blank"><i class="icon fa fa-graduation-cap fa-fw"></i></a> + <a href="{{message_link}}" target="_blank"><i class="icon fa fa-envelope fa-fw"></i></a>{{firstname}} {{lastname}} + </th> + {{#progress}} + <td title="{{name}}" data-section="{{id}}" class="pointer-help state-{{completionstate}}"></td> + {{/progress}} + </tr> + {{/students}} + </tbody> +</table> \ No newline at end of file diff --git a/templates/suivi-table.mustache b/templates/suivi-table.mustache new file mode 100644 index 0000000..fe7574e --- /dev/null +++ b/templates/suivi-table.mustache @@ -0,0 +1,31 @@ +<table id="suivi"> + <thead> + <tr id="modules"> + <th id="first-column-head">Etudiants</th> + {{#modules}} + <th class="th-rotate" data-fullname="{{name}}" data-section="{{section}}"> + <div> + <span> + {{displayname}} + </span> + </div> + </th> + {{/modules}} + </tr> + </thead> + <tbody id="table-body"> + {{#students}} + <tr data-userid="{{id}}" data-percent={{percent}}> + <th> + <input type="checkbox" name="checkstudent"> + <span class="stud_perc">{{percent}}</span> + <a href="{{report_link}}" target="_blank"><i class="icon fa fa-graduation-cap fa-fw"></i></a> + <a href="{{message_link}}" target="_blank"><i class="icon fa fa-envelope fa-fw"></i></a>{{name}} + </th> + {{#progress}} + <td title="{{namemodule}}" data-section="{{id}}" class="pointer-help state-{{completionstate}}"><span class="icon-progress"></span></td> + {{/progress}} + </tr> + {{/students}} + </tbody> +</table> \ No newline at end of file diff --git a/templates/suivi.mustache b/templates/suivi.mustache new file mode 100644 index 0000000..c883ebc --- /dev/null +++ b/templates/suivi.mustache @@ -0,0 +1,76 @@ +<div> + + <h2 class="mb1" style="display: inline-block;">Suivi des étudiants</h2> + + <div style="float: right; line-height: 3rem;"> + <a target="_blank">Paramétrer le suivi</a> + + <a target="_blank" href="">Vue classique</a> + </div> + +</div> + +<form style="clear: right;" class="form-inline" action="" method="GET"> + <input name="courseid" type="number" hidden="hidden" style="display: none;" value=""> + + <label class="sr-only" for="section-select">Section</label> + <div class="input-group"> + <div class="input-group-prepend"> + <div class="input-group-text">Section</div> + </div> + <select class="custom-select mr-sm-2" id="section-select" name="sectionid"> + <option value="0">Toutes</option> + {{#sections}} + <option selected="{{selected}}" value={{id}}>{{name}}</option> + {{/sections}} + </select> + </div> + + <label class="sr-only" for="group-select">Groupes</label> + <div class="input-group"> + <div class="input-group-prepend"> + <div class="input-group-text">Groupes</div> + </div> + <select class="custom-select mr-sm-2" id="group-select" name="groupid"> + <option value="0">Tous</option> + + <optgroup label="Mes groupes"> + {{#current_user_groups}} + + <option selected='{{selected}}' value='{{id}}'>{{name}}</option>"; + + {{/current_user_groups}} + </optgroup> + + + <optgroup label="Autres groupes"> + {{#groups}} + <option selected='{{selected}}' value='{{id}}'>{{name}}</option>"; + {{/groups}} + </optgroup> + + + + </select> + </div> + + <label class="sr-only" for="filter-select">Filtre %</label> + <div class="input-group"> + <div class="input-group-prepend"> + <div class="input-group-text">Filtre</div> + </div> + <select class="custom-select mr-sm-2" id="filter-select" name="filter"> + + {{#filters}} + <option selected='{{selected}}' value='{{value}}'>{{name}}</option> + {{/filters}} + + </select> + </div> + + + +</form> + + +{{> format_iena/suivi-table}} \ No newline at end of file diff --git a/view/view_suivi_unit3.php b/view/view_suivi_unit3.php index 225212c..830b84a 100644 --- a/view/view_suivi_unit3.php +++ b/view/view_suivi_unit3.php @@ -335,29 +335,7 @@ if (isset($_GET['filter'])) { </select> </div> - <!-- <label class="my-1 mr-2" for="filter-select">Filtre %</label> - <select class="custom-select my-1 mr-sm-2" id="filter-select" name="filter"> - <?php - // $filters = [ - // "all" => "Tous", - // "=100" => "100%", - // "=0" => "0%", - // "<100" => "<100%", - // "<50" => "<50%", - // "<25" => "<25%", - // ">50" => ">50%", - // ">25" => ">25%", - // ">0" => ">0%", - // ]; - // foreach ($filters as $value => $name) { - // if ( $value == $filter ) { - // echo "<option selected='selected' value='$value'>$name</option>"; - // } else { - // echo "<option value='$value'>$name</option>"; - // } - // } - ?> - </select> --> + </form> -- GitLab