(wip) schedle generation;
added confirmations on schedule DELETE!!! better reports page; log every delete over API, more logging;
This commit is contained in:
@ -32,13 +32,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
//get target action
|
||||
if (req.method === 'DELETE') {
|
||||
switch (targetTable) {
|
||||
case 'publishers':
|
||||
case 'availabilities':
|
||||
// case 'publishers':
|
||||
// case 'availabilities':
|
||||
default:
|
||||
const targetId = req.query.nextcrud[1];
|
||||
logger.info('[nextCrud] ' + targetTable + ': ' + targetId + ' DELETED by ' + session.user.email);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nextCrudHandler(req, res);
|
||||
|
@ -366,13 +366,13 @@ export default async function handler(req, res) {
|
||||
+ " до: " + pubsToSend.map(p => p.firstName + " " + p.lastName + "<" + p.email + ">").join(", "),
|
||||
}
|
||||
});
|
||||
logger.info("User: " + publisher.email + " sent a 'CoverMe' request for his assignment " + assignmentId + " - " + assignment.shift.cartEvent.location.name + " " + assignment.shift.startTime.toISOString() + " to " + pubsToSend.map(p => p.firstName + " " + p.lastName + "<" + p.email + ">").join(", ") + ". EventLogId: " + eventLog.id + "");
|
||||
logger.info(". EventLogId: " + eventLog.id + "User: " + publisher.email + " sent a 'CoverMe' request for his assignment " + assignmentId + ", shift " + assignment.shift.id + " - " + assignment.shift.cartEvent.location.name + " " + assignment.shift.startTime.toISOString() + " to " + pubsToSend.map(p => p.firstName + " " + p.lastName + "<" + p.email + ">").join(", "));
|
||||
|
||||
//send email to all subscribed publishers
|
||||
for (let i = 0; i < pubsToSend.length; i++) {
|
||||
|
||||
//send email to subscribed publisher
|
||||
let acceptUrl = process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=coverMeAccept&userId=" + pubsToSend[i].id + "&shiftId=" + assignment.shift.id + "&assignmentPID=" + newPublicGuid;
|
||||
let acceptUrl = process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=coverMeAccept&userId=" + pubsToSend[i].id + "&shiftId=" + assignment.shift.id + "&assignmentPID=" + newPublicGuid + "&eventLogID=" + eventLog.id;
|
||||
publisher.prefix = publisher.isMale ? "Брат" : "Сестра";
|
||||
|
||||
let model = {
|
||||
|
@ -10,7 +10,7 @@ import { filterPublishers, /* other functions */ } from './index';
|
||||
|
||||
import CAL from "../../src/helpers/calendar";
|
||||
//const common = require("@common");
|
||||
import common from "../../src/helpers/common";
|
||||
import common, { logger } from "../../src/helpers/common";
|
||||
import data from "../../src/helpers/data";
|
||||
import { Axios } from 'axios';
|
||||
|
||||
@ -59,6 +59,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
break;
|
||||
case "delete":
|
||||
result = await DeleteSchedule(axios, req.query.date, common.parseBool(req.query.forDay));
|
||||
let msg = "Deleted schedule for " + (req.query.forDay ? req.query.date : "the entire month of ") + req.query.date + ". Action requested by " + token.email;
|
||||
logger.warn(msg);
|
||||
console.log(msg);
|
||||
res.send("deleted"); // JSON.stringify(result, null, 2)
|
||||
break;
|
||||
case "createcalendarevent":
|
||||
@ -694,7 +697,8 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
|
||||
return (hasTransportInAvailability || hasTransportOutAvailability) && isNotAssigned && isNotAssignedToday;
|
||||
});
|
||||
// rank publishers based on different factors
|
||||
let rankedPublishers = await RankPublishersForShift(availablePublishers)
|
||||
let rankedPublishersOld = await RankPublishersForShift(availablePublishers)
|
||||
let rankedPublishers = await RankPublishersForShiftWeighted(availablePublishers)
|
||||
if (rankedPublishers.length > 0) {
|
||||
const newAssignment = await prisma.assignment.create({
|
||||
data: {
|
||||
@ -801,36 +805,36 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
|
||||
});
|
||||
shift.assignments.push(newAssignment);
|
||||
publishersToday.push(rankedPublishers[0].id);
|
||||
}
|
||||
//check if publisher.familyMembers are also available and add them to the shift. ToDo: test case
|
||||
let familyMembers = availablePubsForTheShift.filter(p => p.familyHeadId && p.familyHeadId === rankedPublishers[0].familyHeadId);
|
||||
if (familyMembers.length > 0) {
|
||||
familyMembers.forEach(async familyMember => {
|
||||
if (shift.assignments.length < event.numberOfPublishers) {
|
||||
console.log("Assigning " + familyMember.firstName + " " + familyMember.lastName + " to " + shift.startDate.getDate() + " " + shift.name);
|
||||
const newAssignment = await prisma.assignment.create({
|
||||
data: {
|
||||
shift: {
|
||||
connect: {
|
||||
id: shift.id,
|
||||
|
||||
//check if publisher.familyMembers are also available and add them to the shift. ToDo: test case
|
||||
let familyMembers = availablePubsForTheShift.filter(p => p.familyHeadId && p.familyHeadId === rankedPublishers[0].familyHeadId);
|
||||
if (familyMembers.length > 0) {
|
||||
familyMembers.forEach(async familyMember => {
|
||||
if (shift.assignments.length < event.numberOfPublishers) {
|
||||
console.log("Assigning " + familyMember.firstName + " " + familyMember.lastName + " to " + shift.startDate.getDate() + " " + shift.name);
|
||||
const newAssignment = await prisma.assignment.create({
|
||||
data: {
|
||||
shift: {
|
||||
connect: {
|
||||
id: shift.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
publisher: {
|
||||
connect: {
|
||||
id: familyMember.id,
|
||||
publisher: {
|
||||
connect: {
|
||||
id: familyMember.id,
|
||||
},
|
||||
},
|
||||
isConfirmed: true,
|
||||
isBySystem: false,
|
||||
},
|
||||
isConfirmed: true,
|
||||
isBySystem: false,
|
||||
},
|
||||
});
|
||||
shift.assignments.push(newAssignment);
|
||||
publishersToday.push(familyMember.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
shift.assignments.push(newAssignment);
|
||||
publishersToday.push(familyMember.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -887,9 +891,6 @@ async function RankPublishersForShift(publishers) {
|
||||
const availabilityDifference = a.currentMonthAvailabilityHoursCount - b.currentMonthAvailabilityHoursCount;
|
||||
if (availabilityDifference !== 0) return availabilityDifference;
|
||||
|
||||
// biggest difference between desired and current assignments first (descending)
|
||||
const desiredCurrentDifference = b.DesiredMinusCurrent - a.DesiredMinusCurrent;
|
||||
if (desiredCurrentDifference !== 0) return desiredCurrentDifference;
|
||||
|
||||
// less assigned first (ascending)
|
||||
return a.currentMonthAssignments - b.currentMonthAssignments;
|
||||
@ -897,6 +898,52 @@ async function RankPublishersForShift(publishers) {
|
||||
|
||||
return ranked;
|
||||
}
|
||||
|
||||
async function RankPublishersForShiftWeighted(publishers) {
|
||||
// Define weights for each criterion
|
||||
const weights = {
|
||||
gender: 2,
|
||||
desiredCompletion: 3,
|
||||
availability: 2,
|
||||
lastMonthCompletion: 3,
|
||||
currentAssignments: 1
|
||||
};
|
||||
|
||||
// Normalize weights to ensure they sum to 1
|
||||
const totalWeight = Object.values(weights).reduce((acc, val) => acc + val, 0);
|
||||
Object.keys(weights).forEach(key => {
|
||||
weights[key] /= totalWeight;
|
||||
});
|
||||
|
||||
publishers.forEach(p => {
|
||||
p.lastMonthCompletion = p.previousMonthAssignments / p.currentMonthAssignments;
|
||||
p.desiredCompletion = p.currentMonthAssignments / p.desiredShiftsPerMonth;
|
||||
});
|
||||
|
||||
let ranked = publishers.sort((a, b) => {
|
||||
// Calculate weighted score for each publisher
|
||||
const scoreA = (a.isMale ? weights.gender : 0) -
|
||||
(a.desiredCompletion * weights.desiredCompletion) +
|
||||
((1 - a.currentMonthAvailabilityHoursCount / 24) * weights.availability) +
|
||||
(a.currentMonthAssignments * weights.lastMonthCompletion) -
|
||||
(a.currentMonthAssignments * weights.currentAssignments);
|
||||
|
||||
const scoreB = (b.isMale ? weights.gender : 0) -
|
||||
(b.desiredCompletion * weights.desiredCompletion) +
|
||||
((1 - b.currentMonthAvailabilityHoursCount / 24) * weights.availability) +
|
||||
(b.currentMonthAssignments * weights.lastMonthCompletion) -
|
||||
(b.currentMonthAssignments * weights.currentAssignments);
|
||||
|
||||
return scoreB - scoreA; // Sort descending by score
|
||||
});
|
||||
|
||||
return ranked;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function DeleteShiftsForMonth(monthInfo) {
|
||||
try {
|
||||
const prisma = common.getPrismaClient();
|
||||
|
Reference in New Issue
Block a user