new student creation system implemented, not tested

parent 919dcf1c
...@@ -15,14 +15,15 @@ ALTER TABLE student MODIFY gender CHAR(1) DEFAULT NULL; ...@@ -15,14 +15,15 @@ ALTER TABLE student MODIFY gender CHAR(1) DEFAULT NULL;
ALTER TABLE student MODIFY country CHAR(2) DEFAULT NULL; ALTER TABLE student MODIFY country CHAR(2) DEFAULT NULL;
ALTER TABLE student MODIFY lang VARCHAR(5) DEFAULT NULL; ALTER TABLE student MODIFY lang VARCHAR(5) DEFAULT NULL;
ALTER TABLE license ADD type enum('trial', 'official') NOT NULL DEFAULT 'official';
CREATE TABLE IF NOT EXISTS sup_off ( CREATE TABLE IF NOT EXISTS sup_off (
id_sup int(11) NOT NULL, id_sup int(11) NOT NULL,ALTER TABLE license ADD type enum('trial', 'official') NOT NULL DEFAULT 'official';
id_off int(11) DEFAULT NULL, id_off int(11) DEFAULT NULL,
CONSTRAINT fk_sup_off FOREIGN KEY (id_sup) REFERENCES supervisor (id), CONSTRAINT fk_sup_off FOREIGN KEY (id_sup) REFERENCES supervisor (id),
CONSTRAINT fk_off_sup FOREIGN KEY (id_off) REFERENCES supervisor (id) CONSTRAINT fk_off_sup FOREIGN KEY (id_off) REFERENCES supervisor (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; )ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
UPDATE supervisor UPDATE supervisor
SET role = 'office' SET role = 'office'
WHERE id IN (SELECT admin FROM office WHERE admin IS NOT NULL); WHERE id IN (SELECT admin FROM office WHERE admin IS NOT NULL);
...@@ -35,6 +36,10 @@ UPDATE supervisor ...@@ -35,6 +36,10 @@ UPDATE supervisor
SET role = 'tutor' SET role = 'tutor'
WHERE id_off IS NULL; WHERE id_off IS NULL;
UPDATE license
SET type = 'official'
WHERE type is not 'trial';
DELIMITER $$ DELIMITER $$
DROP PROCEDURE IF EXISTS supervisor_adapt $$ DROP PROCEDURE IF EXISTS supervisor_adapt $$
CREATE PROCEDURE supervisor_adapt() CREATE PROCEDURE supervisor_adapt()
......
...@@ -43,46 +43,10 @@ module.exports = { ...@@ -43,46 +43,10 @@ module.exports = {
var params = req.allParams(); var params = req.allParams();
function get_new_random_license (callback) {
function random_license () {
var length = 16;
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var result = '';
for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
return result;
}
var license;
var found = true;
var maxtries = 10;
async.doWhilst(
function (cb) {
license = random_license();
License.findOne({number: license})
.then((l) => {
if (!l)
found = false;
cb();
})
.catch((err) => {
found = false;
cb();
});
},
function () {
return found;
},
function () {
callback(license);
}
);
}
if (!params.duration || params.duration < 0) if (!params.duration || params.duration < 0)
return res.badRequest(); return res.badRequest();
get_new_random_license(function (license) { License.genLicenseNumber(function (license) {
License.create({ License.create({
number: license, number: license,
duration: params.duration, duration: params.duration,
......
...@@ -190,31 +190,73 @@ module.exports = { ...@@ -190,31 +190,73 @@ module.exports = {
}); });
}, },
// /**
// Adds a new student into the database * Adds a new student into the database
// * It inmediatly related to the supervisor who created it
* If no license is specified, a test one will be created
* @param {request} req {
* username,
* password,
* id_sup,
* lang
* license (optional)
* }
*/
create: function (req, res) { create: function (req, res) {
var params = req.params.all(); var params = req.allParams();
License.isActivable(params.license_number, function(err) {
if (err)
return res.serverError(err);
Student.create(params) //
.then(function(created) { // License number, if passed is used, otherwise, a trial one is generated
sails.log.debug('Student ' + created.id + ' created: ' + JSON.stringify(created)); //
License.activate(params.license_number, created.id, function(err, license) { Promise(function(resolve, reject) {
if (params.license)
resolve(params.license);
else {
License.newTrial(params.id_sup, function(license, err) {
if (err) if (err)
return res.serverError(err); reject(err);
created = created.toJSON(); else
created.license = license.toObject(); resolve(license.number);
return res.ok(created); });
}
})
.then((license) => {
// Check license
License.isActivable(params.license, function(err) {
if (err)
return res.serverError(err);
// Create student
Student.create(params)
.then(function(created) {
sails.log.debug('Student ' + created.id + ' created: ' + JSON.stringify(created));
// Activate license
License.activate(params.license, created.id, function(err, license) {
if (err)
return res.serverError(err);
created = created.toJSON();
created.license = license.toObject();
// Link to supervisor
StuSup.create({id_stu: created.id, id_sup: params.id_sup})
.then((stu_sup) => {
if (!stu_sup)
throw new Error("Unable to link to supervisor");
return res.ok(created);
})
.catch((err) => {throw err});
})
.catch((err) => {throw err});
})
.catch(function(err) {
sails.log.debug(err.message);
return res.serverError(err.message);
}); });
})
.error(function(err) {
sails.log.debug(err.message);
return res.serverError(err.message);
}); });
}); })
.catch((err) => {res.serverError()};
}, },
/** /**
...@@ -409,71 +451,95 @@ module.exports = { ...@@ -409,71 +451,95 @@ module.exports = {
* Creates a relation between the student and a given supervisor. * Creates a relation between the student and a given supervisor.
* It broadcasts the event linkSupervisorToStudent to both the student room * It broadcasts the event linkSupervisorToStudent to both the student room
* and the supervisor room. * and the supervisor room.
* @param {request} { (with id_stu and id_sup as url parameters) * @param {request} { (with id_stu and id_sup as url parameters or with license and id_sup)
* asTherapist: true/false (optional) // assigns supervisor to student's office is true, set id_off to null otherwise
* } * }
* @param {response} {} * @param {response} {}
*/ */
link_supervisor: function (req, res) { link_supervisor: function (req, res) {
StuSup.create({
student: req.param('id_stu'),
supervisor: req.param('id_sup')
})
.then(function (stuSup) {
if (!stuSup)
throw new Error('stusup not created');
const socketToOmit = (req.isSocket) ? req.socket : undefined; var params = req.allParams();
const linkSupervisorToStudentEvent = sails.hooks.events.linkSupervisorToStudent(
stuSup.supervisor,
stuSup.student
);
sails.hooks.events.broadcastEvent(
sails.hooks.rooms.supervisor(stuSup.supervisor),
linkSupervisorToStudentEvent,
socketToOmit
);
sails.hooks.events.broadcastEvent(
sails.hooks.rooms.student(stuSup.student),
linkSupervisorToStudentEvent,
socketToOmit
);
return stuSup; // Check input parameters coherence
if (!params.id_sup || !params.id_stu && !params.license)
return res.badRequest("Invalid params");
//
// Get student ID either because we got it or through a license
// As it can be resolved through two possibilities, better to user promises!
//
Promise(funcion(reject, resolve) {
if (!params.id_stu && params.license) {
// get student, license attribute will be completed
License.getStudent(params.license, function(err, stu) {
if (err)
reject(err);
else
resolve(stu);
});
} else
Student.findOne(params.id_stu)
.populate('license')
.then((stu) => {
if (!stu)
throw new Error("Student not found");
resolve(stu);
})
.catch((err) => {reject(err)});
}) })
.catch((err) => { .then((stu) => {
StuSup.findOne({ student: req.param('id_stu'), supervisor: req.param('id_sup') })
//
// Let's check it is not already linked
//
StuSup.findOne({ student: stu.id, supervisor: params.id_sup })
.then((stuSup) => { .then((stuSup) => {
// It was already there! // It was already there!
if (stuSup) if (stuSup)
return stuSup; throw new Error("Already linked");
else
throw err; //
}); // Non existing relation, let's create it!
}) //
.then((stuSup) => { else {
// update supervisor office if it is linked as therapist
Supervisor.findOne({id: req.param('id_sup')}) StuSup.create({
.then((sup) => { student: id_stu,
if (sup && !sup.office) { supervisor: params.id_sup
Student.findOne({id: req.param('id_stu')}) })
.then((stu) => { .then(function (stuSup) {
if (stu) { if (!stuSup)
if (req.body.asTherapist) throw new Error('stusup not created');
sup.office = stu.office;
else //
sup.office = null; // Send sockets messages
delete sup.password; //
sup.save(); const socketToOmit = (req.isSocket) ? req.socket : undefined;
} const linkSupervisorToStudentEvent = sails.hooks.events.linkSupervisorToStudent(
}); stuSup.supervisor,
stuSup.student
);
sails.hooks.events.broadcastEvent(
sails.hooks.rooms.supervisor(stuSup.supervisor),
linkSupervisorToStudentEvent,
socketToOmit
);
sails.hooks.events.broadcastEvent(
sails.hooks.rooms.student(stuSup.student),
linkSupervisorToStudentEvent,
socketToOmit
);
//
// Done!
//
return res.ok(stu);
} }
}); })
return res.ok(); .catch((err) => {throw err});
}) })
.catch((err) => { .catch((err) => {
return res.serverError("Error: " + err); res.serverError(err);
}); });
}, },
......
...@@ -48,6 +48,11 @@ module.exports = { ...@@ -48,6 +48,11 @@ module.exports = {
size: 16, size: 16,
unique: true unique: true
}, },
type: {
type: "string",
enum: ['trial', 'official'],
columnName: 'type'
},
creator: { creator: {
columnName: "creator", columnName: "creator",
type: "string", type: "string",
...@@ -66,6 +71,105 @@ module.exports = { ...@@ -66,6 +71,105 @@ module.exports = {
*/ */
/** /**
* Generates a new trial license
* @param {ID} id_sup ID of the supervisor who creates the license
* @param {function} callback Callback function: license, err
*/
newTrial: function(id_sup, callback) {
var now = new Date();
License.genLicenseNumber(function(number) {
if (!number)
callback(null, new Error("Unable to generate new license"));
License.create({
type: 'trial',
creation_ts: now,
duration: sails.config.pictogram.trial_license_duration,
number: number
})
.then((l) => {
if (!l) throw new Error("Unable to create license");
callback(l, null);
})
.catch((err) => {
callback(null, err);
});
});
},
/**
* Generates a random license number (checking that is not duplicated)
* @param {function} callback Prototype: function(license) returns the create number
*/
genLicenseNumber: function(callback) {
function random_license () {
var length = 16;
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var result = '';
for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
return result;
}
var license;
var found = true;
var maxtries = 10;
async.doWhilst(
function (cb) {
license = random_license();
License.findOne({number: license})
.then((l) => {
if (!l)
found = false;
cb();
})
.catch((err) => {
found = false;
cb();
});
},
function () {
return found;
},
function () {
callback(license);
}
);
}
/**
* Get the student associated to the license
* If the license is not active or doesn't exist, and error is returned
* @param {number} number License number
* @param {function} callback Callback function: err, student
*/
getStudent: function(number, callback) {
License.findOne({ number: number })
.then((l) => {
if (!l || !l.activation_ts || !l.id_stu)
throw new Error("Invalid license: " + number);
// License ok, check student
Student.findOne(l.id_stu)
.then((s) => {
if (!s)
throw new Error("Student not found");
s.license = l;
callback(null, s);
})
.catch((err) => {
throw err;
});
})
.catch((err) => {
callback(err, null);
});
},
/**
Activates a license Activates a license
@param {number} number License number @param {number} number License number
@param {ID} id_stu ID of the student the license is associated to @param {ID} id_stu ID of the student the license is associated to
......
...@@ -31,21 +31,17 @@ module.exports = { ...@@ -31,21 +31,17 @@ module.exports = {
size: 40 size: 40
}, },
name: { name: {
required: true,
type: 'string', type: 'string',
size: 40 size: 40
}, },
surname: { surname: {
required: true,
type: 'string', type: 'string',
size: 60 size: 60
}, },
birthdate: { birthdate: {
required: true,
type: 'date' type: 'date'
}, },
gender: { gender: {
required: true,
type: 'string', type: 'string',
size: 1 size: 1
}, },
...@@ -63,16 +59,9 @@ module.exports = { ...@@ -63,16 +59,9 @@ module.exports = {
}, },
lang: { lang: {
columnName: 'lang', columnName: 'lang',
required: true,
type: 'string', type: 'string',
size: 2 size: 2
}, },
office: {
columnName: 'id_off',
type: 'integer',
required: false,
model: 'Office'
},
id_active_scene: { id_active_scene: {
columnName: 'id_active_scene', columnName: 'id_active_scene',
type: 'integer', type: 'integer',
......
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
"activation": "Activation", "activation": "Activation",
"add": "Add", "add": "Add",
"add_existing": "Add existing account", "add_existing": "Add existing account",
"add_existing_desc": "Link your profile to an already registered student's account to start working with him/her. You'll be asked for the current active license of the student.",
"add_expression": "Add expression", "add_expression": "Add expression",
"add_instruction": "Add instruction", "add_instruction": "Add instruction",
"add_new_official": "New official account", "add_new_official": "New official account",
"add_new_official_desc": "Create a new student account with a new official license.",
"add_office": "Add office", "add_office": "Add office",
"add_picto": "Add pictogram", "add_picto": "Add pictogram",
"add_pictos": "Add pictograms", "add_pictos": "Add pictograms",
...@@ -244,6 +246,7 @@ ...@@ -244,6 +246,7 @@
"new_scene_without_categories": "Create scene without categories", "new_scene_without_categories": "Create scene without categories",
"new_session": "New session", "new_session": "New session",
"new_test_account": "New test account", "new_test_account": "New test account",
"new_test_account_desc": "Create a test account to try Pictogram for free. You will be able to keep the account beyond the three months trial by activating an official license on the account.",
"next": "Next", "next": "Next",
"next_actions": "Next actions", "next_actions": "Next actions",
"next_sessions": "Next sessions", "next_sessions": "Next sessions",
...@@ -393,6 +396,7 @@ ...@@ -393,6 +396,7 @@
"student_account_confirm": "The new account is now available from the students list.", "student_account_confirm": "The new account is now available from the students list.",
"student_added": "Student added", "student_added": "Student added",
"student_already_exists": "A student with that username already exists, please try with another one", "student_already_exists": "A student with that username already exists, please try with another one",
"student_already_linked": "The student is already linked to your account",
"student_deleted": "Student deleted", "student_deleted": "Student deleted",
"student_not_added": "Student not added", "student_not_added": "Student not added",
"student_not_deleted": "Student not deleted", "student_not_deleted": "Student not deleted",
......
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
"activation": "Activación", "activation": "Activación",
"add": "Añadir", "add": "Añadir",
"add_existing": "Asociar cuenta existente", "add_existing": "Asociar cuenta existente",
"add_existing_desc": "Asocie a su perfil la cuenta de un/a alumno/a ya registrado/a para poder gestionarla. Necesitará el número de licencia en uso del estudiante.",
"add_expression": "Añadir expresión", "add_expression": "Añadir expresión",
"add_instruction": "Añadir instrucción", "add_instruction": "Añadir instrucción",
"add_new_official": "Nueva cuenta oficial", "add_new_official": "Nueva cuenta oficial",
"add_new_official_desc": "Alta de una nueva cuenta de alumno/a con licencia oficial.",
"add_office": "Añadir gabinete", "add_office": "Añadir gabinete",
"add_picto": "Añadir pictograma", "add_picto": "Añadir pictograma",
"add_pictos": "Añadir pictogramas", "add_pictos": "Añadir pictogramas",
...@@ -244,6 +246,7 @@ ...@@ -244,6 +246,7 @@
"new_scene_without_categories": "Crear escena sin categorías", "new_scene_without_categories": "Crear escena sin categorías",
"new_session": "Nueva sesión", "new_session": "Nueva sesión",
"new_test_account": "Crear cuenta de prueba", "new_test_account": "Crear cuenta de prueba",
"new_test_account_desc": "Cree una cuenta temporal para probar gratuitamente Pictogram. Podrá activar una licencia en esta cuenta para mantenerla más allá de los tres meses de prueba.",
"next": "Siguiente", "next": "Siguiente",
"next_actions": "Acciones posteriores", "next_actions": "Acciones posteriores",
"next_sessions": "Sesiones posteriores", "next_sessions": "Sesiones posteriores",
...@@ -393,6 +396,7 @@ ...@@ -393,6 +396,7 @@
"student_account_confirm": "La nueva cuenta ahora está disponible en su lista de alumnos.", "student_account_confirm": "La nueva cuenta ahora está disponible en su lista de alumnos.",
"student_added": "Estudiante añadido", "student_added": "Estudiante añadido",
"student_already_exists": "Ya existe un estudiante con ese nombre de usuario. Por favor, inténtelo de nuevo con algo diferente.", "student_already_exists": "Ya existe un estudiante con ese nombre de usuario. Por favor, inténtelo de nuevo con algo diferente.",
"student_already_linked": "El estudiante ya está vinculado a su cuenta",
"student_deleted": "Estudiante eliminado", "student_deleted": "Estudiante eliminado",
"student_not_added": "Estudiante no añadido", "student_not_added": "Estudiante no añadido",
"student_not_deleted": "No se ha podido eliminar el estudiante", "student_not_deleted": "No se ha podido eliminar el estudiante",
......
...@@ -16,19 +16,37 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -16,19 +16,37 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
$timeout, $timeout,
IOService) { IOService) {
$scope.formdatastudent = {
// Flags for showing buttons according to role
$scope.user = JSON.parse($window.sessionStorage.user);
// --------------------------------------------------------
// Create new account
// --------------------------------------------------------
var formdata_empty = {
username: '', username: '',
password: '', password: '',
name: '', name: $translate.instant('name'),
surname: '', surname: $translate.instant('surname'),
birthdate: '', birthdate: Date(),
country: 'ES', country: 'ES',
gender: 'M', gender: 'M',
lang: 'es-es', lang: 'es-es',
notes: '', notes: '',
office: $scope.user.office || { name: '' } current_method: 'no_method',
current_instruction: 'no_instruction',
license_number: null,
id_sup: -1
}; };
// Hide new student form
$scope.hidestudentadd = true;
// forms container
$scope.forms = {};
// slider object
$scope.slide = { $scope.slide = {
state: 'accounts', state: 'accounts',
prev: 'accounts', prev: 'accounts',
...@@ -51,49 +69,17 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -51,49 +69,17 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
} }
}; };
// Flags for showing buttons according to role // calendar function
$scope.user = JSON.parse($window.sessionStorage.user); $scope.open_calendar = function ($event) {
$event.preventDefault();
// Identify if the user is office administrator $event.stopPropagation();
if ($scope.user.office) { $scope.opened_cal_student = true;
if ($scope.user.office.admin === $scope.user.id) { };
$scope.user.isAdmin = true;
}
} else {
$scope.user.office = { name: '' };
}
// Hide new student form
$scope.hidestudentadd = true;
// Get list of supervisor's students
$http.get(config.backend + '/sup/' + $scope.user.id + '/students')
.success(function (data) {
$scope.students = data;
})
.error(function () {
ngToast.danger({ content: $translate.instant('error_fetching_students') });
});
// Reset form Student // Show student form
$scope.showForm = function () { $scope.showForm = function () {
// Empty the form // Reset the form
$scope.formdatastudent = { $scope.formdata = formdata_empty;
username: '',
password: '',
password_confirm: '',
name: '',
surname: '',
birthdate: '',
country: 'ES',
gender: 'M',
lang: 'es-es',
notes: '',
office: $scope.user.office || { name: '' },
current_method: 'no_method',
current_instruction: 'no_instruction',
license_number: ''
};
$scope.slide.state ='accounts'; $scope.slide.state ='accounts';
$scope.slide.show = true; $scope.slide.show = true;
...@@ -108,10 +94,46 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -108,10 +94,46 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
} }
/** /**
* Add Student * Add existing account
*/
function addExisting () {
// Send link call to server
$http.post(config.backend + '/stu/license/id_sup/' + $scope.user.id, {license: $scope.formdata.license})
.success(function (data) {
ngToast.success({ content: $translate.instant('student_added') });
// default values
data.current_method = $translate.instant('no_method');
data.current_instruction = $translate.instant('no_instruction');
data.licenseIsValid = new Date(data.license.expiration_ts) - new Date() > 0 ? true : false;
$scope.students.push(data);
$scope.slide.rightTo('confirmation');
})
.error(function (err) {
var errorMessage = 'student_not_added';
if (err.message && err.message.search('nvalid license') > 0)
errorMessage = 'license_invalid';
else if (err.message && err.message.search('in use') > 0)
errorMessage = 'license_already_activated';
else if (typeof err == "string" && err.search("lready linked") > 0)
errorMessage = 'student_already_linked';
else if (err && err.status === 400)
errorMessage = 'invalid_fields';
ngToast.danger({ content: $translate.instant(errorMessage) });
});
}
/**
* Add new account
*/ */
$scope.add_student = function () { function addNew(type) {
var student = $scope.formdatastudent;
// set language according to interface settings
$scope.formdata.lang = $translate.use();
// Validate password match // Validate password match
if (student.password_confirm.length && student.password !== student.password_confirm) { if (student.password_confirm.length && student.password !== student.password_confirm) {
...@@ -119,13 +141,11 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -119,13 +141,11 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
return; return;
} }
// password not changed (don't send it to DB) // Select API call according to type of account to create: office or test
if (!student.password_confirm.length) { var apicall = type == 'test' ? '/stu/test' : '/stu';
delete student.password;
delete student.password_confirm;
}
$http.post(config.backend + '/stu', student) // Send creating call to server
$http.post(config.backend + apicall, $scope.formdata)
.success(function (data) { .success(function (data) {
ngToast.success({ content: $translate.instant('student_added') }); ngToast.success({ content: $translate.instant('student_added') });
...@@ -136,7 +156,7 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -136,7 +156,7 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
data.licenseIsValid = new Date(data.license.expiration_ts) - new Date() > 0 ? true : false; data.licenseIsValid = new Date(data.license.expiration_ts) - new Date() > 0 ? true : false;
$scope.students.push(data); $scope.students.push(data);
$scope.resetForm(); $scope.slide.rightTo('confirmation');
}) })
.error(function (err) { .error(function (err) {
var errorMessage = 'student_not_added'; var errorMessage = 'student_not_added';
...@@ -144,8 +164,6 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -144,8 +164,6 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
errorMessage = 'license_invalid'; errorMessage = 'license_invalid';
else if (err.message && err.message.search('in use') > 0) else if (err.message && err.message.search('in use') > 0)
errorMessage = 'license_already_activated'; errorMessage = 'license_already_activated';
else if (typeof err == "string" && err.search("Maximum number of enrolments reached") > 0)
errorMessage = 'max_licenses_reached';
else if (typeof err == "string" && err.search("already exists") > 0) else if (typeof err == "string" && err.search("already exists") > 0)
errorMessage = 'student_already_exists'; errorMessage = 'student_already_exists';
else if (err && err.status === 400) else if (err && err.status === 400)
...@@ -153,14 +171,28 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -153,14 +171,28 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
ngToast.danger({ content: $translate.instant(errorMessage) }); ngToast.danger({ content: $translate.instant(errorMessage) });
}); });
};
}
// --------------------------------------------------------
// Students list
// --------------------------------------------------------
// Get list of supervisor's students
$http.get(config.backend + '/sup/' + $scope.user.id + '/students')
.success(function (data) {
$scope.students = data;
})
.error(function () {
ngToast.danger({ content: $translate.instant('error_fetching_students') });
});
/** /**
* Delete Student * Unlink Student
*/ */
$scope.delete_student = function (student) { $scope.unlink_student = function (student) {
if ($window.confirm($translate.instant('confirmation'))) { if ($window.confirm($translate.instant('confirmation'))) {
$http.delete(config.backend + '/stu/' + student.id) $http.delete(config.backend + '/stu/' + student.id + '/sup/' + $scope.user.id)
.success(function () { .success(function () {
var i; var i;
for (i = 0; i < $scope.students.length; i++) { for (i = 0; i < $scope.students.length; i++) {
...@@ -180,6 +212,11 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -180,6 +212,11 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
} }
}; };
/**
* WEBSOCKETS
*/
// When a new student is added to the supervisor, we should update // When a new student is added to the supervisor, we should update
// the student list if necesary // the student list if necesary
IOService.on('linkSupervisorToStudent', function (eventData) { IOService.on('linkSupervisorToStudent', function (eventData) {
...@@ -205,14 +242,3 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl( ...@@ -205,14 +242,3 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
} }
}); });
}); });
/**
* StudentAddCtrl
*/
dashboardControllers.controller('StudentAddCtrl', function StudentsCtrl($scope) {
$scope.open_calendar = function ($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened_cal_student = true;
};
});
...@@ -50,28 +50,54 @@ ...@@ -50,28 +50,54 @@
</td> </td>
<td> <!-- BUTTONS --> <td> <!-- BUTTONS -->
<a class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/collections" alt="{{ 'collections' | translate}}" popover="{{ 'collections' | translate}}" popover-trigger="mouseenter" ng-if="student.supervision != 0"><span class="glyphicon glyphicon-th" aria-hidden="true"></span></a> <a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/collections"
<span class="btn btn-default btn-lg" role="button" alt="{{ 'collections' | translate}}" popover="{{ 'collections' | translate}}" popover-trigger="mouseenter" ng-if="student.supervision == 0"><span class="glyphicon glyphicon-th" style="color: #bbb" aria-hidden="true"></span></span> alt="{{ 'collections' | translate}}" popover="{{ 'collections' | translate}}" popover-trigger="mouseenter">
<span class="glyphicon glyphicon-th" aria-hidden="true"></span>
<a class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/instructions" alt="{{ 'instructions' | translate}}" popover="{{ 'instructions' | translate}}" popover-trigger="mouseenter" ng-if="student.supervision == 2"><span class="glyphicon glyphicon-tasks" aria-hidden="true"></span></a> </a>
<span class="btn btn-default btn-lg" role="button" alt="{{ 'instructions' | translate}}" popover="{{ 'instructions' | translate}}" popover-trigger="mouseenter" ng-if="student.supervision != 2"><span class="glyphicon glyphicon-tasks" aria-hidden="true" style="color: #bbb" ></span></span>
<a class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/session" alt="{{ 'session' | translate}}" popover="{{ 'session' | translate}}" popover-trigger="mouseenter" ng-if="student.supervision == 2"><span class="glyphicon glyphicon-transfer" aria-hidden="true"></span></a>
<span class="btn btn-default btn-lg" role="button" alt="{{ 'session' | translate}}" popover="{{ 'session' | translate}}" popover-trigger="mouseenter" ng-if="student.supervision != 2"><span class="glyphicon glyphicon-transfer" aria-hidden="true" style="color: #bbb"></span></span> <a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/instructions"
alt="{{ 'instructions' | translate}}" popover="{{ 'instructions' | translate}}" popover-trigger="mouseenter" ng-if="!user.isTutor">
<span class="glyphicon glyphicon-tasks" aria-hidden="true"></span>
</a>
<span
class="btn btn-default btn-lg" role="button"
alt="{{ 'instructions' | translate}}" popover="{{ 'instructions' | translate}}" popover-trigger="mouseenter" ng-if="user.isTutor">
<span class="glyphicon glyphicon-tasks" aria-hidden="true" style="color: #bbb" ></span>
</span>
<a class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/reports" alt="{{ 'reports' | translate}}" popover="{{ 'reports' | translate}}" popover-trigger="mouseenter" ng-if="student.supervision != 1"><i class="fa fa-bar-chart" aria-hidden="true"></i></a> <a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/session"
alt="{{ 'session' | translate}}" popover="{{ 'session' | translate}}" popover-trigger="mouseenter" ng-if="!user.isTutor">
<span class="glyphicon glyphicon-transfer" aria-hidden="true"></span>
</a>
<span
class="btn btn-default btn-lg" role="button"
alt="{{ 'session' | translate}}" popover="{{ 'session' | translate}}" popover-trigger="mouseenter" ng-if="user.isTutor">
<span class="glyphicon glyphicon-transfer" aria-hidden="true" style="color: #bbb"></span>
</span>
<span class="btn btn-default btn-lg" role="button" alt="{{ 'reports' | translate}}" popover="{{ 'reports' | translate}}" popover-trigger="mouseenter" ng-if="student.supervision == 1"><i class="fa fa-bar-chart" aria-hidden="true" style="color: #bbb"></i></span> <a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/reports"
alt="{{ 'reports' | translate}}" popover="{{ 'reports' | translate}}" popover-trigger="mouseenter" ng-if="!user.isTutor">
<i class="fa fa-bar-chart" aria-hidden="true"></i>
</a>
<span class="btn btn-default btn-lg" role="button"
alt="{{ 'reports' | translate}}" popover="{{ 'reports' | translate}}" popover-trigger="mouseenter" ng-if="user.isTutor">
<i class="fa fa-bar-chart" aria-hidden="true" style="color: #bbb"></i>
</span>
<a class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/setup" alt="{{ 'setup' | translate}}" popover="{{ 'setup' | translate}}" popover-trigger="mouseenter"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span></a> <a
class="btn btn-default btn-lg" role="button" href="/app/#/student/{{student.id}}/setup"
alt="{{ 'setup' | translate}}" popover="{{ 'setup' | translate}}" popover-trigger="mouseenter">
<span class="glyphicon glyphicon-cog" aria-hidden="true"></span>
</a>
</td> <!-- /BUTTONS --> </td> <!-- /BUTTONS -->
<td> <td>
<a ng-if="user.isSupAdmin" ng-click="delete_student(student)" class="delete_stu" title="{{ 'delete' | translate}}"> <a ng-click="unlink_student(student)" class="delete_stu" title="{{ 'unlink' | translate}}">
<span class="glyphicon glyphicon-remove-circle text-danger" aria-hidden="true"></span> <span class="glyphicon glyphicon-remove-circle text-danger" aria-hidden="true"></span>
</a> </a>
</td> </td>
......
...@@ -21,7 +21,7 @@ module.exports.pictogram = { ...@@ -21,7 +21,7 @@ module.exports.pictogram = {
], ],
serialSize: 10, // number of characters in generated serial numbers serialSize: 10, // number of characters in generated serial numbers
pageLimit: 10, // number of elements per "page" pageLimit: 10, // number of elements per "page"
trial_license_duration: 3, // number of moths the trial license is valid
password_minlength: 8, // minimal size for the password string password_minlength: 8, // minimal size for the password string
urls: { urls: {
...@@ -54,11 +54,11 @@ module.exports.pictogram = { ...@@ -54,11 +54,11 @@ module.exports.pictogram = {
*/ */
getSupervisorCustomPictoUrl: function (filename) { getSupervisorCustomPictoUrl: function (filename) {
return `/upload/supervisorCustomPicto/${filename}`; return `/upload/supervisorCustomPicto/${filename}`;
}, },
/** /**
* Gets the public url of a sound for a given picto * Gets the public url of a sound for a given picto
* @param {String} filename filename of sound * @param {String} filename filename of sound
* @return {String} Public url of the picto sound * @return {String} Public url of the picto sound
*/ */
getSoundUrl: function (filename) { getSoundUrl: function (filename) {
return `/upload/pictoSound/${filename}`; return `/upload/pictoSound/${filename}`;
...@@ -72,7 +72,7 @@ module.exports.pictogram = { ...@@ -72,7 +72,7 @@ module.exports.pictogram = {
supervisorAvatarDirectory: path.join(UPLOAD_PATH, 'supervisorAvatar'), supervisorAvatarDirectory: path.join(UPLOAD_PATH, 'supervisorAvatar'),
studentAvatarDirectory: path.join(UPLOAD_PATH, 'studentAvatar'), studentAvatarDirectory: path.join(UPLOAD_PATH, 'studentAvatar'),
supervisorCustomPictoDirectory: path.join(UPLOAD_PATH, 'supervisorCustomPicto'), supervisorCustomPictoDirectory: path.join(UPLOAD_PATH, 'supervisorCustomPicto'),
pictoSoundDirectory: path.join(UPLOAD_PATH, 'pictoSound'), pictoSoundDirectory: path.join(UPLOAD_PATH, 'pictoSound'),
/** /**
* Get a random name used for uploaded file names * Get a random name used for uploaded file names
* @param {string} randomString String used for generating the name * @param {string} randomString String used for generating the name
......
...@@ -90,6 +90,7 @@ module.exports.routes = { ...@@ -90,6 +90,7 @@ module.exports.routes = {
'GET /stu/:id_stu/therapists': 'StudentController.therapists', 'GET /stu/:id_stu/therapists': 'StudentController.therapists',
'GET /stu/:id_stu/tutors': 'StudentController.tutors', 'GET /stu/:id_stu/tutors': 'StudentController.tutors',
'POST /stu/:id_stu/sup/:id_sup': 'StudentController.link_supervisor', 'POST /stu/:id_stu/sup/:id_sup': 'StudentController.link_supervisor',
'POST /stu/license/sup/:id_sup': "StudentController.link_supervisor",
'GET /stu/:id_stu/pictos': 'StudentController.pictos', 'GET /stu/:id_stu/pictos': 'StudentController.pictos',
'GET /stu/:id_stu/activeScene': 'StudentController.getActiveScene', 'GET /stu/:id_stu/activeScene': 'StudentController.getActiveScene',
'GET /stu/:id_stu/scenes': 'StudentController.getScenes', 'GET /stu/:id_stu/scenes': 'StudentController.getScenes',
...@@ -104,9 +105,10 @@ module.exports.routes = { ...@@ -104,9 +105,10 @@ module.exports.routes = {
'PUT /stu/:id_stu/legend/:legend_value': 'StudentController.update_legend', 'PUT /stu/:id_stu/legend/:legend_value': 'StudentController.update_legend',
'PUT /stu/:id_stu/picto': 'StudentController.update_picto', 'PUT /stu/:id_stu/picto': 'StudentController.update_picto',
'PUT /stu/:id_stu/cat': 'StudentController.update_category', 'PUT /stu/:id_stu/cat': 'StudentController.update_category',
'PUT /stu/:id_stu/activeScene/:id_scene': 'StudentController.updateActiveScene', 'PUT /stu/:id_stu/activeScene/:id_scene': 'StudentController.updateActiveScene',
'POST /stu/login': 'StudentController.login', 'POST /stu/login': 'StudentController.login',
'POST /stu': 'StudentController.create', 'POST /stu': 'StudentController.create',
'POST /stu/license': 'StudentController.createTest',
'POST /stu/upload': 'StudentController.upload', 'POST /stu/upload': 'StudentController.upload',
'POST /stu/:id_stu/upload_sound/:id_picto': 'StudentController.upload_sound', 'POST /stu/:id_stu/upload_sound/:id_picto': 'StudentController.upload_sound',
'POST /stu/:id_stu/picto/:id_picto': 'StudentController.add_picto', 'POST /stu/:id_stu/picto/:id_picto': 'StudentController.add_picto',
......
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 sign in to comment