diff --git a/.travis.yml b/.travis.yml index d9ca353..8a9dbc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,4 +12,3 @@ before_script: - 'cd ../frontend' - 'npm install' - 'bower install' - - 'grunt test' diff --git a/.tsdrc b/.tsdrc deleted file mode 100644 index d8f386d..0000000 --- a/.tsdrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "token":"73d28fdc4f1e8b73f18ca6de7c64fd560e97d5fe" -} \ No newline at end of file diff --git a/backend/Gruntfile.js b/backend/Gruntfile.js index 55674cd..c375bdb 100644 --- a/backend/Gruntfile.js +++ b/backend/Gruntfile.js @@ -5,6 +5,7 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-express-server'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-typescript'); grunt.loadNpmTasks('grunt-mocha-test'); @@ -109,8 +110,29 @@ module.exports = function (grunt) { dist: ['dist/'], test: ['buildTests/'], all:['src/**/*.js', 'src/**/*js.map','tests/**/*.js', 'tests/**/*js.map'] - } + }, +// --------------------------------------------- + + // --------------------------------------------- +// copy task +// --------------------------------------------- + copy: { + testFiles: { + files: [{ + expand: true, + src: ['db_test.json', 'stub_values_test.json', 'db.json', 'stub_values.json'], + dest: 'buildTests/' + }] + }, + prodFiles: { + files: [{ + expand: true, + src: ['db_test.json', 'stub_values_test.json', 'db.json', 'stub_values.json'], + dest: 'build/' + }] + } + } }); // register tasks @@ -119,7 +141,8 @@ module.exports = function (grunt) { grunt.registerTask('build', function () { grunt.task.run(['clean:build','clean:test']); - grunt.task.run(['typescript:build', 'typescript:test']); + grunt.task.run(['typescript:build','typescript:test', 'copy:prodFiles']); + //grunt.task.run(['typescript:build']); }); grunt.registerTask('develop', function() { @@ -135,7 +158,7 @@ module.exports = function (grunt) { grunt.registerTask('test', function() { grunt.task.run(['clean:test']); - grunt.task.run(['typescript:test', 'mochaTest:test']); + grunt.task.run(['copy:testFiles','typescript:test', 'mochaTest:test']); }); } \ No newline at end of file diff --git a/backend/db.json b/backend/db.json index 3774b85..fcf9cd3 100644 --- a/backend/db.json +++ b/backend/db.json @@ -3,33 +3,31 @@ { "id": "3221c575-85ca-447b-86f3-3a4ef39985dc", "name": "Clim", - "timeBox": { - "startDate": "2015-07-01T06:58:42.000Z", - "endDate": "2015-08-31T13:01:24.000Z" + "validityPeriod": { + "start": "2015-07-01T06:58:42.000Z", + "end": "2015-09-30T13:01:24.000Z" }, - "duration": "week", + "recurringPeriod": "week", "conditions": [ { "id": "ab72f9b4-a368-4ea2-8adb-738ea0e6f30b", + "description": "la température a augmenté de 25% par rapport à la semaine dernière", "expression": { "valueLeft": { "value": "TMP_CLI", - "sensor": true + "symbolicName": true }, "valueRight": { "value": "15", - "sensor": false + "symbolicName": false }, - "comparison": ">", - "description": "a desc", - "periodOfTime": "-2208474000000" + "comparison": ">" }, "threshold": 25, - "startDate": "2015-08-02T22:00:00.000Z", - "dateOfCreation": "2015-08-09T22:00:00.000Z", - "endDate": "2015-08-14T21:59:59.999Z", - "percentageAchieved": 100, - "percentageOfTimeElapsed": 100, + "referencePeriod": { + "numberOfUnitToSubtract": 1, + "unitToSubtract": "week" + }, "filter": { "dayOfWeekFilter": "all", "periodOfDayFilter": [ @@ -38,41 +36,24 @@ ] }, "type": "comparison" - } - ], - "badgeID": "44bb8108-8830-4f43-abd1-3ef643303d92" - }, - { - "id": "9bddaf87-5065-4df7-920a-d1d249c9171d", - "name": "Obj1", - "timeBox": { - "startDate": "2015-08-04T12:25:57.787Z", - "endDate": "2015-08-31T12:25:57.787Z" - }, - "duration": "week", - "conditions": [ - { + }, + { "id": "7713cb13-e86d-40d0-a39f-c4ad5a33546d", + "description": "tmp_cli > 28, 50% du temps", "expression": { "valueLeft": { "value": "TMP_CLI", - "sensor": true + "symbolicName": true }, "valueRight": { - "value": 1, - "sensor": false + "value": 28, + "symbolicName": false }, - "comparison": ">", - "description": "tmp_cli > 1" + "comparison": ">" }, - "threshold": 100, - "startDate": "2015-08-04T12:25:57.787Z", - "dateOfCreation": "2015-08-07T07:56:06.107Z", - "endDate": "2015-08-31T12:25:57.787Z", - "percentageAchieved": 0, - "percentageOfTimeElapsed": 0, + "threshold": 50, "filter": { - "dayOfWeekFilter": "working-week", + "dayOfWeekFilter": "all", "periodOfDayFilter": [ "all" ] @@ -80,31 +61,43 @@ "type": "overall" } ], - "badgeID": "fde68334-f515-4563-954b-ac91b4a42f88" + "badgeID": "44bb8108-8830-4f43-abd1-3ef643303d92" } ], "badges": [ { "id": "44bb8108-8830-4f43-abd1-3ef643303d92", - "name": "Un challenge de démo !", + "name": "Un challenge de d\u00e9mo !", "points": 100 }, { "id": "fde68334-f515-4563-954b-ac91b4a42f88", "name": "Pas froid aux yeux", - "points": "100" - }, - { - "id": "faa78334-f515-4563-954b-ac91b4a42f88", - "name": "Pas chiche de tout fermer !", - "points": "100" + "points": "40" } ], "users": [ { "id": "2cf91e02-a320-4766-aa9f-6efce3142d44", - "name": "Jackie!", + "name": "Charlie", + "mapSymbolicNameToSensor": { + "TMP_CLI": "TEMP_443V" + }, "currentChallenges": [ + + ], + "finishedBadgesMap": { + "44bb8108-8830-4f43-abd1-3ef643303d92": 1 + } + }, + { + "id": "6efce3142d44-a320-4766-4766-2cf91e02", + "name": "G\u00e9g\u00e9", + "mapSymbolicNameToSensor": { + "TMP_CLI": "AC_555V" + }, + "currentChallenges": [ + ], "finishedBadgesMap": { "44bb8108-8830-4f43-abd1-3ef643303d92": 1, @@ -112,67 +105,29 @@ } } ], - "challenges": [ + "teams": [ { - "id": "af0947e9-bf85-4233-8d50-2de787bf6021", - "name": "Clim", - "timeProgress": 100, - "startDate": "2015-08-02T22:00:00.000Z", - "endDate": "2015-08-07T21:59:59.999Z", - "goal": { - "id": "3221c575-85ca-447b-86f3-3a4ef39985dc", - "conditions": { - "TMP_CLI": "TEMP_443V" - } - }, - "progress": [ - { - "id": "ab72f9b4-a368-4ea2-8adb-738ea0e6f30b", - "expression": { - "valueLeft": { - "value": "TMP_CLI", - "sensor": true - }, - "valueRight": { - "value": "15", - "sensor": false - }, - "comparison": ">", - "description": "a desc", - "periodOfTime": "-2208474000000" - }, - "threshold": 25, - "startDate": "2015-07-26T22:00:00.000Z", - "dateOfCreation": "2015-08-02T22:00:00.000Z", - "endDate": "2015-08-07T21:59:59.999Z", - "percentageAchieved": 100, - "percentageOfTimeElapsed": 100, - "filter": { - "dayOfWeekFilter": "all", - "periodOfDayFilter": [ - "morning", - "afternoon" - ] - }, - "type": "comparison" - } + "id": "28aa8108-8830-4f43-abd1-3ab643303d92", + "name": "croquette", + "members": [ + "2cf91e02-a320-4766-aa9f-6efce3142d44", + "6efce3142d44-a320-4766-4766-2cf91e02" ], - "status": "SUCCESS" - }, - { - "id": "f3abd585-b5a2-43d2-bced-738d646921b8", - "name": "Clim", - "timeProgress": 0, - "startDate": "2015-08-09T22:00:00.000Z", - "endDate": "2015-08-14T21:59:59.999Z", - "goal": { - "id": "3221c575-85ca-447b-86f3-3a4ef39985dc", - "conditions": { - "TMP_CLI": "TEMP_443V" - } - }, - "progress": [], - "status": "WAIT" + "leader": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "currentChallenges": [ + + ], + "finishedBadgesMap": { + "44bb8108-8830-4f43-abd1-3ef643303d92": 2 + } } - ] + ], + "challenges": { + "userChallenges": [ + + ], + "teamChallenges": [ + + ] + } } \ No newline at end of file diff --git a/backend/db_test.json b/backend/db_test.json new file mode 100644 index 0000000..f2f5bf9 --- /dev/null +++ b/backend/db_test.json @@ -0,0 +1,139 @@ +{ + "definitions": [ + { + "id": "3221c575-85ca-447b-86f3-3a4ef39985dc", + "name": "Clim", + "validityPeriod": { + "start": "2015-08-03T22:00:00.000Z", + "end": "2015-09-03T22:00:00.000Z" + }, + "recurringPeriod": "week", + "conditions": [ + { + "id": "ab72f9b4-a368-4ea2-8adb-738ea0e6f30b", + "description":"a desc", + "expression": { + "valueLeft": { + "value": "TMP_CLI", + "symbolicName": true + }, + "valueRight": { + "value": "15", + "symbolicName": false + }, + "comparison": ">", + "periodOfTime": "-2208474000000" + }, + "threshold": 25, + "referencePeriod": { + "numberOfUnitToSubtract": 1, + "unitToSubtract": "week" + }, + "filter": { + "dayOfWeekFilter": "all", + "periodOfDayFilter": [ + "morning", + "afternoon" + ] + }, + "type": "comparison" + } + ], + "badgeID": "44bb8108-8830-4f43-abd1-3ef643303d92" + }, + { + "id": "9bddaf87-5065-4df7-920a-d1d249c9171d", + "name": "Obj1", + "validityPeriod": { + "start": "2015-08-04T12:25:57.787Z", + "end": "2015-08-31T12:25:57.787Z" + }, + "recurringPeriod": "week", + "conditions": [ + { + "id": "7713cb13-e86d-40d0-a39f-c4ad5a33546d", + "expression": { + "valueLeft": { + "value": "TMP_CLI", + "symbolicName": true + }, + "valueRight": { + "value": 1, + "symbolicName": false + }, + "comparison": ">", + "description": "tmp_cli > 1" + }, + "threshold": 100, + "filter": { + "dayOfWeekFilter": "working-week", + "periodOfDayFilter": [ + "all" + ] + }, + "type": "overall" + } + ], + "badgeID": "fde68334-f515-4563-954b-ac91b4a42f88" + } + ], + "badges": [ + { + "id": "44bb8108-8830-4f43-abd1-3ef643303d92", + "name": "Un challenge de d\u00e9mo !", + "points": 100 + } + ], + "users": [ + { + "id": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "name": "Charlie", + "currentChallenges": [ + + ], + "finishedBadgesMap": { + + }, + "mapSymbolicNameToSensor": { + "TMP_CLI": "TEMP_443V" + } + }, + { + "id": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "name": "Gégé", + "currentChallenges": [ + + ], + "finishedBadgesMap": { + + }, + "mapSymbolicNameToSensor": { + "TMP_CLI": "TEMP_352" + } + } + ], + "teams": [ + { + "id": "28aa8108-8830-4f43-abd1-3ab643303d92", + "name": "croquette", + "members": [ + "2cf91e02-a320-4766-aa9f-6efce3142d44" + ], + "leader": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "currentChallenges": [ + + ], + "finishedBadgesMap": { + + } + } + ], + "challenges": { + "userChallenges": [ + + ], + "teamChallenges": [ + + ] + } +} \ No newline at end of file diff --git a/backend/new-dv-save.txt b/backend/new-dv-save.txt new file mode 100644 index 0000000..fcf9cd3 --- /dev/null +++ b/backend/new-dv-save.txt @@ -0,0 +1,133 @@ +{ + "definitions": [ + { + "id": "3221c575-85ca-447b-86f3-3a4ef39985dc", + "name": "Clim", + "validityPeriod": { + "start": "2015-07-01T06:58:42.000Z", + "end": "2015-09-30T13:01:24.000Z" + }, + "recurringPeriod": "week", + "conditions": [ + { + "id": "ab72f9b4-a368-4ea2-8adb-738ea0e6f30b", + "description": "la température a augmenté de 25% par rapport à la semaine dernière", + "expression": { + "valueLeft": { + "value": "TMP_CLI", + "symbolicName": true + }, + "valueRight": { + "value": "15", + "symbolicName": false + }, + "comparison": ">" + }, + "threshold": 25, + "referencePeriod": { + "numberOfUnitToSubtract": 1, + "unitToSubtract": "week" + }, + "filter": { + "dayOfWeekFilter": "all", + "periodOfDayFilter": [ + "morning", + "afternoon" + ] + }, + "type": "comparison" + }, + { + "id": "7713cb13-e86d-40d0-a39f-c4ad5a33546d", + "description": "tmp_cli > 28, 50% du temps", + "expression": { + "valueLeft": { + "value": "TMP_CLI", + "symbolicName": true + }, + "valueRight": { + "value": 28, + "symbolicName": false + }, + "comparison": ">" + }, + "threshold": 50, + "filter": { + "dayOfWeekFilter": "all", + "periodOfDayFilter": [ + "all" + ] + }, + "type": "overall" + } + ], + "badgeID": "44bb8108-8830-4f43-abd1-3ef643303d92" + } + ], + "badges": [ + { + "id": "44bb8108-8830-4f43-abd1-3ef643303d92", + "name": "Un challenge de d\u00e9mo !", + "points": 100 + }, + { + "id": "fde68334-f515-4563-954b-ac91b4a42f88", + "name": "Pas froid aux yeux", + "points": "40" + } + ], + "users": [ + { + "id": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "name": "Charlie", + "mapSymbolicNameToSensor": { + "TMP_CLI": "TEMP_443V" + }, + "currentChallenges": [ + + ], + "finishedBadgesMap": { + "44bb8108-8830-4f43-abd1-3ef643303d92": 1 + } + }, + { + "id": "6efce3142d44-a320-4766-4766-2cf91e02", + "name": "G\u00e9g\u00e9", + "mapSymbolicNameToSensor": { + "TMP_CLI": "AC_555V" + }, + "currentChallenges": [ + + ], + "finishedBadgesMap": { + "44bb8108-8830-4f43-abd1-3ef643303d92": 1, + "fde68334-f515-4563-954b-ac91b4a42f88": 1 + } + } + ], + "teams": [ + { + "id": "28aa8108-8830-4f43-abd1-3ab643303d92", + "name": "croquette", + "members": [ + "2cf91e02-a320-4766-aa9f-6efce3142d44", + "6efce3142d44-a320-4766-4766-2cf91e02" + ], + "leader": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "currentChallenges": [ + + ], + "finishedBadgesMap": { + "44bb8108-8830-4f43-abd1-3ef643303d92": 2 + } + } + ], + "challenges": { + "userChallenges": [ + + ], + "teamChallenges": [ + + ] + } +} \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index fe450fe..1343031 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,6 +15,7 @@ "chai": "^3.0.0", "grunt": "^0.4.5", "grunt-contrib-clean": "^0.6.0", + "grunt-contrib-copy": "^0.8.1", "grunt-contrib-watch": "^0.6.1", "grunt-express-server": "^0.5.1", "grunt-mocha-test": "^0.12.7", diff --git a/backend/src/Backend.ts b/backend/src/Backend.ts index c049ca4..b8deffa 100644 --- a/backend/src/Backend.ts +++ b/backend/src/Backend.ts @@ -3,45 +3,23 @@ */ import Server = require('./Server'); -import BadgeRouter = require('./api/BadgeRouter'); -import GoalDefinitionRouter = require('./api/GoalDefinitionRouter'); -import GoalInstanceRouter = require('./api/GoalInstanceRouter'); - -import BadgeRepository = require('./badge/BadgeRepository'); -import BadgeFactory = require('./badge/BadgeFactory'); - -import GoalRepository = require('./goal/GoalRepository'); -import GoalFactory = require('./goal/GoalFactory'); - -import ChallengeRepository = require('./challenge/ChallengeRepository'); -import ChallengeFactory = require('./challenge/ChallengeFactory'); -import UserRepository = require('./user/UserRepository'); -import UserFactory = require('./user/UserFactory'); -import User = require('./user/User'); +import DashboardRouter = require('./api/DashboardRouter'); +import LoginRouter = require('./api/LoginRouter'); +import BadgeRouter = require('./api/BadgeRouter'); +import GoalRouter = require('./api/GoalRouter'); -import Operand = require('./condition/expression/Operand'); -import GoalExpression = require('./condition/expression/GoalExpression'); -import OverallGoalCondition = require('./condition/OverallGoalCondition'); -import TimeBox = require('./TimeBox'); +import Context = require('./Context'); import StoringHandler = require('./StoringHandler'); +import Middleware = require('./Middleware'); class Backend extends Server { - public badgeRepository:BadgeRepository; - public badgeFactory:BadgeFactory; - - public goalDefinitionRepository:GoalRepository; - public goalDefinitionFactory:GoalFactory; - - public goalInstanceRepository:ChallengeRepository; - public goalInstanceFactory:ChallengeFactory; - - public userRepository:UserRepository; - public userFactory:UserFactory; + private context:Context; - private storingHandler:StoringHandler; + public static PATH_TO_DB:string = "./db.json"; + public static PATH_TO_STUB:string = "./stub_values.json"; /** * Constructor. @@ -52,22 +30,11 @@ class Backend extends Server { constructor(listeningPort:number, arguments:Array) { super(listeningPort, arguments); - this.badgeRepository = new BadgeRepository(); - this.badgeFactory = new BadgeFactory(); + this.context = new Context(Backend.PATH_TO_DB, Backend.PATH_TO_STUB); - this.goalDefinitionRepository = new GoalRepository(this.badgeRepository); - this.goalDefinitionFactory = new GoalFactory(); - this.goalInstanceRepository = new ChallengeRepository(); - this.goalInstanceFactory = new ChallengeFactory(); - - this.userRepository = new UserRepository(); - this.userFactory = new UserFactory(); - - this.storingHandler = new StoringHandler(this); - - this.buildAPI(); this.loadData(); + this.buildAPI(); } /** @@ -78,12 +45,17 @@ class Backend extends Server { buildAPI() { var self = this; - this.app.use("/badges", (new BadgeRouter(self.badgeRepository, self.badgeFactory, self.userRepository)).getRouter()); - this.app.use("/goals", (new GoalDefinitionRouter(self.goalDefinitionRepository, self.goalDefinitionFactory, self.goalInstanceRepository, self.userRepository)).getRouter()); - this.app.use("/challenges", (new GoalInstanceRouter(self.goalInstanceRepository, self.goalInstanceFactory, self.goalDefinitionRepository, self.userRepository)).getRouter()); + this.app.use('/dashboard', (new DashboardRouter(self.context, new Middleware())).getRouter()); + this.app.use('/login', (new LoginRouter(self.context)).getRouter()); + + this.app.use("/badges", (new BadgeRouter(self.context)).getRouter()); + this.app.use("/goals", (new GoalRouter(self.context)).getRouter()); + /* + this.app.use("/challenges", (new GoalInstanceRouter(self.challengeRepository, self.challengeFactory, self.goalDefinitionRepository, self.userRepository)).getRouter()); + */ - this.app.get('/test', function (req, res) { - self.storingHandler.save( + this.app.get('/save', function (req, res) { + self.context.saveData( function (result) { console.log(result.success); }, @@ -95,21 +67,14 @@ class Backend extends Server { } loadData():void { - var self = this; - var result = self.storingHandler.load(); - if(result.success) { - console.log(result.success); - } - else { - this.userRepository.setCurrentUser(new User('Jackie')); - } + var result = this.context.loadData(); } } export = Backend; /** - * Server's Backend listening port. + * Server's Context listening port. * * @property _BackendListeningPort * @type number @@ -118,7 +83,7 @@ export = Backend; var _BackendListeningPort:number = 3000; /** - * Server's Backend command line arguments. + * Server's Context command line arguments. * * @property _BackendArguments * @type Array diff --git a/backend/src/Clock.ts b/backend/src/Clock.ts index c641184..45d453b 100644 --- a/backend/src/Clock.ts +++ b/backend/src/Clock.ts @@ -4,21 +4,24 @@ var moment = require('moment'); var moment_timezone = require('moment-timezone'); -import GoalInstanceRouter = require('./api/GoalInstanceRouter'); - class Clock { - private static now:number = Date.now(); + private static now:moment.Moment = moment(); static getNow():number { - return Clock.now; + return Clock.now.valueOf(); } static setNow(newNow:number) { - Clock.now = newNow; + Clock.now = moment.tz(newNow, Clock.getTimeZone()); + } + + static setNowByString(newNow:string) { + Clock.now = moment.tz(newNow, Clock.getTimeZone()); } + static getTimeZone():string { return 'Europe/Paris'; } @@ -27,6 +30,10 @@ class Clock { return moment.tz(date, Clock.getTimeZone()); } + static getMomentFromUnixTimeInMillis(date:number):moment.Moment { + return moment.tz(date, Clock.getTimeZone()); + } + static getMomentFromString(date:string):moment.Moment { return moment.tz(date, Clock.getTimeZone()); } diff --git a/backend/src/Context.ts b/backend/src/Context.ts index 0f92992..1053898 100644 --- a/backend/src/Context.ts +++ b/backend/src/Context.ts @@ -1,9 +1,237 @@ +import BadgeRepository = require('./badge/BadgeRepository'); +import BadgeFactory = require('./badge/BadgeFactory'); + import GoalRepository = require('./goal/GoalRepository'); -import ChallengeRepository = require('./challenge/ChallengeRepository'); +import GoalFactory = require('./goal/GoalFactory'); + +import UserChallengeRepository = require('./challenge/UserChallengeRepository'); +import UserChallengeFactory = require('./challenge/UserChallengeFactory'); + +import TeamChallengeRepository = require('./challenge/TeamChallengeRepository'); +import TeamChallengeFactory = require('./challenge/TeamChallengeFactory'); + import UserRepository = require('./user/UserRepository'); +import UserFactory = require('./user/UserFactory'); +import User = require('./user/User'); + +import TeamRepository = require('./user/TeamRepository'); +import TeamFactory = require('./user/TeamFactory'); + +import Clock = require('./Clock'); + + +import StoringHandler = require('./StoringHandler'); + +class Context { + + + private _badgeRepository:BadgeRepository; + private _badgeFactory:BadgeFactory; + + private _goalRepository:GoalRepository; + private _goalFactory:GoalFactory; + + private _userChallengeRepository:UserChallengeRepository; + private _userChallengeFactory:UserChallengeFactory; + + private _teamChallengeRepository:TeamChallengeRepository; + private _teamChallengeFactory:TeamChallengeFactory; + + private _userRepository:UserRepository; + private _userFactory:UserFactory; + + private _teamRepository:TeamRepository; + private _teamFactory:TeamFactory; + + private storingHandler:StoringHandler; + + private _pathToFileToLoad:string; + private _pathToStubFile:string; + + constructor(pathToFileToLoad:string, pathToStubFile:string = null) { + this._pathToFileToLoad = pathToFileToLoad; + this._pathToStubFile = pathToStubFile; + + this._userRepository = new UserRepository(); + + this._badgeRepository = new BadgeRepository(); + this._badgeFactory = new BadgeFactory(); + + this._goalRepository = new GoalRepository(this._badgeRepository); + this._goalFactory = new GoalFactory(); + + this._userChallengeRepository = new UserChallengeRepository(); + this._userChallengeFactory = new UserChallengeFactory(); + + this._teamChallengeRepository = new TeamChallengeRepository(); + this._teamChallengeFactory = new TeamChallengeFactory(); + + this._userRepository = new UserRepository(); + this._userFactory = new UserFactory(); + + this._teamRepository = new TeamRepository(); + this._teamFactory = new TeamFactory(); + + this.storingHandler = new StoringHandler(this); + } + + loadData() { + var data = this.storingHandler.load(this._pathToFileToLoad); + this.fillRepositories(data); + } + + saveData(successFunc:Function, failFunc:Function) { + this.storingHandler.save(this._pathToFileToLoad, successFunc, failFunc); + } + + fillRepositories(data) { + //console.log("___________________________________________________________"); + + this.fillGoalDefinitionRepository(data); + //this._goalRepository.displayShortState(); + + this.fillBadgesRepository(data); + //this._badgeRepository.displayShortState(); + + this.fillUsersRepository(data); + //this._userRepository.displayShortState(); + + this.fillTeamRepository(data); + //this._teamRepository.displayShortState(); + + this.fillChallengesRepository(data); + //this._userChallengeRepository.displayShortState(); + + //console.log("___________________________________________________________"); + + return {success: '+++\tRepositories filled correctly\t+++'}; + } + + fillGoalDefinitionRepository(data) { + var goalDefinitions = data.definitions; + + for (var currentGoalDefinitionIndex in goalDefinitions) { + var currentGoalDefinition = goalDefinitions[currentGoalDefinitionIndex]; + var currentGoal = this._goalFactory.createGoal(currentGoalDefinition); + this._goalRepository.addGoal(currentGoal); + } + } + + fillBadgesRepository(data) { + var badges = data.badges; + + for (var currentBadgeIndex in badges) { + var currentBadgeDescription = badges[currentBadgeIndex]; + this._badgeRepository.addBadge(this._badgeFactory.createBadge(currentBadgeDescription)); + } + } + + fillUsersRepository(data) { + var users = data.users; + + for (var currentUserIndex in users) { + var currentUserDescription = users[currentUserIndex]; + var currentUser = this._userFactory.createUser(currentUserDescription, this._userChallengeFactory); + this._userRepository.addUser(currentUser); + this._userRepository.setCurrentUser(currentUser); + } + } + + fillTeamRepository(data) { + var teams = data.teams; + + for (var currentTeamIndex in teams) { + var currentTeamDescription = teams[currentTeamIndex]; + var currentTeam = this._teamFactory.createTeam(currentTeamDescription, this._userRepository, this._teamChallengeFactory); + this._teamRepository.addTeam(currentTeam); + } + } + + fillChallengesRepository(data) { + var challenges = data.challenges; + this.fillUserChallengeRepository(challenges); + this.fillTeamChallengeRepository(challenges); + } + + fillUserChallengeRepository(data) { + var challenges = data.userChallenges; + + for (var currentChallengeIndex = 0; currentChallengeIndex < challenges.length; currentChallengeIndex++) { + var currentChallengeDescription = challenges[currentChallengeIndex]; + + var currentChallenge = this._userChallengeFactory.restoreChallenge(currentChallengeDescription, this._goalRepository, this._userRepository, Clock.getMoment(Clock.getNow())); + + this._userChallengeRepository.addUserChallenge(currentChallenge); + } + } + + fillTeamChallengeRepository(data) { + var challenges = data.teamChallenges; + + for (var currentChallengeIndex = 0; currentChallengeIndex < challenges.length; currentChallengeIndex++) { + var currentChallengeDescription = challenges[currentChallengeIndex]; + + var currentChallenge = this._teamChallengeFactory.restoreTeamChallenge(currentChallengeDescription, this._teamRepository,this._goalRepository, this._userChallengeRepository, Clock.getMoment(Clock.getNow())); + + this._teamChallengeRepository.addTeamChallenge(currentChallenge); + } + } + + public getBadgeRepository():BadgeRepository { + return this._badgeRepository; + } + + public getBadgeFactory():BadgeFactory { + return this._badgeFactory; + } + + public getGoalRepository():GoalRepository { + return this._goalRepository; + } + + public getGoalFactory():GoalFactory { + return this._goalFactory; + } + + public getUserChallengeRepository():UserChallengeRepository { + return this._userChallengeRepository; + } + + public getUserChallengeFactory():UserChallengeFactory { + return this._userChallengeFactory; + } + + public getTeamChallengeRepository():TeamChallengeRepository { + return this._teamChallengeRepository; + } + + public getTeamChallengeFactory():TeamChallengeFactory { + return this._teamChallengeFactory; + } + + public getUserRepository():UserRepository { + return this._userRepository; + } + + public getUserFactory():UserFactory { + return this._userFactory; + } + + public getTeamRepository():TeamRepository { + return this._teamRepository; + } + + public getTeamFactory():TeamFactory { + return this._teamFactory; + } + + public getPathToFileToLoad():string { + return this._pathToFileToLoad; + } -interface Context { - fill(goalDefinitionRepository:GoalRepository, goalInstanceRepository:ChallengeRepository, userRepository:UserRepository); + public getPathToStubFile():string { + return this._pathToStubFile; + } } export = Context; \ No newline at end of file diff --git a/backend/src/JSONSerializer.ts b/backend/src/JSONSerializer.ts index add756c..e378a1c 100644 --- a/backend/src/JSONSerializer.ts +++ b/backend/src/JSONSerializer.ts @@ -4,25 +4,23 @@ var fs = require('fs'); class JSONSerializer { - public static JSON_DB_FILE:string = 'db.json'; - - public load():any { - if (!fs.existsSync(JSONSerializer.JSON_DB_FILE)) { - return {error: 'File ' + JSONSerializer.JSON_DB_FILE + ' not found'}; + public load(pathToFileToLoad:string):any { + if (!fs.existsSync(pathToFileToLoad)) { + throw new Error('File ' + pathToFileToLoad + ' not found'); } - var data = fs.readFileSync(JSONSerializer.JSON_DB_FILE, "utf-8"); + var data = fs.readFileSync(pathToFileToLoad, "utf-8"); if (Object.keys(JSON.parse(data)).length == 0) { return {error: '+++\tDatabase was empty !\t+++', data: data}; } - return {success: '+++\tDatabase loaded correctly !\t+++', data: data}; + return {success: '+++\tDatabase loaded correctly !\t+++', data: JSON.parse(data)}; } - public save(data:any, successCallBack:Function, failCallBack:Function):void { + public save(pathToFile:string,data:any, successCallBack:Function, failCallBack:Function):void { - fs.writeFile(JSONSerializer.JSON_DB_FILE, JSON.stringify(data, null, 2), function (err) { + fs.writeFile(pathToFile, JSON.stringify(data, null, 2), function (err) { if (err) { failCallBack(err); } diff --git a/backend/src/Server.ts b/backend/src/Server.ts index 9a214bc..fa0f02c 100644 --- a/backend/src/Server.ts +++ b/backend/src/Server.ts @@ -8,10 +8,7 @@ var http:any = require("http"); var express:any = require("express"); var bodyParser:any = require("body-parser"); -import Badge = require('./badge/Badge'); -import Operand = require('./condition/expression/Operand'); -import GoalCondition = require('./condition/Condition'); -import TimeBox = require('./TimeBox'); +import UserRepository = require('./user/UserRepository'); /** * Represents a Server managing Namespaces. @@ -44,6 +41,7 @@ class Server { */ httpServer:any; + /** * Constructor. * @@ -72,9 +70,11 @@ class Server { res.header("Access-Control-Allow-Methods", "POST, GET, DELETE"); next(); }); + this.httpServer = http.createServer(this.app); } + /** * Runs the Server. * diff --git a/backend/src/StoringHandler.ts b/backend/src/StoringHandler.ts index fcdd9df..ab4403b 100644 --- a/backend/src/StoringHandler.ts +++ b/backend/src/StoringHandler.ts @@ -1,19 +1,19 @@ import JSONSerializer = require('./JSONSerializer'); -import Backend = require('./Backend'); +import Context = require('./Context'); import Clock = require('./Clock'); class StoringHandler { private serializer:JSONSerializer; - private backend:Backend; + private context:Context; - constructor(backend:Backend) { + constructor(backend:Context) { this.serializer = new JSONSerializer(); - this.backend = backend; + this.context = backend; } - load():any { - var result:any = this.serializer.load(); + load(pathToFileToLoad:string):any { + var result:any = this.serializer.load(pathToFileToLoad); if (result.error) { return result.error; @@ -21,84 +21,21 @@ class StoringHandler { console.log(result.success); - return this.fillRepositories(JSON.parse(result.data)); + return result.data; } - save(successCallBack:Function, failCallBack:Function) { + save(pathToFile:string, successCallBack:Function, failCallBack:Function) { var result:any = {}; - result['definitions'] = this.backend.goalDefinitionRepository.getDataInJSON(); - result['badges'] = this.backend.badgeRepository.getDataInJSON(); - result['users'] = this.backend.userRepository.getDataInJSON(); - result['challenges'] = this.backend.goalInstanceRepository.getDataInJSON(); + result['definitions'] = this.context.getGoalRepository().getDataInJSON(); + result['badges'] = this.context.getBadgeRepository().getDataInJSON(); + result['users'] = this.context.getUserRepository().getDataInJSON(); + result['teams'] = this.context.getTeamRepository().getDataInJSON(); + result['challenges'] = {}; + result['challenges']['userChallenges'] = this.context.getUserChallengeRepository().getDataInJSON(); + result['challenges']['teamChallenges'] = this.context.getTeamChallengeRepository().getDataInJSON(); - this.serializer.save(result, successCallBack, failCallBack); - } - - fillRepositories(data) { - console.log("___________________________________________________________"); - - this.fillGoalDefinitionRepository(data); - this.backend.goalDefinitionRepository.displayShortState(); - - this.fillBadgesRepository(data); - this.backend.badgeRepository.displayShortState(); - - this.fillChallengesRepository(data); - this.backend.goalInstanceRepository.displayShortState(); - - this.fillUsersRepository(data); - this.backend.userRepository.displayShortState(); - - console.log("___________________________________________________________"); - - return {success: '+++\tRepositories filled correctly\t+++'}; - } - - fillGoalDefinitionRepository(data) { - var goalDefinitions = data.definitions; - - for (var currentGoalDefinitionIndex in goalDefinitions) { - var currentGoalDefinition = goalDefinitions[currentGoalDefinitionIndex]; - var currentGoal = this.backend.goalDefinitionFactory.createGoal(currentGoalDefinition); - this.backend.goalDefinitionRepository.addGoal(currentGoal); - } - } - - fillBadgesRepository(data) { - var badges = data.badges; - - for (var currentBadgeIndex in badges) { - var currentBadgeDescription = badges[currentBadgeIndex]; - this.backend.badgeRepository.addBadge(this.backend.badgeFactory.createBadge(currentBadgeDescription)); - } - } - - fillUsersRepository(data) { - var users = data.users; - - for (var currentUserIndex in users) { - var currentUserDescription = users[currentUserIndex]; - var currentUser = this.backend.userFactory.createUser(currentUserDescription); - this.backend.userRepository.addUser(currentUser); - this.backend.userRepository.setCurrentUser(currentUser); - } - } - - fillChallengesRepository(data) { - var challenges = data.challenges; - - for (var currentChallengeIndex = 0 ; currentChallengeIndex < challenges.length ; currentChallengeIndex++) { - var currentChallengeDescription = challenges[currentChallengeIndex]; - - var currentChallenge = this.backend.goalInstanceFactory.createGoalInstance(currentChallengeDescription, - this.backend.goalDefinitionRepository, - this.backend.userRepository, - - Clock.getMoment(Clock.getNow())); - - this.backend.goalInstanceRepository.addGoalInstance(currentChallenge); - } + this.serializer.save(pathToFile, result, successCallBack, failCallBack); } } diff --git a/backend/src/api/BadgeRouter.ts b/backend/src/api/BadgeRouter.ts index 2caad8e..3a69600 100644 --- a/backend/src/api/BadgeRouter.ts +++ b/backend/src/api/BadgeRouter.ts @@ -1,12 +1,10 @@ -/** - * @author Christian Brel - */ import RouterItf = require('./RouterItf'); import BadgeRepository = require('../badge/BadgeRepository'); import BadgeFactory = require('../badge/BadgeFactory'); import UserRepository = require('../user/UserRepository'); +import Context = require('../Context'); import BadArgumentException = require('../exceptions/BadArgumentException'); @@ -30,66 +28,29 @@ class BadgeRouter extends RouterItf { * @param badgeRepository * The badge repository to save and retrieve badges */ - constructor(badgeRepository:BadgeRepository, badgeFactory:BadgeFactory, userRepository:UserRepository) { + constructor(context:Context) { super(); - - if(!badgeRepository) { - throw new BadArgumentException('Badge repository is null'); - } - - if(!badgeFactory) { - throw new BadArgumentException('Badge factory is null'); - } - - if(!userRepository) { - throw new BadArgumentException('User repository is null'); - } - - this.badgeRepository = badgeRepository; - this.badgeFactory = badgeFactory; - this.userRepository = userRepository; + this.badgeRepository = context.getBadgeRepository(); + this.badgeFactory = context.getBadgeFactory(); + this.userRepository = context.getUserRepository(); } buildRouter() { var self = this; - this.router.get('/trophyWall', function(req,res) { - self.getAllFinishedBadges(req,res); - }); this.router.post('/new', function(req,res) { self.newBadge(req,res); }); + this.router.get('/all', function(req,res) { - self.getAllBadges(req,res); + self.getAllBadges(req,res); }); + this.router.get('/:id', function(req, res) { - self.getBadge(req, res); + self.getBadge(req, res); }); } - /** - * This method will return all badges - * from the trophy wall of a user - * using badgeRepository#getBadge - * @param req - * @param res - */ - getAllFinishedBadges(req:any, res:any) { - var badges = this.userRepository.getCurrentUser().getFinishedBadges(); - var result:any[] = []; - - for(var currentBadgeIDIndex in badges) { - var currentBadge = this.badgeRepository.getBadge(currentBadgeIDIndex).getData(); - var dataTrophy = { - number:badges[currentBadgeIDIndex], - badge:currentBadge - }; - - result.push(dataTrophy); - } - res.send(result); - } - /** * This method will return all badges * using badgeRepository#getAllBadges diff --git a/backend/src/api/DashboardRouter.ts b/backend/src/api/DashboardRouter.ts new file mode 100644 index 0000000..6a08e20 --- /dev/null +++ b/backend/src/api/DashboardRouter.ts @@ -0,0 +1,706 @@ +import RouterItf = require('./RouterItf'); + +import UserChallengeRepository = require('../challenge/UserChallengeRepository'); +import UserChallengeFactory = require('../challenge/UserChallengeFactory'); + +import TeamChallengeRepository = require('../challenge/TeamChallengeRepository'); +import TeamChallengeFactory = require('../challenge/TeamChallengeFactory'); + +import GoalRepository = require('../goal/GoalRepository'); +import BadgeRepository = require('../badge/BadgeRepository'); +import UserRepository = require('../user/UserRepository'); +import TeamRepository = require('../user/TeamRepository'); + +import UserChallenge = require('../challenge/UserChallenge'); +import TeamChallenge = require('../challenge/TeamChallenge'); + +import Clock = require('../Clock'); +import ChallengeStatus = require('../Status'); +import Goal = require('../goal/Goal'); +import User = require('../user/User'); +import Team = require('../user/Team'); +import Entity = require('../user/Entity'); + +import Middleware = require('../Middleware'); +import Context = require('../Context'); + +var merge = require('merge'); + +import BadRequestException = require('../exceptions/BadRequestException'); +import BadArgumentException = require('../exceptions/BadArgumentException'); + +class DashboardRouter extends RouterItf { + public static DEMO:boolean = true; + private jsonStub:any = {}; + private pathToStubFile:string; + + private userChallengeRepository:UserChallengeRepository; + private userChallengeFactory:UserChallengeFactory; + + private teamChallengeRepository:TeamChallengeRepository; + private teamChallengeFactory:TeamChallengeFactory; + + private goalRepository:GoalRepository; + + private userRepository:UserRepository; + + private teamRepository:TeamRepository; + + private badgeRepository:BadgeRepository; + + private middleware:Middleware; + + constructor(context:Context, middleware:Middleware) { + super(); + + this.pathToStubFile = context.getPathToStubFile(); + + var fs = require('fs'); + var data = fs.readFileSync(this.pathToStubFile, "utf-8"); + this.jsonStub = JSON.parse(data); + + console.log("STUB", JSON.stringify(this.jsonStub)); + + this.userChallengeRepository = context.getUserChallengeRepository(); + this.userChallengeFactory = context.getUserChallengeFactory(); + + this.teamChallengeRepository = context.getTeamChallengeRepository(); + this.teamChallengeFactory = context.getTeamChallengeFactory(); + + this.goalRepository = context.getGoalRepository(); + + this.userRepository = context.getUserRepository(); + this.teamRepository = context.getTeamRepository(); + + this.badgeRepository = context.getBadgeRepository(); + + this.middleware = middleware; + } + + buildRouter() { + var self = this; + + this.router.get('/view/:id/:dashboard?', function (req, res) { + + var userID = req.params.id; + var dashboardWanted = req.params.dashboard; + + console.log('UserID : ', userID, 'Dashboard wanted', dashboardWanted); + + var result:any = {}; + + var currentUser:User = self.checkUserID(userID); + + if (currentUser == null) { + res.status(404).send({error: 'Le profil utilisateur n\'existe pas'}); + return; + } + + + // User dashboard wanted + if (dashboardWanted == 'personal') { + result = self.getPersonalDashboard(currentUser); + + // Build dashboardList, every views possible for current user + var dashboardList:any[] = []; + + var teams = self.teamRepository.getTeamsByMember(currentUser.getUUID()); + + for (var teamIndex in teams) { + var currentTeam = teams[teamIndex]; + var teamDescription:any = {}; + teamDescription.id = currentTeam.getUUID(); + teamDescription.name = currentTeam.getName(); + dashboardList.push(teamDescription); + } + result.user = { + username : currentUser.getName(), + dashboard:'Personnel' + }; + result.dashboardList = dashboardList; + } + + // Team dashboard wanted + else { + + var team:Team = self.teamRepository.getTeam(dashboardWanted); + if (team == null) { + res.status(404).send({error: 'Le dashboard demandé n\'existe pas'}); + return; + } + + console.log("Dashboard de la team", team.getName(), "désiré"); + + var currentUserIsLeaderOfTargetedTeam = team.hasLeader(currentUser.getUUID()); + if (currentUserIsLeaderOfTargetedTeam) { + console.log("L'utilisateur est leader de la team", team.getName()); + result = self.getTeamDashboardForALeader(team); + } + else { + console.log("L'utilisateur n'est que membre de la team", team.getName()); + result = self.getTeamDashboardForAMember(team); + } + + // Build dashboardList, every views possible for current user + var dashboardList:any[] = []; + + var teams = self.teamRepository.getTeamsByMember(currentUser.getUUID()); + + + for (var teamIndex in teams) { + var currentTeam = teams[teamIndex]; + var teamDescription:any = {}; + teamDescription.id = currentTeam.getUUID(); + teamDescription.name = currentTeam.getName(); + dashboardList.push(teamDescription); + } + + result.user = { + username : currentUser.getName(), + dashboard:team.getName() }; + + result.dashboardList = dashboardList; + } + + + res.send({data: result}); + }); + + + this.router.delete('/delete/:id/:challengeID/:target', function (req, res) { + self.deleteChallenge(req, res); + }); + + this.router.post('/takeGoal/', function (req, res) { + self.newGoalInstance(req, res); + }); + + // Debug routes only + this.router.post('/addstub', function (req, res) { + self.addStub(req, res); + }); + + this.router.post('/setNow', function (req, res) { + self.setNow(req, res); + }); + } + + addStub(req, res) { + + var data = req.body; + var value = data.value; + var key = data.key; + + var valueDesc:any = {}; + valueDesc.date = Clock.getMoment(Clock.getNow()).valueOf(); + valueDesc.value = value; + + var oldJson:any[] = this.jsonStub[key]; + oldJson.push(valueDesc); + this.jsonStub[key] = oldJson; + + res.send('Valeur' + JSON.stringify(valueDesc) + " ajoutee au stub !"); + } + + setNow(req, res) { + var data = req.body; + + var newNow:moment.Moment = Clock.getMomentFromString(data.now); + + console.log("Mise a jour de la date actuelle. Nous sommes maintenant le", newNow.toISOString()); + Clock.setNowByString(newNow.toISOString()); + res.send("New 'now' : " + newNow.toISOString()); + } + + newGoalInstance(req:any, res:any) { + console.log(req.body); + + var goalID = req.body.goalID; + + if (!goalID) { + res.status(400).send({'error': 'goalID field is missing in request'}); + } + + //TODO check if it's a user taking a challenge for himself + // or a team leader taking a challenge for its team + + var currentUserID = req.body.userID; + var currentUser:User = this.checkUserID(currentUserID); + + var target = req.body.target; + + var newChallenge = null; + if (target == 'personal') { + newChallenge = this.createUserChallenge(currentUserID, goalID, Clock.getMoment(Clock.getNow())); + } + else { + newChallenge = this.createTeamChallenge(target, goalID, Clock.getMoment(Clock.getNow())); + } + + if (newChallenge == null) { + res.send({'error': 'Can not take this challenge'}); + return; + } + + res.send({"success": ("Objectif ajouté !" + newChallenge.getDataInJSON())}); + } + + deleteChallenge(req:any, res:any) { + + var challengeID = req.params.challengeID; + var userID = req.params.id; + + var target = req.params.target; + + console.log("TARGET", target); + + try { + if (target == 'personal') { + + var user:User = null; + user = this.checkUserID(userID); + + user.deleteChallenge(challengeID); + res.send({"success": "Objectif supprimé !"}); + } + else { + + var team:Team = null; + team = this.checkTeamID(target); + + var challengeToDelete = this.teamChallengeRepository.getChallengeByID(challengeID); + + team.deleteChallenge(challengeToDelete); + res.send({"success": "Objectif supprimé !"}); + } + } + catch (e) { + if (e instanceof BadRequestException) { + res.status(400).send({error: e.getMessage()}); + return; + + } + else if (e instanceof BadArgumentException) { + res.status(400).send({error: e.getMessage()}); + return; + + } + else { + res.status(500).send({error: e.getMessage()}); + return; + } + } + } + + getTeamDashboardForAMember(teamDescriptionWanted:Team):any { + var result:any = {}; + // Evaluate challenge and return them + // Done before everything to be up to date + this.evaluateChallengeForGivenTeam(teamDescriptionWanted); + + // First col : available goal + var descriptionOfAvailableGoals = this.goalRepository.getListOfNotTakenGoalInJSONFormat(teamDescriptionWanted, this.teamChallengeRepository); + + // Second col : badge description + var descriptionOfBadges:any[] = this.buildBadgesDescriptionForGivenTeam(teamDescriptionWanted); + + // Third col : Build the description of updated challenges (potential additions/deletions) + var descriptionOfChallenges:any[] = this.buildCurrentChallengesDescriptionForGivenTeam(teamDescriptionWanted); + + result.goals = {'canTakeGoal': false, goalsData: descriptionOfAvailableGoals}; + result.badges = descriptionOfBadges; + result.challenges = descriptionOfChallenges; + + return result; + } + + getTeamDashboardForALeader(teamDescriptionWanted:Team):any { + var result:any = {}; + + console.log("Current user is leader of given team"); + + // Evaluate challenge and return them + // Done before everything to be up to date + this.evaluateChallengeForGivenTeam(teamDescriptionWanted); + + // First col : available goal + var descriptionOfAvailableGoals = this.goalRepository.getListOfNotTakenGoalInJSONFormat(teamDescriptionWanted, this.teamChallengeRepository); + + // Second col : badge description + var descriptionOfBadges:any[] = this.buildBadgesDescriptionForGivenTeam(teamDescriptionWanted); + + // Third col : Build the description of updated challenges (potential additions/deletions) + var descriptionOfChallenges:any[] = this.buildCurrentChallengesDescriptionForGivenTeam(teamDescriptionWanted); + + // Build the response + result.goals = {'canTakeGoal': true, goalsData: descriptionOfAvailableGoals}; + result.badges = descriptionOfBadges; + result.challenges = descriptionOfChallenges; + + return result; + } + + getPersonalDashboard(user:User):any { + console.log("\t Personal Dashboard mode"); + console.log('\t Current client', user.getName()); + + + var result:any = {}; + + // Evaluate challenge and return them + // Done before everything to be up to date + this.evaluateChallengesForGivenUser(user); + + // First col : available goal + var descriptionOfAvailableGoals = this.goalRepository.getListOfNotTakenGoalInJSONFormat(user, this.userChallengeRepository); + + // Second col : badge description + var descriptionOfBadges:any[] = this.buildBadgesDescriptionForGivenUser(user); + + // Third col : Build the description of updated challenges (potential additions/deletions) + var descriptionOfChallenges:any[] = this.buildCurrentChallengesDescriptionForGivenUser(user); + + // Build the response + result.goals = {'canTakeGoal': true, goalsData: descriptionOfAvailableGoals}; + result.badges = descriptionOfBadges; + result.challenges = descriptionOfChallenges; + + return result; + } + + private buildCurrentChallengesDescriptionForGivenTeam(team:Team):any[] { + var descriptionOfChallenges:any[] = []; + var challenges = team.getCurrentChallenges(); + for (var challengeIndex in challenges) { + var currentChallengeID = challenges[challengeIndex]; + var currentChallenge = this.teamChallengeRepository.getChallengeByID(currentChallengeID); + var currentChallengeDesc = currentChallenge.getDataForClient(); + descriptionOfChallenges.push(currentChallengeDesc); + } + + return descriptionOfChallenges; + } + + private buildCurrentChallengesDescriptionForGivenUser(user:User):any[] { + var descriptionOfChallenges:any[] = []; + + var challenges = user.getCurrentChallenges(); + for (var challengeIndex in challenges) { + var currentChallengeID = challenges[challengeIndex]; + var currentChallenge = this.userChallengeRepository.getChallengeByID(currentChallengeID); + var currentChallengeDesc = currentChallenge.getDataForClient(); + descriptionOfChallenges.push(currentChallengeDesc); + } + + return descriptionOfChallenges; + } + + private buildBadgesDescriptionForGivenTeam(team:Team):any[] { + var descriptionOfBadges:any[] = []; + + var badges = team.getBadges(); + + for (var currentBadgeIDIndex in badges) { + var currentBadge = this.badgeRepository.getBadge(currentBadgeIDIndex).getData(); + var dataTrophy = { + number: badges[currentBadgeIDIndex], + badge: currentBadge + }; + + descriptionOfBadges.push(dataTrophy); + } + + return descriptionOfBadges; + } + + private buildBadgesDescriptionForGivenUser(user:User):any[] { + var descriptionOfBadges:any[] = []; + + var badges = user.getBadges(); + + for (var currentBadgeIDIndex in badges) { + var currentBadge = this.badgeRepository.getBadge(currentBadgeIDIndex).getData(); + var dataTrophy = { + number: badges[currentBadgeIDIndex], + badge: currentBadge + }; + + descriptionOfBadges.push(dataTrophy); + } + + return descriptionOfBadges; + } + + private evaluateChallengeForGivenTeam(team:Team):void { + var challenges = team.getCurrentChallenges(); + for (var challengeIndex in challenges) { + var currentChallengeID = challenges[challengeIndex]; + var currentChallenge = this.teamChallengeRepository.getChallengeByID(currentChallengeID); + + this.evaluateTeamChallenge(team, currentChallenge, currentChallengeID); + } + } + + evaluateChallengesForGivenUser(user:User):void { + var challenges = user.getCurrentChallenges(); + + for (var challengeIndex in challenges) { + var currentChallengeID = challenges[challengeIndex]; + var currentChallenge = this.userChallengeRepository.getChallengeByID(currentChallengeID); + + if(currentChallenge.isAPersonalChallenge()) { + this.evaluateUserChallenge(user, currentChallenge, currentChallengeID); + } + else { + // Retrieve related TeamChallenge + var teamChallenge:TeamChallenge = this.teamChallengeRepository.getTeamChallengeFromUserChallengeID(currentChallengeID); + + // Retrieve team linked + var team:Team = teamChallenge.getTeam(); + + // Evaluate team challenge + this.evaluateTeamChallenge(team, teamChallenge, teamChallenge.getID()); + } + } + } + + private evaluateTeamChallenge(entity:Team, challengeToEvaluate:TeamChallenge, challengeID) { + var self = this; + + if (!DashboardRouter.DEMO) { + + } + else { + console.log('++++++++++++++++++++++ \tMODE DEMO\t+++++++++++++++++++++'); + + if (challengeToEvaluate.haveToStart(Clock.getCurrentDemoMoment())) { + console.log("Le challenge doit démarrer"); + challengeToEvaluate.setStatus(ChallengeStatus.RUN); + } + + if(challengeToEvaluate.getStatus() == ChallengeStatus.FAIL) { + console.log("Le challenge est fail, ça sert à rien de l'évaluer"); + return challengeToEvaluate; + } + + + var result:any = challengeToEvaluate.evaluate(this.jsonStub); + + + console.log("RESULT", result); + + // Check if the challenge is achieved and finished + if (result.achieved && result.finished) { + console.log("Le challenge de team est réussi et terminé"); + + var badgeID:string = challengeToEvaluate.getBadge(); + + // Add finished badge to current team + this.addFinishedBadgeToTeam(badgeID, entity); + + entity.deleteChallenge(challengeToEvaluate); + + /* + // Build the new challenge (recurring) and evaluate it + var newChallenge = self.createTeamChallenge(entity.getUUID(), challengeToEvaluate.getGoal().getUUID(), challengeToEvaluate.getEndDate()); + if (newChallenge != null) { + self.evaluateTeamChallenge(entity, newChallenge, newChallenge.getID()); + } + */ + } + + // Check if the challenge is not achieved but finished + else if (!result.achieved && result.finished) { + console.log("Le challenge de team est FAIL et terminé"); + + //entity.deleteChallenge(challengeToEvaluate); + + /* + // Build the new challenge (recurring) and evaluate it + var newChallenge = self.createTeamChallenge(entity.getUUID(), challengeToEvaluate.getGoal().getUUID(), challengeToEvaluate.getEndDate()); + if (newChallenge != null) { + self.evaluateTeamChallenge(entity, newChallenge, newChallenge.getID()); + } + */ + } + + return challengeToEvaluate; + } + } + + + private evaluateUserChallenge(entity, challengeToEvaluate:UserChallenge, challengeID) { + var self = this; + + if (!DashboardRouter.DEMO) { + + //TODO move what follow + var required = challengeToEvaluate.getSensors(); + + var requiredSensorName = Object.keys(required); + var numberToLoad:number = requiredSensorName.length; + + for (var currentSensorName in requiredSensorName) { + (function (currentSensorName) { + var startDate:string = '' + required[currentSensorName].startDate; + var endDate:string = '' + required[currentSensorName].endDate; + + var path = 'http://smartcampus.unice.fr/sensors/' + currentSensorName + '/data?date=' + startDate + '/' + endDate; + var dataJsonString = ""; + + this.middleware.getSensorsInfo(required, numberToLoad, dataJsonString, path, + function () { + var result = challengeToEvaluate.evaluate(required); + if (result) { + var newChall = self.createUserChallenge(entity, challengeToEvaluate.getGoal().getUUID(), challengeToEvaluate.getEndDate()); + this.addBadge(challengeID, entity.getUUID()); + if (newChall != null) { + self.evaluateUserChallenge(entity, newChall, newChall.getID()); + } + } + console.log("All data were retrieve properly"); + return challengeToEvaluate; + }, + function () { + return {error: "Error occurred in middleware"}; + }); + + })(requiredSensorName[currentSensorName]); + } + } + else { + console.log('++++++++++++++++++++++ \tMODE DEMO\t+++++++++++++++++++++'); + + if (challengeToEvaluate.haveToStart(Clock.getCurrentDemoMoment())) { + console.log("Le challenge doit démarrer"); + challengeToEvaluate.setStatus(ChallengeStatus.RUN); + } + + + var result:any = challengeToEvaluate.evaluate(this.jsonStub); + + + // Check if the challenge is achieved and finished + if (challengeToEvaluate.getStatus() == ChallengeStatus.SUCCESS) { + console.log("Le challenge est réussi et terminé"); + + // Add finished badge to current user + this.addFinishedBadgeToUser(challengeID, entity.getUUID()); + + entity.deleteChallenge(challengeToEvaluate.getID()); + + + // Build the new challenge (recurring) and evaluate it + var newChallenge = self.createUserChallenge(entity.getUUID(), challengeToEvaluate.getGoal().getUUID(), challengeToEvaluate.getEndDate()); + if (newChallenge != null) { + self.evaluateUserChallenge(entity, newChallenge, newChallenge.getID()); + } + } + + // Check if the challenge is not achieved but finished + else if (challengeToEvaluate.getStatus() == ChallengeStatus.FAIL) { + console.log("Le challenge est FAIL et terminé"); + + entity.deleteChallenge(challengeToEvaluate.getID()); + + // Build the new challenge (recurring) and evaluate it + var newChallenge = self.createUserChallenge(entity.getUUID(), challengeToEvaluate.getGoal().getUUID(), challengeToEvaluate.getEndDate()); + if (newChallenge != null) { + self.evaluateUserChallenge(entity, newChallenge, newChallenge.getID()); + } + } + + return challengeToEvaluate; + } + } + + // debug only + private addFinishedBadgeToUser(challengeID:string, userID:string) { + /* + console.log('add finished badge'); + console.log('user id : ', userID); + console.log('challenge ID : ', challengeID); + */ + var user = this.userRepository.getUser(userID); + var badgeID = this.userChallengeRepository.getBadgeByChallengeID(challengeID); + user.addBadge(badgeID); + } + + private addFinishedBadgeToTeam(badgeID:string, team:Team) { + /* + console.log('add finished badge'); + console.log('user id : ', userID); + console.log('challenge ID : ', challengeID); + */ + team.addBadge(badgeID); + } + + + createUserChallenge(userID:string, goalID:string, date:moment.Moment):UserChallenge { + + var user:User = this.userRepository.getUser(userID); + var goal:Goal = this.goalRepository.getGoal(goalID); + + var newChallenge = user.addChallenge(goal, date); + + if (newChallenge == null) { + return null; + } + + this.userChallengeRepository.addUserChallenge(newChallenge); + return newChallenge; + } + + createTeamChallenge(teamID:string, goalID:string, date:moment.Moment) { + + var team:Team = this.teamRepository.getTeam(teamID); + var goal:Goal = this.goalRepository.getGoal(goalID); + + + var newChallenge = team.addChallenge(goal, this.userChallengeRepository, date); + + if (newChallenge == null) { + return null; + } + + this.teamChallengeRepository.addTeamChallenge(newChallenge); + return newChallenge; + } + + + checkTeamID(teamID):Team { + var currentTeam:Team = this.teamRepository.getTeam(teamID); + if (currentTeam == null) { + throw new BadArgumentException('Given team can not be found'); + } + + return currentTeam; + } + + checkUserID(userID):User { + + var currentUser:User = this.userRepository.getUser(userID); + + if (currentUser == null) { + throw new BadArgumentException('Given username can not be found'); + } + + return currentUser; + } + + checkUserProfile(username):User { + + if (!username) { + throw new BadRequestException('Field username is missing in request'); + } + + var currentUser:User = this.userRepository.getUserByName(username); + if (currentUser == null) { + throw new BadArgumentException('Given username can not be found'); + } + + return currentUser; + } +} + +export = DashboardRouter; \ No newline at end of file diff --git a/backend/src/api/GoalDefinitionRouter.ts b/backend/src/api/GoalDefinitionRouter.ts deleted file mode 100644 index 772e3c5..0000000 --- a/backend/src/api/GoalDefinitionRouter.ts +++ /dev/null @@ -1,91 +0,0 @@ -import RouterItf = require('./RouterItf'); - -import GoalRepository = require('../goal/GoalRepository'); -import GoalFactory = require('../goal/GoalFactory'); -import UserRepository = require('../user/UserRepository'); -import ChallengeRepository = require('../challenge/ChallengeRepository'); - -/** - * GoalDefinitionRouter class
- * This class handle all the API for - * goal definition class and related. - * - * @class GoalDefinitionRouter - * @extends RouterItf - */ - -class GoalDefinitionRouter extends RouterItf { - - private goalDefinitionRepository:GoalRepository; - private goalDefinitionFactory:GoalFactory; - private challengeRepository:ChallengeRepository; - private userRepository:UserRepository; - - constructor(goalDefinitionRepository:GoalRepository, goalDefinitionFactory:GoalFactory, challengeRepository:ChallengeRepository, userRepository:UserRepository) { - super(); - this.goalDefinitionRepository = goalDefinitionRepository; - this.goalDefinitionFactory = goalDefinitionFactory; - this.challengeRepository = challengeRepository; - this.userRepository = userRepository; - } - - buildRouter() { - var self = this; - this.router.get('/all', function (req, res) { - self.getAllGoalsDefinition(req, res); - }); - this.router.get('/:id', function (req, res) { - self.getGoalDefinition(req, res); - }); - this.router.post('/new', function (req, res) { - self.addGoalDefinition(req, res); - }); - } - - /** - * This method will return the goal definition - * of a specific goal, using its uuid and - * using goalDefinitionRepository#getListOfGoalsInJsonFormat - * @param req - * @param res - */ - getGoalDefinition(req, res) { - var result = this.goalDefinitionRepository.getGoal(req.params.id).getDataInJSON(); - res.send(result); - } - - /** - * This method will return all goals definition - * using goalDefinitionRepository#getListOfGoalsInJsonFormat - * @param req - * @param res - */ - getAllGoalsDefinition(req:any, res:any) { - var result = this.goalDefinitionRepository.getListOfUntakedGoalInJSONFormat(this.userRepository.getCurrentUser(), this.challengeRepository); - res.send(result); - } - - /** - * This method will create the goal definition - * via its internal goal definition factory and - * will add it into the specified goal definition - * repository
- * See goalDefinitionFactory#createGoal method to - * see required request fields - * @param req - * @param res - */ - addGoalDefinition(req:any, res:any) { - var data = req.body; - try { - var newGoal = this.goalDefinitionFactory.createGoal(data); - this.goalDefinitionRepository.addGoal(newGoal); - res.send("OK : définition d'objectif créee avec succès"); - } - catch (e) { - res.send(e.toString()); - } - } -} - -export = GoalDefinitionRouter; \ No newline at end of file diff --git a/backend/src/api/GoalInstanceRouter.ts b/backend/src/api/GoalInstanceRouter.ts deleted file mode 100644 index d2dc718..0000000 --- a/backend/src/api/GoalInstanceRouter.ts +++ /dev/null @@ -1,311 +0,0 @@ -/// -/// -/// - -var moment = require('moment'); -var moment_timezone = require('moment-timezone'); - -import RouterItf = require('./RouterItf'); - -import ChallengeRepository = require('../challenge/ChallengeRepository'); -import ChallengeFactory = require('../challenge/ChallengeFactory'); -import GoalRepository = require('../goal/GoalRepository'); -import Challenge = require('../challenge/Challenge'); -import ChallengeStatus = require('../Status'); -import UserRepository = require('../user/UserRepository'); -import Goal = require('../goal/Goal'); -import Middleware = require('../Middleware'); - -import Clock = require('../Clock'); - -import BadRequestException = require('../exceptions/BadRequestException'); - -var moment = require('moment'); - -class GoalInstanceRouter extends RouterItf { - public static DEMO:boolean = true; - - public static STUB_FILE:string = './stub_values.json'; - - private goalInstanceRepository:ChallengeRepository; - private goalInstanceFactory:ChallengeFactory; - private goalDefinitionRepository:GoalRepository; - private userRepository:UserRepository; - - private middleware:Middleware; - - private jsonStub:any = {}; - - constructor(goalInstanceRepository:ChallengeRepository, goalInstanceFactory:ChallengeFactory, goalDefinitionRepository:GoalRepository, userRepository:UserRepository) { - super(); - this.goalInstanceRepository = goalInstanceRepository; - this.goalInstanceFactory = goalInstanceFactory; - - this.goalDefinitionRepository = goalDefinitionRepository; - this.userRepository = userRepository; - - var fs = require('fs'); - var data = fs.readFileSync(GoalInstanceRouter.STUB_FILE, "utf-8"); - this.jsonStub = JSON.parse(data); - console.log("++ Fichier stub chargé correctement\n"); - } - - buildRouter() { - var self = this; - - this.router.post('/new', function (req, res) { - self.newGoalInstance(req, res); - }); - - this.router.get('/all', function (req, res) { - self.getAllChallenges(req, res); - }); - - this.router.delete('/delete/:id', function (req, res) { - self.deleteGoalInstance(req, res); - }); - - this.router.get('/evaluate', function (req, res) { - self.evaluate(req, res); - }); - - this.router.get('/evaluate/all', function (req, res) { - self.evaluateAll(req, res); - }); - - // Debug routes only - this.router.post('/addstub', function (req, res) { - self.addStub(req, res); - }); - - this.router.post('/setNow', function (req, res) { - self.setNow(req, res); - }); - - this.router.post('/addBadge', function (req, res) { - self.addFinishedBadge(req.challengeID, req.userID); - }); - } - - - addStub(req, res) { - - var data = req.body; - var value = data.value; - var key = data.key; - - var valueDesc:any = {}; - valueDesc.date = Clock.getMoment(Clock.getNow()).valueOf(); - valueDesc.value = value; - - // console.log("DATE DU STUB AJOUTE", valueDesc.date); - - var oldJson:any[] = this.jsonStub[key].values; - oldJson.push(valueDesc); - this.jsonStub[key].values = oldJson; - - res.send('Valeur' + JSON.stringify(valueDesc) + " ajoutee au stub !"); - } - - - setNow(req, res) { - var data = req.body; - var newNow:moment.Moment = Clock.getMomentFromString(data.now); - - console.log("Mise a jour de la date actuelle. Nous sommes maintenant le", newNow.date()); - Clock.setNow(newNow.valueOf()); - res.send("New 'now' : " + newNow.date()); - } - - - newGoalInstance(req:any, res:any) { - var goalID = req.body.id; - - if (!goalID) { - res.status(400).send({'error': 'goalID field is missing in request'}); - } - - var newChall:Challenge = this.createGoalInstance(goalID, Clock.getMoment(Clock.getNow())); - - if(newChall == null) { - res.send({'error': 'Can not take this challenge'}); - return; - } - - res.send({"success": ("Objectif ajouté !" + newChall.getDataInJSON())}); - } - - createGoalInstance(goalID:string, date:moment.Moment):Challenge { - // TODO ! stub ! - // The data object below is a stub to manually - // bind a symbolic name to a sensor name. - // In the future, this won't be hardcoded but - // will be set by final user during the account - // creation process - - var data = - { - "goal": { - "id": goalID, - "conditions": {"TMP_CLI": "TEMP_443V"} - } - }; - - var goal:Goal = this.goalDefinitionRepository.getGoal(goalID); - - //console.log("Je construit un challenge en partant du principe que nous sommes le ", date.toISOString()); - var goalInstance = this.goalInstanceFactory.createGoalInstance(data, this.goalDefinitionRepository, null, date); - - if(goalInstance.getEndDate().isAfter(goal.getEndDate())) { - return null; - } - - this.goalInstanceRepository.addGoalInstance(goalInstance); - this.userRepository.getCurrentUser().addChallenge(goalInstance.getId()); - return goalInstance; - } - - getAllChallenges(req:any, res:any) { - var result:any[] = []; - - var challenges = this.userRepository.getCurrentUser().getChallenges(); - - for (var currentChallengeIDIndex in challenges) { - var currentChallengeID = challenges[currentChallengeIDIndex]; - var currentChallenge = this.goalInstanceRepository.getGoalInstance(currentChallengeID); - var currentChallengeDesc = currentChallenge.getDataInJSON(); - result.push(currentChallengeDesc); - } - - res.send(result); - } - - deleteGoalInstance(req:any, res:any) { - var goalID = req.params.id; - - try { - this.userRepository.getCurrentUser().deleteChallenge(goalID); - res.send({"success": "Objectif supprimé !"}); - } - catch (e) { - res.send({error: e.toString()}); - } - } - - evaluate(req:any, res:any) { - var goalInstanceID:string = req.query.id; - - var goalInstanceToEvaluate:Challenge = this.goalInstanceRepository.getGoalInstance(goalInstanceID); - - if (!goalInstanceToEvaluate) { - var msg:string = 'Can not find challenge with given id'; - res.status(400).send({error: msg}); - throw new BadRequestException(msg); - } - - res.send(this.evaluateChallenge(goalInstanceToEvaluate, goalInstanceID)); - } - - evaluateAll(req:any, res:any) { - try { - var challenges = this.userRepository.getCurrentUser().getChallenges(); - for (var challenge in challenges) { - var currentChallengeID = challenges[challenge]; - var challengeToEvaluate = this.goalInstanceRepository.getGoalInstance(currentChallengeID); - this.evaluateChallenge(challengeToEvaluate, currentChallengeID); - } - res.send({success: ''}); - } - catch (e) { - res.send(e.toString()); - } - } - - // debug only - private addFinishedBadge(challengeID:string, userID:string) { - /* - console.log('add finished badge'); - console.log('user id : ', userID); - console.log('challenge ID : ', challengeID); - */ - var user = this.userRepository.getUser(userID); - var badgeID = this.goalInstanceRepository.getBadgeByChallengeID(challengeID); - user.addFinishedBadge(badgeID); - user.deleteChallenge(challengeID); - } - - private evaluateChallenge(goalInstanceToEvaluate:Challenge, goalInstanceID) { - var self = this; - - if (!GoalInstanceRouter.DEMO) { - - //TODO move what follow - var required = goalInstanceToEvaluate.getSensors(); - - var requiredSensorName = Object.keys(required); - var numberToLoad:number = requiredSensorName.length; - - for (var currentSensorName in requiredSensorName) { - (function (currentSensorName) { - var startDate:string = '' + required[currentSensorName].startDate; - var endDate:string = '' + required[currentSensorName].endDate; - - var path = 'http://smartcampus.unice.fr/sensors/' + currentSensorName + '/data?date=' + startDate + '/' + endDate; - var dataJsonString = ""; - - this.middleware.getSensorsInfo(required, numberToLoad, dataJsonString, path, - function () { - var result = goalInstanceToEvaluate.evaluate(required); - if (result) { - var newChall = self.createGoalInstance(goalInstanceToEvaluate.getGoalDefinition().getUUID(), goalInstanceToEvaluate.getEndDate()); - this.addFinishedBadge(goalInstanceID, this.userRepository.getCurrentUser().getUUID()); - if (newChall != null) { - self.evaluateChallenge(newChall, newChall.getId()); - } - } - console.log("All data were retrieve properly"); - return goalInstanceToEvaluate.getProgress(); - }, - function () { - return {error: "Error occured in middleware"}; - }); - - })(requiredSensorName[currentSensorName]); - } - } - else { - console.log('++++++++++++++++++++++ \tMODE DEMO\t+++++++++++++++++++++'); - - if(goalInstanceToEvaluate.haveToStart(Clock.getCurrentDemoMoment())) { - goalInstanceToEvaluate.setStatus(ChallengeStatus.RUN); - } - - var result = goalInstanceToEvaluate.evaluate(this.jsonStub); - if (result && goalInstanceToEvaluate.isFinished()) { - //console.log("Le challenge est réussi et terminé"); - var newChall = self.createGoalInstance(goalInstanceToEvaluate.getGoalDefinition().getUUID(), goalInstanceToEvaluate.getEndDate()); - this.addFinishedBadge(goalInstanceID, this.userRepository.getCurrentUser().getUUID()); - if (newChall != null) { - self.evaluateChallenge(newChall, newChall.getId()); - } - } - else if (!result && goalInstanceToEvaluate.isFinished()) { - console.log("Le challenge est FAIL et terminé"); - var newChall = self.createGoalInstance(goalInstanceToEvaluate.getGoalDefinition().getUUID(), goalInstanceToEvaluate.getEndDate()); - - var user = this.userRepository.getCurrentUser(); - user.deleteChallenge(goalInstanceToEvaluate.getId()); - - if (newChall != null) { - self.evaluateChallenge(newChall, newChall.getId()); - } - } - - - - return goalInstanceToEvaluate.getProgress(); - } - } -} - -export = GoalInstanceRouter; \ No newline at end of file diff --git a/backend/src/api/GoalRouter.ts b/backend/src/api/GoalRouter.ts new file mode 100644 index 0000000..d4d479c --- /dev/null +++ b/backend/src/api/GoalRouter.ts @@ -0,0 +1,51 @@ +import RouterItf = require('./RouterItf'); + +import GoalRepository = require('../goal/GoalRepository'); +import GoalFactory = require('../goal/GoalFactory'); +import Context = require('../Context'); + +/** + * GoalDefinitionRouter class
+ * This class handle all the API for + * goal definition class and related. + * + * @class GoalDefinitionRouter + * @extends RouterItf + */ + +class GoalRouter extends RouterItf { + + private goalRepository:GoalRepository; + private goalFactory:GoalFactory; + + + constructor(context:Context) { + super(); + this.goalRepository = context.getGoalRepository(); + this.goalFactory = context.getGoalFactory(); + + } + + buildRouter() { + var self = this; + + this.router.post('/new', function (req, res) { + self.addGoalDefinition(req, res); + }); + } + + addGoalDefinition(req:any, res:any) { + var data = req.body; + try { + var newGoal = this.goalFactory.createGoal(data); + this.goalRepository.addGoal(newGoal); + res.send("OK : définition d'objectif créee avec succès"); + } + catch (e) { + console.log("err", e); + res.send(e.toString()); + } + } +} + +export = GoalRouter; \ No newline at end of file diff --git a/backend/src/api/LoginRouter.ts b/backend/src/api/LoginRouter.ts new file mode 100644 index 0000000..3750fb9 --- /dev/null +++ b/backend/src/api/LoginRouter.ts @@ -0,0 +1,64 @@ +import RouterItf = require('./RouterItf'); + +import UserRepository = require('../user/UserRepository'); +import User = require('../user/User'); +import Context = require('../Context'); +import BadRequestException = require('../exceptions/BadRequestException'); +import BadArgumentException = require('../exceptions/BadArgumentException'); + +class LoginRouter extends RouterItf { + private userRepository:UserRepository; + + constructor(context:Context){ + super(); + this.userRepository = context.getUserRepository(); + } + + buildRouter() { + var self = this; + this.router.get('/', function (req, res) { + res.send({}); + }); + + this.router.post('/', function (req, res) { + var userProfile:User = null; + + try { + userProfile = self.checkUserProfile(req.body); + res.send({success:'User profile was found', data:{token:userProfile.getUUID()}}); + } + catch(e) { + if(e instanceof BadRequestException) { + res.status(400).send({error:e.getMessage()}); + + } + else if(e instanceof BadArgumentException) { + res.status(400).send({error:e.getMessage()}); + + } + else { + res.status(500).send({error:e.getMessage()}); + } + } + + }); + } + + checkUserProfile(data):User { + console.log('Data received ', data); + console.log('Login :', data.username); + + if(!data.username) { + throw new BadRequestException('Field username is missing in request'); + } + + var currentUser:User = this.userRepository.getUserByName(data.username); + if(currentUser == null) { + throw new BadArgumentException('Given username can not be found'); + } + + return currentUser; + } +} + +export = LoginRouter; \ No newline at end of file diff --git a/backend/src/api/RouterItf.ts b/backend/src/api/RouterItf.ts index a7799d8..d6a0c3c 100644 --- a/backend/src/api/RouterItf.ts +++ b/backend/src/api/RouterItf.ts @@ -3,7 +3,7 @@ */ /// -var express : any = require("express"); +var express:any = require("express"); /** * Router Interface @@ -18,7 +18,7 @@ class RouterItf { * @property router * @type any */ - router : any; + router:any; /** * Constructor. @@ -52,6 +52,8 @@ class RouterItf { buildRouter() { //Logger.warn("RouterItf - buildRouter : Method need to be implemented."); } + + } export = RouterItf; \ No newline at end of file diff --git a/backend/src/challenge/Challenge.ts b/backend/src/challenge/Challenge.ts deleted file mode 100644 index 750fedd..0000000 --- a/backend/src/challenge/Challenge.ts +++ /dev/null @@ -1,242 +0,0 @@ -/// -/// -/// - -var moment = require('moment'); -var moment_timezone = require('moment-timezone'); - -import UUID = require('node-uuid'); - -import Goal = require('../goal/Goal'); -import Badge = require('../badge/Badge'); -import User = require('../user/User'); -import BadgeStatus = require('../Status'); -import TimeBox = require('../TimeBox'); -import Clock = require('../Clock'); - -class Challenge { - private id:string; - private goalDefinition:Goal; - - private startDate:moment.Moment; - private endDate:moment.Moment; - - private description:string; - private status:BadgeStatus; - - private progress:any[] = []; - - private percentageOfTime:number = 0; - - // { 'tmp_cli':'ac_443', 'tmp_ext':'TEMP_444', 'door_o':'D_55', ... } - private mapSymbolicNameToSensor:any = {}; - - constructor(startDate:moment.Moment, endDate:moment.Moment, description:string, goal:Goal, - mapGoalToConditionAndSensor:any, id = null) { - - //onsole.log("Building the challenge with startdate", startDate.format(), "and enddate", endDate.format()); - - this.id = (id) ? id : UUID.v4(); - this.description = description; - - this.startDate = startDate; - this.endDate = endDate; - - this.goalDefinition = goal; - this.goalDefinition.setTimeBoxes(new TimeBox(startDate, endDate)); - - this.mapSymbolicNameToSensor = mapGoalToConditionAndSensor; - - this.status = BadgeStatus.RUN; - } - - public updateDurationAchieved(currentDate:number) { - var duration:number = this.endDate.valueOf() - this.startDate.valueOf(); - var current:moment.Moment = Clock.getMoment(currentDate); - if (current.isBefore(this.startDate)) { - throw new Error('Time given is before dateOfCreation !'); - } - var durationAchieved:number = current.valueOf() - this.startDate.valueOf(); - - this.percentageOfTime = durationAchieved * 100 / duration; - // It can have tiny incorrect decimal values - this.percentageOfTime = (this.percentageOfTime > 100) ? 100 : this.percentageOfTime; - } - - isFinished():boolean { - return this.getTimeProgress() >= 100; - } - - public getTimeProgress():number { - return this.percentageOfTime; - } - - public resetProgress() { - this.progress = []; - } - - public addProgress(progressDescription:any) { - this.progress.push(progressDescription); - } - - public getStartDate():moment.Moment { - return this.startDate; - } - - public getEndDate():moment.Moment { - return this.endDate; - } - - public getDescription():string { - return this.description; - } - - public getGoalDefinition():Goal { - return this.goalDefinition; - } - - public getBadge():string { - return this.goalDefinition.getBadgeID(); - } - - public getName():string { - return this.goalDefinition.getName(); - } - - public getId():string { - return this.id; - } - - public hasUUID(aUUID:string):boolean { - return this.id === aUUID; - } - - public getProgress():any { - console.log("PROGRESS", JSON.stringify(this.progress)); - return this.progress; - } - - public getStatus():BadgeStatus { - return this.status; - } - - public hasStatus(badgeStatus:BadgeStatus):boolean { - return this.status === badgeStatus; - } - - public setStatus(badgeStatus:BadgeStatus) { - this.status = badgeStatus; - } - - public getSensors():any { - - var result:any = {}; - - for (var currentSymbolicName in this.mapSymbolicNameToSensor) { - var currentSensor = this.mapSymbolicNameToSensor[currentSymbolicName]; - result[currentSensor] = this.goalDefinition.getRequired()[currentSymbolicName]; - } - - return result; - } - - setGoal(goal) { - this.goalDefinition = goal; - } - - public haveToStart(now:moment.Moment):boolean { - return now.isAfter(this.startDate) && now.isBefore(this.endDate); - } - - /** - * - * @param values - * - * - describing a required of a condition - * { - * 'name' : - symbolic name of the required field, eg : 'Temp_cli', - * 'sensor' : 'sensor_id ', - sensor id bound to achieve current goal condition, eg : 'AC_443', - * 'value' : - current value of specified sensor - * } - * @returns {boolean} - */ - public evaluate(values:any):boolean { - console.log('evaluate de challenge'); - // Check if badge is running. If Waiting or failed, it must be left unchanged - if(this.status != BadgeStatus.RUN) { - return false; - } - - this.resetProgress(); - - this.updateDurationAchieved(Clock.getNow()); - var numberOfValues = Object.keys(values).length; - var numberOfValuesNeeded = Object.keys(this.mapSymbolicNameToSensor).length; - - if (numberOfValues < numberOfValuesNeeded) { - throw new Error("Can not evaluate goal " + this.goalDefinition.getName() - + "! There are " + numberOfValuesNeeded + " symbolic names needed and only " - + numberOfValues + " values given"); - } - - var mapSymbolicNameToValue = this.bindSymbolicNameToValue(values); - - var resultEval = this.goalDefinition.evaluate(mapSymbolicNameToValue, this); - - if (resultEval && this.percentageOfTime >= 100) { - this.status = BadgeStatus.SUCCESS; - console.log('success!'); - return true; - } else if (this.percentageOfTime >= 100) { - this.status = BadgeStatus.FAIL; - console.log('Fail!'); - } else { - console.log('run'); - this.status = BadgeStatus.RUN; - } - return false; - } - - public bindSymbolicNameToValue(mapSensorToValue:any) { - var result:any = {}; - - for (var currentSymbolicName in this.mapSymbolicNameToSensor) { - var currentSensorName = this.mapSymbolicNameToSensor[currentSymbolicName]; - var currentSensorToValue = mapSensorToValue[currentSensorName]; - - result[currentSymbolicName] = currentSensorToValue; - } - - return result; - } - - public getDataInJSON():any { - console.log('time progress : ', this.percentageOfTime); - return { - id: this.id, - name: this.getName(), - timeProgress: this.percentageOfTime, - startDate: this.startDate, - endDate: this.endDate, - goal: { - id: this.goalDefinition.getUUID(), - conditions: this.mapSymbolicNameToSensor - }, - progress: this.progress, - status: this.getStatusAsString() - } - } - - - private getStatusAsString():string { - switch(this.status){ - case 0: return 'WAIT'; break; - case 1: return 'RUNNING'; break; - case 2: return 'SUCCESS'; break; - case 3: return 'FAIL'; break; - default: return 'UNKNOWN'; break; - } - } -} - -export = Challenge; diff --git a/backend/src/challenge/ChallengeFactory.ts b/backend/src/challenge/ChallengeFactory.ts deleted file mode 100644 index 78d4924..0000000 --- a/backend/src/challenge/ChallengeFactory.ts +++ /dev/null @@ -1,149 +0,0 @@ -/// -/// -/// - -import Challenge = require('./Challenge'); -import Goal = require('../goal/Goal'); -import GoalRepository = require('../goal/GoalRepository'); -import UserRepository = require('../user/UserRepository'); -import TimeBoxFactory = require('../TimeBoxFactory'); -import TimeBox = require('../TimeBox'); -import ChallengeStatus = require('../Status'); -import Clock = require('../Clock'); - -var moment = require('moment'); -var moment_timezone = require('moment-timezone'); - -class GoalInstanceFactory { - - /** - * - * @param data - * { - * description : , - * timeBox - * goal : - * { - * id : , - * conditions : - * { - * : - * } - * } - * } - * @param goalRepository - * @param userProvider - * @returns {Challenge} - */ - public createGoalInstance(data:any, goalRepository:GoalRepository, userProvider:UserRepository, now:moment.Moment):Challenge { - - var challengeID = data.id; - - var challengeDescription:string = data.description; - - var goalJSONDesc:any = data.goal; - var goalID = goalJSONDesc.id; - var goal:Goal = goalRepository.getGoal(goalID); - - if (goal == null) { - throw new Error('Can not create goal instance because ID given of goal definition can not be found'); - } - - // Check if challenge is built from db (startDate and endDate are provided in data parameter) - // Or if challengeFactory was called from a 'newChallenge' method - - var nowDate:moment.Moment = now; - var mapGoalsToConditionAndSensors:any = goalJSONDesc.conditions; - - // We restore it from DB - if (data.startDate != null) { - return this.restoreChallenge(challengeID, data, goal, challengeDescription, goalRepository, mapGoalsToConditionAndSensors, now); - } - - // The user just took a new challenge - - var clone = now.clone(); - //console.log("NOW?", clone.format()); - - var startDate = goal.getStartDateOfSession(clone); - var endDate = goal.getEndDateOfSession(clone.clone()); - - /* - console.log("START DATE OF SESSION", startDate.format()); - console.log("START DATE OF SESSION", startDate.toISOString()); - - console.log("END DATE OF SESSION", endDate.format()); - console.log("END DATE OF SESSION", endDate.toISOString()); - */ - - /* - if(!this.checkDates(goalDefinition,startDate)) { - throw new Error('Can not build goal instance ! it does not have the time to be achieved. We are the ' - + now + ', the goal start the' + goalDefinition.getStartDate() + ' and end the ' +goalDefinition.getEndDate() + ' with a duration of ' + goalDefinition.getDuration() + ' days'); - } - */ - - - var challenge:Challenge = new Challenge(startDate, endDate, challengeDescription, goal, mapGoalsToConditionAndSensors, challengeID); - - - if(now.isBefore(startDate)) { - //console.log("Le challenge est en WAIT"); - challenge.setStatus(ChallengeStatus.WAIT); - } - if(now.isAfter(startDate) && now.isBefore(endDate)) { - ///console.log("Le challenge est en RUN"); - challenge.setStatus(ChallengeStatus.RUN); - } - - // TODO attach badge to user - // user.addBadgeByDescription(badge); - - // console.log("L'objectif ", goalDefinition.getName(), "a ete instancie ! Intervalle de vie de l'objectif : du", startDate, "au", endDate); - - return challenge; - } - - private restoreChallenge(id, data:any, goalDefinition:Goal, goalInstanceDescription:string, goalRepository:GoalRepository, mapGoalsToConditionAndSensors, now:moment.Moment):Challenge{ - //console.log("RESTORE CHALLENGE"); - - var startDateDesc = data.startDate; - var endDateDesc = data.endDate; - - var startDate = Clock.getMoment(startDateDesc); - var endDate = Clock.getMoment(endDateDesc); - - var challenge:Challenge = new Challenge(startDate, endDate, goalInstanceDescription, goalDefinition, mapGoalsToConditionAndSensors, id); - - if(now.isBefore(startDate)) { - //console.log("Le challenge est en WAIT"); - challenge.setStatus(ChallengeStatus.WAIT); - } - if(now.isAfter(startDate) && now.isBefore(endDate)) { - ///console.log("Le challenge est en RUN"); - challenge.setStatus(ChallengeStatus.RUN); - } - - return challenge; - } - - private realignStartAndEndDates(goal:Goal, now:moment.Moment) { - - } - - /** - * Check if the goal instance is started today, Date.now + goalDefinition#duration <= goalDefinition#endDate - * @param goalDefinition - */ - public checkDates(goalDefinition:Goal, startDate:moment.Moment):boolean { - var durationInDays:number = goalDefinition.getDuration(); - - var endDate:moment.Moment = Clock.getMoment(0).year(startDate.year()).month(startDate.month()).date(startDate.date() + durationInDays); - var endDateOfValidityPeriod:moment.Moment = goalDefinition.getEndDate(); - - return endDate.isBefore(endDateOfValidityPeriod); - } - -} - -export = GoalInstanceFactory; \ No newline at end of file diff --git a/backend/src/challenge/ChallengeRepository.ts b/backend/src/challenge/ChallengeRepository.ts deleted file mode 100644 index de58190..0000000 --- a/backend/src/challenge/ChallengeRepository.ts +++ /dev/null @@ -1,159 +0,0 @@ -import Challenge = require('./Challenge'); -import ChallengeFactory = require('./ChallengeFactory'); -import GoalRepository = require('../goal/GoalRepository'); -import UserProvider = require('../user/UserRepository'); -import Badge = require('../badge/Badge'); - - -class BadgeProvider { - - private goalInstancesArray:Challenge[] = []; - - private factory:ChallengeFactory; - - constructor() { - this.factory = new ChallengeFactory(); - } - - public displayShortState() { - console.log("\n\n+++\t Etat du repository des defis\t+++"); - - for(var currentChallengeIndex in this.goalInstancesArray) { - var currentChallenge = this.goalInstancesArray[currentChallengeIndex]; - console.log("#",currentChallenge.getId(),"\t |\tDefi : '", currentChallenge.getName(), "'") - } - } - - public getBadgeByChallengeID(challengeID:string):string { - return this.getGoalInstance(challengeID).getBadge(); - } - - public addGoalInstance(aChallenge:Challenge) { - this.goalInstancesArray.push(aChallenge); - } - - public getGoalInstance(challengeID:string):Challenge { - for (var i in this.goalInstancesArray) { - var currentBadge = this.goalInstancesArray[i]; - if (currentBadge.hasUUID(challengeID)) { - return currentBadge; - } - } - - return null; - } - - public getListOfGoalInstancesInJsonFormat():any[] { - var result = []; - for (var i in this.goalInstancesArray) { - var currentGoalInstanceDesc:any = {}; - currentGoalInstanceDesc.name = this.goalInstancesArray[i].getName(); - currentGoalInstanceDesc.id = this.goalInstancesArray[i].getId(); - result.push(currentGoalInstanceDesc); - } - - return result; - } - - public getGoalInstancesDescriptionInJsonFormat(datastub:any = null):any[] { - var result = []; - - for (var i in this.goalInstancesArray) { - - this.goalInstancesArray[i].evaluate(datastub); - - var currentBadgeDesc:any = {}; - currentBadgeDesc.name = this.goalInstancesArray[i].getName(); - currentBadgeDesc.id = this.goalInstancesArray[i].getId(); - currentBadgeDesc.desc = this.goalInstancesArray[i].getDescription(); - currentBadgeDesc.progress = this.goalInstancesArray[i].getProgress(); - - currentBadgeDesc.startDate = this.goalInstancesArray[i].getStartDate(); - currentBadgeDesc.endDate = this.goalInstancesArray[i].getEndDate(); - - currentBadgeDesc.timeProgress = this.goalInstancesArray[i].getTimeProgress(); - - - var statusDesc:string = ''; - var badgeStatus:number = this.goalInstancesArray[i].getStatus(); - - statusDesc = this.getBadgeStatus(badgeStatus); - - currentBadgeDesc.status = statusDesc; - if (currentBadgeDesc.status != 'SUCCESS') { - result.push(currentBadgeDesc); - } - } - - return result; - } - - public getFinishedGoalInstances():Challenge[] { - console.log('get finished goal'); - var goalFinished:Challenge[] = []; - for (var i in this.goalInstancesArray) { - var statusDesc = this.getBadgeStatus(this.goalInstancesArray[i].getStatus()); - if (statusDesc === 'SUCCESS') { - console.log('---Find one', this.goalInstancesArray[i]); - goalFinished.push(this.goalInstancesArray[i]); - this.goalInstancesArray.splice(i, 1); - } - } - - return goalFinished; - } - - public removeUselessGoalInstances() { - for (var i in this.goalInstancesArray) { - var statusDesc = this.getBadgeStatus(this.goalInstancesArray[i].getStatus()); - if (statusDesc === 'SUCCESS') { - this.goalInstancesArray.splice(i, 1); - } - } - } - - public removeGoalInstance(goalInstanceUuid:string) { - for (var i in this.goalInstancesArray) { - console.log('searching for the correct goal instance'); - var currentBadge = this.goalInstancesArray[i]; - if (currentBadge.hasUUID(goalInstanceUuid)) { - console.log('goal instance found'); - this.goalInstancesArray.splice(i, 1); - console.log('removed : ', this.goalInstancesArray); - break; - } - } - } - - private getBadgeStatus(badgeStatus:number):string { - switch (badgeStatus) { - case 0: - return 'WAIT'; - case 1: - return 'RUNNING'; - break; - case 2: - return 'SUCCESS'; - break; - case 3: - return 'FAIL'; - break; - default: - return 'UNKNOWN'; - break; - } - return 'UNKNOWN' - } - - public getDataInJSON():any { - var result:any[] = []; - - for(var currentChallengeIndex in this.goalInstancesArray) { - result.push(this.goalInstancesArray[currentChallengeIndex].getDataInJSON()); - } - - return result; - } -} - -export = BadgeProvider; \ No newline at end of file diff --git a/backend/src/challenge/TeamChallenge.ts b/backend/src/challenge/TeamChallenge.ts new file mode 100644 index 0000000..e661e36 --- /dev/null +++ b/backend/src/challenge/TeamChallenge.ts @@ -0,0 +1,268 @@ +/// +/// + +import uuid = require('node-uuid'); +var merge = require('merge'); + +var moment = require('moment'); +var moment_timezone = require('moment-timezone'); + +import Status= require('../Status'); +import UserChallenge= require('./UserChallenge'); +import UserChallengeRepository= require('./UserChallengeRepository'); +import Team= require('../user/Team'); +import BadArgumentException = require('../exceptions/BadArgumentException'); + +class TeamChallenge { + + private id:string; + + private team:Team; + private childChallenges:UserChallenge[]; + private userChallengeRepository:UserChallengeRepository; + + private status:Status; + + private startDate:moment.Moment; + private endDate:moment.Moment; + + private durationAchieved:number; + private progress:any = {}; + + constructor(id, team:Team, childChallenges:UserChallenge[], userChallengeRepository:UserChallengeRepository) { + if (childChallenges.length == 0) { + throw new BadArgumentException('Can not build team challenge because there is no child challenges to attach'); + } + + this.id = id; + + this.team = team; + this.childChallenges = childChallenges; + this.userChallengeRepository = userChallengeRepository; + + this.status = this.getStatus(); + this.checkChildrenTimeBoxes(); + } + + getChildUserChallengeByID(userChallengeID:string):UserChallenge { + for(var currentChildIndex in this.childChallenges) { + var currentChild = this.childChallenges[currentChildIndex]; + if(currentChild.getID() == userChallengeID) { + return currentChild; + } + } + + return null; + } + + getID():string { + return this.id; + } + + hasUUID(uuid:string) { + return this.id == uuid; + } + + getName():string { + return this.childChallenges[0].getName(); + } + + getTeam():Team { + return this.team; + } + + getBadge() { + return this.childChallenges[0].getBadge(); + } + + setStatus(status) { + this.status = status; + } + + getGoal() { + return this.childChallenges[0].getGoal(); + } + + getStartDate() { + return this.childChallenges[0].getStartDate(); + } + + getEndDate() { + return this.childChallenges[0].getEndDate(); + } + + haveToStart(now):boolean { + return this.childChallenges[0].haveToStart(now); + } + + removeFromMembers() { + for(var currentUserChallengeIndex in this.childChallenges) { + var currentUserChallenge:UserChallenge = this.childChallenges[currentUserChallengeIndex]; + currentUserChallenge.removeFromInternalUser(); + } + } + + getStatus():Status { + var succeed:boolean = true; + var stillRunning:boolean = true; + + for (var currentChildIndex in this.childChallenges) { + var currentChild = this.childChallenges[currentChildIndex]; + var currentChildStatus = currentChild.getStatus(); + + console.log("Current child status", currentChild.getStatusAsString()); + + if(currentChildStatus == Status.WAIT) { + return Status.WAIT; + } + + stillRunning = stillRunning && (currentChildStatus == Status.RUN); + if (stillRunning) { + return Status.RUN; + } + + succeed = succeed && (currentChildStatus == Status.SUCCESS); + if (!succeed) { + return Status.FAIL; + } + } + + return Status.SUCCESS; + } + + checkChildrenTimeBoxes() { + var startDate:moment.Moment = this.childChallenges[0].getStartDate(); + var endDate:moment.Moment = this.childChallenges[0].getEndDate(); + + for (var currentChildIndex in this.childChallenges) { + var currentChild = this.childChallenges[currentChildIndex]; + + var startDateOfCurrentChild = currentChild.getStartDate(); + if (startDate.toISOString() != startDateOfCurrentChild.toISOString()) { + throw new BadArgumentException('Can not build team challenge ! Some child challenge does not have the same start date as the other'); + } + + var endDateOfCurrentChild = currentChild.getEndDate(); + if (endDate.toISOString() != endDateOfCurrentChild.toISOString()) { + throw new BadArgumentException('Can not build team challenge ! Some child challenge does not have the same end date as the other'); + } + } + + this.startDate = startDate; + this.endDate = endDate; + } + + evaluate(data):void { + + var childProgress:number = 0; + var achieved:boolean = false; + var finished:boolean = false; + + var childProgressDescription:any[] = []; + + for (var currentChildIndex in this.childChallenges) { + var currentChild:UserChallenge = this.childChallenges[currentChildIndex]; + + var childResult = currentChild.evaluate(data); + + console.log("SON RESULT\n", childResult, '\n\n'); + achieved = achieved && childResult.achieved; + + var currentChildGlobalProgression:number = childResult.percentageAchieved; + childProgress += currentChildGlobalProgression; + + var currentChildProgressionDescription:any = { + description: currentChild.getUser().getName(), + percentageAchieved: currentChildGlobalProgression, + achieved: childResult.achieved + }; + + childProgressDescription.push(currentChildProgressionDescription); + + } + this.durationAchieved = this.childChallenges[0].getTimeProgress(); + var percentageAchieved = childProgress / this.childChallenges.length; + this.progress['percentageAchieved'] = percentageAchieved; + this.progress['durationAchieved'] = this.durationAchieved; + this.progress['finished'] = this.durationAchieved == 100; + this.progress['achieved'] = achieved || percentageAchieved == 100; + this.progress['conditions'] = childProgressDescription; + this.progress['status'] = this.getStatusAsString(); + + return this.progress; + } + + getSensors() { + /* + Precisions : + We can not assume that a specific sensor is bound to a specific user + since two users can share an office. + But we can assume that for a given challenge, sensor of each user + have the same timeBox and will be identical. + Because of this, we can simply merge required sensors and not + try to merge different timeBoxes or whatever. + */ + + var result:any = {}; + + for (var currentChildIndex in this.childChallenges) { + var currentChild:UserChallenge = this.childChallenges[currentChildIndex]; + result = merge(result, currentChild.getSensors()); + } + + return result; + } + + getStatusAsString():string { + switch (this.getStatus()) { + case 0: + return 'WAIT'; + break; + case 1: + return 'RUNNING'; + break; + case 2: + return 'SUCCESS'; + break; + case 3: + return 'FAIL'; + break; + default: + return 'UNKNOWN'; + break; + } + } + + getDataInJSON():any { + var childrenIDs:string[] = []; + for (var currentChildrenIndex in this.childChallenges) { + var currentChild:UserChallenge = this.childChallenges[currentChildrenIndex]; + + var currentChildID:string = currentChild.getID(); + childrenIDs.push(currentChildID); + } + + return { + id: this.id, + team: this.team.getUUID(), + children: childrenIDs + } + + } + + + getDataForClient():any { + return { + id: this.id, + type: 'team', + startDate: this.startDate, + endDate: this.endDate, + goal: this.childChallenges[0].getGoal().getName(), + user: this.getName(), + progress: this.progress, + status: this.getStatusAsString() + } + } +} + +export = TeamChallenge; \ No newline at end of file diff --git a/backend/src/challenge/TeamChallengeFactory.ts b/backend/src/challenge/TeamChallengeFactory.ts new file mode 100644 index 0000000..3a914bf --- /dev/null +++ b/backend/src/challenge/TeamChallengeFactory.ts @@ -0,0 +1,58 @@ +import Team = require('../user/Team'); +import User = require('../user/User'); +import Goal = require('../goal/Goal'); +import UserChallenge = require('../challenge/UserChallenge'); +import TeamChallenge = require('../challenge/TeamChallenge'); +import UserChallengeRepository = require('../challenge/UserChallengeRepository'); +import TeamRepository = require('../user/TeamRepository'); +import GoalRepository = require('../goal/GoalRepository'); +import uuid = require('node-uuid'); + +class TeamChallengeFactory { + createTeamChallenge(team:Team, goal:Goal, userChallengeRepository:UserChallengeRepository, now) { + + var membersChallenges:UserChallenge[] = []; + + var members:User[] = team.getMembers(); + + console.log("Team's members", team.getStringDescriptionOfMembers()); + + for(var currentMemberIndex in members) { + var currentMember:User = members[currentMemberIndex]; + + var currentUserChallenge:UserChallenge = currentMember.addChallenge(goal, now, team.getName()); + + userChallengeRepository.addUserChallenge(currentUserChallenge); + + membersChallenges.push(currentUserChallenge); + + console.log("Le user", currentMember.getName(), "has this challenge added", currentUserChallenge.getID()); + } + var id = uuid.v4(); + + return new TeamChallenge(id, team, membersChallenges, userChallengeRepository); + } + + restoreTeamChallenge(data, teamRepository:TeamRepository, goalRepository:GoalRepository, userChallengeRepository:UserChallengeRepository,now):TeamChallenge { + + var id:string = data.id; + var teamID:string = data.team; + var team:Team = teamRepository.getTeam(teamID); + + var childrenIDs:string[] = data.children; + var children:UserChallenge[] = []; + + for(var currentChildIDIndex in childrenIDs) { + var currentChildID = childrenIDs[currentChildIDIndex]; + var currentChild = userChallengeRepository.getChallengeByID(currentChildID); + currentChild.setTakenBy(team.getName()); + + children.push(currentChild); + } + + + return new TeamChallenge(id, team, children, userChallengeRepository); + } +} + +export = TeamChallengeFactory \ No newline at end of file diff --git a/backend/src/challenge/TeamChallengeRepository.ts b/backend/src/challenge/TeamChallengeRepository.ts new file mode 100644 index 0000000..963755a --- /dev/null +++ b/backend/src/challenge/TeamChallengeRepository.ts @@ -0,0 +1,69 @@ + +import TeamChallenge = require('./TeamChallenge'); +import TeamChallengeFactory = require('./TeamChallengeFactory'); +import GoalRepository = require('../goal/GoalRepository'); +import UserRepository = require('../user/UserRepository'); +import Badge = require('../badge/Badge'); + + +class TeamChallengeRepository { + + private teamChallengesArray:TeamChallenge[] = []; + private factory:TeamChallengeFactory; + + constructor() { + this.factory = new TeamChallengeFactory(); + } + + + addTeamChallenge(aChallenge:TeamChallenge) { + this.teamChallengesArray.push(aChallenge); + } + + getChallengeByID(challengeID:string):TeamChallenge { + for (var i in this.teamChallengesArray) { + var currentChallenge = this.teamChallengesArray[i]; + if (currentChallenge.hasUUID(challengeID)) { + return currentChallenge; + } + } + + return null; + } + + getTeamChallengeFromUserChallengeID(userChallengeID:string):TeamChallenge { + for (var i in this.teamChallengesArray) { + var currentChallenge = this.teamChallengesArray[i]; + if (currentChallenge.getChildUserChallengeByID(userChallengeID) != null) { + return currentChallenge; + } + } + + return null; + } + + getBadgeByChallengeID(challengeID:string):string { + return this.getChallengeByID(challengeID).getBadge(); + } + + displayShortState() { + console.log("\n\n+++\t Etat du repository des defis\t+++"); + + for (var currentChallengeIndex in this.teamChallengesArray) { + var currentChallenge = this.teamChallengesArray[currentChallengeIndex]; + console.log("#", currentChallenge.getID(), "\t |\tDefi : '", currentChallenge.getName(), "'") + } + } + + getDataInJSON():any { + var result:any[] = []; + + for (var currentChallengeIndex in this.teamChallengesArray) { + result.push(this.teamChallengesArray[currentChallengeIndex].getDataInJSON()); + } + + return result; + } +} + +export = TeamChallengeRepository; \ No newline at end of file diff --git a/backend/src/challenge/UserChallenge.ts b/backend/src/challenge/UserChallenge.ts new file mode 100644 index 0000000..144b592 --- /dev/null +++ b/backend/src/challenge/UserChallenge.ts @@ -0,0 +1,358 @@ +/// +/// +/// + +var moment = require('moment'); +var moment_timezone = require('moment-timezone'); + +import Goal = require('../goal/Goal'); +import Badge = require('../badge/Badge'); +import User = require('../user/User'); +import BadgeStatus = require('../Status'); +import TimeBox = require('../TimeBox'); +import Clock = require('../Clock'); + +class UserChallenge { + private id:string; + private goal:Goal; + + private startDate:moment.Moment; + private endDate:moment.Moment; + + private status:BadgeStatus; + + private progress:any = {}; + + // { A_CONDITION_ID : { symbolic_name: tmp_cli, timeBox: { startDate:..., endDate } } } + private mapConditionIDToSensorAndTimeBoxRequired:any = {}; + + + // { 'tmp_cli':'ac_443', 'tmp_ext':'TEMP_444', 'door_o':'D_55', ... } + private mapSymbolicNameToSensor:any = {}; + private user:User; + + private takenBy:string; + + constructor(id:string, goal:Goal, user:User, startDate:moment.Moment, endDate:moment.Moment, mapConditionIDToSensorAndTimeBoxRequired:any, takenBy = null) { + + this.id = id; + + this.startDate = startDate; + this.endDate = endDate; + + this.goal = goal; + + this.user = user; + this.takenBy = (takenBy == null) ? this.user.getName() : takenBy; + + this.mapConditionIDToSensorAndTimeBoxRequired = mapConditionIDToSensorAndTimeBoxRequired; + this.mapSymbolicNameToSensor = this.user.getMapSymbolicNameToSensor(); + + this.buildRequired(); + + } + + getConditionDescriptionByID(conditionID:string) { + return this.mapConditionIDToSensorAndTimeBoxRequired[conditionID]; + } + + public updateDurationAchieved(currentDate:number):number { + + var current:moment.Moment = Clock.getMoment(currentDate); + + if (current.isBefore(this.startDate)) { + throw new Error('Time given is before dateOfCreation !'); + } + + var duration:number = this.endDate.valueOf() - this.startDate.valueOf(); + var durationAchieved:number = current.valueOf() - this.startDate.valueOf(); + var percentageOfTime = durationAchieved * 100 / duration; + + // It can have tiny incorrect decimal values + percentageOfTime = (percentageOfTime > 100) ? 100 : percentageOfTime; + + return percentageOfTime; + } + + getUser():User { + return this.user; + } + + getStartDate():moment.Moment { + return this.startDate; + } + + getEndDate():moment.Moment { + return this.endDate; + } + + getGoal():Goal { + return this.goal; + } + + getBadge():string { + return this.goal.getBadgeID(); + } + + getName():string { + return this.goal.getName(); + } + + getID():string { + return this.id; + } + + hasUUID(aUUID:string):boolean { + return this.id === aUUID; + } + + + getStatus():BadgeStatus { + return this.status; + } + + hasStatus(badgeStatus:BadgeStatus):boolean { + return this.status === badgeStatus; + } + + setStatus(badgeStatus:BadgeStatus) { + this.status = badgeStatus; + } + + removeFromInternalUser() { + this.user.deleteChallenge(this.getID()); + } + + getSensors():any { + + var required:any = {}; + + for (var conditionID in this.mapConditionIDToSensorAndTimeBoxRequired) { + var currentConditionDescription = this.mapConditionIDToSensorAndTimeBoxRequired[conditionID]; + var sensorsOfCurrentCondition = currentConditionDescription.sensors; + + for(var currentSensorIndex in sensorsOfCurrentCondition) { + var currentSensor:string = sensorsOfCurrentCondition[currentSensorIndex]; + + if(required[currentSensor] == null) { + required[currentSensor] = currentConditionDescription.timeBox; + } + else { + var oldTimeBox = required[currentSensor]; + var oldStartDate:moment.Moment = oldTimeBox.start; + var oldEndDate:moment.Moment = oldTimeBox.end; + + var currentTimeBox = currentConditionDescription.timeBox; + var currentStartDate:moment.Moment = currentTimeBox.start; + var currentEndDate:moment.Moment = currentTimeBox.end; + + var newStartDate:moment.Moment = null; + var newEndDate:moment.Moment = null; + + if(currentStartDate.isBefore(oldStartDate)) { + newStartDate = currentStartDate; + } + else { + newStartDate = oldStartDate; + } + + if(currentEndDate.isAfter(oldEndDate)) { + newEndDate = currentEndDate; + } + else { + newEndDate = oldEndDate; + } + + var newTimeBox:any = { + start:newStartDate, + end:newEndDate + }; + + required[currentSensor] = newTimeBox; + } + } + + } + + return this.mapConditionIDToSensorAndTimeBoxRequired; + } + + private buildRequired():any { + + for (var conditionID in this.mapConditionIDToSensorAndTimeBoxRequired) { + var sensors:string[] = []; + + var currentConditionDescription = this.mapConditionIDToSensorAndTimeBoxRequired[conditionID]; + var symbolicNames:string[] = currentConditionDescription.symbolicNames; + + for (var symbolicNamesIndex in symbolicNames) { + var currentSymbolicName = symbolicNames[symbolicNamesIndex]; + var currentSensor = this.mapSymbolicNameToSensor[currentSymbolicName]; + sensors.push(currentSensor); + currentConditionDescription[currentSensor] = {}; + } + + currentConditionDescription.sensors = sensors; + } + + return this.mapConditionIDToSensorAndTimeBoxRequired; + } + + setGoal(goal) { + this.goal = goal; + } + + haveToStart(now:moment.Moment):boolean { + return now.isAfter(this.startDate) && now.isBefore(this.endDate); + } + + retrieveSymbolicNameFromSensor(sensorName:string):string { + for(var currentSymbolicName in this.mapSymbolicNameToSensor) { + + var sensorNameBound = this.mapSymbolicNameToSensor[currentSymbolicName]; + + if(sensorNameBound == sensorName) { + return currentSymbolicName; + } + } + + return null; + } + + /** + * + * @param values + * { + * : [ { date:"time in millis", value:__}, ... ] + * } + * @returns {any} + */ + evaluate(values:any):any { + + + for (var conditionID in this.mapConditionIDToSensorAndTimeBoxRequired) { + var currentConditionDescription = this.mapConditionIDToSensorAndTimeBoxRequired[conditionID]; + + currentConditionDescription['values'] = {}; + + var sensorsRequired = currentConditionDescription.sensors; + for(var currentSensorRequiredIndex in sensorsRequired) { + var currentSensorRequired = sensorsRequired[currentSensorRequiredIndex]; + var symbolicNameBound = this.retrieveSymbolicNameFromSensor(currentSensorRequired); + currentConditionDescription['values'][symbolicNameBound] = values[currentSensorRequired]; + } + + } + + // Check if badge is running. If Waiting or failed, it must be left unchanged + if (this.status != BadgeStatus.RUN) { + return false; + } + + var resultEval = this.goal.evaluate(this.mapConditionIDToSensorAndTimeBoxRequired, this); + + + var durationAchieved:number = this.updateDurationAchieved(Clock.getNow()); + resultEval['durationAchieved'] = durationAchieved; + + var finished:boolean = (durationAchieved === 100) ? true : false; + resultEval['finished'] = finished; + + var achieved:boolean = resultEval['achieved']; + + if (achieved && finished) { + this.status = BadgeStatus.SUCCESS; + console.log('success!'); + } else if (!achieved && finished) { + this.status = BadgeStatus.FAIL; + console.log('Fail!'); + } else { + console.log('run'); + this.status = BadgeStatus.RUN; + } + + this.progress = resultEval; + console.log('Résultat de l\'évaluation du challenge : achieved', resultEval.achieved, 'finished', resultEval.finished); + + return resultEval; + } + getGlobalProgression():any { + return this.progress; + } + + getTimeProgress():any { + return this.progress['durationAchieved']; + } + + bindSymbolicNameToValue(mapSensorToValue:any) { + var result:any = {}; + + for (var currentSymbolicName in this.mapSymbolicNameToSensor) { + var currentSensorName = this.mapSymbolicNameToSensor[currentSymbolicName]; + var currentSensorToValue = mapSensorToValue[currentSensorName]; + + result[currentSymbolicName] = currentSensorToValue; + } + + return result; + } + + isAPersonalChallenge():boolean { + return this.takenBy == this.getUser().getName(); + } + + getDataInJSON():any { + return { + id: this.id, + startDate: this.startDate, + endDate: this.endDate, + goalID: this.goal.getUUID(), + userID: this.user.getUUID(), + takenBy:this.takenBy, + mapConditionIDToSensorAndTimeBoxRequired: this.mapConditionIDToSensorAndTimeBoxRequired, + progress: this.progress + } + } + + getDataForClient():any { + + var type = (this.takenBy == this.user.getName()) ? 'personal' : 'team'; + + return { + id: this.id, + type:type, + startDate: this.startDate, + endDate: this.endDate, + goal: this.goal.getName(), + user: this.user.getName(), + takenBy:this.takenBy, + progress: this.progress, + status:this.status + } + } + + + setTakenBy(teamName:string) {this.takenBy = teamName;} + + getStatusAsString():string { + switch (this.status) { + case 0: + return 'WAIT'; + break; + case 1: + return 'RUNNING'; + break; + case 2: + return 'SUCCESS'; + break; + case 3: + return 'FAIL'; + break; + default: + return 'UNKNOWN'; + break; + } + } +} + +export = UserChallenge; diff --git a/backend/src/challenge/UserChallengeFactory.ts b/backend/src/challenge/UserChallengeFactory.ts new file mode 100644 index 0000000..8e7a723 --- /dev/null +++ b/backend/src/challenge/UserChallengeFactory.ts @@ -0,0 +1,133 @@ +/// +/// +/// + +import UserChallenge = require('./UserChallenge'); +import User = require('../user/User'); +import Goal = require('../goal/Goal'); +import GoalRepository = require('../goal/GoalRepository'); +import UserRepository = require('../user/UserRepository'); +import TimeBoxFactory = require('../TimeBoxFactory'); +import TimeBox = require('../TimeBox'); +import ChallengeStatus = require('../Status'); +import Clock = require('../Clock'); + +import BadArgumentException = require('../exceptions/BadArgumentException'); + +var moment = require('moment'); +var moment_timezone = require('moment-timezone'); +import UUID = require('node-uuid'); + +class GoalInstanceFactory { + + restoreChallenge(data:any, goalRepository:GoalRepository, userRepository:UserRepository, now:moment.Moment):UserChallenge { + + this.checkDataFromRestore(data, goalRepository, userRepository); + + var challengeID:string = data.id; + challengeID = (challengeID == null) ? UUID.v4() : challengeID; + + var goalID:string = data.goalID; + var userID:string = data.userID; + + var goal:Goal = goalRepository.getGoal(goalID); + var user:User = userRepository.getUser(userID); + + var startDateDesc = data.startDate; + var endDateDesc = data.endDate; + + var startDate = Clock.getMoment(startDateDesc); + var endDate = Clock.getMoment(endDateDesc); + + var mapConditionIDToSensorAndTimeBoxRequired:any = data.mapConditionIDToSensorAndTimeBoxRequired; + + var challenge:UserChallenge = this.newChallenge(challengeID, goal, user, mapConditionIDToSensorAndTimeBoxRequired, startDate, endDate, now); + + return challenge; + } + + createChallenge(goal:Goal, user:User, now:moment.Moment, takenBy = null):UserChallenge { + this.checkDataFromCreate(goal, user, now); + + var challengeID = UUID.v4(); + + var clone = now.clone(); + + var startDateOfChallenge = goal.getStartDateOfSession(clone); + var endDateOfChallenge = goal.getEndDateOfSession(clone.clone()); + + var mapConditionIDToSensorAndTimeBoxRequired:any = {}; + + var goalConditions = goal.getConditions(); + for (var conditionIndex in goalConditions) { + var currentCondition = goalConditions[conditionIndex]; + var conditionID = currentCondition.getID(); + var symbolicNamesAndTimeBoxRequired = currentCondition.getRequiredByCondition(startDateOfChallenge, endDateOfChallenge); + + mapConditionIDToSensorAndTimeBoxRequired[conditionID] = symbolicNamesAndTimeBoxRequired; + + } + + var result = this.newChallenge(challengeID, goal, user, mapConditionIDToSensorAndTimeBoxRequired, startDateOfChallenge, endDateOfChallenge, now, takenBy); + + return result; + } + + private newChallenge(challengeID, goal, user, mapConditionIDToSymbolicNamesAndTimeBoxesRequired, startDate, endDate, now, takenBy = null):UserChallenge { + var newChallenge:UserChallenge = new UserChallenge(challengeID, goal, user, startDate, endDate, mapConditionIDToSymbolicNamesAndTimeBoxesRequired, takenBy); + + if (newChallenge.getEndDate().isAfter(goal.getEndOfValidityPeriod())) { + return null; + } + if (now.isBefore(startDate)) { + newChallenge.setStatus(ChallengeStatus.WAIT); + } + if (now.isAfter(startDate) && now.isBefore(endDate)) { + newChallenge.setStatus(ChallengeStatus.RUN); + } + return newChallenge; + } + + checkDataFromCreate(goal, user, now) { + if (goal == null) { + throw new BadArgumentException('Can not create given challenge because given goal is null'); + } + + if (user == null) { + throw new BadArgumentException('Can not create given challenge because given user is null'); + } + + if (now == null) { + throw new BadArgumentException('Can not create given challenge because given "now" is null'); + } + } + + checkDataFromRestore(data:any, goalRepository:GoalRepository, userRepository:UserRepository):void { + var goalID:string = data.goalID; + if (goalID == null) { + throw new BadArgumentException('Can not restore given challenge because goalID is null'); + } + + var userID:string = data.userID; + if (userID == null) { + throw new BadArgumentException('Can not restore given challenge because userID is null'); + } + + var goal:Goal = goalRepository.getGoal(goalID); + if (goal == null) { + throw new BadArgumentException('Can not restore given challenge because goal with id ' + goalID + ' was not found'); + } + + var user:User = userRepository.getUser(userID); + if (user == null) { + throw new BadArgumentException('Can not restore given challenge because user with id ' + userID + ' was not found'); + } + + var mapConditionIDToSensorAndTimeBoxRequired:any = data.mapConditionIDToSensorAndTimeBoxRequired; + if (mapConditionIDToSensorAndTimeBoxRequired == null) { + throw new BadArgumentException('Can not restore given challenge because map (conditionID) -> to -> (sensor and timeBoxRequired) is null'); + } + } +} + +export = GoalInstanceFactory; \ No newline at end of file diff --git a/backend/src/challenge/UserChallengeRepository.ts b/backend/src/challenge/UserChallengeRepository.ts new file mode 100644 index 0000000..e718907 --- /dev/null +++ b/backend/src/challenge/UserChallengeRepository.ts @@ -0,0 +1,57 @@ +import UserChallenge = require('./UserChallenge'); +import UserChallengeFactory = require('./UserChallengeFactory'); +import GoalRepository = require('../goal/GoalRepository'); +import UserRepository = require('../user/UserRepository'); +import Badge = require('../badge/Badge'); + + +class UserChallengeRepository { + + private userChallengesArray:UserChallenge[] = []; + private factory:UserChallengeFactory; + + constructor() { + this.factory = new UserChallengeFactory(); + } + + + addUserChallenge(aChallenge:UserChallenge) { + this.userChallengesArray.push(aChallenge); + } + + getChallengeByID(challengeID:string):UserChallenge { + for (var i in this.userChallengesArray) { + var currentChallenge = this.userChallengesArray[i]; + if (currentChallenge.hasUUID(challengeID)) { + return currentChallenge; + } + } + + return null; + } + + getBadgeByChallengeID(challengeID:string):string { + return this.getChallengeByID(challengeID).getBadge(); + } + + displayShortState() { + console.log("\n\n+++\t Etat du repository des defis\t+++"); + + for (var currentChallengeIndex in this.userChallengesArray) { + var currentChallenge = this.userChallengesArray[currentChallengeIndex]; + console.log("#", currentChallenge.getID(), "\t |\tDefi : '", currentChallenge.getName(), "'") + } + } + + getDataInJSON():any { + var result:any[] = []; + + for (var currentChallengeIndex in this.userChallengesArray) { + result.push(this.userChallengesArray[currentChallengeIndex].getDataInJSON()); + } + + return result; + } +} + +export = UserChallengeRepository; \ No newline at end of file diff --git a/backend/src/condition/AverageOnValue.ts b/backend/src/condition/AverageOnValue.ts index a6fd834..c212a16 100644 --- a/backend/src/condition/AverageOnValue.ts +++ b/backend/src/condition/AverageOnValue.ts @@ -1,5 +1,4 @@ /// -/// /// /// @@ -8,154 +7,106 @@ import Condition = require('./Condition'); import TimeBox = require('../TimeBox'); import Clock = require('../Clock'); import Filter = require('../filter/Filter'); +import ReferencePeriod = require('./ReferencePeriod'); var moment = require('moment'); var moment_timezone = require('moment-timezone'); class AverageOnValue extends Condition { - private oldTimeBox:TimeBox; - private newTimeBox:TimeBox; - private referencePeriod:moment.Moment; - - constructor(id:string, condition:GoalExpression, thresholdRate:number, - startDate:moment.Moment, dateOfCreation:moment.Moment, endDate:moment.Moment, referencePeriod:moment.Moment, - percentageAchieved:number = 0, percentageOfTimeElapsed:number = 0, filter:Filter = null) { - - super(id, condition, thresholdRate, startDate, dateOfCreation, endDate, - percentageAchieved, percentageOfTimeElapsed, filter); - - this.oldTimeBox = new TimeBox(startDate, dateOfCreation); - this.newTimeBox = new TimeBox(dateOfCreation, endDate); + private referencePeriod:ReferencePeriod; + constructor(id:string, description:string, expression:GoalExpression, thresholdRate:number, + filter:Filter, referencePeriod:ReferencePeriod) { + super(id, description, expression, thresholdRate, filter); this.referencePeriod = referencePeriod; } - public setTimeBox(newTimeBox:TimeBox) { - - this.dateOfCreation = newTimeBox.getStartDate(); - this.endDate = newTimeBox.getEndDate(); - - console.log("DOC", this.dateOfCreation.format(), "END DATE", this.endDate.format()); - - var timeOfTheUltimateOriginOfOrigins:moment.Moment = Clock.getMoment(new Date(0, 0, 0, 0, 0, 0, 0).getTime()); - - console.log("ORIGINS", timeOfTheUltimateOriginOfOrigins.format()); - - var year:number = this.referencePeriod.year() - timeOfTheUltimateOriginOfOrigins.year(); + getTimeBoxRequired(startDateOfChallenge:moment.Moment, endDateOfChallenge:moment.Moment):any { - var month:number = this.referencePeriod.month() - timeOfTheUltimateOriginOfOrigins.month(); - var day:number = this.referencePeriod.date() - timeOfTheUltimateOriginOfOrigins.date(); - - var momentObj:moment.Moment = moment.tz( Clock.getTimeZone()).year(this.dateOfCreation.year() - year).month(this.dateOfCreation.month() - month).date(this.dateOfCreation.date() - day).hours(this.dateOfCreation.hour()).minute(this.dateOfCreation.minute()) - .second(this.dateOfCreation.second()).millisecond(this.dateOfCreation.millisecond()); - - - this.startDate = momentObj; + var beginningOfReferencePeriod:moment.Moment = this.referencePeriod.getTimeBoxRequired(startDateOfChallenge); + + return { + start: beginningOfReferencePeriod.clone(), + dateOfCreation: startDateOfChallenge.clone(), + end: endDateOfChallenge.clone() + }; + } - console.log("CONSTRUCTION DE LA CONDITION AVEC START DATE", this.startDate.format()); + public evaluate(data:any, conditionDescription:any):boolean { - var timeBox:TimeBox = new TimeBox(this.startDate, this.endDate); - this.timeBox = timeBox; - } + var remainingData:any = super.keepUsefulValues(data, conditionDescription); + remainingData = super.applyFilters(remainingData); - public evaluate(data:any):boolean { - var remainingData:any = super.applyFilters(data); data = remainingData; var sensorNames:string[] = this.expression.getRequired(); - var result = false; - - for (var currentSensorNameIndex in sensorNames) { + // This type of condition must have one and exactly one required + var currentSensorName:string = sensorNames[0]; - var currentSensorName:string = sensorNames[currentSensorNameIndex]; + var oldAndNewData:any[] = data[currentSensorName]; - var oldAndNewData:any[] = data[currentSensorName].values; + var timeBox:any = conditionDescription.timeBox; + var dateOfCreation:moment.Moment = timeBox.dateOfCreation; - var oldData:number[] = []; - var newData:number[] = []; + var oldData:number[] = []; + var newData:number[] = []; - this.separateOldAndNewData(oldAndNewData, oldData, newData); + this.separateOldAndNewData(oldAndNewData, oldData, newData, dateOfCreation); - console.log("OLD DATA", oldData, "NEW DATA", newData); + var percentageAchieved = 0; + var rate = 0; - this.percentageAchieved = 0; - var rate = 0; + if (oldData.length != 0 && newData.length != 0) { - if (oldData.length != 0 && newData.length != 0) { + var oldAverage = this.computeAverageValues(oldData); + var newAverage = this.computeAverageValues(newData); - var result = true; - - var oldAverage = this.computeAverageValues(oldData); - var newAverage = this.computeAverageValues(newData); + if (newAverage) { + rate = (newAverage * 100 / oldAverage); + } - if (newAverage) { - rate = (newAverage * 100 / oldAverage); - } + // < baisse + // > hausse + var changeRate = 0; - // < baisse - // > hausse - var changeRate = 0; + if (this.expression.getComparisonType() === '<') { + changeRate = 100 - rate; + } else { + changeRate = rate - 100; + } - if (this.expression.getComparisonType() === '<') { - changeRate = 100 - rate; - } else { - changeRate = rate - 100; - } + percentageAchieved = changeRate * 100 / this.thresholdRate; - this.percentageAchieved = changeRate * 100 / this.thresholdRate; + // It can be infinite + percentageAchieved = (percentageAchieved > 100) ? 100 : percentageAchieved; - // It can be infinite - this.percentageAchieved = (this.percentageAchieved > 100) ? 100 : this.percentageAchieved; - result = result && this.percentageAchieved >= 100; + // If there is no values yet + if(isNaN(percentageAchieved)) { + percentageAchieved = 0; } - - this.updateDurationAchieved(Clock.getNow()); } + var achieved:boolean = percentageAchieved === 100; + var result:any = {description: this.description, percentageAchieved: percentageAchieved, achieved: achieved}; return result; } - public updateDurationAchieved(currentDate:number) { - console.log(this.dateOfCreation.date()); - - var currentMoment:moment.Moment = Clock.getMoment(currentDate); - console.log(currentMoment.date()); - if (currentMoment.isBefore(this.dateOfCreation)) { - throw new Error('Time given is before dateOfCreation !'); - } - - var duration = this.endDate.valueOf() - this.dateOfCreation.valueOf(); - var durationAchieved = (currentMoment.valueOf() - this.dateOfCreation.valueOf()); - - this.percentageOfTimeElapsed = durationAchieved * 100 / duration; - - // It can have tiny incorrect decimal values - this.percentageOfTimeElapsed = (this.percentageOfTimeElapsed > 100) ? 100 : this.percentageOfTimeElapsed; - } - - public separateOldAndNewData(values:any[], oldValues:number[], newValues:number[]) { + public separateOldAndNewData(values:any[], oldValues:number[], newValues:number[], dateOfCreation:moment.Moment) { for (var currentValueIndex in values) { - // { date : __ , value : __ } var currentPairDateValue:any = values[currentValueIndex]; + var currentMoment:moment.Moment = Clock.getMomentFromUnixTimeInMillis(parseInt(currentPairDateValue.date)); - - var currentMoment:moment.Moment = Clock.getMoment(parseInt(currentPairDateValue.date)); - - //console.log("PROCESSING MOMENT", currentMoment.format()); - // console.log("START DATE", this.startDate.format(), "END DATE", this.endDate.format()); - - if (currentMoment.isAfter(this.startDate) - && currentMoment.isBefore(this.dateOfCreation)) { - oldValues.push(currentPairDateValue.value); - } - else if (currentMoment.isAfter(this.dateOfCreation)) { + if (currentMoment.isAfter(dateOfCreation)) { newValues.push(currentPairDateValue.value); } + else if (currentMoment.isBefore(dateOfCreation)) { + oldValues.push(currentPairDateValue.value); + } } } @@ -175,7 +126,7 @@ class AverageOnValue extends Condition { public getDataInJSON():any { var data:any = super.getDataInJSON(); data.type = 'comparison'; - data.expression.periodOfTime = (''+this.referencePeriod.valueOf()); + data.referencePeriod = this.referencePeriod.getDataInJSON(); return data; } diff --git a/backend/src/condition/Condition.ts b/backend/src/condition/Condition.ts index baffb69..e10f28c 100644 --- a/backend/src/condition/Condition.ts +++ b/backend/src/condition/Condition.ts @@ -14,189 +14,135 @@ import Clock = require('../Clock'); class Condition { protected id:string; + protected description:string; protected expression:GoalExpression; protected thresholdRate:number; - protected startDate:moment.Moment; - protected dateOfCreation:moment.Moment; - protected endDate:moment.Moment; - - protected timeBox:TimeBox; - - protected percentageAchieved:number; - protected percentageOfTimeElapsed:number; - protected filter:Filter; - /** - * Constructor of base class Condition, it allows you to build a goal condition - * @param id - * The id of the condition. If null, the id will be generated. - * @param expression - * The expression object, for instance 'TMP_Cli > 25' - * @param thresholdRate - * This value represents the threshold rate when the condition must be verified - * @param startDate - * The start of the application of the current condition - for instance start date of the goal instance - * @param dateOfCreation - * The date of creation of the parent goal - * @param endDate - * The end of the application of the current condition - for instance end date of the goal instance - * @param percentageAchieved - * The percentage of progression achieved - * @param percentageOfTimeElapsed - * The percentage of time elapsed between 'now' and startDate - */ - constructor(id:string, expression:GoalExpression, thresholdRate:number, - startDate:moment.Moment, dateOfCreation:moment.Moment, endDate:moment.Moment, - percentageAchieved:number = 0, percentageOfTimeElapsed:number = 0, filter:Filter = null) { + constructor(id:string, description:string, expression:GoalExpression, thresholdRate:number, filter:Filter = null) { + + this.id = id; - this.id = (id) ? id : UUID.v4(); + this.description = description; this.expression = expression; this.thresholdRate = thresholdRate; - this.startDate = startDate; - this.dateOfCreation = dateOfCreation; - this.endDate = endDate; - - this.timeBox = new TimeBox(this.startDate, this.endDate); - - this.percentageAchieved = percentageAchieved; - this.percentageOfTimeElapsed = percentageOfTimeElapsed; - this.filter = (filter) ? filter : new Filter('all', ['all']); } - getStringRepresentation():string { - return this.expression.getStringRepresentation() + " - " + this.expression.getDescription() + " filtre " + JSON.stringify(this.filter.getDataInJSON()); - } - /** - * This method will return the id of the current condition - * @returns {string} - * The id of the current condition - */ - getID():string { - return this.id; + getTimeBoxRequired(startDateOfChallenge:moment.Moment, endDateOfChallenge:moment.Moment):any { + + // Must be overridden by any condition that need more time than + // start date and end date of challenge (caller) + return {start: startDateOfChallenge, end: endDateOfChallenge}; } /** - * This method will return the field required by its expression, - * the symbolic name(s) of the expression. - * See GoalExpression#getRequired method - * @returns {string[]} - * The array of symbolic names in the expression + * + * @param data + * { + * : [ {date : ., value : .}, ... ] + * } + * @param conditionDescription + * { + * timeBox: { + * start: ., + * end: . + * } + * } + * @returns {any[]} */ - getRequired():any { + keepUsefulValues(data:any, conditionDescription:any):any { var result:any = {}; - var sensorRequired:string[] = this.expression.getRequired(); - for (var currentSensorRequiredIndex in sensorRequired) { - var currentSensorRequired:string = sensorRequired[currentSensorRequiredIndex]; - result[currentSensorRequired] = this.timeBox.getRequired(); - } + var startDate:moment.Moment = conditionDescription.timeBox.start; + var endDate:moment.Moment = conditionDescription.timeBox.end; - return result; - } + // For each symbolic names in data + for (var currentSymbolicName in data) { + var currentResult:any[] =[]; - hasLeftOperand(operandName:string):boolean { - return this.expression.hasLeftOperand(operandName); - } - - hasRightOperand(operandName:string):boolean { - return this.expression.hasRightOperand(operandName); - } + var currentDataArray:any = data[currentSymbolicName]; + for (var currentDataIndex in currentDataArray) { + var currentData:any = currentDataArray[currentDataIndex]; + var date:moment.Moment = Clock.getMomentFromUnixTimeInMillis(parseInt(currentData.date)); - hasComparisonType(comparisonType:string):boolean { - return this.expression.getComparisonType() === comparisonType; - } - getStartDate():moment.Moment { - return this.startDate; - } + if (date.isAfter(startDate) && date.isBefore(endDate)) { + currentResult.push(currentData); + } + } - setStartDate(newStartDate:moment.Moment):void { - this.startDate = newStartDate; - } + result[currentSymbolicName] = currentResult; + } - getEndDate():moment.Moment { - return this.endDate; + return result; } - setEndDate(newEndDate:moment.Moment):void { - this.endDate = newEndDate; - } + getRequiredByCondition(startDate, endDate) { + var result:any = {}; + var symbolicNames:string[] = this.expression.getRequired(); - setTimeBox(newTimeBox:TimeBox) { + result.symbolicNames = symbolicNames; + result.timeBox = this.getTimeBoxRequired(startDate, endDate); - this.timeBox = newTimeBox; - this.startDate = newTimeBox.getStartDate(); - this.endDate = newTimeBox.getEndDate(); - console.log("TIMEBOX SET AT", newTimeBox, "So now, condition have", this.startDate.format(), "and", this.endDate.format()); + return result; } - isInTimeBox(date:moment.Moment):boolean { - return this.timeBox.isDateInTimeBox(date); + getStringRepresentation():string { + return this.expression.getStringRepresentation() + " - filtre " + JSON.stringify(this.filter.getDataInJSON()); } - setPercentageAchieved(newPercentageAchieved:number) { - this.percentageAchieved = newPercentageAchieved; + getID():string { + return this.id; } - updatePercentageOfTimeElapsed(currentDate:number) { - - var currentMoment:moment.Moment = Clock.getMoment(currentDate); - if (currentMoment.isBefore(this.getStartDate())) { - throw new Error('Time given is before dateOfCreation !'); - } - var duration = this.getEndDate().valueOf() - this.getStartDate().valueOf(); - - var durationAchieved = currentMoment.valueOf() - this.getStartDate().valueOf(); - this.percentageOfTimeElapsed = durationAchieved * 100 / duration; - } - - getPercentageOfTimeElapsed():number { - return this.percentageOfTimeElapsed; + hasLeftOperand(operandName:string):boolean { + return this.expression.hasLeftOperand(operandName); } - setPercentageOfTimeElapsed(newPercentageOfTimeElapsed:number) { - this.percentageOfTimeElapsed = newPercentageOfTimeElapsed; + hasRightOperand(operandName:string):boolean { + return this.expression.hasRightOperand(operandName); } - getPercentageAchieved():number { - return this.percentageAchieved; + hasComparisonType(comparisonType:string):boolean { + return this.expression.getComparisonType() === comparisonType; } getDataInJSON():any { return { id: this.id, + description: this.description, expression: this.expression.getDataInJSON(), threshold: this.thresholdRate, - startDate: this.startDate, - dateOfCreation: this.dateOfCreation, - endDate: this.endDate, - percentageAchieved: this.percentageAchieved, - percentageOfTimeElapsed: this.percentageOfTimeElapsed, filter: this.filter.getDataInJSON() } } - evaluate(data:any):boolean { + /** + * + * @param data + * [ { date : ..., value : ...} ] + * @param conditionDescription + * { + * symbolic_names: [..], + * timeBox: { + * start:..., + * end:... + * } + * } + */ + evaluate(data:any, conditionDescription:any):any { throw new Error('Can not call base class method ! Must be overridden and implemented.'); } applyFilters(data:any):any { var remainingData = this.filter.apply(data); - /* - console.log("APPLICATION DU FILTRE SUR", JSON.stringify(data)); - console.log("FILTER?", this.filter.getDataInJSON()); - - - console.log("REMAINING DATA AFTER FITLER", JSON.stringify(remainingData)); - */ return remainingData; } } diff --git a/backend/src/condition/ConditionList.ts b/backend/src/condition/ConditionList.ts deleted file mode 100644 index aa328f2..0000000 --- a/backend/src/condition/ConditionList.ts +++ /dev/null @@ -1,151 +0,0 @@ -/// - -var merge:any = require('merge'); -import uuid = require('node-uuid'); - -import Condition = require('./Condition'); -import GoalExpression = require('./expression/GoalExpression'); -import TimeBox = require('../TimeBox'); - -import Challenge = require('../challenge/Challenge'); - -class ConditionList { - - private conditions:Condition[] = []; - - public getConditions():Condition[] { - return this.conditions; - } - - public addCondition(condition:Condition):void { - this.conditions.push(condition); - } - - public setTimeBoxes(newTimeBox:TimeBox) { - - for (var currentExpressionIndex in this.conditions) { - this.conditions[currentExpressionIndex].setTimeBox(newTimeBox); - } - } - - /** - * - * @returns {any} - * { - * : { start:_, end:_ } <-- timebox for the sensor-name - * ... - * } - */ - public getRequired():any { - - var result:any = {}; - - // For each expression - for (var currentConditionIndex in this.conditions) { - - // Get its description --> { : null | {start:_, end:_} } - var currentCondition:Condition = this.conditions[currentConditionIndex]; - var currentConditionDesc = currentCondition.getRequired(); - - // for each sensors required by the current expression - for (var currentSensorName in currentConditionDesc) { - - // check if current sensor has already been added - if (result[currentSensorName] != null) { - // if so, retrieve previous timeBox and current timeBox - var currentTimeBox = currentConditionDesc[currentSensorName]; - var oldTimeBox = result[currentSensorName]; - - // merge two timeBoxes - var newTimeBox = this.mergeTimeBox(currentTimeBox, oldTimeBox); - - // add newly merged timeBox into result - result[currentSensorName] = newTimeBox; - } - else { - // if not, add current expression description - result[currentSensorName] = currentConditionDesc[currentSensorName]; - } - } - } - - return result; - } - - // TODO test - public mergeTimeBox(currentTimeBox:any, oldTimeBox:any):any { - if (currentTimeBox == null || oldTimeBox == null) { - return null; - } - - var currentStart = currentTimeBox.start; - var currentEnd = currentTimeBox.end; - - var oldStart = oldTimeBox.start; - var oldEnd = oldTimeBox.end; - - var newStart = (currentStart > oldStart) ? oldStart : currentStart; - var newEnd = (currentEnd > oldEnd) ? currentEnd : oldEnd; - - /* - console.log("currentStart", currentStart, "currentEnd", currentEnd, "oldStart", oldStart, - "oldEnd", oldEnd, "newstart", newStart, "newEnd", newEnd); - */ - - return { - start: newStart, - end: newEnd - } - } - - /** - * - * @param values - * { - * : { timebox { start:_, end:_ }, values : [ {date:_, value:_}, ... ] } - * ... - * } - * @returns {boolean} - */ - - public evaluate(values:any, challenge:Challenge):boolean { - - var result:boolean = true; - - for (var i = 0; i < this.conditions.length; i++) { - - result = result && this.conditions[i].evaluate(values); - - var conditionDescription:any = this.conditions[i].getDataInJSON(); - if (challenge != null) { - challenge.addProgress(conditionDescription); - } - } - - return result; - } - - public getDataInJSON():any { - var result:any[] = []; - - for (var i = 0; i < this.conditions.length; i++) { - result.push(this.conditions[i].getDataInJSON()); - } - - return result; - } - - getStringRepresentation():string { - var result:string = ''; - - for(var currentConditionIndex in this.conditions) { - var currentCondition = this.conditions[currentConditionIndex]; - result += '\t|\t\t' + currentCondition.getStringRepresentation(); - } - - return result; - } - -} - -export = ConditionList; diff --git a/backend/src/condition/OverallGoalCondition.ts b/backend/src/condition/OverallGoalCondition.ts index 7ed0474..79cba45 100644 --- a/backend/src/condition/OverallGoalCondition.ts +++ b/backend/src/condition/OverallGoalCondition.ts @@ -14,78 +14,60 @@ import BadArgumentException = require('../exceptions/BadArgumentException'); class OverallGoalCondition extends Condition { - - constructor(id:string, condition:GoalExpression, thresholdRate:number, - startDate:moment.Moment, dateOfCreation:moment.Moment, endDate:moment.Moment, - percentageAchieved:number = 0, percentageOfTimeElapsed:number = 0, filter:Filter = null) { - - super(id, condition, thresholdRate, startDate, dateOfCreation, endDate, - percentageAchieved, percentageOfTimeElapsed, filter); + constructor(id:string, description:string, condition:GoalExpression, thresholdRate:number, filter:Filter) { + super(id, description, condition, thresholdRate, filter); } - /** - * - * @param values - * { - * : { timebox { start:_, end:_ }, values : [ {date:_, value:_}, ... ] } - * ... - * } - */ + public evaluate(data:any, conditionDescription:any):any { + + var remainingData:any = super.keepUsefulValues(data, conditionDescription); - public evaluate(data:any) { + remainingData = super.applyFilters(remainingData); - var remainingData:any = super.applyFilters(data); data = remainingData; var conditionDesc:string[] = this.expression.getRequired(); // For each sensors required by internal condition - for (var currentSensorNameIndex in conditionDesc) { - var currentSensorName:string = conditionDesc[currentSensorNameIndex]; + for (var currentSymbolicNameIndex in conditionDesc) { + var currentSymbolicName:string = conditionDesc[currentSymbolicNameIndex]; // Retrieve values associated - var currentConditionDesc = data[currentSensorName]; + var values:any[] = data[currentSymbolicName]; - if(!currentConditionDesc) { - throw new BadArgumentException('Can not evaluate condition ! Proper argument were not provided. Field' + currentSensorName + ' is missing'); + if (values == null) { + throw new BadArgumentException('Can not evaluate condition ! Proper argument were not provided. Field' + currentSymbolicName + ' is missing'); } - - var values:any[] = currentConditionDesc.values; - var numberOfValues:number = (values).length; var numberOfCorrectValues:number = 0; // Check how many values are correct - for (var currentValueIndex in values) { - var value = values[currentValueIndex]; + for (var currentPairDateValueIndex in values) { + var currentPairDateValue = values[currentPairDateValueIndex]; - var date:moment.Moment = Clock.getMomentFromString(value.date); + var dataToEvaluate:any = {}; + dataToEvaluate[currentSymbolicName] = currentPairDateValue.value; - if (this.isInTimeBox(date)) { - var dataToEvaluate:any = {}; - dataToEvaluate[currentSensorName] = value.value; - - // Check value by value if internal condition is satisfied - if (this.expression.evaluate(dataToEvaluate)) { - ++numberOfCorrectValues; - } - } - else { - numberOfValues--; + // Check value by value if internal condition is satisfied + if (this.expression.evaluate(dataToEvaluate)) { + ++numberOfCorrectValues; } } } + var percentageAchieved = ((numberOfCorrectValues * 100 / numberOfValues) * 100) / this.thresholdRate; + percentageAchieved = (percentageAchieved > 100) ? 100 : percentageAchieved; - this.percentageAchieved = ((numberOfCorrectValues * 100 / numberOfValues) * 100) / this.thresholdRate; - - this.percentageAchieved = (this.percentageAchieved > 100) ? 100 : this.percentageAchieved; + // If there is no values yet + if(isNaN(percentageAchieved)) { + percentageAchieved = 0; + } - this.updatePercentageOfTimeElapsed(Clock.getNow()); + var achieved:boolean = percentageAchieved === 100; - return (numberOfCorrectValues * 100 / numberOfValues) >= this.thresholdRate; + return {description: this.description, percentageAchieved: percentageAchieved, achieved: achieved}; } public getDataInJSON():any { diff --git a/backend/src/condition/ReferencePeriod.ts b/backend/src/condition/ReferencePeriod.ts new file mode 100644 index 0000000..30ffd6b --- /dev/null +++ b/backend/src/condition/ReferencePeriod.ts @@ -0,0 +1,32 @@ +/// +/// +/// + +var moment = require('moment'); +var moment_timezone = require('moment-timezone'); + +class ReferencePeriod { + + private numberOfUnitToSubtract:number; + private unitToSubtract:string; + + constructor(numberOfUnitToSubtract:number, unitToSubtract:string) { + this.numberOfUnitToSubtract = numberOfUnitToSubtract; + this.unitToSubtract = unitToSubtract; + } + + getTimeBoxRequired(startDate:moment.Moment) { + var dateOfCreation:moment.Moment = startDate.clone(); + dateOfCreation = dateOfCreation.subtract(this.numberOfUnitToSubtract, this.unitToSubtract); + return dateOfCreation; + } + + getDataInJSON():any { + return { + numberOfUnitToSubtract: this.numberOfUnitToSubtract, + unitToSubtract:this.unitToSubtract + } + } +} + +export = ReferencePeriod; \ No newline at end of file diff --git a/backend/src/condition/expression/GoalExpression.ts b/backend/src/condition/expression/GoalExpression.ts index 2215b64..83749bd 100644 --- a/backend/src/condition/expression/GoalExpression.ts +++ b/backend/src/condition/expression/GoalExpression.ts @@ -5,15 +5,12 @@ import Comparator = require('./Comparator'); import BadArgumentException = require('../../exceptions/BadArgumentException'); class GoalExpression { - private description:string; - private leftOperand:Operand; private rightOperand:Operand; private comparator:Comparator; - constructor(leftOperand:Operand, typeOfComparison:string, rightOperand:Operand, description:string) { - this.description = description; + constructor(leftOperand:Operand, typeOfComparison:string, rightOperand:Operand) { this.leftOperand = leftOperand; this.rightOperand = rightOperand; @@ -21,10 +18,6 @@ class GoalExpression { this.comparator = new Comparator(typeOfComparison); } - public getDescription():string { - return this.description; - } - public getComparisonType():string { return this.comparator.getTypeOfComparison(); } @@ -120,14 +113,13 @@ class GoalExpression { return { valueLeft: { value: this.leftOperand.getStringDescription(), - sensor: this.leftOperand.hasToBeDefined() + symbolicName: this.leftOperand.hasToBeDefined() }, valueRight: { value: this.rightOperand.getStringDescription(), - sensor: this.rightOperand.hasToBeDefined() + symbolicName: this.rightOperand.hasToBeDefined() }, - comparison: this.comparator.getTypeOfComparison(), - description: this.description, + comparison: this.comparator.getTypeOfComparison() }; } } diff --git a/backend/src/condition/factory/ConditionFactory.ts b/backend/src/condition/factory/ConditionFactory.ts index 5e62089..8e2d84e 100644 --- a/backend/src/condition/factory/ConditionFactory.ts +++ b/backend/src/condition/factory/ConditionFactory.ts @@ -4,6 +4,7 @@ var moment = require('moment'); var moment_timezone = require('moment-timezone'); +import UUID = require('node-uuid'); import Condition = require('../Condition'); import GoalExpression = require('../expression/GoalExpression'); @@ -12,16 +13,17 @@ import AverageOnValue = require('../AverageOnValue'); import ExpressionFactory = require('./ExpressionFactory'); import Clock = require('../../Clock'); import Filter = require('../../filter/Filter'); +import ReferencePeriod = require('../ReferencePeriod'); class ConditionFactory { private expressionFactory:ExpressionFactory = new ExpressionFactory(); - public createCondition(data:any, goalTimeBox:any, duration:number):Condition { + public createCondition(data:any):Condition { var type:string = data.type; var expression = null; switch (type) { case 'overall': - expression = this.createOverall(data, goalTimeBox, duration); + expression = this.createOverall(data); break; case 'comparison': expression = this.createComparison(data); @@ -33,33 +35,36 @@ class ConditionFactory { return expression; } - public createOverall(data:any, goaltimeBox:any, duration:number):Condition { + public createOverall(data:any):Condition { + var id = (data.id == null) ? UUID.v4() : data.id; + var description:string = data.description; - data.expression.timeBox = goaltimeBox; - - var goalCondition:GoalExpression = this.expressionFactory.createExpression(data.expression); - - var startDateOfValidityPeriod:moment.Moment = goaltimeBox.startDate; - var endDateOfValidityPeriod:moment.Moment = goaltimeBox.endDate; + var goalExpression:GoalExpression = this.expressionFactory.createExpression(data.expression); var threshold:number = data.threshold; var dayOfWeekFilterDesc:string = data.filter.dayOfWeekFilter; var periodOfDayFilterDesc:string[] = data.filter.periodOfDayFilter; - var filter:Filter = new Filter(dayOfWeekFilterDesc, periodOfDayFilterDesc); - var overallCondition:OverallGoalCondition = new OverallGoalCondition(null, goalCondition, threshold, startDateOfValidityPeriod, Clock.getMoment(Clock.getNow()), endDateOfValidityPeriod, 0, 0, filter); + var filter:Filter = new Filter(dayOfWeekFilterDesc, periodOfDayFilterDesc); + var overallCondition:OverallGoalCondition = new OverallGoalCondition(id, description, goalExpression, threshold, filter); return overallCondition; } public createComparison(data:any):Condition { + var id = (data.id == null) ? UUID.v4() : data.id; + var description:string = data.description; + var dayOfWeekFilterDesc:string = data.filter.dayOfWeekFilter; var periodOfDayFilterDesc:string[] = data.filter.periodOfDayFilter; var filter:Filter = new Filter(dayOfWeekFilterDesc, periodOfDayFilterDesc); + var referencePeriodDesc = data.referencePeriod; + var referencePeriod:ReferencePeriod = new ReferencePeriod(referencePeriodDesc.numberOfUnitToSubtract, referencePeriodDesc.unitToSubtract); + var goalExpression:GoalExpression = this.expressionFactory.createExpression(data.expression); - var averageOnValue:AverageOnValue = new AverageOnValue(null, goalExpression, data.threshold, data.startDate, data.dateOfCreation, data.endDate,Clock.getMoment(new Date(parseInt(data.expression.periodOfTime)).getTime()),0,0,filter); + var averageOnValue:AverageOnValue = new AverageOnValue(id, description, goalExpression, data.threshold, filter, referencePeriod); return averageOnValue; } diff --git a/backend/src/condition/factory/ExpressionFactory.ts b/backend/src/condition/factory/ExpressionFactory.ts index b17a7d5..130c079 100644 --- a/backend/src/condition/factory/ExpressionFactory.ts +++ b/backend/src/condition/factory/ExpressionFactory.ts @@ -4,41 +4,56 @@ import Operand = require('../expression/Operand'); import TimeBox = require('../../TimeBox'); +import BadArgumentException = require('../../exceptions/BadArgumentException'); class ExpressionFactory { - static REQUIRED_JSON_FIELD:string[] = ['comparison', 'valueLeft', 'valueRight', 'description']; + public createExpression(data:any):GoalExpression { + this.checksData(data); - public createExpression(expression:any):GoalExpression { - for (var currentRequiredFieldIndex in ExpressionFactory.REQUIRED_JSON_FIELD) { - var currentRequiredField = ExpressionFactory.REQUIRED_JSON_FIELD[currentRequiredFieldIndex]; + var leftOperandName = data.valueLeft.value; + var leftOperandRequired = data.valueLeft.symbolicName; + var leftOperand:Operand = new Operand(leftOperandName, leftOperandRequired); + + var rightOperandName = data.valueRight.value; + var rightOperandRequired = data.valueRight.symbolicName; + var rightOperand:Operand = new Operand(rightOperandName, rightOperandRequired); + + var typeOfComparison:string = data.comparison; - if (!expression[currentRequiredField] || expression[currentRequiredField] === "undefined") { - throw new Error('Can not build expression ! Field ' - + currentRequiredField + ' is missing'); - } + var newExpression:GoalExpression = new GoalExpression(leftOperand, typeOfComparison, rightOperand); + return newExpression; + } + + private checksData(data:any) { + if(data.valueLeft == null) { + throw new BadArgumentException('Can not build expression, field "valueLeft" is null'); } - var leftOperandName = expression.valueLeft.value; - var leftOperandRequired = expression.valueLeft.sensor; - var leftOperand:Operand = new Operand(leftOperandName, leftOperandRequired); + if(data.valueLeft.value == null) { + throw new BadArgumentException('Can not build expression, field "valueLeft.value" is null'); + } - var rightOperandName = expression.valueRight.value; - var rightOperandRequired = expression.valueRight.sensor; - var rightOperand:Operand = new Operand(rightOperandName, rightOperandRequired); + if(data.valueLeft.symbolicName == null) { + throw new BadArgumentException('Can not build expression, field "valueLeft.symbolicName" is null'); + } + + if(data.valueRight == null) { + throw new BadArgumentException('Can not build expression, field "valueRight" is null'); + } - var typeOfComparison:string = expression.comparison; - var description:string = expression.description; + if(data.valueRight.value == null) { + throw new BadArgumentException('Can not build expression, field "valueRight.value" is null'); + } + + if(data.valueRight.symbolicName == null) { + throw new BadArgumentException('Can not build expression, field "valueRight.symbolicName" is null'); + } - /*FIXME - var timeBox:any = expression.timeBox; - var startDate:number = timeBox.startDate; - var endDate:number = timeBox.endDate; + if(data.comparison == null) { + throw new BadArgumentException('Can not build expression, field "comparison" is null'); + } - var timeBoxObj:TimeBox = new TimeBox(startDate, endDate); -*/ - var newGoalCondition:GoalExpression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - return newGoalCondition; } } diff --git a/backend/src/context/DemoContext.ts b/backend/src/context/DemoContext.ts deleted file mode 100644 index 6bc90e5..0000000 --- a/backend/src/context/DemoContext.ts +++ /dev/null @@ -1,74 +0,0 @@ -import UUID = require('node-uuid'); - -import Context = require('../Context'); - -import GoalRepository = require('../goal/GoalRepository'); -import ChallengeRepository = require('../challenge/ChallengeRepository'); -import UserRepository = require('../user/UserRepository'); - -import Goal = require('../goal/Goal'); -import GoalExpression = require('../condition/expression/GoalExpression'); -import Operand = require('../condition/expression/Operand'); - - -import Challenge = require('../challenge/Challenge'); - -class DemoContext implements Context { - - private aGoal:Goal; - private aUUID:string; - - - fill(goalDefinitionRepository:GoalRepository, goalInstanceRepository:ChallengeRepository, userRepository:UserRepository) { - if (goalDefinitionRepository) { - this.fillGoalProvider(goalDefinitionRepository); - } - - if(goalInstanceRepository) { - this.fillBadgeProvider(goalInstanceRepository); - } - } - - public fillGoalProvider(goalProvider:GoalRepository) { - /*FIXME - this.aUUID = UUID.v4(); - - this.aGoal = new GoalDefinition('Clim éco !'); - this.aGoal.setUUID(this.aUUID); - - this.aGoal.addCondition(new GoalCondition(new Operand('Temp_cli', true), '>', new Operand('15', false), - 'la température de la clim doit être supérieure à 14°C')); - this.aGoal.addCondition(new GoalCondition(new Operand('Temp_ext', true), '>', new Operand('40', false), - 'la température extérieure doit être supérieure à 40°C')); - - goalProvider.addChallenge(this.aGoal); - */ - } - - public fillBadgeProvider(badgeProvider:ChallengeRepository) { - /* - var mapGoalToConditionAndSensor:any = {}; - - - var condition1Desc:any = {}; - condition1Desc.name = 'Temp_cli'; - condition1Desc.sensor = 'AC_443'; - - var condition2Desc:any = {}; - condition2Desc.name = 'Temp_ext'; - condition2Desc.sensor = 'TEMP_444'; - - var arrayOfConditions:any[] = [condition1Desc, condition2Desc]; - - - mapGoalToConditionAndSensor[this.aUUID.toString()] = arrayOfConditions; - var aBadge = new GoalInstance("Vous n'êtes pas un esquimau !", - this.aGoal,mapGoalToConditionAndSensor); - - badgeProvider.addGoalInstance(aBadge); - FIXME - */ - } -} - -export = DemoContext; \ No newline at end of file diff --git a/backend/src/exceptions/BadArgumentException.ts b/backend/src/exceptions/BadArgumentException.ts index 89caa54..554a52a 100644 --- a/backend/src/exceptions/BadArgumentException.ts +++ b/backend/src/exceptions/BadArgumentException.ts @@ -6,6 +6,10 @@ class BadArgumentException implements Error { this.name = "BadArgumentException"; this.message = message; } + + getMessage():string { + return this.message; + } } export = BadArgumentException; \ No newline at end of file diff --git a/backend/src/exceptions/BadRequestException.ts b/backend/src/exceptions/BadRequestException.ts index 1966aef..9806943 100644 --- a/backend/src/exceptions/BadRequestException.ts +++ b/backend/src/exceptions/BadRequestException.ts @@ -6,6 +6,10 @@ class BadRequestException implements Error { this.name = "BadRequestException"; this.message = message; } + + getMessage():string { + return this.message; + } } export = BadRequestException; \ No newline at end of file diff --git a/backend/src/filter/Filter.ts b/backend/src/filter/Filter.ts index 48c6213..5fe35d6 100644 --- a/backend/src/filter/Filter.ts +++ b/backend/src/filter/Filter.ts @@ -34,7 +34,7 @@ class Filter { for (var currentSensorName in data) { var correctValues:any[] = []; - var arrayOfValues = data[currentSensorName].values; + var arrayOfValues = data[currentSensorName]; //console.log("Array of values", arrayOfValues); @@ -80,9 +80,7 @@ class Filter { } - var correctValuesContainer:any = {}; - correctValuesContainer.values = correctValues; - result[currentSensorName] = correctValuesContainer; + result[currentSensorName] = correctValues; } //console.log("DONC ON GARDE", result); diff --git a/backend/src/goal/Goal.ts b/backend/src/goal/Goal.ts index c9e8bb4..aa1b8df 100644 --- a/backend/src/goal/Goal.ts +++ b/backend/src/goal/Goal.ts @@ -9,9 +9,8 @@ var moment_timezone = require('moment-timezone'); import uuid = require('node-uuid'); -import ConditionList = require('../condition/ConditionList'); import Condition = require('../condition/Condition'); -import Challenge = require('../challenge/Challenge'); +import Challenge = require('../challenge/UserChallenge'); import TimeBox = require('../TimeBox'); import Clock = require('../Clock'); import RecurringSession = require('./RecurringSession'); @@ -20,39 +19,35 @@ import RecurringSession = require('./RecurringSession'); class Goal { private id; private name:string; - private conditionsList:ConditionList; - - private startDate:moment.Moment; - private endDate:moment.Moment; - - private durationInDays:number; - private recurringSession:RecurringSession; - private badgeID:string; + private conditionsArray:Condition[] = []; - constructor(name:string, startDate:moment.Moment, endDate:moment.Moment, durationInDays:number, badgeID:string, id = null, recurringSession:RecurringSession = new RecurringSession('month')) { - if (!name) { - throw new Error('Bad argument : name given is null'); - } + private beginningOfValidityPeriod:moment.Moment; + private endOfValidityPeriod:moment.Moment; - this.conditionsList = new ConditionList(); + private recurringSession:RecurringSession; - this.badgeID = badgeID; + constructor(id:string, name:string, badgeID:string, beginningOfValidityPeriod:moment.Moment, + endOfValidityPeriod:moment.Moment, recurringSession:RecurringSession) { + this.id = id; this.name = name; + this.badgeID = badgeID; + - this.id = (id) ? id : uuid.v4(); + this.beginningOfValidityPeriod = beginningOfValidityPeriod; + this.endOfValidityPeriod = endOfValidityPeriod; - if (startDate != null && endDate != null && endDate.isBefore(startDate)) { - throw new Error('End date is before start date'); - } + this.recurringSession = recurringSession; + } - this.startDate = startDate; - this.endDate = endDate; - this.recurringSession = recurringSession; + getBeginningOfValidityPeriod():moment.Moment { + return this.beginningOfValidityPeriod; + } - this.durationInDays = durationInDays; + getEndOfValidityPeriod():moment.Moment { + return this.endOfValidityPeriod; } getStartDateOfSession(now) { @@ -67,90 +62,123 @@ class Goal { return this.badgeID; } - public getUUID() { + getUUID() { return this.id; } - public hasUUID(aUUID:string):boolean { + hasUUID(aUUID:string):boolean { return this.id === aUUID; } - public setUUID(aUUID) { + setUUID(aUUID) { this.id = aUUID; } - public setTimeBoxes(newTimeBox:TimeBox) { - this.conditionsList.setTimeBoxes(newTimeBox); - } - public getStartDate():moment.Moment { - return this.startDate; + getName():string { + return this.name; } - public getEndDate():moment.Moment { - return this.endDate; + public addCondition(condition:Condition) { + this.conditionsArray.push(condition); } - public getDuration():number { - return this.durationInDays; - } + public evaluate(data:any, challenge:Challenge):any { - public getName():string { - return this.name; - } + var result:any = {}; - public addCondition(expression:Condition) { - this.conditionsList.addCondition(expression); - } + var numberOfConditions:number = 0; + var numberOfConditionsAchieved:number = 0; + + var globalPercentageAchieved:number = 0; + + var achieved:boolean = true; + + var conditionsDescription:any[] =[]; + + for (var i = 0; i < this.conditionsArray.length; i++) { + var currentCondition:Condition = this.conditionsArray[i]; - public evaluate(values:any, challenge:Challenge = null):boolean { + var currentConditionDescription:any = data[currentCondition.getID()]; + var currentConditionState = currentCondition.evaluate(currentConditionDescription.values, currentConditionDescription); + conditionsDescription.push(currentConditionState); - if (challenge != null) { - challenge.resetProgress(); + achieved = achieved && currentConditionState.achieved; + globalPercentageAchieved += currentConditionState.percentageAchieved; + + result[currentCondition.getID()] = currentConditionState; + + numberOfConditions++; + numberOfConditionsAchieved = (currentConditionState.achieved)?numberOfConditionsAchieved+1 : numberOfConditionsAchieved; } - return this.conditionsList.evaluate(values, challenge); - } + result['conditions'] = conditionsDescription; - public getRequired():any { - return this.conditionsList.getRequired(); - } + var percentageAchieved:number = (globalPercentageAchieved) / numberOfConditions; + result['percentageAchieved'] = percentageAchieved; + result['achieved'] = achieved; - public getConditions():ConditionList { - return this.conditionsList; + return result; } - public getData():any { - return { - "name": this.name, - "conditions": this.conditionsList.getDataInJSON(), - "timeBox": { - "startDate": this.startDate, - "endDate": this.endDate - }, - "durationInDays": this.durationInDays, - "badge": this.badgeID + public getRequired(startDateOfChallenge, endDateOfChallenge):any { + + var result:any = {}; + for(var conditionIndex in this.conditionsArray) { + var currentCondition = this.conditionsArray[conditionIndex]; + var currentConditionID = currentCondition.getID(); + var currentConditionRequired = currentCondition.getRequiredByCondition(startDateOfChallenge, endDateOfChallenge); + result[currentConditionID] = currentConditionRequired; } + + + return result; + } + + public getConditions():Condition[] { + return this.conditionsArray; } public getDataInJSON():any { return { id: this.id, name: this.name, - timeBox: { - startDate: this.startDate, - endDate: this.endDate + validityPeriod: { + start: this.beginningOfValidityPeriod, + end: this.endOfValidityPeriod }, - duration: this.recurringSession.getDescription(), - conditions: this.conditionsList.getDataInJSON(), + recurringPeriod: this.recurringSession.getDescription(), + conditions: this.getDataOfConditionsInJSON(), badgeID: this.badgeID } } + public getDataOfConditionsInJSON():any { + var result:any[] = []; + + for (var i = 0; i < this.conditionsArray.length; i++) { + result.push(this.conditionsArray[i].getDataInJSON()); + } + + return result; + } + + getStringRepresentation():string { - return '\n#' + this.id + '\n' - + '\t' + this.name + '\t-\t' + this.startDate.toISOString() + ' :: ' + this.endDate.toISOString() + '\n' + - ' - Récurrent : ' + this.recurringSession.getDescription() + '\n' + this.conditionsList.getStringRepresentation(); + return '\n#' + this.id + '\t' + this.name + '\n-\t' + this.beginningOfValidityPeriod.toISOString() + ' :: ' + this.endOfValidityPeriod.toISOString() + '\n' + + ' - Récurrent : ' + this.recurringSession.getDescription() + '\n' + this.getStringRepresentationOfCondition(); + } + + + getStringRepresentationOfCondition():string { + var result:string = ''; + + for(var currentConditionIndex in this.conditionsArray) { + var currentCondition = this.conditionsArray[currentConditionIndex]; + result += '\t|\t\t' + currentCondition.getStringRepresentation(); + } + + return result; } } diff --git a/backend/src/goal/GoalFactory.ts b/backend/src/goal/GoalFactory.ts index 15397df..8fbc219 100644 --- a/backend/src/goal/GoalFactory.ts +++ b/backend/src/goal/GoalFactory.ts @@ -4,6 +4,7 @@ var moment = require('moment'); var moment_timezone = require('moment-timezone'); +import UUID = require('node-uuid'); import Goal = require('./Goal'); import ConditionFactory = require('../condition/factory/ConditionFactory'); @@ -19,47 +20,75 @@ class GoalFactory { this.conditionFactory = new ConditionFactory(); } - public createGoal(data:any):Goal { - var goalName:string = data.name; - - var startDateOfValidityPeriod:moment.Moment = Clock.getMomentFromString(data.timeBox.startDate); - var endDateOfValidityPeriod:moment.Moment = Clock.getMomentFromString(data.timeBox.endDate); - - var durationAllowedDesc:string = data.duration; - var durationAllowed:number = 0; - - switch (durationAllowedDesc) { - case 'day': - durationAllowed = 1; - break; - case 'week' : - durationAllowed = 7; - break; - case 'month' : - durationAllowed = 30; - break; - default : - throw new BadArgumentException('Can not build goal. Given duration allowed' + durationAllowedDesc + ' is unknown'); + public restoreGoal(data:any):Goal { + if (data.id == null) { + throw new BadArgumentException('Can not create given goal because field "id" is null'); } + return this.createGoal(data); + } + public createGoal(data:any):Goal { + + this.checkData(data); + var goalID = (data.id == null) ? UUID.v4() : data.id; + var goalName:string = data.name; var badge:string = data.badgeID; - var goalID:string = data.id; - var recurringType = new RecurringSession(durationAllowedDesc); + var startDateOfValidityPeriod:moment.Moment = Clock.getMomentFromString(data.validityPeriod.start); + var endDateOfValidityPeriod:moment.Moment = Clock.getMomentFromString(data.validityPeriod.end); - var newGoal:Goal = new Goal(goalName, startDateOfValidityPeriod, endDateOfValidityPeriod, - durationAllowed, badge, goalID,recurringType); + var recurringType:string = data.recurringPeriod; + var recurringPeriod = new RecurringSession(recurringType); + + var newGoal:Goal = new Goal(goalID, goalName, badge, startDateOfValidityPeriod, endDateOfValidityPeriod, recurringPeriod); var goalConditions:any[] = data.conditions; for (var i = 0; i < goalConditions.length; i++) { - var currentExpression = this.conditionFactory.createCondition(goalConditions[i], data.timeBox, durationAllowed); + var currentExpression = this.conditionFactory.createCondition(goalConditions[i]); newGoal.addCondition(currentExpression); } // console.log("Creation de l'objectif", goalName, "valide du", startDateOfValidityPeriod, "au", endDateOfValidityPeriod, "avec le badge", newGoal.getBadgeID()); return newGoal; } + + private checkData(data:any) { + if (data.name == null) { + throw new BadArgumentException('Can not create given goal because field "name" is null'); + } + + if (data.badgeID == null) { + throw new BadArgumentException('Can not create given goal because field "badgeID" is null'); + } + + if (data.conditions == null) { + throw new BadArgumentException('Can not create given goal because array "conditions" is null'); + } + + if (data.recurringPeriod == null) { + throw new BadArgumentException('Can not create given goal because field "recurringPeriod" is null'); + } + + if (data.validityPeriod == null) { + throw new BadArgumentException('Can not create given goal because field "validityPeriod" is missing'); + } + + if (data.validityPeriod.start == null) { + throw new BadArgumentException('Can not create given goal because field "validityPeriod.start" is null'); + } + + if (data.validityPeriod.end == null) { + throw new BadArgumentException('Can not create given goal because field "validityPeriod.end" is null'); + } + + var startDateOfValidityPeriod:moment.Moment = Clock.getMomentFromString(data.validityPeriod.start); + var endDateOfValidityPeriod:moment.Moment = Clock.getMomentFromString(data.validityPeriod.end); + + if (startDateOfValidityPeriod != null && endDateOfValidityPeriod != null && endDateOfValidityPeriod.isBefore(startDateOfValidityPeriod)) { + throw new BadArgumentException('Can not create given goal because "validityPeriod.end" is before "validityPeriod.start"'); + } + } } export = GoalFactory; \ No newline at end of file diff --git a/backend/src/goal/GoalRepository.ts b/backend/src/goal/GoalRepository.ts index 6e873ee..eeb3e35 100644 --- a/backend/src/goal/GoalRepository.ts +++ b/backend/src/goal/GoalRepository.ts @@ -1,10 +1,12 @@ import Goal = require('./Goal'); import GoalFactory = require('./GoalFactory'); import BadgeRepository = require('../badge/BadgeRepository'); -import ChallengeRepository = require('../challenge/ChallengeRepository'); -import Challenge = require('../challenge/Challenge'); +import UserChallengeRepository = require('../challenge/UserChallengeRepository'); +import TeamChallengeRepository = require('../challenge/TeamChallengeRepository'); +import UserChallenge = require('../challenge/UserChallenge'); import Badge = require('../badge/Badge'); import User = require('../user/User'); +import Team = require('../user/Team'); class GoalDefinitionRepository { @@ -59,7 +61,8 @@ class GoalDefinitionRepository { return null; } - getListOfUntakedGoalInJSONFormat(user:User, challengeRepository:ChallengeRepository) { + // TODO DELETE OR + getListOfNotTakenGoalInJSONFormat(user:User|Team, challengeRepository:UserChallengeRepository|TeamChallengeRepository) { var result = []; var currentChallengesID:string[] = user.getCurrentChallenges(); @@ -67,14 +70,12 @@ class GoalDefinitionRepository { var takenGoals:Goal[] = []; for (var currentChallengeIDIndex in currentChallengesID) { var currentChallengeID:string = currentChallengesID[currentChallengeIDIndex]; - var currentChallenge:Challenge = challengeRepository.getGoalInstance(currentChallengeID); - takenGoals.push(currentChallenge.getGoalDefinition()); + var currentChallenge = challengeRepository.getChallengeByID(currentChallengeID); + takenGoals.push(currentChallenge.getGoal()); } var goals:Goal[] = this.diffBetweenTakenGoalsAndAvailableGoals(takenGoals, this.goals); - var result = []; - for (var goalIndex in goals) { var currentGoal = goals[goalIndex]; @@ -93,7 +94,7 @@ class GoalDefinitionRepository { for (var currentAvailableGoalIndex in availableGoals) { var currentAvailableGoal = availableGoals[currentAvailableGoalIndex]; - if(!this.goalExistsIn(currentAvailableGoal.getUUID(), takenGoals)) { + if (!this.goalExistsIn(currentAvailableGoal.getUUID(), takenGoals)) { result.push(currentAvailableGoal); } } @@ -127,19 +128,6 @@ class GoalDefinitionRepository { return result; } - public evaluateGoal(data:any):boolean { - var goalID:string = data.id; - var goal:Goal = this.getGoal(goalID); - - var goalValues:any[] = data.values; - var values = []; - for (var i = 0; i < goalValues.length; i++) { - values.push(goalValues[i].value); - } - - return goal.evaluate(values); - } - public getDataInJSON():any { var result:any[] = []; diff --git a/backend/src/user/BadgeIDsToNumberOfTimesEarnedMap.ts b/backend/src/user/BadgeIDsToNumberOfTimesEarnedMap.ts new file mode 100644 index 0000000..96abc51 --- /dev/null +++ b/backend/src/user/BadgeIDsToNumberOfTimesEarnedMap.ts @@ -0,0 +1,10 @@ +/** + * Map of finished badges
+ * key : badgeID, + * associated : number of times that you earned this badge + */ +interface BadgeIDsToNumberOfTimesEarnedMap { + [idBadge:number]: number; +} + +export = BadgeIDsToNumberOfTimesEarnedMap; \ No newline at end of file diff --git a/backend/src/user/Entity.ts b/backend/src/user/Entity.ts new file mode 100644 index 0000000..bc3dc46 --- /dev/null +++ b/backend/src/user/Entity.ts @@ -0,0 +1,131 @@ +import uuid = require('node-uuid'); + +import Goal = require('../goal/Goal'); +import Challenge = require('../challenge/UserChallenge'); +import Badge = require('../badge/Badge'); +import BadgeIDsToNumberOfTimesEarnedMap = require('./BadgeIDsToNumberOfTimesEarnedMap'); + +import BadArgumentException = require('../exceptions/BadArgumentException'); + +class Entity { + + private id; + private name:string; + protected currentChallenges:string[] = []; + private badgesMap:BadgeIDsToNumberOfTimesEarnedMap = {}; + + constructor(id:string, name:string, currentChallenges:string[], + finishedBadgesMap:BadgeIDsToNumberOfTimesEarnedMap) { + + this.id = id; + + this.name = name; + this.currentChallenges = currentChallenges; + this.badgesMap = finishedBadgesMap; + } + + + getUUID() { + return this.id; + } + + hasUUID(aUUID:string):boolean { + return this.id === aUUID; + } + + setUUID(aUUID:string):void { + this.id = aUUID; + } + + getName():string { + return this.name; + } + + hasName(name:string):boolean { + return this.getName() === name; + } + + setName(name:string):void { + this.name = name; + } + + getCurrentChallenges():string [] { + return this.currentChallenges; + } + + addChallengeFromGoal(goal:Goal):Challenge { + return null; + } + + public addChallenge(challengeID:string):void { + if (!challengeID) { + throw new Error('Can not add a new goal to user ' + this.getName() + ' given goal is null'); + } + + this.currentChallenges.push(challengeID); + } + + public deleteChallenge(challengeID:string):void { + + var challengeIndex:number = this.getChallenge(challengeID); + if (challengeIndex == -1) { + throw new BadArgumentException('Can not find given challenge ID'); + } + else { + this.currentChallenges.splice(challengeIndex, 1); + } + + console.log("UserChallenge deleted ! Current challenges:", this.currentChallenges); + } + + public getChallenge(challengeID:string):number { + var result:number = -1; + + for (var currentChallengeIndex = 0; currentChallengeIndex < this.currentChallenges.length; currentChallengeIndex++) { + if (this.currentChallenges[currentChallengeIndex] === challengeID) { + result = currentChallengeIndex; + } + } + + return result; + } + + public setChallenges(challenges:string[]):void { + this.currentChallenges = challenges; + } + + public getBadges():BadgeIDsToNumberOfTimesEarnedMap { + return this.badgesMap; + } + + public getBadgesID():string[] { + return Object.keys(this.badgesMap); + } + + public setBadges(finishedBadges:BadgeIDsToNumberOfTimesEarnedMap) { + this.badgesMap = finishedBadges; + } + + public addBadge(badgeID:string) { + if (!badgeID) { + throw new BadArgumentException('Can not add given badge to user' + this.getName() + '. Badge given is null'); + } + + if (this.badgesMap.hasOwnProperty(badgeID)) { + this.badgesMap[badgeID]++; + } else { + this.badgesMap[badgeID] = 1; + } + } + + public getDataInJSON():any { + return { + id: this.id, + name: this.name, + currentChallenges: this.currentChallenges, + finishedBadgesMap: this.badgesMap + } + } +} + +export = Entity; \ No newline at end of file diff --git a/backend/src/user/Team.ts b/backend/src/user/Team.ts new file mode 100644 index 0000000..f478236 --- /dev/null +++ b/backend/src/user/Team.ts @@ -0,0 +1,189 @@ +import uuid = require('node-uuid'); +import BadArgumentException = require('../exceptions/BadArgumentException'); + +import Entity = require('./Entity'); +import BadgeIDsToNumberOfTimesEarnedMap = require('./BadgeIDsToNumberOfTimesEarnedMap'); +import User = require('./User'); +import Goal = require('../goal/Goal'); +import TeamChallengeFactory = require('../challenge/TeamChallengeFactory'); +import TeamChallenge = require('../challenge/TeamChallenge'); +import UserChallengeRepository = require('../challenge/UserChallengeRepository'); + +class Team { + private id; + private name:string; + private currentChallenges:string[] = []; + private badgesMap:BadgeIDsToNumberOfTimesEarnedMap = {}; + + private members:User[] = []; + private leader:User; + + private challengeFactory:TeamChallengeFactory; + + constructor(id:string, name:string, leader:User, members:User[], + currentChallenges:string[], badgesMap:BadgeIDsToNumberOfTimesEarnedMap, teamChallengeFactory:TeamChallengeFactory) { + + this.id = id; + this.name = name; + this.badgesMap = badgesMap; + this.currentChallenges = currentChallenges; + + this.leader = leader; + this.members = members; + + this.challengeFactory = teamChallengeFactory; + } + + getUUID() { + return this.id; + } + + hasUUID(aUUID:string):boolean { + return this.id === aUUID; + } + + setUUID(aUUID:string):void { + this.id = aUUID; + } + + getName():string { + return this.name; + } + + hasName(name:string):boolean { + return this.getName() === name; + } + + setName(name:string):void { + this.name = name; + } + + getCurrentChallenges():string [] { + return this.currentChallenges; + } + + addChallenge(goal:Goal, userChallengeRepository:UserChallengeRepository, now:moment.Moment):TeamChallenge { + var newChallenge = this.challengeFactory.createTeamChallenge(this, goal,userChallengeRepository, now); + + /*FIXME + // Check if we try + if (newChallenge.getEndDate().isAfter(goal.getEndOfValidityPeriod())) { + return null; + } + + this.currentChallenges.push(newChallenge.getID()); + return newChallenge; + */ + + this.currentChallenges.push(newChallenge.getID()); + return newChallenge; + } + + addBadge(badgeID:string) { + if (!badgeID) { + throw new BadArgumentException('Can not add given badge to team' + this.getName() + '. Badge given is null'); + } + + if (this.badgesMap.hasOwnProperty(badgeID)) { + this.badgesMap[badgeID]++; + } else { + this.badgesMap[badgeID] = 1; + } + } + + deleteChallenge(challenge:TeamChallenge):void { + + var challengeIndex:number = this.getChallengeByID(challenge.getID()); + + if (challengeIndex == -1) { + throw new BadArgumentException('Can not find given challenge ID'); + } + + // Remove team challenge from team + this.currentChallenges.splice(challengeIndex, 1); + + // Remove challenges from team's members + challenge.removeFromMembers(); + + + console.log("TeamChallenge deleted ! Current challenges:", this.currentChallenges); + } + + private getChallengeByID(challengeID:string):number { + var result:number = -1; + + for (var currentChallengeIndex = 0; currentChallengeIndex < this.currentChallenges.length; currentChallengeIndex++) { + if (this.currentChallenges[currentChallengeIndex] === challengeID) { + result = currentChallengeIndex; + } + } + + return result; + } + + getLeader():User { + return this.leader; + } + + getMembers():User[] { + return this.members; + } + + hasLeader(aUserID:string):boolean { + return this.leader.hasUUID(aUserID); + } + + hasMember(aUserID:string):boolean { + for (var currentMemberIndex in this.members) { + var currentMember = this.members[currentMemberIndex]; + if (currentMember.hasUUID(aUserID)) { + return true; + } + } + + return false; + } + + getStringDescription():string { + return 'Team:#' + this.getUUID() + '\t|Name : ' + this.getName() + '\t|LEADER : ' + this.leader + '\n'; + } + + getStringDescriptionOfMembers():string { + var result = ''; + + for (var currentMemberIndex in this.members) { + var currentMember = this.members[currentMemberIndex]; + result += '\t\t- ' + currentMember.getName() + '\n'; + } + + return result; + } + + getBadgesID():string[] { + return Object.keys(this.badgesMap); + } + + getBadges():BadgeIDsToNumberOfTimesEarnedMap { + return this.badgesMap; + } + + public getDataInJSON():any { + var membersIDs:any[] =[]; + for(var memberIndex in this.members) { + var currentMember = this.members[memberIndex]; + var currentMemberID = currentMember.getUUID(); + membersIDs.push(currentMemberID); + } + + return { + id: this.id, + name: this.name, + leader: this.leader.getUUID(), + members: membersIDs, + currentChallenges: this.currentChallenges, + finishedBadgesMap: this.badgesMap + } + } +} + +export = Team; \ No newline at end of file diff --git a/backend/src/user/TeamFactory.ts b/backend/src/user/TeamFactory.ts new file mode 100644 index 0000000..17e9031 --- /dev/null +++ b/backend/src/user/TeamFactory.ts @@ -0,0 +1,57 @@ +import uuid = require('node-uuid'); + +import Entity = require('./Entity'); +import Team = require('./Team'); +import User = require('./User'); +import UserRepository = require('./UserRepository'); +import TeamChallengeFactory = require ('../challenge/TeamChallengeFactory'); +/* + + if (name == null) { + throw new BadArgumentException('Can not build team, given name is null'); + } + + if (leader == null) { + throw new BadArgumentException('Can not build team ' + name + ' given leader is null'); + } + + if (members == null) { + throw new BadArgumentException('Can not build team ' + name + ' given members is null'); + } + + if (currentChallenges == null) { + throw new BadArgumentException('Can not build team ' + name + ' given current challenges are null'); + } + + if (badgesMap == null) { + throw new BadArgumentException('Can not build team ' + name + ' given badges map is null'); + } + */ +class TeamFactory { + public createTeam(data:any, userRepository:UserRepository, teamChallengeFactory:TeamChallengeFactory):Team { + var teamID:string = data.id; + teamID = (teamID == null) ? uuid.v4() : teamID; + + var teamName:string = data.name; + + var currentChallenges:string[] = data.currentChallenges; + var finishedBadgesMap:any = data.finishedBadgesMap; + + var members:User[] = []; + + var membersIDs:string[] = data.members; + for (var membersIDsIndex in membersIDs) { + var currentMemberID = membersIDs[membersIDsIndex]; + var currentMember = userRepository.getUser(currentMemberID); + members.push(currentMember); + } + + var leaderID:string = data.leader; + var leader = userRepository.getUser(leaderID); + + var team:Team = new Team(teamID, teamName, leader, members, currentChallenges, finishedBadgesMap, teamChallengeFactory); + return team; + } +} + +export = TeamFactory; \ No newline at end of file diff --git a/backend/src/user/TeamRepository.ts b/backend/src/user/TeamRepository.ts new file mode 100644 index 0000000..e9bab4d --- /dev/null +++ b/backend/src/user/TeamRepository.ts @@ -0,0 +1,74 @@ +import Team = require('./Team'); + +class TeamRepository { + + private teams:Team[] = [] + + teamExists(teamID:string, successCallBack:Function, failCallBack:Function) { + var team:Team = this.getTeam(teamID); + + (team != null) ? successCallBack(team) : failCallBack('User not found'); + } + + public addTeam(team:Team) { + this.teams.push(team); + } + + public getTeam(aUUID:string):Team { + for (var i in this.teams) { + var currentTeam = this.teams[i]; + if (currentTeam.hasUUID(aUUID)) { + return currentTeam; + } + } + + return null; + } + + getTeamsByMember(aUserID:string):Team[] { + var teams:Team[] = []; + + for (var currentTeamIndex in this.teams) { + var team = this.teams[currentTeamIndex]; + if (team.hasMember(aUserID)) { + teams.push(team); + } + } + + return teams; + } + + hasMember(aUserID:string):boolean { + for (var currentTeamIndex in this.teams) { + var team = this.teams[currentTeamIndex]; + if (team.hasMember(aUserID)) { + return true; + } + } + + return false; + } + + public getDataInJSON():any { + var result:any[] = []; + + for (var currentTeamIndex in this.teams) { + var currentTeam = this.teams[currentTeamIndex]; + result.push(currentTeam.getDataInJSON()); + } + + return result; + } + + public displayShortState() { + console.log("\n\n+++\t Etat du repository des Teams\t+++"); + + for (var currentTeamIndex in this.teams) { + var currentTeam = this.teams[currentTeamIndex]; + console.log("#", currentTeam.getUUID(), "\t\nLeader:", currentTeam.getLeader().getName(), "\t| \tName : '", currentTeam.getName(), "'\n", "\tMembers:\n", currentTeam.getStringDescriptionOfMembers()); + + } + } +} + +export = TeamRepository; \ No newline at end of file diff --git a/backend/src/user/User.ts b/backend/src/user/User.ts index e0c8a2c..58f7109 100644 --- a/backend/src/user/User.ts +++ b/backend/src/user/User.ts @@ -1,77 +1,103 @@ /// -import uuid = require('node-uuid'); -import Goal = require('../goal/Goal'); -import Challenge = require('../challenge/Challenge'); -import Badge = require('../badge/Badge'); +/// +/// + +import uuid = require("node-uuid"); +var moment = require('moment'); +var moment_timezone = require('moment-timezone'); +import BadgeIDsToNumberOfTimesEarnedMap = require('./BadgeIDsToNumberOfTimesEarnedMap'); import BadArgumentException = require('../exceptions/BadArgumentException'); -/** - * Map of finished badges
- * key : badgeID, - * associated : number of times that you earned this badge - */ -interface BadgeIDsToNumberOfTimesEarnedMap { - [idBadge:number]: number; -} +import Goal = require('../goal/Goal'); +import Challenge = require('../challenge/UserChallenge'); +import ChallengeFactory = require('../challenge/UserChallengeFactory'); class User { private id; private name:string; private currentChallenges:string[] = []; - private finishedBadgesMap:BadgeIDsToNumberOfTimesEarnedMap = {}; + private badgesMap:BadgeIDsToNumberOfTimesEarnedMap = {}; - constructor(name:string, id = null, currentChallenges:string[] = [], finishedBadgesMap:BadgeIDsToNumberOfTimesEarnedMap = {}) { + private mapSymbolicNameToSensor:any = {}; + private challengeFactory:ChallengeFactory; - this.id = (id) ? id : uuid.v4(); + constructor(name:string, mapSymbolicNameToSensor:any, currentChallenges:string[], + finishedBadgesMap:BadgeIDsToNumberOfTimesEarnedMap, challengeFactory:ChallengeFactory, id = null) { + this.id = id; this.name = name; - this.currentChallenges = currentChallenges; - this.finishedBadgesMap = finishedBadgesMap; - } + this.mapSymbolicNameToSensor = mapSymbolicNameToSensor; - getCurrentChallenges():string [] { - return this.currentChallenges; + this.currentChallenges = (currentChallenges == null) ? [] : currentChallenges; + this.badgesMap = (finishedBadgesMap == null) ? [] : finishedBadgesMap; + + this.challengeFactory = challengeFactory; } - public getUUID() { + + getUUID() { return this.id; } - public hasUUID(aUUID:string):boolean { + hasUUID(aUUID:string):boolean { return this.id === aUUID; } - public setUUID(aUUID:string):void { + setUUID(aUUID:string):void { this.id = aUUID; } - public getName():string { + getName():string { return this.name; } - public hasName(name:string):boolean { + hasName(name:string):boolean { return this.getName() === name; } - public setName(name:string):void { - this.name = name; + getBadges():BadgeIDsToNumberOfTimesEarnedMap { + return this.badgesMap; } - public addChallenge(challengeID:string):void { - if (!challengeID) { - throw new Error('Can not add a new goal to user ' + this.getName() + ' given goal is null'); + addBadge(badgeID:string) { + if (!badgeID) { + throw new BadArgumentException('Can not add given badge to user' + this.getName() + '. Badge given is null'); } - this.currentChallenges.push(challengeID); + if (this.badgesMap.hasOwnProperty(badgeID)) { + this.badgesMap[badgeID]++; + } else { + this.badgesMap[badgeID] = 1; + } + } + + getCurrentChallenges():string [] { + return this.currentChallenges; } - public deleteChallenge(challengeID:string):void { + wipeCurrentChallenges():void { + this.currentChallenges = []; + } + + addChallenge(goal:Goal, now:moment.Moment, takenBy = null):Challenge { + var newChallenge = this.challengeFactory.createChallenge(goal, this, now, takenBy); - var challengeIndex:number = this.getChallenge(challengeID); + // Check if we try + if (newChallenge.getEndDate().isAfter(goal.getEndOfValidityPeriod())) { + return null; + } + + this.currentChallenges.push(newChallenge.getID()); + return newChallenge; + } + + deleteChallenge(challengeID:string):void { + + var challengeIndex:number = this.getChallengeByID(challengeID); if (challengeIndex == -1) { throw new BadArgumentException('Can not find given challenge ID'); } @@ -79,17 +105,10 @@ class User { this.currentChallenges.splice(challengeIndex, 1); } - console.log("Challenge deleted ! Current challenges:", this.currentChallenges); + console.log("UserChallenge deleted ! Current challenges:", this.currentChallenges); } - /** - * This method will return the index of the given - * challenge id - * @param challengeID - * @returns {number} - * Index of given challenge ID - */ - public getChallenge(challengeID:string):number { + private getChallengeByID(challengeID:string):number { var result:number = -1; for (var currentChallengeIndex = 0; currentChallengeIndex < this.currentChallenges.length; currentChallengeIndex++) { @@ -101,46 +120,18 @@ class User { return result; } - public getChallenges():string[] { - return this.currentChallenges; - } - - public setChallenges(challenges:string[]):void { - this.currentChallenges = challenges; - } - - public getFinishedBadges():BadgeIDsToNumberOfTimesEarnedMap { - return this.finishedBadgesMap; - } - - public setFinishedBadges(finishedBadges:BadgeIDsToNumberOfTimesEarnedMap) { - this.finishedBadgesMap = finishedBadges; - } - - public getFinishedBadgesID():string[] { - return Object.keys(this.finishedBadgesMap); - } - - public addFinishedBadge(badgeID:string) { - if (!badgeID) { - throw new BadArgumentException('Can not add given badge to user' + this.getName() + '. Badge given is null'); - } - - if (this.finishedBadgesMap.hasOwnProperty(badgeID)) { - this.finishedBadgesMap[badgeID]++; - } else { - this.finishedBadgesMap[badgeID] = 1; - } + getMapSymbolicNameToSensor():any { + return this.mapSymbolicNameToSensor; } public getDataInJSON():any { return { id: this.id, name: this.name, + mapSymbolicNameToSensor: this.mapSymbolicNameToSensor, currentChallenges: this.currentChallenges, - finishedBadgesMap: this.finishedBadgesMap + finishedBadgesMap: this.badgesMap } } } - -export = User; +export = User; \ No newline at end of file diff --git a/backend/src/user/UserFactory.ts b/backend/src/user/UserFactory.ts index f60a27f..a115892 100644 --- a/backend/src/user/UserFactory.ts +++ b/backend/src/user/UserFactory.ts @@ -1,13 +1,17 @@ +import Entity = require('./Entity'); import User = require('./User'); +import ChallengeFactory = require('../challenge/UserChallengeFactory'); class UserFactory { - public createUser(data:any):User { + public createUser(data:any, challengeFactory:ChallengeFactory):User { var userID:string = data.id; var userName:string = data.name; + var mapSymoblicNameToSensor:any = data.mapSymbolicNameToSensor; + var currentChallenges:string[] = data.currentChallenges; var finishedBadgesMap:any = data.finishedBadgesMap; - var user:User = new User(userName, userID, currentChallenges, finishedBadgesMap); + var user:User = new User(userName, mapSymoblicNameToSensor, currentChallenges, finishedBadgesMap, challengeFactory, userID); return user; } } diff --git a/backend/src/user/UserRepository.ts b/backend/src/user/UserRepository.ts index 5664836..a27a4cf 100644 --- a/backend/src/user/UserRepository.ts +++ b/backend/src/user/UserRepository.ts @@ -6,10 +6,27 @@ class UserRepository { private currentUser:User; + userExists(userID:string, successCallBack:Function, failCallBack:Function) { + var user:User = this.getUser(userID); + + (user != null) ? successCallBack(user) : failCallBack('User not found'); + } + public addUser(user:User) { this.users.push(user); } + public getUserByName(username:string):User { + for (var i in this.users) { + var currentUser = this.users[i]; + if (currentUser.hasName(username)) { + return currentUser; + } + } + + return null; + } + public getUser(aUUID:string):User { for (var i in this.users) { var currentUser = this.users[i]; @@ -32,7 +49,7 @@ class UserRepository { public getDataInJSON():any { var result:any[] = []; - for(var currentUserIndex in this.users) { + for (var currentUserIndex in this.users) { var currentUser = this.users[currentUserIndex]; result.push(currentUser.getDataInJSON()); } @@ -43,9 +60,9 @@ class UserRepository { public displayShortState() { console.log("\n\n+++\t Etat du repository des utilisateurs\t+++"); - for(var currentUserIndex in this.users) { + for (var currentUserIndex in this.users) { var currentUser = this.users[currentUserIndex]; - console.log("#",currentUser.getUUID(),"\t |\tUser : '", currentUser.getName(), "'") + console.log("#", currentUser.getUUID(), "\n\tUser : '", currentUser.getName(), "'\n\t", currentUser.getMapSymbolicNameToSensor()); } } } diff --git a/backend/stub_values.json b/backend/stub_values.json index cdd7b22..f184126 100644 --- a/backend/stub_values.json +++ b/backend/stub_values.json @@ -1,14 +1,22 @@ { - "TEMP_443V": { - "values": [ - { - "date": "1438346523000", - "value": 20 - }, - { - "date":"1438778523000", + "TEMP_443V": [ + { + "date": 1440849600000, + "value": 20 + }, + { + "date":1440856800000, + "value":22 + } + ], + "AC_555V": [ + { + "date": 1440849600000, + "value": 24 + }, + { + "date":1440856800000, "value":22 } - ] - } + ] } \ No newline at end of file diff --git a/backend/stub_values_test.json b/backend/stub_values_test.json new file mode 100644 index 0000000..f67cd8f --- /dev/null +++ b/backend/stub_values_test.json @@ -0,0 +1,12 @@ +{ + "TEMP_443V": [ + { + "date": 1440849600000, + "value": 20 + }, + { + "date":1440856800000, + "value":22 + } + ] +} \ No newline at end of file diff --git a/backend/tests/TeamTest.ts b/backend/tests/TeamTest.ts new file mode 100644 index 0000000..6961a00 --- /dev/null +++ b/backend/tests/TeamTest.ts @@ -0,0 +1,65 @@ +/// +/// +/// + +import chai = require('chai'); +import sinon = require('sinon'); +var assert = chai.assert; + + +import Team = require('../src/user/Team'); +import User = require('../src/user/User'); + +describe("Test a team", function () { + var aMember:User; + var aMapSymoblicNameToSensor = { + 'TMP_CLI': 'AC_555' + }; + + var anotherMember:User; + var anotherMapSymoblicNameToSensor = { + 'TMP_CLI': 'AC_666' + }; + + var members:User[] = []; + var team:Team; + aMember = new User('Gégé', aMapSymoblicNameToSensor, [], [], null); + anotherMember = new User('Dédé', anotherMapSymoblicNameToSensor, [], [], null); + members = [aMember, anotherMember]; + + + describe('Check its composition', () => { + beforeEach(() => { + team = new Team("id", "Croquette", aMember, members, [], null, null); + }); + it('should have proper leader', () => { + chai.expect(team.hasLeader(aMember.getUUID())).to.be.true; + }); + + it('should have proper members', () => { + chai.expect(team.hasMember(aMember.getUUID())).to.be.true; + chai.expect(team.hasMember(anotherMember.getUUID())).to.be.true; + }); + }); + describe('Check add method', () => { + + beforeEach(() => { + team = new Team("id", "Croquette", aMember, members, [], null, null); + }); + + it('should have no challenge by default', () => { + chai.expect(team.getCurrentChallenges()).to.be.eqls([]); + }); + + it('should have a challenge when it was previously added', () => { + /* FIXME + chai.expect(team.getCurrentChallenges()).to.be.eqls([]); + + var aChallengeID = 'aChallengeID'; + team.addChallenge(aChallengeID); + + chai.expect(team.getCurrentChallenges()).to.be.eqls([aChallengeID]); + */ + }); + }); +}); diff --git a/backend/tests/UserTest.ts b/backend/tests/UserTest.ts index 0eb74a0..4eff80b 100644 --- a/backend/tests/UserTest.ts +++ b/backend/tests/UserTest.ts @@ -9,51 +9,22 @@ var assert = chai.assert; import User = require('../src/user/User'); -describe("Build a User", function () { - var user:User; +describe('User test', () => { - it("should have given name", () => { - user = new User("aName"); - assert.equal(user.getName(), "aName"); - }); -}); + var mapSymbolicNameToSensor:any = { + 'TMP_CLI': 'AC_554' + }; -describe("Add a goal", function () { - var user:User; - - beforeEach(() => { - user = new User("aName"); - }); - - it("should throw an error if given goal is null", () => { - chai.expect(() => user.addChallenge(null)).to.throw(Error); - }); - - it("should add the goal to the user's description", () => { - /* - user.addGoalByDescription(new Goal("a", "a", 0)); - chai.expect(user.getGoals().length).to.be.equals(1); - */ - }); - it("should add the given goal to the user's description", () => { - /* - var goal:Goal = new Goal("a", "a", 0); - user.addGoalByDescription(goal); - chai.expect(user.getGoals().pop()).to.be.equals(goal); - */ - }); -}); + describe("Build a User", function () { + var user:User; + + it("should have given name", () => { + user = new User("aName", mapSymbolicNameToSensor, [], null, null); + assert.equal(user.getName(), "aName"); + }); + }); -describe("evaluate a goal", function () { - var user:User; - beforeEach(() => { - user = new User("aName"); - }); + //TODO TESTS - it("should return false if given goalName doesn't exist", () => { - // var goal:Goal = new Goal("a", "inf", 10); - //user.addGoalByDescription(goal); - //assert.isFalse(user.evaluateGoal("aNameThatDoesntExist", 0)); - }); }); diff --git a/backend/tests/challenge/ChallengeFactoryTest.ts b/backend/tests/challenge/ChallengeFactoryTest.ts deleted file mode 100644 index 63c426f..0000000 --- a/backend/tests/challenge/ChallengeFactoryTest.ts +++ /dev/null @@ -1,122 +0,0 @@ -/// -/// -/// -/// -/// -/// - -var moment = require('moment'); -var moment_timezone = require('moment-timezone'); - -import chai = require('chai'); -import sinon = require('sinon'); -var assert = chai.assert; - -import ChallengeFactory = require('../../src/challenge/ChallengeFactory'); -import GoalRepository = require('../../src/goal/GoalRepository'); -import Goal = require('../../src/goal/Goal'); -import OverallGoalCondition = require('../../src/condition/OverallGoalCondition'); -import GoalExpression = require('../../src/condition/expression/GoalExpression'); -import Operand = require('../../src/condition/expression/Operand'); -import TimeBox = require('../../src/TimeBox'); -import Clock = require('../../src/Clock'); - -describe("GoalInstanceFactory test", () => { - - var factory:ChallengeFactory = new ChallengeFactory(); - var goalDefinitionRepository:GoalRepository = new GoalRepository(null); - - var aGoal:Goal; - var aGoalName:string = "goal 1"; - - var data:any; - - var aGoalID:string = "5d34ae6e-e9ca-4352-9a67-3fdf205cce26"; - - var aGoalName:string = 'badge 1'; - var aGoalDescription:string = 'a desc'; - - var aConditionName:string = 'Temp_cli'; - var aSensorName:string = 'AC_443'; - - var anotherConditionName:string = 'Temp_ext'; - var anotherSensorName:string = 'TEMP_443'; - var now:moment.Moment = moment(new Date(Clock.getNow()).valueOf()); - var endDate:moment.Moment = moment(new Date(now.year(), now.month(), now.date() + 5, now.hours(), now.minutes(), now.seconds()).valueOf()); - - - var conditions:any = {}; - beforeEach(() => { - - aGoal = new Goal(aGoalName, now, endDate, 5, null); - aGoal.setUUID(aGoalID); - - var goalCondition:OverallGoalCondition = new OverallGoalCondition(null, new GoalExpression(new Operand(aConditionName, true), '<', - new Operand(anotherConditionName, true), aGoalDescription), 0, null, null, null); - - aGoal.addCondition(goalCondition); - - goalDefinitionRepository.addGoal(aGoal); - - data = {}; - - data.id = aGoal.getUUID(); - - data.description = aGoalDescription; - - conditions[aConditionName] = aSensorName; - conditions[anotherConditionName] = anotherSensorName; - - data.goal = {}; - - data.goal.id = aGoal.getUUID(); - data.goal.conditions = conditions; - }); - - it("should have proper name when built", () => { - /* - var goalInstance = factory.createGoalInstance(data, goalDefinitionRepository, null, now); - chai.expect(goalInstance.getName()).to.be.equal(aGoalName); - */ - }); - - it("should have proper description when built", () => { - /* - var goalInstance = factory.createGoalInstance(data, goalDefinitionRepository, null, now); - chai.expect(goalInstance.getDescription()).to.be.equal(aGoalDescription); - */ - }); - - it("should have proper sensors when build", () => { - /* - var goalInstance = factory.createGoalInstance(data, goalDefinitionRepository, null, now); - - var timeBox:TimeBox = new TimeBox(now, endDate); - - var timeBoxDesc:any = {}; - timeBoxDesc.startDate = timeBox.getStartDateInStringFormat(); - timeBoxDesc.endDate = timeBox.getEndDateInStringFormat(); - - var expectedConditionsDescription = {}; - expectedConditionsDescription[aSensorName] = timeBoxDesc; - expectedConditionsDescription[anotherSensorName] = timeBoxDesc; - - chai.expect(goalInstance.getSensors()).to.be.eqls(expectedConditionsDescription); - */ - }); - - it('should have proper startDate when built', () => { - /* - var goalInstance = factory.createGoalInstance(data, goalDefinitionRepository, null, now); - chai.expect(goalInstance.getStartDate()).to.be.eq(now); - */ - }); - - it('should have proper endDate when built', () => { - /* - var goalInstance = factory.createGoalInstance(data, goalDefinitionRepository, null, now); - var aEndDate:Date = new Date(now.getFullYear(), now.getMonth(), now.getDate() + aGoal.getDuration(), now.getHours(), now.getMinutes(), now.getSeconds()); - chai.expect(goalInstance.getEndDate().getTime()).to.be.eq(aEndDate.getTime()); - */ - }); -}); \ No newline at end of file diff --git a/backend/tests/challenge/ChallengeTest.ts b/backend/tests/challenge/ChallengeTest.ts deleted file mode 100644 index 2b77684..0000000 --- a/backend/tests/challenge/ChallengeTest.ts +++ /dev/null @@ -1,115 +0,0 @@ -/// -/// -/// -/// -/// -/// - -var moment = require('moment'); -var moment_timezone = require('moment-timezone'); - - -import chai = require('chai'); -import sinon = require('sinon'); -var assert = chai.assert; - -import Challenge = require('../../src/challenge/Challenge'); -import Goal = require('../../src/goal/Goal'); -import GoalExpression = require('../../src/condition/expression/GoalExpression'); -import Operand = require('../../src/condition/expression/Operand'); -import AverageOnValue = require('../../src/condition/AverageOnValue'); -import TimeBox = require('../../src/TimeBox'); -import Clock = require('../../src/Clock'); - -describe("GoalInstance test", () => { - - var goalInstance:Challenge; - var goalDefinition:Goal; - - var aStartDate:moment.Moment = Clock.getMomentFromString('2000-05-01T00:00:00'); - var aDateOfCreation:moment.Moment = Clock.getMomentFromString('2000-05-01T00:00:00'); - var aEndDate:moment.Moment = Clock.getMomentFromString('2000-08-01T00:00:00'); - - var aSymbolicName:string = 'Temperature_cli'; - var anotherSymbolicName:string = 'Temperature_ext'; - - var aSensorName:string = 'AC_443'; - var anotherSensorName:string = 'TEMP_444'; - - var anExpression:GoalExpression = new GoalExpression(new Operand(aSymbolicName, true), '<', new Operand('40', false), 'desc'); - var anotherExpression:GoalExpression = new GoalExpression(new Operand(anotherSymbolicName, true), '>', new Operand('25', false), 'desc'); - - var anAverageCondition:AverageOnValue = new AverageOnValue(null, anExpression, 10, aStartDate, aDateOfCreation, aEndDate, Clock.getMoment(new Date(0,1,0,0,0,0,0).getTime())); - var anotherAverageCondition:AverageOnValue = new AverageOnValue(null, anotherExpression, 10, aStartDate, aDateOfCreation, aEndDate, Clock.getMoment(new Date(0,1,0,0,0,0,0).getTime())); - - beforeEach(() => { - goalDefinition = new Goal("goal1", aStartDate, aEndDate, 100, null); - - goalDefinition.addCondition(anAverageCondition); - goalDefinition.addCondition(anotherAverageCondition); - - var mapSymbolicNameToSensor:any = {}; - mapSymbolicNameToSensor[aSymbolicName] = aSensorName; - mapSymbolicNameToSensor[anotherSymbolicName] = anotherSensorName; - - goalInstance = new Challenge(aStartDate, aEndDate, "the badge for noobs", goalDefinition, mapSymbolicNameToSensor); - }); - - - it("should return sensors required correctly", () => { - var expectedConditionsDescription = {}; - - var timeBox:any = {}; - timeBox.startDate = "2000-04-01 00:00:00"; - timeBox.endDate = "2000-08-01 00:00:00"; - - expectedConditionsDescription[aSensorName] = timeBox; - expectedConditionsDescription[anotherSensorName] = timeBox; - - var result = goalInstance.getSensors(); - - chai.expect(result).to.be.eqls(expectedConditionsDescription); - }); - - it("should call goal definition evaluate on evaluate method", () => { - var goalStubObj = sinon.stub(goalDefinition, "evaluate"); - goalStubObj.returns(true); - - var fakeParams:any = {'a': null, 'b': null}; - - goalInstance.evaluate(fakeParams); - - chai.assert(goalStubObj.calledOnce); - chai.assert(goalStubObj.calledWith(goalInstance.bindSymbolicNameToValue(fakeParams), goalInstance)); - }); - - /* - FIXME - it("should evaluate the goal instance as OK", () => { - var aStartDatePlus1Day:Date = new Date(Date.UTC(2000, 5, 2)); - - var timeBoxObj:TimeBox = new TimeBox(aStartDatePlus1Day, aEndDate); - - var correctValuesDescription = {}; - var valuesForASensorName = {'values': [{date:timeBoxObj.getStartDateInStringFormat(), value:35}]}; - correctValuesDescription[aSensorName] = valuesForASensorName; - - var valuesForAnotherSensorName = {'values': [{date:timeBoxObj.getStartDateInStringFormat(), value:27}]}; - correctValuesDescription[anotherSensorName] = valuesForAnotherSensorName; - - chai.expect(goalInstance.evaluate(correctValuesDescription)).to.be.true; - }); - - it("should evaluate the goal instance as KO", () => { - var incorrectValuesDescription = {}; - - var valuesForASensorName = {'values': [{value: 35}]}; - incorrectValuesDescription[aSensorName] = valuesForASensorName; - - var valuesForAnotherSensorName = {'values': [{value: 20}]}; - incorrectValuesDescription[anotherSensorName] = valuesForAnotherSensorName; - - chai.expect(goalInstance.evaluate(incorrectValuesDescription)).to.be.false; - }); - */ -}); \ No newline at end of file diff --git a/backend/tests/challenge/UserChallengeFactoryTest.ts b/backend/tests/challenge/UserChallengeFactoryTest.ts new file mode 100644 index 0000000..31b86ac --- /dev/null +++ b/backend/tests/challenge/UserChallengeFactoryTest.ts @@ -0,0 +1,124 @@ +/// +/// +/// +/// +/// +/// + +var moment = require('moment'); +var moment_timezone = require('moment-timezone'); + +import chai = require('chai'); +import sinon = require('sinon'); +var assert = chai.assert; + +import ChallengeFactory = require('../../src/challenge/UserChallengeFactory'); +import RecurringSession = require('../../src/goal/RecurringSession'); +import Goal = require('../../src/goal/Goal'); +import GoalExpression = require('../../src/condition/expression/GoalExpression'); +import User = require('../../src/user/User'); +import ExpressionFactory = require('../../src/condition/factory/ExpressionFactory'); +import OverallGoalCondition = require('../../src/condition/OverallGoalCondition'); + +describe("GoalInstanceFactory test", () => { + + var factory:ChallengeFactory = new ChallengeFactory(); + + var aGoalID:string = "5d34ae6e-e9ca-4352-9a67-3fdf205cce26"; + var aGoalName:string = "goal 1"; + var aBadgeID:string = 'badge 1'; + + var now:moment.Moment = moment('2015-08-26T00:00:00'); + var startDate:moment.Moment = moment("2015-08-17T00:00:00"); + var endDate:moment.Moment = moment("2015-09-17T23:59:59"); + + var aRecurringSession:RecurringSession = new RecurringSession('week'); + + var aGoal:Goal = new Goal(aGoalID, aGoalName, aBadgeID, now, endDate, aRecurringSession); + + var aSymbolicName:string = 'Temp_cli'; + var aSensorName:string = 'AC_443'; + + var mapSymbolicNameToSensor:any = {}; + mapSymbolicNameToSensor[aSymbolicName] = aSensorName; + var aUser:User = new User('Gérard', mapSymbolicNameToSensor, [], null, factory); + + var expression:GoalExpression; + var expressionDescription:any = { + valueLeft: { + value: aSymbolicName, + symbolicName: true + }, + valueRight: { + value: "15", + symbolicName: false + }, + comparison: ">" + }; + + var expressionFactory:ExpressionFactory = new ExpressionFactory(); + + var aConditionID = "id1"; + var aConditionDescription = "a desc"; + var aThresholdRate = 80; + + expression = expressionFactory.createExpression(expressionDescription); + var condition = new OverallGoalCondition(aConditionID, aConditionDescription, expression, aThresholdRate, null); + + aGoal.addCondition(condition); + + + var userChallenge = factory.createChallenge(aGoal, aUser, now); + + describe('Constructor', () => { + it("should have proper name when built", () => { + chai.expect(userChallenge.getName()).to.be.equal(aGoalName); + }); + + it("should have proper user when built", () => { + chai.expect(userChallenge.getUser().getName()).to.be.equal(aUser.getName()); + }); + + + it('should have proper startDate when built', () => { + var expectedStartDate = moment('2015-08-24T00:00:00'); + // Because challenge taken during a session are "realigned" with this session + + chai.expect(userChallenge.getStartDate().toISOString()).to.be.eql(expectedStartDate.toISOString()); + + }); + + it('should have proper endDate when built', () => { + var expectedEndDate = moment('2015-08-28T23:59:59.999'); + // Because challenge taken during a session are "realigned" with this session + + chai.expect(userChallenge.getEndDate().toISOString()).to.be.eql(expectedEndDate.toISOString()); + }); + }); + + describe('GetSensor method', () => { + + var result = userChallenge.getSensors(); + + it("should have condition's id of goal, recorded", () => { + chai.expect(result[aConditionID]).not.to.be.null; + }); + + it("should have condition's symbolic names recorded", () => { + chai.expect(result[aConditionID].symbolicNames).to.be.eqls([aSymbolicName]); + }) + + it("should have condition's timeBox recorded", () => { + chai.expect(result[aConditionID].timeBox).not.to.be.null; + }); + + it("should have sensors array built", () => { + chai.expect(result[aConditionID].sensors).to.be.eqls([aSensorName]); + }); + + it("should have sensor entry built", () => { + chai.expect(result[aConditionID][aSensorName]).not.to.be.null; + }); + }); + +}); \ No newline at end of file diff --git a/backend/tests/challenge/UserChallengeTest.ts b/backend/tests/challenge/UserChallengeTest.ts new file mode 100644 index 0000000..a987bf8 --- /dev/null +++ b/backend/tests/challenge/UserChallengeTest.ts @@ -0,0 +1,98 @@ +/// +/// +/// +/// +/// +/// + +var moment = require('moment'); +var moment_timezone = require('moment-timezone'); + + +import chai = require('chai'); +import sinon = require('sinon'); +var assert = chai.assert; + +import ChallengeFactory = require('../../src/challenge/UserChallengeFactory'); +import RecurringSession = require('../../src/goal/RecurringSession'); +import Goal = require('../../src/goal/Goal'); +import GoalExpression = require('../../src/condition/expression/GoalExpression'); +import User = require('../../src/user/User'); +import ExpressionFactory = require('../../src/condition/factory/ExpressionFactory'); +import OverallGoalCondition = require('../../src/condition/OverallGoalCondition'); + +describe("UserChallenge test", () => { + + var factory:ChallengeFactory = new ChallengeFactory(); + + var aGoalID:string = "5d34ae6e-e9ca-4352-9a67-3fdf205cce26"; + var aGoalName:string = "goal 1"; + var aBadgeID:string = 'badge 1'; + + var now:moment.Moment = moment('2015-08-26T00:00:00'); + var startDate:moment.Moment = moment("2015-08-17T00:00:00"); + var endDate:moment.Moment = moment("2015-09-17T23:59:59"); + + var aRecurringSession:RecurringSession = new RecurringSession('week'); + + var aGoal:Goal = new Goal(aGoalID, aGoalName, aBadgeID, now, endDate, aRecurringSession); + + var aSymbolicName:string = 'Temp_cli'; + var aSensorName:string = 'AC_443'; + + var mapSymbolicNameToSensor:any = {}; + mapSymbolicNameToSensor[aSymbolicName] = aSensorName; + var aUser:User = new User('Gérard', mapSymbolicNameToSensor, [], null, factory); + + var expression:GoalExpression; + var expressionDescription:any = { + valueLeft: { + value: aSymbolicName, + symbolicName: true + }, + valueRight: { + value: "15", + symbolicName: false + }, + comparison: ">" + }; + + var expressionFactory:ExpressionFactory = new ExpressionFactory(); + + var aConditionID = "id1"; + var aConditionDescription = "a desc"; + var aThresholdRate = 80; + + expression = expressionFactory.createExpression(expressionDescription); + var condition = new OverallGoalCondition(aConditionID, aConditionDescription, expression, aThresholdRate, null); + + aGoal.addCondition(condition); + + var userChallenge = factory.createChallenge(aGoal, aUser, now); + + describe('GetSensor method', () => { + + var result = userChallenge.getSensors(); + + it("should have condition's id of goal, recorded", () => { + chai.expect(result[aConditionID]).not.to.be.null; + }); + + it("should have condition's symbolic names recorded", () => { + chai.expect(result[aConditionID].symbolicNames).to.be.eqls([aSymbolicName]); + }) + + it("should have condition's timeBox recorded", () => { + chai.expect(result[aConditionID].timeBox).not.to.be.null; + }); + + it("should have sensors array built", () => { + chai.expect(result[aConditionID].sensors).to.be.eqls([aSensorName]); + }); + + it("should have sensor entry built", () => { + chai.expect(result[aConditionID][aSensorName]).not.to.be.null; + }); + }); + +}); \ No newline at end of file diff --git a/backend/tests/condition/AverageOnValueTest.ts b/backend/tests/condition/AverageOnValueTest.ts index 58bf335..67369f9 100644 --- a/backend/tests/condition/AverageOnValueTest.ts +++ b/backend/tests/condition/AverageOnValueTest.ts @@ -19,54 +19,212 @@ import Operand = require('../../src/condition/expression/Operand'); import TimeBox = require('../../src/TimeBox'); import Clock = require('../../src/Clock'); import TimeBoxFactory = require('../../src/TimeBoxFactory'); - +import ExpressionFactory = require('../../src/condition/factory/ExpressionFactory'); +import Filter = require('../../src/filter/Filter'); +import ReferencePeriod = require('../../src/condition/ReferencePeriod'); describe('Test AverageOnValueTest', () => { + var aSymbolicName = "TMP_Cli"; + var expressionDescription:any = { + valueLeft: { + value: aSymbolicName, + symbolicName: true + }, + valueRight: { + value: "15", + symbolicName: false + }, + comparison: "<" + }; + + var expressionFactory:ExpressionFactory = new ExpressionFactory(); + var expression:GoalExpression = expressionFactory.createExpression(expressionDescription); + + var aConditionID = "id1"; + var aConditionDescription = "a desc"; + var aThresholdRate = 10; + var filterOfCondition:Filter = new Filter('all', ['all']); + var referencePeriod:ReferencePeriod = new ReferencePeriod(1, 'week'); + + var averageOnValue:AverageOnValue = new AverageOnValue(aConditionID, aConditionDescription, expression, aThresholdRate, filterOfCondition, referencePeriod); + + var startDateOfChallenge:moment.Moment = Clock.getMomentFromString("2000-01-07T00:00:00"); + var endDateOfChallenge:moment.Moment = Clock.getMomentFromString("2000-01-14T00:00:00"); + var conditionDescription = averageOnValue.getRequiredByCondition(startDateOfChallenge, endDateOfChallenge); + + describe('GetRequired method', () => { + it('should have proper symbolic names field', () => { + var expected = [aSymbolicName]; + var result = conditionDescription.symbolicNames; + chai.expect(result).to.be.eqls(expected); + }); + + it('should have the proper start date', ()=> { + var expectedStartDate:moment.Moment = startDateOfChallenge.clone().subtract(1, 'week'); + var result = conditionDescription.timeBox.start; + chai.expect(result.toISOString()).to.be.eql(expectedStartDate.toISOString()); + }); + + it('should have the proper date of creation', ()=> { + var expectedDateOfCreation:moment.Moment = startDateOfChallenge.clone(); + var result = conditionDescription.timeBox.dateOfCreation; + chai.expect(result.toISOString()).to.be.eql(expectedDateOfCreation.toISOString()); + }); + + it('should have the proper end date', ()=> { + var expectedEndDate:moment.Moment = endDateOfChallenge.clone(); + var result = conditionDescription.timeBox.end; + chai.expect(result.toISOString()).to.be.eql(expectedEndDate.toISOString()); + }); + }); + + describe('separate data', () => { + + it('should separate data correctly', () => { + + var values:any[] = [ + { + date: "946771200000", + value: 100 + }, + { + date: "946857600000", + value: 101 + }, + { + date: "946944000000", + value: 99 + }, + { + date: "947030400000", + value: 102 + }, + { + date: "947116800000", + value: 98 + }, + + // OLD/NEW DATA + { + date: "947289600000", + value: 89 + }, + { + date: "947376000000", + value: 90 + }, + { + date: "947462400000", + value: 91 + }, + { + date: "947548800000", + value: 70 + } + ]; + + + var expectedOldValues:number[] = [100, 101, 99, 102, 98]; + var expectedNewValues:number[] = [89, 90, 91, 70]; + + var actualOldValues:number[] = []; + var actualNewValues:number[] = []; + + averageOnValue.separateOldAndNewData(values, actualOldValues, actualNewValues, Clock.getMomentFromString("2000-01-07T00:00:00")); + chai.expect(actualOldValues).to.be.eqls(expectedOldValues); + chai.expect(actualNewValues).to.be.eqls(expectedNewValues); + }); + + + it('should separate data even if these are older than start date', () => { + var values:any[] = [ + { + date: "946771200000", + value: 100 + }, + { + date: "946857600000", + value: 101 + }, + { + date: "946944000000", + value: 99 + }, + { + date: "947030400000", + value: 102 + }, + { + date: "947116800000", + value: 98 + }, + + // OLD/NEW DATA + { + date: "947289600000", + value: 89 + }, + { + date: "947376000000", + value: 90 + }, + { + date: "947462400000", + value: 91 + }, + { + date: "947548800000", + value: 70 + }, - var averageOnValue:AverageOnValue; + //too old data, so it won't be in the arrays + { + date: "942796800000", + value: 42 + } + ]; - var expressionStub = sinon.createStubInstance(GoalExpression); + var expectedOldValues:number[] = [100, 101, 99, 102, 98, 42]; + var expectedNewValues:number[] = [89, 90, 91, 70]; + + var actualOldValues:number[] = []; + var actualNewValues:number[] = []; - var leftOperand:Operand = new Operand('TMP_Cli', true); - var rightOperand:Operand = new Operand('15', false); - var typeOfComparison:string = '<'; //baisse - var typeOfComparisonUp:string = '>'; //hausse - var description:string = 'un test'; + averageOnValue.separateOldAndNewData(values, actualOldValues, actualNewValues, conditionDescription.timeBox.dateOfCreation); + chai.expect(actualOldValues).to.be.eqls(expectedOldValues); + chai.expect(actualNewValues).to.be.eqls(expectedNewValues); + }); - var startDate:moment.Moment = moment(new Date(Date.UTC(2000, 1, 1)).getTime()); - var dateOfCreation:moment.Moment = moment(new Date(Date.UTC(2000, 1, 7)).getTime()); - var endDate:moment.Moment = moment(new Date(Date.UTC(2000, 1, 15)).getTime()); - var expression:GoalExpression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + }); + /* describe('evaluate method decrease', () => { it('should return true if threshold is reached', () => { - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate, moment(new Date(0,1,0,0,0,0,0).getTime())); var data:any = {}; var oldValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '' + '', + date: "946771200000", value: '100' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '' + '', + date: "946857600000", value: '101' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '' + '', + date: "946944000000", value: '99' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '' + '', + date: "947030400000", value: '102' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '' + '', + date: "947116800000", value: '98' } ]; @@ -74,136 +232,132 @@ describe('Test AverageOnValueTest', () => { var newValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '' + '', - value:'89' + date: "947289600000", + value: '89' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '' + '', + date: "947376000000", value: '90' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '' + '', + date: "947462400000", value: '91' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 11)).getTime()).valueOf() + '' + '', + date: "947548800000", value: '70' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 12)).getTime()).valueOf() + '' + '', + date: "947635200000", value: '110' } ]; - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); - + data[aSymbolicName] = oldValues.concat(newValues); - chai.expect(averageOnValue.evaluate(data)).to.be.true; + var result:any = averageOnValue.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.true; }); it('should return true if threshold is reached with different number of measures', () => { - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate,moment(new Date(0,1,0,0,0,0,0).getTime())); - var data:any = {}; var oldValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', - value: 100 + date: "946771200000", + value: '100' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', - value: 101 + date: "946857600000", + value: '101' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', - value: 99 + date: "946944000000", + value: '99' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', - value: 102 + date: "947030400000", + value: '102' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', - value: 98 + date: "947116800000", + value: '98' } ]; var newValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', - value: 89 + date: "947289600000", + value: '89' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', - value: 90 + date: "947376000000", + value: '90' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', - value: 91 + date: "947462400000", + value: '91' } ]; - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); + data[aSymbolicName] = oldValues.concat(newValues); - chai.expect(averageOnValue.evaluate(data)).to.be.true; + var result:any = averageOnValue.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.true; }); it('should return false if threshold is close but not reached', () => { - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate,Clock.getMoment(new Date(0,1,0,0,0,0,0).getTime())); var data:any = {}; var oldValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', - value: 100 + date: "946771200000", + value: '100' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', - value: 101 + date: "946857600000", + value: '101' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', - value: 99 + date: "946944000000", + value: '99' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', - value: 102 + date: "947030400000", + value: '102' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', - value: 98 + date: "947116800000", + value: '98' } ]; + var newValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', - value: 89 + date: "947289600000", + value: '89' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', - value: 91 + date: "947376000000", + value: '91' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', - value: 91 + date: "947462400000", + value: '91' } ]; + data[aSymbolicName] = oldValues.concat(newValues); - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); + var result:any = averageOnValue.evaluate(data, conditionDescription); - chai.expect(averageOnValue.evaluate(data)).to.be.false; + chai.expect(result.achieved).to.be.false; }); + }); describe('progression fields', () => { @@ -213,9 +367,6 @@ describe('Test AverageOnValueTest', () => { var oldValues:any[] = []; var newValues:any[] = []; - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate,moment(new Date(0,1,0,0,0,0,0).getTime())); - beforeEach(() => { data = {}; @@ -223,23 +374,23 @@ describe('Test AverageOnValueTest', () => { // average : 100 oldValues = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', + date: "946771200000", value: 100 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', + date: "946857600000", value: 101 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', + date: "946944000000", value: 99 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', + date: "947030400000", value: 102 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', + date: "947116800000", value: 98 } ]; @@ -249,25 +400,26 @@ describe('Test AverageOnValueTest', () => { // average : 100 newValues = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', + date: "947289600000", value: 100 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', + date: "947376000000", value: 101 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', + date: "947462400000", value: 99 } ]; - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); - averageOnValue.evaluate(data); + data[aSymbolicName] = oldValues.concat(newValues); + + var result:any = averageOnValue.evaluate(data, conditionDescription); - assert.equal(averageOnValue.getPercentageAchieved(), 0); + + assert.equal(result.percentageAchieved, 0); }); it('should have 50 percentage achieved', () => { @@ -275,460 +427,264 @@ describe('Test AverageOnValueTest', () => { // average : 95 newValues = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', + date: "947289600000", value: 90 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', + date: "947376000000", value: 100 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', + date: "947462400000", value: 95 } ]; - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); + data[aSymbolicName] = oldValues.concat(newValues); + + var result:any = averageOnValue.evaluate(data, conditionDescription); - averageOnValue.evaluate(data); - assert.equal(averageOnValue.getPercentageAchieved(), 50); + + assert.equal(result.percentageAchieved, 50); }); - it('should have 100 percentage achieved', () => { + it('should have 50 percentage achieved when old values must be ignored', () => { + + oldValues.push({ + date: "1999-12-10T00:00:00", + value: 110 + }); // average : 95 newValues = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', - value: 85 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', - value: 95 + date: "947289600000", + value: 90 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', - value: 91 + date: "947376000000", + value: 100 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 11)).getTime()).valueOf() + '', - value: 89 + date: "947462400000", + value: 95 } ]; - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); + data[aSymbolicName] = oldValues.concat(newValues); - averageOnValue.evaluate(data); - assert.equal(averageOnValue.getPercentageAchieved(), 100); - }); - }); - - describe('duration achieved', () => { + var result:any = averageOnValue.evaluate(data, conditionDescription); - beforeEach(() => { - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate, moment(new Date(0,1,0,0,0,0,0).getTime())); - }); - it('should be at 50 percent of time', () => { - var currentDate:moment.Moment = moment(new Date(Date.UTC(2000, 1, 11)).getTime()); - averageOnValue.updateDurationAchieved(currentDate.valueOf()); - assert.equal(averageOnValue.getPercentageOfTimeElapsed(), 50); - }); - it('should be at 0 percent of time', () => { - var currentDate:moment.Moment = moment(new Date(Date.UTC(2000, 1, 7)).getTime()); - averageOnValue.updateDurationAchieved(currentDate.valueOf()); - assert.equal(averageOnValue.getPercentageOfTimeElapsed(), 0); + assert.equal(result.percentageAchieved, 50); }); - it('should throw an error if time given is before dateOfCreation', () => { - var currentDate:moment.Moment = moment(new Date(Date.UTC(2000, 1, 1)).getTime()); - chai.expect(() => averageOnValue.updateDurationAchieved(currentDate.valueOf())).to.throw(Error); - }); - }); - }); - - describe('separate data', () => { - it('should separate data correctly', () => { - - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate,moment(new Date(0,1,0,0,0,0,0).getTime())); - - var values:any[] = [ - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', - value: 100 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', - value: 101 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', - value: 99 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', - value: 102 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', - value: 98 - }, - - // OLD/NEW DATA - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', - value: 89 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', - value: 90 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', - value: 91 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 11)).getTime()).valueOf() + '', - value: 70 - } - ]; - - - var expectedOldValues:number[] = [100, 101, 99, 102, 98]; - var expectedNewValues:number[] = [89, 90, 91, 70]; - - var actualOldValues:number[] = []; - var actualNewValues:number[] = []; - - averageOnValue.separateOldAndNewData(values, actualOldValues, actualNewValues); - chai.expect(actualOldValues).to.be.eqls(expectedOldValues); - chai.expect(actualNewValues).to.be.eqls(expectedNewValues); - }); - - it('should separate data if these are older than 20 days', () => { - var newStartDate:moment.Moment = moment(new Date(Date.UTC(1999, 12, 18)).getTime()); - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, newStartDate, dateOfCreation, endDate,moment(new Date(0,0,20,0,0,0,0).getTime())); - - var values:any[] = [ - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', - value: 100 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', - value: 101 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', - value: 99 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', - value: 102 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', - value: 98 - }, - - // OLD/NEW DATA - - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', - value: 89 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', - value: 90 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', - value: 91 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 11)).getTime()).valueOf() + '', - value: 70 - }, - - //too old data, so it won't be in the arrays - { - date: Clock.getMoment(new Date(Date.UTC(1999, 12, 17)).getTime()).valueOf() + '', - value: 42 - } - ]; - - - var expectedOldValues:number[] = [100, 101, 99, 102, 98]; - var expectedNewValues:number[] = [89, 90, 91, 70]; - - var actualOldValues:number[] = []; - var actualNewValues:number[] = []; - - averageOnValue.separateOldAndNewData(values, actualOldValues, actualNewValues); - chai.expect(actualOldValues).to.be.eqls(expectedOldValues); - chai.expect(actualNewValues).to.be.eqls(expectedNewValues); - }); - - it('should add data if these are younger than 30 days', () => { - var newStartDate:moment.Moment = Clock.getMoment(new Date(Date.UTC(1999, 12, 8)).getTime()); - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, newStartDate, dateOfCreation, endDate,moment(new Date(0,0,30,0,0,0,0).getTime())); - - var values:any[] = [ - - //datas after 1999-12-8 - { - date: Clock.getMoment(new Date(Date.UTC(1999, 12, 8, 1)).getTime()).valueOf() + '', - value: 95 - }, - { - date: Clock.getMoment(new Date(Date.UTC(1999, 12, 12)).getTime()).valueOf() + '', - value: 105 - }, - { - date: Clock.getMoment(new Date(Date.UTC(1999, 12, 31)).getTime()).valueOf() + '', - value: 100 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', - value: 100 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', - value: 101 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', - value: 99 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', - value: 102 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', - value: 98 - }, - - // OLD/NEW DATA - - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', - value: 89 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', - value: 90 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', - value: 91 - }, - { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 11)).getTime()).valueOf() + '', - value: 70 - } - ]; + it('should have 100 percentage achieved', () => { + // average : 95 + //85.95.91.89 + newValues = [ + { + date: "947289600000", + value: '85' + }, + { + date: "947376000000", + value: '95' + }, + { + date: "947462400000", + value: '91' + }, + { + date: "947548800000", + value: '89' + } + ]; - var expectedOldValues:number[] = [95,105,100,100, 101, 99, 102, 98]; - var expectedNewValues:number[] = [89, 90, 91, 70]; + data[aSymbolicName] = oldValues.concat(newValues); - var actualOldValues:number[] = []; - var actualNewValues:number[] = []; + var result:any = averageOnValue.evaluate(data, conditionDescription); - averageOnValue.separateOldAndNewData(values, actualOldValues, actualNewValues); - chai.expect(actualOldValues).to.be.eqls(expectedOldValues); - chai.expect(actualNewValues).to.be.eqls(expectedNewValues); + assert.equal(result.percentageAchieved, 100); + }); }); - }); - - describe('getRequired method', () => { - - expression = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate,moment(new Date(0,1,0,0,0,0,0).getTime())); - - var expected:any = {}; - var timeBoxDesc:any = {}; - timeBoxDesc['startDate'] = '2000-02-01 00:00:00'; - timeBoxDesc['endDate'] = '2000-02-15 00:00:00'; - expected['TMP_Cli'] = timeBoxDesc; - - console.log(JSON.stringify(expected)); - console.log(JSON.stringify(averageOnValue.getRequired())); - - //chai.expect(averageOnValue.getRequired()).to.be.eql(expected); }); describe('evaluate method increase', () => { + var expressionDescription:any = { + valueLeft: { + value: aSymbolicName, + symbolicName: true + }, + valueRight: { + value: "15", + symbolicName: false + }, + comparison: ">" + }; + + var expressionFactory:ExpressionFactory = new ExpressionFactory(); + var expression:GoalExpression = expressionFactory.createExpression(expressionDescription); + var averageOnValue:AverageOnValue = new AverageOnValue(aConditionID, aConditionDescription, expression, aThresholdRate, filterOfCondition, referencePeriod); + it('should return true if threshold is reached', () => { - expression = new GoalExpression(leftOperand, typeOfComparisonUp, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate,moment(new Date(0,1,0,0,0,0,0).getTime())); var data:any = {}; var oldValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', - value: 100 + date: "946771200000", + value: '100' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', - value: 101 + date: "946857600000", + value: '101' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', - value: 99 + date: "946944000000", + value: '99' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', - value: 102 + date: "947030400000", + value: '102' }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', - value: 98 + date: "947116800000", + value: '98' } ]; var newValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', + date: "947289600000", value: 121 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', + date: "947376000000", value: 110 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', + date: "947462400000", value: 119 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 11)).getTime()).valueOf() + '', + date: "947548800000", value: 70 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 12)).getTime()).valueOf() + '', + date: "947635200000", value: 130 } ]; - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); + data[aSymbolicName] = oldValues.concat(newValues); - - chai.expect(averageOnValue.evaluate(data)).to.be.true; + var result:any = averageOnValue.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.true; }); it('should return true if threshold is reached with different number of measures', () => { - expression = new GoalExpression(leftOperand, typeOfComparisonUp, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate,moment(new Date(0,1,0,0,0,0,0).getTime())); - var data:any = {}; var oldValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', + date: "946771200000", value: 100 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', + date: "946857600000", value: 101 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', + date: "946944000000", value: 99 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', + date: "947030400000", value: 102 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', + date: "947116800000", value: 98 } ]; var newValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', + date: "947289600000", value: 111 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', + date: "947376000000", value: 110 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '', + date: "947462400000", value: 109 } ]; + data[aSymbolicName] = oldValues.concat(newValues); - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); - - chai.expect(averageOnValue.evaluate(data)).to.be.true; + var result:any = averageOnValue.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.true; }); it('should return false if threshold is close but not reached', () => { - expression = new GoalExpression(leftOperand, typeOfComparisonUp, rightOperand, description); - averageOnValue = new AverageOnValue(null,expression,10, startDate, dateOfCreation, endDate, moment(new Date(0,1,0,0,0,0,0).getTime())); - var data:any = {}; var oldValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 2)).getTime()).valueOf() + '', + date: "946771200000", value: 100 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 3)).getTime()).valueOf() + '', + date: "946857600000", value: 101 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 4)).getTime()).valueOf() + '', + date: "946944000000", value: 99 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 5)).getTime()).valueOf() + '', + date: "947030400000", value: 102 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 6)).getTime()).valueOf() + '', + date: "947116800000", value: 98 } ]; var newValues:any[] = [ { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 8)).getTime()).valueOf() + '', + date: "947289600000", value: 111 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 9)).getTime()).valueOf() + '', + date: "947376000000", value: 109 }, { - date: Clock.getMoment(new Date(Date.UTC(2000, 1, 10)).getTime()).valueOf() + '' + '', + date: "947462400000" + '', value: 109 } ]; - data['TMP_Cli'] = {}; - data['TMP_Cli'].values = oldValues.concat(newValues); + data[aSymbolicName] = oldValues.concat(newValues); - chai.expect(averageOnValue.evaluate(data)).to.be.false; + var result:any = averageOnValue.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.false; }); + }); + + */ + }); \ No newline at end of file diff --git a/backend/tests/condition/ConditionTest.ts b/backend/tests/condition/ConditionTest.ts new file mode 100644 index 0000000..46fbf06 --- /dev/null +++ b/backend/tests/condition/ConditionTest.ts @@ -0,0 +1,166 @@ +/// +/// +/// + +import chai = require('chai'); +import sinon = require('sinon'); +var assert = chai.assert; + + +import Condition = require('../../src/condition/Condition'); +import ExpressionFactory = require('../../src/condition/factory/ExpressionFactory'); +import Filter = require('../../src/filter/Filter'); +import GoalExpression = require('../../src/condition/expression/GoalExpression'); +import Clock = require('../../src/Clock'); + + +describe('Test Condition', () => { + var condition:Condition; + var conditionDescription:any = { + symbolicNames: ["TMP_CLI"], + timeBox: { + start: Clock.getMomentFromString("2000-01-01T00:00:00"), + end: Clock.getMomentFromString("2000-08-01T00:00:00") + } + }; + + var aSymbolicName = "TMP_CLI"; + var aValue = "15"; + var aComparison = ">"; + var expression:GoalExpression; + var expressionDescription:any = { + valueLeft: { + value: aSymbolicName, + symbolicName: true + }, + valueRight: { + value: aValue, + symbolicName: false + }, + comparison: aComparison + }; + + var expressionFactory:ExpressionFactory = new ExpressionFactory(); + expression = expressionFactory.createExpression(expressionDescription); + + var aConditionID = "id1"; + var aConditionDescription = "a desc"; + var aThresholdRate = 80; + var filterOfCondition:Filter = new Filter('all', ['all']); + + condition = new Condition(aConditionID, aConditionDescription, expression, aThresholdRate, filterOfCondition); + + describe('Build test', () => { + + var dataInJSON:any = condition.getDataInJSON(); + + it('should have proper id', () => { + chai.expect(dataInJSON.id).to.be.eq(aConditionID); + }); + + it('should have proper description', () => { + chai.expect(dataInJSON.description).to.be.eq(aConditionDescription); + }); + + it('should have proper threshold', () => { + chai.expect(dataInJSON.threshold).to.be.eq(aThresholdRate); + }); + + it('should have proper threshold', () => { + var expectedFilters:any = { + dayOfWeekFilter: 'all', + periodOfDayFilter: ['all'] + } + + chai.expect(dataInJSON.filter).to.be.eqls(expectedFilters); + }); + + it('should have proper expression', () => { + var expected:any = { + valueLeft: { + value: aSymbolicName, + symbolicName: true + }, + valueRight: { + value: aValue, + symbolicName: false + }, + comparison: aComparison + }; + + chai.expect(dataInJSON.expression).to.be.eqls(expected); + }); + }); + + describe('KeepUsefulValues method', () => { + it('should keep nothing if nothing is in the timeBox', () => { + var expected:any = {}; + expected[aSymbolicName] = []; + + var data:any = {}; + data[aSymbolicName] = [ + {date: "967766400000", value: 10}, + {date: "967766400000", value: 10}, + {date: "967766400000", value: 10}, + {date: "967766400000", value: 10} + ]; + + + var result:any[] = condition.keepUsefulValues(data, conditionDescription); + + chai.expect(result).to.be.eqls(expected); + }); + + it('should keep everything if everything is in the timeBox', () => { + var data:any = {}; + data[aSymbolicName] = [ + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10} + ]; + var result:any[] = condition.keepUsefulValues(data, conditionDescription); + + chai.expect(result).to.be.eqls(data); + }); + + it('should keep what is in the timeBox', () => { + var expected:any = {}; + expected[aSymbolicName] = [ + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10} + ]; + + var data:any = {}; + data[aSymbolicName] = [ + {date: "915148800000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "967766400000", value: 10}, + ]; + var result:any[] = condition.keepUsefulValues(data, conditionDescription); + + chai.expect(result).to.be.eqls(expected); + }); + }); + + describe('GetRequired chain', () => { + it('should have proper required result', () => { + var aStart:moment.Moment = Clock.getMomentFromString("2000-01-01T00:00:00"); + var anEnd:moment.Moment = Clock.getMomentFromString("2000-07-01T00:00:00"); + var expected:any = { + symbolicNames: [aSymbolicName], + timeBox: { + start: aStart, + end: anEnd + } + }; + var result = condition.getRequiredByCondition(aStart, anEnd); + chai.expect(result).to.be.eqls(expected); + }); + }); +}); diff --git a/backend/tests/condition/OverallGoalConditionTest.ts b/backend/tests/condition/OverallGoalConditionTest.ts index 97b4443..ee09f54 100644 --- a/backend/tests/condition/OverallGoalConditionTest.ts +++ b/backend/tests/condition/OverallGoalConditionTest.ts @@ -19,119 +19,124 @@ import TimeBox = require('../../src/TimeBox'); import Clock = require('../../src/Clock'); import TimeBoxFactory = require('../../src/TimeBoxFactory'); -describe('Test OverallGoalCondition', () => { - - var overallGoalCondition:OverallGoalCondition; - - var condition:GoalExpression; +import ExpressionFactory = require('../../src/condition/factory/ExpressionFactory'); +import Filter = require('../../src/filter/Filter'); - var leftOperand:Operand = new Operand('TMP_Cli', true); - var rightOperand:Operand = new Operand('15', false); - var typeOfComparison:string = '>'; - var description:string = 'un test'; +describe('Test OverallGoalCondition', () => { - var startDate:moment.Moment = moment(new Date(Date.UTC(2000,1,1)).getTime()); - var endDate:moment.Moment = moment(new Date(Date.UTC(2000,8,1)).getTime()); + var aSymbolicName = "TMP_CLI"; + var expression:GoalExpression; + var expressionDescription:any = { + valueLeft: { + value: aSymbolicName, + symbolicName: true + }, + valueRight: { + value: "15", + symbolicName: false + }, + comparison: ">" + }; + + var expressionFactory:ExpressionFactory = new ExpressionFactory(); + + var aConditionID = "id1"; + var aConditionDescription = "a desc"; + var aThresholdRate = 80; + var filterOfCondition:Filter = new Filter('all', ['all']); + + expression = expressionFactory.createExpression(expressionDescription); + var condition = new OverallGoalCondition(aConditionID, aConditionDescription, expression, aThresholdRate, filterOfCondition); + var conditionDescription:any = { + symbolicNames: ["TMP_CLI"], + timeBox: { + start: Clock.getMomentFromString("2000-01-01T00:00:00"), + end: Clock.getMomentFromString("2000-08-01T00:00:00") + } + }; it('should return false if min threshold is absolutely not reached', () => { - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - overallGoalCondition = new OverallGoalCondition(null, condition, 80, startDate, moment(new Date(Clock.getNow()).getTime()), endDate); - var data:any = {}; var values:any[] = [ - {date:null,value:10}, - {date:null,value:10}, - {date:null,value:10}, - {date:null,value:10}, - {date:null,value:10}, - {date:null,value:16}, - {date:null,value:18} + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 16}, + {date: "949363200000", value: 18} ]; - var valuesDesc:any = {}; - valuesDesc['values'] = values; - - data['TMP_Cli'] = valuesDesc; + data[aSymbolicName] = values; - chai.expect(overallGoalCondition.evaluate(data)).to.be.false; + var result = condition.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.false; }); it('should return false if min threshold is not reached', () => { - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - overallGoalCondition = new OverallGoalCondition(null, condition, 80, startDate, moment(new Date(Clock.getNow()).getTime()), endDate); - var data:any = {}; var values:any[] = [ - {date:null,value:18}, - {date:null,value:18}, - {date:null,value:18}, - {date:null,value:18}, - {date:null,value:18}, - {date:null,value:18}, - {date:null,value:18}, - {date:null,value:10}, - {date:null,value:10}, - {date:null,value:10} + {date: "949363200000", value: 18}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10} ]; - var valuesDesc:any = {}; - valuesDesc['values'] = values; + data[aSymbolicName] = values; - data['TMP_Cli'] = valuesDesc; - chai.expect(overallGoalCondition.evaluate(data)).to.be.false; + var result = condition.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.false; }); it('should return true if min threshold is just reached', () => { - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - overallGoalCondition = new OverallGoalCondition(null, condition, 50, startDate, moment(new Date(Clock.getNow()).getTime()), endDate); - var data:any = {}; var values:any[] = [ - {date:null,value:17}, - {date:null,value:16}, - {date:null,value:16}, - {date:null,value:17}, - {date:null,value:18}, - {date:null,value:19}, - {date:null,value:18}, - {date:null,value:17}, - {date:null,value:10}, - {date:null,value:10} + {date: "949363200000", value: 17}, + {date: "949363200000", value: 16}, + {date: "949363200000", value: 16}, + {date: "949363200000", value: 17}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 19}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 17}, + {date: "949363200000", value: 10}, + {date: "949363200000", value: 10} ]; - var valuesDesc:any = {}; - valuesDesc['values'] = values; + data[aSymbolicName] = values; - data['TMP_Cli'] = valuesDesc; - // FIXME DATE NULL chai.expect(overallGoalCondition.evaluate(data)).to.be.true; + var result = condition.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.true; }); - it('should return true if min threshold is reached', () => { - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); - overallGoalCondition = new OverallGoalCondition(null, condition, 50, startDate, moment(new Date(Clock.getNow()).getTime()), endDate); + it('should return true if min threshold is reached', () => { var data:any = {}; var values:any[] = [ - {date:null,value:16}, - {date:null,value:17}, - {date:null,value:18}, - {date:null,value:19}, - {date:null,value:18}, - {date:null,value:18}, - {date:null,value:17}, - {date:null,value:18}, - {date:null,value:16}, - {date:null,value:17} + {date: "949363200000", value: 16}, + {date: "949363200000", value: 17}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 19}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 17}, + {date: "949363200000", value: 18}, + {date: "949363200000", value: 16}, + {date: "949363200000", value: 17} ]; - var valuesDesc:any = {}; - valuesDesc['values'] = values; - - data['TMP_Cli'] = valuesDesc; + data[aSymbolicName] = values; - // FIXME DATE NULL chai.expect(overallGoalCondition.evaluate(data)).to.be.true; + var result = condition.evaluate(data, conditionDescription); + chai.expect(result.achieved).to.be.true; }); }); \ No newline at end of file diff --git a/backend/tests/condition/expression/GoalExpressionTest.ts b/backend/tests/condition/expression/GoalExpressionTest.ts index 1b94487..6e350d5 100644 --- a/backend/tests/condition/expression/GoalExpressionTest.ts +++ b/backend/tests/condition/expression/GoalExpressionTest.ts @@ -25,42 +25,42 @@ describe('Test GoalExpression', () => { var description:string = 'un test'; it('should not have required', () => { - goalExpression = new GoalExpression(leftOperandNotRequired, typeOfComparison, rightOperandNotRequired, description); + goalExpression = new GoalExpression(leftOperandNotRequired, typeOfComparison, rightOperandNotRequired); var expected:string[] = []; chai.expect(goalExpression.getRequired()).to.be.eqls(expected); }); it('should have proper required on left operand', () => { - goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandNotRequired, description); + goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandNotRequired); var expected:string[] = ['TMP_Cli']; chai.expect(goalExpression.getRequired()).to.be.eqls(expected); }); it('should have proper required on right operand', () => { - goalExpression = new GoalExpression(leftOperandNotRequired, typeOfComparison, rightOperandRequired, description); + goalExpression = new GoalExpression(leftOperandNotRequired, typeOfComparison, rightOperandRequired); var expected:string[] = ['10']; chai.expect(goalExpression.getRequired()).to.be.eqls(expected); }); it('should have proper required on both operand', () => { - goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandRequired, description); + goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandRequired); var expected:string[] = ['TMP_Cli', '10']; chai.expect(goalExpression.getRequired()).to.be.eqls(expected); }); it('should have proper left operand', () => { - goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandRequired, description); + goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandRequired); chai.expect(goalExpression.hasLeftOperand('TMP_Cli')).to.be.true; }); it('should have proper right operand', () => { - goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandRequired, description); + goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandRequired); chai.expect(goalExpression.hasRightOperand('10')).to.be.true; }); it('should have proper type of comparison', () => { - goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandRequired, description); + goalExpression = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandRequired); chai.expect(goalExpression.getComparisonType()).to.be.eq(typeOfComparison); }); @@ -79,7 +79,7 @@ describe('Test GoalExpression', () => { typeOfComparison = '<'; description = 'un test'; - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); }); it('should evaluate basic boolean comparison should not throw error', () => { @@ -201,7 +201,7 @@ describe('Test GoalExpression', () => { typeOfComparison = '>'; description = 'un test'; - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); }); describe('Evaluate with >', () => { @@ -289,7 +289,7 @@ describe('Test GoalExpression', () => { typeOfComparison = '<'; description = 'un test'; - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); }); describe('Evaluate with <', () => { @@ -307,7 +307,7 @@ describe('Test GoalExpression', () => { describe('Evaluate with >', () => { beforeEach(() => { typeOfComparison = '>'; - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); }); it('should evaluate at true with 10', () => { @@ -324,7 +324,7 @@ describe('Test GoalExpression', () => { describe('Evaluate with ==', () => { beforeEach(() => { typeOfComparison = '=='; - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); }); it('should evaluate at true with 15', () => { @@ -341,7 +341,7 @@ describe('Test GoalExpression', () => { describe('Evaluate with !=', () => { beforeEach(() => { typeOfComparison = '!='; - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); }); it('should evaluate at true with 10', () => { @@ -358,7 +358,7 @@ describe('Test GoalExpression', () => { describe('Evaluate with a UNKNOWN field tagged as non required', () => { it('should throw an error when evaluate with 15 { rightOperand = new Operand('TMP_CLIM', false); - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); chai.expect(() => condition.evaluate([])).to.throw(Error); }); }); @@ -377,7 +377,7 @@ describe('Test GoalExpression', () => { typeOfComparison = '>'; description = 'un test'; - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); }); it('should evaluate at true when 20,10 are passed', () => { @@ -412,7 +412,7 @@ describe('Test GoalExpression', () => { typeOfComparison = '>'; description = 'un test'; - condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand, description); + condition = new GoalExpression(leftOperand, typeOfComparison, rightOperand); }); it('should throw an error if left operand is required and not provided', () => { @@ -422,7 +422,7 @@ describe('Test GoalExpression', () => { }); it('should throw an error if right operand is required and not provided', () => { - condition = new GoalExpression(rightOperand, typeOfComparison, leftOperand, description); + condition = new GoalExpression(rightOperand, typeOfComparison, leftOperand); chai.expect(() => { condition.evaluate({TMP_CLIIIIIIIII: 20}) }).to.throw(BadArgumentException); @@ -444,7 +444,7 @@ describe('Test GoalExpression', () => { var typeOfComparison:string = '<'; var description:string = 'un test'; - condition = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandNotRequired, description); + condition = new GoalExpression(leftOperandRequired, typeOfComparison, rightOperandNotRequired); it('should return correct protocol', () => { var expected:string[] = ['TMP_CLI']; diff --git a/backend/tests/condition/factory/ConditionFactoryTest.ts b/backend/tests/condition/factory/ConditionFactoryTest.ts index d8c9ded..acc6ce1 100644 --- a/backend/tests/condition/factory/ConditionFactoryTest.ts +++ b/backend/tests/condition/factory/ConditionFactoryTest.ts @@ -20,8 +20,8 @@ describe("ConditionFactory test", () => { jsonExpression.type = 'number'; jsonExpression.description = 'description blabla ..'; - jsonExpression.valueLeft = {'value' : 'TEMP_CLI', 'sensor':true}; - jsonExpression.valueRight = {'value' : '15', 'sensor':false}; + jsonExpression.valueLeft = {'value' : 'TEMP_CLI', 'symbolicName':true}; + jsonExpression.valueRight = {'value' : '15', 'symbolicName':false}; expression = factory.createExpression(jsonExpression); diff --git a/backend/tests/database/GoalConditionStoreTest.ts b/backend/tests/database/GoalConditionStoreTest.ts index 1f28a8d..d8eb00c 100644 --- a/backend/tests/database/GoalConditionStoreTest.ts +++ b/backend/tests/database/GoalConditionStoreTest.ts @@ -25,19 +25,18 @@ describe('Test store GoalCondition class', () => { var description = 'a desc'; var goalExpression:GoalExpression = new GoalExpression(leftOperand, typeOfComparison, - rightOperand, description); + rightOperand); var expected:any = { valueLeft: { value: leftOperand.getStringDescription(), - sensor: leftOperand.hasToBeDefined() + symbolicName: leftOperand.hasToBeDefined() }, valueRight: { value: rightOperand.getStringDescription(), - sensor: rightOperand.hasToBeDefined() + symbolicName: rightOperand.hasToBeDefined() }, - comparison: typeOfComparison, - description: description + comparison: typeOfComparison }; it('should return the proper json object', () => { @@ -69,8 +68,5 @@ describe('Test store GoalCondition class', () => { chai.expect(goalConditionClone.getComparisonType()).to.be.eq(typeOfComparison); }); - it('should have the same description', () => { - chai.expect(goalConditionClone.getDescription()).to.be.eq(description); - }); }); }); \ No newline at end of file diff --git a/backend/tests/database/UserStoreTest.ts b/backend/tests/database/UserStoreTest.ts index f47f746..9f75c12 100644 --- a/backend/tests/database/UserStoreTest.ts +++ b/backend/tests/database/UserStoreTest.ts @@ -12,16 +12,21 @@ describe('Test store user class', () => { var aName:string = 'aName'; var anID:string = 'anID'; + var mapSymbolicNameToSensor:any = { + 'TMP_CLI':'AC_444' + }; + var currentChallenges:string[] = ['c1', 'c2']; var finishedBadgesMap:any = { 'b1':2, 'b2':4 }; - var user:User = new User(aName, anID, currentChallenges, finishedBadgesMap); + var user:User = new User(aName,mapSymbolicNameToSensor, currentChallenges, finishedBadgesMap, null, anID); var expected:any = { id:anID, name:aName, + mapSymbolicNameToSensor: mapSymbolicNameToSensor, currentChallenges:currentChallenges, finishedBadgesMap:finishedBadgesMap }; @@ -31,7 +36,7 @@ describe('Test store user class', () => { }); describe('build with its own description', () => { - var userClone:User = new User(expected.name, expected.id, expected.currentChallenges, expected.finishedBadgesMap); + var userClone:User = new User(expected.name,expected.mapSymbolicNameToSensor,expected.currentChallenges, expected.finishedBadgesMap,null, expected.id); it('should have the same name', () => { chai.expect(userClone.getName()).to.be.eq(aName); @@ -42,11 +47,11 @@ describe('Test store user class', () => { }); it('should have the same current challenges', () => { - chai.expect(userClone.getChallenges()).to.be.eq(currentChallenges); + chai.expect(userClone.getCurrentChallenges()).to.be.eq(currentChallenges); }); it('should have the same finished badges map', () => { - chai.expect(userClone.getFinishedBadges()).to.be.eq(finishedBadgesMap); + chai.expect(userClone.getBadges()).to.be.eq(finishedBadgesMap); }); }); }); \ No newline at end of file diff --git a/backend/tests/db_test.json b/backend/tests/db_test.json new file mode 100644 index 0000000..0e640cf --- /dev/null +++ b/backend/tests/db_test.json @@ -0,0 +1,139 @@ +{ + "definitions": [ + { + "id": "3221c575-85ca-447b-86f3-3a4ef39985dc", + "name": "Clim", + "validityPeriod": { + "start": "2015-08-03T22:00:00.000Z", + "end": "2015-09-03T22:00:00.000Z" + }, + "recurringPeriod": "week", + "conditions": [ + { + "id": "ab72f9b4-a368-4ea2-8adb-738ea0e6f30b", + "expression": { + "valueLeft": { + "value": "TMP_CLI", + "symbolicName": true + }, + "valueRight": { + "value": "15", + "symbolicName": false + }, + "comparison": ">", + "description": "a desc", + "periodOfTime": "-2208474000000" + }, + "threshold": 25, + "referencePeriod": { + "numberOfUnitToSubtract": 1, + "unitToSubtract": "week" + }, + "filter": { + "dayOfWeekFilter": "all", + "periodOfDayFilter": [ + "morning", + "afternoon" + ] + }, + "type": "comparison" + } + ], + "badgeID": "44bb8108-8830-4f43-abd1-3ef643303d92" + }, + { + "id": "9bddaf87-5065-4df7-920a-d1d249c9171d", + "name": "Obj1", + "validityPeriod": { + "start": "2015-08-04T12:25:57.787Z", + "end": "2015-08-31T12:25:57.787Z" + }, + "recurringPeriod": "week", + "conditions": [ + { + "id": "7713cb13-e86d-40d0-a39f-c4ad5a33546d", + "expression": { + "valueLeft": { + "value": "TMP_CLI", + "symbolicName": true + }, + "valueRight": { + "value": 1, + "symbolicName": false + }, + "comparison": ">", + "description": "tmp_cli > 1" + }, + "threshold": 100, + "filter": { + "dayOfWeekFilter": "working-week", + "periodOfDayFilter": [ + "all" + ] + }, + "type": "overall" + } + ], + "badgeID": "fde68334-f515-4563-954b-ac91b4a42f88" + } + ], + "badges": [ + { + "id": "44bb8108-8830-4f43-abd1-3ef643303d92", + "name": "Un challenge de d\u00e9mo !", + "points": 100 + } + ], + "users": [ + { + "id": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "name": "Charlie", + "currentChallenges": [ + + ], + "finishedBadgesMap": { + + }, + "mapSymbolicNameToSensor": { + "TMP_CLI": "TEMP_443V" + } + }, + { + "id": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "name": "Gégé", + "currentChallenges": [ + + ], + "finishedBadgesMap": { + + }, + "mapSymbolicNameToSensor": { + "TMP_CLI": "TEMP_352" + } + } + ], + "teams": [ + { + "id": "28aa8108-8830-4f43-abd1-3ab643303d92", + "name": "croquette", + "members": [ + "2cf91e02-a320-4766-aa9f-6efce3142d44" + ], + "leader": "2cf91e02-a320-4766-aa9f-6efce3142d44", + "currentChallenges": [ + + ], + "finishedBadgesMap": { + + } + } + ], + "challenges": { + "userChallenges": [ + + ], + "teamChallenges": [ + + ] + } +} \ No newline at end of file diff --git a/backend/tests/filter/FilterTest.ts b/backend/tests/filter/FilterTest.ts index bfe2044..0832c92 100644 --- a/backend/tests/filter/FilterTest.ts +++ b/backend/tests/filter/FilterTest.ts @@ -16,32 +16,30 @@ describe('FilterTest test', () => { var jsonValuesInMorningAndAfternoon:any = { - 'TMP_CLI': { - 'values': [ - {"date": "1436522344000", "value": "28"}, // 10/7/2015 11:59:04 GMT+2:00 DST - {"date": "1436522374000", "value": "29"}, // 10/7/2015 11:59:34 GMT+2:00 DST - {"date": "1436522404000", "value": "17"}, // 10/7/2015 12:00:04 GMT+2:00 DST - {"date": "1436522434000", "value": "30"}, // 10/7/2015 12:00:34 GMT+2:00 DST - {"date": "1436522464000", "value": "25"}, // 10/7/2015 12:01:04 GMT+2:00 DST - {"date": "1436522494000", "value": "21"}, // 10/7/2015 12:01:34 GMT+2:00 DST - {"date": "1438608351000", "value": "3"} // 3/8/2015 15:25:51 - ] - } + 'TMP_CLI': [ + {"date": "1436522344000", "value": "28"}, // 10/7/2015 11:59:04 GMT+2:00 DST + {"date": "1436522374000", "value": "29"}, // 10/7/2015 11:59:34 GMT+2:00 DST + {"date": "1436522404000", "value": "17"}, // 10/7/2015 12:00:04 GMT+2:00 DST + {"date": "1436522434000", "value": "30"}, // 10/7/2015 12:00:34 GMT+2:00 DST + {"date": "1436522464000", "value": "25"}, // 10/7/2015 12:01:04 GMT+2:00 DST + {"date": "1436522494000", "value": "21"}, // 10/7/2015 12:01:34 GMT+2:00 DST + {"date": "1438608351000", "value": "3"} // 3/8/2015 15:25:51 + ] + }; var jsonValuesInAfternoon:any = { - 'TMP_CLI': { - 'values': [ - {"date": "1436446840000", "value": "28"}, // 9/7/2015 15:00:40 GMT+2:00 DST - {"date": "1436446870000", "value": "26"}, // 9/7/2015 15:01:10 GMT+2:00 DST - {"date": "1436446900000", "value": "28"}, // 9/7/2015 15:01:40 GMT+2:00 DST - {"date": "1436446930000", "value": "28"}, // 9/7/2015 15:02:10 GMT+2:00 DST - {"date": "1436446960000", "value": "27"}, // 9/7/2015 15:02:40 GMT+2:00 DST - {"date": "1436446990000", "value": "28"}, // 9/7/2015 15:03:10 GMT+2:00 DST - {"date": "1436447020000", "value": "28"} // 9/7/2015 15:03:40 GMT+2:00 DST - ] - } + 'TMP_CLI': [ + {"date": "1436446840000", "value": "28"}, // 9/7/2015 15:00:40 GMT+2:00 DST + {"date": "1436446870000", "value": "26"}, // 9/7/2015 15:01:10 GMT+2:00 DST + {"date": "1436446900000", "value": "28"}, // 9/7/2015 15:01:40 GMT+2:00 DST + {"date": "1436446930000", "value": "28"}, // 9/7/2015 15:02:10 GMT+2:00 DST + {"date": "1436446960000", "value": "27"}, // 9/7/2015 15:02:40 GMT+2:00 DST + {"date": "1436446990000", "value": "28"}, // 9/7/2015 15:03:10 GMT+2:00 DST + {"date": "1436447020000", "value": "28"} // 9/7/2015 15:03:40 GMT+2:00 DST + ] + }; describe('apply method', () => { @@ -62,15 +60,14 @@ describe('FilterTest test', () => { var expected:any = { - 'TMP_CLI': { - 'values': [ - {"date": "1436522404000", "value": "17"}, // 10/7/2015 12:00:04 GMT+2:00 DST - {"date": "1436522434000", "value": "30"}, // 10/7/2015 12:00:34 GMT+2:00 DST - {"date": "1436522464000", "value": "25"}, // 10/7/2015 12:01:04 GMT+2:00 DST - {"date": "1436522494000", "value": "21"}, // 10/7/2015 12:01:34 GMT+2:00 DST - {"date": "1438608351000", "value": "3"} // 3/8/2015 15:25:51 - ] - } + 'TMP_CLI': [ + {"date": "1436522404000", "value": "17"}, // 10/7/2015 12:00:04 GMT+2:00 DST + {"date": "1436522434000", "value": "30"}, // 10/7/2015 12:00:34 GMT+2:00 DST + {"date": "1436522464000", "value": "25"}, // 10/7/2015 12:01:04 GMT+2:00 DST + {"date": "1436522494000", "value": "21"}, // 10/7/2015 12:01:34 GMT+2:00 DST + {"date": "1438608351000", "value": "3"} // 3/8/2015 15:25:51 + ] + }; console.log("RESULT", JSON.stringify(result), "VS", JSON.stringify(expected)); @@ -94,9 +91,8 @@ describe('FilterTest test', () => { var result:any[] = filter.apply(jsonValuesInMorningAndAfternoon); var expected:any = { - 'TMP_CLI': { - 'values': [] - } + 'TMP_CLI': [] + }; chai.expect(result).to.be.eqls(expected); @@ -107,9 +103,8 @@ describe('FilterTest test', () => { var result:any[] = filter.apply(jsonValuesInMorningAndAfternoon); var expected:any = { - 'TMP_CLI': { - 'values': [] - } + 'TMP_CLI': [] + }; chai.expect(result).to.be.eqls(expected); diff --git a/backend/tests/goal/GoalFactoryTest.ts b/backend/tests/goal/GoalFactoryTest.ts index ada9d37..af16054 100644 --- a/backend/tests/goal/GoalFactoryTest.ts +++ b/backend/tests/goal/GoalFactoryTest.ts @@ -15,14 +15,21 @@ describe("GoalFactory test", () => { var factory:GoalFactory = new GoalFactory(); var goal:Goal; + var start = Clock.getMomentFromString("2000-01-01T00:00:00"); + var end = Clock.getMomentFromString("2000-08-01T00:00:00"); + + console.log("START ?", start.toISOString()); + beforeEach(() => { var jsonDefinition:any = {}; jsonDefinition.name = "Clim"; + jsonDefinition.badgeID = "badgeID"; + jsonDefinition.recurringPeriod = "week"; - var timeBox:any = {}; - timeBox.startDate = Clock.getCurrentMoment(); - timeBox.endDate = Clock.getCurrentMoment().add(10,'minutes'); - jsonDefinition.timeBox = timeBox; + var validityPeriod:any = {}; + validityPeriod.start = start.toISOString(); + validityPeriod.end = end.toISOString(); + jsonDefinition.validityPeriod = validityPeriod; jsonDefinition.duration = 'day'; @@ -30,8 +37,8 @@ describe("GoalFactory test", () => { jsonExpression.comparison = '<'; jsonExpression.type = 'number'; jsonExpression.description = 'description blabla ..'; - jsonExpression.valueLeft = {'value': 'TEMP_CLI', 'sensor': true}; - jsonExpression.valueRight = {'value': '15', 'sensor': false}; + jsonExpression.valueLeft = {'value': 'TEMP_CLI', 'symbolicName': true}; + jsonExpression.valueRight = {'value': '15', 'symbolicName': false}; var aJsonCondition:any = {}; aJsonCondition.type = 'overall'; @@ -53,6 +60,6 @@ describe("GoalFactory test", () => { }); it("should build a goal with non null conditions", () => { - chai.expect(goal.getRequired()).to.be.not.null; + chai.expect(goal.getRequired(start, end)).to.be.not.null; }); }); \ No newline at end of file diff --git a/backend/tests/goal/GoalTest.ts b/backend/tests/goal/GoalTest.ts index 4c0a682..33425a1 100644 --- a/backend/tests/goal/GoalTest.ts +++ b/backend/tests/goal/GoalTest.ts @@ -5,70 +5,108 @@ /// /// -var moment = require('moment'); -var moment_timezone = require('moment-timezone'); import chai = require('chai'); import sinon = require('sinon'); var assert = chai.assert; +var moment = require('moment'); +var moment_timezone = require('moment-timezone'); + import Goal = require('../../src/goal/Goal'); -import ConditionList = require('../../src/condition/ConditionList'); import GoalExpression = require('../../src/condition/expression/GoalExpression'); import OverallGoalCondition = require('../../src/condition/OverallGoalCondition'); import Operand = require('../../src/condition/expression/Operand'); import Clock = require('../../src/Clock'); +import RecurringSession = require('../../src/goal/RecurringSession'); +import ExpressionFactory = require('../../src/condition/factory/ExpressionFactory'); -describe("Build a goal", function () { - var goal:Goal; +describe('Goal Test', () => { + var aGoalID:string = "5d34ae6e-e9ca-4352-9a67-3fdf205cce26"; + var aGoalName:string = "goal 1"; + var aBadgeID:string = 'badge 1'; - it("should have given name", () => { - goal = new Goal("aName", null, null, 0, null); - assert.equal(goal.getName(), "aName"); - }); -}); + var now:moment.Moment = moment('2015-08-26T00:00:00.000'); + var startDate:moment.Moment = moment("2015-08-17T00:00:00.000"); + var endDate:moment.Moment = moment("2015-09-17T23:59:59.000"); -describe("Add a condition to a goal", () => { - var goal:Goal; + var aRecurringSession:RecurringSession = new RecurringSession('week'); - beforeEach(() => { - goal = new Goal("aGoal", null, null, 0, null); - }); + var goal:Goal = new Goal(aGoalID, aGoalName, aBadgeID, now, endDate, aRecurringSession); - it('should have its own uuid', () => { - chai.expect(goal.hasUUID(goal.getUUID())).to.be.true; - }); + describe("Build a goal", function () { - it('should be possible to add an expression', () => { - var comparison:GoalExpression = new GoalExpression(new Operand("Température", true), 'inf', new Operand('10', false), 'desc'); - var expression:OverallGoalCondition = new OverallGoalCondition(null, comparison, 0, moment(new Date(Date.UTC(2000, 10, 10)).getTime()), moment(new Date(Clock.getNow()).getTime()), moment(new Date(Date.UTC(2000, 10, 15)).getTime())); - chai.expect(goal.addCondition(expression)).not.to.throw; - }); + it("should have given name", () => { + assert.equal(goal.getName(), aGoalName); + }); + + it("should have given id", () => { + assert.equal(goal.getUUID(), aGoalID); + }); - it("should return the proper json", () => { - var expected:any = { - id: goal.getUUID(), - name: goal.getName(), - timeBox: { - startDate: goal.getStartDate(), - endDate: goal.getEndDate() - }, - duration: 'month', - conditions: goal.getConditions().getDataInJSON(), - badgeID: goal.getBadgeID() - } - - var actual = goal.getDataInJSON(); - - chai.expect(expected).to.be.eqls(actual); + it("should have given badgeID", () => { + assert.equal(goal.getBadgeID(), aBadgeID); + }); + + it("should have given beginningOfValidityPeriod", () => { + assert.equal(goal.getBeginningOfValidityPeriod().toISOString(), now.toISOString()); + }); + + it("should have given endOfValidityPeriod", () => { + assert.equal(goal.getEndOfValidityPeriod().toISOString(), endDate.toISOString()); + }); }); - it("should call conditionsList evaluate on evaluate call", () => { - var goalStubObj = sinon.stub(goal.getConditions(), "evaluate"); - goalStubObj.onFirstCall().returns(true); - goal.evaluate({'a': null, 'b': null}); - chai.assert(goalStubObj.calledOnce); + describe("Add a condition to a goal", () => { + it('should be possible to add an expression', () => { + var aSymbolicName:string = 'Temp_cli'; + + var expression:GoalExpression; + var expressionDescription:any = { + valueLeft: { + value: aSymbolicName, + symbolicName: true + }, + valueRight: { + value: "15", + symbolicName: false + }, + comparison: ">" + }; + + var expressionFactory:ExpressionFactory = new ExpressionFactory(); + + var aConditionID = "id1"; + var aConditionDescription = "a desc"; + var aThresholdRate = 80; + + expression = expressionFactory.createExpression(expressionDescription); + var condition = new OverallGoalCondition(aConditionID, aConditionDescription, expression, aThresholdRate, null); + + goal.addCondition(condition); + + chai.expect(goal.getConditions().length).to.be.eql(1); + }); + + it("should return the proper json", () => { + var expected:any = { + id: goal.getUUID(), + name: goal.getName(), + validityPeriod: { + start: goal.getBeginningOfValidityPeriod(), + end: goal.getEndOfValidityPeriod() + }, + recurringPeriod: aRecurringSession.getDescription(), + conditions: goal.getDataOfConditionsInJSON(), + badgeID: goal.getBadgeID() + }; + + var actual = goal.getDataInJSON(); + + chai.expect(expected).to.be.eqls(actual); + }); + }); }); diff --git a/backend/tests/integration/ChallengeBuildingTest.ts b/backend/tests/integration/ChallengeBuildingTest.ts deleted file mode 100644 index 69e84fb..0000000 --- a/backend/tests/integration/ChallengeBuildingTest.ts +++ /dev/null @@ -1,83 +0,0 @@ -/// -/// -/// -/// - -var moment = require('moment'); - -import chai = require('chai'); -import sinon = require('sinon'); -var assert = chai.assert; - -import BadgeRepository = require('../../src/badge/BadgeRepository'); -import Badge = require('../../src/badge/Badge'); - -import ChallengeRepository = require('../../src/challenge/ChallengeRepository'); -import ChallengeFactory = require('../../src/challenge/ChallengeFactory'); -import Challenge = require('../../src/challenge/Challenge'); - -import GoalRepository = require('../../src/goal/GoalRepository'); -import Goal = require('../../src/goal/Goal'); -import RecurringSession = require('../../src/goal/RecurringSession'); - -import UserRepository = require('../../src/user/UserRepository'); -import User = require('../../src/user/User'); - -import Clock = require('../../src/Clock'); -import ChallengeStatus = require('../../src/Status'); - -import ChallengeRouter = require('../../src/api/GoalInstanceRouter'); - -describe('Challenge integration test', () => { - - // Important ! Allow us to set time - ChallengeRouter.DEMO = true; - - var badgeRepository:BadgeRepository = new BadgeRepository(); - var challengeRepository:ChallengeRepository = new ChallengeRepository(); - var challengeFactory:ChallengeFactory = new ChallengeFactory(); - var goalRepository:GoalRepository = new GoalRepository(badgeRepository); - - // Build a default user / current user - var userRepository:UserRepository = new UserRepository(); - var user:User = new User('Charlie'); - userRepository.addUser(user); - userRepository.setCurrentUser(user); - - // Init the router under test - var challengeRouter:ChallengeRouter = new ChallengeRouter(challengeRepository, challengeFactory, goalRepository, userRepository); - - // Create a fake badge for fake goal - var aBadgeName = 'Badge 1'; - var aBadgePoint = 100; - var aBadge:Badge = new Badge(aBadgeName, aBadgePoint); - - // Create fake goal - var aGoalName = 'Objectif 1'; - var startDate:moment.Moment = moment("August 03, 2015 00:00:00"); - var endDate:moment.Moment = moment("September 03, 2015 00:00:00"); - - var aGoal = new Goal(aGoalName, startDate, endDate, 5, aBadge.getUuid(), null, new RecurringSession('week')); - goalRepository.addGoal(aGoal); - - it('should have initialized the new challenge status to "RUN" when challenge is created during a working week', () => { - var newChallenge = challengeRouter.createGoalInstance(aGoal.getUUID(), moment("2015-08-05T12:15:00+02:00")); - chai.expect(newChallenge.getStatus()).to.be.eq(ChallengeStatus.RUN); - }); - - it('should have initialized the new challenge status to "WAITING" when challenge is created during week-end', () => { - // The goal is recurrent every week (monday-friday). A goal created saturday must be in WAITING status - var newChallenge = challengeRouter.createGoalInstance(aGoal.getUUID(), moment("2015-08-08T12:15:00+02:00")); - chai.expect(newChallenge.getStatus()).to.be.eq(ChallengeStatus.WAIT); - }); - - it('should have set the startDate to monday if goal is "week recurrent"', () => { - var newChallenge = challengeRouter.createGoalInstance(aGoal.getUUID(), moment("2015-08-05T12:15:00+02:00")); - chai.expect(newChallenge.getStartDate().toISOString()).to.be.eq(startDate.toISOString()); - }); - - it('should have set the endDate to friday if goal is "week recurrent"', () => { - var newChallenge = challengeRouter.createGoalInstance(aGoal.getUUID(), moment("2015-08-07T23:59:59+02:00")); - chai.expect(newChallenge.getStartDate().toISOString()).to.be.eq(startDate.toISOString()); - }); -}); \ No newline at end of file diff --git a/backend/tests/integration/ChallengeIntegrationTest.ts b/backend/tests/integration/ChallengeIntegrationTest.ts new file mode 100644 index 0000000..d39c7e8 --- /dev/null +++ b/backend/tests/integration/ChallengeIntegrationTest.ts @@ -0,0 +1,108 @@ +/// +/// +/// +/// + +var moment = require('moment'); + +import chai = require('chai'); +import sinon = require('sinon'); +var assert = chai.assert; + +import BadgeRepository = require('../../src/badge/BadgeRepository'); +import Badge = require('../../src/badge/Badge'); + +import UserChallengeRepository = require('../../src/challenge/UserChallengeRepository'); +import UserChallengeFactory = require('../../src/challenge/UserChallengeFactory'); +import UserChallenge = require('../../src/challenge/UserChallenge'); + +import GoalRepository = require('../../src/goal/GoalRepository'); +import Goal = require('../../src/goal/Goal'); +import RecurringSession = require('../../src/goal/RecurringSession'); + +import UserRepository = require('../../src/user/UserRepository'); +import TeamRepository = require('../../src/user/TeamRepository'); + +import User = require('../../src/user/User'); + +import Clock = require('../../src/Clock'); +import ChallengeStatus = require('../../src/Status'); + +import Middleware = require('../../src/Middleware'); + +import DashboardRouter = require('../../src/api/DashboardRouter'); +import LoginRouter = require('../../src/api/LoginRouter'); +import Context = require('../../src/Context'); + +describe('UserChallenge integration test', () => { + + // Important ! Allow us to set time + DashboardRouter.DEMO = true; + + var context:Context = new Context('./db_test.json', './stub_values_test.json'); + + + var dashboardRouter:DashboardRouter = new DashboardRouter(context, null); + var loginRouter:LoginRouter = new LoginRouter(context); + + var aUsername:string = 'Charlie'; + var aGoalID:string = '3221c575-85ca-447b-86f3-3a4ef39985dc'; + + + var requestForLogin:any = { + username: aUsername + }; + + describe('Connection', () => { + context.loadData(); + + it('should have proper id', () => { + var token:string = loginRouter.checkUserProfile(requestForLogin).getUUID(); + var expected:string = '2cf91e02-a320-4766-aa9f-6efce3142d44'; + chai.expect(token).to.be.eq(expected); + }); + }); + + describe('Take a challenge', () => { + + + var user = loginRouter.checkUserProfile(requestForLogin); + var token:string = user.getUUID(); + + beforeEach(() => { + user.wipeCurrentChallenges(); + }); + + it('should not throw', () => { + dashboardRouter.createUserChallenge(token, aGoalID, Clock.getMomentFromString("2015-08-05T12:15:00")); + }); + + it('should have added given challenge to current user', () => { + var challenge = dashboardRouter.createUserChallenge(token, aGoalID,Clock.getMomentFromString("2015-08-05T12:15:00")); + var expected = [challenge.getID()]; + var result = user.getCurrentChallenges(); + chai.expect(result).to.be.eqls(expected); + }); + + it('should have initialized the new challenge status to "RUN" when challenge is created during a working week', () => { + var newChallenge = dashboardRouter.createUserChallenge(token, aGoalID, Clock.getMomentFromString("2015-08-05T12:15:00")); + chai.expect(newChallenge.getStatus()).to.be.eq(ChallengeStatus.RUN); + }); + + it('should have initialized the new challenge status to "WAITING" when challenge is created during week-end', () => { + var newChallenge = dashboardRouter.createUserChallenge(token, aGoalID, Clock.getMomentFromString("2015-08-08T12:15:00")); + chai.expect(newChallenge.getStatus()).to.be.eq(ChallengeStatus.WAIT); + }); + + it('should have set the startDate to monday if goal is "week recurrent"', () => { + var newChallenge = dashboardRouter.createUserChallenge(token, aGoalID, Clock.getMomentFromString("2015-08-07T12:15:00")); + chai.expect(newChallenge.getStartDate().toISOString()).to.be.eq(Clock.getMomentFromString("2015-08-03T00:00:00.000").toISOString()); + }); + + it('should have set the endDate to friday if goal is "week recurrent"', () => { + var newChallenge = dashboardRouter.createUserChallenge(token, aGoalID, Clock.getMomentFromString("2015-08-07T12:15:00")); + chai.expect(newChallenge.getEndDate().toISOString()).to.be.eq(Clock.getMomentFromString("2015-08-07T23:59:59.999").toISOString()); + }); + }); + +}); \ No newline at end of file diff --git a/frontend/Gruntfile.js b/frontend/Gruntfile.js index 8189812..128ba32 100644 --- a/frontend/Gruntfile.js +++ b/frontend/Gruntfile.js @@ -410,6 +410,10 @@ module.exports = function (grunt) { grunt.task.run(['serve:' + target]); }); + // Because npm test (which was not triggered) trigger this task + grunt.registerTask('test', []); + + /* grunt.registerTask('test', [ 'clean:server', 'wiredep', @@ -418,6 +422,7 @@ module.exports = function (grunt) { 'connect:test', 'karma' ]); +*/ grunt.registerTask('build', [ 'clean:dist', diff --git a/frontend/app/favicon.ico b/frontend/app/favicon.ico index 6527905..ea32605 100644 Binary files a/frontend/app/favicon.ico and b/frontend/app/favicon.ico differ diff --git a/frontend/app/images/Thumbs.db b/frontend/app/images/Thumbs.db new file mode 100644 index 0000000..84bf64d Binary files /dev/null and b/frontend/app/images/Thumbs.db differ diff --git a/frontend/app/images/down.png b/frontend/app/images/down.png new file mode 100644 index 0000000..61a45ab Binary files /dev/null and b/frontend/app/images/down.png differ diff --git a/frontend/app/images/fail.jpeg b/frontend/app/images/fail.jpeg new file mode 100644 index 0000000..c3aa34c Binary files /dev/null and b/frontend/app/images/fail.jpeg differ diff --git a/frontend/app/images/fail.png b/frontend/app/images/fail.png new file mode 100644 index 0000000..21164aa Binary files /dev/null and b/frontend/app/images/fail.png differ diff --git a/frontend/app/images/shame.jpg b/frontend/app/images/shame.jpg new file mode 100644 index 0000000..5ba59e8 Binary files /dev/null and b/frontend/app/images/shame.jpg differ diff --git a/frontend/app/images/youfail.png b/frontend/app/images/youfail.png new file mode 100644 index 0000000..0f60a7e Binary files /dev/null and b/frontend/app/images/youfail.png differ diff --git a/frontend/app/index.html b/frontend/app/index.html index b125872..e59b10f 100644 --- a/frontend/app/index.html +++ b/frontend/app/index.html @@ -9,7 +9,7 @@ - + @@ -52,7 +52,7 @@ @@ -62,14 +62,14 @@
- + - + - - - + - + + - - - - + + + diff --git a/frontend/app/scripts/app.js b/frontend/app/scripts/app.js index 5fac02b..dbb5c3f 100644 --- a/frontend/app/scripts/app.js +++ b/frontend/app/scripts/app.js @@ -10,55 +10,58 @@ */ var app = angular .module('ecoknowledgeApp', [ - 'ngAnimate', 'ngCookies', 'ngResource', 'ngRoute', 'ngSanitize', + 'ngAnimate', 'ngTouch', - 'datePicker' + 'ui.bootstrap' ]) .config(function ($routeProvider) { + $routeProvider .when('/', { - templateUrl: '../views/homepage/homepage.html', - controller: 'HomeCtrl', - controllerAs: 'homeCtrl' + templateUrl: '../views/login.html', + controller: 'LoginCtrl', + controllerAs: 'login' + }) + .when('/dashboard/view/', { + templateUrl: '../views/dashboard.html', + controller: 'DashboardCtrl', + controllerAs: 'dashboard' + }) + .when('/dashboard/view/:id/', { + templateUrl: '../views/dashboard.html', + controller: 'DashboardCtrl', + controllerAs: 'dashboard' }) - .when('/about', { - templateUrl: 'views/about.html', - controller: 'AboutCtrl' + .when('/dashboard/view/:id/:dashboardType', { + templateUrl: '../views/dashboard.html', + controller: 'DashboardCtrl', + controllerAs: 'dashboard' }) .when('/create-goal', { - templateUrl: 'views/create-goal.html', + templateUrl: '../views/create-goal.html', controller: 'GoalCtrl' }) .when('/create-badge', { - templateUrl: 'views/create-badge.html', + templateUrl: '../views/create-badge.html', controller: 'BadgeCtrl', - controllerAs: 'badgeCreateCtrl' + controllerAs: 'badgeCtrl' }) - .when('/view-goal/:goalId', { - templateUrl: 'views/view-goal.html', - controller: 'ViewGoalCtrl', - controllerAs: 'viewGoalCtrl' - }) - .when('/create-badge-perso', { - templateUrl: 'views/create-badge-perso.html', - controller: 'BadgeCtrlV2' - }) .otherwise({ redirectTo: '/' }); }); -app.filter('range', function() { - return function(input, total) { +app.filter('range', function () { + return function (input, total) { total = parseInt(total); - for (var i=0; i -1 && periodOfDayFilter != 'all') { + if(allIndex > -1 && periodOfDayFilter !== 'all') { iteration.filter.periodOfDayFilter.splice(allIndex,1); self.togglePeriodOfDayFilter(iteration, 'morning'); @@ -72,7 +64,7 @@ var app = angular.module('ecoknowledgeApp') self.togglePeriodOfDayFilter(iteration, 'night'); } - if(allIndex == -1 && periodOfDayFilter == 'all') { + if(allIndex === -1 && periodOfDayFilter === 'all') { iteration.filter.periodOfDayFilter = ['all']; return; } @@ -99,13 +91,20 @@ var app = angular.module('ecoknowledgeApp') type:'number', valueLeft:{ value:null, - sensor:true + symbolicName:true }, valueRight:{ value:null, - sensor:false - }, - description:null + symbolicName:false + } + }, + filter: { + dayOfWeekFilter: 'all', + periodOfDayFilter : ['all'] + }, + referencePeriod:{ + numberOfUnitToSubtract:1, + unitToSubtract:null } }; }; @@ -124,6 +123,10 @@ var app = angular.module('ecoknowledgeApp') }else if(iteration.threshold>100) { iteration.threshold = 100; } + + if(iteration.type == 'comparison') { + iteration.expression.valueRight.value = iteration.threshold; + } }; self.week = function(){ diff --git a/frontend/app/scripts/controllers/home.js b/frontend/app/scripts/controllers/home.js deleted file mode 100644 index 3b54e5c..0000000 --- a/frontend/app/scripts/controllers/home.js +++ /dev/null @@ -1,159 +0,0 @@ -'use strict'; - -/** - * @ngdoc function - * @name ecoknowledgeApp.controller:HomeCtrl - * @description - * # HomeCtrl - * Controller of the ecoknowledgeApp for the firstpage which display - * the objectives and the badges we have. - */ -var app = angular.module('ecoknowledgeApp') - .controller('HomeCtrl', ['ServiceChallenge', 'ServiceGoal', 'ServiceBadgeV2','$window', function (ServiceChallenge, ServiceGoal, ServiceBadgeV2, $window) { - var self = this; - self.goals = []; - self.goalsInstance = []; - self.obtentionValue = {}; - self.trophies = []; - /* - * Add a goal the the array goals - */ - - self.takeGoal = function (goalID) { - - var toSend = {}; - toSend.id = goalID; - - console.log('TAKE GOAL', toSend); - ServiceChallenge.takeGoal(toSend, function (data) { - console.log('Objectif instancié ', data); - $window.location.reload(); - }, function (data) { - console.log('Fail sur l\'instanciation de l\'objectif', data); - }); - }; - - self.getGoals = function () { - self.goals = []; - ServiceGoal.get('', function (data) { - self.goals = data; - }, function () { - console.log('fail to get the goals'); - }); - }; - - self.getBadges = function () { - ServiceChallenge.evaluate('all',function(data){ - console.log('achieve eval'); - },function(data){ - console.log('fail eval'); - }); - - self.goalsInstance = []; - ServiceChallenge.get('', function (data) { - console.log('get the badges : '); - self.goalsInstance = data; - - for (var badgeIndex in self.goalsInstance) { - var currentBadge = self.goalsInstance[badgeIndex]; - var startDate = new Date(currentBadge.startDate); - var formattedStartDate = '' + startDate.getDate() + '/' + (startDate.getMonth() + 1) + '/' + startDate.getFullYear(); - - var endDate = new Date(currentBadge.endDate); - var formattedEndDate = '' + (endDate.getDate()) + '/' + (endDate.getMonth() + 1 )+ '/' + endDate.getFullYear(); - - currentBadge.startDate = formattedStartDate; - currentBadge.endDate = formattedEndDate; - } - console.log('goals : ', angular.toJson(self.goalsInstance)); - - }, function (data) { - console.debug('Fail to get the badges', data); - }); - }; - - self.addGoal = function (g) { - self.goals.push(g); - }; - - /* - * add a badge to the array badges - */ - self.addBadge = function (bdg) { - self.goalsInstance.add(bdg); - }; - - self.getTrophies = function(){ - self.trophies = []; - ServiceBadgeV2.getTrophies(function(data){ - self.trophies = data; - console.log('trophies get : ', self.trophies); - },function(data){ - console.log('Error getting trophies', data); - }); - }; - - self.removeObjective = function(objective){ - console.log("OBJECTIVE", objective); - - console.log('objective to remove : ',objective.id); - ServiceChallenge.delete(objective.id,function(data){ - console.log('Succeed to remove a goal instance : ', data); - var index = self.goalsInstance.indexOf(objective); - self.goalsInstance.splice(index, 1); - $window.location.reload(); - }, function(data){ - console.log('Failed to remove a goal',data); - }); - - }; - - self.getGoals(); - self.getBadges(); - self.getTrophies(); - }]); - -app.directive('listGoal', function () { - return { - restrict: 'E', - templateUrl: '../../views/homepage/list-goal.html', - controller: 'HomeCtrl', - controllerAs: 'homeCtrl' - }; -}); - -app.directive('listChallenge', function () { - return { - restrict: 'E', - templateUrl: '../../views/homepage/list-challenge.html' - }; -}); - -app.directive('listTrophies', function () { - return { - restrict: 'E', - templateUrl: '../../views/homepage/list-trophies.html' - }; -}); - - -app.directive('homepageChallenge', function () { - return { - restrict: 'E', - templateUrl: '../../views/homepage/homepage-challenge.html' - }; -}); - -app.directive('homepageGoal', function () { - return { - restrict: 'E', - templateUrl: '../../views/homepage/homepage-goal.html' - }; -}); - -app.directive('homepageTrophy', function () { - return { - restrict: 'E', - templateUrl: '../../views/homepage/homepage-trophy.html' - }; -}); diff --git a/frontend/app/scripts/controllers/login.js b/frontend/app/scripts/controllers/login.js new file mode 100644 index 0000000..5ba3331 --- /dev/null +++ b/frontend/app/scripts/controllers/login.js @@ -0,0 +1,30 @@ +'use strict'; + +var app = angular.module('ecoknowledgeApp'); + +app.controller('LoginCtrl', ['ServiceLogin', '$rootScope', '$location', '$cookies', function (ServiceLogin, $rootScope, $location, $cookies) { + this.username = ''; + this.debug = ''; + + var self = this; + this.connect = function () { + ServiceLogin.login(self.username, + function (data) { + console.log('Login success: data received', data); + + $cookies.put('token', data.data.token); + $cookies.put('dashboardWanted', 'personal'); + + console.log('Token stored : ', $cookies.get('token')); + + var pathToDashboard = '/dashboard/view/' + data.data.token; + + console.log('Redirection vers', pathToDashboard); + $location.path(pathToDashboard); + }, + function (data) { + console.log('Login fail: data received', data); + }); + }; + +}]); diff --git a/frontend/app/scripts/controllers/main.js b/frontend/app/scripts/controllers/main.js deleted file mode 100644 index 027e0b5..0000000 --- a/frontend/app/scripts/controllers/main.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -/** - * @ngdoc function - * @name ecoknowledgeApp.controller:MainCtrl - * @description - * # MainCtrl - * Controller of the ecoknowledgeApp - */ -angular.module('ecoknowledgeApp') - .controller('MainCtrl', function ($scope) { - $scope.awesomeThings = [ - 'HTML5 Boilerplate', - 'AngularJS', - 'Karma' - ]; - }); diff --git a/frontend/app/scripts/controllers/viewGoal.js b/frontend/app/scripts/controllers/viewGoal.js deleted file mode 100644 index 88e44c1..0000000 --- a/frontend/app/scripts/controllers/viewGoal.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -/** - * @ngdoc function - * @name ecoknowledgeApp.controller:ViewGoalCtrl - * @description - * # ViewGoalCtrl - * Controller of the ecoknowledgeApp for the view of a goal - */ - -angular.module('ecoknowledgeApp') - .controller('ViewGoalCtrl',['ServiceGoal','ServiceBadgeV2', '$route', function (ServiceGoal, ServiceBadgeV2, $route) { - var self = this; - self.goal = {}; - self.badge = {}; - console.log('route : ',$route.current.params.goalId); - self.getGoal = function() { - ServiceGoal.get($route.current.params.goalId,function(data){ - console.log('SUCCESS data in GET goal/idGoal',data); - self.goal = data; - ServiceBadgeV2.get(self.goal.badgeID, function(dataBadge){ - console.log('SUCCESS badge data in GET badge/idBadge : ', dataBadge); - self.badge = dataBadge; - },function(dataBadge){ - console.log('FAIL badge data in GET badge/idBadge : ', dataBadge); - }); - },function(data){ - console.log('FAIL data in GET goal/idGoal',data); - }); - }; - - self.getGoal(); - } -]); \ No newline at end of file diff --git a/frontend/app/styles/create-goal.css b/frontend/app/styles/create-goal.css index cffec77..269631e 100644 --- a/frontend/app/styles/create-goal.css +++ b/frontend/app/styles/create-goal.css @@ -2,4 +2,15 @@ color:#fff; background-color: #337ab7; border-color: #2e6da4; -} \ No newline at end of file +} + +.create-goal-container { + width:800px; + margin-left:auto; + margin-right:auto; + padding:50px; +} + +#create-goal-date { + margin-top : 50px; +} diff --git a/frontend/app/styles/homepage-challenges.css b/frontend/app/styles/homepage-challenges.css index c047893..3e21e06 100644 --- a/frontend/app/styles/homepage-challenges.css +++ b/frontend/app/styles/homepage-challenges.css @@ -1,36 +1,46 @@ -.challenges-list{ +.challenges-list { list-style-type: none; margin: 0; padding: 0; } + .challenge-btn { - float:right; + float: right; } + .challenge-item { list-style-type: none; margin-top: 10px; text-align: center; - font-size:150%; + font-size: 150%; } + .challenge-name { - text-align:center; + text-align: center; } .challenge-container { - width:70%; - background-color:#F5F6F6; - margin-bottom : 35px; - width:550px; - padding:15px; + width: 70%; + margin-bottom: 35px; + width: 550px; + padding: 15px; +} + +.user { + background-color: #5ECD6D; +} + +.team { + background-color: #0086b3; } .challenge-container table { - width:100%; - height:100%; + width: 100%; + height: 100%; } .challenge-container .challenge-name, .challenge-container .challenge-points { - margin-bottom:15px; + margin-bottom: 15px; } .challenge-container .challenge-desc { @@ -49,19 +59,37 @@ } .challenge-status-WAIT { - background-color:grey; + background-color: grey; opacity: 0.5; } .challenge-status-RUNNING { - background-color:#28a4c9; + background-color: #28a4c9; } .challenge-status-SUCCESS { - background-color:green; + background-color: green; } .challenge-status-FAIL { + background-color: #E14545; +} + +.challenge-date { + font-size:80%; +} + +/* +.condition-achieved-true { + background-color: #5cb85c; +} +*/ + +.condition-achieved-false { background-color:red; } +.condition-achieved-false-username { + font-weight: bold; + font-size:150%; +} diff --git a/frontend/app/styles/login.css b/frontend/app/styles/login.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/app/styles/main.css b/frontend/app/styles/main.css index 13010c8..ef4cf22 100644 --- a/frontend/app/styles/main.css +++ b/frontend/app/styles/main.css @@ -1,3 +1,8 @@ +.navbar { + margin-bottom:0px; + border : 0px solid white; +} + .browsehappy { margin: 0.2em 0; background: #ccc; @@ -69,7 +74,7 @@ body { /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { .container { - max-width: 730px; + max-width: 1200px; } /* Remove the padding we set earlier */ diff --git a/frontend/app/ui-bootstrap-tpls-0.9.0.min.js b/frontend/app/ui-bootstrap-tpls-0.9.0.min.js new file mode 100644 index 0000000..da950fb --- /dev/null +++ b/frontend/app/ui-bootstrap-tpls-0.9.0.min.js @@ -0,0 +1,2 @@ +angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/popup.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(a,b,c){function d(a){for(var b in a)if(void 0!==f.style[b])return a[b]}var e=function(d,f,g){g=g||{};var h=a.defer(),i=e[g.animation?"animationEndEventName":"transitionEndEventName"],j=function(){c.$apply(function(){d.unbind(i,j),h.resolve(d)})};return i&&d.bind(i,j),b(function(){angular.isString(f)?d.addClass(f):angular.isFunction(f)?f(d):angular.isObject(f)&&d.css(f),i||h.resolve(d)}),h.promise.cancel=function(){i&&d.unbind(i,j),h.reject("Transition cancelled")},h.promise},f=document.createElement("trans"),g={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},h={WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"};return e.transitionEndEventName=d(g),e.animationEndEventName=d(h),e}]),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(a){return{link:function(b,c,d){function e(b){function d(){j===e&&(j=void 0)}var e=a(c,b);return j&&j.cancel(),j=e,e.then(d,d),e}function f(){k?(k=!1,g()):(c.removeClass("collapse").addClass("collapsing"),e({height:c[0].scrollHeight+"px"}).then(g))}function g(){c.removeClass("collapsing"),c.addClass("collapse in"),c.css({height:"auto"})}function h(){if(k)k=!1,i(),c.css({height:0});else{c.css({height:c[0].scrollHeight+"px"});{c[0].offsetWidth}c.removeClass("collapse in").addClass("collapsing"),e({height:0}).then(i)}}function i(){c.removeClass("collapsing"),c.addClass("collapse")}var j,k=!0;b.$watch(d.collapse,function(a){a?h():f()})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(this.groups.indexOf(a),1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",["$parse",function(a){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(b,c,d,e){var f,g;e.addGroup(b),b.isOpen=!1,d.isOpen&&(f=a(d.isOpen),g=f.assign,b.$parent.$watch(f,function(a){b.isOpen=!!a})),b.$watch("isOpen",function(a){a&&e.closeOthers(b),g&&g(b.$parent,a)})}}}]).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",compile:function(a,b,c){return function(a,b,d,e){e.setHeading(c(a,function(){}))}}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(a,b,c,d){a.$watch(function(){return d[c.accordionTransclude]},function(a){a&&(b.html(""),b.append(a))})}}}),angular.module("ui.bootstrap.alert",[]).controller("AlertController",["$scope","$attrs",function(a,b){a.closeable="close"in b}]).directive("alert",function(){return{restrict:"EA",controller:"AlertController",templateUrl:"template/alert/alert.html",transclude:!0,replace:!0,scope:{type:"=",close:"&"}}}),angular.module("ui.bootstrap.bindHtml",[]).directive("bindHtmlUnsafe",function(){return function(a,b,c){b.addClass("ng-binding").data("$binding",c.bindHtmlUnsafe),a.$watch(c.bindHtmlUnsafe,function(a){b.html(a||"")})}}),angular.module("ui.bootstrap.buttons",[]).constant("buttonConfig",{activeClass:"active",toggleEvent:"click"}).controller("ButtonsController",["buttonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("btnRadio",function(){return{require:["btnRadio","ngModel"],controller:"ButtonsController",link:function(a,b,c,d){var e=d[0],f=d[1];f.$render=function(){b.toggleClass(e.activeClass,angular.equals(f.$modelValue,a.$eval(c.btnRadio)))},b.bind(e.toggleEvent,function(){b.hasClass(e.activeClass)||a.$apply(function(){f.$setViewValue(a.$eval(c.btnRadio)),f.$render()})})}}}).directive("btnCheckbox",function(){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){var d=a.$eval(b);return angular.isDefined(d)?d:c}var h=d[0],i=d[1];i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.bind(h.toggleEvent,function(){a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.carousel",["ui.bootstrap.transition"]).controller("CarouselController",["$scope","$timeout","$transition","$q",function(a,b,c){function d(){e();var c=+a.interval;!isNaN(c)&&c>=0&&(g=b(f,c))}function e(){g&&(b.cancel(g),g=null)}function f(){h?(a.next(),d()):a.pause()}var g,h,i=this,j=i.slides=[],k=-1;i.currentSlide=null;var l=!1;i.select=function(e,f){function g(){if(!l){if(i.currentSlide&&angular.isString(f)&&!a.noTransition&&e.$element){e.$element.addClass(f);{e.$element[0].offsetWidth}angular.forEach(j,function(a){angular.extend(a,{direction:"",entering:!1,leaving:!1,active:!1})}),angular.extend(e,{direction:f,active:!0,entering:!0}),angular.extend(i.currentSlide||{},{direction:f,leaving:!0}),a.$currentTransition=c(e.$element,{}),function(b,c){a.$currentTransition.then(function(){h(b,c)},function(){h(b,c)})}(e,i.currentSlide)}else h(e,i.currentSlide);i.currentSlide=e,k=m,d()}}function h(b,c){angular.extend(b,{direction:"",active:!0,leaving:!1,entering:!1}),angular.extend(c||{},{direction:"",active:!1,leaving:!1,entering:!1}),a.$currentTransition=null}var m=j.indexOf(e);void 0===f&&(f=m>k?"next":"prev"),e&&e!==i.currentSlide&&(a.$currentTransition?(a.$currentTransition.cancel(),b(g)):g())},a.$on("$destroy",function(){l=!0}),i.indexOfSlide=function(a){return j.indexOf(a)},a.next=function(){var b=(k+1)%j.length;return a.$currentTransition?void 0:i.select(j[b],"next")},a.prev=function(){var b=0>k-1?j.length-1:k-1;return a.$currentTransition?void 0:i.select(j[b],"prev")},a.select=function(a){i.select(a)},a.isActive=function(a){return i.currentSlide===a},a.slides=function(){return j},a.$watch("interval",d),a.$on("$destroy",e),a.play=function(){h||(h=!0,d())},a.pause=function(){a.noPause||(h=!1,e())},i.addSlide=function(b,c){b.$element=c,j.push(b),1===j.length||b.active?(i.select(j[j.length-1]),1==j.length&&a.play()):b.active=!1},i.removeSlide=function(a){var b=j.indexOf(a);j.splice(b,1),j.length>0&&a.active?b>=j.length?i.select(j[b-1]):i.select(j[b]):k>b&&k--}}]).directive("carousel",[function(){return{restrict:"EA",transclude:!0,replace:!0,controller:"CarouselController",require:"carousel",templateUrl:"template/carousel/carousel.html",scope:{interval:"=",noTransition:"=",noPause:"="}}}]).directive("slide",["$parse",function(a){return{require:"^carousel",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/carousel/slide.html",scope:{},link:function(b,c,d,e){if(d.active){var f=a(d.active),g=f.assign,h=b.active=f(b.$parent);b.$watch(function(){var a=f(b.$parent);return a!==b.active&&(a!==h?h=b.active=a:g(b.$parent,a=h=b.active)),a})}e.addSlide(b,c),b.$on("$destroy",function(){e.removeSlide(b)}),b.$watch("active",function(a){a&&e.select(b)})}}}]),angular.module("ui.bootstrap.position",[]).factory("$position",["$document","$window",function(a,b){function c(a,c){return a.currentStyle?a.currentStyle[c]:b.getComputedStyle?b.getComputedStyle(a)[c]:a.style[c]}function d(a){return"static"===(c(a,"position")||"static")}var e=function(b){for(var c=a[0],e=b.offsetParent||c;e&&e!==c&&d(e);)e=e.offsetParent;return e||c};return{position:function(b){var c=this.offset(b),d={top:0,left:0},f=e(b[0]);f!=a[0]&&(d=this.offset(angular.element(f)),d.top+=f.clientTop-f.scrollTop,d.left+=f.clientLeft-f.scrollLeft);var g=b[0].getBoundingClientRect();return{width:g.width||b.prop("offsetWidth"),height:g.height||b.prop("offsetHeight"),top:c.top-d.top,left:c.left-d.left}},offset:function(c){var d=c[0].getBoundingClientRect();return{width:d.width||c.prop("offsetWidth"),height:d.height||c.prop("offsetHeight"),top:d.top+(b.pageYOffset||a[0].body.scrollTop||a[0].documentElement.scrollTop),left:d.left+(b.pageXOffset||a[0].body.scrollLeft||a[0].documentElement.scrollLeft)}}}}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.position"]).constant("datepickerConfig",{dayFormat:"dd",monthFormat:"MMMM",yearFormat:"yyyy",dayHeaderFormat:"EEE",dayTitleFormat:"MMMM yyyy",monthTitleFormat:"yyyy",showWeeks:!0,startingDay:0,yearRange:20,minDate:null,maxDate:null}).controller("DatepickerController",["$scope","$attrs","dateFilter","datepickerConfig",function(a,b,c,d){function e(b,c){return angular.isDefined(b)?a.$parent.$eval(b):c}function f(a,b){return new Date(a,b,0).getDate()}function g(a,b){for(var c=new Array(b),d=a,e=0;b>e;)c[e++]=new Date(d),d.setDate(d.getDate()+1);return c}function h(a,b,d,e){return{date:a,label:c(a,b),selected:!!d,secondary:!!e}}var i={day:e(b.dayFormat,d.dayFormat),month:e(b.monthFormat,d.monthFormat),year:e(b.yearFormat,d.yearFormat),dayHeader:e(b.dayHeaderFormat,d.dayHeaderFormat),dayTitle:e(b.dayTitleFormat,d.dayTitleFormat),monthTitle:e(b.monthTitleFormat,d.monthTitleFormat)},j=e(b.startingDay,d.startingDay),k=e(b.yearRange,d.yearRange);this.minDate=d.minDate?new Date(d.minDate):null,this.maxDate=d.maxDate?new Date(d.maxDate):null,this.modes=[{name:"day",getVisibleDates:function(a,b){var d=a.getFullYear(),e=a.getMonth(),k=new Date(d,e,1),l=j-k.getDay(),m=l>0?7-l:-l,n=new Date(k),o=0;m>0&&(n.setDate(-m+1),o+=m),o+=f(d,e+1),o+=(7-o%7)%7;for(var p=g(n,o),q=new Array(7),r=0;o>r;r++){var s=new Date(p[r]);p[r]=h(s,i.day,b&&b.getDate()===s.getDate()&&b.getMonth()===s.getMonth()&&b.getFullYear()===s.getFullYear(),s.getMonth()!==e)}for(var t=0;7>t;t++)q[t]=c(p[t].date,i.dayHeader);return{objects:p,title:c(a,i.dayTitle),labels:q}},compare:function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},split:7,step:{months:1}},{name:"month",getVisibleDates:function(a,b){for(var d=new Array(12),e=a.getFullYear(),f=0;12>f;f++){var g=new Date(e,f,1);d[f]=h(g,i.month,b&&b.getMonth()===f&&b.getFullYear()===e)}return{objects:d,title:c(a,i.monthTitle)}},compare:function(a,b){return new Date(a.getFullYear(),a.getMonth())-new Date(b.getFullYear(),b.getMonth())},split:3,step:{years:1}},{name:"year",getVisibleDates:function(a,b){for(var c=new Array(k),d=a.getFullYear(),e=parseInt((d-1)/k,10)*k+1,f=0;k>f;f++){var g=new Date(e+f,0,1);c[f]=h(g,i.year,b&&b.getFullYear()===g.getFullYear())}return{objects:c,title:[c[0].label,c[k-1].label].join(" - ")}},compare:function(a,b){return a.getFullYear()-b.getFullYear()},split:5,step:{years:k}}],this.isDisabled=function(b,c){var d=this.modes[c||0];return this.minDate&&d.compare(b,this.minDate)<0||this.maxDate&&d.compare(b,this.maxDate)>0||a.dateDisabled&&a.dateDisabled({date:b,mode:d.name})}}]).directive("datepicker",["dateFilter","$parse","datepickerConfig","$log",function(a,b,c,d){return{restrict:"EA",replace:!0,templateUrl:"template/datepicker/datepicker.html",scope:{dateDisabled:"&"},require:["datepicker","?^ngModel"],controller:"DatepickerController",link:function(a,e,f,g){function h(){a.showWeekNumbers=0===o&&q}function i(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c}function j(b){var c=null,e=!0;n.$modelValue&&(c=new Date(n.$modelValue),isNaN(c)?(e=!1,d.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):b&&(p=c)),n.$setValidity("date",e);var f=m.modes[o],g=f.getVisibleDates(p,c);angular.forEach(g.objects,function(a){a.disabled=m.isDisabled(a.date,o)}),n.$setValidity("date-disabled",!c||!m.isDisabled(c)),a.rows=i(g.objects,f.split),a.labels=g.labels||[],a.title=g.title}function k(a){o=a,h(),j()}function l(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}var m=g[0],n=g[1];if(n){var o=0,p=new Date,q=c.showWeeks;f.showWeeks?a.$parent.$watch(b(f.showWeeks),function(a){q=!!a,h()}):h(),f.min&&a.$parent.$watch(b(f.min),function(a){m.minDate=a?new Date(a):null,j()}),f.max&&a.$parent.$watch(b(f.max),function(a){m.maxDate=a?new Date(a):null,j()}),n.$render=function(){j(!0)},a.select=function(a){if(0===o){var b=n.$modelValue?new Date(n.$modelValue):new Date(0,0,0,0,0,0,0);b.setFullYear(a.getFullYear(),a.getMonth(),a.getDate()),n.$setViewValue(b),j(!0)}else p=a,k(o-1)},a.move=function(a){var b=m.modes[o].step;p.setMonth(p.getMonth()+a*(b.months||0)),p.setFullYear(p.getFullYear()+a*(b.years||0)),j()},a.toggleMode=function(){k((o+1)%m.modes.length)},a.getWeekNumber=function(b){return 0===o&&a.showWeekNumbers&&7===b.length?l(b[0].date):null}}}}}]).constant("datepickerPopupConfig",{dateFormat:"yyyy-MM-dd",currentText:"Today",toggleWeeksText:"Weeks",clearText:"Clear",closeText:"Done",closeOnDateSelection:!0,appendToBody:!1,showButtonBar:!0}).directive("datepickerPopup",["$compile","$parse","$document","$position","dateFilter","datepickerPopupConfig","datepickerConfig",function(a,b,c,d,e,f,g){return{restrict:"EA",require:"ngModel",link:function(h,i,j,k){function l(a){u?u(h,!!a):q.isOpen=!!a}function m(a){if(a){if(angular.isDate(a))return k.$setValidity("date",!0),a;if(angular.isString(a)){var b=new Date(a);return isNaN(b)?(k.$setValidity("date",!1),void 0):(k.$setValidity("date",!0),b)}return k.$setValidity("date",!1),void 0}return k.$setValidity("date",!0),null}function n(a,c,d){a&&(h.$watch(b(a),function(a){q[c]=a}),y.attr(d||c,c))}function o(){q.position=s?d.offset(i):d.position(i),q.position.top=q.position.top+i.prop("offsetHeight")}var p,q=h.$new(),r=angular.isDefined(j.closeOnDateSelection)?h.$eval(j.closeOnDateSelection):f.closeOnDateSelection,s=angular.isDefined(j.datepickerAppendToBody)?h.$eval(j.datepickerAppendToBody):f.appendToBody;j.$observe("datepickerPopup",function(a){p=a||f.dateFormat,k.$render()}),q.showButtonBar=angular.isDefined(j.showButtonBar)?h.$eval(j.showButtonBar):f.showButtonBar,h.$on("$destroy",function(){B.remove(),q.$destroy()}),j.$observe("currentText",function(a){q.currentText=angular.isDefined(a)?a:f.currentText}),j.$observe("toggleWeeksText",function(a){q.toggleWeeksText=angular.isDefined(a)?a:f.toggleWeeksText}),j.$observe("clearText",function(a){q.clearText=angular.isDefined(a)?a:f.clearText}),j.$observe("closeText",function(a){q.closeText=angular.isDefined(a)?a:f.closeText});var t,u;j.isOpen&&(t=b(j.isOpen),u=t.assign,h.$watch(t,function(a){q.isOpen=!!a})),q.isOpen=t?t(h):!1;var v=function(a){q.isOpen&&a.target!==i[0]&&q.$apply(function(){l(!1)})},w=function(){q.$apply(function(){l(!0)})},x=angular.element("
");x.attr({"ng-model":"date","ng-change":"dateSelection()"});var y=angular.element(x.children()[0]);j.datepickerOptions&&y.attr(angular.extend({},h.$eval(j.datepickerOptions))),k.$parsers.unshift(m),q.dateSelection=function(a){angular.isDefined(a)&&(q.date=a),k.$setViewValue(q.date),k.$render(),r&&l(!1)},i.bind("input change keyup",function(){q.$apply(function(){q.date=k.$modelValue})}),k.$render=function(){var a=k.$viewValue?e(k.$viewValue,p):"";i.val(a),q.date=k.$modelValue},n(j.min,"min"),n(j.max,"max"),j.showWeeks?n(j.showWeeks,"showWeeks","show-weeks"):(q.showWeeks=g.showWeeks,y.attr("show-weeks","showWeeks")),j.dateDisabled&&y.attr("date-disabled",j.dateDisabled);var z=!1,A=!1;q.$watch("isOpen",function(a){a?(o(),c.bind("click",v),A&&i.unbind("focus",w),i[0].focus(),z=!0):(z&&c.unbind("click",v),i.bind("focus",w),A=!0),u&&u(h,a)}),q.today=function(){q.dateSelection(new Date)},q.clear=function(){q.dateSelection(null)};var B=a(x)(q);s?c.find("body").append(B):i.after(B)}}}]).directive("datepickerPopupWrap",function(){return{restrict:"EA",replace:!0,transclude:!0,templateUrl:"template/datepicker/popup.html",link:function(a,b){b.bind("click",function(a){a.preventDefault(),a.stopPropagation()})}}}),angular.module("ui.bootstrap.dropdownToggle",[]).directive("dropdownToggle",["$document","$location",function(a){var b=null,c=angular.noop;return{restrict:"CA",link:function(d,e){d.$watch("$location.path",function(){c()}),e.parent().bind("click",function(){c()}),e.bind("click",function(d){var f=e===b;d.preventDefault(),d.stopPropagation(),b&&c(),f||e.hasClass("disabled")||e.prop("disabled")||(e.parent().addClass("open"),b=e,c=function(d){d&&(d.preventDefault(),d.stopPropagation()),a.unbind("click",c),e.parent().removeClass("open"),c=angular.noop,b=null},a.bind("click",c))})}}}]),angular.module("ui.bootstrap.modal",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c0),h&&-1==e()&&(h.remove(),h=void 0),d.modalScope.$destroy()}var g,h,i="modal-open",j=c.$new(!0),k=d.createNew(),l={};return c.$watch(e,function(a){j.index=a}),a.bind("keydown",function(a){var b;27===a.which&&(b=k.top(),b&&b.value.keyboard&&c.$apply(function(){l.dismiss(b.key)}))}),l.open=function(c,d){k.add(c,{deferred:d.deferred,modalScope:d.scope,backdrop:d.backdrop,keyboard:d.keyboard});var f=a.find("body").eq(0);e()>=0&&!h&&(g=angular.element("
"),h=b(g)(j),f.append(h));var l=angular.element("
");l.attr("window-class",d.windowClass),l.attr("index",k.length()-1),l.html(d.content);var m=b(l)(d.scope);k.top().value.modalDomEl=m,f.append(m),f.addClass(i)},l.close=function(a,b){var c=k.get(a).value;c&&(c.deferred.resolve(b),f(a))},l.dismiss=function(a,b){var c=k.get(a).value;c&&(c.deferred.reject(b),f(a))},l.getTop=function(){return k.top()},l}]).provider("$modal",function(){var a={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?d.when(a.template):e.get(a.templateUrl,{cache:f}).then(function(a){return a.data})}function j(a){var c=[];return angular.forEach(a,function(a){(angular.isFunction(a)||angular.isArray(a))&&c.push(d.when(b.invoke(a)))}),c}var k={};return k.open=function(b){var e=d.defer(),f=d.defer(),k={result:e.promise,opened:f.promise,close:function(a){h.close(k,a)},dismiss:function(a){h.dismiss(k,a)}};if(b=angular.extend({},a.options,b),b.resolve=b.resolve||{},!b.template&&!b.templateUrl)throw new Error("One of template or templateUrl options is required.");var l=d.all([i(b)].concat(j(b.resolve)));return l.then(function(a){var d=(b.scope||c).$new();d.$close=k.close,d.$dismiss=k.dismiss;var f,i={},j=1;b.controller&&(i.$scope=d,i.$modalInstance=k,angular.forEach(b.resolve,function(b,c){i[c]=a[j++]}),f=g(b.controller,i)),h.open(k,{scope:d,deferred:e,content:a[0],backdrop:b.backdrop,keyboard:b.keyboard,windowClass:b.windowClass})},function(a){e.reject(a)}),l.then(function(){f.resolve(!0)},function(){f.reject(!1)}),k},k}]};return a}),angular.module("ui.bootstrap.pagination",[]).controller("PaginationController",["$scope","$attrs","$parse","$interpolate",function(a,b,c,d){var e=this,f=b.numPages?c(b.numPages).assign:angular.noop;this.init=function(d){b.itemsPerPage?a.$parent.$watch(c(b.itemsPerPage),function(b){e.itemsPerPage=parseInt(b,10),a.totalPages=e.calculateTotalPages()}):this.itemsPerPage=d},this.noPrevious=function(){return 1===this.page},this.noNext=function(){return this.page===a.totalPages},this.isActive=function(a){return this.page===a},this.calculateTotalPages=function(){var b=this.itemsPerPage<1?1:Math.ceil(a.totalItems/this.itemsPerPage);return Math.max(b||0,1)},this.getAttributeValue=function(b,c,e){return angular.isDefined(b)?e?d(b)(a.$parent):a.$parent.$eval(b):c},this.render=function(){this.page=parseInt(a.page,10)||1,this.page>0&&this.page<=a.totalPages&&(a.pages=this.getPages(this.page,a.totalPages))},a.selectPage=function(b){!e.isActive(b)&&b>0&&b<=a.totalPages&&(a.page=b,a.onSelectPage({page:b}))},a.$watch("page",function(){e.render()}),a.$watch("totalItems",function(){a.totalPages=e.calculateTotalPages()}),a.$watch("totalPages",function(b){f(a.$parent,b),e.page>b?a.selectPage(b):e.render()})}]).constant("paginationConfig",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0}).directive("pagination",["$parse","paginationConfig",function(a,b){return{restrict:"EA",scope:{page:"=",totalItems:"=",onSelectPage:" &"},controller:"PaginationController",templateUrl:"template/pagination/pagination.html",replace:!0,link:function(c,d,e,f){function g(a,b,c,d){return{number:a,text:b,active:c,disabled:d}}var h,i=f.getAttributeValue(e.boundaryLinks,b.boundaryLinks),j=f.getAttributeValue(e.directionLinks,b.directionLinks),k=f.getAttributeValue(e.firstText,b.firstText,!0),l=f.getAttributeValue(e.previousText,b.previousText,!0),m=f.getAttributeValue(e.nextText,b.nextText,!0),n=f.getAttributeValue(e.lastText,b.lastText,!0),o=f.getAttributeValue(e.rotate,b.rotate);f.init(b.itemsPerPage),e.maxSize&&c.$parent.$watch(a(e.maxSize),function(a){h=parseInt(a,10),f.render()}),f.getPages=function(a,b){var c=[],d=1,e=b,p=angular.isDefined(h)&&b>h;p&&(o?(d=Math.max(a-Math.floor(h/2),1),e=d+h-1,e>b&&(e=b,d=e-h+1)):(d=(Math.ceil(a/h)-1)*h+1,e=Math.min(d+h-1,b)));for(var q=d;e>=q;q++){var r=g(q,q,f.isActive(q),!1);c.push(r)}if(p&&!o){if(d>1){var s=g(d-1,"...",!1,!1);c.unshift(s)}if(b>e){var t=g(e+1,"...",!1,!1);c.push(t)}}if(j){var u=g(a-1,l,!1,f.noPrevious());c.unshift(u);var v=g(a+1,m,!1,f.noNext());c.push(v)}if(i){var w=g(1,k,!1,f.noPrevious());c.unshift(w);var x=g(b,n,!1,f.noNext());c.push(x)}return c}}}}]).constant("pagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("pager",["pagerConfig",function(a){return{restrict:"EA",scope:{page:"=",totalItems:"=",onSelectPage:" &"},controller:"PaginationController",templateUrl:"template/pagination/pager.html",replace:!0,link:function(b,c,d,e){function f(a,b,c,d,e){return{number:a,text:b,disabled:c,previous:i&&d,next:i&&e}}var g=e.getAttributeValue(d.previousText,a.previousText,!0),h=e.getAttributeValue(d.nextText,a.nextText,!0),i=e.getAttributeValue(d.align,a.align);e.init(a.itemsPerPage),e.getPages=function(a){return[f(a-1,g,e.noPrevious(),!0,!1),f(a+1,h,e.noNext(),!1,!0)]}}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.bindHtml"]).provider("$tooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",animation:!0,popupDelay:0},c={mouseenter:"mouseleave",click:"click",focus:"blur"},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$parse","$document","$position","$interpolate",function(e,f,g,h,i,j,k){return function(e,l,m){function n(a){var b=a||o.trigger||m,d=c[b]||b;return{show:b,hide:d}}var o=angular.extend({},b,d),p=a(e),q=k.startSymbol(),r=k.endSymbol(),s="
';return{restrict:"EA",scope:!0,link:function(a,b,c){function d(){a.tt_isOpen?m():k()}function k(){(!y||a.$eval(c[l+"Enable"]))&&(a.tt_popupDelay?(t=g(p,a.tt_popupDelay),t.then(function(a){a()})):a.$apply(p)())}function m(){a.$apply(function(){q()})}function p(){return a.tt_content?(r&&g.cancel(r),u.css({top:0,left:0,display:"block"}),v?i.find("body").append(u):b.after(u),z(),a.tt_isOpen=!0,z):angular.noop}function q(){a.tt_isOpen=!1,g.cancel(t),a.tt_animation?r=g(function(){u.remove()},500):u.remove()}var r,t,u=f(s)(a),v=angular.isDefined(o.appendToBody)?o.appendToBody:!1,w=n(void 0),x=!1,y=angular.isDefined(c[l+"Enable"]),z=function(){var c,d,e,f;switch(c=v?j.offset(b):j.position(b),d=u.prop("offsetWidth"),e=u.prop("offsetHeight"),a.tt_placement){case"right":f={top:c.top+c.height/2-e/2,left:c.left+c.width};break;case"bottom":f={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"left":f={top:c.top+c.height/2-e/2,left:c.left-d};break;default:f={top:c.top-e,left:c.left+c.width/2-d/2}}f.top+="px",f.left+="px",u.css(f)};a.tt_isOpen=!1,c.$observe(e,function(b){a.tt_content=b,!b&&a.tt_isOpen&&q()}),c.$observe(l+"Title",function(b){a.tt_title=b}),c.$observe(l+"Placement",function(b){a.tt_placement=angular.isDefined(b)?b:o.placement}),c.$observe(l+"PopupDelay",function(b){var c=parseInt(b,10);a.tt_popupDelay=isNaN(c)?o.popupDelay:c});var A=function(){x&&(b.unbind(w.show,k),b.unbind(w.hide,m))};c.$observe(l+"Trigger",function(a){A(),w=n(a),w.show===w.hide?b.bind(w.show,d):(b.bind(w.show,k),b.bind(w.hide,m)),x=!0});var B=a.$eval(c[l+"Animation"]);a.tt_animation=angular.isDefined(B)?!!B:o.animation,c.$observe(l+"AppendToBody",function(b){v=angular.isDefined(b)?h(b)(a):v}),v&&a.$on("$locationChangeSuccess",function(){a.tt_isOpen&&q()}),a.$on("$destroy",function(){g.cancel(r),g.cancel(t),A(),u.remove(),u.unbind(),u=null})}}}}]}).directive("tooltipPopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html"}}).directive("tooltip",["$tooltip",function(a){return a("tooltip","tooltip","mouseenter")}]).directive("tooltipHtmlUnsafePopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-unsafe-popup.html"}}).directive("tooltipHtmlUnsafe",["$tooltip",function(a){return a("tooltipHtmlUnsafe","tooltip","mouseenter")}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("popoverPopup",function(){return{restrict:"EA",replace:!0,scope:{title:"@",content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html"}}).directive("popover",["$compile","$timeout","$parse","$window","$tooltip",function(a,b,c,d,e){return e("popover","popover","click")}]),angular.module("ui.bootstrap.progressbar",["ui.bootstrap.transition"]).constant("progressConfig",{animate:!0,max:100}).controller("ProgressController",["$scope","$attrs","progressConfig","$transition",function(a,b,c,d){var e=this,f=[],g=angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max,h=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.addBar=function(a,b){var c=0,d=a.$parent.$index;angular.isDefined(d)&&f[d]&&(c=f[d].value),f.push(a),this.update(b,a.value,c),a.$watch("value",function(a,c){a!==c&&e.update(b,a,c)}),a.$on("$destroy",function(){e.removeBar(a)})},this.update=function(a,b,c){var e=this.getPercentage(b);h?(a.css("width",this.getPercentage(c)+"%"),d(a,{width:e+"%"})):a.css({transition:"none",width:e+"%"})},this.removeBar=function(a){f.splice(f.indexOf(a),1)},this.getPercentage=function(a){return Math.round(100*a/g)}}]).directive("progress",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",require:"progress",scope:{},template:'
'}}).directive("bar",function(){return{restrict:"EA",replace:!0,transclude:!0,require:"^progress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b)}}}).directive("progressbar",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]))}}}),angular.module("ui.bootstrap.rating",[]).constant("ratingConfig",{max:5,stateOn:null,stateOff:null}).controller("RatingController",["$scope","$attrs","$parse","ratingConfig",function(a,b,c,d){this.maxRange=angular.isDefined(b.max)?a.$parent.$eval(b.max):d.max,this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):d.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):d.stateOff,this.createRateObjects=function(a){for(var b={stateOn:this.stateOn,stateOff:this.stateOff},c=0,d=a.length;d>c;c++)a[c]=angular.extend({index:c},b,a[c]);return a},a.range=angular.isDefined(b.ratingStates)?this.createRateObjects(angular.copy(a.$parent.$eval(b.ratingStates))):this.createRateObjects(new Array(this.maxRange)),a.rate=function(b){a.readonly||a.value===b||(a.value=b)},a.enter=function(b){a.readonly||(a.val=b),a.onHover({value:b})},a.reset=function(){a.val=angular.copy(a.value),a.onLeave()},a.$watch("value",function(b){a.val=b}),a.readonly=!1,b.readonly&&a.$parent.$watch(c(b.readonly),function(b){a.readonly=!!b})}]).directive("rating",function(){return{restrict:"EA",scope:{value:"=",onHover:"&",onLeave:"&"},controller:"RatingController",templateUrl:"template/rating/rating.html",replace:!0}}),angular.module("ui.bootstrap.tabs",[]).controller("TabsetController",["$scope",function(a){var b=this,c=b.tabs=a.tabs=[]; +b.select=function(a){angular.forEach(c,function(a){a.active=!1}),a.active=!0},b.addTab=function(a){c.push(a),(1===c.length||a.active)&&b.select(a)},b.removeTab=function(a){var d=c.indexOf(a);if(a.active&&c.length>1){var e=d==c.length-1?d-1:d+1;b.select(c[e])}c.splice(d,1)}}]).directive("tabset",function(){return{restrict:"EA",transclude:!0,replace:!0,scope:{},controller:"TabsetController",templateUrl:"template/tabs/tabset.html",link:function(a,b,c){a.vertical=angular.isDefined(c.vertical)?a.$parent.$eval(c.vertical):!1,a.justified=angular.isDefined(c.justified)?a.$parent.$eval(c.justified):!1,a.type=angular.isDefined(c.type)?a.$parent.$eval(c.type):"tabs"}}}).directive("tab",["$parse",function(a){return{require:"^tabset",restrict:"EA",replace:!0,templateUrl:"template/tabs/tab.html",transclude:!0,scope:{heading:"@",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},compile:function(b,c,d){return function(b,c,e,f){var g,h;e.active?(g=a(e.active),h=g.assign,b.$parent.$watch(g,function(a,c){a!==c&&(b.active=!!a)}),b.active=g(b.$parent)):h=g=angular.noop,b.$watch("active",function(a){h(b.$parent,a),a?(f.select(b),b.onSelect()):b.onDeselect()}),b.disabled=!1,e.disabled&&b.$parent.$watch(a(e.disabled),function(a){b.disabled=!!a}),b.select=function(){b.disabled||(b.active=!0)},f.addTab(b),b.$on("$destroy",function(){f.removeTab(b)}),b.$transcludeFn=d}}}}]).directive("tabHeadingTransclude",[function(){return{restrict:"A",require:"^tab",link:function(a,b){a.$watch("headingElement",function(a){a&&(b.html(""),b.append(a))})}}}]).directive("tabContentTransclude",function(){function a(a){return a.tagName&&(a.hasAttribute("tab-heading")||a.hasAttribute("data-tab-heading")||"tab-heading"===a.tagName.toLowerCase()||"data-tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:"^tabset",link:function(b,c,d){var e=b.$eval(d.tabContentTransclude);e.$transcludeFn(e.$parent,function(b){angular.forEach(b,function(b){a(b)?e.headingElement=b:c.append(b)})})}}}),angular.module("ui.bootstrap.timepicker",[]).constant("timepickerConfig",{hourStep:1,minuteStep:1,showMeridian:!0,meridians:null,readonlyInput:!1,mousewheel:!0}).directive("timepicker",["$parse","$log","timepickerConfig","$locale",function(a,b,c,d){return{restrict:"EA",require:"?^ngModel",replace:!0,scope:{},templateUrl:"template/timepicker/timepicker.html",link:function(e,f,g,h){function i(){var a=parseInt(e.hours,10),b=e.showMeridian?a>0&&13>a:a>=0&&24>a;return b?(e.showMeridian&&(12===a&&(a=0),e.meridian===q[1]&&(a+=12)),a):void 0}function j(){var a=parseInt(e.minutes,10);return a>=0&&60>a?a:void 0}function k(a){return angular.isDefined(a)&&a.toString().length<2?"0"+a:a}function l(a){m(),h.$setViewValue(new Date(p)),n(a)}function m(){h.$setValidity("time",!0),e.invalidHours=!1,e.invalidMinutes=!1}function n(a){var b=p.getHours(),c=p.getMinutes();e.showMeridian&&(b=0===b||12===b?12:b%12),e.hours="h"===a?b:k(b),e.minutes="m"===a?c:k(c),e.meridian=p.getHours()<12?q[0]:q[1]}function o(a){var b=new Date(p.getTime()+6e4*a);p.setHours(b.getHours(),b.getMinutes()),l()}if(h){var p=new Date,q=angular.isDefined(g.meridians)?e.$parent.$eval(g.meridians):c.meridians||d.DATETIME_FORMATS.AMPMS,r=c.hourStep;g.hourStep&&e.$parent.$watch(a(g.hourStep),function(a){r=parseInt(a,10)});var s=c.minuteStep;g.minuteStep&&e.$parent.$watch(a(g.minuteStep),function(a){s=parseInt(a,10)}),e.showMeridian=c.showMeridian,g.showMeridian&&e.$parent.$watch(a(g.showMeridian),function(a){if(e.showMeridian=!!a,h.$error.time){var b=i(),c=j();angular.isDefined(b)&&angular.isDefined(c)&&(p.setHours(b),l())}else n()});var t=f.find("input"),u=t.eq(0),v=t.eq(1),w=angular.isDefined(g.mousewheel)?e.$eval(g.mousewheel):c.mousewheel;if(w){var x=function(a){a.originalEvent&&(a=a.originalEvent);var b=a.wheelDelta?a.wheelDelta:-a.deltaY;return a.detail||b>0};u.bind("mousewheel wheel",function(a){e.$apply(x(a)?e.incrementHours():e.decrementHours()),a.preventDefault()}),v.bind("mousewheel wheel",function(a){e.$apply(x(a)?e.incrementMinutes():e.decrementMinutes()),a.preventDefault()})}if(e.readonlyInput=angular.isDefined(g.readonlyInput)?e.$eval(g.readonlyInput):c.readonlyInput,e.readonlyInput)e.updateHours=angular.noop,e.updateMinutes=angular.noop;else{var y=function(a,b){h.$setViewValue(null),h.$setValidity("time",!1),angular.isDefined(a)&&(e.invalidHours=a),angular.isDefined(b)&&(e.invalidMinutes=b)};e.updateHours=function(){var a=i();angular.isDefined(a)?(p.setHours(a),l("h")):y(!0)},u.bind("blur",function(){!e.validHours&&e.hours<10&&e.$apply(function(){e.hours=k(e.hours)})}),e.updateMinutes=function(){var a=j();angular.isDefined(a)?(p.setMinutes(a),l("m")):y(void 0,!0)},v.bind("blur",function(){!e.invalidMinutes&&e.minutes<10&&e.$apply(function(){e.minutes=k(e.minutes)})})}h.$render=function(){var a=h.$modelValue?new Date(h.$modelValue):null;isNaN(a)?(h.$setValidity("time",!1),b.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(a&&(p=a),m(),n())},e.incrementHours=function(){o(60*r)},e.decrementHours=function(){o(60*-r)},e.incrementMinutes=function(){o(s)},e.decrementMinutes=function(){o(-s)},e.toggleMeridian=function(){o(720*(p.getHours()<12?1:-1))}}}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.position","ui.bootstrap.bindHtml"]).factory("typeaheadParser",["$parse",function(a){var b=/^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error("Expected typeahead specification in form of '_modelValue_ (as _label_)? for _item_ in _collection_' but got '"+c+"'.");return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).directive("typeahead",["$compile","$parse","$q","$timeout","$document","$position","typeaheadParser",function(a,b,c,d,e,f,g){var h=[9,13,27,38,40];return{require:"ngModel",link:function(i,j,k,l){var m,n=i.$eval(k.typeaheadMinLength)||1,o=i.$eval(k.typeaheadWaitMs)||0,p=i.$eval(k.typeaheadEditable)!==!1,q=b(k.typeaheadLoading).assign||angular.noop,r=b(k.typeaheadOnSelect),s=k.typeaheadInputFormatter?b(k.typeaheadInputFormatter):void 0,t=k.typeaheadAppendToBody?b(k.typeaheadAppendToBody):!1,u=b(k.ngModel).assign,v=g.parse(k.typeahead),w=angular.element("
");w.attr({matches:"matches",active:"activeIdx",select:"select(activeIdx)",query:"query",position:"position"}),angular.isDefined(k.typeaheadTemplateUrl)&&w.attr("template-url",k.typeaheadTemplateUrl);var x=i.$new();i.$on("$destroy",function(){x.$destroy()});var y=function(){x.matches=[],x.activeIdx=-1},z=function(a){var b={$viewValue:a};q(i,!0),c.when(v.source(i,b)).then(function(c){if(a===l.$viewValue&&m){if(c.length>0){x.activeIdx=0,x.matches.length=0;for(var d=0;d=n?o>0?(A&&d.cancel(A),A=d(function(){z(a)},o)):z(a):(q(i,!1),y()),p?a:a?(l.$setValidity("editable",!1),void 0):(l.$setValidity("editable",!0),a)}),l.$formatters.push(function(a){var b,c,d={};return s?(d.$model=a,s(i,d)):(d[v.itemName]=a,b=v.viewMapper(i,d),d[v.itemName]=void 0,c=v.viewMapper(i,d),b!==c?b:a)}),x.select=function(a){var b,c,d={};d[v.itemName]=c=x.matches[a].model,b=v.modelMapper(i,d),u(i,b),l.$setValidity("editable",!0),r(i,{$item:c,$model:b,$label:v.viewMapper(i,d)}),y(),j[0].focus()},j.bind("keydown",function(a){0!==x.matches.length&&-1!==h.indexOf(a.which)&&(a.preventDefault(),40===a.which?(x.activeIdx=(x.activeIdx+1)%x.matches.length,x.$digest()):38===a.which?(x.activeIdx=(x.activeIdx?x.activeIdx:x.matches.length)-1,x.$digest()):13===a.which||9===a.which?x.$apply(function(){x.select(x.activeIdx)}):27===a.which&&(a.stopPropagation(),y(),x.$digest()))}),j.bind("blur",function(){m=!1});var B=function(a){j[0]!==a.target&&(y(),x.$digest())};e.bind("click",B),i.$on("$destroy",function(){e.unbind("click",B)});var C=a(w)(x);t?e.find("body").append(C):j.after(C)}}}]).directive("typeaheadPopup",function(){return{restrict:"EA",scope:{matches:"=",query:"=",active:"=",position:"=",select:"&"},replace:!0,templateUrl:"template/typeahead/typeahead-popup.html",link:function(a,b,c){a.templateUrl=c.templateUrl,a.isOpen=function(){return a.matches.length>0},a.isActive=function(b){return a.active==b},a.selectActive=function(b){a.active=b},a.selectMatch=function(b){a.select({activeIdx:b})}}}}).directive("typeaheadMatch",["$http","$templateCache","$compile","$parse",function(a,b,c,d){return{restrict:"EA",scope:{index:"=",match:"=",query:"="},link:function(e,f,g){var h=d(g.templateUrl)(e.$parent)||"template/typeahead/typeahead-match.html";a.get(h,{cache:b}).success(function(a){f.replaceWith(c(a.trim())(e))})}}}]).filter("typeaheadHighlight",function(){function a(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}return function(b,c){return c?b.replace(new RegExp(a(c),"gi"),"$&"):b}}),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(a){a.put("template/accordion/accordion-group.html",'
\n
\n

\n {{heading}}\n

\n
\n
\n
\n
\n
')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(a){a.put("template/accordion/accordion.html",'
')}]),angular.module("template/alert/alert.html",[]).run(["$templateCache",function(a){a.put("template/alert/alert.html","
\n \n
\n
\n")}]),angular.module("template/carousel/carousel.html",[]).run(["$templateCache",function(a){a.put("template/carousel/carousel.html",'\n')}]),angular.module("template/carousel/slide.html",[]).run(["$templateCache",function(a){a.put("template/carousel/slide.html","
\n")}]),angular.module("template/datepicker/datepicker.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/datepicker.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
#{{label}}
{{ getWeekNumber(row) }}\n \n
\n')}]),angular.module("template/datepicker/popup.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/popup.html","
    \n
  • \n"+'
  • \n \n \n \n \n \n \n
  • \n
\n')}]),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(a){a.put("template/modal/backdrop.html",'')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(a){a.put("template/modal/window.html",'')}]),angular.module("template/pagination/pager.html",[]).run(["$templateCache",function(a){a.put("template/pagination/pager.html",'')}]),angular.module("template/pagination/pagination.html",[]).run(["$templateCache",function(a){a.put("template/pagination/pagination.html",'')}]),angular.module("template/tooltip/tooltip-html-unsafe-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-html-unsafe-popup.html",'
\n
\n
\n
\n')}]),angular.module("template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-popup.html",'
\n
\n
\n
\n')}]),angular.module("template/popover/popover.html",[]).run(["$templateCache",function(a){a.put("template/popover/popover.html",'
\n
\n\n
\n

\n
\n
\n
\n')}]),angular.module("template/progressbar/bar.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/bar.html",'
')}]),angular.module("template/progressbar/progress.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/progress.html",'
')}]),angular.module("template/progressbar/progressbar.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/progressbar.html",'
')}]),angular.module("template/rating/rating.html",[]).run(["$templateCache",function(a){a.put("template/rating/rating.html",'\n \n')}]),angular.module("template/tabs/tab.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tab.html",'
  • \n {{heading}}\n
  • \n')}]),angular.module("template/tabs/tabset-titles.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tabset-titles.html","
      \n
    \n")}]),angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tabset.html",'\n
    \n \n
    \n
    \n
    \n
    \n
    \n')}]),angular.module("template/timepicker/timepicker.html",[]).run(["$templateCache",function(a){a.put("template/timepicker/timepicker.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
     
    \n \n :\n \n
     
    \n')}]),angular.module("template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(a){a.put("template/typeahead/typeahead-match.html",'')}]),angular.module("template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(a){a.put("template/typeahead/typeahead-popup.html","
      \n"+'
    • \n
      \n
    • \n
    ')}]); \ No newline at end of file diff --git a/frontend/app/views/create goal/comparison.html b/frontend/app/views/create goal/comparison.html index 6a37179..02402de 100644 --- a/frontend/app/views/create goal/comparison.html +++ b/frontend/app/views/create goal/comparison.html @@ -9,13 +9,13 @@ - - - - + + + - {{iteration.expression.periodOfTime}} + {{iteration.referencePeriod.unitToSubtract}}
    - \ No newline at end of file + diff --git a/frontend/app/views/create goal/date-validity-goal.html b/frontend/app/views/create goal/date-validity-goal.html index 0e533eb..fb07eb6 100644 --- a/frontend/app/views/create goal/date-validity-goal.html +++ b/frontend/app/views/create goal/date-validity-goal.html @@ -1,7 +1,8 @@
    -
    +
    {{(goalCtrl.goal.timeBox.startDate|date)}} - {{(goalCtrl.goal.timeBox.endDate|date)}}
    -
    \ No newline at end of file + diff --git a/frontend/app/views/create-badge-perso.html b/frontend/app/views/create-badge-perso.html deleted file mode 100644 index 07e75fc..0000000 --- a/frontend/app/views/create-badge-perso.html +++ /dev/null @@ -1,23 +0,0 @@ -
    - - -
    -
    - - -
    - -
    - Points à gagner - - {{badgeCreateCtrl.badge.points}} -
    - -
    - -
    -
    -
    \ No newline at end of file diff --git a/frontend/app/views/create-badge.html b/frontend/app/views/create-badge.html index 56b896f..a21b471 100644 --- a/frontend/app/views/create-badge.html +++ b/frontend/app/views/create-badge.html @@ -1,4 +1,4 @@ -
    +
    @@ -10,60 +10,12 @@

    Créer un badge

    ng-model="badgeCreateCtrl.badge.name" required>
    -
    - -