brand new schedule auto generation
This commit is contained in:
@ -363,7 +363,11 @@ exports.getDateFormated = function (date) {
|
||||
return `${dayOfWeekName} ${day} ${monthName} ${year} г.`;
|
||||
}
|
||||
|
||||
exports.getDateFormatedShort = function (date) {
|
||||
exports.getDateFormattedShort = function (date) {
|
||||
if (!date) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const day = date.getDate();
|
||||
const monthName = exports.getMonthName(date.getMonth());
|
||||
return `${day} ${monthName}`;
|
||||
@ -592,8 +596,13 @@ exports.setTime = (baseDateTime, timeDateTime) => {
|
||||
});
|
||||
};
|
||||
|
||||
exports.timeToInteger = (hours, minutes) => {
|
||||
return hours * 100 + minutes;
|
||||
}
|
||||
|
||||
// Format date to a specified format, defaulting to 'HH:mm'
|
||||
exports.getTimeFormatted = (input, format = 'HH:mm') => {
|
||||
if (!input) return "";
|
||||
const dateTime = parseDate(input);
|
||||
return dateTime.toFormat(format);
|
||||
};
|
||||
@ -695,6 +704,16 @@ exports.parseBool = function (value) {
|
||||
return truthyValues.includes(String(value).toLowerCase());
|
||||
}
|
||||
|
||||
exports.getStartOfDay = function (date) {
|
||||
const result = new Date(date); // create a copy of the input date
|
||||
result.setHours(0, 0, 0, 0); // set time to midnight
|
||||
return result;
|
||||
}
|
||||
exports.getEndOfDay = function (date) {
|
||||
const result = new Date(date);
|
||||
result.setHours(23, 59, 59, 999); // set time to the last millisecond of the day
|
||||
return result;
|
||||
}
|
||||
|
||||
exports.getStartOfWeek = function (date) {
|
||||
const result = new Date(date); // create a copy of the input date
|
||||
|
@ -1,5 +1,9 @@
|
||||
|
||||
const common = require('./common');
|
||||
// const { Prisma, PrismaClient, Publisher, Shift, DayOfWeek } = require("@prisma/client");
|
||||
// or
|
||||
const DayOfWeek = require("@prisma/client").DayOfWeek;
|
||||
|
||||
|
||||
async function findPublisher(names, email, select, getAll = false) {
|
||||
// Normalize and split the name if provided
|
||||
@ -78,53 +82,28 @@ async function findPublisher(names, email, select, getAll = false) {
|
||||
}
|
||||
}
|
||||
|
||||
async function findPublisherAvailability(publisherId, date) {
|
||||
const prisma = common.getPrismaClient();
|
||||
date = new Date(date); // Convert to date object if not already
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
|
||||
const potentialAvailabilities = await prisma.availability.findMany({
|
||||
where: {
|
||||
publisherId: publisherId,
|
||||
AND: [ // Ensure both conditions must be met
|
||||
{
|
||||
startTime: {
|
||||
lte: new Date(date), // startTime is less than or equal to the date
|
||||
},
|
||||
},
|
||||
{
|
||||
endTime: {
|
||||
gte: new Date(date), // endTime is greater than or equal to the date
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
});
|
||||
//# new - to verify
|
||||
// should be equivalent to the following prisma filer
|
||||
// whereClause["availabilities"] = {
|
||||
// some: {
|
||||
// OR: [
|
||||
// // Check if dayOfMonth is set and filterDate is between start and end dates (Assignments on specific days AND time)
|
||||
// {
|
||||
// dayOfMonth: filterDate.getDate(),
|
||||
// startTime: { lte: filterDate },
|
||||
// endTime: { gte: filterDate }
|
||||
// },
|
||||
// // Check if dayOfMonth is null and match by day of week using the enum (Assigments every week)
|
||||
// {
|
||||
// dayOfMonth: null,
|
||||
// dayofweek: dayOfWeekEnum,
|
||||
// startTime: { gte: filterDate },
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// };
|
||||
|
||||
if (potentialAvailabilities.length === 0) {
|
||||
return null; // No availability found
|
||||
}
|
||||
// Filter the results based on time and other criteria when not exact date match
|
||||
const availability = potentialAvailabilities.find(avail => {
|
||||
const availStartHours = avail.startTime.getHours();
|
||||
const availStartMinutes = avail.startTime.getMinutes();
|
||||
const availEndHours = avail.endTime.getHours();
|
||||
const availEndMinutes = avail.endTime.getMinutes();
|
||||
|
||||
const isAfterStartTime = hours > availStartHours || (hours === availStartHours && minutes >= availStartMinutes);
|
||||
const isBeforeEndTime = hours < availEndHours || (hours === availEndHours && minutes <= availEndMinutes);
|
||||
// check day of week if not null
|
||||
const isCorrectDayOfWeek = avail.repeatWeekly ? avail.startTime.getDay() === date.getDay() : true;
|
||||
const isExactDateMatch = avail.dayOfMonth ? avail.startTime.toDateString() === date.toDateString() : true;
|
||||
const isBeforeEndDate = avail.repeatWeekly ? true : avail.endTime > date;
|
||||
//const isCorrectWeekOfMonth = avail.repeatWeekly ? true : avail.weekOfMonth === weekOfMonth;
|
||||
|
||||
return isAfterStartTime && isBeforeEndTime && isCorrectDayOfWeek && isExactDateMatch && isBeforeEndDate;
|
||||
});
|
||||
|
||||
return availability;
|
||||
}
|
||||
|
||||
|
||||
async function getAvailabilities(userId) {
|
||||
@ -227,7 +206,21 @@ async function getAvailabilities(userId) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters publishers based on various criteria including exact times, monthly duration,
|
||||
* and whether or not to include statistics about publishers' availabilities and assignments.
|
||||
* This function heavily relies on the `prisma` client to query and manipulate data related to publishers.
|
||||
*
|
||||
* @param {Array|string} selectFields - Fields to select from the publishers data. Can be an array of field names or a comma-separated string of field names.
|
||||
* @param {string|Date} filterDate - The reference date for filtering. Can be a date string or a Date object. Used to determine relevant time frames like current month, previous month, etc.
|
||||
* @param {boolean} [isExactTime=false] - If true, filters publishers who are available at the exact time of `filterDate` plus/minus a specific duration (e.g., 90 minutes).
|
||||
* @param {boolean} [isForTheMonth=false] - If true, adjusts the filtering to encompass the entire month based on `filterDate`.
|
||||
* @param {boolean} [noEndDateFilter=false] - If true, removes any filtering based on the end date of publishers' availabilities.
|
||||
* @param {boolean} [isWithStats=true] - If true, includes statistical data about publishers' availabilities and assignments in the output.
|
||||
* @param {boolean} [includeOldAvailabilities=false] - If true, includes publishers' previous availabilities in the calculations and output.
|
||||
*
|
||||
* @returns {Promise<Array>} Returns a promise that resolves to an array of publishers with filtered data according to the specified criteria.
|
||||
*/
|
||||
async function filterPublishersNew(selectFields, filterDate, isExactTime = false, isForTheMonth = false, noEndDateFilter = false, isWithStats = true, includeOldAvailabilities = false) {
|
||||
|
||||
const prisma = common.getPrismaClient();
|
||||
@ -347,7 +340,7 @@ async function filterPublishersNew(selectFields, filterDate, isExactTime = false
|
||||
}
|
||||
|
||||
console.log(`getting publishers for date: ${filterDate}, isExactTime: ${isExactTime}, isForTheMonth: ${isForTheMonth}`);
|
||||
console.log`whereClause: ${JSON.stringify(whereClause)}`
|
||||
//console.log`whereClause: ${JSON.stringify(whereClause)}`
|
||||
//include availabilities if flag is true
|
||||
let publishers = await prisma.publisher.findMany({
|
||||
where: whereClause,
|
||||
@ -357,8 +350,9 @@ async function filterPublishersNew(selectFields, filterDate, isExactTime = false
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`publishers: ${publishers.length}, WhereClause: ${JSON.stringify(whereClause)}`);
|
||||
///console.log(`publishers: ${publishers.length}, WhereClause: ${JSON.stringify(whereClause)}`);
|
||||
|
||||
// include repeating weekly availabilities. generate occurrences for the month
|
||||
// convert matching weekly availabilities to availabilities for the day to make further processing easier on the client.
|
||||
// we trust that the filtering was OK, so we use the dateFilter as date.
|
||||
publishers.forEach(pub => {
|
||||
@ -448,16 +442,16 @@ async function filterPublishersNew(selectFields, filterDate, isExactTime = false
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// ToDo: test case/unit test
|
||||
// ToDo: check and validate the filtering and calculations
|
||||
if (isExactTime) {
|
||||
//HERE WE FILTER by time for repeating availabilities. We can't do that if we don't have
|
||||
// whereClause["availabilities"].some.OR[1].startTime = { gte: filterTimeFrom };
|
||||
// whereClause["availabilities"].some.OR[1].endTime = { gte: filterTimeTo }
|
||||
publishers.forEach(pub => {
|
||||
pub.availabilities.filter(a => a.startTime > filterTimeFrom && a.endTime < filterTimeTo)
|
||||
pub.availabilities = pub.availabilities.filter(a => a.startTime <= filterTimeFrom && a.endTime >= filterTimeTo);
|
||||
});
|
||||
publishers.filter(pub => pub.availabilities.length > 0);
|
||||
publishers = publishers.filter(pub => pub.availabilities.length > 0);
|
||||
}
|
||||
|
||||
// if (isExactTime) {
|
||||
@ -472,10 +466,10 @@ async function filterPublishersNew(selectFields, filterDate, isExactTime = false
|
||||
}
|
||||
|
||||
//ToDo: refactor this function
|
||||
async function getAllPublishersWithStatistics(filterDate, noEndDateFilter = false) {
|
||||
async function getAllPublishersWithStatisticsMonth(filterDateDuringMonth, noEndDateFilter = false, includeOldAvailabilities = true) {
|
||||
|
||||
const prisma = common.getPrismaClient();
|
||||
const monthInfo = common.getMonthDatesInfo(new Date(filterDate));
|
||||
const monthInfo = common.getMonthDatesInfo(new Date(filterDateDuringMonth));
|
||||
const dateStr = new Date(monthInfo.firstMonday).toISOString().split('T')[0];
|
||||
|
||||
let publishers = await filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type', dateStr, false, true, noEndDateFilter, true, true);
|
||||
@ -787,6 +781,393 @@ async function getCoverMePublisherEmails(shiftId) {
|
||||
return { shift, availablePublishers: availablePublishers, subscribedPublishers };
|
||||
}
|
||||
|
||||
// ### COPIED TO shift api (++) ###
|
||||
|
||||
/** JSDoc
|
||||
* Generates a schedule.
|
||||
*
|
||||
0. generate shifts and assign publishers from the previous month if still available
|
||||
1. Make sure we always put people only when they are available.
|
||||
2. First provision one male or two females that are available for transport in the first and last shifts.
|
||||
3, Then gradually fill all other shifts with day by day troughout the whole month (monthInfo.firstMonday to .lastSunday) with first one, then two, then 3 and wherever possible more (up to CartEvent.numberOfPublishers number)
|
||||
4. Some publishers are available only at specific time (somoetimes only once) and other are more available. if people are available only for this time, prioritize them so they are not left behind.
|
||||
5. prioritize based on publisher's desiredShiftsPerMonth and previous months assignments.
|
||||
6. Idealy noone should be more than once a week. disqualify publishers already on a shift this week. only assign them if there are no other options and we have less than 3 publishers on a specific shift.
|
||||
*
|
||||
* @param {Axios} axios Axios instance for making requests.
|
||||
* @param {string} date The date for the schedule.
|
||||
* @param {boolean} [copyFromPreviousMonth=false] Whether to copy from the previous month.
|
||||
* @param {boolean} [autoFill=false] Whether to autofill data.
|
||||
* @param {boolean} forDay Specific day flag.
|
||||
*/
|
||||
async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, autoFill = false, forDay) {
|
||||
let missingPublishers = [];
|
||||
let publishersWithChangedPref = [];
|
||||
|
||||
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));
|
||||
|
||||
if (forDay) {
|
||||
await DeleteShiftsForDay(monthInfo.date);
|
||||
} else {
|
||||
await DeleteShiftsForMonth(monthInfo);
|
||||
}
|
||||
|
||||
const events = await prisma.cartEvent.findMany({
|
||||
where: {
|
||||
isActive: true
|
||||
}
|
||||
});
|
||||
let shiftsLastMonth = await getShiftsFromLastMonth(lastMonthInfo);
|
||||
let publishers = await getAllPublishersWithStatisticsMonth('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type', date, false, true, false, true, true);
|
||||
|
||||
let shiftAssignments = [];
|
||||
let day = new Date(monthInfo.firstMonday);
|
||||
let endDate = monthInfo.lastSunday;
|
||||
let dayNr = 1;
|
||||
let weekNr = 1;
|
||||
|
||||
if (forDay) {
|
||||
day = monthInfo.date;
|
||||
endDate.setDate(monthInfo.date.getDate() + 1);
|
||||
dayNr = monthInfo.date.getDate();
|
||||
weekNr = common.getWeekNumber(monthInfo.date);
|
||||
}
|
||||
|
||||
let publishersThisWeek = [];
|
||||
|
||||
// 0. generate shifts and assign publishers from the previous month if still available
|
||||
while (day < endDate) {
|
||||
let availablePubsForTheDay = await filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type', day, false, false, false, true, true);
|
||||
console.log("passing schedule generation for " + day.toLocaleDateString());
|
||||
const dayOfM = day.getDate();
|
||||
let dayOfWeekEnum = common.getDayOfWeekNameEnEnumForDate(day);
|
||||
let dayName = common.DaysOfWeekArray[day.getDayEuropean()];
|
||||
const event = events.find((event) => event.dayofweek == dayName && (event.dayOfMonth == null || event.dayOfMonth == dayOfM));
|
||||
|
||||
if (!event) {
|
||||
day.setDate(day.getDate() + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
event.startTime = new Date(event.startTime);
|
||||
event.endTime = new Date(event.endTime);
|
||||
|
||||
let startTime = new Date(day);
|
||||
startTime.setHours(event.startTime.getHours());
|
||||
startTime.setMinutes(event.startTime.getMinutes());
|
||||
let endTime = new Date(day);
|
||||
endTime.setHours(event.endTime.getHours());
|
||||
endTime.setMinutes(event.endTime.getMinutes());
|
||||
|
||||
let shiftStart = new Date(startTime);
|
||||
let shiftEnd = new Date(startTime);
|
||||
shiftEnd.setMinutes(shiftStart.getMinutes() + event.shiftDuration);
|
||||
|
||||
let 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();
|
||||
|
||||
const shiftLastMonthSameDay = findTheSameShiftFromLastMonth(shiftsLastMonth, day, weekNr, shiftNr);
|
||||
|
||||
if (shiftLastMonthSameDay) {
|
||||
for (let assignment of shiftLastMonthSameDay.assignments) {
|
||||
let publisher = assignment.publisher;
|
||||
console.log("found publisher from last month: " + publisher.firstName + " " + publisher.lastName);
|
||||
let availability = await FindPublisherAvailability(publisher.id, shiftStart, shiftEnd, dayOfWeekEnum, weekNr);
|
||||
console.log("availability " + availability?.id + ": " + common.getDateFormattedShort(availability?.startTime) + " " + common.getTimeFormatted(availability?.startTime) + " - " + common.getTimeFormatted(availability?.endTime));
|
||||
|
||||
if (availability && copyFromPreviousMonth && !publishersThisWeek.includes(publisher.id)) {
|
||||
shiftAssignments.push({
|
||||
publisherId: publisher.id,
|
||||
isConfirmed: true,
|
||||
isWithTransportIn: availability.isWithTransportIn,
|
||||
isWithTransportOut: availability.isWithTransportOut
|
||||
});
|
||||
publishersThisWeek.push(publisher.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let publishersNeeded = event.numberOfPublishers - shiftAssignments.length;
|
||||
//ToDo: check if getAvailablePublishersForShift is working correctly
|
||||
let availablePublishers = await getAvailablePublishersForShift(shiftStart, shiftEnd, publishers, publishersThisWeek);
|
||||
|
||||
console.log("shift " + __shiftName + " needs " + publishersNeeded + " publishers, available: " + availablePublishers.length + " for the day: " + availablePubsForTheDay.length);
|
||||
|
||||
// Prioritize publishers with minimal availability
|
||||
// SKIP ADDING PUBLISHERS FOR NOW
|
||||
// availablePublishers = availablePublishers.sort((a, b) => a.currentMonthAvailabilityHoursCount - b.currentMonthAvailabilityHoursCount);
|
||||
|
||||
// for (let i = 0; i < publishersNeeded; i++) {
|
||||
// if (availablePublishers[i]) {
|
||||
// shiftAssignments.push({ publisherId: availablePublishers[i].id });
|
||||
// publishersThisWeek.push(availablePublishers[i].id);
|
||||
// }
|
||||
// }
|
||||
|
||||
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,
|
||||
isBySystem: true,
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
shiftStart = new Date(shiftEnd);
|
||||
shiftEnd.setMinutes(shiftStart.getMinutes() + event.shiftDuration);
|
||||
}
|
||||
|
||||
day.setDate(day.getDate() + 1);
|
||||
dayNr++;
|
||||
if (common.DaysOfWeekArray[day.getDayEuropean()] === DayOfWeek.Sunday) {
|
||||
weekNr++;
|
||||
publishersThisWeek = [];
|
||||
publishers.forEach(p => p.currentWeekAssignments = 0);
|
||||
}
|
||||
if (forDay) break;
|
||||
}
|
||||
|
||||
let allShifts = await prisma.shift.findMany({
|
||||
where: {
|
||||
startTime: {
|
||||
gte: monthInfo.firstMonday,
|
||||
lt: monthInfo.lastSunday,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
assignments: {
|
||||
include: {
|
||||
publisher: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log(" second pass " + monthInfo.monthName + " " + monthInfo.year);
|
||||
// 2. First pass - prioritize shifts with transport where it is needed
|
||||
day = monthInfo.firstMonday;
|
||||
dayNr = 1;
|
||||
weekNr = 1;
|
||||
while (day < endDate) {
|
||||
let dayOfWeekEnum = common.getDayOfWeekNameEnEnumForDate(day);
|
||||
let event = events.find((event) => event.dayofweek == common.DaysOfWeekArray[day.getDayEuropean()]);
|
||||
if (event) {
|
||||
let availablePubsForTheDay = await filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type', day, false, false, false, true, true);
|
||||
|
||||
let shifts = allShifts.filter(s => common.getISODateOnly(s.startTime) === common.getISODateOnly(day));
|
||||
let transportShifts = shifts.filter(s => s.requiresTransport);
|
||||
transportShifts.forEach(shift => {
|
||||
let availablePublishers = availablePubsForTheDay.filter(p => !shift.assignments.some(a => a.publisher.id === p.id));
|
||||
availablePublishers = availablePublishers.sort((a, b) => a.currentMonthAvailabilityHoursCount - b.currentMonthAvailabilityHoursCount);
|
||||
let publishersNeeded = event.numberOfPublishers - shift.assignments.length;
|
||||
if (publishersNeeded > 0) {//get the beset match
|
||||
if (availablePublishers[0]) {
|
||||
shift.assignments.push({ publisherId: availablePublishers[i].id });
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
// 3. Second pass - fill the rest of the shifts
|
||||
let shiftsToFill = shifts.filter(s => !s.requiresTransport);
|
||||
shiftsToFill.forEach(shift => {
|
||||
let availablePublishers = availablePubsForTheDay.filter(p => !shift.assignments.some(a => a.publisher.id === p.id));
|
||||
availablePublishers = availablePublishers.sort((a, b) => a.currentMonthAvailabilityHoursCount - b.currentMonthAvailabilityHoursCount);
|
||||
let publishersNeeded = event.numberOfPublishers - shift.assignments.length;
|
||||
if (publishersNeeded > 0) {//get the beset match
|
||||
if (availablePublishers[0]) {
|
||||
shift.assignments.push({ publisherId: availablePublishers[i].id });
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
day.setDate(day.getDate() + 1);
|
||||
}
|
||||
|
||||
if (!forDay) {
|
||||
console.log("###############################################");
|
||||
console.log(" DONE CREATING SCHEDULE FOR " + monthInfo.monthName + " " + monthInfo.year);
|
||||
console.log("###############################################");
|
||||
}
|
||||
|
||||
return {};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return { error: error };
|
||||
}
|
||||
}
|
||||
|
||||
async function DeleteShiftsForMonth(monthInfo) {
|
||||
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) {
|
||||
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 findTheSameShiftFromLastMonth(shiftsLastMonth, day, weekNr, shiftNr) {
|
||||
let weekDay = common.DaysOfWeekArray[day.getDayEuropean()];
|
||||
return shiftsLastMonth.find(s => {
|
||||
return s.weekNr === weekNr &&
|
||||
s.shiftNr === shiftNr &&
|
||||
s.weekDay === weekDay;
|
||||
});
|
||||
}
|
||||
|
||||
//ToDo use bulk find instead of loop
|
||||
async function getAvailablePublishersForShift(startTime, endTime, allPublishers, publishersThisWeek) {
|
||||
let availablePublishers = [];
|
||||
|
||||
for (let publisher of allPublishers) {
|
||||
let availability = await FindPublisherAvailability(publisher.id, startTime, endTime);
|
||||
|
||||
if (availability && !publishersThisWeek.includes(publisher.id)) {
|
||||
availablePublishers.push(publisher);
|
||||
}
|
||||
}
|
||||
|
||||
return availablePublishers;
|
||||
}
|
||||
|
||||
async function FindPublisherAvailability(publisherId, startDate, endDate, dayOfWeekEnum, weekNr) {
|
||||
const prisma = common.getPrismaClient();
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
const hours = start.getHours();
|
||||
const minutes = start.getMinutes();
|
||||
|
||||
const exactAvailabilities = await prisma.availability.findMany({
|
||||
where: {
|
||||
publisherId: publisherId,
|
||||
// type: AvailabilityType.OneTime,
|
||||
AND: [ // Ensure both conditions must be met
|
||||
{ startTime: { lte: start } }, // startTime is less than or equal to the date
|
||||
{ endTime: { gte: end } },// endTime is greater than or equal to the date
|
||||
],
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Query for repeating availabilities, ignoring exact date, focusing on time and day of week/month
|
||||
let repeatingAvailabilities = await prisma.availability.findMany({
|
||||
where: {
|
||||
publisherId: publisherId,
|
||||
dayOfMonth: null, // This signifies a repeating availability
|
||||
OR: [
|
||||
{ dayofweek: dayOfWeekEnum },// Matches the specific day of the week
|
||||
{ weekOfMonth: weekNr } // Matches specific weeks of the month
|
||||
]
|
||||
}
|
||||
});
|
||||
//filter out availabilities that does not match the time
|
||||
// repeatingAvailabilities = repeatingAvailabilities.filter(avail => {
|
||||
// return avail.startTime.getHours() <= hours && avail.endTime.getHours() >= hours
|
||||
// && avail.startTime.getMinutes() <= minutes && avail.endTime.getMinutes() >= minutes
|
||||
// && avail.startTime <= new Date(startDate) && (endDate ? avail.endTime >= new Date(endDate) : true)
|
||||
// });
|
||||
|
||||
repeatingAvailabilities = repeatingAvailabilities.filter(avail => {
|
||||
const availStart = new Date(avail.startTime);
|
||||
const availEnd = new Date(avail.endTime);
|
||||
const availUntil = avail.endDate ? new Date(avail.endDate) : null;
|
||||
|
||||
const availStartTimeInt = common.timeToInteger(availStart.getHours(), availStart.getMinutes());
|
||||
const availEndTimeInt = common.timeToInteger(availEnd.getHours(), availEnd.getMinutes());
|
||||
const startTimeInt = common.timeToInteger(start.getHours(), start.getMinutes());
|
||||
const endTimeInt = common.timeToInteger(end.getHours(), end.getMinutes());
|
||||
|
||||
const isValid = availStartTimeInt <= startTimeInt && availEndTimeInt >= endTimeInt
|
||||
&& availStart <= start
|
||||
&& (!availUntil || availUntil >= end);
|
||||
|
||||
return isValid;
|
||||
});
|
||||
|
||||
// return [...exactAvailabilities, ...repeatingAvailabilities];
|
||||
// Combine the exact and repeating availabilities, return first or null if no availabilities are found
|
||||
return exactAvailabilities.length > 0 ? exactAvailabilities[0] : repeatingAvailabilities.length > 0 ? repeatingAvailabilities[0] : null;
|
||||
}
|
||||
|
||||
// ### COPIED TO shift api (--) ###
|
||||
|
||||
|
||||
// function matchesAvailability(avail, filterDate) {
|
||||
// // Setting the start and end time of the filterDate
|
||||
// filterDate.setHours(0, 0, 0, 0);
|
||||
@ -824,11 +1205,14 @@ async function runSqlFile(filePath) {
|
||||
|
||||
module.exports = {
|
||||
findPublisher,
|
||||
findPublisherAvailability,
|
||||
FindPublisherAvailability,
|
||||
runSqlFile,
|
||||
getAvailabilities,
|
||||
filterPublishersNew,
|
||||
getCoverMePublisherEmails,
|
||||
getAllPublishersWithStatistics,
|
||||
getCalendarEvents
|
||||
getAllPublishersWithStatisticsMonth,
|
||||
getCalendarEvents,
|
||||
GenerateSchedule,
|
||||
DeleteShiftsForMonth,
|
||||
DeleteShiftsForDay,
|
||||
};
|
Reference in New Issue
Block a user