From 0ec7e2a6126b752d22aaeee2a876fb7bc8fa2679 Mon Sep 17 00:00:00 2001 From: gorco Date: Thu, 18 Jan 2018 19:40:41 +0100 Subject: [PATCH 01/17] Added changes to use the new API --- app/public/js/controllers/class.js | 24 +++++++++++++----------- app/public/js/controllers/game.js | 6 +++++- app/views/view/classactivity.jade | 8 ++++---- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/public/js/controllers/class.js b/app/public/js/controllers/class.js index 8c1dd8e..3149497 100644 --- a/app/public/js/controllers/class.js +++ b/app/public/js/controllers/class.js @@ -21,7 +21,6 @@ angular.module('classApp', ['ngStorage', 'services']) .controller('ClassCtrl', ['$rootScope', '$scope', '$attrs', '$location', '$http', 'Classes', 'CONSTANTS', function ($rootScope, $scope, $attrs, $location, $http, Classes, CONSTANTS) { - var onSetClass = function() { if (!$scope.class) { throw new Error('No class for ClassCtrl'); @@ -61,7 +60,7 @@ angular.module('classApp', ['ngStorage', 'services']) // Teachers $scope.isRemovable = function (dev) { - var teachers = $scope.class.teachers; + var teachers = $scope.class.participants.teachers; if (teachers && teachers.length === 1) { return false; } @@ -73,17 +72,20 @@ angular.module('classApp', ['ngStorage', 'services']) $scope.inviteTeacher = function () { if ($scope.teacher.name && $scope.teacher.name.trim() !== '') { - $scope.class.teachers.push($scope.teacher.name); - $scope.class.$update(function () { - $scope.teacher.name = ''; + var route = CONSTANTS.PROXY + '/classes/' + $scope.class._id; + $http.put(route, { participants: {teachers: $scope.teacher.name}}).success(function (data) { + $scope.class = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); }); } }; $scope.ejectTeacher = function (teacher) { var route = CONSTANTS.PROXY + '/classes/' + $scope.class._id + '/remove'; - $http.put(route, {teachers: teacher}).success(function (data) { - $scope.class.teachers = data.teachers; + $http.put(route, { participants: {teachers: teacher}}).success(function (data) { + $scope.class = data; }).error(function (data, status) { console.error('Error on put' + route + ' ' + JSON.stringify(data) + ', status: ' + status); @@ -95,7 +97,7 @@ angular.module('classApp', ['ngStorage', 'services']) $scope.inviteStudent = function () { if ($scope.student.name && $scope.student.name.trim() !== '') { var route = CONSTANTS.PROXY + '/classes/' + $scope.class._id; - $http.put(route, {students: $scope.student.name}).success(function (data) { + $http.put(route, { participants: {students: $scope.student.name}}).success(function (data) { $scope.class = data; }).error(function (data, status) { console.error('Error on put' + route + ' ' + @@ -112,7 +114,7 @@ angular.module('classApp', ['ngStorage', 'services']) } else { route = CONSTANTS.PROXY + '/activities/' + $scope.selectedActivity._id + '/remove'; } - $http.put(route, {students: student}).success(function (data) { + $http.put(route, { participants: {students: student}}).success(function (data) { $scope.class = data; }).error(function (data, status) { console.error('Error on put' + route + ' ' + @@ -128,8 +130,8 @@ angular.module('classApp', ['ngStorage', 'services']) } }); var route = CONSTANTS.PROXY + '/classes/' + $scope.selectedClass._id; - $http.put(route, {students: students}).success(function (data) { - $scope.class.students = data.students; + $http.put(route, { participants: {students: students}}).success(function (data) { + $scope.class = data; }).error(function (data, status) { console.error('Error on put', route, status); }); diff --git a/app/public/js/controllers/game.js b/app/public/js/controllers/game.js index 6d29d2c..e701205 100644 --- a/app/public/js/controllers/game.js +++ b/app/public/js/controllers/game.js @@ -68,7 +68,11 @@ angular.module('gameApp', ['ngStorage', 'services', 'ngFileUpload']) $scope.public = 'btn-default'; $scope.publicGame = function () { - $scope.game.$update(); + $http.put(CONSTANTS.PROXY + '/games/' + $scope.game._id, {public: $scope.game.public}).success(function (data) { + }).error(function (data, status) { + $scope.game.public = !$scope.game.public; + console.error('Error on post /games/' + $scope.game._id + ' ' + JSON.stringify(data) + ', status: ' + status); + }); }; $scope.changeGameLink = function () { diff --git a/app/views/view/classactivity.jade b/app/views/view/classactivity.jade index ceaa047..7bce835 100644 --- a/app/views/view/classactivity.jade +++ b/app/views/view/classactivity.jade @@ -115,10 +115,10 @@ th Teacher th Remove tbody - tr(ng-if='class.teachers.length == 0') + tr(ng-if='class.participants.teachers.length == 0') td(colspan=6) div.alert.alert-warning(style='margin-bottom: 0px') No students found - tr(ng-repeat='teacher in class.teachers') + tr(ng-repeat='teacher in class.participants.teachers') td label {{teacher}} td @@ -147,10 +147,10 @@ th Student th Remove tbody - tr(ng-if='class.students.length == 0') + tr(ng-if='class.participants.students.length == 0') td(colspan=6) div.alert.alert-warning(style='margin-bottom: 0px') No students found - tr(ng-repeat='student in class.students') + tr(ng-repeat='student in class.participants.students') td label {{student}} td From 3d56b9a9b967592b72eecbe7250b1e7f1764510e Mon Sep 17 00:00:00 2001 From: gorco Date: Thu, 1 Feb 2018 18:29:10 +0100 Subject: [PATCH 02/17] courses front --- app/public/js/controllers/class-list.js | 101 +++++++++++++++++++++++- app/public/js/services.js | 8 ++ app/views/view/home.jade | 14 ++++ 3 files changed, 119 insertions(+), 4 deletions(-) diff --git a/app/public/js/controllers/class-list.js b/app/public/js/controllers/class-list.js index 9c62baa..fb3e7ff 100644 --- a/app/public/js/controllers/class-list.js +++ b/app/public/js/controllers/class-list.js @@ -19,22 +19,57 @@ 'use strict'; angular.module('classesApp', ['ngStorage', 'services']) - .controller('ClassListCtrl', ['$scope', '$rootScope', '$location', '$http', 'Classes', '$timeout', - function ($scope, $rootScope, $location, $http, Classes, $timeout) { + .controller('ClassListCtrl', ['$scope', '$rootScope', '$location', '$http', 'Classes', 'Courses', '$timeout', 'CONSTANTS', + function ($scope, $rootScope, $location, $http, Classes, Courses, $timeout, CONSTANTS) { $scope.activity = {}; + $scope.courseId = {}; $scope.class = {}; + $scope.newCourse = {}; + + $scope.editCourse = {}; + $scope.editBoxCourse = {}; + $scope.coursesTitles = {}; $scope.goToClass = function(c) { $rootScope.$broadcast('selectClass', { class: c}); }; var getClasses = function () { - $scope.classes = Classes.my(); + Classes.my().$promise.then(function(classes) { + $scope.classes = classes; + refreshCoursesTitles(); + }); + }; + + var getCourses = function () { + $scope.courses = Courses.all().$promise.then(function(courses) { + $scope.courses = courses; + $scope.courses.unshift({_id: 'NEW', title:'New Course'}); + $scope.courses.unshift({title:'No Course'}); + refreshCoursesTitles(); + }); }; getClasses(); + getCourses(); $scope.loading = false; + var refreshCoursesTitles = function(){ + if($scope.courses && $scope.classes ) { + $scope.classes.forEach(function (cl) { + $scope.coursesTitles[cl.courseId] = 'No Course'; + if(cl.courseId) { + for (var i = 0; $scope.courses.length; i++) { + if ($scope.courses[i]._id === cl.courseId) { + $scope.coursesTitles[cl.courseId] = $scope.courses[i].title; + break; + } + } + } + }); + } + }; + $scope.createClass = function () { var c = new Classes(); c.name = $scope.class.name ? $scope.class.name : 'New class'; @@ -44,6 +79,64 @@ angular.module('classesApp', ['ngStorage', 'services']) }); }; + $scope.editBox = function(classObj) { + return $scope.editBoxCourse[classObj._id]; + }; + + $scope.showSelectBox = function(classObj) { + return $scope.editCourse[classObj._id]; + }; + + $scope.editCourseClass = function (classObj) { + Object.keys($scope.editCourse).forEach(function(key) { + $scope.editCourse[key] = false; + $scope.editBoxCourse[classObj._id] = false; + }); + $scope.editCourse[classObj._id] = true; + }; + + $scope.acceptCourseClass = function (classObj) { + var courseId = $scope.courseId.id; + if(courseId === 'NEW') { + if ($scope.newCourse.newName){ + $http.post(CONSTANTS.PROXY + '/courses', {title: $scope.newCourse.newName}) + .success(function(data){ + $http.put(CONSTANTS.PROXY + '/classes/' + classObj._id, {courseId: data._id}) + .success(function () { + getClasses(); + }).error(function (data, status) { + console.error('Error on put /classes/' + classObj._id + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + }).error(function (data, status) { + console.error('Error on put /classes/' + classObj._id + ' ' + + JSON.stringify(data) + ', status: ' + status); + }).finally(function () { + $scope.editBoxCourse[classObj._id] = false; + $scope.editCourse[classObj._id] = false; + $scope.newCourse = {}; + getCourses(); + }); + } else { + $scope.editBoxCourse[classObj._id] = true; + } + } else { + var reqObj; + if(!courseId){ + courseId = ''; + } + reqObj = {courseId: courseId}; + $http.put(CONSTANTS.PROXY + '/classes/' + classObj._id, reqObj) + .success(function () { + $scope.editCourse[classObj._id] = false; + getClasses(); + }).error(function (data, status) { + console.error('Error on put /classes/' + classObj._id + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + }; + $scope.deleteClass = function (classObj) { classObj.$remove().then(function() { $timeout(function() { @@ -53,7 +146,7 @@ angular.module('classesApp', ['ngStorage', 'services']) }; $scope.$on('refreshClasses', function () { - Classes.my().$promise.then(function(classes) { $scope.classes = classes; }); + getClasses(); }); } ]); \ No newline at end of file diff --git a/app/public/js/services.js b/app/public/js/services.js index 0062099..19422e5 100644 --- a/app/public/js/services.js +++ b/app/public/js/services.js @@ -61,6 +61,14 @@ services.factory('Classes', ['$resource', 'CONSTANTS', } ]); +services.factory('Courses', ['$resource', 'CONSTANTS', + function ($resource, CONSTANTS) { + return $resource(CONSTANTS.PROXY + '/courses', {}, { + all: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/courses'} + }); + } +]); + var loadingStatus = {}; services.factory('Activities', ['$resource', 'CONSTANTS', diff --git a/app/views/view/home.jade b/app/views/view/home.jade index 900b592..89f6520 100644 --- a/app/views/view/home.jade +++ b/app/views/view/home.jade @@ -117,6 +117,8 @@ thead tr th Name + th Course + th(ng-if='isTeacher()') th Created th(ng-if='isTeacher()') Delete tbody @@ -127,6 +129,18 @@ td(ng-controller='AppCtrl') span.glyphicon.glyphicon-stats.right10 a(ng-href='#', ng-click='goToClass(class)') {{class.name}} + td(ng-if='showSelectBox(class) && !editBox(class)') + select.form-control(ng-model='courseId.id') + option(ng-repeat='course in courses' value='{{ course._id }}') {{ course.title }} + td(ng-if='!showSelectBox(class) && !editBox(class)') {{coursesTitles[class.courseId]}} + td(ng-if='editBox(class)') + input.form-control(placeholder='New Course Name' type='text' ng-model='newCourse.newName') + td(ng-if='isTeacher() && !showSelectBox(class)') + a(ng-click='editCourseClass(class)') + span.glyphicon.glyphicon-pencil + td(ng-if='isTeacher() && showSelectBox(class)') + a(ng-click='acceptCourseClass(class)') + span.glyphicon.glyphicon-ok td {{class._id | prettyDateId }} td(ng-if='isTeacher()') a(ng-click='deleteClass(class)') From 65532da4b89f6ea8d6dedc074f32b377e828f92b Mon Sep 17 00:00:00 2001 From: gorco Date: Tue, 20 Feb 2018 13:49:48 +0100 Subject: [PATCH 03/17] Added modal dependencies --- app/public/js/controllers/app.js | 2 +- app/views/layout.jade | 3 +++ bower.json | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/public/js/controllers/app.js b/app/public/js/controllers/app.js index f179340..2ee7c9b 100644 --- a/app/public/js/controllers/app.js +++ b/app/public/js/controllers/app.js @@ -20,7 +20,7 @@ // Declare app level module which depends on filters, and services angular.module('myApp', [ - 'ngRoute', 'toolbarApp', 'signupApp', 'loginApp', 'loginPluginApp', 'classApp', 'classesApp','activitiesApp', + 'ngRoute', 'toolbarApp', 'signupApp', 'loginApp', 'loginPluginApp', 'classApp', 'participantsApp', 'classesApp', 'activitiesApp', 'activityApp', 'gameApp', 'analysisApp', 'kibanaApp', 'gamesApp', 'activityApp', 'analyticsApp', 'devVisualizatorApp', 'services', 'xeditable', 'env-vars', 'ui.router' ]).run(function (editableOptions, $localStorage, $cookies) { diff --git a/app/views/layout.jade b/app/views/layout.jade index a6ca3d2..d2872ea 100644 --- a/app/views/layout.jade +++ b/app/views/layout.jade @@ -56,6 +56,8 @@ html script(src="bower/jquery-file-upload/js/jquery.fileupload.js") script(src="bower/checklist-model/checklist-model.js") script(src="bower/ngstorage/ngStorage.min.js") + script(src="bower/angular-animate/angular-animate.min.js") + script(src="bower/angular-sanitize/angular-sanitize.min.js") script(src="bower/ng-file-upload/ng-file-upload.js") script(src="bower/d3/d3.min.js") script(src='libs/xapicollection.min.js') @@ -72,6 +74,7 @@ html script(src='js/controllers/kibana.js') script(src='js/controllers/game-list.js') script(src='js/controllers/class.js') + script(src='js/controllers/participantsConf.js') script(src='js/controllers/class-list.js') script(src='js/controllers/activity.js') script(src='js/controllers/activity-list.js') diff --git a/bower.json b/bower.json index e190b90..3a9e13f 100644 --- a/bower.json +++ b/bower.json @@ -9,9 +9,12 @@ "angular-mocks": "1.5.8", "angular-xeditable": "0.5.0", "angular-gridster": "0.13.15", + "angular-animate": "1.5.8", + "angular-sanitize": "1.5.8", "checklist-model": "0.10.0", "angular-bootstrap": "2.1.4", "angular-ui-router": "^1.0.3", + "angular-ui-bootstrap": "^2.5.6", "ng-file-upload": "12.2.13", "ngstorage" : "0.3.11", "bootstrap": "3.3.7", From 9f72c79eb2816cc8e212b83c7219afb5efe9f51f Mon Sep 17 00:00:00 2001 From: gorco Date: Tue, 20 Feb 2018 13:50:08 +0100 Subject: [PATCH 04/17] Added controller to configure participants in a class --- app/public/js/controllers/class-list.js | 26 +- app/public/js/controllers/class.js | 114 ++----- app/public/js/controllers/participantsConf.js | 321 ++++++++++++++++++ 3 files changed, 365 insertions(+), 96 deletions(-) create mode 100644 app/public/js/controllers/participantsConf.js diff --git a/app/public/js/controllers/class-list.js b/app/public/js/controllers/class-list.js index fb3e7ff..30d4bcd 100644 --- a/app/public/js/controllers/class-list.js +++ b/app/public/js/controllers/class-list.js @@ -44,8 +44,8 @@ angular.module('classesApp', ['ngStorage', 'services']) var getCourses = function () { $scope.courses = Courses.all().$promise.then(function(courses) { $scope.courses = courses; - $scope.courses.unshift({_id: 'NEW', title:'New Course'}); - $scope.courses.unshift({title:'No Course'}); + $scope.courses.unshift({_id: 'NEW', title: 'New Course'}); + $scope.courses.unshift({title: 'No Course'}); refreshCoursesTitles(); }); }; @@ -54,11 +54,11 @@ angular.module('classesApp', ['ngStorage', 'services']) getCourses(); $scope.loading = false; - var refreshCoursesTitles = function(){ - if($scope.courses && $scope.classes ) { + var refreshCoursesTitles = function() { + if ($scope.courses && $scope.classes) { $scope.classes.forEach(function (cl) { $scope.coursesTitles[cl.courseId] = 'No Course'; - if(cl.courseId) { + if (cl.courseId) { for (var i = 0; $scope.courses.length; i++) { if ($scope.courses[i]._id === cl.courseId) { $scope.coursesTitles[cl.courseId] = $scope.courses[i].title; @@ -86,7 +86,7 @@ angular.module('classesApp', ['ngStorage', 'services']) $scope.showSelectBox = function(classObj) { return $scope.editCourse[classObj._id]; }; - + $scope.editCourseClass = function (classObj) { Object.keys($scope.editCourse).forEach(function(key) { $scope.editCourse[key] = false; @@ -97,10 +97,10 @@ angular.module('classesApp', ['ngStorage', 'services']) $scope.acceptCourseClass = function (classObj) { var courseId = $scope.courseId.id; - if(courseId === 'NEW') { - if ($scope.newCourse.newName){ + if (courseId === 'NEW') { + if ($scope.newCourse.newName) { $http.post(CONSTANTS.PROXY + '/courses', {title: $scope.newCourse.newName}) - .success(function(data){ + .success(function(data) { $http.put(CONSTANTS.PROXY + '/classes/' + classObj._id, {courseId: data._id}) .success(function () { getClasses(); @@ -109,8 +109,8 @@ angular.module('classesApp', ['ngStorage', 'services']) JSON.stringify(data) + ', status: ' + status); }); }).error(function (data, status) { - console.error('Error on put /classes/' + classObj._id + ' ' + - JSON.stringify(data) + ', status: ' + status); + console.error('Error on put /classes/' + classObj._id + ' ' + + JSON.stringify(data) + ', status: ' + status); }).finally(function () { $scope.editBoxCourse[classObj._id] = false; $scope.editCourse[classObj._id] = false; @@ -122,7 +122,7 @@ angular.module('classesApp', ['ngStorage', 'services']) } } else { var reqObj; - if(!courseId){ + if (!courseId) { courseId = ''; } reqObj = {courseId: courseId}; @@ -136,7 +136,7 @@ angular.module('classesApp', ['ngStorage', 'services']) }); } }; - + $scope.deleteClass = function (classObj) { classObj.$remove().then(function() { $timeout(function() { diff --git a/app/public/js/controllers/class.js b/app/public/js/controllers/class.js index 3149497..2524e03 100644 --- a/app/public/js/controllers/class.js +++ b/app/public/js/controllers/class.js @@ -18,9 +18,9 @@ 'use strict'; -angular.module('classApp', ['ngStorage', 'services']) - .controller('ClassCtrl', ['$rootScope', '$scope', '$attrs', '$location', '$http', 'Classes', 'CONSTANTS', - function ($rootScope, $scope, $attrs, $location, $http, Classes, CONSTANTS) { +angular.module('classApp', ['ngStorage', 'services', 'ngAnimate', 'ngSanitize', 'ui.bootstrap']) + .controller('ClassCtrl', ['$rootScope', '$scope', '$attrs', '$location', '$http', '$uibModal', 'Classes', 'CONSTANTS', + function ($rootScope, $scope, $attrs, $location, $http, $uibModal, Classes, CONSTANTS) { var onSetClass = function() { if (!$scope.class) { throw new Error('No class for ClassCtrl'); @@ -49,91 +49,38 @@ angular.module('classApp', ['ngStorage', 'services']) $scope.student = {}; $scope.teacher = {}; - // Class - - $scope.changeName = function () { - $scope.class.$update(function() { - $rootScope.$broadcast('refreshClasses'); + $scope.open = function (size) { + var modalInstance = $uibModal.open({ + animation: this.animationsEnabled, + templateUrl: 'participantsModal', + controller: 'ModalInstanceCtrl', + size: size, + controllerAs: '$scope', + resolve: { + items: function () { + return { + username: $scope.username, + classId: $scope.class._id, + classes: Classes, + http: $http, + constants: CONSTANTS + }; + } + } }); - }; - - // Teachers - - $scope.isRemovable = function (dev) { - var teachers = $scope.class.participants.teachers; - if (teachers && teachers.length === 1) { - return false; - } - if ($scope.username === dev) { - return false; - } - return true; - }; - - $scope.inviteTeacher = function () { - if ($scope.teacher.name && $scope.teacher.name.trim() !== '') { - var route = CONSTANTS.PROXY + '/classes/' + $scope.class._id; - $http.put(route, { participants: {teachers: $scope.teacher.name}}).success(function (data) { - $scope.class = data; - }).error(function (data, status) { - console.error('Error on put' + route + ' ' + - JSON.stringify(data) + ', status: ' + status); - }); - } - }; - $scope.ejectTeacher = function (teacher) { - var route = CONSTANTS.PROXY + '/classes/' + $scope.class._id + '/remove'; - $http.put(route, { participants: {teachers: teacher}}).success(function (data) { - $scope.class = data; - }).error(function (data, status) { - console.error('Error on put' + route + ' ' + - JSON.stringify(data) + ', status: ' + status); + modalInstance.result.then(function () { + console.log('HOLA'); + }, function () { + console.log('Modal dismissed at: ' + new Date()); }); }; - // Students - - $scope.inviteStudent = function () { - if ($scope.student.name && $scope.student.name.trim() !== '') { - var route = CONSTANTS.PROXY + '/classes/' + $scope.class._id; - $http.put(route, { participants: {students: $scope.student.name}}).success(function (data) { - $scope.class = data; - }).error(function (data, status) { - console.error('Error on put' + route + ' ' + - JSON.stringify(data) + ', status: ' + status); - }); - } - }; - - - $scope.ejectStudent = function (student, fromClass) { - var route = ''; - if (fromClass) { - route = CONSTANTS.PROXY + '/classes/' + $scope.selectedClass._id + '/remove'; - } else { - route = CONSTANTS.PROXY + '/activities/' + $scope.selectedActivity._id + '/remove'; - } - $http.put(route, { participants: {students: student}}).success(function (data) { - $scope.class = data; - }).error(function (data, status) { - console.error('Error on put' + route + ' ' + - JSON.stringify(data) + ', status: ' + status); - }); - }; + // Class - $scope.addCsvClass = function () { - var students = []; - $scope.fileContent.contents.trim().split(',').forEach(function (student) { - if (student) { - students.push(student); - } - }); - var route = CONSTANTS.PROXY + '/classes/' + $scope.selectedClass._id; - $http.put(route, { participants: {students: students}}).success(function (data) { - $scope.class = data; - }).error(function (data, status) { - console.error('Error on put', route, status); + $scope.changeName = function () { + $scope.class.$update(function() { + $rootScope.$broadcast('refreshClasses'); }); }; @@ -159,4 +106,5 @@ angular.module('classApp', ['ngStorage', 'services']) } }; } - ]); \ No newline at end of file + ]); + diff --git a/app/public/js/controllers/participantsConf.js b/app/public/js/controllers/participantsConf.js new file mode 100644 index 0000000..04d6e68 --- /dev/null +++ b/app/public/js/controllers/participantsConf.js @@ -0,0 +1,321 @@ +/* + * Copyright 2016 e-UCM (http://www.e-ucm.es/) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * This project has received funding from the European Union’s Horizon + * 2020 research and innovation programme under grant agreement No 644187. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 (link is external) + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +// Please note that the close and dismiss bindings are from $uibModalInstance. + +angular.module('participantsApp', []) + .controller('ModalInstanceCtrl', + function ($uibModalInstance, items) { + var $http = items.http; + var CONSTANTS = items.constants; + var Classes = items.classes; + + var $ctrl = this; + $ctrl.classId = items.classId; + $ctrl.username = items.username; + $ctrl.classGroups = []; + $ctrl.classGroupings = []; + + $ctrl.selectedGroup = undefined; + $ctrl.selectedGrouping = undefined; + + $ctrl.$onInit = function () { + Classes.get({classId: $ctrl.classId}).$promise.then(function (data) { + $ctrl.class = data; + }); + updateGroups(); + updateGroupings(); + }; + + var updateGroups = function () { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.classId + '/groups'; + $http.get(route).success(function (data) { + $ctrl.classGroups = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + }; + + var updateGroupings = function () { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.classId + '/groupings'; + $http.get(route).success(function (data) { + $ctrl.classGroupings = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + }; + + $ctrl.selectGroup = function (group) { + if ($ctrl.selectedGroup === group) { + $ctrl.selectedGroup = undefined; + } else { + $ctrl.selectedGroup = group; + } + + $ctrl.selectedGrouping = undefined; + }; + + $ctrl.isInSelectedGroup = function (usr, role, group) { + if (group) { + return group.participants[role].indexOf(usr) !== -1; + } + if ($ctrl.selectedGroup) { + return $ctrl.selectedGroup.participants[role].indexOf(usr) !== -1; + } + return false; + }; + + $ctrl.selectGrouping = function (grouping) { + if ($ctrl.selectedGrouping === grouping) { + $ctrl.selectedGrouping = undefined; + } else { + $ctrl.selectedGrouping = grouping; + } + + $ctrl.selectedGroup = undefined; + }; + + $ctrl.getGroupThClass = function(group) { + if ($ctrl.selectedGroup && $ctrl.selectedGroup._id === group._id) { + return 'bg-success'; + } + if ($ctrl.selectedGrouping && $ctrl.isInSelectedGrouping(group._id, 'group')) { + return 'bg-warning'; + } + return ''; + }; + + $ctrl.getUserThClass = function(usr, role) { + if ($ctrl.selectedGroup && $ctrl.isInSelectedGroup(usr, role)) { + return 'bg-success'; + } + if ($ctrl.selectedGrouping && $ctrl.isInSelectedGrouping(usr, role)) { + return 'bg-warning'; + } + return ''; + }; + + $ctrl.isInSelectedGrouping = function (id, role) { + if ($ctrl.selectedGrouping) { + if (role === 'group') { + return $ctrl.selectedGrouping.groups.indexOf(id) !== -1; + } + + for (var i = 0; i < $ctrl.selectedGrouping.groups.length; i++) { + for (var j = 0; j < $ctrl.classGroups.length; j++) { + if ($ctrl.classGroups[j]._id === $ctrl.selectedGrouping.groups[i]) { + if ($ctrl.isInSelectedGroup(id, role, $ctrl.classGroups[j])) { + return true; + } + } + } + + } + + } + return false; + }; + + // Teachers + $ctrl.isRemovable = function (tea) { + var teachers = $ctrl.class.participants.teachers; + if (teachers && teachers.length === 1) { + return false; + } + if ($ctrl.username === tea) { + return false; + } + return true; + }; + + $ctrl.inviteUser = function (role) { + var object = {participants: {}}; + var user; + switch (role) { + case 'teacher': { + user = $ctrl.teacher.name; + object.participants = {teachers: user}; + break; + } + case 'assistant': { + user = $ctrl.assistant.name; + object.participants = {assistants: user}; + break; + } + case 'student': { + user = $ctrl.student.name; + object.participants = {students: user}; + break; + } + } + if (user && user.trim() !== '') { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id; + $http.put(route, object).success(function (data) { + $ctrl.class = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + }; + + $ctrl.ejectUser = function (user, role) { + var object = {participants: {}}; + switch (role) { + case 'teacher': { + object.participants = {teachers: user}; + break; + } + case 'assistant': { + object.participants = {assistants: user}; + break; + } + case 'student': { + object.participants = {students: user}; + break; + } + } + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id + '/remove'; + $http.put(route, object).success(function (data) { + $ctrl.class = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + }; + + $ctrl.addCsvClass = function () { + var students = []; + $ctrl.fileContent.contents.trim().split(',').forEach(function (student) { + if (student) { + students.push(student); + } + }); + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.selectedClass._id; + $http.put(route, {participants: {students: students}}).success(function (data) { + $ctrl.class = data; + }).error(function (data, status) { + console.error('Error on put', route, status); + }); + }; + + $ctrl.createGroup = function () { + if ($ctrl.group.name && $ctrl.group.name.trim() !== '') { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id + '/groups'; + $http.post(route, { + name: $ctrl.group.name, + participants: {students: [], assistants: []} + }).success(function (data) { + updateGroups(); + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + }; + + $ctrl.modifyGroup = function (usr, role, toAdd) { + if ($ctrl.selectedGroup) { + var route; + if (toAdd) { + route = CONSTANTS.PROXY + '/classes/groups/' + $ctrl.selectedGroup._id; + } else { + route = CONSTANTS.PROXY + '/classes/groups/' + $ctrl.selectedGroup._id + '/remove'; + } + var participants = {participants: {students: [], assistants: [], teachers: []}}; + switch (role) { + case 'student': { + participants.participants.students.push(usr); + break; + } + case 'assistant': { + participants.participants.assistants.push(usr); + break; + } + case 'teacher': { + participants.participants.teachers.push(usr); + break; + } + } + $http.put(route, participants).success(function (data) { + $ctrl.selectedGroup = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + }; + + $ctrl.removeGroup = function (group) { + if ($ctrl.group.name && $ctrl.group.name.trim() !== '') { + var route = CONSTANTS.PROXY + '/classes/groups/' + group._id; + $http.delete(route).success(function (data) { + updateGroups(); + }).error(function (data, status) { + console.error('Error on delete' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + }; + + $ctrl.createGrouping = function () { + if ($ctrl.grouping.name && $ctrl.grouping.name.trim() !== '') { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id + '/groupings'; + $http.post(route, {name: $ctrl.grouping.name, groups: []}).success(function (data) { + updateGroupings(); + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + }; + + $ctrl.modifyGrouping = function (group, toAdd) { + if ($ctrl.selectedGrouping) { + var route; + if (toAdd) { + route = CONSTANTS.PROXY + '/classes/groupings/' + $ctrl.selectedGrouping._id; + } else { + route = CONSTANTS.PROXY + '/classes/groupings/' + $ctrl.selectedGrouping._id + '/remove'; + } + $http.put(route, {groups: [group._id]}).success(function (data) { + $ctrl.selectedGrouping = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + }; + + $ctrl.removeGrouping = function (grouping) { + if ($ctrl.grouping.name && $ctrl.grouping.name.trim() !== '') { + var route = CONSTANTS.PROXY + '/classes/groupings/' + grouping._id; + $http.delete(route).success(function (data) { + updateGroupings(); + }).error(function (data, status) { + console.error('Error on delete' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + }; + } + ); From f50f5e165343a930b909389379751d6851de2e72 Mon Sep 17 00:00:00 2001 From: gorco Date: Tue, 20 Feb 2018 13:50:35 +0100 Subject: [PATCH 05/17] Added view to configure participants in a class by a modal windows --- app/views/view/classactivity.jade | 239 +++++++++++++++++++++++------- 1 file changed, 185 insertions(+), 54 deletions(-) diff --git a/app/views/view/classactivity.jade b/app/views/view/classactivity.jade index 7bce835..c6e1b5c 100644 --- a/app/views/view/classactivity.jade +++ b/app/views/view/classactivity.jade @@ -96,64 +96,195 @@ .col-md-10(ng-if='lti.key') label Launch URL: kbd.label-success {{ lti.launch }} + .col-md-4(ng-if='isTeacher()') .panel.panel-default .panel-heading h3 - label Class teachers + label Configure your class participants .panel-body - p An activity will inherit all the class teachers when created - .panel.panel-primary - .panel-heading Add teacher to class + button.btn.btn-primary(ng-click="open('lg')") Configure + span.right20 + span.glyphicon.glyphicon-user + + // PARTICIPANTS MODAL + script#participantsModal(type="text/ng-template") + div.modal-header.row + .modal-title + .col-md-6 + h1 Configure Participants + div.modal-body.row + .col-md-6 + .panel.panel-default + .panel-heading + h3 + label Class Teachers .panel-body - label Username: - input.form-control(type='text' ng-model='teacher.name') - a.btn.btn-primary(type='button', ng-click='inviteTeacher(true)') - span.glyphicon.glyphicon-plus.right10 - table.table.table-hover - thead - tr - th Teacher - th Remove - tbody - tr(ng-if='class.participants.teachers.length == 0') - td(colspan=6) - div.alert.alert-warning(style='margin-bottom: 0px') No students found - tr(ng-repeat='teacher in class.participants.teachers') - td - label {{teacher}} - td - a(ng-if='isRemovable(teacher)' ng-click='ejectTeacher(teacher, true)') - span.glyphicon.glyphicon-remove-sign - .panel.panel-default - .panel-heading - h3 - label Class Students - .panel-body - p An activity will inherit all the class students when created - .panel.panel-primary - .panel-heading Add students to class + p An activity will inherit all the class teachers when created + .panel.panel-primary + .panel-heading Add teacher to class + .panel-body + label Username: + input.form-control(type='text' ng-model='$scope.teacher.name') + a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("teacher")') + span.glyphicon.glyphicon-plus.right10 + table.table.table-hover + thead + tr + th Teacher + th(ng-show='$scope.selectedGroup') Add or Remove + th(ng-show='!$scope.selectedGroup') Remove + tbody + tr(ng-if='$scope.class.participants.teachers.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No students found + tr(ng-repeat='teacher in $scope.class.participants.teachers') + td(ng-class='$scope.getUserThClass(teacher, "teachers")') + label {{teacher}} + td(ng-show='$scope.selectedGroup && !$scope.isInSelectedGroup(teacher, "teachers")' ng-class='$scope.isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') + a(ng-click='$scope.modifyGroup(teacher, "teacher", true)') + span.glyphicon.glyphicon-plus + td(ng-show='$scope.isInSelectedGroup(teacher, "teachers")' ng-class='$scope.isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') + a(ng-click='$scope.modifyGroup(teacher, "teacher", false)') + span.glyphicon.glyphicon-minus + td(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping' ng-class='$scope.isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') + a(ng-if='$scope.isRemovable(teacher)' ng-click='$scope.ejectUser(teacher, "teacher")') + span.glyphicon.glyphicon-remove-sign + .panel.panel-default + .panel-heading + h3 + label Class Assistants .panel-body - label Add students to class from file: - input#inputFile.file(type='file' file-reader="fileContent") - button.btn.btn-primary(ng-click='addCsvClass(true)') Add class - hr - label Username: - input.form-control(type='text' ng-model='student.name') - a.btn.btn-primary(type='button', ng-click='inviteStudent(true)') - span.glyphicon.glyphicon-plus.right10 - table.table.table-hover - thead - tr - th Student - th Remove - tbody - tr(ng-if='class.participants.students.length == 0') - td(colspan=6) - div.alert.alert-warning(style='margin-bottom: 0px') No students found - tr(ng-repeat='student in class.participants.students') - td - label {{student}} - td - a(ng-click='ejectStudent(student, true)') - span.glyphicon.glyphicon-remove-sign + p An activity will inherit all the class assistants when created + .panel.panel-primary + .panel-heading Add assistant to class + .panel-body + label Username: + input.form-control(type='text' ng-model='$scope.assistant.name') + a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("assistant")') + span.glyphicon.glyphicon-plus.right10 + table.table.table-hover + thead + tr + th Assistant + th(ng-show='$scope.selectedGroup') Add or Remove + th(ng-show='!$scope.selectedGroup') Remove + tbody + tr(ng-if='$scope.class.participants.assistants.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No assistants found + tr(ng-repeat='assistant in $scope.class.participants.assistants') + td(ng-class='$scope.getUserThClass(assistant, "assistants")') + label {{assistant}} + td(ng-show='$scope.selectedGroup && !$scope.isInSelectedGroup(assistant, "assistants")' ng-class='$scope.isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') + a(ng-click='$scope.modifyGroup(assistant, "assistant", true)') + span.glyphicon.glyphicon-plus + td(ng-show='$scope.isInSelectedGroup(assistant, "assistants")' ng-class='$scope.isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') + a(ng-click='$scope.modifyGroup(assistant, "assistant", false)') + span.glyphicon.glyphicon-minus + td(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping' ng-class='$scope.isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') + a(ng-click='$scope.ejectUser(assistant, "assistant")') + span.glyphicon.glyphicon-remove-sign + .panel.panel-default + .panel-heading + h3 + label Class Students + .panel-body + p An activity will inherit all the class students when created + .panel.panel-primary + .panel-heading Add students to class + .panel-body + label Add students to class from file: + input#inputFile.file(type='file' file-reader="$scope.fileContent") + button.btn.btn-primary(ng-click='$scope.addCsvClass()') Add class + hr + label Username: + input.form-control(type='text' ng-model='$scope.student.name') + a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("student")') + span.glyphicon.glyphicon-plus.right10 + table.table.table-hover + thead + tr + th Student + th(ng-show='$scope.selectedGroup') Add or Remove + th(ng-show='!$scope.selectedGroup') Remove + tbody + tr(ng-if='$scope.class.participants.students.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No students found + tr(ng-repeat='student in $scope.class.participants.students') + td(ng-class='$scope.getUserThClass(student, "students")') + label {{student}} + td(ng-show='$scope.selectedGroup && !$scope.isInSelectedGroup(student, "students")' ng-class='$scope.isInSelectedGroup(student, "students") ? "bg-success" : ""') + a(ng-click='$scope.modifyGroup(student, "student", true)') + span.glyphicon.glyphicon-plus + td(ng-show='$scope.isInSelectedGroup(student, "students")' ng-class='$scope.isInSelectedGroup(student, "students") ? "bg-success" : ""') + a(ng-click='$scope.modifyGroup(student, "student", false)') + span.glyphicon.glyphicon-minus + td(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping' ng-class='$scope.isInSelectedGroup(student, "students") ? "bg-success" : ""') + a(ng-click='$scope.ejectUser(student, "student")') + span.glyphicon.glyphicon-remove-sign + .col-md-6 + .panel.panel-default + .panel-heading + h3 + label Class Groups + .panel-body + p The groups help you to configure your users activities easely + .panel.panel-success + .panel-heading Add group to class + .panel-body + label Group name: + input.form-control(type='text' ng-model='$scope.group.name') + a.btn.btn-success(type='button', ng-click='$scope.createGroup()') + span.glyphicon.glyphicon-plus.right10 + table.table.table-hover + thead + tr + th Group + th Remove + tbody + tr(ng-if='$scope.classGroups.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No groups created yet + tr(ng-repeat='group in $scope.classGroups') + td(ng-class='$scope.getGroupThClass(group)') + a.btn(ng-click='$scope.selectGroup(group)') {{group.name}} + td(ng-show='$scope.selectedGrouping && !$scope.isInSelectedGrouping(group._id, "group")' ng-class='$scope.getGroupThClass(group)') + a(ng-click='$scope.modifyGrouping(group, true)') + span.glyphicon.glyphicon-plus + td(ng-show='$scope.selectedGrouping && $scope.isInSelectedGrouping(group._id, "group")' ng-class='$scope.getGroupThClass(group)') + a(ng-click='$scope.modifyGrouping(group, false)') + span.glyphicon.glyphicon-minus + td(ng-show='!$scope.selectedGrouping' ng-class='$scope.getGroupThClass(group)') + a(ng-click='$scope.removeGroup(group)') + span.glyphicon.glyphicon-remove-sign + .panel.panel-default + .panel-heading + h3 + label Class Groupings + .panel-body + p The groupings help you to configure your users activities grouping groups of participants + .panel.panel-warning + .panel-heading Add grouping to class + .panel-body + label Grouping name: + input.form-control(type='text' ng-model='$scope.grouping.name') + a.btn.btn-warning(type='button', ng-click='$scope.createGrouping()') + span.glyphicon.glyphicon-plus.right10 + table.table.table-hover + thead + tr + th Grouping + th Remove + tbody + tr(ng-if='$scope.classGroupings.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No groups created yet + tr(ng-class='$scope.selectedGrouping._id === grouping._id ? "bg-warning" : ""' ng-repeat='grouping in $scope.classGroupings') + td + a.btn(ng-click='$scope.selectGrouping(grouping)') {{grouping.name}} + td + a(ng-click='$scope.removeGrouping(grouping)') + span.glyphicon.glyphicon-remove-sign + From 88b06cb560f9f77a474af1f234526e93b099b0cf Mon Sep 17 00:00:00 2001 From: gorco Date: Tue, 20 Feb 2018 17:39:27 +0100 Subject: [PATCH 06/17] Improved WIP --- app/public/js/controllers/participantsConf.js | 6 +- app/views/view/classactivity.jade | 93 ++++++++++--------- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/app/public/js/controllers/participantsConf.js b/app/public/js/controllers/participantsConf.js index 04d6e68..214c054 100644 --- a/app/public/js/controllers/participantsConf.js +++ b/app/public/js/controllers/participantsConf.js @@ -65,7 +65,7 @@ angular.module('participantsApp', []) }; $ctrl.selectGroup = function (group) { - if ($ctrl.selectedGroup === group) { + if ($ctrl.selectedGroup && $ctrl.selectedGroup._id === group._id) { $ctrl.selectedGroup = undefined; } else { $ctrl.selectedGroup = group; @@ -85,7 +85,7 @@ angular.module('participantsApp', []) }; $ctrl.selectGrouping = function (grouping) { - if ($ctrl.selectedGrouping === grouping) { + if ($ctrl.selectedGrouping && $ctrl.selectedGrouping._id === grouping._id) { $ctrl.selectedGrouping = undefined; } else { $ctrl.selectedGrouping = grouping; @@ -258,6 +258,7 @@ angular.module('participantsApp', []) } $http.put(route, participants).success(function (data) { $ctrl.selectedGroup = data; + updateGroups(); }).error(function (data, status) { console.error('Error on put' + route + ' ' + JSON.stringify(data) + ', status: ' + status); @@ -299,6 +300,7 @@ angular.module('participantsApp', []) } $http.put(route, {groups: [group._id]}).success(function (data) { $ctrl.selectedGrouping = data; + updateGroupings(); }).error(function (data, status) { console.error('Error on put' + route + ' ' + JSON.stringify(data) + ', status: ' + status); diff --git a/app/views/view/classactivity.jade b/app/views/view/classactivity.jade index c6e1b5c..018d6fa 100644 --- a/app/views/view/classactivity.jade +++ b/app/views/view/classactivity.jade @@ -119,14 +119,15 @@ h3 label Class Teachers .panel-body - p An activity will inherit all the class teachers when created - .panel.panel-primary - .panel-heading Add teacher to class - .panel-body - label Username: - input.form-control(type='text' ng-model='$scope.teacher.name') - a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("teacher")') - span.glyphicon.glyphicon-plus.right10 + div(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping') + p An activity will inherit all the class teachers when created + .panel.panel-primary + .panel-heading Add teacher to class + .panel-body + label Username: + input.form-control(type='text' ng-model='$scope.teacher.name') + a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("teacher")') + span.glyphicon.glyphicon-plus.right10 table.table.table-hover thead tr @@ -154,14 +155,15 @@ h3 label Class Assistants .panel-body - p An activity will inherit all the class assistants when created - .panel.panel-primary - .panel-heading Add assistant to class - .panel-body - label Username: - input.form-control(type='text' ng-model='$scope.assistant.name') - a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("assistant")') - span.glyphicon.glyphicon-plus.right10 + div(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping') + p An activity will inherit all the class assistants when created + .panel.panel-primary + .panel-heading Add assistant to class + .panel-body + label Username: + input.form-control(type='text' ng-model='$scope.assistant.name') + a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("assistant")') + span.glyphicon.glyphicon-plus.right10 table.table.table-hover thead tr @@ -189,18 +191,19 @@ h3 label Class Students .panel-body - p An activity will inherit all the class students when created - .panel.panel-primary - .panel-heading Add students to class - .panel-body - label Add students to class from file: - input#inputFile.file(type='file' file-reader="$scope.fileContent") - button.btn.btn-primary(ng-click='$scope.addCsvClass()') Add class - hr - label Username: - input.form-control(type='text' ng-model='$scope.student.name') - a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("student")') - span.glyphicon.glyphicon-plus.right10 + div(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping') + p An activity will inherit all the class students when created + .panel.panel-primary + .panel-heading Add students to class + .panel-body + label Add students to class from file: + input#inputFile.file(type='file' file-reader="$scope.fileContent") + button.btn.btn-primary(ng-click='$scope.addCsvClass()') Add class + hr + label Username: + input.form-control(type='text' ng-model='$scope.student.name') + a.btn.btn-primary(type='button', ng-click='$scope.inviteUser("student")') + span.glyphicon.glyphicon-plus.right10 table.table.table-hover thead tr @@ -229,14 +232,15 @@ h3 label Class Groups .panel-body - p The groups help you to configure your users activities easely - .panel.panel-success - .panel-heading Add group to class - .panel-body - label Group name: - input.form-control(type='text' ng-model='$scope.group.name') - a.btn.btn-success(type='button', ng-click='$scope.createGroup()') - span.glyphicon.glyphicon-plus.right10 + div(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping') + p The groups help you to configure your users activities easely + .panel.panel-success + .panel-heading Add group to class + .panel-body + label Group name: + input.form-control(type='text' ng-model='$scope.group.name') + a.btn.btn-success(type='button', ng-click='$scope.createGroup()') + span.glyphicon.glyphicon-plus.right10 table.table.table-hover thead tr @@ -263,14 +267,15 @@ h3 label Class Groupings .panel-body - p The groupings help you to configure your users activities grouping groups of participants - .panel.panel-warning - .panel-heading Add grouping to class - .panel-body - label Grouping name: - input.form-control(type='text' ng-model='$scope.grouping.name') - a.btn.btn-warning(type='button', ng-click='$scope.createGrouping()') - span.glyphicon.glyphicon-plus.right10 + div(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping') + p The groupings help you to configure your users activities grouping groups of participants + .panel.panel-warning + .panel-heading Add grouping to class + .panel-body + label Grouping name: + input.form-control(type='text' ng-model='$scope.grouping.name') + a.btn.btn-warning(type='button', ng-click='$scope.createGrouping()') + span.glyphicon.glyphicon-plus.right10 table.table.table-hover thead tr From 8122cb370ec6e407cf3b375e095daf5673d3feef Mon Sep 17 00:00:00 2001 From: Victorma Perez Colado Date: Thu, 8 Feb 2018 20:27:01 +0100 Subject: [PATCH 07/17] Added attempt calls in services --- app/public/js/services.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/public/js/services.js b/app/public/js/services.js index 19422e5..3a769a1 100644 --- a/app/public/js/services.js +++ b/app/public/js/services.js @@ -76,12 +76,16 @@ services.factory('Activities', ['$resource', 'CONSTANTS', var Activity = $resource(CONSTANTS.PROXY + '/activities/:activityId', { activityId: '@_id', versionId: '@versionId', - gameId: '@gameId' + gameId: '@gameId', + username: '@username' }, { my: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/activities/my' }, forClass: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/classes/:classId/activities/my' }, forGame: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/games/:gameId/versions/:versionId/activities/my' }, - update: { method: 'PUT' } + update: { method: 'PUT' }, + attempts: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/activities/:activityId/attempts'}, + myAttempts: { method: 'GET', url: CONSTANTS.PROXY + '/activities/:activityId/attempts/my'}, + userAttempts: { method: 'GET', url: CONSTANTS.PROXY + '/activities/:activityId/attempts/:username'} }); Object.defineProperty(Activity.prototype, 'loading', { @@ -109,7 +113,7 @@ services.factory('Role', ['$localStorage', function ($localStorage) { return { isUser: function () { - return $localStorage && $localStorage.user; + return ($localStorage !== undefined) && ($localStorage.user !== undefined); }, isAdmin: function () { return $localStorage.user && $localStorage.user.roles && $localStorage.user.roles.indexOf('admin') !== -1; From 92de8366e511859dedad69e9e96a00b770ec1851 Mon Sep 17 00:00:00 2001 From: Victorma Perez Colado Date: Thu, 8 Feb 2018 20:27:13 +0100 Subject: [PATCH 08/17] Added new view for attempts --- app/views/view/data.jade | 4 +- app/views/view/data/attempts.jade | 74 +++++++++++++++++++++++++++++++ app/views/view/data/menu.jade | 4 ++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 app/views/view/data/attempts.jade diff --git a/app/views/view/data.jade b/app/views/view/data.jade index 745635f..ddd8426 100644 --- a/app/views/view/data.jade +++ b/app/views/view/data.jade @@ -48,6 +48,8 @@ include data/realtime/realtime #students.tab-pane include data/students + #attempts.tab-pane + include data/attempts #teachers.tab-pane include data/teachers #config.tab-pane @@ -58,7 +60,7 @@ include data/dev-visualization #game-analytics.tab-pane include data/game-analytics - .panel.panel-default(ng-if='!isDeveloper() && selectedActivity' ng-controller='ActivityCtrl' lite='true' activity='{{ selectedActivity }}' style='width=100%') + .panel.panel-default(ng-if='!isDeveloper() && selectedActivity' ng-controller='ActivityCtrl' lite='false' activity='{{ selectedActivity }}' style='width=100%') .panel-body(ng-if='!game || !version || !activity') img(src='loading.svg') .panel-heading(ng-if='game && version && activity' style='font-size: 2.5em') diff --git a/app/views/view/data/attempts.jade b/app/views/view/data/attempts.jade new file mode 100644 index 0000000..450f0fc --- /dev/null +++ b/app/views/view/data/attempts.jade @@ -0,0 +1,74 @@ +//- + Copyright 2016 e-UCM (http://www.e-ucm.es/) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + This project has received funding from the European Union’s Horizon + 2020 research and innovation programme under grant agreement No 644187. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 (link is external) + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +.container-fluid.theme-showcase + h1 Attempts + .row(ng-if='isTeacher()') + hr + .col-xs-12 + h4 All Attempts + .row + .col-md-12 + table.table.table-hover + thead + tr + th Type + th Username + th Animal Name + th See attempts + tbody + tr(ng-if='!attempts || attempts.length === 0') + td(colspan=4) + div.alert.alert-warning(style='margin-bottom: 0px') No attempts found + tbody(ng-repeat='gameplay in attempts') + tr + td + span.glyphicon(ng-class='(gameplay.playerType === "identified") ? "glyphicon-user" : "glyphicon-sunglasses"') + td + label {{gameplay.playerName}} + td + label {{gameplay.animalName}} + td + a.btn.btn-primary(type='button', ng-click='gameplayShown[gameplay._id] = !gameplayShown[gameplay._id]') + .glyphicon(ng-class='gameplayShown[gameplay._id] ? "glyphicon-remove-sign" : "glyphicon-plus"') + tr.attemptRow(ng-show='gameplayShown[gameplay._id]') + td(colspan=4) + .panel.panel-primary + .panel-heading {{ gameplay.playerName }} attempts + .panel-body + table.table.table-hover + thead + tr + th Number + th Start + th End + th View Stats + tbody + tr(ng-if='gameplay.attempts.length === 0') + td(colspan=4) + div.alert.alert-warning(style='margin-bottom: 0px') No attempts for this player + tbody(ng-repeat='attempt in gameplay.attempts') + tr + td + label {{attempt.number}} + td + label {{attempt.start}} + td + label {{attempt.end}} + td + a.btn.btn-primary(type='button', ng-click='goToAttemptGraph(attempt)') + .glyphicon.glyphicon-stats \ No newline at end of file diff --git a/app/views/view/data/menu.jade b/app/views/view/data/menu.jade index 94f1f47..cea58c7 100644 --- a/app/views/view/data/menu.jade +++ b/app/views/view/data/menu.jade @@ -28,6 +28,10 @@ ul.nav span.left-menu-item(href="#students", role="tab", data-toggle="tab", ng-click="view = 'students'") span.glyphicon.glyphicon-apple.right20 | Students + li + span.left-menu-item(href="#attempts", role="tab", data-toggle="tab", ng-click="view = 'attempts'") + span.glyphicon.glyphicon-apple.right20 + | Attempts li(ng-if='isTeacher() || isStudent()') span.left-menu-item(href="#realtime", role="tab", data-toggle="tab", ng-click="view = 'realtime'") span.glyphicon.glyphicon-dashboard.right20 From 36cc68c96732c7fa4edb7ca60888ff3e8be95587 Mon Sep 17 00:00:00 2001 From: Victorma Perez Colado Date: Tue, 13 Feb 2018 19:09:42 +0100 Subject: [PATCH 09/17] Updated lite usage of activity controller --- app/public/js/controllers/activity.js | 23 ++++++++++++++++++---- app/views/view/classactivity.jade | 2 +- app/views/view/data.jade | 2 +- app/views/view/data/realtime/realtime.jade | 2 +- app/views/view/data/students.jade | 2 +- app/views/view/data/teachers.jade | 2 +- app/views/view/gameactivity.jade | 2 +- app/views/view/home.jade | 2 +- 8 files changed, 26 insertions(+), 11 deletions(-) diff --git a/app/public/js/controllers/activity.js b/app/public/js/controllers/activity.js index c61dade..2227d54 100644 --- a/app/public/js/controllers/activity.js +++ b/app/public/js/controllers/activity.js @@ -34,12 +34,28 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) }; }) .controller('ActivityCtrl', ['$rootScope', '$scope', '$attrs', '$location', '$http', 'Activities', 'Classes', '_', - 'Results', 'Versions', '$sce', '$interval', 'CONSTANTS', - function ($rootScope, $scope, $attrs, $location, $http, Activities, Classes, _, Results, Versions, $sce, $interval, CONSTANTS) { + 'Results', 'Versions', '$sce', '$interval', 'Role', 'CONSTANTS', + function ($rootScope, $scope, $attrs, $location, $http, Activities, Classes, _, Results, Versions, $sce, $interval, Role, CONSTANTS) { var refresh; var onSetActivity = function() { $scope.refreshResults = function () { + if (Role.isUser()) { + /*$scope.activity.$myAttempts(function (myAttempts) { + $scope.myAttempts = myAttempts; + });*/ + if (!$scope.gameplaysShown) { + $scope.gameplaysShown = {}; + } + if (Role.isTeacher()) { + Activities.attempts({activityId: $scope.activity._id}, function (attempts) { + $scope.attempts = attempts; + }); + /*$scope.activity.$attempts(function (attempts) { + $scope.attemps = attempts; + });*/ + } + } var rawResults = Results.query({ id: $scope.activity._id }, @@ -80,9 +96,8 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) $scope.activity = JSON.parse($attrs.activity); Activities.get({activityId: $scope.activity._id}).$promise.then(function(a) { $scope.activity = a; + onSetActivity(); }); - - onSetActivity(); }); $scope.student = {}; diff --git a/app/views/view/classactivity.jade b/app/views/view/classactivity.jade index 018d6fa..72741a9 100644 --- a/app/views/view/classactivity.jade +++ b/app/views/view/classactivity.jade @@ -56,7 +56,7 @@ td a(ng-href='#', ng-click='goToGame(getGameById(activity.gameId))') {{ getGameById(activity.gameId).title }} td {{activity._id | prettyDateId }} - td(ng-if='isTeacher()', ng-controller='ActivityCtrl' activity='{{ activity }}') + td(ng-if='isTeacher()', ng-controller='ActivityCtrl' lite activity='{{ activity }}') button.btn-success(ng-if='activityState() == 2' ng-click='endActivity()') Opened button.btn-warning(ng-if='activityState() == 0' ng-click='startActivity()') Closed button.btn-info(ng-if='activityState() == 1') Loading diff --git a/app/views/view/data.jade b/app/views/view/data.jade index ddd8426..1375867 100644 --- a/app/views/view/data.jade +++ b/app/views/view/data.jade @@ -60,7 +60,7 @@ include data/dev-visualization #game-analytics.tab-pane include data/game-analytics - .panel.panel-default(ng-if='!isDeveloper() && selectedActivity' ng-controller='ActivityCtrl' lite='false' activity='{{ selectedActivity }}' style='width=100%') + .panel.panel-default(ng-if='!isDeveloper() && selectedActivity' ng-controller='ActivityCtrl' activity='{{ selectedActivity }}' style='width=100%') .panel-body(ng-if='!game || !version || !activity') img(src='loading.svg') .panel-heading(ng-if='game && version && activity' style='font-size: 2.5em') diff --git a/app/views/view/data/realtime/realtime.jade b/app/views/view/data/realtime/realtime.jade index 0170fb8..c1d898c 100644 --- a/app/views/view/data/realtime/realtime.jade +++ b/app/views/view/data/realtime/realtime.jade @@ -16,7 +16,7 @@ limitations under the License. link(rel="stylesheet", href="css/realtime.css") -.container-fluid.theme-showcase.height100(ng-if='selectedActivity' ng-controller="ActivityCtrl" activityid='{{selectedActivity._id}}' role="tabpanel" style="position:relative") +.container-fluid.theme-showcase.height100(role="tabpanel" style="position:relative") .row(ng-if="isTeacher()") h1(ng-if="results.length > 0") Alerts & Warnings .row(ng-show="player") diff --git a/app/views/view/data/students.jade b/app/views/view/data/students.jade index 68c74bb..152c2ad 100644 --- a/app/views/view/data/students.jade +++ b/app/views/view/data/students.jade @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. -.container-fluid.theme-showcase(ng-if='selectedActivity' ng-controller="ActivityCtrl" lite='true' ng-attr-activityid='{{selectedActivity._id}}') +.container-fluid.theme-showcase h1 Students .row .col-md-12 diff --git a/app/views/view/data/teachers.jade b/app/views/view/data/teachers.jade index 4a3dbcd..9c39037 100644 --- a/app/views/view/data/teachers.jade +++ b/app/views/view/data/teachers.jade @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. -.container-fluid.theme-showcase(ng-if='selectedActivity' ng-controller="ActivityCtrl" lite='true' ng-attr-activityid='{{selectedActivity._id}}') +.container-fluid.theme-showcase h1 Teachers .row .col-md-12 diff --git a/app/views/view/gameactivity.jade b/app/views/view/gameactivity.jade index 2a9d467..dc4cda1 100644 --- a/app/views/view/gameactivity.jade +++ b/app/views/view/gameactivity.jade @@ -48,7 +48,7 @@ tr(ng-if='activities.length == 0') td(colspan=6) div.alert.alert-info(style='margin-bottom: 0px') No activities found - tr(ng-repeat='activity in activities', ng-controller='ActivityCtrl' activity='{{ activity }}') + tr(ng-repeat='activity in activities', ng-controller='ActivityCtrl' lite activity='{{ activity }}') td span.glyphicon.glyphicon-stats.right10 a(ng-href="#" ng-click='goToActivity(activity)') {{activity.name}} diff --git a/app/views/view/home.jade b/app/views/view/home.jade index 89f6520..3073a0c 100644 --- a/app/views/view/home.jade +++ b/app/views/view/home.jade @@ -50,7 +50,7 @@ tr(ng-if='activities.length == 0') td(colspan=6) div.alert.alert-info(style='margin-bottom: 0px') No activities found - tr(ng-repeat='localactivity in activities', ng-controller='ActivityCtrl' activity='{{ localactivity }}') + tr(ng-repeat='localactivity in activities', ng-controller='ActivityCtrl' lite activity='{{ localactivity }}') td span.glyphicon.glyphicon-stats.right10 a(ng-href="#" ng-click='goToActivity(localactivity)') {{ localactivity.name }} From defefbb31c82e951536779437034de6efad6f071 Mon Sep 17 00:00:00 2001 From: Synpheros Date: Tue, 8 May 2018 13:31:58 +0200 Subject: [PATCH 10/17] Added LoginByPlugin view in order to manage External Login # Conflicts: # app/public/js/controllers/app.js # app/viewRoutes.js. --- app/public/js/controllers/activity-list.js | 7 ++- app/public/js/controllers/activity.js | 53 +++++++++++++++++----- app/views/view/data/attempts.jade | 36 ++++++++++----- app/views/view/data/information.jade | 3 +- app/views/view/data/menu.jade | 2 +- 5 files changed, 73 insertions(+), 28 deletions(-) diff --git a/app/public/js/controllers/activity-list.js b/app/public/js/controllers/activity-list.js index fd4c3c8..d79146c 100644 --- a/app/public/js/controllers/activity-list.js +++ b/app/public/js/controllers/activity-list.js @@ -92,7 +92,6 @@ angular.module('activitiesApp', ['ngStorage', 'services']) var classId = $scope.classId ? $scope.classId : $scope.activity.classId; var gameId = $scope.gameId ? $scope.gameId : $scope.activity.gameId; var versionId = $scope.versionId; - console.log(gameId); $scope.activityCreatedError = ''; if (!gameId) { @@ -182,9 +181,9 @@ angular.module('activitiesApp', ['ngStorage', 'services']) $scope.goToActivity(activity); $rootScope.$broadcast('refreshActivities'); }).error(function (data, status) { - console.error('Error on post /kibana/dashboard/activity/' + activity._id + ' ' + - JSON.stringify(data) + ', status: ' + status); - }); + console.error('Error on post /kibana/dashboard/activity/' + activity._id + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); } }).error(function (data, status) { console.error('Error on post /kibana/visualization/activity/' + visualizationId + '/' + activity._id + ' ' + diff --git a/app/public/js/controllers/activity.js b/app/public/js/controllers/activity.js index 2227d54..6501670 100644 --- a/app/public/js/controllers/activity.js +++ b/app/public/js/controllers/activity.js @@ -41,9 +41,6 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) var onSetActivity = function() { $scope.refreshResults = function () { if (Role.isUser()) { - /*$scope.activity.$myAttempts(function (myAttempts) { - $scope.myAttempts = myAttempts; - });*/ if (!$scope.gameplaysShown) { $scope.gameplaysShown = {}; } @@ -51,9 +48,6 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) Activities.attempts({activityId: $scope.activity._id}, function (attempts) { $scope.attempts = attempts; }); - /*$scope.activity.$attempts(function (attempts) { - $scope.attemps = attempts; - });*/ } } var rawResults = Results.query({ @@ -113,20 +107,35 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) }; - var dashboardLink = function (userName) { + var dashboardLink = function (userName, attempt) { var url = CONSTANTS.KIBANA + '/app/kibana#/dashboard/dashboard_' + $scope.activity._id + '?embed=true_g=(refreshInterval:(display:\'5%20seconds\',' + 'pause:!f,section:1,value:5000),time:(from:now-1h,mode:quick,to:now))'; if (url.startsWith('localhost')) { url = 'http://' + url; } + var filter = {}; if (userName) { - url += '&_a=(filters:!(),options:(darkTheme:!f),query:(query_string:(analyze_wildcard:!t,query:\'out.name:' + - userName + '\')))'; + filter.name = userName; } else if ($scope.player) { - url += '&_a=(filters:!(),options:(darkTheme:!f),query:(query_string:(analyze_wildcard:!t,query:\'out.name:' + - $scope.player.name + '\')))'; + filter.name = $scope.player.name; + } + + if (attempt) { + filter.session = attempt; + } else if ($scope.attempt) { + filter.session = $scope.attempt.number; + } + + if (filter.length > 0) { + var props = []; + for (var key in filter) { + if (filter.hasOwnProperty(key)) { + props.push(key + ': ' + filter[key]); + } + } + url += '&_a=(filters:!(),options:(darkTheme:!f),query:(query_string:(analyze_wildcard:!t,query:\'' + props.join(',') + '\')))'; } if (url.startsWith('localhost')) { @@ -174,11 +183,33 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) $scope.viewAll = function () { $scope.player = null; + $scope.attempt = null; $scope.iframeDashboardUrl = dashboardLink(); }; $scope.viewPlayer = function (result) { $scope.player = result; + $scope.attempt = null; + $scope.iframeDashboardUrl = dashboardLink(); + }; + + $scope.viewAttempt = function (gameplay, attempt) { + if ($scope.results) { + var lookForName = gameplay.playerType === 'anonymous' ? gameplay.animalName : gameplay.playerName; + for (var i = 0; i < $scope.results.length; i++) { + if ($scope.results[i].name === lookForName) { + $scope.player = $scope.results[i]; + break; + } + } + } + // This code manually changes the tab, this might be solved with tab('show') in newer versions + // as mentioned in https://github.com/twbs/bootstrap/issues/23594 + $('.active[data-toggle=\'tab\']').toggleClass('active').toggleClass('show'); + $('span[href=\'#realtime\'][data-toggle=\'tab\']').toggleClass('active').toggleClass('show'); + $('.tab-pane.active').toggleClass('active').toggleClass('show'); + $('#realtime').toggleClass('active').toggleClass('show'); + $scope.attempt = attempt; $scope.iframeDashboardUrl = dashboardLink(); }; diff --git a/app/views/view/data/attempts.jade b/app/views/view/data/attempts.jade index 450f0fc..617b486 100644 --- a/app/views/view/data/attempts.jade +++ b/app/views/view/data/attempts.jade @@ -29,24 +29,38 @@ th Type th Username th Animal Name - th See attempts + th Created + th Last start + th Last end + th Last + th Attempts tbody tr(ng-if='!attempts || attempts.length === 0') td(colspan=4) div.alert.alert-warning(style='margin-bottom: 0px') No attempts found tbody(ng-repeat='gameplay in attempts') tr - td + td.text-center span.glyphicon(ng-class='(gameplay.playerType === "identified") ? "glyphicon-user" : "glyphicon-sunglasses"') td - label {{gameplay.playerName}} + label(tooltip="gameplay.playerName" tooltip-placement="left") {{gameplay.playerType === 'anonymous' ? 'Anonymous' : gameplay.playerName }} td label {{gameplay.animalName}} td - a.btn.btn-primary(type='button', ng-click='gameplayShown[gameplay._id] = !gameplayShown[gameplay._id]') - .glyphicon(ng-class='gameplayShown[gameplay._id] ? "glyphicon-remove-sign" : "glyphicon-plus"') - tr.attemptRow(ng-show='gameplayShown[gameplay._id]') - td(colspan=4) + label {{ gameplay.firstSessionStarted | prettyDate }} + td + label {{ gameplay.attempts[gameplay.sessions-1].start | prettyDate }} + td + label {{ gameplay.attempts[gameplay.sessions-1].end | prettyDate }} + td + a.btn.btn-primary(type='button' ng-click='viewAttempt(gameplay, gameplay.attempts[gameplay.sessions-1])') + .glyphicon.glyphicon-stats + td + a.btn.btn-primary(type='button', ng-click='gameplaysShown[gameplay._id] = !gameplaysShown[gameplay._id]') + label {{gameplay.sessions}}   + .glyphicon(ng-class='gameplaysShown[gameplay._id] ? "glyphicon-remove-sign" : "glyphicon-plus"') + tr.attemptRow(ng-show='gameplaysShown[gameplay._id]') + td(colspan=8) .panel.panel-primary .panel-heading {{ gameplay.playerName }} attempts .panel-body @@ -64,11 +78,11 @@ tbody(ng-repeat='attempt in gameplay.attempts') tr td - label {{attempt.number}} + label {{ attempt.number }} td - label {{attempt.start}} + label {{ attempt.start | prettyDate }} td - label {{attempt.end}} + label {{ attempt.end | prettyDate }} td - a.btn.btn-primary(type='button', ng-click='goToAttemptGraph(attempt)') + a.btn.btn-primary(type='button' ng-click='viewAttempt(gameplay, attempt)') .glyphicon.glyphicon-stats \ No newline at end of file diff --git a/app/views/view/data/information.jade b/app/views/view/data/information.jade index 74e1012..ff891e7 100644 --- a/app/views/view/data/information.jade +++ b/app/views/view/data/information.jade @@ -23,7 +23,8 @@ a(href='{{ game.link }}') {{ game.link }} .col-xs-12 | Tracking code: - kbd.left10 {{ version.trackingCode }} + kbd.left10(ng-if='isDeveloper()') {{ version.trackingCode }} + kbd.left10(ng-if='isTeacher()') {{ activity.trackingCode }} .row(ng-if='isDeveloper()') hr .col-xs-12 diff --git a/app/views/view/data/menu.jade b/app/views/view/data/menu.jade index cea58c7..d53f98f 100644 --- a/app/views/view/data/menu.jade +++ b/app/views/view/data/menu.jade @@ -30,7 +30,7 @@ ul.nav | Students li span.left-menu-item(href="#attempts", role="tab", data-toggle="tab", ng-click="view = 'attempts'") - span.glyphicon.glyphicon-apple.right20 + span.glyphicon.glyphicon-tasks.right20 | Attempts li(ng-if='isTeacher() || isStudent()') span.left-menu-item(href="#realtime", role="tab", data-toggle="tab", ng-click="view = 'realtime'") From 96c9539c6ab4183b604c37757deba02d0caf19df Mon Sep 17 00:00:00 2001 From: Victorma Perez Colado Date: Wed, 14 Feb 2018 18:35:20 +0100 Subject: [PATCH 11/17] Added services extra fields removal and activity events --- app/public/js/controllers/activity.js | 39 ++++------ app/public/js/services.js | 104 ++++++++++++++++++++++++-- app/views/view/data/attempts.jade | 4 +- 3 files changed, 114 insertions(+), 33 deletions(-) diff --git a/app/public/js/controllers/activity.js b/app/public/js/controllers/activity.js index 6501670..7ad3307 100644 --- a/app/public/js/controllers/activity.js +++ b/app/public/js/controllers/activity.js @@ -361,32 +361,29 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) }; $scope.$on('refreshActivity', function(evt, activity) { - $scope.activity = activity; - console.log('Activity updated'); + if ($scope.activity._id === activity._id) { + $scope.activity = activity; + console.log('Activity updated'); + } }); + var finishEvent = function(activity) { + $scope.activity = activity; + $rootScope.$broadcast('refreshActivity', $scope.activity); + }; + $scope.startActivity = function () { if (!$scope.activity || $scope.activity.loading) { return; } - $scope.activity.loading = true; - $http.post(CONSTANTS.PROXY + '/activities/' + $scope.activity._id + '/event/start').success(function (s) { - $scope.activity.loading = false; - $scope.activity.start = s.start; - $scope.activity.end = s.end; - $rootScope.$broadcast('refreshActivity', $scope.activity); - }).error(function (data, status) { - console.error('Error on get /activities/' + $scope.activity._id + '/event/start ' + - JSON.stringify(data) + ', status: ' + status); - + $scope.activity.$event({event: 'start'}).$promise.then(finishEvent).fail(function (error) { + console.error(error); $.notify('Error while opening the activity:
If the session was recently closed it ' + 'might need to be cleaned by the system.
Please try again in a few seconds.', { offset: { x: 10, y: 65 }, type: 'danger'// jscs:ignore requireCamelCaseOrUpperCaseIdentifiers }); - - $scope.activity.loading = false; $rootScope.$broadcast('refreshActivity', $scope.activity); }); }; @@ -396,22 +393,12 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) return; } - $scope.activity.loading = true; - $http.post(CONSTANTS.PROXY + '/activities/' + $scope.activity._id + '/event/end').success(function (s) { - $scope.activity.loading = false; - $scope.activity.start = s.start; - $scope.activity.end = s.end; - $rootScope.$broadcast('refreshActivity', $scope.activity); - }).error(function (data, status) { - console.error('Error on get /activities/' + $scope.activity._id + '/event/end ' + - JSON.stringify(data) + ', status: ' + status); - + $scope.activity.$event({event: 'end'}).$promise.then(finishEvent).fail(function (error) { + console.error(error); $.notify('Error while closing the activity:
Please try again in a few seconds.', { offset: { x: 10, y: 65 }, type: 'danger'// jscs:ignore requireCamelCaseOrUpperCaseIdentifiers }); - - $scope.activity.loading = false; $rootScope.$broadcast('refreshActivity', $scope.activity); }); }; diff --git a/app/public/js/services.js b/app/public/js/services.js index 3a769a1..cfdfc0a 100644 --- a/app/public/js/services.js +++ b/app/public/js/services.js @@ -25,7 +25,18 @@ services.factory('Games', ['$resource', 'CONSTANTS', return $resource(CONSTANTS.PROXY + '/games/:gameId', { gameId: '@_id' }, { my: { method: 'GET', isArray: true , url: CONSTANTS.PROXY + '/games/my' }, public: { method: 'GET', isArray: true , url: CONSTANTS.PROXY + '/games/public' }, - update: { method: 'PUT' } + update: { + method: 'PUT', + transformRequest: function (data, headersGetter) { + if (data._id !== undefined) { + delete data._id; + } + if (data.created !== undefined) { + delete data.created; + } + return angular.toJson(data); + } + } }); } ]); @@ -45,7 +56,21 @@ services.factory('Versions', ['$resource', 'CONSTANTS', gameId: '@gameId' }, { forGame: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/games/:gameId/versions' }, - update: { method: 'POST' } // TODO Update this to PUT or update all the others to POST + update: { + method: 'POST', // TODO Update this to PUT or update all the others to POST + transformRequest: function (data, headersGetter) { + if (data._id !== undefined) { + delete data._id; + } + if (data.created !== undefined) { + delete data.created; + } + if (data.trackingCode !== undefined) { + delete data.trackingCode; + } + return angular.toJson(data); + } + } }); } ]); @@ -56,7 +81,18 @@ services.factory('Classes', ['$resource', 'CONSTANTS', classId: '@_id' }, { my: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/classes/my' }, - update: { method: 'PUT' } + update: { + method: 'PUT', + transformRequest: function (data, headersGetter) { + if (data._id !== undefined) { + delete data._id; + } + if (data.created !== undefined) { + delete data.created; + } + return angular.toJson(data); + } + } }); } ]); @@ -64,7 +100,16 @@ services.factory('Classes', ['$resource', 'CONSTANTS', services.factory('Courses', ['$resource', 'CONSTANTS', function ($resource, CONSTANTS) { return $resource(CONSTANTS.PROXY + '/courses', {}, { - all: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/courses'} + all: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/courses'}, + update: { + method: 'PUT', + transformRequest: function (data, headersGetter) { + if (data._id !== undefined) { + delete data._id; + } + return angular.toJson(data); + } + } }); } ]); @@ -82,7 +127,56 @@ services.factory('Activities', ['$resource', 'CONSTANTS', my: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/activities/my' }, forClass: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/classes/:classId/activities/my' }, forGame: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/games/:gameId/versions/:versionId/activities/my' }, - update: { method: 'PUT' }, + update: { + method: 'PUT', + transformRequest: function (data, headersGetter) { + if (data._id !== undefined) { + delete data._id; + } + if (data.classId !== undefined) { + delete data.classId; + } + if (data.gameId !== undefined) { + delete data.gameId; + } + if (data.versionId !== undefined) { + delete data.versionId; + } + if (data.start !== undefined) { + delete data.versionId; + } + if (data.end !== undefined) { + delete data.versionId; + } + if (data.open !== undefined) { + delete data.open; + } + if (data.created !== undefined) { + delete data.created; + } + if (data.trackingCode !== undefined) { + delete data.trackingCode; + } + return angular.toJson(data); + } + }, + event: { + method: 'POST', + url: CONSTANTS.PROXY + '/activities/:activityId/event/:event', + transformRequest: function (data, headersGetter) { + loadingStatus[data._id] = true; + return angular.toJson({_id: data._id, event: data.event}); + }, + transformResponse: function (data, headersGetter) { + var object = angular.fromJson(data); + console.info(data); + console.info(object); + if (object._id !== undefined) { + loadingStatus[object._id] = false; + } + return object; + } + }, attempts: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/activities/:activityId/attempts'}, myAttempts: { method: 'GET', url: CONSTANTS.PROXY + '/activities/:activityId/attempts/my'}, userAttempts: { method: 'GET', url: CONSTANTS.PROXY + '/activities/:activityId/attempts/:username'} diff --git a/app/views/view/data/attempts.jade b/app/views/view/data/attempts.jade index 617b486..394facf 100644 --- a/app/views/view/data/attempts.jade +++ b/app/views/view/data/attempts.jade @@ -36,8 +36,8 @@ th Attempts tbody tr(ng-if='!attempts || attempts.length === 0') - td(colspan=4) - div.alert.alert-warning(style='margin-bottom: 0px') No attempts found + td(colspan=8) + div.alert.alert-info(style='margin-bottom: 0px') No attempts found tbody(ng-repeat='gameplay in attempts') tr td.text-center From 38636bb24ecd5a9ec635a56e87ac568836d28a40 Mon Sep 17 00:00:00 2001 From: Synpheros Date: Wed, 14 Mar 2018 17:34:48 +0100 Subject: [PATCH 12/17] Ignoring some parameters when Activity PUT --- app/public/js/services.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/public/js/services.js b/app/public/js/services.js index cfdfc0a..8b9e122 100644 --- a/app/public/js/services.js +++ b/app/public/js/services.js @@ -143,10 +143,10 @@ services.factory('Activities', ['$resource', 'CONSTANTS', delete data.versionId; } if (data.start !== undefined) { - delete data.versionId; + delete data.start; } if (data.end !== undefined) { - delete data.versionId; + delete data.end; } if (data.open !== undefined) { delete data.open; @@ -154,6 +154,9 @@ services.factory('Activities', ['$resource', 'CONSTANTS', if (data.created !== undefined) { delete data.created; } + if (data.rootId !== undefined) { + delete data.rootId; + } if (data.trackingCode !== undefined) { delete data.trackingCode; } From ff42766130ba88778de7e2ae5ac80268c9d695d5 Mon Sep 17 00:00:00 2001 From: Synpheros Date: Wed, 14 Mar 2018 17:38:09 +0100 Subject: [PATCH 13/17] Added LoginByPlugin view in order to manage External Login --- app/public/js/controllers/app.js | 2 +- app/viewRoutes.js | 5 +++++ app/views/layout.jade | 1 + app/views/view/loginplugin.jade | 7 ++++++- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/public/js/controllers/app.js b/app/public/js/controllers/app.js index 2ee7c9b..9908c87 100644 --- a/app/public/js/controllers/app.js +++ b/app/public/js/controllers/app.js @@ -283,7 +283,7 @@ angular.module('myApp', [ $scope.selectedGame = Games.get({gameId: activity.gameId}); }); } - } else { + } else if(!$window.location.pathname.endsWith('loginbyplugin')){ $location.url('login'); } } diff --git a/app/viewRoutes.js b/app/viewRoutes.js index 38c71e9..1ecf6fb 100644 --- a/app/viewRoutes.js +++ b/app/viewRoutes.js @@ -30,6 +30,11 @@ var getBasePath = function(req) { return proto + '://' + req.headers['x-forwarded-host']; }; +router.get('/loginbyplugin', function (req, res) { + console.log("loginbyplugin"); + res.render('view/loginplugin', {user: JSON.stringify(req.query), basePath: getBasePath(req)}); +}); + router.get('/view/:page', function (req, res) { res.render('view/' + req.param('page'), {basePath: getBasePath(req)}); }); diff --git a/app/views/layout.jade b/app/views/layout.jade index d2872ea..c8ccf71 100644 --- a/app/views/layout.jade +++ b/app/views/layout.jade @@ -81,4 +81,5 @@ html script(src='js/controllers/app.js') script(src='js/env-vars.js') block toolbar + block loginplugin ui-view \ No newline at end of file diff --git a/app/views/view/loginplugin.jade b/app/views/view/loginplugin.jade index bf6ae6d..0aa8047 100644 --- a/app/views/view/loginplugin.jade +++ b/app/views/view/loginplugin.jade @@ -11,4 +11,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -.row(ng-controller='LoginPluginCtrl' ng-init="setupUser(#{user})") + +extends ../page + +block loginplugin + p login by plugin + #login(ng-controller='LoginPluginCtrl' ng-init="setupUser(#{user})") \ No newline at end of file From 95aa7ad351bc961b411d2c4c876e13251532b5c1 Mon Sep 17 00:00:00 2001 From: gorco Date: Fri, 16 Mar 2018 12:46:40 +0100 Subject: [PATCH 14/17] Improved view to configure participants in a class by a modal windows --- app/public/js/controllers/class-list.js | 2 +- app/public/js/controllers/class.js | 92 ++++++++++++++++++- app/public/js/controllers/participantsConf.js | 85 ++++++++++++++++- app/public/js/services.js | 16 ++++ app/views/view/classactivity.jade | 74 ++++++++++++--- 5 files changed, 248 insertions(+), 21 deletions(-) diff --git a/app/public/js/controllers/class-list.js b/app/public/js/controllers/class-list.js index 30d4bcd..a8881ef 100644 --- a/app/public/js/controllers/class-list.js +++ b/app/public/js/controllers/class-list.js @@ -123,7 +123,7 @@ angular.module('classesApp', ['ngStorage', 'services']) } else { var reqObj; if (!courseId) { - courseId = ''; + courseId = null; } reqObj = {courseId: courseId}; $http.put(CONSTANTS.PROXY + '/classes/' + classObj._id, reqObj) diff --git a/app/public/js/controllers/class.js b/app/public/js/controllers/class.js index 2524e03..57671d7 100644 --- a/app/public/js/controllers/class.js +++ b/app/public/js/controllers/class.js @@ -19,12 +19,19 @@ 'use strict'; angular.module('classApp', ['ngStorage', 'services', 'ngAnimate', 'ngSanitize', 'ui.bootstrap']) - .controller('ClassCtrl', ['$rootScope', '$scope', '$attrs', '$location', '$http', '$uibModal', 'Classes', 'CONSTANTS', - function ($rootScope, $scope, $attrs, $location, $http, $uibModal, Classes, CONSTANTS) { + .controller('ClassCtrl', ['$rootScope', '$scope', '$attrs', '$location', '$http', '$uibModal', 'Classes', 'Groups', 'Groupings', 'CONSTANTS', + function ($rootScope, $scope, $attrs, $location, $http, $uibModal, Classes, Groups, Groupings, CONSTANTS) { + var groupsReady = false; + var groupingsReady = false; + var classReady = false; var onSetClass = function() { if (!$scope.class) { throw new Error('No class for ClassCtrl'); } else { + classReady = true; + if (groupsReady && groupingsReady && classReady) { + onReadyParticipants(); + } $http.get(CONSTANTS.PROXY + '/lti/keyid/' + $scope.class._id).success(function (data) { if (data && data.length > 0) { $scope.lti.key = data[0]._id; @@ -34,8 +41,76 @@ angular.module('classApp', ['ngStorage', 'services', 'ngAnimate', 'ngSanitize', } }; - $attrs.$observe('classid', function() { + var onReadyGroups = function() { + groupsReady = true; + if (groupsReady && groupingsReady && classReady) { + onReadyParticipants(); + } + }; + + var onReadyGroupings = function() { + groupingsReady = true; + if (groupsReady && groupingsReady && classReady) { + onReadyParticipants(); + } + }; + + var onReadyParticipants = function() { + $scope.participants = {teachers: [], assistants: [], students: []}; + if ($scope.isUsingGroupings()) { + $scope.class.groupings.forEach(function(groupingId) { + addParticipantsFromGroupingId(groupingId); + }); + } else if ($scope.isUsingGroups()) { + $scope.class.groups.forEach(function(groupId) { + addParticipantsFromGroupId(groupId); + }); + } else { + $scope.participants = $scope.class.participants; + } + }; + + var getClassInfo = function() { + groupsReady = false; + groupingsReady = false; + classReady = false; $scope.class = Classes.get({classId: $attrs.classid}, onSetClass); + $scope.groups = Groups.get({classId: $attrs.classid}, onReadyGroups); + $scope.groupings = Groupings.get({classId: $attrs.classid}, onReadyGroupings); + }; + + var addParticipantsFromGroupingId = function(groupingId) { + for (var i = 0; i < $scope.groupings.length; i++) { + if (groupingId === $scope.groupings[i]._id) { + for (var j = 0; j < $scope.groupings[i].groups.length; j++) { + addParticipantsFromGroupId($scope.groupings[i].groups[j]); + } + break; + } + } + }; + + var addParticipantsFromGroupId = function(groupId) { + for (var i = 0; i < $scope.groups.length; i++) { + if (groupId === $scope.groups[i]._id) { + pushUsrFromGroupToParticipants($scope.groups[i], 'teachers'); + pushUsrFromGroupToParticipants($scope.groups[i], 'assistants'); + pushUsrFromGroupToParticipants($scope.groups[i], 'students'); + return; + } + } + }; + + var pushUsrFromGroupToParticipants = function(group, role) { + group.participants[role].forEach(function (usr) { + if ($scope.participants[role].indexOf(usr) === -1) { + $scope.participants[role].push(usr); + } + }); + }; + + $attrs.$observe('classid', function() { + getClassInfo(); }); $attrs.$observe('forclass', function() { @@ -70,9 +145,8 @@ angular.module('classApp', ['ngStorage', 'services', 'ngAnimate', 'ngSanitize', }); modalInstance.result.then(function () { - console.log('HOLA'); }, function () { - console.log('Modal dismissed at: ' + new Date()); + getClassInfo(); }); }; @@ -84,6 +158,14 @@ angular.module('classApp', ['ngStorage', 'services', 'ngAnimate', 'ngSanitize', }); }; + $scope.isUsingGroupings = function () { + return $scope.class.groupings && $scope.class.groupings.length > 0; + }; + + $scope.isUsingGroups = function () { + return !$scope.isUsingGroupings() && $scope.class.groups && $scope.class.groups.length > 0; + }; + // LTI $scope.lti = {}; $scope.lti.key = ''; diff --git a/app/public/js/controllers/participantsConf.js b/app/public/js/controllers/participantsConf.js index 214c054..8d17a02 100644 --- a/app/public/js/controllers/participantsConf.js +++ b/app/public/js/controllers/participantsConf.js @@ -36,9 +36,17 @@ angular.module('participantsApp', []) $ctrl.selectedGroup = undefined; $ctrl.selectedGrouping = undefined; + $ctrl.unlockedGroups = false; + $ctrl.unlockedGroupings = false; + $ctrl.$onInit = function () { Classes.get({classId: $ctrl.classId}).$promise.then(function (data) { $ctrl.class = data; + if (data.groupings && data.groupings.length > 0) { + $ctrl.unlockGroupings(); + } else if (data.groups && data.groups.length > 0) { + $ctrl.unlockGroups(); + } }); updateGroups(); updateGroupings(); @@ -64,6 +72,80 @@ angular.module('participantsApp', []) }); }; + $ctrl.unlockGroups = function() { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id + '/remove'; + if ($ctrl.unlockedGroupings) { + $http.put(route, {groupings: $ctrl.class.groupings}).success(function (data) { + $ctrl.class = data; + $ctrl.unlockedGroupings = false; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + if ($ctrl.unlockedGroups) { + $http.put(route, {groups: $ctrl.class.groups}).success(function (data) { + $ctrl.class = data; + $ctrl.unlockedGroups = false; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } else { + $ctrl.unlockedGroups = true; + } + }; + + $ctrl.unlockGroupings = function() { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id + '/remove'; + if ($ctrl.unlockedGroups) { + $http.put(route, {groups: $ctrl.class.groups}).success(function (data) { + $ctrl.class = data; + $ctrl.unlockedGroups = false; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + if ($ctrl.unlockedGroupings) { + $http.put(route, {groupings: $ctrl.class.groupings}).success(function (data) { + $ctrl.class = data; + $ctrl.unlockedGroupings = false; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } else { + $ctrl.unlockedGroupings = true; + } + }; + + $ctrl.checkGroup = function (group) { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id; + if ($ctrl.class.groups && $ctrl.class.groups.indexOf(group._id) !== -1) { + route += '/remove'; + } + $http.put(route, {groups: [group._id]}).success(function (data) { + $ctrl.class = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + }; + + $ctrl.checkGrouping = function (grouping) { + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id; + if ($ctrl.class.groupings && $ctrl.class.groupings.indexOf(grouping._id) !== -1) { + route += '/remove'; + } + $http.put(route, {groupings: [grouping._id]}).success(function (data) { + $ctrl.class = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + }; + $ctrl.selectGroup = function (group) { if ($ctrl.selectedGroup && $ctrl.selectedGroup._id === group._id) { $ctrl.selectedGroup = undefined; @@ -210,7 +292,8 @@ angular.module('participantsApp', []) students.push(student); } }); - var route = CONSTANTS.PROXY + '/classes/' + $ctrl.selectedClass._id; + + var route = CONSTANTS.PROXY + '/classes/' + $ctrl.class._id; $http.put(route, {participants: {students: students}}).success(function (data) { $ctrl.class = data; }).error(function (data, status) { diff --git a/app/public/js/services.js b/app/public/js/services.js index 8b9e122..9333bcb 100644 --- a/app/public/js/services.js +++ b/app/public/js/services.js @@ -198,6 +198,22 @@ services.factory('Activities', ['$resource', 'CONSTANTS', } ]); +services.factory('Groups', ['$resource', 'CONSTANTS', + function ($resource, CONSTANTS) { + return $resource(CONSTANTS.PROXY + '/classes/:classId/groups', {classId: '@_id'}, { + get: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/classes/:classId/groups'} + }); + } +]); + +services.factory('Groupings', ['$resource', 'CONSTANTS', + function ($resource, CONSTANTS) { + return $resource(CONSTANTS.PROXY + '/classes/:classId/groupings', {classId: '@_id'}, { + get: { method: 'GET', isArray: true, url: CONSTANTS.PROXY + '/classes/:classId/groupings'} + }); + } +]); + services.factory('Results', ['$resource', 'CONSTANTS', function ($resource, CONSTANTS) { return $resource(CONSTANTS.PROXY + '/activities/:id/results/:resultId', { diff --git a/app/views/view/classactivity.jade b/app/views/view/classactivity.jade index 72741a9..a4b5942 100644 --- a/app/views/view/classactivity.jade +++ b/app/views/view/classactivity.jade @@ -1,20 +1,16 @@ //- Copyright 2016 e-UCM (http://www.e-ucm.es/) - Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreement No 644187. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 (link is external) - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - .container.theme-showcase(ng-if='selectedClass' ng-controller='ClassCtrl' ng-attr-classid='{{ selectedClass._id }}') div(ng-controller='ActivityListCtrl', ng-attr-classid='{{ class._id }}' ng-class='(isTeacher() ? "col-md-8" : "col-md-12")') .panel.panel-default @@ -105,6 +101,34 @@ button.btn.btn-primary(ng-click="open('lg')") Configure span.right20 span.glyphicon.glyphicon-user + .panel.panel-default + .panel-heading + h3 + label List of current participants + label.label-success(ng-if='isUsingGroups()') Using a subclass (Groups selected) + label.label-warning(ng-if='isUsingGroupings()') Using a subclass (Groupings selected) + .panel-body + table.table.table-hover + thead + tr + th User + th Role + tbody + tr(ng-repeat='teacher in participants.teachers') + td + label {{teacher}} + td + label teacher + tr(ng-repeat='assistant in participants.assistants') + td + label {{assistant}} + td + label assistant + tr(ng-repeat='student in participants.students') + td + label {{student}} + td + label student // PARTICIPANTS MODAL script#participantsModal(type="text/ng-template") @@ -112,6 +136,12 @@ .modal-title .col-md-6 h1 Configure Participants + .col-md-12(ng-if='!$scope.unlockedGroups && !$scope.unlockedGroupings') + label.alert-info *If you want restrict the default access to the class with groups o groupings, please click on a star icon and check the groups or groupings + .col-md-12(ng-if='$scope.unlockedGroups') + label.alert-success You are using groups (if you want use all participants, please, click star icon in groups area) + .col-md-12(ng-if='$scope.unlockedGroupings') + label.alert-warning You are using groupings (if you want use all participants, please, click star icon in groupings area) div.modal-body.row .col-md-6 .panel.panel-default @@ -229,8 +259,13 @@ .col-md-6 .panel.panel-default .panel-heading - h3 - label Class Groups + .row + .col-md-9 + h3 + label Class Groups + .col-md-3 + a.btn.btn-default(type='button', ng-click='$scope.unlockGroups()' ng-class='$scope.unlockedGroups ? "btn-success" : "btn-default"') + span.glyphicon(ng-class='$scope.unlockedGroups ? "glyphicon-star" : "glyphicon-star-empty"') .panel-body div(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping') p The groups help you to configure your users activities easely @@ -245,6 +280,7 @@ thead tr th Group + th(ng-if='$scope.unlockedGroups') Use this groups in the class th Remove tbody tr(ng-if='$scope.classGroups.length == 0') @@ -253,19 +289,27 @@ tr(ng-repeat='group in $scope.classGroups') td(ng-class='$scope.getGroupThClass(group)') a.btn(ng-click='$scope.selectGroup(group)') {{group.name}} + td(ng-if='$scope.unlockedGroups') + a.btn(ng-click='$scope.checkGroup(group)') + span.glyphicon(ng-class='$scope.class.groups.indexOf(group._id)===-1 ? "glyphicon-unchecked" : "glyphicon-saved"') td(ng-show='$scope.selectedGrouping && !$scope.isInSelectedGrouping(group._id, "group")' ng-class='$scope.getGroupThClass(group)') - a(ng-click='$scope.modifyGrouping(group, true)') + a.btn(ng-click='$scope.modifyGrouping(group, true)') span.glyphicon.glyphicon-plus td(ng-show='$scope.selectedGrouping && $scope.isInSelectedGrouping(group._id, "group")' ng-class='$scope.getGroupThClass(group)') - a(ng-click='$scope.modifyGrouping(group, false)') + a.btn(ng-click='$scope.modifyGrouping(group, false)') span.glyphicon.glyphicon-minus td(ng-show='!$scope.selectedGrouping' ng-class='$scope.getGroupThClass(group)') - a(ng-click='$scope.removeGroup(group)') + a.btn(ng-click='$scope.removeGroup(group)') span.glyphicon.glyphicon-remove-sign .panel.panel-default .panel-heading - h3 - label Class Groupings + .row + .col-md-9 + h3 + label Class Groupings + .col-md-3 + a.btn(type='button', ng-click='$scope.unlockGroupings()' ng-class='$scope.unlockedGroupings ? "btn-warning" : "btn-default"') + span.glyphicon(ng-class='$scope.unlockedGroupings ? "glyphicon-star" : "glyphicon-star-empty"') .panel-body div(ng-show='!$scope.selectedGroup && !$scope.selectedGrouping') p The groupings help you to configure your users activities grouping groups of participants @@ -280,6 +324,7 @@ thead tr th Grouping + th(ng-if='$scope.unlockedGroupings') Use this groupings in the class th Remove tbody tr(ng-if='$scope.classGroupings.length == 0') @@ -288,8 +333,9 @@ tr(ng-class='$scope.selectedGrouping._id === grouping._id ? "bg-warning" : ""' ng-repeat='grouping in $scope.classGroupings') td a.btn(ng-click='$scope.selectGrouping(grouping)') {{grouping.name}} + td(ng-if='$scope.unlockedGroupings') + a.btn(ng-click='$scope.checkGrouping(grouping)') + span.glyphicon(ng-class='$scope.class.groupings.indexOf(grouping._id)===-1 ? "glyphicon-unchecked" : "glyphicon-saved"') td a(ng-click='$scope.removeGrouping(grouping)') - span.glyphicon.glyphicon-remove-sign - - +span.glyphicon.glyphicon-remove-sign \ No newline at end of file From 20eb398e2a632768545dd79cf7db17f429356e62 Mon Sep 17 00:00:00 2001 From: gorco Date: Fri, 16 Mar 2018 12:47:10 +0100 Subject: [PATCH 15/17] Changed students and teacher view by participants view in activities --- app/public/js/controllers/activity.js | 317 +++++++++++++++++--------- app/views/view/data.jade | 6 +- app/views/view/data/menu.jade | 8 +- app/views/view/data/participants.jade | 182 +++++++++++++++ app/views/view/data/students.jade | 64 ------ app/views/view/data/teachers.jade | 45 ---- 6 files changed, 389 insertions(+), 233 deletions(-) create mode 100644 app/views/view/data/participants.jade delete mode 100644 app/views/view/data/students.jade delete mode 100644 app/views/view/data/teachers.jade diff --git a/app/public/js/controllers/activity.js b/app/public/js/controllers/activity.js index 7ad3307..3238f54 100644 --- a/app/public/js/controllers/activity.js +++ b/app/public/js/controllers/activity.js @@ -34,46 +34,81 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) }; }) .controller('ActivityCtrl', ['$rootScope', '$scope', '$attrs', '$location', '$http', 'Activities', 'Classes', '_', - 'Results', 'Versions', '$sce', '$interval', 'Role', 'CONSTANTS', - function ($rootScope, $scope, $attrs, $location, $http, Activities, Classes, _, Results, Versions, $sce, $interval, Role, CONSTANTS) { + 'Results', 'Versions', 'Groups', 'Groupings', '$sce', '$interval', 'Role', 'CONSTANTS', + function ($rootScope, $scope, $attrs, $location, $http, Activities, Classes, _, Results, Versions, Groups, Groupings, $sce, $interval, Role, CONSTANTS) { var refresh; + var groupsReady = false; + var groupingsReady = false; + var classReady = false; + $scope.class = {}; var onSetActivity = function() { - $scope.refreshResults = function () { - if (Role.isUser()) { - if (!$scope.gameplaysShown) { - $scope.gameplaysShown = {}; + Classes.get({classId: $scope.activity.classId}).$promise.then(function(c) { + classReady = true; + $scope.class = c; + if ($scope.activity.groupings && $scope.activity.groupings.length > 0) { + $scope.unlockGroupings(); + } else if ($scope.activity.groups && $scope.activity.groups.length > 0) { + $scope.unlockGroups(); + } + updateGroups(); + updateGroupings(); + $scope.refreshResults = function () { + if (Role.isUser()) { + if (!$scope.gameplaysShown) { + $scope.gameplaysShown = {}; + } + if (Role.isTeacher()) { + Activities.attempts({activityId: $scope.activity._id}, function (attempts) { + $scope.attempts = attempts; + }); + } } - if (Role.isTeacher()) { - Activities.attempts({activityId: $scope.activity._id}, function (attempts) { - $scope.attempts = attempts; + var rawResults = Results.query({ + id: $scope.activity._id + }, + function () { + calculateResults(rawResults); }); - } - } - var rawResults = Results.query({ - id: $scope.activity._id - }, - function () { - calculateResults(rawResults); + }; + + if (!$attrs.lite) { + $scope.iframeDashboardUrl = dashboardLink(); + $scope.studentIframe = dashboardLink($scope.$storage.user.username); + + $scope.version = Versions.get({ + gameId: $scope.activity.gameId, + versionId: $scope.activity.versionId + }, function () { + $scope.refreshResults(); + if (!$scope.activity.end) { + refresh = $interval(function () { + $scope.refreshResults(); + }, 10000); + } }); - }; - - if (!$attrs.lite) { - $scope.iframeDashboardUrl = dashboardLink(); - $scope.studentIframe = dashboardLink($scope.$storage.user.username); - - $scope.version = Versions.get({ - gameId: $scope.activity.gameId, - versionId: $scope.activity.versionId - }, function () { - $scope.refreshResults(); - if (!$scope.activity.end) { - refresh = $interval(function () { - $scope.refreshResults(); - }, 10000); - } - }); - } + } + }); + }; + + var updateGroups = function () { + var route = CONSTANTS.PROXY + '/classes/' + $scope.class._id + '/groups'; + $http.get(route).success(function (data) { + $scope.classGroups = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + }; + + var updateGroupings = function () { + var route = CONSTANTS.PROXY + '/classes/' + $scope.class._id + '/groupings'; + $http.get(route).success(function (data) { + $scope.classGroupings = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); }; $scope.$on('$destroy', function() { @@ -87,6 +122,9 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) }); $attrs.$observe('activity', function() { + groupsReady = false; + groupingsReady = false; + classReady = false; $scope.activity = JSON.parse($attrs.activity); Activities.get({activityId: $scope.activity._id}).$promise.then(function(a) { $scope.activity = a; @@ -221,118 +259,169 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) $scope.activity.$update(); }; - // Teachers + // Students + $scope.classGroups = []; + $scope.classGroupings = []; - $scope.isRemovable = function (dev) { - var teachers = $scope.activity.teachers; - if (teachers && teachers.length === 1) { - return false; - } - if ($scope.username === dev) { - return false; - } - return true; + $scope.selectedGroup = undefined; + $scope.selectedGrouping = undefined; + + $scope.unlockedGroups = false; + $scope.unlockedGroupings = false; + + $scope.isUsingGroupings = function () { + return $scope.activity.groupings && $scope.activity.groupings.length > 0; }; - $scope.inviteTeacher = function () { - if ($scope.teacher.name && $scope.teacher.name.trim() !== '') { - $scope.activity.teachers.push($scope.teacher.name); - $scope.activity.$update(function() { - $scope.teacher.name = ''; - }); - } + $scope.isUsingGroups = function () { + return !$scope.isUsingGroupings() && $scope.activity.groups && $scope.activity.groups.length > 0; }; - $scope.ejectTeacher = function (teacher) { + $scope.unlockGroups = function() { var route = CONSTANTS.PROXY + '/activities/' + $scope.activity._id + '/remove'; - $http.put(route, {teachers: teacher}).success(function (data) { - $scope.activity.teachers = data.teachers; - }).error(function (data, status) { - console.error('Error on put' + route + ' ' + - JSON.stringify(data) + ', status: ' + status); - }); + if ($scope.unlockedGroupings) { + $http.put(route, {groupings: $scope.activity.groupings}).success(function (data) { + $scope.activity = data; + $scope.unlockedGroupings = false; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } + if ($scope.unlockedGroups) { + $http.put(route, {groups: $scope.activity.groups}).success(function (data) { + $scope.class = data; + $scope.unlockedGroups = false; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } else { + $scope.unlockedGroups = true; + } }; - // Students - - $scope.inviteStudent = function () { - if ($scope.student.name && $scope.student.name.trim() !== '') { - var route = CONSTANTS.PROXY + '/activities/' + $scope.activity._id; - $http.put(route, {students: $scope.student.name}).success(function (data) { - $scope.student.name = ''; - $scope.activity.students = data.students; + $scope.unlockGroupings = function() { + var route = CONSTANTS.PROXY + '/activities/' + $scope.activity._id + '/remove'; + if ($scope.unlockedGroups) { + $http.put(route, {groups: $scope.activity.groups}).success(function (data) { + $scope.activity = data; + $scope.unlockedGroups = false; }).error(function (data, status) { console.error('Error on put' + route + ' ' + JSON.stringify(data) + ', status: ' + status); }); } - + if ($scope.unlockedGroupings) { + $scope.put(route, {groupings: $scope.activity.groupings}).success(function (data) { + $scope.class = data; + $scope.unlockedGroupings = false; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + } else { + $scope.unlockedGroupings = true; + } }; - $scope.ejectStudent = function (student) { - var route = CONSTANTS.PROXY + '/activities/' + $scope.activity._id + '/remove'; - $http.put(route, {students: student}).success(function (data) { - $scope.activity.students = data.students; - }).error(function (data, status) { - console.error('Error on put' + route + ' ' + - JSON.stringify(data) + ', status: ' + status); - }); + $scope.selectGroup = function (group) { + if ($scope.selectedGroup && $scope.selectedGroup._id === group._id) { + $scope.selectedGroup = undefined; + } else { + $scope.selectedGroup = group; + } + + $scope.selectedGrouping = undefined; }; - $scope.updateActivityToClass = function () { - Classes.get({classId: $scope.activity.classId}).$promise.then(function(c) { - angular.extend($scope.activity.students, c.students); - $scope.activity.$update(); - }); + $scope.isInSelectedGroup = function (usr, role, group) { + if (group) { + return group.participants[role].indexOf(usr) !== -1; + } + if ($scope.selectedGroup) { + return $scope.selectedGroup.participants[role].indexOf(usr) !== -1; + } + return false; }; - $scope.resetActivityToClass = function () { + $scope.selectGrouping = function (grouping) { + if ($scope.selectedGrouping && $scope.selectedGrouping._id === grouping._id) { + $scope.selectedGrouping = undefined; + } else { + $scope.selectedGrouping = grouping; + } - Classes.get({classId: $scope.activity.classId}).$promise.then(function(c) { + $scope.selectedGroup = undefined; + }; - var toRemove = _.difference($scope.activity.students, c.students); - $scope.activity.students = _.intersection($scope.activity.students, c.students); - var then = function() { - angular.extend($scope.activity.students, c.students); - $scope.activity.$update(); - }; - if (toRemove.length > 0) { - removeStudentsFromActivity(toRemove, then); - } else { - then(); - } - }); + $scope.getGroupThClass = function(group) { + if ($scope.selectedGroup && $scope.selectedGroup._id === group._id) { + return 'bg-success'; + } + if ($scope.selectedGrouping && $scope.isInSelectedGrouping(group._id, 'group')) { + return 'bg-warning'; + } + return ''; }; - var removeStudentsFromActivity = function (students, then) { - if (students.length > 0) { - var route = CONSTANTS.PROXY + '/activities/' + $scope.activity._id + '/remove'; - $http.put(route, {students: students}).success(function (data) { - $scope.activity.students = data.students; - then(); - }).error(function (data, status) { - console.error('Error on put' + route + ' ' + - JSON.stringify(data) + ', status: ' + status); - }); + $scope.getUserThClass = function(usr, role) { + if ($scope.selectedGroup && $scope.isInSelectedGroup(usr, role)) { + return 'bg-success'; } + if ($scope.selectedGrouping && $scope.isInSelectedGrouping(usr, role)) { + return 'bg-warning'; + } + return ''; }; - $scope.addCsvActivity = function () { - var students = []; - $scope.fileContent.contents.trim().split(',').forEach(function (student) { - if (student) { - students.push(student); + $scope.isInSelectedGrouping = function (id, role) { + if ($scope.selectedGrouping) { + if (role === 'group') { + return $scope.selectedGrouping.groups.indexOf(id) !== -1; } - }); + + for (var i = 0; i < $scope.selectedGrouping.groups.length; i++) { + for (var j = 0; j < $scope.classGroups.length; j++) { + if ($scope.classGroups[j]._id === $scope.selectedGrouping.groups[i]) { + if ($scope.isInSelectedGroup(id, role, $scope.classGroups[j])) { + return true; + } + } + } + + } + + } + return false; + }; + + $scope.checkGroup = function (group) { var route = CONSTANTS.PROXY + '/activities/' + $scope.activity._id; - $http.put(route, {students: students}).success(function (data) { - $scope.activity.students = data.students; + if ($scope.activity.groups && $scope.activity.groups.indexOf(group._id) !== -1) { + route += '/remove'; + } + $http.put(route, {groups: [group._id]}).success(function (data) { + $scope.activity = data; }).error(function (data, status) { - console.error('Error on put', route, status); + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); }); }; - + $scope.checkGrouping = function (grouping) { + var route = CONSTANTS.PROXY + '/activities/' + $scope.activity._id; + if ($scope.activity.groupings && $scope.activity.groupings.indexOf(grouping._id) !== -1) { + route += '/remove'; + } + $http.put(route, {groupings: [grouping._id]}).success(function (data) { + $scope.activity = data; + }).error(function (data, status) { + console.error('Error on put' + route + ' ' + + JSON.stringify(data) + ', status: ' + status); + }); + }; + // Name $scope.changeActivityName = function () { diff --git a/app/views/view/data.jade b/app/views/view/data.jade index 1375867..03d22c5 100644 --- a/app/views/view/data.jade +++ b/app/views/view/data.jade @@ -46,12 +46,10 @@ include data/information #realtime.tab-pane include data/realtime/realtime - #students.tab-pane - include data/students + #participants.tab-pane + include data/participants #attempts.tab-pane include data/attempts - #teachers.tab-pane - include data/teachers #config.tab-pane include data/config #analysis.tab-pane diff --git a/app/views/view/data/menu.jade b/app/views/view/data/menu.jade index d53f98f..1aa571f 100644 --- a/app/views/view/data/menu.jade +++ b/app/views/view/data/menu.jade @@ -21,13 +21,9 @@ ul.nav span.glyphicon.glyphicon-info-sign.right20 | Information li(ng-if='isTeacher()') - span.left-menu-item(href="#teachers", role="tab", data-toggle="tab", ng-click="view = 'teachers'") - span.glyphicon.glyphicon-briefcase.right20 - | Teachers - li(ng-if='isTeacher()') - span.left-menu-item(href="#students", role="tab", data-toggle="tab", ng-click="view = 'students'") + span.left-menu-item(href="#participants", role="tab", data-toggle="tab", ng-click="view = 'participants'") span.glyphicon.glyphicon-apple.right20 - | Students + | Participants li span.left-menu-item(href="#attempts", role="tab", data-toggle="tab", ng-click="view = 'attempts'") span.glyphicon.glyphicon-tasks.right20 diff --git a/app/views/view/data/participants.jade b/app/views/view/data/participants.jade new file mode 100644 index 0000000..44000ef --- /dev/null +++ b/app/views/view/data/participants.jade @@ -0,0 +1,182 @@ +//- + Copyright 2016 e-UCM (http://www.e-ucm.es/) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + This project has received funding from the European Union’s Horizon + 2020 research and innovation programme under grant agreement No 644187. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 (link is external) + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +.container-fluid.theme-showcase + h1 Participants + .row + .col-md-12 + .checkbox + label + input(type="checkbox" ng-change='allowAnonymous()' ng-model='activity.allowAnonymous') + | Allow anonymous users + br + div.modal-header.row + .modal-title + .col-md-12(ng-if='!unlockedGroups && !unlockedGroupings') + label.alert-info *If you want restrict the default access to the class with groups o groupings, please click on a star icon and check the groups or groupings + .col-md-12(ng-if='unlockedGroups') + label.alert-success You are using groups (if you want use all participants, please, click star icon in groups area) + .col-md-12(ng-if='unlockedGroupings') + label.alert-warning You are using groupings (if you want use all participants, please, click star icon in groupings area) + div.modal-body.row + .col-md-6 + .panel.panel-default + .panel-heading + h3 + label Class Teachers + .panel-body + table.table.table-hover + thead + tr + th Teacher + th(ng-show='selectedGroup') Add or Remove + th(ng-show='selectedGroup') Remove + tbody + tr(ng-if='class.participants.teachers.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No students found + tr(ng-repeat='teacher in class.participants.teachers') + td(ng-class='getUserThClass(teacher, "teachers")') + label {{teacher}} + td(ng-show='selectedGroup && !isInSelectedGroup(teacher, "teachers")' ng-class='isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') + a(ng-click='modifyGroup(teacher, "teacher", true)') + span.glyphicon.glyphicon-plus + td(ng-show='isInSelectedGroup(teacher, "teachers")' ng-class='isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') + a(ng-click='modifyGroup(teacher, "teacher", false)') + span.glyphicon.glyphicon-minus + td(ng-show='!selectedGroup && !selectedGrouping' ng-class='isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') + a(ng-if='isRemovable(teacher)' ng-click='ejectUser(teacher, "teacher")') + span.glyphicon.glyphicon-remove-sign + .panel.panel-default + .panel-heading + h3 + label Class Assistants + .panel-body + table.table.table-hover + thead + tr + th Assistant + th(ng-show='selectedGroup') Add or Remove + th(ng-show='!selectedGroup') Remove + tbody + tr(ng-if='class.participants.assistants.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No assistants found + tr(ng-repeat='assistant in class.participants.assistants') + td(ng-class='getUserThClass(assistant, "assistants")') + label {{assistant}} + td(ng-show='!selectedGroup && !isInSelectedGroup(assistant, "assistants")' ng-class='isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') + a(ng-click='modifyGroup(assistant, "assistant", true)') + span.glyphicon.glyphicon-plus + td(ng-show='isInSelectedGroup(assistant, "assistants")' ng-class='isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') + a(ng-click='modifyGroup(assistant, "assistant", false)') + span.glyphicon.glyphicon-minus + td(ng-show='!selectedGroup && !selectedGrouping' ng-class='isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') + a(ng-click='ejectUser(assistant, "assistant")') + span.glyphicon.glyphicon-remove-sign + .panel.panel-default + .panel-heading + h3 + label Class Students + .panel-body + table.table.table-hover + thead + tr + th Student + th(ng-show='selectedGroup') Add or Remove + th(ng-show='!selectedGroup') Remove + tbody + tr(ng-if='class.participants.students.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No students found + tr(ng-repeat='student in class.participants.students') + td(ng-class='getUserThClass(student, "students")') + label {{student}} + td(ng-show='selectedGroup && !isInSelectedGroup(student, "students")' ng-class='isInSelectedGroup(student, "students") ? "bg-success" : ""') + a(ng-click='modifyGroup(student, "student", true)') + span.glyphicon.glyphicon-plus + td(ng-show='isInSelectedGroup(student, "students")' ng-class='isInSelectedGroup(student, "students") ? "bg-success" : ""') + a(ng-click='modifyGroup(student, "student", false)') + span.glyphicon.glyphicon-minus + td(ng-show='!selectedGroup && !selectedGrouping' ng-class='isInSelectedGroup(student, "students") ? "bg-success" : ""') + a(ng-click='ejectUser(student, "student")') + span.glyphicon.glyphicon-remove-sign + .col-md-6 + .panel.panel-default + .panel-heading + .row + .col-md-9 + h3 + label Class Groups + .col-md-3 + a.btn.btn-default(type='button', ng-click='unlockGroups()' ng-class='unlockedGroups ? "btn-success" : "btn-default"') + span.glyphicon(ng-class='unlockedGroups ? "glyphicon-star" : "glyphicon-star-empty"') + .panel-body + table.table.table-hover + thead + tr + th Group + th(ng-if='unlockedGroups') Use this groups in the class + th Remove + tbody + tr(ng-if='classGroups.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No groups created yet + tr(ng-repeat='group in classGroups') + td(ng-class='getGroupThClass(group)') + a.btn(ng-click='selectGroup(group)') {{group.name}} + td(ng-if='unlockedGroups') + a.btn(ng-click='checkGroup(group)') + span.glyphicon(ng-class='activity.groups.indexOf(group._id)===-1 ? "glyphicon-unchecked" : "glyphicon-saved"') + td(ng-show='selectedGrouping && !isInSelectedGrouping(group._id, "group")' ng-class='getGroupThClass(group)') + a.btn(ng-click='modifyGrouping(group, true)') + span.glyphicon.glyphicon-plus + td(ng-show='selectedGrouping && isInSelectedGrouping(group._id, "group")' ng-class='getGroupThClass(group)') + a.btn(ng-click='modifyGrouping(group, false)') + span.glyphicon.glyphicon-minus + td(ng-show='!selectedGrouping' ng-class='getGroupThClass(group)') + a.btn(ng-click='removeGroup(group)') + span.glyphicon.glyphicon-remove-sign + .panel.panel-default + .panel-heading + .row + .col-md-9 + h3 + label Class Groupings + .col-md-3 + a.btn(type='button', ng-click='unlockGroupings()' ng-class='unlockedGroupings ? "btn-warning" : "btn-default"') + span.glyphicon(ng-class='unlockedGroupings ? "glyphicon-star" : "glyphicon-star-empty"') + .panel-body + table.table.table-hover + thead + tr + th Grouping + th(ng-if='unlockedGroupings') Use this groupings in the class + th Remove + tbody + tr(ng-if='classGroupings.length == 0') + td(colspan=6) + div.alert.alert-warning(style='margin-bottom: 0px') No groups created yet + tr(ng-class='selectedGrouping._id === grouping._id ? "bg-warning" : ""' ng-repeat='grouping in classGroupings') + td + a.btn(ng-click='selectGrouping(grouping)') {{grouping.name}} + td(ng-if='unlockedGroupings') + a.btn(ng-click='checkGrouping(grouping)') + span.glyphicon(ng-class='activity.groupings.indexOf(grouping._id)===-1 ? "glyphicon-unchecked" : "glyphicon-saved"') + td + a(ng-click='removeGrouping(grouping)') + span.glyphicon.glyphicon-remove-sign diff --git a/app/views/view/data/students.jade b/app/views/view/data/students.jade deleted file mode 100644 index 152c2ad..0000000 --- a/app/views/view/data/students.jade +++ /dev/null @@ -1,64 +0,0 @@ -//- - Copyright 2016 e-UCM (http://www.e-ucm.es/) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - This project has received funding from the European Union’s Horizon - 2020 research and innovation programme under grant agreement No 644187. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 (link is external) - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -.container-fluid.theme-showcase - h1 Students - .row - .col-md-12 - .checkbox - label - input(type="checkbox" ng-change='allowAnonymous()' ng-model='activity.allowAnonymous') - | Allow anonymous users - br - .row - .col-md-12 - .panel.panel-primary - .panel-heading Add students to this activity - .panel-body - label Add class from file: - input.form-control#inputFile.file(type='file' file-reader="fileContent") - span   - button.btn.btn-primary(ng-click='addCsvActivity()') Add class - hr - label Username: - input.form-control(type='text' ng-model='student.name') - span   - a.btn.btn-primary(type='button', ng-click='inviteStudent()') - .glyphicon.glyphicon-plus - span   - button.btn.btn-primary Add missing students from class  - .glyphicon.glyphicon-plus(ng-click='updateActivityToClass(class)' title="Add missing students from class") - span   - button.btn.btn-primary Reset students to class students  - .glyphicon.glyphicon-adjust(ng-click='resetActivityToClass(class)' title="Reset students to class students") - .row - .col-md-12 - table.table.table-hover - thead - tr - th Students - th Remove - tbody - tr(ng-if='activity.students.length == 0') - td(colspan=6) - div.alert.alert-warning(style='margin-bottom: 0px') No students found - tr(ng-repeat='student in activity.students') - td - label {{student}} - td - a(ng-click='ejectStudent(student)') - span.glyphicon.glyphicon-remove-sign diff --git a/app/views/view/data/teachers.jade b/app/views/view/data/teachers.jade deleted file mode 100644 index 9c39037..0000000 --- a/app/views/view/data/teachers.jade +++ /dev/null @@ -1,45 +0,0 @@ -//- - Copyright 2016 e-UCM (http://www.e-ucm.es/) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - This project has received funding from the European Union’s Horizon - 2020 research and innovation programme under grant agreement No 644187. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 (link is external) - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -.container-fluid.theme-showcase - h1 Teachers - .row - .col-md-12 - .panel.panel-primary - .panel-heading Add teacher to this activity - .panel-body - label Username: - input.form-control(type='text' ng-model='teacher.name') - a.btn.btn-primary(type='button', ng-click='inviteTeacher()') - .glyphicon.glyphicon-plus - .row - .col-md-12 - table.table.table-hover - thead - tr - th Teachers - th Remove - tbody - tr(ng-if='activity.teachers.length == 0') - td(colspan=6) - div.alert.alert-warning(style='margin-bottom: 0px') No teachers found - tr(ng-repeat='teacher in activity.teachers') - td - label {{teacher}} - td - a(ng-if='isRemovable(teacher)' ng-click='ejectTeacher(teacher)') - span.glyphicon.glyphicon-remove-sign From 40af47390f885067e219ec34c44904291dd09d76 Mon Sep 17 00:00:00 2001 From: Synpheros Date: Tue, 8 May 2018 13:35:13 +0200 Subject: [PATCH 16/17] lint fixes # Conflicts: # app/public/js/controllers/login.js --- app/public/js/controllers/activity.js | 5 +++-- app/public/js/controllers/app.js | 2 +- app/public/js/controllers/login.js | 2 +- app/viewRoutes.js | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/public/js/controllers/activity.js b/app/public/js/controllers/activity.js index 3238f54..39da4b1 100644 --- a/app/public/js/controllers/activity.js +++ b/app/public/js/controllers/activity.js @@ -35,7 +35,8 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) }) .controller('ActivityCtrl', ['$rootScope', '$scope', '$attrs', '$location', '$http', 'Activities', 'Classes', '_', 'Results', 'Versions', 'Groups', 'Groupings', '$sce', '$interval', 'Role', 'CONSTANTS', - function ($rootScope, $scope, $attrs, $location, $http, Activities, Classes, _, Results, Versions, Groups, Groupings, $sce, $interval, Role, CONSTANTS) { + function ($rootScope, $scope, $attrs, $location, $http, Activities, Classes, _, Results, + Versions, Groups, Groupings, $sce, $interval, Role, CONSTANTS) { var refresh; var groupsReady = false; @@ -421,7 +422,7 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) JSON.stringify(data) + ', status: ' + status); }); }; - + // Name $scope.changeActivityName = function () { diff --git a/app/public/js/controllers/app.js b/app/public/js/controllers/app.js index 9908c87..4d0b320 100644 --- a/app/public/js/controllers/app.js +++ b/app/public/js/controllers/app.js @@ -283,7 +283,7 @@ angular.module('myApp', [ $scope.selectedGame = Games.get({gameId: activity.gameId}); }); } - } else if(!$window.location.pathname.endsWith('loginbyplugin')){ + } else if (!$window.location.pathname.endsWith('loginbyplugin')) { $location.url('login'); } } diff --git a/app/public/js/controllers/login.js b/app/public/js/controllers/login.js index de93017..8b0c30b 100644 --- a/app/public/js/controllers/login.js +++ b/app/public/js/controllers/login.js @@ -52,7 +52,7 @@ angular.module('loginApp', ['ngStorage', 'ngCookies']) $scope.loginSaml = function () { var location = CONSTANTS.APIPATH + '/login/' + $scope.saml.pluginId + '?callback=' + encodeURIComponent( $window.location.origin + $window.location.pathname + 'byplugin'); - $location.url(location); + $window.location.href = location; }; $scope.saml = null; diff --git a/app/viewRoutes.js b/app/viewRoutes.js index 1ecf6fb..7a517f2 100644 --- a/app/viewRoutes.js +++ b/app/viewRoutes.js @@ -31,7 +31,7 @@ var getBasePath = function(req) { }; router.get('/loginbyplugin', function (req, res) { - console.log("loginbyplugin"); + console.log('loginbyplugin'); res.render('view/loginplugin', {user: JSON.stringify(req.query), basePath: getBasePath(req)}); }); From 577e09421191ea8d65a5df4c0b2b33b065bb26d9 Mon Sep 17 00:00:00 2001 From: gorco Date: Wed, 21 Mar 2018 12:41:32 +0100 Subject: [PATCH 17/17] Improved and fixed activity participants view --- app/public/js/controllers/activity.js | 4 +-- app/views/view/data/participants.jade | 41 --------------------------- 2 files changed, 2 insertions(+), 43 deletions(-) diff --git a/app/public/js/controllers/activity.js b/app/public/js/controllers/activity.js index 39da4b1..7903f8a 100644 --- a/app/public/js/controllers/activity.js +++ b/app/public/js/controllers/activity.js @@ -291,7 +291,7 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) } if ($scope.unlockedGroups) { $http.put(route, {groups: $scope.activity.groups}).success(function (data) { - $scope.class = data; + $scope.activity = data; $scope.unlockedGroups = false; }).error(function (data, status) { console.error('Error on put' + route + ' ' + @@ -315,7 +315,7 @@ angular.module('activityApp', ['myApp', 'ngStorage', 'services']) } if ($scope.unlockedGroupings) { $scope.put(route, {groupings: $scope.activity.groupings}).success(function (data) { - $scope.class = data; + $scope.activity = data; $scope.unlockedGroupings = false; }).error(function (data, status) { console.error('Error on put' + route + ' ' + diff --git a/app/views/view/data/participants.jade b/app/views/view/data/participants.jade index 44000ef..780fc44 100644 --- a/app/views/view/data/participants.jade +++ b/app/views/view/data/participants.jade @@ -43,8 +43,6 @@ thead tr th Teacher - th(ng-show='selectedGroup') Add or Remove - th(ng-show='selectedGroup') Remove tbody tr(ng-if='class.participants.teachers.length == 0') td(colspan=6) @@ -52,15 +50,6 @@ tr(ng-repeat='teacher in class.participants.teachers') td(ng-class='getUserThClass(teacher, "teachers")') label {{teacher}} - td(ng-show='selectedGroup && !isInSelectedGroup(teacher, "teachers")' ng-class='isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') - a(ng-click='modifyGroup(teacher, "teacher", true)') - span.glyphicon.glyphicon-plus - td(ng-show='isInSelectedGroup(teacher, "teachers")' ng-class='isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') - a(ng-click='modifyGroup(teacher, "teacher", false)') - span.glyphicon.glyphicon-minus - td(ng-show='!selectedGroup && !selectedGrouping' ng-class='isInSelectedGroup(teacher, "teachers") ? "bg-success" : ""') - a(ng-if='isRemovable(teacher)' ng-click='ejectUser(teacher, "teacher")') - span.glyphicon.glyphicon-remove-sign .panel.panel-default .panel-heading h3 @@ -70,8 +59,6 @@ thead tr th Assistant - th(ng-show='selectedGroup') Add or Remove - th(ng-show='!selectedGroup') Remove tbody tr(ng-if='class.participants.assistants.length == 0') td(colspan=6) @@ -79,15 +66,6 @@ tr(ng-repeat='assistant in class.participants.assistants') td(ng-class='getUserThClass(assistant, "assistants")') label {{assistant}} - td(ng-show='!selectedGroup && !isInSelectedGroup(assistant, "assistants")' ng-class='isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') - a(ng-click='modifyGroup(assistant, "assistant", true)') - span.glyphicon.glyphicon-plus - td(ng-show='isInSelectedGroup(assistant, "assistants")' ng-class='isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') - a(ng-click='modifyGroup(assistant, "assistant", false)') - span.glyphicon.glyphicon-minus - td(ng-show='!selectedGroup && !selectedGrouping' ng-class='isInSelectedGroup(assistant, "assistants") ? "bg-success" : ""') - a(ng-click='ejectUser(assistant, "assistant")') - span.glyphicon.glyphicon-remove-sign .panel.panel-default .panel-heading h3 @@ -97,8 +75,6 @@ thead tr th Student - th(ng-show='selectedGroup') Add or Remove - th(ng-show='!selectedGroup') Remove tbody tr(ng-if='class.participants.students.length == 0') td(colspan=6) @@ -106,15 +82,6 @@ tr(ng-repeat='student in class.participants.students') td(ng-class='getUserThClass(student, "students")') label {{student}} - td(ng-show='selectedGroup && !isInSelectedGroup(student, "students")' ng-class='isInSelectedGroup(student, "students") ? "bg-success" : ""') - a(ng-click='modifyGroup(student, "student", true)') - span.glyphicon.glyphicon-plus - td(ng-show='isInSelectedGroup(student, "students")' ng-class='isInSelectedGroup(student, "students") ? "bg-success" : ""') - a(ng-click='modifyGroup(student, "student", false)') - span.glyphicon.glyphicon-minus - td(ng-show='!selectedGroup && !selectedGrouping' ng-class='isInSelectedGroup(student, "students") ? "bg-success" : ""') - a(ng-click='ejectUser(student, "student")') - span.glyphicon.glyphicon-remove-sign .col-md-6 .panel.panel-default .panel-heading @@ -131,7 +98,6 @@ tr th Group th(ng-if='unlockedGroups') Use this groups in the class - th Remove tbody tr(ng-if='classGroups.length == 0') td(colspan=6) @@ -148,9 +114,6 @@ td(ng-show='selectedGrouping && isInSelectedGrouping(group._id, "group")' ng-class='getGroupThClass(group)') a.btn(ng-click='modifyGrouping(group, false)') span.glyphicon.glyphicon-minus - td(ng-show='!selectedGrouping' ng-class='getGroupThClass(group)') - a.btn(ng-click='removeGroup(group)') - span.glyphicon.glyphicon-remove-sign .panel.panel-default .panel-heading .row @@ -166,7 +129,6 @@ tr th Grouping th(ng-if='unlockedGroupings') Use this groupings in the class - th Remove tbody tr(ng-if='classGroupings.length == 0') td(colspan=6) @@ -177,6 +139,3 @@ td(ng-if='unlockedGroupings') a.btn(ng-click='checkGrouping(grouping)') span.glyphicon(ng-class='activity.groupings.indexOf(grouping._id)===-1 ? "glyphicon-unchecked" : "glyphicon-saved"') - td - a(ng-click='removeGrouping(grouping)') - span.glyphicon.glyphicon-remove-sign