Commit fb5ce783 by Fernando Martínez Santiago

Merge remote-tracking branch 'origin/develop' into develop

parents 2a978514 80e9c9a3
Showing with 498 additions and 861 deletions
......@@ -7,6 +7,8 @@ elif [ -e "/vagrant/src/app.js" ]; then
cd /vagrant/src && forever start app.js --debug
elif [ -e "/home/vagrant/sync/src/app.js" ]; then
cd /home/vagrant/sync/src && forever start app.js --debug
elif [ -e "/home/ubuntu/pictogram/sails/src/app.js" ]; then
cd /home/ubuntu/pictogram/sails/src && forever start app.js --debug
else
echo "-- app.js not found, cannot run pictogram server"
exit
......
......@@ -2,10 +2,9 @@ SET foreign_key_checks=0;
DROP USER IF EXISTS 'pictodbu';
DELETE FROM mysql.user WHERE user='pictodbu';
FLUSH PRIVILEGES;
CREATE USER 'pictodbu'@'localhost' identified by 'p1KT0!15.';
GRANT USAGE ON *.* TO 'pictodbu'@'localhost';
CREATE USER 'pictodbu'@'%' identified by 'r"YjtnB+a4$.M*nJ';
DROP DATABASE IF EXISTS pictodb;
CREATE DATABASE pictodb;
SET foreign_key_checks=1;
GRANT all privileges ON pictodb.* to 'pictodbu'@'localhost' identified by 'p1KT0!15.';
GRANT all privileges ON pictodb.* to 'pictodbu'@'localhost' identified by 'r"YjtnB+a4$.M*nJ';
FLUSH PRIVILEGES;
......@@ -138,6 +138,7 @@ COMMENT="Methods predefined in the platform or stored by users for cloning";
CREATE TABLE IF NOT EXISTS `office` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(80) COLLATE utf8_unicode_ci NOT NULL,
`logo_url` varchar(240) COLLATE utf8_unicode_ci NOT NULL,
`address` varchar(180) COLLATE utf8_unicode_ci NOT NULL,
`country` varchar(2) COLLATE utf8_unicode_ci NOT NULL,
`lang` varchar(5) COLLATE utf8_unicode_ci NOT NULL,
......
......@@ -73,7 +73,7 @@ INSERT IGNORE INTO `student` (
'es-es',
(SELECT id from office WHERE email='centrodestrezas@gmail.com'),
'{"stu-att":[{"categories":"on","input feedback":["vibration","tts"],"input selection":"click","pictogram size":"medium"}]}'
)
);
INSERT IGNORE INTO `stu_picto` (
`id_stu`,
......
......@@ -53,6 +53,16 @@ thisTrigger: BEGIN
current_students=current_students+1
WHERE
office.id=new.id_off;
INSERT INTO stu_picto(id_stu,id_pic,attributes)
SELECT new.id,id_pic, concat('{"id_cat":', if (id_cat_pic is null, 'null',id_cat_pic),
',"coord_x":',coord_x,
',"coord_y":',coord_y,
',"status":"invisible"',
',"highlight":false',
',"color":', if (color is null, 'null',concat('"',color,'"')),
'}') as attributes
FROM picto_core P;
END;;
-- Integrity rule 3: office.current_enrolments and supervisor assigments updating.
......
......@@ -2,5 +2,5 @@
database_files_relative_path: 'roles/database/files'
database_name: pictodb
database_user: pictodbu
database_user_passwd: p1KT0!15.
database_user_passwd: r"YjtnB+a4$.M*nJ
database_tests: [caja] #[caja, autismojaen] # can be empty
......@@ -36,11 +36,14 @@ module.exports = {
* responses over https:// and/or use websockets over the wss:// protocol
* (recommended for HTTP, strongly encouraged for WebSockets)
*/
ssl: {
// ca: fs.readFileSync(path.join(__dirname, 'ssl', 'development-bundle.crt')),
key: fs.readFileSync(path.join(__dirname, 'ssl', 'development.key')),
cert: fs.readFileSync(path.join(__dirname, 'ssl', 'development.crt')),
/* UNCOMMENT FOR HTTPS */
/* ssl: {
ca: fs.readFileSync(path.join(__dirname, 'ssl', '/etc/letsencrypt/live/xxx.yottacode.com/chain.pem')),
key: fs.readFileSync(path.join(__dirname, 'ssl', '/etc/letsencrypt/live/xxx.yottacode.com/privkey.pem')),
cert: fs.readFileSync(path.join(__dirname, 'ssl', '/etc/letsencrypt/live/xxx.yottacode.com/fullchain.pem')),
},
*/
/**
* The `port` setting determines which TCP port your app will be
......
......@@ -50,6 +50,25 @@ module.exports = {
},
/**
* Get basic informacion (logo_url and name) about an office identified by a code
*/
getBasic: function (req, res) {
// We simply remove first 3 characters
var id = req.params.code.substr(3, req.params.code.length-1);
Office.findOne({ id: id }).populate('admin').then(function (office) {
if (office) {
res.ok({logo_url: office.logo_url, name: office.name});
} else {
res.notFound();
}
})
.catch(function () {
res.badRequest();
});
},
/**
* Return all offices
* @param {request} req {}
* @param {response} res
......
......@@ -158,7 +158,7 @@ module.exports = {
Student.create(params)
.then(function(created) {
sails.log.debug('Student ' + created.id + ' created: ' + JSON.stringify(created));
return res.ok({ student: created });
return res.ok(created);
})
.error(function(err) {
if (err.message.search("Maximum number of enrolments reached") > 0) {
......@@ -169,8 +169,8 @@ module.exports = {
return res.serverError(err.message);
}
else {
sails.log.debug(err);
return res.serverError(err);
sails.log.debug(err.message);
return res.serverError(err.message);
}
});
},
......
......@@ -31,6 +31,11 @@ module.exports = {
type: "string",
size: 80
},
logo_url: {
required: true,
type: "string",
size: 240
},
contactPerson: {
columnName: "contact_person",
required: true,
......@@ -85,4 +90,4 @@ module.exports = {
via: 'office'
}
}
};
\ No newline at end of file
};
......@@ -238,53 +238,6 @@ module.exports = {
},
/**
* Sets initial vocabulary of students to Picto Core
* @param {Object} attrs All student properties stored
* @param {Function} next Function to be executed when the check process
* has been completed (an error object will be passed
* to the function if necesary)
*/
afterCreate: function (attrs, next) {
// Assign the initial collection of pictos to the student
PictoCore.find()
.then(function(pictoCore) {
var i;
// Every picto from 'picto_core_cat' is going to be created
// in 'stu_picto'
for (var i = 0; i < pictoCore.length; i++) {
StuPicto.create({
student: created.id,
picto: pictoCore[i].picto,
attributes: {
id_cat: pictoCore[i].category,
coord_x: pictoCore[i].coord_x,
coord_y: pictoCore[i].coord_y,
status: 'invisible', // Default, the pictos don't appear to the user
color: pictoCore[i].color
}
})
.then(function (stuPictoError, added) {
if (added) {
sails.log.debug(
'Picto ' + added.picto +
' added to student ' + created.id +
' with attributes: ' + JSON.stringify(added.attributes));
}
})
.error(function(err) {
sails.log.debug('StuPicto.create: ' + err);
throw err;
});
}
next();
})
.error(function(err) {
next(err);
});
},
/**
* Checks the given properties before updating a new Student
* @param {Object} attrs All student properties to be stored
* @param {Function} next Function to be executed when the check process
......
......@@ -26,7 +26,7 @@
"ngtoast": "~1.5.4",
"angular-animate": "~1.4.1",
"angular-sanitize": "~1.4.1",
"angular-chart.js": "~0.7.2",
"angular-chart.js": "latest",
"ng-lodash": "~0.3.0"
},
"resolutions": {
......
No preview for this file type
This diff could not be displayed because it is too large.
......@@ -26,6 +26,7 @@
"alert": "Alert",
"all": "All",
"animation": "Animation",
"annual": "Annual",
"April": "April",
"attributes_not_updated": "Changes not saved",
"attributes_updated": "Changes saved",
......@@ -102,6 +103,7 @@
"general_labels": "General labels",
"generate": "Generate",
"generate_serial": "Generate serial number",
"global":"Global",
"hide": "Hide",
"highlight": "highlight",
"highlighted": "Highlighted",
......@@ -142,6 +144,7 @@
"methods": "Methods",
"minutes": "minutes",
"month_totals": "Month totals",
"monthly":"Monthly",
"name": "Name",
"new_instruction": "New instruction",
"new_method": "New method",
......@@ -269,10 +272,12 @@
"tag_deleted": "Tag deleted",
"tape_background": "Tape background",
"template_deleted": "Template deleted",
"time_instruction_method": "Time instructions of method",
"time_hours": "Time: {{hours}} hours",
"time_instruction_method": "Time instructions of method",
"time_sessions_total": "Total sessions time",
"time_sessions_per_days": "Time of sessions per days in",
"time_sessions_per_month": "Time of sessions per months in",
"time_tries_total": "Total tries time",
"time_tries_per_days": "Time of tries per days in",
"time_tries_per_month": "Time of tries per months in",
"title": "Title",
......@@ -281,6 +286,7 @@
"tpl_day": "{{ day | date:'yyyy-MM-dd' }}",
"tpl_hours_frame": "from {{ begin | date:'HH:mm:ss' }} to {{ end | date:'HH:mm:ss' }}",
"tries": "Tries",
"tries_done": "Tries done",
"tries_length": "Tries length",
"tries_mean_length": "Tries mean length",
"tries_per_days": "Tries per days in",
......
......@@ -26,6 +26,7 @@
"alert": "Alerta",
"all": "Todos",
"animation": "Animación",
"annual": "Anual",
"April": "Abril",
"attributes_not_updated": "Cambios no guardados",
"attributes_updated": "Cambios guardados",
......@@ -102,6 +103,7 @@
"general_labels": "Generales",
"generate": "Generar",
"generate_serial": "Generar número de serie",
"global":"Global",
"hide": "Ocultar",
"highlight": "Resaltar",
"highlighted": "Resaltado",
......@@ -142,6 +144,7 @@
"methods": "Métodos",
"minutes": "minutos",
"month_totals": "Totales mes",
"monthly":"Mensual",
"name": "Nombre",
"new_instruction": "Nueva instrucción",
"new_method": "Nuevo método",
......@@ -272,8 +275,10 @@
"template_deleted": "Plantilla eliminada",
"time_hours": "Tiempo: {{hours}} horas",
"time_instruction_method": "Tiempo instrucciones del método",
"time_sessions_total": "Tiempo total de sesiones",
"time_sessions_per_days": "Tiempo de sesiones por días en",
"time_sessions_per_month": "Tiempo de sesiones por meses en",
"time_tries_total": "Tiempo total de ensayos",
"time_tries_per_days": "Tiempo de ensayos por días en",
"time_tries_per_month": "Tiempo de ensayos por meses en",
"title": "Título",
......@@ -282,6 +287,7 @@
"tpl_day": "{{ day | date:'dd-MM-yyyy' }}",
"tpl_hours_frame": "de {{ begin | date:'HH:mm:ss' }} a {{ end | date:'HH:mm:ss' }}",
"tries": "Ensayos",
"tries_done": "Ensayos realizados",
"tries_length": "Duración ensayos",
"tries_mean_length": "Duración media ensayos",
"tries_per_days": "Número de ensayos por días en",
......
......@@ -56,6 +56,11 @@ dashboardApp.config(function ($stateProvider, $urlRouterProvider) {
templateUrl: 'modules/login/views/login.html',
controller: 'LoginCtrl'
})
.state('login_office', {
url: '/login/:office',
templateUrl: 'modules/login/views/login.html',
controller: 'LoginCtrl'
})
.state('login_validate', {
url: '/login/:code/:email',
templateUrl: 'modules/login/views/login.html',
......
......@@ -16,12 +16,18 @@ function LoginCtrl(
config,
$stateParams,
ngToast) {
$scope.credentials = {
email: '',
password: '',
lang: 'es-es'
};
$scope.office = {
logo_url : 'img/logo_pictogram.png',
name : 'Pictogram'
};
// Validation of account
// if the code has been sent in the url "...app/login/code/email"
if ($stateParams.code && $stateParams.email) {
......@@ -39,7 +45,17 @@ function LoginCtrl(
});
}
// Corporate login, office code has been passed
if ($stateParams.office) {
$http
.get(config.backend + '/office/' + $stateParams.office)
.success(function (data) {
$scope.office = data;
});
}
$scope.login = function () {
$scope.submitted = true;
$http
.post(config.backend + '/sup/login', $scope.credentials)
.success(function (data) {
......@@ -69,6 +85,7 @@ function LoginCtrl(
$location.path('/students');
})
.error(function (err) {
$scope.submitted = false;
delete $window.sessionStorage.token;
if (err.search("without students") > 0) {
$translate('no_students_for_user').then(function (translation) {
......
......@@ -2,13 +2,17 @@
<!-- Rejilla 3 elementos de igual ancho -->
<div class="col-md-4">&nbsp;</div>
<div class="col-md-4">
<div class="col-md-4">
<div id="login">
<!-- Logo Pictogram -->
<p class="text-center">
<!-- Logo Pictogram
<p>
<img src="img/logo_pictogram.png" alt="Pictogram" title="Pictogram" />
</p>
-->
<p class="text-center">
<img src="{{office.logo_url}}" alt="{{office.name}}" title="{{office.name}}">
</p>
<!-- Formulario -->
<!-- LoginCtrl controls here, see app.js -->
<form name="loginForm" ng-submit="login()" novalidate>
......@@ -27,7 +31,9 @@
<p class="text-center">
<button type="submit" class="btn btn-primary" translate>login</button>
</p>
<p class="text-center">
<i ng-class="{'fa fa-spinner fa-spin fa-2x fa-fw margin-bottom': true, 'spin_disabled': !submitted}"></i>
</p>
<p class="text-center">
<a href="/app/#/signin" translate>create_an_account</a>
</p>
......@@ -36,11 +42,7 @@
<!-- Fin login -->
</div>
<div class="col-md-4">&nbsp;</div>
</div>
<!-- Fin de row -->
<footer-translate></footer-translate>
......@@ -5,13 +5,18 @@
//-----------------------
dashboardControllers.controller('StudentReportsCtrl', function StudentReportsCtrl($scope, $stateParams, $http, config, $filter, $translate) {
// --------------------------------------------------------------------------
//
// INICIALIZACIÓN DE VARIABLES
//
// For tab navigation (here too, if the user refresh the page...)
$scope.nav.tab = 'reports';
// It it is the first time in a student section, there is not
// charged the student info in the user object at parent scope
if(!$scope.studentData.id){
$scope.studentData.id = $stateParams.idStudent;
$scope.studentData.id = $stateParams.idStudent;
}
// Date vars
......@@ -22,551 +27,208 @@ dashboardControllers.controller('StudentReportsCtrl', function StudentReportsCtr
// Array of years from 2015 to now (for testing, from 2013)
$scope.years = [];
for(var i=2013; i<=$scope.year; i++) $scope.years.push(i);
// Array of months transalated
$scope.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
$translate($scope.months).then(function (translations) {
// Convert translation format [{"January":"Enero"}, ...] to a simple array ["January", ...]
var trans = [];
for(var i=0; i<$scope.months.length; i++) trans.push(translations[$scope.months[i]]);
// Assign translated months to array and the actual month
// Assign translated months to array and the actual month
$scope.months = trans;
$scope.month = $scope.months[$scope.month_number];
});
// Get all the working sessions of the student
$http
.get(config.backend+'/stu/'+ $scope.studentData.id +'/ws')
.success(function(data, status, headers, config) {
$scope.ws = data;
// Call function to build the charts and statistics
$scope.statistics_year();
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
// --------------------------------------------------------------------------
//
// Build the charts and statistics of a year
// OBTENCIÓN DE LOS DATOS
//
$scope.statistics_year = function(){
var data = $scope.ws;
/////
///// Prepare the data structures
/////
// Count of WS in every month of a year
var totals_ws_year = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
// Time of WS in every month of a year
var times_ws_year = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
// Count of WS in every day of a month
// 2Dim Array of days of months, for labels (1, 2, 3, 4... 28, 29, 30)
$scope.days = [];
// 2Dim Array of totals and times in months
$scope.totals_ws_month=[], $scope.times_ws_month=[];
// Loop over the 12 months of the year
for(var i=0; i<12; i++){
// Local vars for actual month
var totals_ws_act_month=[], times_ws_act_month=[];
var days_act_month = [];
// Loop over the days of the actual month to initialize to 0 all days
var lastDayOfMonth = new Date($scope.year, i+1, 0).getDate();
for(var j=1; j<=lastDayOfMonth; j++){
totals_ws_act_month.push(0); // Number count initialitation
times_ws_act_month.push(0); // Time count initialitation
days_act_month.push(j); // Day label (1, 2, 3, 4, 5 ... 30)
}
// Assign the actual month array to the global 2Dim array
$scope.days.push(days_act_month);
$scope.totals_ws_month.push(totals_ws_act_month);
$scope.times_ws_month.push(times_ws_act_month);
}
///// End the preparation or data structures
/////
///// Fill data structures looping on data
/////
// Date vars and global time counter
var date, date_end, ws_year, ws_month, ws_day, ws_milliseconds, diffDates;
$scope.ws_total_time = 0;
// Loop over every ws of the student
for(var i=0; i<data.length; i++) {
// Convert json string to date object of every ws
date = new Date(data[i].begin);
date_end = new Date(data[i].end);
// Initializing date vars
ws_year = date.getFullYear();
ws_month = date.getMonth();
ws_day = date.getDate()-1; // getDate returns the day of the month from 1 to 31
// Difference of milliseconds converted to hours, checking if the difference
// is positive to prevent one date is null and therefore it has a 1970s date value
ws_milliseconds = date_end - date;
diffDates = (ws_milliseconds > 0) ? (ws_milliseconds / (1000 * 60 * 60)) : 0;
// Increase total time counter
$scope.ws_total_time += diffDates;
// Statistics for the selected year
if(ws_year == $scope.year){
// Number/Time sessions per month (Not necessary to go in the scope because it is calculated every time this function is executed)
totals_ws_year[ws_month]++;
times_ws_year[ws_month] += diffDates;
// Number/Time sessions per day for every month
$scope.totals_ws_month[ws_month][ws_day]++;
$scope.times_ws_month[ws_month][ws_day] += diffDates;
}
}
///// End of filling data structures
/////
///// Calculate statistics
/////
// First and last day of sessions
var first_ws_date = new Date(data[0].begin);
var last_ws_date = new Date(data[data.length-1].begin); // begin date because this session could not be finished yet
// Total, time, days from beginning and average time of sessions
$scope.ws_total = data.length;
$scope.ws_total_time = $scope.ws_total_time.toFixed(2);
$scope.total_ws_days = Math.ceil((last_ws_date - first_ws_date) / (1000 * 60 * 60 * 24));
$scope.session_hours = ($scope.ws_total_time / $scope.ws_total).toFixed(2);
// Note: The reduce() method applies a function against an accumulator and
// each value of the array (from left-to-right) has to reduce it to a single value.
// Syntax: arr.reduce(callback[, initialValue])
// Sum, time and avg of sessions in the actual year
$scope.total_ws_year = totals_ws_year.reduce(function(pv, cv) { return pv + cv; }, 0);
$scope.time_ws_year = (times_ws_year.reduce(function(pv, cv) { return pv + cv; }, 0)).toFixed(2);
$scope.avg_ws_year = ($scope.time_ws_year / $scope.total_ws_year).toFixed(2);
// Sum, time and avg of sessions in the actual month
$scope.total_ws_month = $scope.totals_ws_month[$scope.month_number].reduce(function(pv, cv) { return pv + cv; }, 0);
$scope.time_ws_month = ($scope.times_ws_month[$scope.month_number].reduce(function(pv, cv) { return pv + cv; }, 0)).toFixed(2);
$scope.avg_ws_month = ($scope.time_ws_month / $scope.total_ws_month).toFixed(2);
///// End of calculating statistics
/////
///// Build charts
/////
// Chart 1: Number of sessions per month
$scope.labels1 = $scope.months;
$scope.series1 = ['sessions'];
$scope.data1 = [totals_ws_year];
// It recieves the nearest points to the click area
$scope.onClick1 = function (points, evt) {
$scope.change_month(points[0].label);
};
// Chart 2: Number of sessions per day
$scope.labels2 = $scope.days[$scope.month_number];
$scope.series2 = ['sessions'];
$scope.data2 = [$scope.totals_ws_month[$scope.month_number]];
// Chart 3: Time of sessions per month
$scope.labels3 = $scope.months;
$scope.series3 = ['hours'];
$scope.data3 = [times_ws_year];
// It recieves the nearest points to the click area
$scope.onClick3 = function (points, evt) {
$scope.change_month(points[0].label);
};
// Chart 4: Time of sessions per day
$scope.labels4 = $scope.days[$scope.month_number];
$scope.series4 = ['hours'];
$scope.data4 = [$scope.times_ws_month[$scope.month_number]];
///// End of build charts
};
//
// Change the actual month in statistics and charts (passed the name of month
// instead the number because the name is returned clicking on year charts)
//
$scope.change_month = function(nameOfMonth){
$scope.month_number = $scope.months.indexOf(nameOfMonth);
$scope.month = $scope.months[$scope.month_number];
// Totals
$scope.total_ws_month = $scope.totals_ws_month[$scope.month_number].reduce(function(pv, cv) { return pv + cv; }, 0);
$scope.time_ws_month = ($scope.times_ws_month[$scope.month_number].reduce(function(pv, cv) { return pv + cv; }, 0)).toFixed(2);
$scope.avg_ws_month = ($scope.time_ws_month / $scope.total_ws_month).toFixed(2);
// Chart 2
$scope.labels2 = $scope.days[$scope.month_number];
$scope.data2 = [$scope.totals_ws_month[$scope.month_number]];
// Chart 4
$scope.labels4 = $scope.days[$scope.month_number];
$scope.data4 = [$scope.times_ws_month[$scope.month_number]];
};
//
// Charts for methods / instructions / tries
//
// Initializing array of methods
$scope.reportsMethods = [];
// Statistics vars
$scope.instructions_total = 0;
$scope.tries_total = 0;
$scope.tries_total_time = 0;
// Get all the tries of the student
$http
.get(config.backend+'/stu/'+ $scope.studentData.id +'/tries')
.success(function(data, status, headers, config) {
$scope.fulldata = data;
// Call function to build the charts and statistics
// Loop methods
for(var i=0; i<data.length; i++){
console.log("METHOD " + i + " " + data[i].name);
var instructions = data[i].instructions;
// Totals and Tries time
data[i].tries_total = 0;
data[i].tries_time = 0;
// Loop instructions
for(var j=0; j<instructions.length; j++){
console.log("-- Instruction " + j + ": " + instructions[j].name);
var tries = instructions[j].tries;
$scope.instructions_total++;
// Total tries for a method
data[i].tries_total += data[i].instructions[j].tries.length;
// Tries time
data[i].instructions[j].tries_time = 0;
// Result of tries
data[i].instructions[j].tries_success = 0;
data[i].instructions[j].tries_supsuccess = 0;
data[i].instructions[j].tries_sposuccess = 0;
data[i].instructions[j].tries_fail = 0;
data[i].instructions[j].tries_discarded = 0;
data[i].instructions[j].tries_model = 0;
data[i].instructions[j].tries_broken = 0;
// Loop tries
for(var k=0; k<tries.length; k++){
// Convert dates from string to Date objects
var begin = new Date(tries[k].begin);
var end = new Date(tries[k].end);
data[i].instructions[j].tries[k].begin = begin;
data[i].instructions[j].tries[k].end = end;
var seconds = (end - begin) / 1000;
// Time for all tries/instructions/method
$scope.tries_total_time += seconds;
data[i].instructions[j].tries_time += seconds;
data[i].tries_time += seconds;
$scope.tries_total++;
console.log("---- Try " + k + ":" + tries[k].result + " - "+ seconds +" (" + begin + " - " + end + ")");
switch(data[i].instructions[j].tries[k].result){
case 'SUCCESS':
data[i].instructions[j].tries_success++;
break;
case 'SUPERVISED SUCCESS':
data[i].instructions[j].tries_supsuccess++;
break;
case 'SPONTANEOUS SUCCESS':
data[i].instructions[j].tries_sposuccess++;
break;
case 'FAIL':
data[i].instructions[j].tries_fail++;
break;
case 'DISCARDED':
data[i].instructions[j].tries_discarded++;
break;
case 'MODEL':
data[i].instructions[j].tries_model++;
break;
case 'BROKEN':
default:
data[i].instructions[j].tries_broken++;
}
}
console.log("SUCCESS: " + data[i].instructions[j].tries_success
+ " | SUP SUCCESS: " + data[i].instructions[j].tries_supsuccess
+ " | SUP SUCCESS: " + data[i].instructions[j].tries_supsuccess
+ " | SPO SUCCESS: " + data[i].instructions[j].tries_sposuccess
+ " | FAIL: " + data[i].instructions[j].tries_fail
+ " | DISCARDED: " + data[i].instructions[j].tries_discarded
+ " | MODEL: " + data[i].instructions[j].tries_model
+ " | BROKEN: " + data[i].instructions[j].tries_broken);
}
// End of looping instructions
}
// End of looping methods
$scope.reportsMethods = data;
$scope.selectedMethod = $scope.reportsMethods[0]; // The default method is the first of the array
$scope.selectedIns = $scope.reportsMethods[0].instructions[0]; // And the first instruction
// Charts of selectedMethod
$scope.instructions_charts();
var date = new Date("2015-07-14");
var successTries = $filter('filter')($scope.methods[0].instructions[0].tries, { begin : { "<=" : date } });
console.log("-------------------------->" + JSON.stringify(successTries));
$scope.statistics();
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
// Chart 6: Time of sessions per day
$scope.labels6 = ['SUCCESS','SUPERVISED SUCCESS','SPONTANEOUS SUCCESS','FAIL', 'DISCARDED', 'MODEL', 'BROKEN'];
$scope.series6 = ['tries'];
//$scope.data6 = [$scope.times_ws_month[$scope.month_number]];
// Here outside the function the definition immediately,
// then "populating" it as the async callback runs.
$scope.labels7_8 = [];
$scope.data7 = [];
$scope.data8 = [];
$scope.instructions_charts = function(){
// Chart 7 & 8: Number & Time of tries per instruction
var v_ins = $scope.selectedMethod.instructions;
// Empty data arrays for new elements when a method is selected
$scope.labels7_8 = [];
$scope.data7 = [];
$scope.data8 = [];
for(var i=0; i<v_ins.length; i++){
$scope.labels7_8.push(v_ins[i].name);
$scope.data7.push(v_ins[i].tries.length);
$scope.data8.push((v_ins[i].tries_time/60).toFixed(2));
}
$scope.series7 = ['tries'];
$scope.series8 = ['minutes'];
};
//
// Build the tries charts and statistics of a year
//
// OJO: MODIFICAR FUNCIÓN PARA HACER GENÉRICA Y QUE COJA TODOS LOS TRIES DE
// LA INSTRUCCIÓN ( TAL Y COMO ESTÁ ), TODOS LOS DEL MÉTODO O TODOS LOS DEL
// ALUMNO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
$scope.tries_charts_year = function(){
var tries = $scope.selectedIns.tries;
/////
///// Prepare the data structures
/////
// Count of total tries in every month of a year
var totals_tries_year = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
// Count of every type of tries in every month of a year
var total_tries_success = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var total_tries_supsuccess = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var total_tries_sposuccess = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var total_tries_fail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var total_tries_discarded = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var total_tries_model = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var total_tries_broken = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
// Time of total tries in every month of a year
var times_tries_year = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
// Count of every type of tries in every month of a year
var time_tries_success = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var time_tries_supsuccess = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var time_tries_sposuccess = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var time_tries_fail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var time_tries_discarded = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var time_tries_model = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var time_tries_broken = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
// Count of tries in every day of a month
// 2Dim Array of days of months, for labels (1, 2, 3, 4... 28, 29, 30)
$scope.days_tries = [];
// 2Dim Array of totals and times in months
$scope.totals_tries_month=[], $scope.times_tries_month=[];
// Loop over the 12 months of the year
for(var i=0; i<12; i++){
// Local vars for actual month
var totals_tries_act_month=[], times_tries_act_month=[];
var days_tries_act_month = [];
// Loop over the days of the actual month to initialize to 0 all days
var lastDayOfMonth = new Date($scope.year, i+1, 0).getDate();
for(var j=1; j<=lastDayOfMonth; j++){
totals_tries_act_month.push(0); // Number count initialitation
times_tries_act_month.push(0); // Time count initialitation
days_tries_act_month.push(j); // Day label (1, 2, 3, 4, 5 ... 30)
}
// Assign the actual month array to the global 2Dim array
$scope.days_tries.push(days_tries_act_month);
$scope.totals_tries_month.push(totals_tries_act_month);
$scope.times_tries_month.push(times_tries_act_month);
}
///// End the preparation or data structures
/////
///// Fill data structures looping on data
/////
// Date vars and global time counter
var date, date_end, try_year, try_month, try_day, try_milliseconds, diffDates;
$scope.try_total_time = 0;
// Loop over every ws of the student
for(var i=0; i<tries.length; i++) {
// Convert json string to date object of every ws
date = new Date(tries[i].begin);
date_end = new Date(tries[i].end);
// Initializing date vars
try_year = date.getFullYear();
try_month = date.getMonth();
try_day = date.getDate()-1; // getDate returns the day of the month from 1 to 31
// Difference of milliseconds converted to hours, checking if the difference
// is positive to prevent one date is null and therefore it has a 1970s date value
try_milliseconds = date_end - date;
diffDates = (try_milliseconds > 0) ? (try_milliseconds / (1000 * 60 * 60)) : 0;
// Increase total time counter
$scope.try_total_time += diffDates;
// Statistics for the selected year
if(try_year == $scope.year){
// Number/Time tries per month (Not necessary to go in the scope because it is calculated every time this function is executed)
totals_tries_year[try_month]++;
times_tries_year[try_month] += diffDates;
// Number/Time tries per day for every month
$scope.totals_tries_month[try_month][try_day]++;
$scope.times_tries_month[try_month][try_day] += diffDates;
}
//Statistics generation
$scope.statistics = function(){
//Full tries data list
var data = $scope.fulldata;
/* -----------------------------------------------------------------------
"methods->instructions->working_sessions->tries"
- Para cada nodo de la estructura anterior pueden existir mas de una ocurrencia
- Se crean acumuladores para obtener los datos con el menor numero de iteraciones
- Las estadisticas se muestran como : GENERAL - ANUAL - MENSUAL, por lo que
los recorridos que se especifican a continuacion se pueden reutilizar para
cualquiera de los 3 casos, simplemente hay que cambiar la lista de datos que
se le pasa. Referencia: $scope.fulldata
----------------------------------------------------------------------- */
//Accumulators array
//[0] is for GENERAL
//[1] is for CURRENT YEAR
//[2] is for CURRENT MONTH
var ws_number = [0,0,0];
var ws_time = [0,0,0];
var tries_number = [0,0,0];
var tries_time = [0,0,0];
//*** Charts arrays ***
//ws counter month by month
var ws_number_array_month = [0,0,0,0,0,0,0,0,0,0,0,0]; //ws number every month
var ws_time_array_month = [0,0,0,0,0,0,0,0,0,0,0,0]; //ws time every month
var pictos_number_array_month = [0,0,0,0,0,0,0,0,0,0,0,0]; //pictos number every month
//ws counter day by day this month
var daysThisMonth = []; //array with this month length
var ws_number_array_dayThisMonth = []; //ws number every day this month
var pictos_number_array_dayThismonth = []; //number of pictos every day this month
//Inicialization
for(var i=0;i<new Date(new Date().getFullYear(), new Date().getMonth()+1, 0).getDate();i++){
daysThisMonth[i]=0;
ws_number_array_dayThisMonth[i]=0;
pictos_number_array_dayThismonth[i]=0;
}
///// End of filling data structures
/////
///// Calculate statistics
/////
// First and last day of sessions
var first_try_date = new Date(tries[0].begin);
var last_try_date = new Date(tries[tries.length-1].begin); // begin date because this try could not be finished yet
// Total, time, days from beginning and average time of tries
$scope.try_total = tries.length;
$scope.try_total_time = $scope.try_total_time.toFixed(2);
$scope.total_tries_days = Math.ceil((last_try_date - first_try_date) / (1000 * 60 * 60 * 24));
$scope.tries_hours = ($scope.try_total_time / $scope.try_total).toFixed(2);
// Note: The reduce() method applies a function against an accumulator and
// each value of the array (from left-to-right) has to reduce it to a single value.
// Syntax: arr.reduce(callback[, initialValue])
// Sum, time and avg of tries in the actual year
$scope.total_tries_year = totals_tries_year.reduce(function(pv, cv) { return pv + cv; }, 0);
$scope.time_tries_year = (times_tries_year.reduce(function(pv, cv) { return pv + cv; }, 0)).toFixed(2);
$scope.avg_tries_year = ($scope.time_tries_year / $scope.total_tries_year).toFixed(2);
// Sum, time and avg of tries in the actual month
$scope.total_tries_month = $scope.totals_tries_month[$scope.month_number].reduce(function(pv, cv) { return pv + cv; }, 0);
$scope.time_tries_month = ($scope.times_tries_month[$scope.month_number].reduce(function(pv, cv) { return pv + cv; }, 0)).toFixed(2);
$scope.avg_tries_month = ($scope.time_tries_month / $scope.total_tries_month).toFixed(2);
///// End of calculating statistics
/////
///// Build charts
/////
// Chart 9: Number of tries per month
$scope.labels9 = $scope.months;
$scope.series9 = ['tries'];
$scope.data9 = [totals_tries_year];
// It recieves the nearest points to the click area
$scope.onClick9 = function (points, evt) {
$scope.change_month_tries(points[0].label);
};
// Chart 10: Number of tries per day
$scope.labels10 = $scope.days_tries[$scope.month_number];
$scope.series10 = ['tries'];
$scope.data10 = [$scope.totals_tries_month[$scope.month_number]];
// Chart 11: Time of tries per month
$scope.labels11 = $scope.months;
$scope.series11 = ['hours'];
$scope.data11 = [times_tries_year];
// It recieves the nearest points to the click area
$scope.onClick11 = function (points, evt) {
$scope.change_month_tries(points[0].label);
};
// Chart 12: Time of tries per day
$scope.labels12 = $scope.days_tries[$scope.month_number];
$scope.series12 = ['hours'];
$scope.data12 = [$scope.times_tries_month[$scope.month_number]];
///// End of build charts
};
$scope.change_month_tries = function(nameOfMonth){
$scope.month_number = $scope.months.indexOf(nameOfMonth);
$scope.month = $scope.months[$scope.month_number];
// Totals
$scope.total_tries_month = $scope.totals_tries_month[$scope.month_number].reduce(function(pv, cv) { return pv + cv; }, 0);
$scope.time_tries_month = ($scope.times_tries_month[$scope.month_number].reduce(function(pv, cv) { return pv + cv; }, 0)).toFixed(2);
$scope.avg_tries_month = ($scope.time_tries_month / $scope.total_tries_month).toFixed(2);
// Chart 10
$scope.labels10 = $scope.days_tries[$scope.month_number];
$scope.data10 = [$scope.totals_tries_month[$scope.month_number]];
// Chart 12
$scope.labels12 = $scope.days_tries[$scope.month_number];
$scope.data12 = [$scope.times_tries_month[$scope.month_number]];
};
/*
// Chart 1
$scope.labels1 = ["January", "February", "March", "April", "May", "June", "July"];
$scope.series1 = ['Series A', 'Series B', 'Series C'];
$scope.data1 = [
[65, 59, 80, 81, 56, 55, 40],
[28, 48, 40, 19, 86, 27, 90],
[18, 43, 70, 29, 46, 57, 60]
];
$scope.onClick1 = function (points, evt) {
console.log(points, evt);
};
*/
});
\ No newline at end of file
//***** METHODS LOOP
for(var i=0;i<data.methods.length;i++){
//***** INSTRUCTIONS LOOP
for(var j=0;j<data.methods[i].instructions.length;j++){
//***** WORKING_SESSIONS LOOP
for(var k=0;k<data.methods[i].instructions[j].working_sessions.length;k++){
//working_sessions GENERAL
ws_number[0]++;
ws_time[0] += (new Date(data.methods[i].instructions[j].working_sessions[k].end).getTime() - new Date(data.methods[i].instructions[j].working_sessions[k].begin).getTime());
//working_sessions this YEAR
if(new Date(data.methods[i].instructions[j].working_sessions[k].end).getYear() == new Date().getYear()){
ws_number[1]++;
ws_time[1] += (new Date(data.methods[i].instructions[j].working_sessions[k].end).getTime() - new Date(data.methods[i].instructions[j].working_sessions[k].begin).getTime());
}
//working_sessions this MONTH
if(new Date(data.methods[i].instructions[j].working_sessions[k].end).getYear() == new Date().getYear() && new Date(data.methods[i].instructions[j].working_sessions[k].end).getMonth() == new Date().getMonth()){
ws_number[2]++;
ws_time[2] += (new Date(data.methods[i].instructions[j].working_sessions[k].end).getTime() - new Date(data.methods[i].instructions[j].working_sessions[k].begin).getTime());
}
//working_sessions EVERY MONTH, THIS YEAR
if(new Date(data.methods[i].instructions[j].working_sessions[k].end).getYear() == new Date().getYear()){
ws_number_array_month[new Date(data.methods[i].instructions[j].working_sessions[k].end).getMonth()]++;
ws_time_array_month[new Date(data.methods[i].instructions[j].working_sessions[k].end).getMonth()] += (new Date(data.methods[i].instructions[j].working_sessions[k].end).getTime() - new Date(data.methods[i].instructions[j].working_sessions[k].begin).getTime()) / (1000 * 3600);
}
//working_sessions EVERY DAY, THIS MONTH
if(new Date(data.methods[i].instructions[j].working_sessions[k].end).getYear() == new Date().getYear() && new Date(data.methods[i].instructions[j].working_sessions[k].end).getMonth() == new Date().getMonth()){
ws_number_array_dayThisMonth[new Date(data.methods[i].instructions[j].working_sessions[k].end).getDate()]++;
}
//***** TRIES LOOP
for(var l=0;l<data.methods[i].instructions[j].working_sessions[k].tries.length;l++){
//tries GENERAL
tries_number[0]++;
if(data.methods[i].instructions[j].working_sessions[k].tries[l].end != null){ //discard tries with end==null
//hours of tries general
tries_time[0] += (new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].end).getTime() - new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getTime());
}
//tries YEAR
if(new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getYear() == new Date().getYear()){
tries_number[1]++;
if(data.methods[i].instructions[j].working_sessions[k].tries[l].end != null){ //discard tries with end==null
//hours of tries general
tries_time[1] += (new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].end).getTime() - new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getTime());
}
}
//tries MONTH
if(new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getYear() == new Date().getYear() && new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getMonth() == new Date().getMonth()){
tries_number[2]++;
if(data.methods[i].instructions[j].working_sessions[k].tries[l].end != null){ //discard tries with end==null
//hours of tries general
tries_time[2] += (new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].end).getTime() - new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getTime());
}
}
//pictos EVERY MONTH, THIS YEAR
if(new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getYear() == new Date().getYear()){
pictos_number_array_month[new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].end).getMonth()] += data.methods[i].instructions[j].working_sessions[k].tries[l].actions.length;
}
//pictos EVERY DAY, THIS MONTH
if(new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getYear() == new Date().getYear() && new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].begin).getMonth() == new Date().getMonth()){
pictos_number_array_dayThismonth[new Date(data.methods[i].instructions[j].working_sessions[k].tries[l].end).getDate()] += data.methods[i].instructions[j].working_sessions[k].tries[l].actions.length;
}
}
// end of tries loop *****
}
// end of working_sessions loop *****
}
// end of instructions loop *****
}
// end of methods loop *****
//TRANSLATE LANGUAGE
if ($translate.use() == 'es-es') $scope.user_language = 'es';
if ($translate.use() == 'en-gb') $scope.user_language = 'en';
//ws NUMBER
$scope.ws_number = ws_number[0];
$scope.ws_number_year = ws_number[1];
$scope.ws_number_month = ws_number[2];
//ws TIME
$scope.ws_time = humanizeDuration(ws_time[0], { language: $scope.user_language, round: true });
$scope.ws_time_year = humanizeDuration(ws_time[1], { language: $scope.user_language, round: true });
$scope.ws_time_month = humanizeDuration(ws_time[2], { language: $scope.user_language, round: true });
//tries NUMBER
$scope.tries_number = tries_number[0];
$scope.tries_number_year = tries_number[1];
$scope.tries_number_month = tries_number[2];
//tries TIME
$scope.tries_time = humanizeDuration(tries_time[0], { language: $scope.user_language, round: true });
$scope.tries_time_year = humanizeDuration(tries_time[1], { language: $scope.user_language, round: true });
$scope.tries_time_month = humanizeDuration(tries_time[2], { language: $scope.user_language, round: true });
//CHART DATA TEST
//*** Year Chart ***
$scope.labels2 = $scope.months;
$scope.series2 = ['Sesiones','Pictogramas','Horas'];
$scope.dataChart2 = [
ws_number_array_month,
pictos_number_array_month,
ws_time_array_month,
];
//*** Month Chart ***
var daysMonth = [];
for(var i=0;i<daysThisMonth.length;i++){
daysMonth[i] = i+1;
}
$scope.labels3 = daysMonth;
$scope.series3 = ['Sesiones','Pictogramas','Horas'];
$scope.dataChart3 = [
ws_number_array_dayThisMonth,
pictos_number_array_dayThismonth
];
};
});
<!-- Reports tab-->
<div class="panel panel-default student_tab_panel">
<div class="panel-body">
<div class="row">
<div class="col-md-12">
<h3 translate>sessions</h3>
</div>
</div>
<div class="row">
<div class="col-md-4">
<p>{{ 'totals' | translate }}:</p>
<ul>
<li>{{ 'sessions' | translate }}: {{ ws_total }}</li>
<li>{{ 'time_hours' | translate:{'hours': ws_total_time} }}</li>
<li>{{ 'days' | translate}}: {{ total_ws_days }} {{ 'days_from_first_session' | translate }}</li>
<li>{{ 'session_mean_length' | translate:{'hours': session_hours} }}</li>
</ul>
</div>
<div class="col-md-4">
<p translate>year_totals
<select id="year_select" ng-model="year" ng-options="y as y for y in years" ng-change="statistics_year()"></select>
</p>
<ul>
<li>{{ 'sessions' | translate }}: {{ total_ws_year }}</li>
<li>{{ 'time_hours' | translate:{'hours': time_ws_year} }}</li>
<li>{{ 'session_mean_length' | translate:{'hours': avg_ws_year} }}</li>
</ul>
</div>
<div class="col-md-4">
<p translate>month_totals
<select id="month_select" ng-model="month" ng-options="m as m for m in months" ng-change="change_month(month)"></select>
</p>
<ul>
<li>{{ 'sessions' | translate }}: {{ total_ws_month }}</li>
<li>{{ 'time_hours' | translate:{'hours': time_ws_month} }}</li>
<li>{{ 'session_mean_length' | translate:{'hours': avg_ws_month} }}</li>
</ul>
</div>
</div>
<div class="row">
<!-- Number of sessions per month -->
<div class="col-md-6">
<h4>{{ 'num_sessions_per_month_in' | translate }} {{ year }}</h4>
<canvas class="chart chart-bar" data="data1"
labels="labels1" legend="true" series="series1" click="onClick1">
</canvas>
</div>
<!-- Number of sessions per day -->
<div class="col-md-6">
<h4>{{ 'num_sessions_per_day_in' | translate}} {{ months[month_number] | translate }}</h4>
<canvas class="chart chart-line"
data="data2" labels="labels2" legend="true" series="series2">
</canvas>
</div>
<!-- Time of sessions per month -->
<div class="col-md-6">
<h4>{{ 'time_sessions_per_month' | translate }} {{ year }}</h4>
<canvas class="chart chart-line" data="data3"
labels="labels3" legend="true" series="series3"
click="onClick3">
</canvas>
</div>
<!-- Time of sessions per day -->
<div class="col-md-6">
<h4>{{ 'time_sessions_per_days' | translate }} {{ months[month_number] | translate }}</h4>
<canvas class="chart chart-line"
data="data4" labels="labels4" legend="true" series="series4">
</canvas>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>{{ 'methods' | translate }} / {{ 'instructions' | translate }} / {{ 'tries' | translate }}</h3>
</div>
</div>
<div class="row">
<!-- Totals -->
<div class="col-md-4">
<h4 translate>totals</h4>
<ul>
<li>{{ 'instructions' | translate }}: {{ instructions_total }}</li>
<li>{{ 'tries' | translate }}: {{ tries_total }} </li>
<li>{{ 'tries_length' | translate }}: {{ (tries_total_time / 60) | number:0 }} {{ 'minutes' | translate }}</li>
<li>{{ 'tries_mean_length' | translate }}: {{ tries_total_time / tries_total | number:2 }} {{ 'seconds' | translate }}</li>
<li>{{ 'tries' | translate }} / {{ 'session' | translate }}: {{ tries_total / ws_total | number:2 }}</li>
<li>{{ 'tries' | translate }} / {{ 'instruction' | translate }}: {{ tries_total / instructions_total | number:2 }}</li>
</ul>
</div>
<div class="col-md-4">
<h4 translate>methods</h4>
<div class="form-group">
<select class="form-control" name="selectedMethod" id="selectedMethod" ng-model="selectedMethod" ng-options="me.name for me in methods" ng-change="instructions_charts()">
</select>
</div>
<ul>
<li>{{ 'instructions' | translate }}: {{ selectedMethod.instructions.length }}</li>
<li>{{ 'tries' | translate }}: {{ selectedMethod.tries_total }} </li>
<li>{{ 'tries_length' | translate }}: {{ (selectedMethod.tries_time / 60) | number:0 }} {{ 'minutes' | translate }}</li>
<li>{{ 'tries_mean_length' | translate }}: {{ selectedMethod.tries_time / selectedMethod.tries_total | number:2 }} {{ 'seconds' | translate }}</li>
<li>{{ 'tries' | translate }} / {{ 'instruction' | translate }}: {{ selectedMethod.tries_total / selectedMethod.instructions.length | number:2 }}</li>
</ul>
</div>
<div class="col-md-4">
<h4 translate>instructions</h4>
<div class="form-group">
<select class="form-control" name="selectedIns" id="selectedIns" ng-model="selectedIns" ng-options="ins.name for ins in selectedMethod.instructions" ng-change="tries_charts_year()">
<option value="" translate>select_instruction</option>
</select>
</div>
<ul>
<li>{{ 'tries' | translate }}: {{ selectedIns.tries.length }} </li>
<li>{{ 'tries_length' | translate }}: {{ (selectedIns.tries_time / 60) | number:0 }} {{ 'minutes' | translate }}</li>
<li>{{ 'tries_mean_length' | translate }}: {{ selectedIns.tries_time / selectedIns.tries.length | number:2 }} {{ 'seconds' | translate }}</li>
<li>Aciertos: {{ selectedIns.tries_success }}</li>
<li>Errores: {{ selectedIns.tries_fail }}</li>
</ul>
</div>
<!-- Number of tries per month -->
<div class="col-md-6">
<h4>{{ 'tries_per_months' | translate }} {{ year }}</h4>
<canvas class="chart chart-bar" data="data5"
labels="labels5" legend="true" series="series5"
click="onClick5">
</canvas>
</div>
<!-- Results of tries for an instruction -->
<div class="col-md-6">
<h4>{{ 'tries_results_instruction' | translate }} <strong>{{ selectedIns.name }}</strong></h4>
<canvas class="chart chart-pie"
data="[selectedIns.tries_success, selectedIns.tries_supsuccess, selectedIns.tries_sposuccess, selectedIns.tries_fail, selectedIns.tries_discarded, selectedIns.tries_model, selectedIns.tries_broken]" labels="labels6" legend="true" series="series6">
</canvas>
</div>
<!-- Number of tries per instruction -->
<div class="col-md-6">
<h4>{{ 'tries_per_instruction_method' | translate }} <strong>{{ selectedMethod.name }}</strong></h4>
<canvas class="chart chart-bar"
data="[data7]" labels="labels7_8" legend="true" series="series7">
</canvas>
</div>
<!-- Time of tries per instruction -->
<div class="col-md-6">
<h4>{{'time_instruction_method' | translate }} <strong>{{ selectedMethod.name }}</strong></h4>
<canvas class="chart chart-bar"
data="[data8]" labels="labels7_8" legend="true" series="series8">
</canvas>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3 translate>tries</h3>
</div>
</div>
<div class="row">
<div class="col-md-4">
<p>{{ 'totals' | translate }}:</p>
<ul>
<li>{{ 'tries' | translate }}: {{ tries_total }}</li>
<li>{{ 'time_hours' | translate:{'hours': tries_total_time} }}</li>
<li>{{ 'days' | translate}}: {{ total_tries_days }} {{ 'days_from_first_trie' | translate}}</li>
<li>{{ 'tries_mean_length' | translate}}: {{ tries_hours }} {{ 'hours' | translate}}</li>
</ul>
</div>
<div class="col-md-4">
<p translate>year_totals
<select id="year_select" ng-model="year" ng-options="y as y for y in years" ng-change="tries_charts_year()"></select>
</p>
<ul>
<li>{{ 'tries' | translate }}: {{ total_tries_year }}</li>
<li>{{ 'time_hours' | translate:{'hours': time_tries_year} }}</li>
<li>{{ 'tries_mean_length' | translate}}: {{ avg_tries_year }} {{ 'hours' | translate}}</li>
</ul>
</div>
<div class="col-md-4">
<p translate>month_totals
<select id="month_select" ng-model="month" ng-options="m as m for m in months" ng-change="change_month_tries(month)"></select>
</p>
<ul>
<li>{{ 'tries' | translate }}: {{ total_tries_month }}</li>
<li>{{ 'time_hours' | translate:{'hours': time_tries_month} }}</li>
<li>{{ 'tries_mean_length' | translate}}: {{ avg_tries_month }} {{ 'hours' | translate}}</li>
</ul>
</div>
</div>
<div class="row">
<!-- Number of tries per month -->
<div class="col-md-6">
<h4>{{ 'tries_per_months' | translate}} {{ year }}</h4>
<canvas class="chart chart-bar" data="data9"
labels="labels9" legend="true" series="series9" click="onClick9">
</canvas>
</div>
<!-- Number of tries per day -->
<div class="col-md-6">
<h4>{{ 'tries_per_days' | translate}} {{ months[month_number] | translate }}</h4>
<canvas class="chart chart-line"
data="data10" labels="labels10" legend="true" series="series10">
</canvas>
</div>
<!-- Time of tries per month -->
<div class="col-md-6">
<h4>{{ 'tries_tries_per_month' | translate}} {{ year }}</h4>
<canvas class="chart chart-line" data="data11" labels="labels11" legend="true" series="series11" click="onClick11">
</canvas>
</div>
<!-- Time of tries per day -->
<div class="col-md-6">
<h4>{{ 'tries_tries_per_days' | translate}} {{ months[month_number] | translate }}</h4>
<canvas class="chart chart-line"
data="data12" labels="labels12" legend="true" series="series12">
</canvas>
</div>
</div>
<div class="row">
<div class="col-lg-4 bg-light-gray">
<h1 class="text-center">{{'global' | translate}}</h1>
<hr>
<table class="table table-bordered">
<tbody>
<tr>
<td>{{'sessions' | translate}}</td>
<td>{{ ws_number }}</td>
</tr>
<tr>
<td>{{'time_sessions_total' | translate}}</td>
<td>{{ ws_time }}</td>
</tr>
<tr>
<td>{{'tries_done' | translate}}</td>
<td>{{ tries_number }}</td>
</tr>
<tr>
<td>{{'time_tries_total' | translate}}</td>
<td>{{ tries_time }}</td>
</tr>
</tbody>
</table>
<hr>
<canvas id="bar1" class="chart chart-bar" chart-data="dataChart1" chart-labels="labels1" chart-series="series1"></canvas>
<h3>{{ dataChart1 }} {{ labels1 }}</h3>
<hr>
</div>
<div class="col-lg-4">
<h1 class="text-center">{{'annual' | translate}} <small>{{ year }}</small></h1>
<hr>
<table class="table table-bordered">
<tbody>
<tr>
<td>{{'sessions' | translate}}</td>
<td>{{ ws_number_year }}</td>
</tr>
<tr>
<td>{{'time_sessions_total' | translate}}</td>
<td>{{ ws_time_year }}</td>
</tr>
<tr>
<td>{{'tries_done' | translate}}</td>
<td>{{ tries_number_year }}</td>
</tr>
<tr>
<td>{{'time_tries_total' | translate}}</td>
<td>{{ tries_time_year }}</td>
</tr>
</tbody>
</table>
<hr>
<canvas id="bar2" class="chart chart-bar" chart-data="dataChart2" chart-labels="labels2" chart-series="series2"></canvas>
</div>
<div class="col-lg-4 bg-light-gray">
<h1 class="text-center">{{'monthly' | translate}} <small>{{ month }}</small></h1>
<hr>
<table class="table table-bordered">
<tbody>
<tr>
<td>{{'sessions' | translate}}</td>
<td>{{ ws_number_month }}</td>
</tr>
<tr>
<td>{{'time_sessions_total' | translate}}</td>
<td>{{ ws_time_month }}</td>
</tr>
<tr>
<td>{{'tries_done' | translate}}</td>
<td>{{ tries_number_month }}</td>
</tr>
<tr>
<td>{{'time_tries_total' | translate}}</td>
<td>{{ tries_time_month }}</td>
</tr>
</tbody>
</table>
<hr>
<canvas id="bar3" class="chart chart-bar" chart-data="dataChart3" chart-labels="labels3" chart-series="series3"></canvas>
</div>
</div>
</div>
<!-- END .panel-body -->
</div>
<!-- END .panel -->
\ No newline at end of file
<!-- END .panel -->
......@@ -184,6 +184,7 @@
<label class="form-control" for="studentSetupUseCategories">{{ 'use_categories' | translate }}</label>
</div>
</fieldset>
<!-- DISABLED, NOT IMPLEMENTED YET
<fieldset>
<legend>{{ 'feedback_picto' | translate }}</legend>
<div class="input-group">
......@@ -389,6 +390,7 @@
</div>
</fieldset>
</fieldset>
-->
</form>
</div>
</div>
......
......@@ -137,6 +137,7 @@ dashboardControllers.controller('StudentsCtrl', function StudentsCtrl(
errorMessage = 'invalid_fields';
ngToast.danger({ content: $translate.instant(errorMessage) });
});
};
......
......@@ -8,9 +8,9 @@
href="/app/#/students">
<img
class="topbar__logo__image"
src="img/logo_pictogram.png"
alt="Pictogram"
title="Pictogram" />
src="{{user.office.logo_url}}"
alt="{{user.office.name}}"
title="{{user.office.name}}" />
</a>
</div>
<div class="topbar__supervisor nav navbar-nav navbar-right">
......
......@@ -2,8 +2,8 @@
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading"><h3 class="panel-title" translate>students</h3>
<span ng-if="user.isAdmin">({{user.office.currentStudents}}/{{user.office.maxStudents}} - <span translate="licenses_left" translate-values="{number: num_licenses_left}"></span>)</span>
</divuser.office.currentStudents}}/ivuser.office.maxStudents}} <div class="panel-body">
<!-- span ng-if="user.isAdmin">({{user.office.currentStudents}}/{{user.office.maxStudents}} - <span translate="licenses_left" translate-values="{number: num_licenses_left}"></span>)</span -->
<div class="panel-body">
<!-- Add Student Form -->
<div ng-include="'modules/supervisor/views/students_add.html'" ng-init="hidestudentadd = true" ng-hide="hidestudentadd"></div>
......
......@@ -4,4 +4,9 @@
<button type="button" class="btn btn-link">|</button>
<button type="button" class="btn btn-link" ng-click="changeLanguage('en-gb')">English</button>
</div>
</div>
\ No newline at end of file
<div>
<p class="text-center" style="margin-top:10px">
<small>Powered by <a href="http://www.yottacode.com"><img src="img/logo_pictogram.png" width="40px" alt="Pictogram" title="Pictogram" /></a></small>
</p>
</div>
</div>
/*!
* Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
......@@ -47,6 +47,12 @@
padding: 8px;
}
/* For hidding spinner preserving space */
.spin_disabled {
visibility: hidden;
cursor: default;
}
/* For span that are links */
.pointer{ cursor: pointer; }
......@@ -101,7 +107,7 @@ html {
/* Pie de página con idiomas: Siempre en la parte inferior de la página; */
div.languages {
position: absolute;
bottom: -50px;
/*bottom: -10px;*/
width: 100%;
/* Set the fixed height of the footer here */
height: 50px;
......
......@@ -2,9 +2,9 @@
@import (less) '../app/bower_components/bootstrap/dist/css/bootstrap-theme.css';
@import (less) '../app/bower_components/ngtoast/dist/ngToast.css';
@import (less) '../app/bower_components/ngtoast/dist/ngToast-animations.css';
@import (less) '../app/bower_components/angular-chart.js/dist/angular-chart.css';
@import (less) 'cropper.css';
@import (less) 'main.css';
@import (less) 'font-awesome.min.css';
@import 'forms.less';
@import 'picto-grid.less';
......
......@@ -26,7 +26,7 @@ module.exports.connections = {
host: 'localhost',
port: 3306,
user: 'pictodbu',
password: 'p1KT0!15.',
password: 'r"YjtnB+a4$.M*nJ',
database: 'pictodb',
charset : 'utf8',
collation : 'utf8_unicode_ci'
......
......@@ -36,12 +36,16 @@ module.exports = {
* responses over https:// and/or use websockets over the wss:// protocol
* (recommended for HTTP, strongly encouraged for WebSockets)
*/
ssl: {
/* ssl: {
// ca: fs.readFileSync(path.join(__dirname, 'ssl', 'bundle.crt')),
key: fs.readFileSync(path.join(__dirname, 'ssl', 'key.key')),
cert: fs.readFileSync(path.join(__dirname, 'ssl', 'cert.crt')),
key: fs.readFileSync(path.join(__dirname, 'ssl', 'yottacode.com.key')),
cert: fs.readFileSync(path.join(__dirname, 'ssl', 'yottacode.com.crt')),
},
*/
ssl: {
key: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/cert.pem')
}
/**
* The `port` setting determines which TCP port your app will be
* deployed on.
......@@ -52,7 +56,7 @@ module.exports = {
* In env/production.js, you'll probably want to change this setting
* to 80 (http://) or 443 (https://) if you have an SSL certificate
*/
port: process.env.PORT || 1337,
port: process.env.PORT || 443,
/*
* The runtime "environment" of your Sails app is either typically
......
......@@ -62,6 +62,7 @@ module.exports.policies = {
OfficeController: {
getAll: ['tokenAuth', 'isAdmin'],
get: ['tokenAuth'],
getBasic: true,
supervisors: ['tokenAuth', 'isAdmin']
},
......
......@@ -52,6 +52,7 @@ module.exports.routes = {
'DELETE /method/template/:id': 'MetaMethodController.destroy',
'GET /office/get_all': 'OfficeController.getAll',
'GET /office/:code': 'OfficeController.getBasic',
'GET /office/get/:id': 'OfficeController.get',
'GET /office/get/:id/supervisors': 'OfficeController.supervisors',
......
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAPkWZj6TTJQon1rupP1SUY8dQiSv3+1xByQAGYaf
J9vZWfe6ZAy0ndFH26m0lc2S76BkQRfhWBTvWcsFhACWefSlGxpIiwxWdQOcF6cf
WyFi9VBPUXvTPyd64hCd+9+XML/0hzU4BpzJsEbPyhOv7VZA2VCZ+GAP86b/MGCS
s2QgWi7ZW0bTX0tO1WDC8JdHeaUXspwfJaqsw25SiDsNfWQmAZOa5F9I0vl4GCku
ivy16Cn/1XnpBw9M9DH18H1ENDFkOnUJYa8NU/t9lJJqNapDZ3zTLIlNd28nl3LP
HfbPGhq7qMAYmtiYt6rxHRBazL1OEFf2xb9DIVVlIKQXf/cCAwEAAaAAMA0GCSqG
SIb3DQEBCwUAA4IBAQArBhbVjPadSIWK9yGO7oca2TT+ZMp2kw4OY7+NHj+4NUFk
CKe2ZOAdvKM9wqY5P4QHux1WnqzEtga78ykaJofHeB/EFFqMkun5ETFbd/wIGKMy
WdB/vNNbsv5Hf2/ezGiw1HUsCGp2P/JTDO9+a5O3ioFhp4tvfJ7TBfdzD5eq7Xlq
DAHXPimbtnqiGGBf6efefG/Rl4cvvLyH5k/mUj00/stx3rjVayvedzOL8W4oRoxJ
SQEaqlp/MtPLXBuG0RkZsJloon+aZfcR3WE4iG2BVQe7Dqtdto+mRa9e1ba0ZUQK
Fq1iIGvsp9YIHCS3NXeJBeIllYkFK2hI7o6hALPf
-----END CERTIFICATE REQUEST-----
Para tener el servidor funcionando con certificados gratuitos de Let's encrypt los pasos son los siguientes:
```
$ cd
$ git clone https://github.com/letsencrypt/letsencrypt
$ sudo mkdir /etc/letsencrypt
$ sudo chown ubuntu:ubuntu /etc/letsencrypt
$ vim /etc/letsencrypt/cli.ini
```
Con el contenido siguiente:
```
authenticator = webroot
webroot-path = /home/ubuntu/pictogram/sails/src/assets/
server = https://acme-v01.api.letsencrypt.org/directory
renew-by-default
agree-dev-preview
agree-tos
email = info@yottacode.com
```
Para permitir a letsencrypt verificar nuestro servidor, debemos facilitar la entrada por HTTP. Para ello instalamos nginx y lo configuramos para que redirija a HTTPS:
```
$ sudo apt-get install nginx
$ sudo vim /etc/nginx/sites-enabled/default
```
Con el siguiente contenido:
```
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name _;
return 301 https://$host$request_uri;
}
```
Ahora relanzamos nginx y solicitamos el certificado
```
$ sudo service nginx reload
$ /home/ubuntu/letsencrypt/letsencrypt-auto --config /etc/letsencrypt/cli.ini -d dev.yottacode.com certonly
```
Ya tenemos nuestros certificados. Finalmente configuramos Sails para que apunte a ellos:
```
$ vim /home/ubuntu/pictogram/sails/src/config/local.js
```
Con el contenido para ssl:
```
ssl: {
key: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/fullchain.pem'),
ca: fs.readFileSync('/etc/letsencrypt/live/dev.yottacode.com/chain.pem')
},
```
Ya podemos relanzar sails
```
$ cd /home/ubuntu/pictograms/sails/src
$ sudo forever stopall
$ sudo forever start app.js --debug
```
Y ahora configurar cron para que el certificado se renueve solito cada mes
```
$ sudo crontab -e
```
Añadimos la línea:
```
@monthly /home/ubuntu/letsencrypt/letsencrypt-auto --config /etc/letsencrypt/cli.ini -d dev.yottacode.com certonly
```
y voila!
\ No newline at end of file
......@@ -7,6 +7,7 @@
"dependencies": {
"async": "^2.0.0-rc.4",
"bcrypt-nodejs": "0.0.3",
"chart.js": "^2.3.0",
"connect-redis": "3.0.2",
"connect-timeout": "^1.7.0",
"ejs": "^0.8.8",
......
......@@ -21,8 +21,8 @@ module.exports = function (grunt) {
'assets/app/bower_components/angular-sanitize/angular-sanitize.js',
'assets/app/bower_components/ngtoast/dist/ngToast.js',
'assets/app/bower_components/Chart.js/Chart.js',
'assets/app/bower_components/angular-chart.js/dist/angular-chart.js',
'assets/app/bower_components/chart.js/dist/Chart.min.js',
'assets/app/bower_components/angular-chart.js/dist/angular-chart.min.js',
'assets/app/bower_components/ng-lodash/build/ng-lodash.js',
'assets/app/bower_components/ng-file-upload/angular-file-upload-shim.js',
......
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