initial commit - code moved to separate repo

This commit is contained in:
Dobromir Popov
2024-02-22 04:19:38 +02:00
commit 560d503219
240 changed files with 105125 additions and 0 deletions

688
pages/api/shiftgenerate.ts Normal file
View File

@ -0,0 +1,688 @@
//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 = [];
console.log("[shift " + shiftNr + "] " + __shiftName);
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(),
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.NEXTAUTH_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
// *********************************************************************************************************************