Updated track work API

This commit is contained in:
Aakash Mittal 2025-12-15 20:00:02 +05:30
parent 645adea832
commit 287d078b49
4 changed files with 284 additions and 63 deletions

View File

@ -167,44 +167,70 @@ async function va_log_track_work_callback(req, res) {
return res.status(400).json({ success: false, message: "No logs to track" }); return res.status(400).json({ success: false, message: "No logs to track" });
} }
// Check last log entry for this VA and project logs[0].from = logEntry.time; // Ensure the first log's 'from' matches the main log time
const lastLog = await LoggedModel.findOne({ vaId, projectId })
.sort({ time: -1 }) // get the most recent log
.lean();
// If the last log is a LOGOUT, prevent new work tracking const firstFromLogTime = new Date(logs[0].from);
if (lastLog.loggedType === "LOGOUT") { const firstToLogTime = new Date(logs[0].to);
return res.status(400).json({ const firstTotalLogTime = Math.abs(firstToLogTime - firstFromLogTime);
success: false, logs[0].totalTime = firstTotalLogTime;
message: "User has already logged out from this project",
});
} // // Check last log entry for this VA and project
// const lastLog = await LoggedModel.findOne({ vaId, projectId })
// .sort({ time: -1 }) // get the most recent log
// .lean();
// // If the last log is a LOGOUT, prevent new work tracking
// if (lastLog.loggedType === "LOGOUT") {
// return res.status(400).json({
// success: false,
// message: "User has already logged out from this project",
// });
// }
// Handle POST → create entry // Handle POST → create entry
// if (req.method === "POST") { // if (req.method === "POST") {
// const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project); // const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project);
const createdTracks = await Promise.all( // const createdTracks = await Promise.all(
logs.map(async (log) => { // logs.map(async (log) => {
const createdTrack = await WorkTrackModel.create({ // // Calculate total time from timestamps
vaId, // const fromTime = new Date(log.from);
projectId, // const toTime = new Date(log.to);
logId, // const totalTime = Math.abs(toTime - fromTime);
titlew: log.title, // const createdTrack = await WorkTrackModel.create({
appName: log.appName, // vaId,
url: log.url, // projectId,
from: log.from, // logId,
to: log.to, // Fixed: Changed from duplicate "from" to "to" // title: log.title,
totalTime: log.totalTime, // appName: log.appName,
}); // url: log.url,
// from: log.from,
// to: log.to,
// totalTime: totalTime,
// });
return createdTrack; // return createdTrack;
}) // })
); // );
const fromLogTime = new Date(logs[0].from);
const toLogTime = new Date(logs[logs.length -1].to);
const totalLogTime = Math.abs(toLogTime - fromLogTime);
const createdTrack = await WorkTrackModel.create({
vaId,
projectId,
logId,
from: logs[0].from,
to: logs[logs.length -1].to,
totalTime: totalLogTime,
logs: logs
});
return res.status(201).json({ return res.status(201).json({
success: true, success: true,
message: "Work tracking created", message: "Work tracking created",
data: createdTracks data: createdTrack
}); });
// } // }

View File

