Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 16 additions & 30 deletions src/controllers/bmdashboard/toolAvailabilityController.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { ObjectId } = require('mongoose').Types;
const mongoose = require('mongoose');
const BuildingProject = require('../../models/bmdashboard/buildingProject');

const { ObjectId } = mongoose.Types;
const toolAvailabilityController = function (ToolAvailability) {
const getToolsAvailability = async (req, res) => {
try {
Expand Down Expand Up @@ -124,36 +126,20 @@

const getUniqueProjectIds = async (req, res) => {
try {
// Use aggregation to get distinct project IDs and lookup their names
const results = await ToolAvailability.aggregate([
{
$group: {
_id: '$projectId',
},
},
{
$lookup: {
from: 'buildingProject',
localField: '_id',
foreignField: '_id',
as: 'projectDetails',
},
},
{
$project: {
_id: 1,
projectName: { $arrayElemAt: ['$projectDetails.projectName', 0] },
},
},
{
$sort: { projectName: 1 },
},
]);
// Get the unique project IDs that actually have tool availability data.
const projectIds = await ToolAvailability.distinct('projectId');

// Only return projects that still exist in the BuildingProject collection.
const projects = await BuildingProject.find({

Check failure on line 133 in src/controllers/bmdashboard/toolAvailabilityController.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

NoSQL Injection via unsanitized user input

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HGNRest&issues=AaEeCPXO0zEdb4XGD0Ob&open=AaEeCPXO0zEdb4XGD0Ob&pullRequest=2306
_id: { $in: projectIds },
})
.select('_id name')
.sort({ name: 1 })
.lean();

// Format the response
const formattedResults = results.map((item) => ({
projectId: item._id,
projectName: item.projectName || 'Unknown Project',
const formattedResults = projects.map((project) => ({
projectId: project._id,
projectName: project.name,
}));

return res.json(formattedResults);
Expand Down
90 changes: 47 additions & 43 deletions src/controllers/summaryDashboard/supplierPerformance.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const mongoose = require('mongoose');
const Project = require('../../models/project');
const SupplierPerformance = require('../../models/summaryDashboard/supplierPerformance');
const logger = require('../../startup/logger');

const supplierPerformanceController = function () {
/**
* Get supplier performance records for a specific project and date range, or all projects
* Get supplier performance records for a specific project and date range,
* or all projects.
*/
const getSupplierPerformance = async function (req, res) {
try {
Expand All @@ -14,86 +16,87 @@
return res.status(400).send('Missing required query parameters: startDate, endDate');
}

// Validate projectId before converting it to a MongoDB ObjectId.
// "all" is a valid special value used by the frontend.
if (projectId && projectId !== 'all' && !mongoose.Types.ObjectId.isValid(projectId)) {
return res.status(400).send('Invalid projectId.');
}

const start = new Date(startDate);
const end = new Date(endDate);

// Build match criteria - if projectId is not provided or is 'all', get data for all projects
const matchCriteria = {
startDate: { $lte: end },
endDate: { $gte: start },
};

if (projectId && projectId !== 'all') {
matchCriteria.projectId = mongoose.Types.ObjectId(projectId);
console.log('Fetching data for specific project:', projectId);
} else {
console.log('Fetching data for all projects');
}

console.log('Query params:', { projectId, startDate, endDate, start, end });
console.log('Match criteria:', matchCriteria);

// MongoDB Aggregation Pipeline
const supplierData = await SupplierPerformance.aggregate([
{
$match: matchCriteria,
},
{
$group: {
_id: '$supplierName',
onTimeDeliveryPercentage: { $avg: '$onTimeDeliveryPercentage' },
onTimeDeliveryPercentage: {
$avg: '$onTimeDeliveryPercentage',
},
},
},
{
$project: {
supplierName: '$_id',
onTimeDeliveryPercentage: { $round: ['$onTimeDeliveryPercentage', 2] },
onTimeDeliveryPercentage: {
$round: ['$onTimeDeliveryPercentage', 2],
},
},
},
{
$sort: {
onTimeDeliveryPercentage: -1,
},
},
{ $sort: { onTimeDeliveryPercentage: -1 } },
]);

console.log('Aggregation result:', supplierData);
res.status(200).send(supplierData);
return res.status(200).send(supplierData);
} catch (error) {
logger.logException(error);
res.status(500).send('Error fetching supplier performance data. Please try again.');
return res.status(500).send('Error fetching supplier performance data. Please try again.');
}
};

/**
* Get all unique projects that have supplier performance data
* Get all unique projects that have supplier performance data.
* Project names are returned along with their IDs so that the
* frontend dropdown can display project names instead of raw IDs.
*/
const getProjectsWithSupplierData = async function (req, res) {
try {
console.log('Fetching projects with supplier performance data');
const projectIds = await SupplierPerformance.distinct('projectId');

// Get distinct project IDs from supplier performance records only
const projectsWithData = await SupplierPerformance.aggregate([
{
$group: {
_id: '$projectId',
},
const projectsWithData = await Project.find({

Check failure on line 80 in src/controllers/summaryDashboard/supplierPerformance.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

NoSQL Injection via unsanitized user input

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HGNRest&issues=AaEeCPd_0zEdb4XGD0Oc&open=AaEeCPd_0zEdb4XGD0Oc&pullRequest=2306
_id: {
$in: projectIds,
},
{
$project: {
_id: 1,
projectId: '$_id',
},
},
{ $sort: { _id: 1 } },
]);

console.log(`Found ${projectsWithData.length} projects with supplier data`);
res.status(200).send(projectsWithData);
})
.select('_id projectName')
.sort({
projectName: 1,
})
.lean();

return res.status(200).send(projectsWithData);
} catch (error) {
logger.logException(error);
res.status(500).send('Error fetching projects with supplier data. Please try again.');
return res.status(500).send('Error fetching projects with supplier data. Please try again.');
}
};

/**
* Add a new supplier performance record
* Add a new supplier performance record.
*/
const postSupplierPerformance = async function (req, res) {
try {
Expand All @@ -116,15 +119,16 @@
});

await newRecord.save();
res.status(201).send('Supplier performance record created successfully.');

return res.status(201).send('Supplier performance record created successfully.');
} catch (error) {
logger.logException(error);
res.status(500).send('Error saving supplier performance data. Please try again.');
return res.status(500).send('Error saving supplier performance data. Please try again.');
}
};

/**
* Delete supplier performance records by Project ID
* Delete supplier performance records by Project ID.
*/
const deleteSupplierPerformanceByProject = async function (req, res) {
try {
Expand All @@ -139,13 +143,13 @@
});

if (result.deletedCount > 0) {
res.status(200).send(`Successfully deleted ${result.deletedCount} record(s).`);
} else {
res.status(404).send('No records found for the specified project.');
return res.status(200).send(`Successfully deleted ${result.deletedCount} record(s).`);
}

return res.status(404).send('No records found for the specified project.');
} catch (error) {
logger.logException(error);
res.status(500).send('Error deleting supplier performance data. Please try again.');
return res.status(500).send('Error deleting supplier performance data. Please try again.');
}
};

Expand Down
7 changes: 5 additions & 2 deletions src/routes/bmdashboard/toolAvailabilityRouter.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
const express = require('express');
const ToolAvailability = require('../../models/bmdashboard/toolAvailability');
const toolAvailabilityController = require('../../controllers/bmdashboard/toolAvailabilityController');

const routes = function () {
const toolAvailabilityRouter = express.Router();
const controller = toolAvailabilityController(ToolAvailability);

// GET /api/bm/projects/:id/tools-availability
toolAvailabilityRouter
.route('/bm/projects/:id/tools-availability')
.get((req, res) => res.json({ message: 'Get tools availability' }));
.get(controller.getToolsAvailability);

// GET /api/bm/tools-availability/projects
toolAvailabilityRouter
.route('/bm/tools-availability/projects')
.get((req, res) => res.json({ message: 'Get unique project IDs' }));
.get(controller.getUniqueProjectIds);

return toolAvailabilityRouter;
};
Expand Down
Loading