691 lines
32 KiB
TypeScript
691 lines
32 KiB
TypeScript
//import { getToken } from "next-auth/jwt";
|
||
|
||
import axiosServer from '../../src/axiosServer';
|
||
import { getToken } from "next-auth/jwt";
|
||
|
||
import type { NextApiRequest, NextApiResponse } from "next";
|
||
import { Prisma, PrismaClient, DayOfWeek, Publisher, Shift } from "@prisma/client";
|
||
import { levenshteinEditDistance } from "levenshtein-edit-distance";
|
||
import { filterPublishers, /* other functions */ } from './index';
|
||
|
||
import CAL from "../../src/helpers/calendar";
|
||
//const common = require("@common");
|
||
import common from "../../src/helpers/common";
|
||
import { Axios } from 'axios';
|
||
|
||
export default handler;
|
||
|
||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||
console.log(req.url);
|
||
console.log(req.query);
|
||
const prisma = common.getPrismaClient();
|
||
|
||
// If you don't have the NEXTAUTH_SECRET environment variable set,
|
||
// you will have to pass your secret as `secret` to `getToken`
|
||
const axios = await axiosServer({ req: req, res: res });
|
||
const token = await getToken({ req: req });
|
||
if (!token) {
|
||
// If no token or invalid token, return unauthorized status
|
||
return res.status(401).json({ message: "Unauthorized" });
|
||
}
|
||
|
||
// const token = req.headers.authorization.split('Bearer ')[1]
|
||
// const { user } = await verify(token, process.env.NEXTAUTH_SECRET, {
|
||
// maxAge: 30 * 24 * 60 * 60, // 30 days
|
||
// })
|
||
// if (!user.roles.includes('admin')) {
|
||
// res.status(401).json({ message: 'Unauthorized' })
|
||
// return
|
||
// }
|
||
// // if (!user.role == "adminer") {
|
||
|
||
// if (token?.userRole !== "adminer") {
|
||
// res.status(401).json({ message: "Unauthorized" });
|
||
// console.log("not authorized");
|
||
// return;
|
||
// }
|
||
|
||
// var result = { error: "Not authorized" };
|
||
var action = req.query.action;
|
||
switch (action) {
|
||
case "generate":
|
||
var result = await GenerateSchedule(axios,
|
||
req.query.date?.toString() || common.getISODateOnly(new Date()),
|
||
common.parseBool(req.query.copyFromPreviousMonth),
|
||
common.parseBool(req.query.autoFill),
|
||
common.parseBool(req.query.forDay));
|
||
res.send(JSON.stringify(result.error?.toString()));
|
||
break;
|
||
case "delete":
|
||
result = await DeleteSchedule(axios, req.query.date, common.parseBool(req.query.forDay));
|
||
res.send("deleted"); // JSON.stringify(result, null, 2)
|
||
break;
|
||
case "createcalendarevent":
|
||
//CAL.GenerateICS();
|
||
result = await CreateCalendarForUser(req.query.id);
|
||
res.send(result); // JSON.stringify(result, null, 2)
|
||
break;
|
||
case "test":
|
||
var data = prisma.shift.findMany({
|
||
where: {
|
||
isActive: true
|
||
}
|
||
});
|
||
|
||
res.send({
|
||
action: "OK",
|
||
shifts: data,
|
||
locations: prisma.location.findMany({
|
||
take: 10, // Limit the number of records to 10
|
||
orderBy: {
|
||
name: 'asc' // Replace 'someField' with a field you want to sort by
|
||
},
|
||
})
|
||
});
|
||
break;
|
||
default:
|
||
res.send("Invalid action");
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
// handle /api/data/schedule?date=2021-08-01&time=08:00:00&duration=60&service=1&provider=1
|
||
//Fix bugs in this code:
|
||
async function GenerateSchedule(axios: Axios, date: string, copyFromPreviousMonth: boolean = false, autoFill: boolean = false, forDay: Boolean) {
|
||
let missingPublishers: any[] = [];
|
||
let publishersWithChangedPref: any[] = [];
|
||
|
||
const prisma = common.getPrismaClient();
|
||
try {
|
||
const monthInfo = common.getMonthDatesInfo(new Date(date));
|
||
const lastMonthInfo = common.getMonthDatesInfo(new Date(monthInfo.date.getFullYear(), monthInfo.date.getMonth() - 1, 1));
|
||
//delete all shifts for this month
|
||
if (forDay) {
|
||
// Delete shifts only for the specific day
|
||
await DeleteShiftsForDay(monthInfo.date);
|
||
} else {
|
||
// Delete all shifts for the entire month
|
||
await DeleteShiftsForMonth(monthInfo);
|
||
}
|
||
|
||
console.log("finding shifts for previous 3 months for statistics (between " + new Date(monthInfo.date.getFullYear(), monthInfo.date.getMonth() - 3, 1).toISOString() + " and " + monthInfo.firstDay.toISOString() + ")");
|
||
const { data: events } = await axios.get(`/api/data/cartevents?where={"isActive":{"$eq":true}}`);
|
||
|
||
//// let [shiftsLastMonth, publishers] = await getShiftsAndPublishersForPreviousMonths(lastMonthInfo);
|
||
//use filterPublishers from /pages/api/data/index.ts to get publishers with stats
|
||
let shiftsLastMonth = await getShiftsFromLastMonth(lastMonthInfo);
|
||
let publishers = await filterPublishers("id,firstName,lastName", null, lastMonthInfo.firstMonday, true, true, false);
|
||
|
||
|
||
|
||
//let publishersWithStatsNew = await filterPublishers("id,firstName,lastName", null, monthInfo.firstMonday, true, true, false);
|
||
//foreach day of the month check if there is an event for this day
|
||
//if there is an event, then generate shifts for this day based on shiftduration and event start and end time
|
||
//####################################################GPT###########################################################
|
||
|
||
let shiftAssignments = [];
|
||
let day = monthInfo.firstMonday; // Start from forDay if provided, otherwise start from first Monday
|
||
let endDate = monthInfo.lastSunday; // End at forDay + 1 day if provided, otherwise end at last Sunday
|
||
let dayNr = 1; // Start from the day number of forDay, or 1 for the entire month
|
||
let weekNr = 1; // Start from the week number of forDay, or 1 for the entire month
|
||
|
||
|
||
if (forDay) {
|
||
day = monthInfo.date;
|
||
endDate.setDate(monthInfo.date.getDate() + 1);
|
||
dayNr = monthInfo.date.getDate();
|
||
weekNr = common.getWeekNumber(monthInfo.date);
|
||
}
|
||
|
||
let publishersThisWeek: any[] = [];
|
||
|
||
console.log("\r\n");
|
||
console.log("###############################################");
|
||
console.log(" SHIFT GENERATION STARTED for " + common.getISODateOnly(monthInfo.date));
|
||
console.log("###############################################");
|
||
|
||
while (day < endDate) {
|
||
const dayOfM = day.getDate();
|
||
let dayName = common.DaysOfWeekArray[day.getDayEuropean()];
|
||
console.log("[day " + dayNr + "] " + dayName + " " + dayOfM);
|
||
//ToDo: rename event to cartEvent
|
||
const event = events.find((event: { dayofweek: string }) => {
|
||
return event.dayofweek == dayName;
|
||
});
|
||
if (!event) {
|
||
console.log("no event for " + dayName);
|
||
day.setDate(day.getDate() + 1);
|
||
continue;
|
||
}
|
||
|
||
event.startTime = new Date(event.startTime);
|
||
event.endTime = new Date(event.endTime);
|
||
|
||
var startTime = new Date(day);
|
||
startTime.setHours(event.startTime.getHours());
|
||
startTime.setMinutes(event.startTime.getMinutes());
|
||
var endTime = new Date(day);
|
||
endTime.setHours(event.endTime.getHours());
|
||
endTime.setMinutes(event.endTime.getMinutes());
|
||
|
||
var shiftStart = new Date(startTime);
|
||
var shiftEnd = new Date(startTime);
|
||
shiftEnd.setMinutes(shiftStart.getMinutes() + event.shiftDuration);
|
||
|
||
var shiftNr = 0;
|
||
while (shiftEnd <= endTime) {
|
||
shiftNr++;
|
||
const __shiftName = String(shiftStart.getHours()).padStart(2, "0") + ":" + String(shiftStart.getMinutes()).padStart(2, "0") + " - " + String(shiftEnd.getHours()).padStart(2, "0") + ":" + String(shiftEnd.getMinutes()).padStart(2, "0");
|
||
shiftAssignments = [];
|
||
let isTransportRequired = shiftNr == 1 || shiftEnd.getTime() == endTime.getTime();
|
||
console.log("[shift " + shiftNr + "] " + __shiftName + ", transport: " + (isTransportRequired ? "yes" : "no") + ", " + shiftStart.toLocaleTimeString() + " - " + shiftEnd.toLocaleTimeString() + " (end time: " + endTime.toLocaleTimeString() + ", " + event.shiftDuration + " min)");
|
||
|
||
if (autoFill || copyFromPreviousMonth) {
|
||
// ###########################################
|
||
// shift cache !!!
|
||
// ###########################################
|
||
|
||
// get last month attendance for this shift for each week, same day of the week and same shift
|
||
const shiftLastMonthSameDay = getShiftFromLastMonth(shiftsLastMonth, day, weekNr, shiftNr);
|
||
if (shiftLastMonthSameDay) {
|
||
console.log("shiftCache: loaded shifts from '" + shiftLastMonthSameDay.startTime + "' for: " + day);
|
||
//log shiftLastMonthSameDay.assignments.publisher names
|
||
console.log("last month attendance for shift " + shiftNr + " (" + __shiftName + ") : " + shiftLastMonthSameDay.assignments.map((a: { publisher: { firstName: string; lastName: string; }; }) => a.publisher.firstName + " " + a.publisher.lastName).join(", "));
|
||
|
||
for (var i = 0; i < shiftLastMonthSameDay.assignments.length; i++) {
|
||
let sameP = shiftLastMonthSameDay.assignments[i].publisher;
|
||
let name = sameP.firstName + " " + sameP.lastName;
|
||
console.log("shiftCache: considerig publisher: " + sameP.firstName + " " + sameP.lastName + ". Checking if he is available for this shift...");
|
||
//get availability for the same dayofweek and time (< startTime, > endTime) OR exact date (< startTime, > endTime)
|
||
|
||
// Query for exact date match
|
||
let availability = (await prisma.availability.findMany({
|
||
where: {
|
||
publisherId: sameP.id,
|
||
dayOfMonth: dayOfM,
|
||
startTime: {
|
||
lte: shiftStart,
|
||
},
|
||
endTime: {
|
||
gte: shiftEnd,
|
||
},
|
||
},
|
||
}))[0] || null;
|
||
|
||
if (copyFromPreviousMonth) {
|
||
//copy from previous month without checking availability
|
||
console.log("shiftCache: copy from previous month. Аvailability is " + (availability ? "available" : "not available")
|
||
+ ". Adding him to the new scedule as " + (availability ? "confirmed" : "tentative") + ".");
|
||
shiftAssignments.push({ publisherId: sameP.id, isConfirmed: availability ? false : true });
|
||
|
||
} else {
|
||
// check if the person filled the form this month
|
||
const allAvailabilities = await prisma.availability.findMany({
|
||
where: {
|
||
publisherId: sameP.id,
|
||
isFromPreviousAssignment: false,
|
||
},
|
||
});
|
||
// // ?? get the date on the same weeknr and dayofweek last month, and check if there is an availability for the same day of the week and required time
|
||
// if (!availability) {
|
||
// // check if there is an availability for the same day of the week and required time
|
||
// availability = allAvailabilities.filter((a: { dayofweek: any; startTime: Date; endTime: Date; }) => {
|
||
// return a.dayofweek === event.dayofweek && a.startTime <= startTime && a.endTime >= endTime;
|
||
// })[0] || null;
|
||
// }
|
||
|
||
// var availability = allAvailabilities.find((a) => {
|
||
// return (a.dayofweek === event.dayofweek && a.dayOfMonth == null) || a.dayOfMonth == dayOfM;
|
||
// });
|
||
//publishers not filled the form will not have an email with @, but rather as 'firstname.lastname'.
|
||
//We will add them to the schedule as manual override until they fill the form
|
||
//ToDo this logic is not valid in all cases.
|
||
if (!availability && sameP.email.includes("@")) {
|
||
if (!publishersWithChangedPref.includes(name)) {
|
||
//publishersWithChangedPref.push(name);
|
||
}
|
||
console.log("shiftCache: publisher is not available for this shift. Available days: " + allAvailabilities.filter((a: { dayOfMonth: any; }) => a.dayOfMonth === dayOfM).map((a) => a.dayofweek + " " + a.dayOfMonth).join(", "));
|
||
//continue;
|
||
}
|
||
if (availability) {
|
||
console.log("shiftCache: publisher is available for this shift. Available days: " + availability.dayofweek + " " + availability.dayOfMonth + " " + availability.startTime + " - " + availability.endTime);
|
||
|
||
console.log("shiftCache: publisher is available for this shift OR manual override is set. Adding him to the new scedule.");
|
||
shiftAssignments.push({ publisherId: sameP.id });
|
||
}
|
||
else {
|
||
// skip publishers without availability now
|
||
// console.warn("NO publisher availability found! for previous assignment for " + name + ". Assuming he does not have changes in his availability. !!! ADD !!! him to the new scedule but mark him as missing.");
|
||
// if (!missingPublishers.includes(name)) {
|
||
// missingPublishers.push(name);
|
||
// }
|
||
// try {
|
||
// console.log("shiftCache: publisher was last month assigned to this shift but he is not in the system. Adding him to the system with id: " + sameP.id);
|
||
// shiftAssignments.push({ publisherId: sameP.id, });
|
||
// } catch (e) {
|
||
// console.error(`shiftCache: error adding MANUAL publisher to the system(${sameP.email} ${sameP.firstName} ${sameP.lastName}): ` + e);
|
||
// }
|
||
}
|
||
}
|
||
}
|
||
|
||
// ###########################################
|
||
// shift CACHE END
|
||
// ###########################################
|
||
|
||
console.log("searching available publisher for " + dayName + " " + __shiftName);
|
||
|
||
if (!copyFromPreviousMonth) {
|
||
|
||
/* We chave the following data:
|
||
availabilities:(6) [{…}, {…}, {…}, {…}, {…}, {…}]
|
||
currentDayAssignments:0
|
||
currentMonthAssignments:2
|
||
currentMonthAvailability:(2) [{…}, {…}]
|
||
currentMonthAvailabilityDaysCount:2
|
||
currentMonthAvailabilityHoursCount:3
|
||
currentWeekAssignments:0
|
||
firstName:'Алесия'
|
||
id:'clqjtcrqj0008oio8kan5lkjn'
|
||
lastName:'Сейз'
|
||
previousMonthAssignments:2
|
||
*/
|
||
|
||
// until we reach event.numberOfPublishers, we will try to fill the shift with publishers from allAvailablePublishers with the following priority:
|
||
// do multiple passes, reecalculating availabilityIndex for each publisher after each pass.
|
||
// !!! Never assign the same publisher twice to the same day! (currentDayAssignments > 0)
|
||
// PASS 1: Prioritize publishers with little currentMonthAvailabilityHoursCount ( < 5 ), as they may not have another opportunity to serve this month
|
||
// PASS 2: try to fill normally based on availabilityIndex, excluding those who were assigned this week
|
||
// PASS 3: try to fill normally based on availabilityIndex, including those who were assigned this week and weighting the desiredShiftsPerMonth
|
||
// PASS 4: include those without availability this month - based on old availabilities and assignments for this day of the week.
|
||
// push found publisers to shiftAssignments with: .push({ publisherId: publisher.id }); and update publisher stats in new function: addAssignmentToPublisher(shiftAssignments, publisher)
|
||
|
||
// ---------------------------------- new code ---------------------------------- //
|
||
// get all publishers who are available for this SPECIFIC day and WEEKDAY
|
||
const queryParams = new URLSearchParams({
|
||
action: 'filterPublishers',
|
||
assignments: 'true',
|
||
availabilities: 'true',
|
||
date: common.getISODateOnly(shiftStart),
|
||
select: 'id,firstName,lastName,isActive,desiredShiftsPerMonth'
|
||
});
|
||
let allAvailablePublishers = (await axios.get(`/api/?${queryParams.toString()}`)).data;
|
||
let availablePublishers = allAvailablePublishers;
|
||
let publishersNeeded = Math.max(0, event.numberOfPublishers - shiftAssignments.length);
|
||
|
||
|
||
// LEVEL 1: Prioritize publishers with little currentMonthAvailabilityHoursCount ( < 5 ), as they may not have another opportunity to serve this month
|
||
// get publishers with little currentMonthAvailabilityHoursCount ( < 5 )
|
||
// let availablePublishers = allAvailablePublishers.filter((p: { currentMonthAvailabilityHoursCount: number; }) => p.currentMonthAvailabilityHoursCount < 5);
|
||
|
||
// // log all available publishers with their currentMonthAvailabilityHoursCount
|
||
// console.info("PASS 1: availablePublishers for this shift with currentMonthAvailabilityHoursCount < 5: " + availablePublishers.length + " (" + publishersNeeded + " needed)");
|
||
|
||
// availablePublishers.slice(0, publishersNeeded).forEach((p: { id: any; }) => { addAssignmentToPublisher(shiftAssignments, p); });
|
||
// publishersNeeded = Math.max(0, event.numberOfPublishers - shiftAssignments.length);
|
||
|
||
// LEVEL 2+3: try to fill normally based on availabilityIndex, excluding those who were assigned this week
|
||
// get candidates that are not assigned this week, and which have not been assigned this month as mutch as the last month.
|
||
// calculate availabilityIndex for each publisher based on various factors:
|
||
// 1. currentMonthAssignments - lastMonth (weight 50%)
|
||
// 2. desiredShiftsPerMonth (weight 30%)
|
||
// 3. publisher type (weight 20%) - regular, auxiliary, pioneer, special, bethel, etc.. (see publisherType in publisher model). exclude betelites who were assigned this month. (index =)
|
||
|
||
//calculate availabilityIndex:
|
||
allAvailablePublishers.forEach((p: { currentMonthAssignments: number; desiredShiftsPerMonth: number; publisherType: string; }) => {
|
||
// 1. currentMonthAssignments - lastMonth (weight 50%)
|
||
// 2. desiredShiftsPerMonth (weight 30%)
|
||
// 3. publisher type (weight 20%) - regular, auxiliary, pioneer, special, bethel, etc.. (see publisherType in publisher model). exclude betelites who were assigned this month. (index =)
|
||
p.availabilityIndex = Math.round(((p.currentMonthAssignments - p.previousMonthAssignments) * 0.5 + p.desiredShiftsPerMonth * 0.3 + (p.publisherType === "bethelite" ? 0 : 1) * 0.2) * 100) / 100;
|
||
});
|
||
|
||
// use the availabilityIndex to sort the publishers
|
||
// LEVEL 2: remove those who are already assigned this week (currentWeekAssignments > 0), order by !availabilityIndex
|
||
availablePublishers = allAvailablePublishers.filter((p: { currentWeekAssignments: number; }) => p.currentWeekAssignments === 0)
|
||
.sort((a: { availabilityIndex: number; }, b: { availabilityIndex: number; }) => a.availabilityIndex - b.availabilityIndex);
|
||
console.warn("PASS 2: availablePublishers for this shift after removing already assigned this week: " + availablePublishers.length + " (" + publishersNeeded + " needed)");
|
||
availablePublishers.slice(0, publishersNeeded).forEach((p: { id: any; }) => { addAssignmentToPublisher(shiftAssignments, p); });
|
||
publishersNeeded = Math.max(0, event.numberOfPublishers - shiftAssignments.length);
|
||
|
||
// LEVEL 3: order by !availabilityIndex
|
||
availablePublishers = allAvailablePublishers.sort((a: { availabilityIndex: number; }, b: { availabilityIndex: number; }) => a.availabilityIndex - b.availabilityIndex);
|
||
console.warn("PASS 3: availablePublishers for this shift including already assigned this week: " + availablePublishers.length + " (" + publishersNeeded + " needed)");
|
||
availablePublishers.slice(0, publishersNeeded).forEach((p: { id: any; }) => { addAssignmentToPublisher(shiftAssignments, p); });
|
||
publishersNeeded = Math.max(0, event.numberOfPublishers - shiftAssignments.length);
|
||
|
||
// LEVEL 4: include those without availability this month - based on old availabilities and assignments for this day of the week.
|
||
// get candidates that are not assigned this week, and which have not been assigned this month as mutch as the last month.
|
||
//query the api again for all publishers with assignments and availabilities for this day of the week including from old assignments (set filterPublishers to false)
|
||
availablePublishers = await filterPublishers("id,firstName,lastName", null, shiftStart, false, true, true);
|
||
console.warn("PASS 4: availablePublishers for this shift including weekly and old assignments: " + availablePublishers.length + " (" + publishersNeeded + " needed)");
|
||
|
||
|
||
|
||
function oldCode() {
|
||
// ---------------------------------- old code ---------------------------------- //
|
||
// console.warn("allAvailablePublishers: " + allAvailablePublishers.length);
|
||
// // remove those who are already assigned this week (currentWeekAssignments > 0)//, # OLD: order by !availabilityIndex
|
||
// let availablePublishers = allAvailablePublishers.filter((p: { currentWeekAssignments: number; }) => p.currentWeekAssignments === 0);
|
||
|
||
// console.warn("availablePublishers for this shift after removing already assigned this week: " + availablePublishers.length + " (" + (event.numberOfPublishers - shiftAssignments.length) + " needed)");
|
||
|
||
// if (availablePublishers.length === 0) {
|
||
// console.error(`------------------- no available publishers for ${dayName} ${dayOfM}!!! -------------------`);
|
||
// // Skipping the rest of the code execution
|
||
// //return;
|
||
// }
|
||
|
||
// let msg = `FOUND ${availablePublishers.length} publishers for ${dayName} ${dayOfM}, ${__shiftName} . ${event.numberOfPublishers - shiftAssignments.length} needed\r\n: `;
|
||
// msg += availablePublishers.map((p: { firstName: any; lastName: any; asignmentsThisMonth: any; availabilityIndex: any; }) => `${p.firstName} ${p.lastName} (${p.asignmentsThisMonth}:${p.availabilityIndex})`).join(", ");
|
||
// console.log(msg);
|
||
|
||
// // ---------------------------------- old code ---------------------------------- //
|
||
} // end of old code
|
||
}
|
||
}
|
||
}
|
||
//###############################################################################################################
|
||
// create shift assignmens
|
||
//###############################################################################################################
|
||
// using prisma client:
|
||
// https://stackoverflow.com/questions/65950407/prisma-many-to-many-relations-create-and-connect
|
||
// connect publishers to shift
|
||
const createdShift = await prisma.shift.create({
|
||
data: {
|
||
startTime: shiftStart,
|
||
endTime: shiftEnd,
|
||
name: event.dayofweek + " " + shiftStart.toLocaleTimeString() + " - " + shiftEnd.toLocaleTimeString(),
|
||
requiresTransport: isTransportRequired,
|
||
cartEvent: {
|
||
connect: {
|
||
id: event.id,
|
||
},
|
||
},
|
||
assignments: {
|
||
create: shiftAssignments.map((a) => {
|
||
return { publisher: { connect: { id: a.publisherId } }, isConfirmed: a.isConfirmed };
|
||
}),
|
||
},
|
||
},
|
||
});
|
||
|
||
|
||
shiftStart = new Date(shiftEnd);
|
||
shiftEnd.setMinutes(shiftStart.getMinutes() + event.shiftDuration);
|
||
}
|
||
|
||
day.setDate(day.getDate() + 1);
|
||
dayNr++;
|
||
let weekDay = common.DaysOfWeekArray[day.getDayEuropean()]
|
||
if (weekDay == DayOfWeek.Sunday) {
|
||
weekNr++;
|
||
publishersThisWeek = [];
|
||
publishers.forEach((p: { currentWeekAssignments: number; }) => {
|
||
p.currentWeekAssignments = 0;
|
||
});
|
||
}
|
||
//the whole day is done, go to next day. break if we are generating for a specific day
|
||
if (forDay) {
|
||
break;
|
||
}
|
||
}
|
||
//###################################################GPT############################################################
|
||
if (!forDay) {
|
||
const fs = require("fs");
|
||
|
||
//fs.writeFileSync("./content/publisherShiftStats.json", JSON.stringify(publishers, null, 2));
|
||
//fs.writeFileSync("./content/publishersWithChangedPref.json", JSON.stringify(publishersWithChangedPref, null, 2));
|
||
//fs.writeFileSync("./content/missingPublishers.json", JSON.stringify(missingPublishers, null, 2));
|
||
|
||
console.log("###############################################");
|
||
console.log(" DONE CREATING SCHEDULE FOR " + monthInfo.monthName + " " + monthInfo.year);
|
||
console.log("###############################################");
|
||
}
|
||
|
||
//create shifts using API
|
||
// const { data: createdShifts } = await axios.post(`${process.env.NEXT_PUBLIC_PUBLIC_URL}/api/data/shifts`, shiftsToCreate);
|
||
//const { data: allshifts } = await axios.get(`/api/data/shifts`);
|
||
return {}; //allshifts;
|
||
|
||
}
|
||
catch (error) {
|
||
console.log(error);
|
||
return { error: error };
|
||
}
|
||
}
|
||
|
||
function addAssignmentToPublisher(shiftAssignments: any[], publisher: Publisher) {
|
||
shiftAssignments.push({ publisherId: publisher.id });
|
||
publisher.currentWeekAssignments++ || 1;
|
||
publisher.currentDayAssignments++ || 1;
|
||
publisher.currentMonthAssignments++ || 1;
|
||
//console.log(`manual assignment: ${dayName} ${dayOfM} ${shiftStart}:${shiftEnd} ${p.firstName} ${p.lastName} ${p.availabilityIndex} ${p.currentMonthAssignments}`);
|
||
console.log(`manual assignment: ${publisher.firstName} ${publisher.lastName} ${publisher.currentMonthAssignments}`);
|
||
return publisher;
|
||
}
|
||
|
||
async function DeleteShiftsForMonth(monthInfo: any) {
|
||
try {
|
||
const prisma = common.getPrismaClient();
|
||
await prisma.shift.deleteMany({
|
||
where: {
|
||
startTime: {
|
||
gte: monthInfo.firstMonday,
|
||
lt: monthInfo.lastSunday,
|
||
},
|
||
},
|
||
});
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
|
||
async function DeleteShiftsForDay(date: Date) {
|
||
const prisma = common.getPrismaClient();
|
||
try {
|
||
// Assuming shifts do not span multiple days, so equality comparison is used
|
||
await prisma.shift.deleteMany({
|
||
where: {
|
||
startTime: {
|
||
gte: date,
|
||
lt: new Date(date.getTime() + 86400000), // +1 day in milliseconds
|
||
},
|
||
},
|
||
});
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
|
||
|
||
async function getShiftsFromLastMonth(monthInfo) {
|
||
const prisma = common.getPrismaClient();
|
||
// Fetch shifts for the month
|
||
const rawShifts = await prisma.shift.findMany({
|
||
where: {
|
||
startTime: {
|
||
gte: monthInfo.firstMonday,
|
||
lte: monthInfo.lastSunday,
|
||
},
|
||
},
|
||
include: {
|
||
assignments: {
|
||
include: {
|
||
publisher: true,
|
||
},
|
||
},
|
||
},
|
||
});
|
||
|
||
// Process shifts to add weekNr and shiftNr
|
||
return rawShifts.map(shift => ({
|
||
...shift,
|
||
weekNr: common.getWeekNumber(new Date(shift.startTime)),
|
||
shiftNr: rawShifts.filter(s => common.getISODateOnly(s.startTime) === common.getISODateOnly(shift.startTime)).indexOf(shift) + 1,
|
||
weekDay: common.DaysOfWeekArray[new Date(shift.startTime).getDayEuropean()],
|
||
}));
|
||
}
|
||
|
||
function getShiftFromLastMonth(shiftsLastMonth, day, weekNr, shiftNr) {
|
||
let weekDay = common.DaysOfWeekArray[day.getDayEuropean()];
|
||
return shiftsLastMonth.find(s => {
|
||
return s.weekNr === weekNr &&
|
||
s.shiftNr === shiftNr &&
|
||
s.weekDay === weekDay;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Dangerous function that deletes all shifts and publishers.
|
||
* @param date
|
||
* @returns
|
||
*/
|
||
async function DeleteSchedule(axios: Axios, date: Date, forDay: Boolean | undefined) {
|
||
try {
|
||
let monthInfo = common.getMonthDatesInfo(new Date(date));
|
||
if (forDay) {
|
||
// Delete shifts only for the specific day
|
||
await DeleteShiftsForDay(monthInfo.date);
|
||
} else {
|
||
// Delete all shifts for the entire month
|
||
await DeleteShiftsForMonth(monthInfo);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.log(error);
|
||
return { error: error };
|
||
}
|
||
}
|
||
|
||
async function CreateCalendarForUser(eventId: string | string[] | undefined) {
|
||
try {
|
||
//CAL.authorizeNew();
|
||
CAL.createEvent(eventId);
|
||
} catch (error) {
|
||
console.log(error);
|
||
return { error: error };
|
||
}
|
||
}
|
||
|
||
/*
|
||
obsolete?
|
||
*/
|
||
async function ImportShiftsFromDocx(axios: Axios) {
|
||
try {
|
||
const { data: shifts } = await axios.get(`/api/data/shifts`);
|
||
shifts.forEach(async (shift: { id: any; }) => {
|
||
await axios.delete(`/api/data/shifts/${shift.id}`);
|
||
});
|
||
const { data: shiftsToCreate } = await axios.get(`/api/data/shiftsToCreate`);
|
||
shiftsToCreate.forEach(async (shift: any) => {
|
||
await axios.post(`/api/data/shifts`, shift);
|
||
});
|
||
} catch (error) {
|
||
console.log(error);
|
||
return { error: error };
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Retrieves shifts and publishers for the previous months based on the given month information.
|
||
* @deprecated This function is deprecated and will be removed in future versions. Use `filterPublishers` from `/pages/api/data/index.ts` instead.
|
||
* @param monthInfo - An object containing information about the last month, including its first day and last Sunday.
|
||
* @returns A Promise that resolves to an array containing the publishers for the previous months.
|
||
*/
|
||
// async function getShiftsAndPublishersForPreviousMonths(monthInfo: { firstDay: any; lastSunday: any; firstMonday: any; nrOfWeeks: number; }) {
|
||
// const prisma = common.getPrismaClient(); //old: (global as any).prisma;
|
||
|
||
|
||
// const [shiftsLastMonth, initialPublishers] = await Promise.all([
|
||
// prisma.shift.findMany({
|
||
// where: {
|
||
// startTime: {
|
||
// gte: monthInfo.firstDay,
|
||
// lte: monthInfo.lastSunday,
|
||
// },
|
||
// },
|
||
// include: {
|
||
// assignments: {
|
||
// include: {
|
||
// publisher: true,
|
||
// },
|
||
// },
|
||
// },
|
||
// }),
|
||
|
||
// prisma.publisher.findMany({
|
||
// where: {
|
||
// isActive: true,
|
||
// },
|
||
// include: {
|
||
// availabilities: {
|
||
// where: {
|
||
// isActive: true,
|
||
// },
|
||
// },
|
||
// assignments: {
|
||
// include: {
|
||
// shift: true,
|
||
// },
|
||
// },
|
||
// },
|
||
// }),
|
||
// ]);
|
||
|
||
// // Group shifts by day
|
||
// function getDayFromDate(date: Date) {
|
||
// return date.toISO String().split('T')[0];
|
||
// }
|
||
// const groupedShifts = shiftsLastMonth.reduce((acc: { [x: string]: any[]; }, shift: { startTime: string | number | Date; }) => {
|
||
// const day = getDayFromDate(new Date(shift.startTime));
|
||
// if (!acc[day]) {
|
||
// acc[day] = [];
|
||
// }
|
||
// acc[day].push(shift);
|
||
// return acc;
|
||
// }, {});
|
||
|
||
// //temp fix - calculate shift.weekNr
|
||
// const updatedShiftsLastMonth = [];
|
||
// for (const day in groupedShifts) {
|
||
// const shifts = groupedShifts[day];
|
||
// for (let i = 0; i < shifts.length; i++) {
|
||
// const shift = shifts[i];
|
||
// updatedShiftsLastMonth.push({
|
||
// ...shift,
|
||
// weekNr: common.getWeekNumber(shift.startTime) + 1,
|
||
// shiftNr: i + 1 // The shift number for the day starts from 1
|
||
// });
|
||
// }
|
||
// }
|
||
// const publishers = initialPublishers.map((publisher: { assignments: any[]; desiredShiftsPerMonth: number; }) => {
|
||
// // const lastMonthStartDate = new Date(date.getFullYear(), date.getMonth() - 1, 1);
|
||
// // const last2MonthsStartDate = new Date(date.getFullYear(), date.getMonth() - 2, 1);
|
||
|
||
// const filterAssignmentsByDate = (startDate: any, endDate: any) =>
|
||
// publisher.assignments.filter((assignment: { shift: { startTime: string | number | Date; }; }) => isDateBetween(new Date(assignment.shift.startTime), startDate, endDate));
|
||
|
||
// const lastMonthAssignments = filterAssignmentsByDate(monthInfo.firstMonday, monthInfo.lastSunday);
|
||
// //const last2MonthsAssignments = filterAssignmentsByDate(last2MonthsStartDate, monthInfo.firstMonday);
|
||
|
||
// const desiredShifts = publisher.desiredShiftsPerMonth * (monthInfo.nrOfWeeks / 4);
|
||
// const availabilityIndex = Math.round((lastMonthAssignments.length / desiredShifts) * 100) / 100;
|
||
|
||
// return {
|
||
// ...publisher,
|
||
// availabilityIndex,
|
||
// currentWeekAssignments: 0,
|
||
// currentMonthAssignments: 0,
|
||
// assignmentsLastMonth: lastMonthAssignments.length,
|
||
// //assignmentsLast2Months: last2MonthsAssignments.length,
|
||
// };
|
||
// });
|
||
|
||
// return [updatedShiftsLastMonth, publishers];
|
||
// }
|
||
|
||
// *********************************************************************************************************************
|
||
//region helpers
|
||
// *********************************************************************************************************************
|