@ -289,6 +289,129 @@ async function va_project_log_callback(req, res) {
// }); // });
// } // }
// If Logout then Update work track entries
if (type === "LOGOUT") {
const { logId } = payload;
const workTrackData = await WorkTrackModel.find({
logId: logId
});
// console.log(`Found work track data for logout:`, workTrackData);
if (workTrackData[0]) {
const fromLogTime = new Date(workTrackData[0].from);
const toLogTime = new Date(logTime);
const totalTimeMs = toLogTime - fromLogTime;
const updatedLogs = workTrackData[0].logs ? [...workTrackData[0].logs] : [];
if (updatedLogs.length > 0) {
const lastLogIndex = updatedLogs.length - 1;
const lastLogEntry = updatedLogs[lastLogIndex];
// Update the last log's 'to' time and recalculate its totalTime
const lastLogFromTime = new Date(lastLogEntry.from);
const lastLogTotalTime = Math.abs(toLogTime - lastLogFromTime);
updatedLogs[lastLogIndex] = {
...lastLogEntry,
to: toLogTime,
totalTime: lastLogTotalTime
};
// console.log(`Updated last log in array - from: ${lastLogFromTime.toISOString()}, to: ${toLogTime.toISOString()}, totalTime: ${lastLogTotalTime}ms`);
}
const updateResult = await WorkTrackModel.updateOne(
{ _id: workTrackData[0]._id },
{
$set: {
to: toLogTime,
totalTime: totalTimeMs,
logs: updatedLogs
}
}
);
console.log(`Updated work track ${workTrackData[0]._id}:`, {
from: fromLogTime.toISOString(),
to: toLogTime.toISOString(),
totalTime: totalTimeMs,
logsUpdated: updatedLogs.length,
mongodbUpdateResult: updateResult
});
// const updatedWorkTrack = await WorkTrackModel.findById(workTrackData[0]._id);
// console.log("Verified updated work track:", {
// to: updatedWorkTrack?.to,
// totalTime: updatedWorkTrack?.totalTime,
// logsCount: updatedWorkTrack?.logs?.length,
// lastLog: updatedWorkTrack?.logs?.[updatedWorkTrack.logs.length - 1]
// });
} else {
console.log(`No work track found for vaId: ${vaId}, projectId: ${projectId}, logId: ${logId}`);
}
// Create the logout log entry
const createdLog = await LoggedModel.create({
loggedType: type,
vaId,
vaEmail,
projectId,
time: logTime
});
try {
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
const sfData = {
"_id": createdLog._id,
"loggedType": type,
"vaId": vaId,
"vaEmail": vaEmail,
"projectId": projectId,
"time": createdLog.time,
}
const response = await fetch(salesforceUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(sfData)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
}
const result = await response.json();
return res.status(201).json({
success: true,
message: `${type} Successfully`,
logId: createdLog._id,
SyncSalesforce: result,
timeUsed: receivedTimeString ? 'received' : 'current',
actualTime: createdLog.time,
});
} catch (error) {
console.error("Failed to sync with Salesforce:", error.message);
console.log(error)
// Still return success since the logout was recorded locally
return res.status(201).json({
success: true,
message: `${type} Successfully (Salesforce sync failed)`,
logId: createdLog._id,
timeUsed: receivedTimeString ? 'received' : 'current',
actualTime: createdLog.time,
warning: "Salesforce sync failed"
});
}
}
// For LOGIN requests, check daily hours (using UTC day range) // For LOGIN requests, check daily hours (using UTC day range)
if (type === "LOGIN") { if (type === "LOGIN") {
const { start, end } = getDayRange(); // UTC day range const { start, end } = getDayRange(); // UTC day range

View File

@ -64,6 +64,18 @@ async function va_projects_report_function(req, res) {
return res.json({ success: true, message: "No logs found for this project", data: [] }); return res.json({ success: true, message: "No logs found for this project", data: [] });
} }
// Get all work tracks for this VA and project in the date range
const workTracks = await WorkTrackModel.find({
projectId,
vaId,
$or: [
{ from: { $gte: fromDate, $lte: toDate } },
{ to: { $gte: fromDate, $lte: toDate } }
]
})
.sort({ from: 1 })
.lean();
// Unique VA IDs // Unique VA IDs
const vaIds = [...new Set(logs.map(log => log.vaId?.toString()))].filter(Boolean); const vaIds = [...new Set(logs.map(log => log.vaId?.toString()))].filter(Boolean);
const vaUsers = await VAModel.find( const vaUsers = await VAModel.find(
@ -73,55 +85,120 @@ async function va_projects_report_function(req, res) {
const projectReport = []; const projectReport = [];
for (const vaId of vaIds) { for (const currentVaId of vaIds) {
const vaLogList = logs.filter(l => l.vaId.toString() === vaId); const vaLogList = logs.filter(l => l.vaId.toString() === currentVaId);
let sessions = []; let sessions = [];
let currentLogin = null; let currentLogin = null;
const vaWorkTracks = workTracks.filter(w => w.vaId.toString() === currentVaId);
// Pair login → logout // Pair login → logout
for (const entry of vaLogList) { for (const entry of vaLogList) {
if (entry.loggedType === "LOGIN") { if (entry.loggedType === "LOGIN") {
currentLogin = entry; currentLogin = entry;
} else if (entry.loggedType === "LOGOUT" && currentLogin) { } else if (entry.loggedType === "LOGOUT" && currentLogin) {
sessions.push({ from: currentLogin.time, to: entry.time }); sessions.push({ from: currentLogin.time, to: entry.time, logId: currentLogin._id });
currentLogin = null; currentLogin = null;
} }
} }
// If still logged in, close at end of day // If still logged in, close at end of day
if (currentLogin) { if (currentLogin) {
sessions.push({ from: currentLogin.time, to: toDate }); sessions.push({ from: currentLogin.time, to: toDate, logId: currentLogin._id });
} }
const vaDetails = vaUsers.find(v => v._id.toString() === vaId); const vaDetails = vaUsers.find(v => v._id.toString() === currentVaId);
let totalWorkMs = 0; let totalWorkMs = 0;
const sessionDetails = []; const sessionDetails = [];
for (const s of sessions) { // for (const s of sessions) {
const workData = await WorkTrackModel.find({ // const workData = await WorkTrackModel.find({
vaId, // vaId,
projectId, // projectId,
from: { $gte: s.from }, // from: { $gte: s.from },
to: { $lte: s.to } // to: { $lte: s.to }
}).sort({ from: 1 }).lean(); // }).sort({ from: 1 }).lean();
const tabs = workData.map(t => ({ // const tabs = workData.map(t => ({
title: t.title, // title: t.title,
appName: t.appName, // appName: t.appName,
from: t.from, // from: t.from,
to: t.to, // to: t.to,
durationMs: t.totalTime, // durationMs: t.totalTime,
durationFormatted: formatHMS(t.totalTime), // durationFormatted: formatHMS(t.totalTime),
})); // }));
const sessionWorkMs = workData.reduce((sum, w) => sum + (w.totalTime || 0), 0); // const sessionWorkMs = workData.reduce((sum, w) => sum + (w.totalTime || 0), 0);
totalWorkMs += sessionWorkMs; // totalWorkMs += sessionWorkMs;
// sessionDetails.push({
// from: s.from,
// to: s.to,
// totalSessionTime: formatHMS(sessionWorkMs),
// tabs,
// });
// }
for (const session of sessions) {
// Find work tracks for this specific session (matching logId)
const sessionWorkTracks = vaWorkTracks.filter(
track => track.logId && track.logId.toString() === session.logId.toString()
);
let sessionTabs = [];
let sessionTotalMs = 0;
// Extract all logs from work tracks for this session
for (const workTrack of sessionWorkTracks) {
if (workTrack.logs && Array.isArray(workTrack.logs)) {
// Process each log entry in the logs array
for (const logEntry of workTrack.logs) {
// Calculate duration for this log entry
const logFrom = new Date(logEntry.from);
const logTo = new Date(logEntry.to || workTrack.to || session.to);
const logDuration = logEntry.totalTime || (logTo - logFrom);
sessionTotalMs += logDuration;
sessionTabs.push({
title: logEntry.title || "",
appName: logEntry.appName || "",
url: logEntry.url || null,
from: logFrom,
to: logTo,
durationMs: logDuration,
durationFormatted: formatHMS(logDuration),
});
}
}
// Also add the main work track if it has separate data
if (workTrack.totalTime && workTrack.totalTime > 0) {
sessionTotalMs += workTrack.totalTime;
// Only add if we don't have logs array data
if (!workTrack.logs || workTrack.logs.length === 0) {
sessionTabs.push({
title: workTrack.title || "",
appName: workTrack.appName || "",
url: workTrack.url || null,
from: workTrack.from,
to: workTrack.to,
durationMs: workTrack.totalTime,
durationFormatted: formatHMS(workTrack.totalTime),
});
}
}
}
totalWorkMs += sessionTotalMs;
sessionDetails.push({ sessionDetails.push({
from: s.from, from: session.from,
to: s.to, to: session.to,
totalSessionTime: formatHMS(sessionWorkMs), totalSessionTime: formatHMS(sessionTotalMs),
tabs, totalSessionMs: sessionTotalMs,
workTracksCount: sessionWorkTracks.length,
tabs: sessionTabs.sort((a, b) => a.from - b.from), // Sort tabs by time
}); });
} }

View File

@ -344,15 +344,6 @@ const vaWorkTrackSchema = new Schema({
type: Types.ObjectId, type: Types.ObjectId,
ref: "TrackLog", ref: "TrackLog",
}, },
title: {
type: String,
},
appName: {
type: String,
},
url: {
type: String,
},
from: { from: {
type: Date, type: Date,
default: Date.now, default: Date.now,
@ -365,6 +356,10 @@ const vaWorkTrackSchema = new Schema({
type: Number, type: Number,
default: 0, default: 0,
}, },
logs: {
type: Array,
default: [],
}
}, },
{ timestamps: true } { timestamps: true }
) )