Commit 1a9bfcdb authored by Clement Brizard's avatar Clement Brizard
Browse files

Merge branch '12-gerer-les-calculs-apres-que-l-utilisateur-a-rempli-ses-parametres' into 'master'

Resolve "Gérer les calculs après que l'utilisateur a rempli ses paramètres"

Closes #12

See merge request tx-techno-num/impactometre!47
parents 42bbb543 6b6232dd
......@@ -94,6 +94,19 @@ const meetingCategoryDamage = {
JOURNEY: 'journey'
}
const possibleJourneys = [
{ distance: 50, mean: 'TRAIN_REGIONAL_ONE_PERSON_KM', numberOfPeople: 1 },
{ distance: 6, mean: 'BUS_CITY_ONE_PERSON_KM', numberOfPeople: 1 },
{ distance: 10, mean: 'CAR_ELECTRIC_ONE_KM', numberOfPeople: 4 }
]
const damageEndpoints = {
HUMAN_HEALTH: 'HUMAN_HEALTH',
ECOSYSTEM_QUALITY: 'ECOSYSTEM_QUALITY',
CLIMATE_CHANGE: 'CLIMATE_CHANGE',
RESOURCES: 'RESOURCES'
}
exports.hourToMinutes = hourToMinutes
exports.dayToHours = dayToHours
exports.minuteToSeconds = minuteToSeconds
......@@ -114,3 +127,7 @@ exports.hardwareOperatingTimePerDay = hardwareOperatingTimePerDay
exports.meetingComponents = meetingComponents
exports.transportationMeanSubCategories = transportationMeanSubCategories
exports.meetingCategoryDamage = meetingCategoryDamage
exports.possibleJourneys = possibleJourneys
exports.damageEndpoints = damageEndpoints
'use strict'
const meetingScenarios = require('../../database/meeting/meetingScenarios')
const MeetingScenario = require('../../model/classes/meeting/MeetingScenario')
/**
* Create a scenario corresponding to a meeting and add it to the database.
* @param {String} user - The user who creates the scenario.
* @param {Number} meetingDuration - The scenario duration in minutes.
* @param {Number} numberOfParticipants - The number of people who participates in the meeting.
* @param {Object} payload - A JSON object that contains three arrays that enable to create the three
* category damages (hardware, software, transport) linked to the meeting. The payload is like:
* { [array_of_all_hardware_data], [array_of_all_software_data], [array_of_all_journey data]}.
*/
function create ({ user, meetingDuration, numberOfParticipants, payload }) {
// Create new MeetingScenario
const meetingScenario = new MeetingScenario({ user, meetingDuration, numberOfParticipants, payload })
// Add it to the database
meetingScenarios.set(meetingScenario.id, meetingScenario)
}
/**
* Read a non-writable meeting scenario.
* @param {String} id - The id of the meeting scenario we want to return.
* @returns {MeetingScenario} The meeting scenario we want to return.
*/
function read (id) {
const meetingScenario = meetingScenarios.get(id)
if (meetingScenario !== undefined) {
return meetingScenario
} else {
throw new Error('Required MeetingScenario not available in database.')
}
}
/**
* Update a meeting scenario.
* @param {String} id - The id of the meeting scenario we want to update.
* @param {Object} payload - A JSON object that contains all necessary data to update a meeting scenario.
*/
function update (id, payload) {
throw new Error('Not implemented yet')
}
/**
* Delete a meeting scenario from the database.
* @param {String} id - The id of the meeting scenario we want to delete.
* @returns {Boolean} True if deletion succeeds, false if not.
*/
function remove (id) {
return meetingScenarios.delete(id)
}
exports.create = create
exports.read = read
exports.update = update
exports.remove = remove
......@@ -2,8 +2,14 @@
const Scenario = require('../shared/Scenario')
const MeetingDamage = require('./MeetingDamage')
const hardwareDatabase = require('../../../database/meeting/hardware')
const softwareDatabase = require('../../../database/meeting/software')
const transportDatabase = require('../../../database/meeting/transportationMean')
const meetingScenarios = require('../../../database/meeting/meetingScenarios')
const {
meetingCategoryDamage
meetingCategoryDamage,
bounds,
possibleJourneys
} = require('../../../constants/meeting')
class MeetingScenario extends Scenario {
......@@ -100,6 +106,217 @@ class MeetingScenario extends Scenario {
computeDamage (damagePayload) {
this.damage.computeDamage(damagePayload)
}
/**
* Update a meeting scenario.
* Enable to update all meeting scenario object attributes, several or only one.
* @param {Number} - The new meeting duration in minutes.
* @param {Object} - A JSON object that enables update all meeting scenario object attributes, several or only one.
* @see MeetingDamage
*/
update ({ meetingDuration = 0, payload = {} }) {
if (meetingDuration !== 0) {
this.meetingDuration = meetingDuration
}
if (payload !== {}) {
this.damage.update({
hardwareComponents: payload[meetingCategoryDamage.HARDWARE],
softwareComponents: payload[meetingCategoryDamage.SOFTWARE],
journeyComponents: payload[meetingCategoryDamage.JOURNEY]
})
}
}
/**
* Generate alternatives scenarios based on this MeetingScenario.
* @returns An array that contains alternative MeetingScenarios.
*/
generateAlternatives () {
const numberOfParticipants = this.numberOfParticipants
const user = this.user
const meetingDuration = this.meetingDuration
// **** Generate first alternative scenario: heavy visio
// Generate hardware components
// - 1/3 of used hardware is heavy visio system
// - 2/3 are light hardware, i.e. desktops with cameras and microphones
const heavyHardwareNumber = Math.ceil(numberOfParticipants / 3)
const lightHardwareNumber = numberOfParticipants - heavyHardwareNumber
let hardwareComponents = []
const heavyHardwareComponent = [
{ name: hardwareDatabase.DESKTOP.name },
{ name: hardwareDatabase.LOGITECH_KIT.name },
{ name: hardwareDatabase.TV.name },
{ name: hardwareDatabase.TV.name },
{ name: hardwareDatabase.METAL_STRUCTURE.name }
]
const lightHardwareComponent = [
{ name: hardwareDatabase.DESKTOP.name },
{ name: hardwareDatabase.CAMERA.name },
{ name: hardwareDatabase.MICROPHONE.name }
]
// Add the ritght number of heavy visio systems used for the meeting
for (let i = 0; i < heavyHardwareNumber; i++) {
hardwareComponents = hardwareComponents.concat(heavyHardwareComponent)
}
// Add the right number of light visio systems used for the meeting
for (let i = 0; i < lightHardwareNumber; i++) {
hardwareComponents = hardwareComponents.concat(lightHardwareComponent)
}
// Generate software component
let softwareComponents = [{ name: softwareDatabase.SKYPE.name }]
// Generate journey components
// - 1/5 of the particpants take the city bus
const journeyNumber = Math.ceil(numberOfParticipants / 5)
let journeyComponents = [{
passenger: 'Meeting passengers',
mean: transportDatabase.BUS_CITY_ONE_PERSON_KM.name,
distance: 5,
numberOfPeople: journeyNumber
}]
// Concatenate all meeting component to create the payload used to initilise the first alternative MeetingScenario
const heavyVisioPayload = {
[meetingCategoryDamage.HARDWARE]: hardwareComponents,
[meetingCategoryDamage.SOFTWARE]: softwareComponents,
[meetingCategoryDamage.JOURNEY]: journeyComponents
}
// Generate the MeetingScenario and add it the database
const heavyVisioScenario = MeetingScenario.create({ user, meetingDuration, numberOfParticipants, payload: heavyVisioPayload })
// Compute its damage
const DamagePayload = {
[meetingCategoryDamage.HARDWARE]: { meetingDuration, bound: bounds.UPPER },
[meetingCategoryDamage.SOFTWARE]: { instancesNumber: numberOfParticipants, bandwithBound: bounds.UPPER, networkBound: bounds.UPPER, meetingDuration },
[meetingCategoryDamage.JOURNEY]: {}
}
heavyVisioScenario.computeDamage(DamagePayload)
// **** Generate the secound alternative scenario: light transport
// Generate hardware components
hardwareComponents = []
const laptop = [{ name: hardwareDatabase.LAPTOP.name }]
// 2/3 of particpants have a laptop
for (let i = 0; i < Math.ceil(numberOfParticipants * (2 / 3)); i++) {
hardwareComponents = hardwareComponents.concat(laptop)
}
// Generate software components (no software compoents for this alternative scenario)
softwareComponents = [{ name: softwareDatabase.SKYPE.name }]
// Generate journey components
// Initialise meeting journey components
journeyComponents = []
/* We considered a mean journey for one person has
- 50 km by train
- 5 km by city bus
- 10 km by electric car shared with 3 other people.
If 4 people participate in the meeting
- 200 (50 * 4) km are done by train
- 20 (5 * 4) are done by city bus
- 40 (10 * 4) ra done by electric car.
By ease for users, we choose to spread these journeys over meeting particpants. Instead of
having 4 times the same journey, we consided that Participant 1 do on journey of 150 km by train,
Participant 2 do one journey of 15 km by city bus, Participant 3 do one journey of 30 km by electric car.
We considered the last participant do 3 journeys (50 km by train, 5 km by city bus, 10 km by electric car)
*/
// Initialise the journey counter needed for the loop (from 0 to possibleJouneys.lenght)
let journeyCounter = 0
// Compute the factor by wich we multiply the distance done by each participant
// For each possible journey, the total distance (done by all meeting participants)
// done is numberOfParticpants * possibleJourneys[i].distance
const computeDistanceFactor = (numberOfParticipants < possibleJourneys.length)
? numberOfParticipants
: possibleJourneys.length
for (let i = 0; i < numberOfParticipants; i++) {
const journey = [{
passenger: 'Participant ' + (i + 1),
mean: possibleJourneys[journeyCounter].mean,
distance: possibleJourneys[journeyCounter].distance * computeDistanceFactor,
numberOfPeople: possibleJourneys[journeyCounter].numberOfPeople
}]
// Add the created journey to meeting journey components
journeyComponents = journeyComponents.concat(journey)
// If the last possible journey is reached, go back to the first one
if (journeyCounter === possibleJourneys.length - 1) {
journeyCounter = 0
} else {
// If the last participant is reached, but not the last possible
// don't increase partcipant counter (i.e. i) in order to give all not reached possible
// journeys to the last participant (see explanations above the loop)
if (i === numberOfParticipants - 1 && journeyCounter !== possibleJourneys.lengt - 1) {
i--
}
journeyCounter++
}
}
// Create the payload
const lightTransportPayload = {
[meetingCategoryDamage.HARDWARE]: hardwareComponents,
[meetingCategoryDamage.SOFTWARE]: softwareComponents,
[meetingCategoryDamage.JOURNEY]: journeyComponents
}
// Generate meeting scenario
const lightTransportScenario = MeetingScenario.create({ user, meetingDuration, numberOfParticipants, payload: lightTransportPayload })
// Compute its damage
lightTransportScenario.computeDamage(DamagePayload)
}
/**
* Create a scenario corresponding to a meeting and add it to the database.
* @param {String} user - The user who creates the scenario.
* @param {Number} meetingDuration - The scenario duration in minutes.
* @param {Number} numberOfParticipants - The number of people who participates in the meeting.
* @param {Object} payload - A JSON object that contains three arrays that enable to create the three
* category damages (hardware, software, transport) linked to the meeting. The payload is like:
* { [array_of_all_hardware_data], [array_of_all_software_data], [array_of_all_journey data]}.
* @returns {MeetingScenario} The created meetingScenario.
*/
static create ({ user, meetingDuration, numberOfParticipants, payload }) {
// Create new MeetingScenario
const meetingScenario = new MeetingScenario({ user, meetingDuration, numberOfParticipants, payload })
// Add it to the database
meetingScenarios.set(meetingScenario.id, meetingScenario)
// Return the created meetingScenario
return meetingScenario
}
/**
* Read a non-writable meeting scenario.
* @param {String} id - The id of the meeting scenario we want to return.
* @returns {MeetingScenario} The meeting scenario we want to return.
*/
static read (id) {
const meetingScenario = meetingScenarios.get(id)
if (meetingScenario !== undefined) {
return meetingScenario
} else {
throw new Error('Required MeetingScenario not available in database.')
}
}
/**
* Update a meeting scenario.
* @param {String} id - The id of the meeting scenario we want to update.
* @param {Object} payload - A JSON object that contains all necessary data to update a meeting scenario.
*/
static update (id, payload) {
throw new Error('Not implemented yet')
}
/**
* Delete a meeting scenario from the database.
* @param {String} id - The id of the meeting scenario we want to delete.
* @returns {Boolean} True if deletion succeeds, false if not.
*/
static remove (id) {
return meetingScenarios.delete(id)
}
}
module.exports = MeetingScenario
'use strict'
const assert = require('assert')
const { create, read, remove } = require('../../../controllers/meeting/meetingScenario')
const { meetingCategoryDamage } = require('../../../constants/meeting')
const hardwareDatabase = require('../../../database/meeting/hardware')
const transportDatabase = require('../../../database/meeting/transportationMean')
const softwareDatabase = require('../../../database/meeting/software')
const meetingScenarios = require('../../../database/meeting/meetingScenarios')
describe('meetingScenario controller', () => {
// The user who creates the meeting
const user = 'vlegauch'
// The meeting duration in minutes
const meetingDuration = 120
// Number of participants
const numberOfParticipants = 4
// The JSON object that enables to creates components linked to the meeting
const payload = {
[meetingCategoryDamage.HARDWARE]: [
{ name: hardwareDatabase.DESKTOP.name },
{ name: hardwareDatabase.DESKTOP.name },
{ name: hardwareDatabase.DESKTOP.name },
{ name: hardwareDatabase.LAPTOP.name },
{ name: hardwareDatabase.LOGITECH_KIT.name },
{ name: hardwareDatabase.TV.name },
{ name: hardwareDatabase.TV.name },
{ name: hardwareDatabase.METAL_STRUCTURE.name }
],
[meetingCategoryDamage.SOFTWARE]: [{ name: softwareDatabase.SKYPE.name }],
[meetingCategoryDamage.JOURNEY]: [
{
passenger: 'Passenger 1',
mean: transportDatabase.CAR_ELECTRIC_ONE_KM.name,
distance: 120,
numberOfPeople: 4
},
{
passenger: 'Passenger 1',
mean: transportDatabase.BUS_LARGE_DISTANCE_ONE_PERSON_KM.name,
distance: 40,
numberOfPeople: 1
},
{
passenger: 'Passenger 2',
mean: transportDatabase.CAR_ELECTRIC_ONE_KM.name,
distance: 120,
numberOfPeople: 4
},
{
passenger: 'Passenger 2',
mean: transportDatabase.TRAIN_REGIONAL_ONE_PERSON_KM.name,
distance: 300,
numberOfPeople: 1
},
{
passenger: 'Passenger 2',
mean: transportDatabase.BIKE_ONE_PERSON_ONE_KM.name,
distance: 10,
numberOfPeople: 1
}
]
}
create({ user, meetingDuration, numberOfParticipants, payload })
describe('#create()', () => {
// Create the meeting scenario
it('shoud create a meeting scenario and add it the database', () => {
assert.deepStrictEqual(
meetingScenarios.size,
1
)
})
})
describe('#read()', () => {
it('should read a meetingScenario thanks to its id', () => {
meetingScenarios.forEach(element => {
const id = element.id
assert.deepStrictEqual(
read(id),
meetingScenarios.get(id)
)
})
})
})
describe('#remove()', () => {
it('should remove a meetingScenario thanks to its id', () => {
meetingScenarios.forEach(element => {
const id = element.id
remove(id)
assert.deepStrictEqual(
meetingScenarios.get(id),
undefined
)
})
})
})
})
......@@ -7,6 +7,7 @@ const MeetingDamage = require('../../../../model/classes/meeting/MeetingDamage')
const hardwareDatabase = require('../../../../database/meeting/hardware')
const transportDatabase = require('../../../../database/meeting/transportationMean')
const softwareDatabase = require('../../../../database/meeting/software')
const meetingScenarios = require('../../../../database/meeting/meetingScenarios')
const {
meetingCategoryDamage,
bounds
......@@ -66,6 +67,9 @@ describe('MeetingScenario class', () => {
]
}
// Create the MeetingScenario object
const meetingScenario = new MeetingScenario({ user, meetingDuration, numberOfParticipants, payload })
// Create scenario with missing components in a category
const incompletePayload = Object.assign({}, payload)
delete incompletePayload[meetingCategoryDamage.JOURNEY]
......@@ -77,9 +81,6 @@ describe('MeetingScenario class', () => {
})
})
describe('#computeDamage()', () => {
// Create the MeetingScenario object
const meetingScenario = new MeetingScenario({ user, meetingDuration, numberOfParticipants, payload })
// Create the JSON object that enables to compute meeting total damage
const damagePayload = {
[meetingCategoryDamage.HARDWARE]: { meetingDuration: 120, bound: bounds.UPPER },
......@@ -102,8 +103,9 @@ describe('MeetingScenario class', () => {
it('should compute the total damage caused by the meeting', () => {
Object.keys(meetingScenario.damage.totalDamage).forEach(category => {
assert.strictEqual(meetingScenario.damage.totalDamage[category], meetingDamage.totalDamage[category])
assert.isNotNaN(meetingScenario.damage.totalDamage)
assert.isNotNull(meetingScenario.damage.totalDamage)
assert.isNotNaN(meetingScenario.damage.totalDamage[category])
assert.isNotNull(meetingScenario.damage.totalDamage[category])
assert.notEqual(0, meetingScenario.damage.totalDamage[category])
})
})
it('should compute the damage of an incomplete scenario', () => {
......@@ -120,6 +122,53 @@ describe('MeetingScenario class', () => {
assert.strictEqual(damage[category], expected[category])
assert.isNotNaN(damage[category])
assert.isNotNull(damage[category])
assert.notEqual(0, damage[category])
})
})
})
describe('#generateAlternatives()', () => {
it('should create two alternative scenarios', () => {
const oldSize = meetingScenarios.size
meetingScenario.generateAlternatives()
assert.deepStrictEqual(
meetingScenarios.size,
oldSize + 2
)
})
})
describe('#create()', () => {
// Create the meeting scenario
it('shoud create a meeting scenario and add it the database', () => {
const oldSize = meetingScenarios.size
MeetingScenario.create({ user, meetingDuration, numberOfParticipants, payload })
assert.deepStrictEqual(
meetingScenarios.size,
oldSize + 1
)
})
})
describe('#read()', () => {
it('should read a meetingScenario thanks to its id', () => {
meetingScenarios.forEach(element => {
const id = element.id
assert.deepStrictEqual(
MeetingScenario.read(id),
meetingScenarios.get(id)
)
})
})
})
describe('#remove()', () => {
it('should remove a meetingScenario thanks to its id', () => {
meetingScenarios.forEach(element => {
const id = element.id
MeetingScenario.remove(id)
assert.deepStrictEqual(
meetingScenarios.get(id),
undefined
)
})
})
})
......
'used strict'
const assert = require('assert')
const normalise = require('../../utils/normalise')
const hardwareDatabase = require('../../database/meeting/hardware')
const softwareDatabase = require('../../database/meeting/software')
const transportDatabase = require('../../database/meeting/transportationMean')
const meetingScenarios = require('../../database/meeting/meetingScenarios')
const MeetingScenario = require('../../model/classes/meeting/MeetingScenario')
const {
meetingCategoryDamage,
bounds
} = require('../../constants/meeting')
const {
normalise,
normaliseDamages
} = require('../../utils/normalise')
describe('Normalise utilitary function', () => {
describe('#normalise()', () => {
......@@ -15,3 +27,106 @@ describe('Normalise utilitary function', () => {
})
})
})
describe('Normalise damages function', () => {
describe('#normaliseDamages()', () => {
it('should return an array with normalised values for several meeting scenarios', () => {
// All necessary data needed to compute meeting total damage
const damagePayload = {
[meetingCategoryDamage.HARDWARE]: { meetingDuration: 90, bound: bounds.UPPER },
[meetingCategoryDamage.SOFTWARE]: { instancesNumber: 5, bandwithBound: bounds.UPPER, networkBound: bounds.UPPER, meetingDuration: 120 },
[meetingCategoryDamage.JOURNEY]: {}
}
// The user who creates the meeting
const user = 'vlegauch'
// The meeting duration in minutes
const meetingDuration = 120
// Number of participants
const numberOfParticipants = 5
// The JSON object that enables to creates components linked to the meeting
const firstPayload = {
[meetingCategoryDamage.HARDWARE]: [
{ name: hardwareDatabase.DESKTOP.name },
{ name: hardwareDatabase.DESKTOP.name },
{ name: hardwareDatabase.DESKTOP.name },
{ name: hardwareDatabase.LAPTOP.name },
{ name: hardwareDatabase.LOGITECH_KIT.name },
{ name: hardwareDatabase.TV.name },
{ name: hardwareDatabase.TV.name },
{ name: hardwareDatabase.METAL_STRUCTURE.name }
],
[meetingCategoryDamage.SOFTWARE]: [{ name: softwareDatabase.SKYPE.name }],
[meetingCategoryDamage.JOURNEY]: [
{
passenger: 'Passenger 1',
mean: transportDatabase.CAR_ELECTRIC_ONE_KM.name,
distance: 120,
numberOfPeople: 4
},
{
passenger: 'Passenger 1',
mean: transportDatabase.BUS_LARGE_DISTANCE_ONE_PERSON_KM.name,
distance: 40,
numberOfPeople: 1
},
{
passenger: 'Passenger 2',
mean: transportDatabase.CAR_ELECTRIC_ONE_KM.name,
distance: 120,
numberOfPeople: 4
},
{
passenger: 'Passenger 2',
mean: transportDatabase.TRAIN_REGIONAL_ONE_PERSON_KM.name,
distance: 300,