diff --git a/package-lock.json b/package-lock.json index 4897ee0..fdf4869 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "glicocheck-api", - "version": "1.1.4", + "version": "1.1.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "glicocheck-api", - "version": "1.1.4", + "version": "1.1.7", "license": "ISC", "dependencies": { "axios": "^1.4.0", @@ -18,7 +18,6 @@ "helmet": "^7.0.0", "jsonwebtoken": "^9.0.0", "knex": "^3.1.0", - "moment": "^2.29.4", "node-cron": "^3.0.3", "nodemailer": "^6.9.15", "pg": "^8.13.0", @@ -57,24 +56,27 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.0.tgz", + "integrity": "sha512-gh7PdNombP8ftL8TinYC8Xd7WEypB8EKV4PI2h0eMzndKjPCXuo2zUiZtD2Hu+MSPt02Ty2MdS788ADl9ai1rA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -234,12 +236,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "version": "22.8.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.1.tgz", + "integrity": "sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==", "dev": true, "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.19.8" } }, "node_modules/@types/superagent": { @@ -293,9 +295,9 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1667,9 +1669,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -2777,14 +2779,6 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "engines": { - "node": "*" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2810,9 +2804,9 @@ } }, "node_modules/node-abi": { - "version": "3.68.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.68.0.tgz", - "integrity": "sha512-7vbj10trelExNjFSBm5kTvZXXa7pZyKWx9RCKIyqe6I9Ev3IzGpQoqBP3a+cOdxY+pWj6VkP28n/2wWysBHD/A==", + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", "dependencies": { "semver": "^7.3.5" }, @@ -3158,9 +3152,9 @@ } }, "node_modules/pg": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz", - "integrity": "sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==", + "version": "8.13.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz", + "integrity": "sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==", "dependencies": { "pg-connection-string": "^2.7.0", "pg-pool": "^3.7.0", diff --git a/package.json b/package.json index 567f94b..c67952c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glicocheck-api", - "version": "1.1.6", + "version": "1.1.7", "description": "API for diabetes management.", "main": "index.js", "type": "module", @@ -34,7 +34,6 @@ "helmet": "^7.0.0", "jsonwebtoken": "^9.0.0", "knex": "^3.1.0", - "moment": "^2.29.4", "node-cron": "^3.0.3", "nodemailer": "^6.9.15", "pg": "^8.13.0", diff --git a/src/controllers/diaryController.js b/src/controllers/diaryController.js index 44aead6..6102146 100644 --- a/src/controllers/diaryController.js +++ b/src/controllers/diaryController.js @@ -2,7 +2,6 @@ import logger from '../loggerUtil/logger.js'; import Messages from '../utils/messages.js'; import DiaryDAO from '../dao/DiaryDAO.js'; import UserDAO from '../dao/UserDAO.js'; -import moment from 'moment'; import DateTimeUtil from '../utils/dateTimeUtil.js'; import GlycemiaStatistics from '../utils/GlycemiaStatistics.js'; @@ -65,18 +64,10 @@ class DiaryController { logger.info('Executing DiaryController.getAll'); try { const { start, end } = req.query; - - const result = await DiaryDAO.getAll(); + const result = await DiaryDAO.getAll({ start, end }); if (result.success) { - let diary = result.diary; - - if (start && end) { - diary = diary.filter((record) => { - return this.isDateBetween(start, end, record.dateTime); - }); - } - return res.status(OK).json(diary); + return res.status(OK).json(result.diary); } else { return res.status(CLIENT_ERROR).json({ message: result.message }); } @@ -88,22 +79,6 @@ class DiaryController { } }; - /** - * Checks if a date is between two given dates, inclusive of the boundaries. - * @param {string} start - The start date in the format 'YYYY-MM-DD'. - * @param {string} end - The end date in the format 'YYYY-MM-DD'. - * @param {string} dateToCheck - The date to be checked in the format 'YYYY-MM-DD'. - * @return {boolean} True if the date is between the start and end dates, inclusive; otherwise, false. - */ - static isDateBetween(start, end, dateToCheck) { - const dateFormat = 'YYYY-MM-DD'; - const inclusivity = '[]'; - const startDate = moment(start, dateFormat); - const endDate = moment(end, dateFormat); - const date = moment(dateToCheck, dateFormat); - return date.isBetween(startDate, endDate, null, inclusivity); - } - static getById = async (req, res) => { logger.info('Executing DiaryController.getById'); try { @@ -232,23 +207,16 @@ class DiaryController { logger.info('Executing DiaryController.getByUserCode'); try { const userCode = req.params.usercode; + let { start, end, orderBy, sort } = req.query; if (!userCode) { return res.status(CLIENT_ERROR).json({ message: Messages.INCOMPLETE_DATA_PROVIDED }); } - const result = await DiaryDAO.getByUserCode(userCode); + const result = await DiaryDAO.getByUserCode(userCode, { start, end, orderBy, sort }); if (result.success) { - const { start, end } = req.query; - let diary = result.diary; - - if (start && end) { - diary = diary.filter((record) => { - return this.isDateBetween(start, end, record.dateTime); - }); - } - return res.status(OK).json(diary); + return res.status(OK).json(result.diary); } else { return res.status(NOT_FOUND).json({ message: result.message }); } @@ -267,12 +235,10 @@ class DiaryController { return res.status(CLIENT_ERROR).json({ message: Messages.INCOMPLETE_DATA_PROVIDED }); } - const result = await DiaryDAO.getByUserCode(userCode); + const result = await DiaryDAO.getByUserCode(userCode, {}); if (result.success) { - const glucoseReadings = result.diary.map(entry => entry.glucose); - const statistics = this.calculateStatistics(glucoseReadings); - res.status(OK).json(statistics); + res.status(OK).json(this.calculateStatistics(result.diary)); } else { res.status(NOT_FOUND).json({ message: result.message }); } @@ -282,10 +248,12 @@ class DiaryController { } }; - static calculateStatistics(readings) { - const date_start = new Date().toLocaleDateString(); - const date_end = new Date().toLocaleDateString(); - const stats = new GlycemiaStatistics(readings).getAllStatistics(); + static calculateStatistics(readings) { + const date_start = new Date(Math.min(...readings.map(item => new Date(item.dateTime)))); + const date_end = new Date(Math.max(...readings.map(item => new Date(item.dateTime)))); + + const glucoseReadings = readings.map(entry => entry.glucose); + const stats = new GlycemiaStatistics(glucoseReadings).getAllStatistics(); return { date_start, date_end, diff --git a/src/dao/DiaryDAO.js b/src/dao/DiaryDAO.js index 56188cd..874d5eb 100644 --- a/src/dao/DiaryDAO.js +++ b/src/dao/DiaryDAO.js @@ -1,6 +1,9 @@ +/* eslint-disable no-undef */ import database from '../db/dbconfig.js'; import logger from '../loggerUtil/logger.js'; import Messages from '../utils/messages.js'; +import dotenv from 'dotenv'; +dotenv.config(); const TABLE_NAME = 'blood_sugar_diary'; @@ -15,16 +18,30 @@ export default class DiaryDAO { return { success: false, message: Messages.NOTHING_FOUND }; } } catch (error) { - logger.error('Error DiaryDAO.add', error); + logger.error(`Error DiaryDAO.add - Details: ${error}`); throw new Error(Messages.ERROR); } } - static async getAll() { + static async getAll({ start, end, orderBy = 'dateTime', sort = 'asc' }) { try { - const diary = await database(TABLE_NAME) - .select('*') - .orderBy('datetime', 'asc'); + let query = database(TABLE_NAME).select('*'); + + if (start && end) { + const environment = process.env.ENVIRONMENT || 'dev'; + + if(environment === 'prod') { + // PostgreSQL + query = query.whereBetween(database.raw('to_timestamp(bsd.dateTime, \'YYYY-MM-DD\')'), [start, end]); + } else { + // SQLite + query = query.whereBetween(database.raw('strftime(\'%Y-%m-%d\', bsd.dateTime)'), [start, end]); + } + } + + query = query.orderBy(orderBy, (sort.toLowerCase() === 'asc' ? 'asc' : 'desc')); + + const diary = await query; if (diary.length > 0) { return { success: true, diary }; @@ -32,7 +49,7 @@ export default class DiaryDAO { return { success: false, message: Messages.NOTHING_FOUND }; } } catch (error) { - logger.error('Error DiaryDAO.getAll', error); + logger.error(`Error DiaryDAO.getAll - Details: ${error}`); throw new Error(Messages.ERROR); } } @@ -47,16 +64,16 @@ export default class DiaryDAO { return { success: false, message: Messages.NOTHING_FOUND }; } } catch (error) { - logger.error('Error DiaryDAO.getById', error); + logger.error(`Error DiaryDAO.getById - Details: ${error}`); throw new Error(Messages.ERROR); } } - static async getByUserCode(userCode) { + static async getByUserCode(userCode, { start, end, orderBy = 'dateTime', sort = 'desc' }) { try { - const result = await database(TABLE_NAME + ' as bsd') - .join('users', 'users.id', 'bsd.id_user') - .where('users.cod_user', userCode) + let query = database(TABLE_NAME + ' as bsd') + .join('users', 'users.id', 'bsd.id_user') + .where('users.cod_user', userCode) .select( 'bsd.id', 'users.cod_user', @@ -66,7 +83,23 @@ export default class DiaryDAO { 'bsd.id_markermeal', 'bsd.created_at', 'bsd.updated_at' - ).orderBy('dateTime', 'asc'); + ); + + if (start && end) { + const environment = process.env.ENVIRONMENT || 'dev'; + + if(environment === 'prod') { + // PostgreSQL + query = query.whereBetween(database.raw('to_timestamp(bsd.dateTime, \'YYYY-MM-DD\')'), [start, end]); + } else { + // SQLite + query = query.whereBetween(database.raw('strftime(\'%Y-%m-%d\', bsd.dateTime)'), [start, end]); + } + } + + query = query.orderBy(orderBy, (sort.toLowerCase() === 'desc' ? 'desc' : 'asc')); + + const result = await query; if (result.length > 0) { return { success: true, diary: result }; @@ -74,8 +107,8 @@ export default class DiaryDAO { return { success: false, message: Messages.NOTHING_FOUND }; } } catch (error) { - logger.error('Error DiaryDAO.getByUserCode', error); - throw new Error(Messages.ERROR); + logger.error(`Error DiaryDAO.getByUserCode - Details: ${error}.`); + throw new Error(Messages.ERROR_CONSULTING_DIARY); } } @@ -96,14 +129,13 @@ export default class DiaryDAO { return { success: true, diary }; } } catch (error) { - logger.error('Error DiaryDAO.updateById', error); + logger.error(`Error DiaryDAO.updateById - Details: ${error}`); throw new Error(Messages.ERROR); } } static async deleteById(id, userCode) { try { - // Check the register existence. const result = await database(TABLE_NAME).where('id', id).select('id'); @@ -122,7 +154,7 @@ export default class DiaryDAO { return { success: false, message: Messages.NOTHING_FOUND }; } } catch (error) { - logger.error('Error DiaryDAO.deleteById', error); + logger.error(`Error DiaryDAO.deleteById - Details: ${error}`); throw new Error(Messages.ERROR); } } @@ -141,7 +173,7 @@ export default class DiaryDAO { return { success: false, message: Messages.NOTHING_FOUND }; } } catch (error) { - logger.error('Error DiaryDAO.deleteByUserCode', error); + logger.error(`Error DiaryDAO.deleteByUserCode - Details: ${error}`); throw new Error(Messages.ERROR); } } diff --git a/src/utils/messages.js b/src/utils/messages.js index 1ed6867..f5e549b 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -37,6 +37,7 @@ export default class Messages { static DIARY_DATA_DELETED = 'Blood sugar diary register deleted.'; static ERROR_UPDATING_DIARY = 'Error updating blood sugar diary.'; static ERROR_DELETING_DIARY = 'Error deleting diary registers.'; + static ERROR_CONSULTING_DIARY = 'Error consulting diary registers.'; // Gender static NEW_GENDER_CREATED = 'New gender created.';