diff --git a/src/controllers/CommunityPortal/NoshowVizController.js b/src/controllers/CommunityPortal/NoshowVizController.js index ba3e8f1eb..1e0b3b088 100644 --- a/src/controllers/CommunityPortal/NoshowVizController.js +++ b/src/controllers/CommunityPortal/NoshowVizController.js @@ -1,21 +1,20 @@ const { participants, events, attendance } = require('./AttendanceMockData'); const noShowVizController = function () { - // Async function to get no-shows data const getNoShowsData = async (req, res) => { - const { period } = req.query; // Get period from request query + const { period } = req.query; // Get period from request query try { const groupedData = {}; - const eventMap = Object.fromEntries(events.map(event => [event.eventID, event.eventType])); - const allEventTypes = [...new Set(events.map(event => event.eventType))]; + const eventMap = Object.fromEntries(events.map((event) => [event.eventID, event.eventType])); + const allEventTypes = [...new Set(events.map((event) => event.eventType))]; - attendance.forEach(record => { + attendance.forEach((record) => { const eventType = eventMap[record.eventID]; - const eventDate = events.find(event => event.eventID === record.eventID)?.date; + const eventDate = events.find((event) => event.eventID === record.eventID)?.date; const parsedDate = new Date(eventDate); - + // Format date using JavaScript's native Date functions const formattedDate = period === 'year' @@ -43,10 +42,10 @@ const noShowVizController = function () { const dateB = period === 'year' ? new Date(b) : new Date(Date.parse(b)); return dateA - dateB; }) - .map(date => { + .map((date) => { const entry = { date }; - allEventTypes.forEach(event => { + allEventTypes.forEach((event) => { entry[event] = groupedData[date][event] || { attended: 0, notAttended: 0 }; }); @@ -54,7 +53,6 @@ const noShowVizController = function () { }); res.status(200).json(result); // Send the result as a JSON response - } catch (error) { console.error('Error fetching no-shows data:', error); res.status(500).json({ message: 'Error fetching no-shows data', error }); @@ -65,11 +63,11 @@ const noShowVizController = function () { const getNoShowsByLocation = async (req, res) => { try { const groupedData = {}; - const eventTypes = [...new Set(events.map(event => event.eventType))]; + const eventTypes = [...new Set(events.map((event) => event.eventType))]; - attendance.forEach(record => { - const participant = participants.find(p => p.participantID === record.participantID); - const event = events.find(e => e.eventID === record.eventID); + attendance.forEach((record) => { + const participant = participants.find((p) => p.participantID === record.participantID); + const event = events.find((e) => e.eventID === record.eventID); if (participant && event) { const { location } = event; @@ -89,21 +87,20 @@ const noShowVizController = function () { } }); - Object.keys(groupedData).forEach(location => { - eventTypes.forEach(eventType => { + Object.keys(groupedData).forEach((location) => { + eventTypes.forEach((eventType) => { if (!groupedData[location][eventType]) { groupedData[location][eventType] = 0; } }); }); - const result = Object.keys(groupedData).map(location => ({ + const result = Object.keys(groupedData).map((location) => ({ location, ...groupedData[location], })); res.status(200).json(result); // Send the result as a JSON response - } catch (error) { console.error('Error fetching no-shows by location:', error); res.status(500).json({ message: 'Error fetching no-shows by location', error }); @@ -125,10 +122,10 @@ const noShowVizController = function () { const groupedData = {}; const genderTypes = new Set(); - attendance.forEach(item => { - const participant = participants.find(p => p.participantID === item.participantID); + attendance.forEach((item) => { + const participant = participants.find((p) => p.participantID === item.participantID); if (participant && !item.attended) { - const ageGroup = Object.keys(ageGroups).find(group => { + const ageGroup = Object.keys(ageGroups).find((group) => { const [min, max] = ageGroups[group]; return participant.age >= min && participant.age <= max; }); @@ -147,11 +144,11 @@ const noShowVizController = function () { } }); - const resultData = Object.keys(ageGroups).map(ageGroup => { + const resultData = Object.keys(ageGroups).map((ageGroup) => { const groupData = groupedData[ageGroup] || {}; const result = { ageGroup }; - [...genderTypes].forEach(gender => { + [...genderTypes].forEach((gender) => { result[gender] = groupData[gender] || 0; }); @@ -159,7 +156,6 @@ const noShowVizController = function () { }); res.status(200).json({ ageGroupData: resultData, genderTypes: Array.from(genderTypes) }); - } catch (error) { console.error('Error fetching no-shows by age group:', error); res.status(500).json({ message: 'Error fetching no-shows by age group', error }); @@ -167,81 +163,82 @@ const noShowVizController = function () { }; // Async function to get no-show proportions -const getNoShowProportions = async (req, res) => { - try { - const genderCounts = {}; + const getNoShowProportions = async (req, res) => { + try { + const genderCounts = {}; - attendance.forEach(item => { - const participant = participants.find(p => p.participantID === item.participantID); + attendance.forEach((item) => { + const participant = participants.find((p) => p.participantID === item.participantID); - if (participant && !item.attended) { - genderCounts[participant.gender] = (genderCounts[participant.gender] || 0) + 1; - } - }); + if (participant && !item.attended) { + genderCounts[participant.gender] = (genderCounts[participant.gender] || 0) + 1; + } + }); - const result = Object.keys(genderCounts).map(gender => ({ - name: gender, - value: genderCounts[gender], - })); + const result = Object.keys(genderCounts).map((gender) => ({ + name: gender, + value: genderCounts[gender], + })); - res.status(200).json(result); // Send the result as a JSON response + res.status(200).json(result); // Send the result as a JSON response + } catch (error) { + console.error('Error fetching no-show proportions:', error); + res.status(500).json({ message: 'Error fetching no-show proportions', error }); + } + }; - } catch (error) { - console.error('Error fetching no-show proportions:', error); - res.status(500).json({ message: 'Error fetching no-show proportions', error }); - } -}; + // Async function to get unique event types + const getUniqueEventTypes = async (req, res) => { + try { + const eventTypes = new Set(events.map((event) => event.eventType)); + const result = [...Array.from(eventTypes)]; -// Async function to get unique event types -const getUniqueEventTypes = async (req, res) => { - try { - const eventTypes = new Set(events.map(event => event.eventType)); - const result = [...Array.from(eventTypes)]; + res.status(200).json(result); // Send the result as a JSON response + } catch (error) { + console.error('Error fetching unique event types:', error); + res.status(500).json({ message: 'Error fetching unique event types', error }); + } + }; - res.status(200).json(result); // Send the result as a JSON response + // Async function to get attendance by day, based on event type + const getAttendanceByDay = async (req, res) => { + const { selectedEventType } = req.query; // Get selected event type from request query - } catch (error) { - console.error('Error fetching unique event types:', error); - res.status(500).json({ message: 'Error fetching unique event types', error }); - } -}; + try { + const groupedData = { + Sunday: 0, + Monday: 0, + Tuesday: 0, + Wednesday: 0, + Thursday: 0, + Friday: 0, + Saturday: 0, + }; -// Async function to get attendance by day, based on event type -const getAttendanceByDay = async (req, res) => { - const { selectedEventType } = req.query; // Get selected event type from request query - - try { - const groupedData = { - Sunday: 0, - Monday: 0, - Tuesday: 0, - Wednesday: 0, - Thursday: 0, - Friday: 0, - Saturday: 0, - }; - - attendance.forEach(item => { - const event = events.find(e => e.eventID === item.eventID); - - if (event && item.attended && (selectedEventType === 'All' || event.eventType === selectedEventType)) { - const day = new Date(event.date).toLocaleString('en-US', { weekday: 'long' }); - groupedData[day] += 1; - } - }); - - const result = Object.keys(groupedData).map(day => ({ - day, - attended: groupedData[day], - })); - - res.status(200).json(result); // Send the result as a JSON response - - } catch (error) { - console.error('Error fetching attendance by day:', error); - res.status(500).json({ message: 'Error fetching attendance by day', error }); - } -}; + attendance.forEach((item) => { + const event = events.find((e) => e.eventID === item.eventID); + + if ( + event && + item.attended && + (selectedEventType === 'All' || event.eventType === selectedEventType) + ) { + const day = new Date(event.date).toLocaleString('en-US', { weekday: 'long' }); + groupedData[day] += 1; + } + }); + + const result = Object.keys(groupedData).map((day) => ({ + day, + attended: groupedData[day], + })); + + res.status(200).json(result); // Send the result as a JSON response + } catch (error) { + console.error('Error fetching attendance by day:', error); + res.status(500).json({ message: 'Error fetching attendance by day', error }); + } + }; return { getNoShowsData, @@ -253,5 +250,4 @@ const getAttendanceByDay = async (req, res) => { }; }; - module.exports = noShowVizController; diff --git a/src/controllers/analyticsPopularPRsController.js b/src/controllers/analyticsPopularPRsController.js index 86f816d19..359853724 100644 --- a/src/controllers/analyticsPopularPRsController.js +++ b/src/controllers/analyticsPopularPRsController.js @@ -1,9 +1,7 @@ const NodeCache = require('node-cache'); - const PullRequest = require('../models/pullRequest'); const PullRequestReview = require('../models/pullRequestReview'); const PullRequestSyncMetadata = require('../models/pullRequestSyncMetadata'); - const { parseDurationValue, isStaleData, diff --git a/src/controllers/badgeController.js b/src/controllers/badgeController.js index 48269aac5..12077a7bc 100644 --- a/src/controllers/badgeController.js +++ b/src/controllers/badgeController.js @@ -92,6 +92,109 @@ const badgeController = function (Badge) { return; } + let userIds; + let badgeCollection; + + if (req.params.userId) { + // Single user update case + userIds = [req.params.userId]; + badgeCollection = req.body.badgeCollection; + } else { + // Multi-user assign case + userIds = req.body.userIds; + console.log('userIDs:', userIds); + badgeCollection = req.body.selectedBadges.map((badgeId) => ({ + badge: badgeId.replace('assign-badge-', ''), + count: 1, + lastModified: Date.now(), + earnedDate: [new Date().toISOString()], + })); + console.log('badgeCollections', badgeCollection); + } + + if ( + !Array.isArray(userIds) || + userIds.length === 0 || + !Array.isArray(badgeCollection) || + badgeCollection.length === 0 + ) { + res + .status(400) + .send('Invalid input. Both userIds and badgeCollection must be non-empty arrays.'); + return; + } + + try { + const results = await Promise.all( + userIds.map(async (userId) => { + const userToBeAssigned = mongoose.Types.ObjectId(userId); + const record = await UserProfile.findById(userToBeAssigned); + + if (!record) { + return { userId, error: 'User not found' }; + } + + let totalNewBadges = 0; + const existingBadges = {}; + if (record.badgeCollection && Array.isArray(record.badgeCollection)) { + record.badgeCollection.forEach((badgeItem) => { + existingBadges[badgeItem.badge.toString()] = badgeItem; + }); + } + + // Merge existing badges with new ones + badgeCollection.forEach((badge) => { + const existingBadge = existingBadges[badge.badge.toString()]; + if (existingBadge) { + // Update the existing badge + existingBadge.count += badge.count; + existingBadge.lastModified = Date.now(); + existingBadge.earnedDate = [ + ...existingBadge.earnedDate, + ...(badge.earnedDate || [new Date().toISOString()]), + ]; + } else { + // Add the new badge + existingBadges[badge.badge.toString()] = { + badge: mongoose.Types.ObjectId(badge.badge), + count: badge.count, + lastModified: Date.now(), + earnedDate: badge.earnedDate || [new Date().toISOString()], + }; + totalNewBadges += badge.count; + } + }); + + // Convert the merged badges back to an array + record.badgeCollection = Object.values(existingBadges); + record.badgeCount += totalNewBadges; + + if (cache.hasCache(`user-${userToBeAssigned}`)) { + cache.removeCache(`user-${userToBeAssigned}`); + } + + await record.save(); + return { userId, success: true }; + }), + ); + + const errors = results.filter((result) => result.error); + if (errors.length > 0) { + res.status(207).send({ message: 'Some users were not assigned badges', errors }); + } else { + res.status(200).send({ message: 'Badges assigned successfully to all users' }); + } + } catch (err) { + res.status(500).send(`Internal Error: Badge Collection. ${err.message}`); + } + }; + + const assignBadgesToSingleUser = async function (req, res) { + if (!(await helper.hasPermission(req.body.requestor, 'assignBadges'))) { + res.status(403).send('You are not authorized to assign badges.'); + return; + } + const userToBeAssigned = mongoose.Types.ObjectId(req.params.userId); try { @@ -357,6 +460,7 @@ const badgeController = function (Badge) { // awardBadgesTest, getAllBadges, assignBadges, + assignBadgesToSingleUser, postBadge, deleteBadge, putBadge, diff --git a/src/controllers/badgeController.spec.js b/src/controllers/badgeController.spec.js index b0f3ec0d6..32c58d927 100644 --- a/src/controllers/badgeController.spec.js +++ b/src/controllers/badgeController.spec.js @@ -10,9 +10,10 @@ const UserProfile = require('../models/userProfile'); const badgeController = require('./badgeController'); const makeSut = () => { - const { postBadge, getAllBadges, assignBadges, deleteBadge } = badgeController(Badge); + const { postBadge, getAllBadges, assignBadges, assignBadgesToSingleUser, deleteBadge } = + badgeController(Badge); - return { postBadge, getAllBadges, assignBadges, deleteBadge }; + return { postBadge, getAllBadges, assignBadges, assignBadgesToSingleUser, deleteBadge }; }; const flushPromises = () => new Promise(setImmediate); @@ -345,18 +346,18 @@ describe('badeController module', () => { describe('assignBadges method', () => { test('Returns 403 if the user is not authorized', async () => { - const { assignBadges } = makeSut(); + const { assignBadgesToSingleUser } = makeSut(); const hasPermissionSpy = mockHasPermission(false); - const response = await assignBadges(mockReq, mockRes); + const response = await assignBadgesToSingleUser(mockReq, mockRes); expect(hasPermissionSpy).toHaveBeenCalledWith(mockReq.body.requestor, 'assignBadges'); assertResMock(403, 'You are not authorized to assign badges.', response, mockRes); }); test('Returns 500 if an error occurs in `findById`', async () => { - const { assignBadges } = makeSut(); + const { assignBadgesToSingleUser } = makeSut(); const hasPermissionSpy = mockHasPermission(true); @@ -365,7 +366,7 @@ describe('badeController module', () => { .spyOn(UserProfile, 'findById') .mockRejectedValueOnce(new Error(errMsg)); - const response = await assignBadges(mockReq, mockRes); + const response = await assignBadgesToSingleUser(mockReq, mockRes); assertResMock(500, `Internal Error: Badge Collection. ${errMsg}`, response, mockRes); expect(findByIdSpy).toHaveBeenCalledWith(mongoose.Types.ObjectId(mockReq.params.userId)); @@ -373,13 +374,13 @@ describe('badeController module', () => { }); test('Returns 400 if user is not found', async () => { - const { assignBadges } = makeSut(); + const { assignBadgesToSingleUser } = makeSut(); const hasPermissionSpy = mockHasPermission(true); const findByIdSpy = jest.spyOn(UserProfile, 'findById').mockResolvedValue(null); - const response = await assignBadges(mockReq, mockRes); + const response = await assignBadgesToSingleUser(mockReq, mockRes); assertResMock(400, 'Can not find the user to be assigned.', response, mockRes); expect(findByIdSpy).toHaveBeenCalledWith(mongoose.Types.ObjectId(mockReq.params.userId)); @@ -389,7 +390,7 @@ describe('badeController module', () => { test('Returns 500 if an error occurs when saving edited user profile', async () => { const { mockCache: hasCacheMock } = makeMockCache('hasCache', false); - const { assignBadges } = makeSut(); + const { assignBadgesToSingleUser } = makeSut(); const hasPermissionSpy = mockHasPermission(true); const errMsg = 'Error when saving'; @@ -397,7 +398,7 @@ describe('badeController module', () => { const findByIdSpy = jest.spyOn(UserProfile, 'findById').mockResolvedValue(findObj); jest.spyOn(findObj, 'save').mockRejectedValueOnce(new Error(errMsg)); - const response = await assignBadges(mockReq, mockRes); + const response = await assignBadgesToSingleUser(mockReq, mockRes); assertResMock(500, `Internal Error: Badge Collection. ${errMsg}`, response, mockRes); expect(findByIdSpy).toHaveBeenCalledWith(mongoose.Types.ObjectId(mockReq.params.userId)); @@ -412,14 +413,14 @@ describe('badeController module', () => { const { mockCache: hasCacheMock, cacheObject } = makeMockCache('hasCache', true); const removeCacheMock = jest.spyOn(cacheObject, 'removeCache').mockReturnValueOnce(null); - const { assignBadges } = makeSut(); + const { assignBadgesToSingleUser } = makeSut(); const hasPermissionSpy = mockHasPermission(true); const findObj = { save: () => {} }; const findByIdSpy = jest.spyOn(UserProfile, 'findById').mockResolvedValue(findObj); jest.spyOn(findObj, 'save').mockResolvedValueOnce({ _id: 'randomId' }); - const response = await assignBadges(mockReq, mockRes); + const response = await assignBadgesToSingleUser(mockReq, mockRes); assertResMock(201, `randomId`, response, mockRes); expect(findByIdSpy).toHaveBeenCalledWith(mongoose.Types.ObjectId(mockReq.params.userId)); @@ -436,14 +437,14 @@ describe('badeController module', () => { test('Returns 201 and if successful and user does not exist in cache', async () => { const { mockCache: hasCacheMock } = makeMockCache('hasCache', false); - const { assignBadges } = makeSut(); + const { assignBadgesToSingleUser } = makeSut(); const hasPermissionSpy = mockHasPermission(true); const findObj = { save: () => {} }; const findByIdSpy = jest.spyOn(UserProfile, 'findById').mockResolvedValue(findObj); jest.spyOn(findObj, 'save').mockResolvedValueOnce({ _id: 'randomId' }); - const response = await assignBadges(mockReq, mockRes); + const response = await assignBadgesToSingleUser(mockReq, mockRes); assertResMock(201, `randomId`, response, mockRes); expect(findByIdSpy).toHaveBeenCalledWith(mongoose.Types.ObjectId(mockReq.params.userId)); diff --git a/src/controllers/helpCategoryController.js b/src/controllers/helpCategoryController.js index 2d2f18052..67a22974d 100644 --- a/src/controllers/helpCategoryController.js +++ b/src/controllers/helpCategoryController.js @@ -1,49 +1,48 @@ const HelpCategory = require('../models/helpCategory'); const helpCategoryController = { - // Get all help categories - getAllHelpCategories: async (req, res) => { - try { - const helpCategories = await HelpCategory.find({isActive: true}) - .sort({ order: 1 }); - res.status(200).json(helpCategories); - } catch (error) { - res.status(500).json({ message: 'Error fetching help categories', error: error.message }); - } - }, - // Create a new help category - createHelpCategory: async (req, res) => { - try { - const { name, order } = req.body; - const category = new HelpCategory({ - name, order: order || 0 - }); + // Get all help categories + getAllHelpCategories: async (req, res) => { + try { + const helpCategories = await HelpCategory.find({ isActive: true }).sort({ order: 1 }); + res.status(200).json(helpCategories); + } catch (error) { + res.status(500).json({ message: 'Error fetching help categories', error: error.message }); + } + }, + // Create a new help category + createHelpCategory: async (req, res) => { + try { + const { name, order } = req.body; + const category = new HelpCategory({ + name, + order: order || 0, + }); - const savedCategory = await category.save(); - res.status(201).json(savedCategory); - } catch (error) { - res.status(500).json({ message: 'Error creating help category', error: error.message }); - } - }, - // Update a help category - updateHelpCategory: async (req, res) => { - try { - const { id } = req.params; - const { name, order, isActive } = req.body; - const updatedCategory = await HelpCategory.findByIdAndUpdate( - id, - { name, order, isActive }, - { new: true } - ); - if (!updatedCategory) { - return res.status(404).json({ message: 'Help category not found' }); - } - res.status(200).json(updatedCategory); - } catch (error) { - res.status(500).json({ message: 'Error updating help category', error: error.message }); - } + const savedCategory = await category.save(); + res.status(201).json(savedCategory); + } catch (error) { + res.status(500).json({ message: 'Error creating help category', error: error.message }); + } + }, + // Update a help category + updateHelpCategory: async (req, res) => { + try { + const { id } = req.params; + const { name, order, isActive } = req.body; + const updatedCategory = await HelpCategory.findByIdAndUpdate( + id, + { name, order, isActive }, + { new: true }, + ); + if (!updatedCategory) { + return res.status(404).json({ message: 'Help category not found' }); + } + res.status(200).json(updatedCategory); + } catch (error) { + res.status(500).json({ message: 'Error updating help category', error: error.message }); } - + }, }; module.exports = helpCategoryController; diff --git a/src/controllers/informationController.js b/src/controllers/informationController.js index 19f6f130b..21c6464f9 100644 --- a/src/controllers/informationController.js +++ b/src/controllers/informationController.js @@ -25,11 +25,9 @@ const informationController = function (Information) { Information.find({ infoName: { $regex: escapeRegex(req.body.infoName), $options: 'i' } }) .then((result) => { if (result.length > 0) { - res - .status(400) - .send({ - error: `Info Name must be unique. Another infoName with name ${result[0].infoName} already exists. Please note that info names are case insensitive`, - }); + res.status(400).send({ + error: `Info Name must be unique. Another infoName with name ${result[0].infoName} already exists. Please note that info names are case insensitive`, + }); return; } const _info = new Information(); diff --git a/src/controllers/inventoryController.js b/src/controllers/inventoryController.js index a6a921c31..ab42c2fbe 100644 --- a/src/controllers/inventoryController.js +++ b/src/controllers/inventoryController.js @@ -43,7 +43,8 @@ const inventoryController = function (Item, ItemType, projects) { //update the project’s inventoryModifiedDatetime const updateProjectInventoryModifiedTime = function (projectId) { - return projects.findByIdAndUpdate(projectId, { inventoryModifiedDatetime: Date.now() }) + return projects + .findByIdAndUpdate(projectId, { inventoryModifiedDatetime: Date.now() }) .then((result) => { return result; }) @@ -108,14 +109,14 @@ const inventoryController = function (Item, ItemType, projects) { return inventoryItem .save() .then((results) => { - return updateProjectInventoryModifiedTime(req.params.projectId) - .then(() => { - res.status(201).send(results); - }) - .catch((err) => { - res.status(500).send(err.message); - }); - }) + return updateProjectInventoryModifiedTime(req.params.projectId) + .then(() => { + res.status(201).send(results); + }) + .catch((err) => { + res.status(500).send(err.message); + }); + }) .catch((errors) => res.status(500).send(errors)); } return Item.findOneAndUpdate( @@ -147,11 +148,11 @@ const inventoryController = function (Item, ItemType, projects) { { new: true }, ).then((result) => { updateProjectInventoryModifiedTime(req.params.projectId) - .then(() => { - res.status(201).send(result); - }) - .catch((err) => res.status(500).send(err.message)); - }); + .then(() => { + res.status(201).send(result); + }) + .catch((err) => res.status(500).send(err.message)); + }); }); } return res @@ -279,12 +280,12 @@ const inventoryController = function (Item, ItemType, projects) { { costPer: results.quantity !== 0 ? results.cost / results.quantity : 0 }, { new: true }, ).then((result) => { - updateProjectInventoryModifiedTime(req.params.projectId) - .then(() => { - res.status(201).send(result); - }) - .catch((err) => res.status(500).send(err.message)); - }); + updateProjectInventoryModifiedTime(req.params.projectId) + .then(() => { + res.status(201).send(result); + }) + .catch((err) => res.status(500).send(err.message)); + }); }); } return res.status(400).send('Valid Project, Quantity and Type Id are necessary'); diff --git a/src/controllers/inventoryController.spec.js b/src/controllers/inventoryController.spec.js index c8e79a3f3..e70f04fae 100644 --- a/src/controllers/inventoryController.spec.js +++ b/src/controllers/inventoryController.spec.js @@ -432,4 +432,4 @@ describe('inventoryController', () => { }); }); -// mockReq.body.typeId ='6515fcc71dd1dbff0999e156' \ No newline at end of file +// mockReq.body.typeId ='6515fcc71dd1dbff0999e156' diff --git a/src/controllers/projectController.spec.js b/src/controllers/projectController.spec.js index cc5c950e3..5608f3702 100644 --- a/src/controllers/projectController.spec.js +++ b/src/controllers/projectController.spec.js @@ -1,4 +1,4 @@ -test.todo("Fix project Controller test suite"); +test.todo('Fix project Controller test suite'); // const mongoose = require('mongoose'); diff --git a/src/controllers/reportsController.js b/src/controllers/reportsController.js index 473792f92..bb4ad65f7 100644 --- a/src/controllers/reportsController.js +++ b/src/controllers/reportsController.js @@ -145,6 +145,7 @@ const reportsController = function () { try { const [ volunteerNumberStats, + mentorNumberStats, volunteerHoursStats, totalHoursWorked, tasksStats, @@ -168,6 +169,12 @@ const reportsController = function () { isoComparisonStartDate, isoComparisonEndDate, ), + overviewReportHelper.getMentorNumberStats( + isoStartDate, + isoEndDate, + isoComparisonStartDate, + isoComparisonEndDate, + ), overviewReportHelper.getHoursStats( isoStartDate, isoEndDate, @@ -273,6 +280,7 @@ const reportsController = function () { res.status(200).send({ volunteerNumberStats, + mentorNumberStats, volunteerHoursStats, totalHoursWorked, tasksStats, diff --git a/src/controllers/tagController.js b/src/controllers/tagController.js index 873b246af..ffc1421df 100644 --- a/src/controllers/tagController.js +++ b/src/controllers/tagController.js @@ -11,68 +11,70 @@ const tagController = function () { const getFrequentTags = async function (req, res) { try { const { projectId, startDate, endDate, limit = 20 } = req.query; - + // Validate the limit parameter const limitNumber = Math.max(1, Math.min(100, parseInt(limit, 10))); - + // Create cache key based on query parameters const cacheKey = `frequent_tags:${projectId || 'all'}:${startDate || ''}:${endDate || ''}:${limitNumber}`; - + // Try to get data from cache first if (cache.hasCache(cacheKey)) { return res.status(200).json(cache.getCache(cacheKey)); } - + // Build query for MongoDB const query = {}; - + if (projectId) { query.projectId = projectId; } - + if (startDate || endDate) { query.createdAt = {}; - + if (startDate) { query.createdAt.$gte = new Date(startDate); } - + if (endDate) { query.createdAt.$lte = new Date(endDate); } } - + // Fetch data with aggregation pipeline const tags = await Tag.aggregate([ { $match: query }, - { $group: { - _id: "$tagName", - frequency: { $sum: "$frequency" } - } + { + $group: { + _id: '$tagName', + frequency: { $sum: '$frequency' }, + }, }, - { $project: { + { + $project: { _id: 0, - tag: "$_id", - frequency: 1 - } + tag: '$_id', + frequency: 1, + }, }, { $sort: { frequency: -1, tagName: 1 } }, - { $limit: limitNumber } + { $limit: limitNumber }, ]); - + const response = { - data: tags + data: tags, }; - + // Store in cache cache.setCache(cacheKey, response); - + return res.status(200).json(response); } catch (error) { logger.logException(error); - return res.status(500).json({ - error: 'Failed to fetch frequent tags', - details: error.message + return res.status(500).json({ + error: 'Failed to fetch frequent tags', + details: error.message, }); } }; @@ -91,7 +93,7 @@ const tagController = function () { // Create cache key const cacheKey = `tags_project:${projectId}:${pageNumber}:${limitNumber}`; - + // Check cache first if (cache.hasCache(cacheKey)) { return res.status(200).json(cache.getCache(cacheKey)); @@ -105,10 +107,10 @@ const tagController = function () { // Find tags for the specified project const query = { projectId }; - + // Get total count for pagination const totalTags = await Tag.countDocuments(query); - + // Fetch paginated results const tags = await Tag.find(query) .sort({ frequency: -1 }) @@ -126,16 +128,16 @@ const tagController = function () { hasPreviousPage: pageNumber > 1, }, }; - + // Cache the response cache.setCache(cacheKey, response); res.status(200).json(response); } catch (error) { logger.logException(error); - res.status(500).json({ - error: 'Failed to fetch tags for project', - details: error.message + res.status(500).json({ + error: 'Failed to fetch tags for project', + details: error.message, }); } }; @@ -146,43 +148,43 @@ const tagController = function () { const upsertTag = async function (req, res) { try { const { tagId, tagName, projectId } = req.body; - + if (!tagName || !projectId || !tagId) { - return res.status(400).json({ - error: 'Tag ID, tag name, and project ID are all required' + return res.status(400).json({ + error: 'Tag ID, tag name, and project ID are all required', }); } - + // Verify project exists const project = await Project.findById(projectId); if (!project) { return res.status(400).json({ error: 'Invalid project' }); } - + // Find and update or create if not exists const now = new Date(); const result = await Tag.findOneAndUpdate( { tagId, projectId }, - { + { tagName, $inc: { frequency: 1 }, - $setOnInsert: { createdAt: now } + $setOnInsert: { createdAt: now }, }, - { new: true, upsert: true } + { new: true, upsert: true }, ); - + // Invalidate related cache entries cache.removeCache(`frequent_tags:${projectId}*`); cache.removeCache(`frequent_tags:all*`); cache.removeCache(`tags_project:${projectId}*`); cache.removeCache(`tag_suggestions:*${projectId}*`); - + res.status(200).json(result); } catch (error) { logger.logException(error); - res.status(500).json({ - error: 'Failed to create or update tag', - details: error.message + res.status(500).json({ + error: 'Failed to create or update tag', + details: error.message, }); } }; @@ -195,27 +197,26 @@ const tagController = function () { // Create cache key const cacheKey = `tag_suggestions:${query}:${projectId || 'all'}`; - + // Check cache first if (cache.hasCache(cacheKey)) { return res.status(200).json(cache.getCache(cacheKey)); } - const queryObj = { - tagName: { $regex: query, $options: 'i' } + const queryObj = { + tagName: { $regex: query, $options: 'i' }, }; - + if (projectId) { queryObj.projectId = projectId; } - const suggestions = await Tag.find(queryObj) - .distinct('tagName'); + const suggestions = await Tag.find(queryObj).distinct('tagName'); const limitedSuggestions = suggestions.slice(0, 10); const response = { limitedSuggestions }; - + // Cache the response with a shorter TTL for suggestions cache.setCache(cacheKey, response); cache.setKeyTimeToLive(cacheKey, 300); // 5 minutes TTL for suggestions @@ -223,9 +224,9 @@ const tagController = function () { res.status(200).json(response); } catch (error) { logger.logException(error); - res.status(500).json({ - error: 'Failed to fetch tag suggestions', - details: error.message + res.status(500).json({ + error: 'Failed to fetch tag suggestions', + details: error.message, }); } }; @@ -236,28 +237,28 @@ const tagController = function () { const getTagById = async function (req, res) { try { const { tagId } = req.params; - + // Try to find by MongoDB ObjectId first let tag = null; if (mongoose.Types.ObjectId.isValid(tagId)) { tag = await Tag.findById(tagId); } - + // If not found by ObjectId, try to find by custom tagId if (!tag) { tag = await Tag.findOne({ tagId }); } - + if (!tag) { return res.status(404).json({ error: 'Tag not found' }); } - + res.status(200).json(tag); } catch (error) { logger.logException(error); - res.status(500).json({ - error: 'Failed to fetch tag', - details: error.message + res.status(500).json({ + error: 'Failed to fetch tag', + details: error.message, }); } }; @@ -268,37 +269,37 @@ const tagController = function () { const deleteTag = async function (req, res) { try { const { tagId } = req.params; - + // Try to find by MongoDB ObjectId first let tag = null; if (mongoose.Types.ObjectId.isValid(tagId)) { tag = await Tag.findById(tagId); } - + // If not found by ObjectId, try to find by custom tagId if (!tag) { tag = await Tag.findOne({ tagId }); } - + if (!tag) { return res.status(404).json({ error: 'Tag not found' }); } - + // Delete the tag await tag.remove(); - + // Invalidate caches cache.removeCache(`frequent_tags:${tag.projectId}*`); cache.removeCache(`frequent_tags:all*`); cache.removeCache(`tags_project:${tag.projectId}*`); cache.removeCache(`tag_suggestions:*${tag.projectId}*`); - + res.status(200).json({ message: 'Tag deleted successfully' }); } catch (error) { logger.logException(error); - res.status(500).json({ - error: 'Failed to delete tag', - details: error.message + res.status(500).json({ + error: 'Failed to delete tag', + details: error.message, }); } }; @@ -309,8 +310,8 @@ const tagController = function () { upsertTag, getTagSuggestions, getTagById, - deleteTag + deleteTag, }; }; -module.exports = tagController; \ No newline at end of file +module.exports = tagController; diff --git a/src/controllers/teamController.js b/src/controllers/teamController.js index 8a4912953..388283677 100644 --- a/src/controllers/teamController.js +++ b/src/controllers/teamController.js @@ -26,7 +26,12 @@ const teamcontroller = function (Team) { { $unwind: { path: '$userProfile', - preserveNullAndEmptyArrays: true, + preserveNullAndEmptyArrays: true, // Ensures that if no userProfile is found, the document is not removed + }, + }, + { + $match: { + isActive: true, }, }, { @@ -36,7 +41,7 @@ const teamcontroller = function (Team) { // Keep the raw value that worked in Compass teamCode: '$userProfile.teamCode', }, - count: { $sum: 1 }, + count: { $sum: { $cond: [{ $ifNull: ['$members', false] }, 1, 0] } }, // Count only if members exist teamName: { $first: '$teamName' }, members: { $push: { @@ -80,6 +85,7 @@ const teamcontroller = function (Team) { res.status(500).send(error); }); }; + const getTeamById = function (req, res) { const { teamId } = req.params; diff --git a/src/cronjobs/pullRequestReviewJobs.js b/src/cronjobs/pullRequestReviewJobs.js index d04ac3c57..e8f528296 100644 --- a/src/cronjobs/pullRequestReviewJobs.js +++ b/src/cronjobs/pullRequestReviewJobs.js @@ -1,6 +1,5 @@ // eslint-disable-next-line no-unused-vars const { CronJob } = require('cron'); - const { syncGitHubData, acquireTodayJob, diff --git a/src/helpers/analyticsPopularPRsControllerHelper.js b/src/helpers/analyticsPopularPRsControllerHelper.js index 5dd10c2f7..cf34ebc06 100644 --- a/src/helpers/analyticsPopularPRsControllerHelper.js +++ b/src/helpers/analyticsPopularPRsControllerHelper.js @@ -1,8 +1,8 @@ const PullRequestSyncMetadata = require('../models/pullRequestSyncMetadata'); const PullRequest = require('../models/pullRequest'); const PullRequestReview = require('../models/pullRequestReview'); -const createGitHubClient = require('./githubPRHelper'); const { RateLimitedError } = require('../utilities/errorHandling/customError'); +const createGitHubClient = require('./githubPRHelper'); function getLastSunday(today = new Date()) { const dayOfWeek = today.getUTCDay(); diff --git a/src/helpers/helperModels/myTeam.js b/src/helpers/helperModels/myTeam.js index a6a225201..4dca09e04 100644 --- a/src/helpers/helperModels/myTeam.js +++ b/src/helpers/helperModels/myTeam.js @@ -3,15 +3,14 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const myTeamSchema = new Schema({ - _id: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, myteam: [ { _id: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile' }, fullName: { type: String }, role: { type: String }, - }], - + }, + ], }); // Add index on the 'myteam._id' field to optimize lookups on team members myTeamSchema.index({ 'myteam._id': 1 }); diff --git a/src/helpers/overviewReportHelper.js b/src/helpers/overviewReportHelper.js index d39ae946d..3eb0687bc 100644 --- a/src/helpers/overviewReportHelper.js +++ b/src/helpers/overviewReportHelper.js @@ -1753,6 +1753,163 @@ const overviewReportHelper = function () { return res; }; + /** + * returns mentor counts for the provided date range + * mirrors volunteer stats but filters strictly on Mentor role + * @param {Date} startDate + * @param {Date} endDate + * @param {Date} comparisonStartDate + * @param {Date} comparisonEndDate + */ + const getMentorNumberStats = async ( + startDate, + endDate, + comparisonStartDate, + comparisonEndDate, + ) => { + const getMentorData = async (isoStartDate, isoEndDate) => { + const data = await UserProfile.aggregate([ + { + $facet: { + activeMentors: [ + { + $match: { + isActive: true, + role: 'Mentor', + createdDate: { $lte: isoEndDate }, + }, + }, + { $count: 'count' }, + ], + newMentors: [ + { + $match: { + role: 'Mentor', + createdDate: { + $gte: isoStartDate, + $lte: isoEndDate, + }, + }, + }, + { $count: 'count' }, + ], + deactivatedMentors: [ + { + $match: { + $and: [ + { lastModifiedDate: { $gte: isoStartDate } }, + { lastModifiedDate: { $lte: isoEndDate } }, + { isActive: false }, + { role: 'Mentor' }, + ], + }, + }, + { $count: 'count' }, + ], + }, + }, + ]); + + const activeMentors = data[0].activeMentors[0]?.count || 0; + const newMentors = data[0].newMentors[0]?.count || 0; + const deactivatedMentors = data[0].deactivatedMentors[0]?.count || 0; + const totalMentors = activeMentors + newMentors + deactivatedMentors; + + return { + activeMentors, + newMentors, + deactivatedMentors, + totalMentors, + }; + }; + + const { + activeMentors: currentActiveMentors, + newMentors: currentNewMentors, + deactivatedMentors: currentDeactivatedMentors, + totalMentors: currentTotalMentors, + } = await getMentorData(startDate, endDate); + + // Calculate existing active mentors (active - new) + const currentExistingActive = currentActiveMentors - currentNewMentors; + + const mentorStats = { + activeMentors: { + count: currentActiveMentors, + percentageOutOfTotal: Math.round((currentActiveMentors / currentTotalMentors) * 100) / 100, + }, + newMentors: { + count: currentNewMentors, + percentageOutOfTotal: Math.round((currentNewMentors / currentTotalMentors) * 100) / 100, + }, + deactivatedMentors: { + count: currentDeactivatedMentors, + percentageOutOfTotal: + Math.round((currentDeactivatedMentors / currentTotalMentors) * 100) / 100, + }, + totalMentors: { count: currentTotalMentors }, + donutChartData: { + existingActive: { + count: currentExistingActive, + percentageOutOfTotal: + Math.round((currentExistingActive / currentTotalMentors) * 100) / 100, + }, + newActive: { + count: currentNewMentors, + percentageOutOfTotal: Math.round((currentNewMentors / currentTotalMentors) * 100) / 100, + }, + deactivated: { + count: currentDeactivatedMentors, + percentageOutOfTotal: + Math.round((currentDeactivatedMentors / currentTotalMentors) * 100) / 100, + }, + }, + }; + + if (comparisonStartDate && comparisonEndDate) { + const { + activeMentors: comparisonActiveMentors, + newMentors: comparisonNewMentors, + deactivatedMentors: comparisonDeactivatedMentors, + totalMentors: comparisonTotalMentors, + } = await getMentorData(comparisonStartDate, comparisonEndDate); + + // Calculate comparison existing active mentors + const comparisonExistingActive = comparisonActiveMentors - comparisonNewMentors; + + mentorStats.activeMentors.comparisonPercentage = calculateGrowthPercentage( + currentActiveMentors, + comparisonActiveMentors, + ); + mentorStats.newMentors.comparisonPercentage = calculateGrowthPercentage( + currentNewMentors, + comparisonNewMentors, + ); + mentorStats.deactivatedMentors.comparisonPercentage = calculateGrowthPercentage( + currentDeactivatedMentors, + comparisonDeactivatedMentors, + ); + mentorStats.totalMentors.comparisonPercentage = calculateGrowthPercentage( + currentTotalMentors, + comparisonTotalMentors, + ); + mentorStats.donutChartData.existingActive.comparisonPercentage = calculateGrowthPercentage( + currentExistingActive, + comparisonExistingActive, + ); + mentorStats.donutChartData.newActive.comparisonPercentage = calculateGrowthPercentage( + currentNewMentors, + comparisonNewMentors, + ); + mentorStats.donutChartData.deactivated.comparisonPercentage = calculateGrowthPercentage( + currentDeactivatedMentors, + comparisonDeactivatedMentors, + ); + } + + return mentorStats; + }; + /** * * @returns The number of teams with 4 or more members. @@ -2535,6 +2692,7 @@ const overviewReportHelper = function () { getAnniversaries, getRoleDistributionStats, getVolunteerNumberStats, + getMentorNumberStats, getTasksStats, getWorkDistributionStats, getTotalHoursWorked, diff --git a/src/helpers/reporthelper.js b/src/helpers/reporthelper.js index 100fe23ce..4f27fcee9 100644 --- a/src/helpers/reporthelper.js +++ b/src/helpers/reporthelper.js @@ -170,10 +170,17 @@ const reporthelper = function () { result.totalSeconds = [0, 0, 0, 0]; if (!result.timeEntries || result.timeEntries.length === 0) return; - + const isSingleWeekRequest = startWeekIndex === endWeekIndex; result.timeEntries.forEach((entry) => { - const index = - startWeekIndex === endWeekIndex ? 0 : absoluteDifferenceInWeeks(entry.dateOfWork, pstEnd); + let index; + + if (isSingleWeekRequest) index = startWeekIndex; + else { + index = + startWeekIndex === endWeekIndex + ? 0 + : absoluteDifferenceInWeeks(entry.dateOfWork, pstEnd); + } if (index >= 0 && index < 4) { result.totalSeconds[index] = diff --git a/src/helpers/userHelper.js b/src/helpers/userHelper.js index 795b72569..507a1fbc5 100644 --- a/src/helpers/userHelper.js +++ b/src/helpers/userHelper.js @@ -1341,7 +1341,7 @@ const userHelper = function () { ), null, combinedCCList, - DEFAULT_REPLY_TO[0], + emailAddress, combinedBCCList, { type: 'blue_square_assignment' }, ); diff --git a/src/models/bmdashboard/metIssue.js b/src/models/bmdashboard/metIssue.js index 9802fdb26..62b243962 100644 --- a/src/models/bmdashboard/metIssue.js +++ b/src/models/bmdashboard/metIssue.js @@ -3,13 +3,18 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; const metIssue = new Schema({ - createdDate: { type: Date, required: true, default: Date.now() }, - issueDate: { type: Date, required: true }, - createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, - issueType: { type: String, required: true, maxLength: 50, enum: ['Safety', 'Labor', 'Weather', 'Other', 'METs quality / functionality'] }, - issueConsequences: [{ type: String, required: true, maxLength: 100 }], - issueResolved: { type: Boolean, required: true, default: false }, - issueDescription: { type: String, required: true, maxLength: 500 }, + createdDate: { type: Date, required: true, default: Date.now() }, + issueDate: { type: Date, required: true }, + createdBy: { type: mongoose.SchemaTypes.ObjectId, ref: 'userProfile', required: true }, + issueType: { + type: String, + required: true, + maxLength: 50, + enum: ['Safety', 'Labor', 'Weather', 'Other', 'METs quality / functionality'], + }, + issueConsequences: [{ type: String, required: true, maxLength: 100 }], + issueResolved: { type: Boolean, required: true, default: false }, + issueDescription: { type: String, required: true, maxLength: 500 }, }); module.exports = mongoose.model('metIssue', metIssue, 'metIssues'); diff --git a/src/models/tag.js b/src/models/tag.js index 1bf416b89..04f8a2504 100644 --- a/src/models/tag.js +++ b/src/models/tag.js @@ -6,31 +6,31 @@ const tagSchema = new Schema({ tagId: { type: String, required: true, - unique: true + unique: true, }, tagName: { type: String, required: true, - index: true + index: true, }, projectId: { type: String, required: true, - index: true + index: true, }, createdAt: { type: Date, required: true, default: Date.now, - index: true + index: true, }, frequency: { type: Number, - default: 0 - } + default: 0, + }, }); tagSchema.index({ projectId: 1, createdAt: 1 }); tagSchema.index({ tagName: 1, projectId: 1 }); -module.exports = mongoose.model('Tag', tagSchema); \ No newline at end of file +module.exports = mongoose.model('Tag', tagSchema); diff --git a/src/models/timeentry.js b/src/models/timeentry.js index 259437b4d..dd2f3af45 100644 --- a/src/models/timeentry.js +++ b/src/models/timeentry.js @@ -21,5 +21,4 @@ TimeEntry.index({ personId: 1, dateOfWork: 1 }); TimeEntry.index({ entryType: 1, teamId: 1, dateOfWork: 1, isActive: 1 }); TimeEntry.index({ personId: 1, dateOfWork: 1 }); - module.exports = mongoose.model('timeEntry', TimeEntry, 'timeEntries'); diff --git a/src/routes/CommunityPortal/NoshowVizRouter.js b/src/routes/CommunityPortal/NoshowVizRouter.js index 48eba0b82..82193633d 100644 --- a/src/routes/CommunityPortal/NoshowVizRouter.js +++ b/src/routes/CommunityPortal/NoshowVizRouter.js @@ -6,27 +6,21 @@ const routes = function () { console.log(controller); // Route for getting no shows data by period (e.g., year, month) - noShowRouter.route('/data') - .get(controller.getNoShowsData); + noShowRouter.route('/data').get(controller.getNoShowsData); // Route for getting no shows by location - noShowRouter.route('/location') - .get(controller.getNoShowsByLocation); + noShowRouter.route('/location').get(controller.getNoShowsByLocation); // Route for getting no shows by age group - noShowRouter.route('/age-group') - .get(controller.getNoShowsByAgeGroup); + noShowRouter.route('/age-group').get(controller.getNoShowsByAgeGroup); // Route for getting no shows by gender (example) - noShowRouter.route('/proportions') - .get(controller.getNoShowProportions); + noShowRouter.route('/proportions').get(controller.getNoShowProportions); - noShowRouter.route('/unique-eventTypes') - .get(controller.getUniqueEventTypes); + noShowRouter.route('/unique-eventTypes').get(controller.getUniqueEventTypes); // Route for getting attendance by day - noShowRouter.route('/by-day') - .get(controller.getAttendanceByDay); + noShowRouter.route('/by-day').get(controller.getAttendanceByDay); return noShowRouter; }; diff --git a/src/routes/actionItemRouter.test.js b/src/routes/actionItemRouter.test.js index 2bd7251a9..5a3e9ca79 100644 --- a/src/routes/actionItemRouter.test.js +++ b/src/routes/actionItemRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix action item router tests"); +test.todo('Fix action item router tests'); // const request = require('supertest'); // const { jwtPayload } = require('../test'); // const { app } = require('../app'); diff --git a/src/routes/badgeRouter.js b/src/routes/badgeRouter.js index cde4b1be0..036882e8b 100644 --- a/src/routes/badgeRouter.js +++ b/src/routes/badgeRouter.js @@ -4,6 +4,7 @@ const routes = function (badge) { const controller = require('../controllers/badgeController')(badge); const badgeRouter = express.Router(); + // badgeRouter.get('/badge/awardBadgesTest', controller.awardBadgesTest); if (typeof controller.awardBadgesTest === 'function') { badgeRouter.get('/badge/awardBadgesTest', controller.awardBadgesTest); } @@ -12,7 +13,9 @@ const routes = function (badge) { badgeRouter.route('/badge/:badgeId').delete(controller.deleteBadge).put(controller.putBadge); - badgeRouter.route('/badge/assign/:userId').put(controller.assignBadges); + badgeRouter.route('/badge/assign').post(controller.assignBadges); + + badgeRouter.route('/badge/assign/:userId').put(controller.assignBadgesToSingleUser); badgeRouter .route('/badge/badgecount/:userId') diff --git a/src/routes/badgeRouter.test.js b/src/routes/badgeRouter.test.js index d6ffe65e4..39bdd1fe1 100644 --- a/src/routes/badgeRouter.test.js +++ b/src/routes/badgeRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix badge router test"); +test.todo('Fix badge router test'); // const request = require('supertest'); // const { jwtPayload } = require('../test'); // const { app } = require('../app'); diff --git a/src/routes/dashboardRouter.js b/src/routes/dashboardRouter.js index d2f35a61c..14c90248e 100644 --- a/src/routes/dashboardRouter.js +++ b/src/routes/dashboardRouter.js @@ -13,44 +13,43 @@ const route = function () { .get(controller.getPromptCopiedDate) .put(controller.updateCopiedPrompt); - Dashboardrouter.route('/dashboard/:userId') - .get(controller.dashboarddata); + Dashboardrouter.route('/dashboard/:userId').get(controller.dashboarddata); - Dashboardrouter.route('/dashboard/monthlydata/:userId/:fromDate/:toDate') - .get(controller.monthlydata); + Dashboardrouter.route('/dashboard/monthlydata/:userId/:fromDate/:toDate').get( + controller.monthlydata, + ); - Dashboardrouter.route('/dashboard/weeklydata/:userId/:fromDate/:toDate') - .get(controller.weeklydata); + Dashboardrouter.route('/dashboard/weeklydata/:userId/:fromDate/:toDate').get( + controller.weeklydata, + ); - Dashboardrouter.route('/dashboard/leaderboard/:userId') - .get(controller.leaderboarddata); + Dashboardrouter.route('/dashboard/leaderboard/:userId').get(controller.leaderboarddata); - Dashboardrouter.route('/dashboard/leaderboard/org/data') - .get(controller.orgData); + Dashboardrouter.route('/dashboard/leaderboard/org/data').get(controller.orgData); - Dashboardrouter.route('/dashboard/leaderboard/trophyIcon/:userId/:trophyFollowedUp') - .post(controller.postTrophyIcon); + Dashboardrouter.route('/dashboard/leaderboard/trophyIcon/:userId/:trophyFollowedUp').post( + controller.postTrophyIcon, + ); - Dashboardrouter.route('/dashboard/suggestionoption/:userId') - .get(controller.getSuggestionOption); + Dashboardrouter.route('/dashboard/suggestionoption/:userId').get(controller.getSuggestionOption); - Dashboardrouter.route('/dashboard/bugreport/:userId') - .post(controller.sendBugReport); + Dashboardrouter.route('/dashboard/bugreport/:userId').post(controller.sendBugReport); - Dashboardrouter.route('/dashboard/suggestionoption/:userId') - .post(controller.editSuggestionOption); + Dashboardrouter.route('/dashboard/suggestionoption/:userId').post( + controller.editSuggestionOption, + ); - Dashboardrouter.route('/dashboard/makesuggestion/:userId') - .post(controller.sendMakeSuggestion); - - Dashboardrouter.route('/dashboard/questionaire/feedbackrequest') - .post(controller.requestFeedbackModal); + Dashboardrouter.route('/dashboard/makesuggestion/:userId').post(controller.sendMakeSuggestion); - Dashboardrouter.route('/dashboard/questionaire/userNamesList') - .get(controller.getUserNames); + Dashboardrouter.route('/dashboard/questionaire/feedbackrequest').post( + controller.requestFeedbackModal, + ); - Dashboardrouter.route('/dashboard/questionaire/checkUserFoundHelpSomewhere') - .post(controller.checkUserFoundHelpSomewhere); + Dashboardrouter.route('/dashboard/questionaire/userNamesList').get(controller.getUserNames); + + Dashboardrouter.route('/dashboard/questionaire/checkUserFoundHelpSomewhere').post( + controller.checkUserFoundHelpSomewhere, + ); return Dashboardrouter; }; diff --git a/src/routes/forgotPwdRouter.test.js b/src/routes/forgotPwdRouter.test.js index 1cdd4d463..11bc1f0a4 100644 --- a/src/routes/forgotPwdRouter.test.js +++ b/src/routes/forgotPwdRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix forgot password router tests"); +test.todo('Fix forgot password router tests'); // const request = require('supertest'); // const { jwtPayload } = require('../test'); // const { app } = require('../app'); diff --git a/src/routes/formRouter.test.js b/src/routes/formRouter.test.js index 3ea2e4317..53ea11b92 100644 --- a/src/routes/formRouter.test.js +++ b/src/routes/formRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix form router test"); +test.todo('Fix form router test'); // // test/formRoutes.test.js // const request = require('supertest'); // const { jwtPayload } = require('../test'); diff --git a/src/routes/helpCategoryRouter.js b/src/routes/helpCategoryRouter.js index 626fc09be..4dfa61501 100644 --- a/src/routes/helpCategoryRouter.js +++ b/src/routes/helpCategoryRouter.js @@ -7,4 +7,4 @@ router.get('/', helpCategoryController.getAllHelpCategories); router.post('/', helpCategoryController.createHelpCategory); router.put('/:id', helpCategoryController.updateHelpCategory); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/src/routes/informationRouter.test.js b/src/routes/informationRouter.test.js index 9e54e10bc..2070796b1 100644 --- a/src/routes/informationRouter.test.js +++ b/src/routes/informationRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix information router tests."); +test.todo('Fix information router tests.'); // const request = require('supertest'); // const { jwtPayload } = require('../test'); // const cache = require('../utilities/nodeCache')(); diff --git a/src/routes/mouseoverTextRouter.test.js b/src/routes/mouseoverTextRouter.test.js index efa0a48ad..ad603df4a 100644 --- a/src/routes/mouseoverTextRouter.test.js +++ b/src/routes/mouseoverTextRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix mouseover test router tests"); +test.todo('Fix mouseover test router tests'); // const request = require('supertest'); // const { jwtPayload } = require('../test'); // const { app } = require('../app'); diff --git a/src/routes/projectRouter.test.js b/src/routes/projectRouter.test.js index 30a324fda..a034ec349 100644 --- a/src/routes/projectRouter.test.js +++ b/src/routes/projectRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix project routers test suite"); +test.todo('Fix project routers test suite'); // const request = require('supertest'); // const { app } = require('../app'); // const { diff --git a/src/routes/reasonRouter.test.js b/src/routes/reasonRouter.test.js index 561d7013d..b743016ae 100644 --- a/src/routes/reasonRouter.test.js +++ b/src/routes/reasonRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix reason routers test suite"); +test.todo('Fix reason routers test suite'); // const request = require('supertest'); // const moment = require('moment-timezone'); // const { jwtPayload } = require('../test'); diff --git a/src/routes/tagRouter.js b/src/routes/tagRouter.js index 78ef9ad8d..6cdd39c85 100644 --- a/src/routes/tagRouter.js +++ b/src/routes/tagRouter.js @@ -5,27 +5,21 @@ const routes = function (Tag) { const controller = require('../controllers/tagController')(Tag); // Get most frequent tags with optional filtering by project and date range - tagRouter.route('/tags/frequent') - .get(controller.getFrequentTags); + tagRouter.route('/tags/frequent').get(controller.getFrequentTags); // Get tag suggestions for autocomplete - tagRouter.route('/tags/suggestions') - .get(controller.getTagSuggestions); + tagRouter.route('/tags/suggestions').get(controller.getTagSuggestions); // Get tags for a specific project - tagRouter.route('/tags/project/:projectId') - .get(controller.getTagsByProject); + tagRouter.route('/tags/project/:projectId').get(controller.getTagsByProject); // Get, update, or delete a specific tag by ID - tagRouter.route('/tags/:tagId') - .get(controller.getTagById) - .delete(controller.deleteTag); + tagRouter.route('/tags/:tagId').get(controller.getTagById).delete(controller.deleteTag); // Create or update a tag - tagRouter.route('/tags') - .post(controller.upsertTag); + tagRouter.route('/tags').post(controller.upsertTag); return tagRouter; }; -module.exports = routes; \ No newline at end of file +module.exports = routes; diff --git a/src/routes/timeZoneAPIRoutes.test.js b/src/routes/timeZoneAPIRoutes.test.js index 0bce2b99d..7aec40932 100644 --- a/src/routes/timeZoneAPIRoutes.test.js +++ b/src/routes/timeZoneAPIRoutes.test.js @@ -1,4 +1,4 @@ -test.todo("Fix time zone API routes tests"); +test.todo('Fix time zone API routes tests'); // const request = require('supertest'); // const { jwtPayload } = require('../test'); // diff --git a/src/routes/timeentryRouter.js b/src/routes/timeentryRouter.js index 12683c6f2..2de672fe3 100644 --- a/src/routes/timeentryRouter.js +++ b/src/routes/timeentryRouter.js @@ -15,7 +15,9 @@ const routes = function (TimeEntry) { controller.getTimeEntriesForSpecifiedPeriod, ); - TimeEntryRouter.route('/TimeEntry/users/totalHours').post(controller.getUsersTotalHoursForSpecifiedPeriod); + TimeEntryRouter.route('/TimeEntry/users/totalHours').post( + controller.getUsersTotalHoursForSpecifiedPeriod, + ); TimeEntryRouter.route('/TimeEntry/users').post(controller.getTimeEntriesForUsersList); diff --git a/src/routes/userProfileRouter.test.js b/src/routes/userProfileRouter.test.js index 99ee9edb8..0e3d3a0c2 100644 --- a/src/routes/userProfileRouter.test.js +++ b/src/routes/userProfileRouter.test.js @@ -1,4 +1,4 @@ -test.todo("Fix user profile router tests"); +test.todo('Fix user profile router tests'); // const request = require('supertest'); // const { jwtPayload } = require('../test'); // const cache = require('../utilities/nodeCache')(); diff --git a/src/test/mock-request.js b/src/test/mock-request.js index e4475b6a9..448bcc2e3 100644 --- a/src/test/mock-request.js +++ b/src/test/mock-request.js @@ -1,21 +1,21 @@ const mockReq = { - body: { - requestor: { - role: 'Administrator', - permissions: { - frontPermissions: ['addDeleteEditOwners', 'postTeam'], - backPermissions: ['postTeam'], - }, - requestorId: '65cf6c3706d8ac105827bb2e', // this one matches the id of the db/createUser for testing purposes - }, - }, - params: { - userid: '5a7e21f00317bc1538def4b7', - userId: '5a7e21f00317bc1538def4b7', - teamId: '5a8e21f00317bc', - wbsId: '5a7e21f00317bc1538def4b7', - projectId: '5a7e21f00317bc1538def4b7', + body: { + requestor: { + role: 'Administrator', + permissions: { + frontPermissions: ['addDeleteEditOwners', 'postTeam'], + backPermissions: ['postTeam'], + }, + requestorId: '65cf6c3706d8ac105827bb2e', // this one matches the id of the db/createUser for testing purposes }, + }, + params: { + userid: '5a7e21f00317bc1538def4b7', + userId: '5a7e21f00317bc1538def4b7', + teamId: '5a8e21f00317bc', + wbsId: '5a7e21f00317bc1538def4b7', + projectId: '5a7e21f00317bc1538def4b7', + }, }; module.exports = mockReq; diff --git a/src/utilities/permissions.js b/src/utilities/permissions.js index bc58456b9..d6219b577 100644 --- a/src/utilities/permissions.js +++ b/src/utilities/permissions.js @@ -26,12 +26,14 @@ const hasIndividualPermission = async (userId, action) => .catch(false); const hasPermission = async (requestor, action) => { - const defaultRemoved = requestor.requestorId && (await hasDefaultPermissionRemoved(requestor.requestorId, action)); + const defaultRemoved = + requestor.requestorId && (await hasDefaultPermissionRemoved(requestor.requestorId, action)); const roleHasPermission = await hasRolePermission(requestor.role, action); - const individualHasPermission = requestor.requestorId && (await hasIndividualPermission(requestor.requestorId, action)); - + const individualHasPermission = + requestor.requestorId && (await hasIndividualPermission(requestor.requestorId, action)); + return (!defaultRemoved && roleHasPermission) || individualHasPermission; -} +}; function getDistinct(arr1, arr2) { // Merge arrays and reduce to distinct elements