From 0a95129e98bd159704da2efbfe796cad6ab0ea02 Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Sun, 23 Jun 2019 11:05:27 +0200 Subject: [PATCH 1/3] do not report unknown member multiple times; read dlock timestamps in correct timezone --- controllers/doorLock.js | 8 +++++++- services/doorLock/doorLock.js | 22 +++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/controllers/doorLock.js b/controllers/doorLock.js index 6e9626f..c34248a 100644 --- a/controllers/doorLock.js +++ b/controllers/doorLock.js @@ -27,7 +27,13 @@ const uploadDoorLockData = (req, res) => { parserResults.forEach((parserResult) => { parsedData.push(...parserResult.parsedData); parserErrors.push(...parserResult.errors); - unknownMembers.push(...parserResult.unknownMembers); + + parserResult.unknownMembers.forEach((newUnknownMember) => { + // Check if member is already labeled as unknown in different file + if (!unknownMembers.find((unknownMember) => unknownMember.details === newUnknownMember.details)){ + unknownMembers.push(newUnknownMember); + } + }); }); const asyncWriteJobs = []; diff --git a/services/doorLock/doorLock.js b/services/doorLock/doorLock.js index c59c6e7..48b87c2 100644 --- a/services/doorLock/doorLock.js +++ b/services/doorLock/doorLock.js @@ -3,10 +3,11 @@ const db = require('../../models'); const fs = require('fs'); const csv = require('csv-parser'); -const moment = require('moment/moment'); +const moment = require('moment-timezone'); const Op = require('sequelize').Op; const { + UI_TIMEZONE, USER_ENTRY_EVENT, ENABLE_PASSAGE_MODE, DISABLE_PASSAGE_MODE, @@ -38,7 +39,7 @@ const parseDoorLockDataFile = (file) => { return new Promise ((resolve, reject) => { const results = []; const errors = []; - const unknownMembers = []; + const unknownMembersToReport = []; let isValidFile = true; const prefetchDataJobs = [getMappingsFromDatabase(), fetchAllMembers()]; @@ -48,6 +49,11 @@ const parseDoorLockDataFile = (file) => { const mappings = result[0]; const allMembers = result[1]; + const membersMap = {}; + const unknownMembersMap = {}; + + allMembers.forEach((member) => membersMap[member.name] = member); + const mappingFromFileName = extractMappingFromFileName(file.name); const mappingObject = checkIfMappingExsists(mappingFromFileName, mappings); if (!mappingObject){ @@ -104,13 +110,15 @@ const parseDoorLockDataFile = (file) => { const secondEntry = results[i+1]; if (firstEntry && (firstEntry.event === USER_ENTRY_EVENT)){ - const memberObject = allMembers.find(member => member.name === firstEntry.name); + const memberObject = membersMap[firstEntry.name]; if (!memberObject){ //Check if member is already labeled as unknown - const unknownMember = unknownMembers.find((member) => member.details === firstEntry.name); + const unknownMember = unknownMembersMap[firstEntry.name]; + if (!unknownMember){ - unknownMembers.push({ + unknownMembersMap[firstEntry.name] = firstEntry.name; + unknownMembersToReport.push({ error: csvParserErrors.UNKNOWN_MEMBER, details: firstEntry.name, file: file.name, @@ -123,7 +131,7 @@ const parseDoorLockDataFile = (file) => { doorLockEvents.USER_UNLOCKED : doorLockEvents.USER_LOCKED; const dateTimeString = `${firstEntry.date} ${firstEntry.time}`; - const timestamp = moment.utc(dateTimeString, 'MM/DD/YY HH:mm:ss A').toISOString(); + const timestamp = moment.tz(dateTimeString, 'MM/DD/YY HH:mm:ss A', UI_TIMEZONE).toISOString(); //Verify that member is registered in OfficeRnD system if (memberObject){ @@ -159,7 +167,7 @@ const parseDoorLockDataFile = (file) => { } resolve({ parsedData, - unknownMembers, + unknownMembers: unknownMembersToReport, errors }); }); From 7f0042633ce9c09f90cbf4fbe28f0e63e6f9185d Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Mon, 24 Jun 2019 09:52:34 +0200 Subject: [PATCH 2/3] refactor; save date and time in UTC correctly --- services/doorLock/doorLock.js | 2 +- services/integration/doorLockCharges.js | 166 +----------------------- services/officeRnD/bookings.js | 2 +- 3 files changed, 8 insertions(+), 162 deletions(-) diff --git a/services/doorLock/doorLock.js b/services/doorLock/doorLock.js index 48b87c2..803401e 100644 --- a/services/doorLock/doorLock.js +++ b/services/doorLock/doorLock.js @@ -131,7 +131,7 @@ const parseDoorLockDataFile = (file) => { doorLockEvents.USER_UNLOCKED : doorLockEvents.USER_LOCKED; const dateTimeString = `${firstEntry.date} ${firstEntry.time}`; - const timestamp = moment.tz(dateTimeString, 'MM/DD/YY HH:mm:ss A', UI_TIMEZONE).toISOString(); + const timestamp = moment.tz(dateTimeString, 'MM/DD/YY HH:mm:ss A', UI_TIMEZONE).tz('UTC').toISOString(); //Verify that member is registered in OfficeRnD system if (memberObject){ diff --git a/services/integration/doorLockCharges.js b/services/integration/doorLockCharges.js index 9ce9228..2a8903e 100644 --- a/services/integration/doorLockCharges.js +++ b/services/integration/doorLockCharges.js @@ -21,160 +21,6 @@ const getSortedIncidentsForMember = (memberId) => { }) }; -const createUnlockedIncident = (reservation) => { - return new Promise((resolve, reject) => { - const { reservationId, memberId, resourceId, start, end } = reservation; - - getLastIncidentForMember(memberId) - .then(incidents => { - const lastIncident = incidents && incidents[0] ? incidents[0] : undefined; - - const incident = { - reservationId, - memberId, - resourceId, - bookingStart: start, - bookingEnd: end, - incidentLevel: null, - incidentLevelPrice: null, - }; - - console.log('=> UNLOCKED INCIDENT'); - console.log('\tMember : ', memberId); - console.log('\tStart : ', start); - console.log('\tEnd : ', end); - console.log('\tMore details : '); - - /* - if (lastIncident){ - const lastIncidentLevel = lastIncident.incidentLevel; - const lastIncidentBeginningOfTheMonth = moment(lastIncident.bookingStart).startOf('month'); - const beginningOfTheMonth = moment.utc().startOf('month'); - - const timePassedFromLastIncident = Math.abs(beginningOfTheMonth.diff(lastIncidentBeginningOfTheMonth, 'months')); - - if (timePassedFromLastIncident >= 6){ - console.log('\t\t-> This is first incident for this member in last 6 months'); - incident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title; - incident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price; - } else { - console.log('\t\t-> This member had incident(s) in past 6 months !!!'); - incident.incidentLevel = lastIncidentLevel; - incident.incidentLevelPrice = unlockedIncidentLevelsPrices[lastIncidentLevel].price; - } - console.log('\t\tLast incident details : '); - console.log('\t\tStart : ', lastIncident.bookingStart); - console.log('\t\tCalculated diff : ', timePassedFromLastIncident); - console.log('\t\t------------------'); - console.log('\tNew incident level : ', incident.incidentLevel); - } else { - console.log('\t\tThis is first incident for this member, EVER !'); - incident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title; - incident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price; - } - */ - - db.unlockedIncident.findOrCreate({ - where: { - reservationId, - memberId, - resourceId, - bookingStart: start, - bookingEnd: end, - }, - defaults: { - ...incident - } - }) - .then(()=>resolve()) - .catch((error)=>reject(error)); - }) - .catch((error) => { - reject(error); - }); - }); -}; - -const createUnscheduledUseIncident = (reservation, doorLockEntry) => { - return new Promise((resolve, reject) => { - const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION) || 5; - const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE) || 5; - - const reservationEndTime = moment(reservation.end); - const lockedTime = moment(doorLockEntry.timestamp); - const timeDifference = Math.abs(reservationEndTime.diff(lockedTime, 'minutes')); - - const timeIntervalsToCharge = Math.floor(timeDifference / timeResolution); - const totalChargeFee = timeIntervalsToCharge * chargePrice; - - if (timeIntervalsToCharge > 0){ - const incident = { - reservationId: reservation.reservationId, - memberId: reservation.memberId, - resourceId: reservation.resourceId, - bookingStart: reservation.start, - bookingEnd: reservation.end, - doorLockEventTimestamp: doorLockEntry.timestamp, - doorLockEventType: doorLockEntry.event, - chargePrice, - timeIntervalsToCharge, - totalChargeFee, - }; - - db.unscheduledIncident.findOrCreate({where: {...incident}, defaults: {...incident}}) - .then(()=>resolve()) - .catch((error)=>reject(error)); - }else{ - resolve(); - } - }); -}; - -const createDoorLockIncident = (reservation, doorLockEntry) => { - return new Promise((resolve, reject) => { - if (!doorLockEntry){ - // Check if there is unlock entry for this reservation - getUnlockEntryForReservation(reservation) - .then((unlockEntry) => { - if (!unlockEntry){ - // check if there is back-to-back booking before current one - getFirstPreviousBooking(reservation) - .then((previousReservation) => { - if (previousReservation){ - const previousReservationEnd = moment(previousReservation.end); - const currentReservationStart = moment(reservation.start); - const timeDifference = Math.abs(currentReservationStart.diff(previousReservationEnd, 'minutes')); - - const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0; - if (timeDifference <= maxBackToBackDifference) { - createUnlockedIncident(reservation) - .then(() => resolve()) - .catch((error) => reject(error)); - }else{ - resolve(); - } - }else{ - resolve(); - } - }) - .catch((error)=>reject(error)); - }else { - createUnlockedIncident(reservation) - .then(()=>resolve()) - .catch((error)=>reject(error)); - } - }) - .catch((error) => { - reject(error); - }); - }else{ - createUnscheduledUseIncident(reservation, doorLockEntry) - .then(()=>resolve()) - .catch((error) => reject(error)); - } - }); -}; - const insertUnscheduledIncidents = (incidents) => { const asyncJobs = []; incidents.forEach((incident) => { @@ -333,8 +179,8 @@ const getIncidentData = (reservation) => { if (nextReservation){ // Check if next reservations is immediately after (back to back reservation) // If yes, then there is no need to check door lock entries related to this booking - const firstReservationEnd = moment(reservation.end); - const secondReservationStart = moment(nextReservation.start); + const firstReservationEnd = moment.utc(reservation.end); + const secondReservationStart = moment.utc(nextReservation.start); const timeDifference = Math.abs(secondReservationStart.diff(firstReservationEnd, 'minutes')); const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0; @@ -355,8 +201,8 @@ const getIncidentData = (reservation) => { const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION) || 5 const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE) || 5; - const reservationEndTime = moment(reservation.end); - const lockedTime = moment(lockEntry.timestamp); + const reservationEndTime = moment.utc(reservation.end); + const lockedTime = moment.utc(lockEntry.timestamp); const timeDifference = Math.abs(reservationEndTime.diff(lockedTime, 'minutes')); const timeIntervalsToCharge = Math.floor(timeDifference / timeResolution); @@ -391,8 +237,8 @@ const getIncidentData = (reservation) => { getFirstPreviousBooking(reservation) .then((previousReservation) => { if (previousReservation){ - const previousReservationEnd = moment(previousReservation.end); - const currentReservationStart = moment(reservation.start); + const previousReservationEnd = moment.utc(previousReservation.end); + const currentReservationStart = moment.utc(reservation.start); const timeDifference = Math.abs(currentReservationStart.diff(previousReservationEnd, 'minutes')); const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0; diff --git a/services/officeRnD/bookings.js b/services/officeRnD/bookings.js index e63d3d3..c727b49 100644 --- a/services/officeRnD/bookings.js +++ b/services/officeRnD/bookings.js @@ -41,7 +41,7 @@ const getAllFinishedBookings = () => { const filters = { canceled: false, end: { - [Op.lt]: moment().toISOString() + [Op.lt]: moment.tz('UTC') } }; From e03f634aff18984260aaab1d16208209be875a7f Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Mon, 24 Jun 2019 22:33:10 +0200 Subject: [PATCH 3/3] fix timezone conversion when fetching booking reservations --- services/officeRnD/bookings.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/officeRnD/bookings.js b/services/officeRnD/bookings.js index c727b49..db98a88 100644 --- a/services/officeRnD/bookings.js +++ b/services/officeRnD/bookings.js @@ -41,7 +41,7 @@ const getAllFinishedBookings = () => { const filters = { canceled: false, end: { - [Op.lt]: moment.tz('UTC') + [Op.lt]: moment.utc().toISOString() } }; @@ -56,8 +56,8 @@ const getAllFinishedBookings = () => { const getFirstNextBooking = (reservation) => { return new Promise ((resolve, reject) => { - const {resourceId, start, timezone} = reservation; - const endOfTheDay = moment.tz(start, timezone).endOf('Day').toISOString(); + const { resourceId, start } = reservation; + const endOfTheDay = moment.utc(start).endOf('Day').toISOString(); const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone']; const filters = { @@ -90,8 +90,8 @@ const getFirstNextBooking = (reservation) => { const getFirstPreviousBooking = (reservation) => { return new Promise ((resolve, reject) => { - const {resourceId, start, timezone} = reservation; - const startOfTheDay = moment.tz(start, timezone).startOf('Day').toISOString(); + const { resourceId, start } = reservation; + const startOfTheDay = moment.utc(start).startOf('Day').toISOString(); const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone']; const filters = {