Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

Commit 39d068a6 authored by Francois Gannaz's avatar Francois Gannaz
Browse files

draft of tem/studentchoice

parent 07743ad7
This diff is collapsed.
This diff is collapsed.
/*
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
/**
* Usage:
*
* TeamChoice.start("#container", {...user...}, {...mission...}, {...class...});
*
*/
var TeamChoice = (function (){
"use strict";
let api = {
stateToLoadRequest: function(s) {
return {};
},
stateToSelectRequest: function(s) {
return {};
}
};
var state = {
// 4 parameters fixed at init
userid: null,
group: null, // { id, name }
mission: null, // { id, code, name, description }
// other parameters are updated, see TeamChoice.php for their content
teams: [],
settings: {},
status: {
allowCreation: true,
allowMoreThanOpt: false,
numTeams: 0,
usersInClass: 0,
usersInTeams: 0
},
selectedTeam: null, // null if not in a team, report record if team exists
alerts: new Set
};
/**
* @param {string} message
* @param {string} category "danger", "warning", "info", "success"
* @param {integer} timeout milliseconds before removing the alert
* @return {undefined}
*/
function addAlert(message, category, timeout) {
if (typeof category === 'undefined') {
category = "danger";
}
if (typeof timeout === 'undefined') {
timeout = 5000;
}
var alert = {message: message, category: category};
state.alerts.add(alert);
if (timeout) {
window.setTimeout(
function() {
state.alerts.delete(alert);
m.redraw();
},
timeout
);
}
}
function resetAlerts() {
state.alerts.clear();
}
/**
* Fetch the teams and related data from the DB (AJAX).
*
* @return {unresolved}
*/
function load() {
return m.request(
api.stateToLoadRequest(state)
).then(function (response) {
if (!response || !response.settings) {
// no team settings, go away ;-)
window.location.href = "/pages/report_choice.php";
return;
}
state.settings = response.settings;
state.teams = response.teams;
state.status = response.status;
state.selectedTeam = response.report;
}).catch(function (error) {
addAlert("Erreur interne. Merci d'utiliser le bouton en pied de page pour nous la signaler.", "danger");
});
}
/**
* Save into the DB (AJAX) and display a notification.
*
* @return {unresolved}
*/
function selectTeam(team) {
resetAlerts();
return m.request({
method: "POST",
url: '/functions_php/team_choice_ajax.php?func=selectTeam',
data: {
id_user: state.userid,
id_class: state.group.id,
id_mission: state.mission.id,
id_report: team ? team.id_report : null
},
// do not send a JSON body
serialize: m.buildQueryString,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function (response) {
addAlert(response.message, response.status, 0);
load();
}).catch(function (error) {
addAlert("Refus de l'inscription. " + error.message, error.status, 0);
load();
});
}
var AlertsView = {
view: function() {
if (state.alerts.length === 0) {
return "";
}
return m("div.alerts", Array.from(state.alerts).map(function(x) {
return m("div", {"class": "alert alert-" + x.category}, x.message);
}));
}
};
// Component for simple info on the global settings
var StartButtonView = {
view: function() {
if (state.selectedTeam) {
return m("div.teamchoice-start",
m("a.btn.btn-primary", {
href: '/pages/process_report.php?id_report=' + state.selectedTeam.id_report
}, "Commencer la mission")
);
} else {
return "";
}
}
};
// Component for simple info on the global settings
var SimpleContextView = {
view: function() {
return m("div#teamchoice-context", [
m("p", [
"La taille souhaitée pour les équipes est de ",
m("strong", state.settings.size_opt),
" personnes."
])
]);
}
};
// Component for the various info on teams and the global settings
var FullContextView = {
view: function() {
return m("div#teamchoice-context", [
m("h2", [
"Mission : ",
m("span.mission-name", state.mission.name)
]),
m("p.alert.alert-info", [
"Votre classe ",
m("em.group-name", state.group.name),
" est inscrite à la mission ",
m("em.mission-name", state.mission.name),
" où chacun peut choisir son équipe."
]),
state.settings ?
m("div.team-settings", m("table.table",
m("caption", "Paramètres des équipes"),
m("tbody", [
m('tr', [m("td", "Taille souhaitée pour chaque équipe"), m("td.team-info-size_opt", state.settings.size_opt + " personnes")]),
m('tr', [m("td", "Nombre maximum d'équipes"), m("td.team-info-teams_max", state.settings.teams_max)]),
m('tr', [m("td", "Déjà inscrits"), m("td", state.status.usersInTeams)]),
m('tr', [m("td", "Non inscrits"), m("td", state.status.usersInClass - state.status.usersInTeams)]),
m('tr', [m("td", "Taille maximale d'une équipe"), m("td", state.settings.size_max)]),
m('tr', [m("td", "Taille minimale d'une équipe"), m("td", state.settings.size_min)]),
m('tr', [m("td", "Nombre maximum d'équipes"), m("td", state.settings.teams_max)])
])
))
: "",
m("p.alert.alert-warning", [
m("strong", state.selectedTeam ?
"Vous êtes inscrit dans une équipe."
: "Vous n'êtes pas encore inscrit dans une équipe. Vous devez en sélectionner ou en créer une pour commencer la mission.")
])
]);
}
};
// Component for displaying a new (empty) team
var NewTeamView = {
view: function() {
return m("tr.teamchoice-newteam", [
m("td", ""),
m("td.members", m("em", "Nouvelle équipe")),
m("td", this.displayAction())
]);
},
displayAction: function() {
if (state.selectedTeam) {
return "";
}
if (state.teams.length >= state.settings.teams_max) {
return "Le maximum d'équipes est atteint.";
}
if (state.status.allowCreation) {
return m("a.btn", {href: "#", onclick: function() { selectTeam(null); }}, "Créer");
} else {
return m("span.label.label-defaulta", "désactivé");
}
}
};
// Component for displaying and selecting an existing team
var TeamView = {
view: function(vnode) {
var team = vnode.children[0];
//console.log(team);
var isMyTeam = state.selectedTeam && state.selectedTeam.id_report == team.id_report;
return m("tr.teamchoice-team[data-id-report=" + team.id_report + "]" + (isMyTeam ? ".my-team" : ""), [
m("td", team.name),
m("td.members", vnode.attrs, team.members.map(function(name){
return m("span.user", name + " ");
})),
m("td", this.displayAction(team))
]);
},
displayAction: function(team) {
if (state.selectedTeam) {
return "";
} else if (team.teamStatus === 'open') {
return m("a.btn.team-status-" + team.teamStatus, {href: "#", onclick: function() { selectTeam(team); }}, "Rejoindre");
} else {
return m("span.label.label-success", "équipe complète");
}
}
};
// Component for the list of teams (one empty team + existing teams)
var TeamListView = {
view: function() {
return m("div#teamchoice-teams", [
state.teams ? m("table.table", [
m("tbody",
[m(NewTeamView)].concat(
state.teams.map(function(team) {
return m(TeamView, {}, team);
})
))
]) : m("p", "Aucune équipe n'a encore été formée.")
]);
}
};
// Highest level component that composes the other component.
var TeamChoiceView = {
view: function() {
return m("div", [
m(SimpleContextView),
m(AlertsView),
m(StartButtonView),
m(TeamListView),
m(StartButtonView)
]);
}
};
// the value of TeamChoice variable is this returned object
return {
/**
* @param {string|DomElement} rootElement CSS selector or DOM node.
* @param {object} user Record from the 'user' table
* @param {object} mission Record from the 'mission' table
* @param {object} group Record from the 'class' table
* @param {object} dataCallbacks Optional. See default value at top of file.
* @return {undefined}
*/
start: function(rootElement, userid, mission, group, dataCallbacks) {
var root = (typeof rootElement === 'string' ? document.querySelector(rootElement) : rootElement);
if (!root) {
console.error("The root element was not found in the DOM.");
return;
}
resetAlerts();
state.userid = userid;
state.mission = mission;
state.group = group;
if (typeof api === 'object') {
api = dataCallbacks;
}
load();
m.mount(root, TeamChoiceView);
window.setInterval(
function() {
load();
m.redraw();
},
30000
);
}
};
})();
......@@ -13,9 +13,9 @@ namespace mod_labnbook\ar;
*/
class teamconfig
{
const METHOD_STUDENTCHOICE = 0;
const METHOD_TEACHERCHOICE = 1;
const METHOD_RANDOM = 2;
const METHOD_STUDENTCHOICE = 1;
const METHOD_TEACHERCHOICE = 2;
const METHOD_RANDOM = 3;
public $id;
public $instanceid;
......
<?php
/*
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
*/
require(dirname(__DIR__) . '/../../config.php');
require_once(dirname(__DIR__) . '/lib.php');
// Course_module ID
$id = empty($_GET['id']) ? null : (int) $_GET['id'];
if ($id <= 0) {
http_response_code(402);
echo '["error":"The parameter id is required."]';
exit();
}
$cm = get_coursemodule_from_id("labnbook", $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$moduleinstance = $DB->get_record(LABNBOOK_TABLE, array('id' => $cm->instance), '*', MUST_EXIST);
require_login($course, true, $cm);
header("Content-type: application/json");
$action = required_param('action', PARAM_ALPHA);
switch($action) {
case "getTeams":
$groupid = empty($_GET['groupid']) ? null : (int) $_GET['groupid'];
$missionid = empty($_GET['missionid']) ? null : (int) $_GET['missionid'];
if ($missionid <= 0) {
http_response_code(402);
echo '["error":"The parameter missionid is required."]';
exit();
}
echo json_encode(getTeams($groupid, $missionid));
break;
case "selectTeam":
break;
}
function getTeams() {
return [
'settings' => new stdClass(),
'teams' => [
["id_report" => 1, "name" => "Équipe 1", "members" => ["Paulo", "Theresa"]],
],
'status' => [
"allowCreation" => true,
"allowMoreThanOpt" => false,
"numTeams" => 0,
"usersInClass" => 0,
"usersInTeams" => 0
],
'selectedTeam' => null,
];
}
<?php
/**
* Let the current student select the team to register into.
*
* @package mod_labnbook
* @copyright 2019 Université Grenoble Alpes
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/* @var $DB moodle_database */
use \mod_labnbook\session\labnbook as labnbookSession;
use mod_labnbook\ar\teamconfig;
require(dirname(__DIR__) . '/../../config.php');
require_once(dirname(__DIR__) . '/lib.php');
// Course_module ID, or
$id = optional_param('id', 0, PARAM_INT);
// ... module instance id.
$l = optional_param('l', 0, PARAM_INT);
if ($id) {
$cm = get_coursemodule_from_id("labnbook", $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$moduleinstance = $DB->get_record(LABNBOOK_TABLE, array('id' => $cm->instance), '*', MUST_EXIST);
} else if ($l) {
$moduleinstance = $DB->get_record(LABNBOOK_TABLE, array('id' => $l), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id' => $moduleinstance->course), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance("labnbook", $moduleinstance->id, $course->id, false, MUST_EXIST);
} else {
print_error(get_string('missingidandcmid', mod_labnbook));
}
require_login($course, true, $cm);
$modulecontext = context_module::instance($cm->id);
require_capability('mod/labnbook:view', $modulecontext);
/* @var $PAGE moodle_page */
$PAGE->set_url('/mod/labnbook/teamchoice.php', array('l' => $cm->instance));
$PAGE->set_title(format_string($moduleinstance->name));
$PAGE->set_heading(format_string($course->fullname));
$PAGE->set_context($modulecontext);
$lnb = labnbookSession::load();
$groupids = groups_get_user_groups($cm->course)[0];
$teamconfig = teamconfig::findForGroups($cm->id, $groupids);
if ($teamconfig->method !== teamconfig::METHOD_STUDENTCHOICE) {
throw new \Exception("Wrong method of team building.");
}
$data = [
'cmid' => (int) $cm->id,
'userid' => (int) $USER->id,
'mission' => $DB->get_record_sql(
"SELECT id, name AS code, name, intro AS description FROM {labnbook} WHERE id = ?",
[$cm->instance]
),
'group' => $teamconfig->groupid
? $DB->get_record_sql(
"SELECT id, name FROM {groups} WHERE id = ? AND courseid = ?",
[$teamconfig->groupid, $course->id]
)
: (object) ['id' => null, 'name' => $course->shortname]
,
];
$templateData = [
'configJson' => json_encode($data),
'moodle_root_url' => (new moodle_url('/'))->out(false),
];
echo $OUTPUT->header();
echo $OUTPUT->render_from_template('mod_labnbook/team_studentchoice', $templateData);
echo $OUTPUT->footer();
<div id="team-studentchoice-widget"></div>
<script src="../assets/mithril.min.js"></script>
<script src="../assets/team_studentchoice.js"></script>
{{#js}}
var teamChoiceConfig = {{{configJson}}};
var teamChoiceApi = {
stateToLoadRequest: function(s) {
return {
url: '{{moodle_root_url}}mod/labnbook/team/api.php?action=getTeams',
method: "GET",
data: {
id: teamChoiceConfig.cmid,
missionid: s.mission.id,
grouid: s.group.id
}
};
}
};
window.addEventListener('load', () => {
TeamChoice.start(
"#team-studentchoice-widget",
teamChoiceConfig.userid,
teamChoiceConfig.mission,
teamChoiceConfig.group,
teamChoiceApi
);
});
{{/js}}
<?xml version="1.0"?>
<libraries>
<library>
<location>assets/mithril.min.js</location>
<name>Mithril</name>
<version>2.0.0</version>
<license>MIT</license>
<licenseversion></licenseversion>
</library>
</libraries>
......@@ -66,7 +66,7 @@ if (has_capability('moodle/course:manageactivities', context_course::instance($c
$groupids = groups_get_user_groups($cm->course)[0];
$teamconfig = teamconfig::findForGroups($cm->id, $groupids);
if ($teamconfig->method === teamconfig::METHOD_STUDENTCHOICE) {
redirect('/mod/labnbook/teamchoice.php?l=' . (int) $cm->instance);
redirect('/mod/labnbook/team/studentchoice.php?id=' . (int) $cm->id);
return;
} else if ($teamconfig->method === teamconfig::METHOD_TEACHERCHOICE) {
// TODO: warn the teacher that this student lacks a team
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment