Files
mwitnessing/src/helpers/data.js
Dobromir Popov af9fc6af97 update LastLogin on login, show it.
remove end date for filter pubs for month;
fix stats end date
2024-04-25 20:22:36 +03:00

531 lines
21 KiB
JavaScript

const common = require('./common');
async function findPublisher(names, email, select, getAll = false) {
// Normalize and split the name if provided
let nameParts = names ? names.normalize('NFC').trim().split(/\s+/) : [];
// Construct the select clause
let selectClause = select
? select.split(",").reduce((acc, curr) => ({ ...acc, [curr]: true }), {})
: { id: true, firstName: true, lastName: true };
// Construct the where clause based on the provided data
let whereClause = [];
if (nameParts.length > 0) {
if (nameParts.length === 1) {
// If only one name part is provided, check it against both firstName and lastName
whereClause.push({ OR: [{ firstName: nameParts[0] }, { lastName: nameParts[0] }] });
} else if (nameParts.length === 2) {
// If two name parts are provided, check combinations of firstName and lastName
whereClause.push({ firstName: nameParts[0], lastName: nameParts[1] });
whereClause.push({ firstName: nameParts[1], lastName: nameParts[0] });
} else if (nameParts.length === 3) {
// If three name parts are provided, consider the middle name part of first or last name
whereClause.push({ firstName: `${nameParts[0]} ${nameParts[1]}`, lastName: nameParts[2] });
whereClause.push({ firstName: nameParts[0], lastName: `${nameParts[1]} ${nameParts[2]}` });
} else if (nameParts.length > 3) {
// Join all parts except the last as the first name, and consider the last part as the last name
const firstName = nameParts.slice(0, -1).join(' ');
const lastName = nameParts.slice(-1).join(' ');
whereClause.push({ firstName: firstName, lastName: lastName });
// Alternative case: First part as first name, and join the rest as the last name
const altFirstName = nameParts.slice(0, 1).join(' ');
const altLastName = nameParts.slice(1).join(' ');
whereClause.push({ firstName: altFirstName, lastName: altLastName });
}
}
if (email) {
whereClause.push({ email: email });
}
if (whereClause.length === 0) {
return null; // No search criteria provided
}
const prisma = common.getPrismaClient();
// let publisher = await prisma.publisher.findFirst({
// select: selectClause,
// where: { OR: whereClause }
// });
// Retrieve all matching records
try {
let result = await prisma.publisher.findMany({
select: selectClause,
where: { OR: whereClause }
});
// If getAll is false, take only the first record
if (!getAll && result.length > 0) {
result = result.length > 0 ? [result[0]] : [];
}
if (result.length === 0 && names) {
console.log("No publisher found, trying fuzzy search for '" + names + "'- email: '" + email + "'");
const publishers = await prisma.publisher.findMany();
result = [await common.fuzzySearch(publishers, names, 0.90)];
console.log("Fuzzy search result: " + result[0]?.firstName + " " + result[0]?.lastName + " - " + result[0]?.email);
}
//always return an array
result = result || [];
return result;
} catch (error) {
console.error("Error finding publisher: ", error);
throw error; // Rethrow the error or handle it as needed
}
}
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
},
},
],
}
});
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) {
const prismaClient = common.getPrismaClient();
const items = await prismaClient.availability.findMany({
where: {
publisherId: userId,
},
select: {
id: true,
name: true,
isActive: true,
isFromPreviousAssignment: true,
dayofweek: true,
dayOfMonth: true,
startTime: true,
endTime: true,
repeatWeekly: true,
endDate: true,
publisher: {
select: {
firstName: true,
lastName: true,
id: true,
},
},
},
});
// Convert Date objects to ISO strings
const serializableItems = items.map(item => ({
...item,
startTime: item.startTime.toISOString(),
endTime: item.endTime.toISOString(),
name: common.getTimeFomatted(item.startTime) + "-" + common.getTimeFomatted(item.endTime),
//endDate can be null
endDate: item.endDate ? item.endDate.toISOString() : null,
type: 'availability',
// Convert other Date fields similarly if they exist
}));
/*model Assignment {
id Int @id @default(autoincrement())
shift Shift @relation(fields: [shiftId], references: [id], onDelete: Cascade)
shiftId Int
publisher Publisher @relation(fields: [publisherId], references: [id], onDelete: Cascade)
publisherId String
isActive Boolean @default(true)
isConfirmed Boolean @default(false)
isWithTransport Boolean @default(false)
Report Report[]
}*/
//get assignments for this user
const assignments = await prismaClient.assignment.findMany({
where: {
publisherId: userId,
},
select: {
id: true,
isBySystem: true,
isConfirmed: true,
isWithTransport: true,
shift: {
select: {
id: true,
name: true,
startTime: true,
endTime: true,
//select all assigned publishers names as name - comma separated
assignments: {
select: {
publisher: {
select: {
firstName: true,
lastName: true,
}
}
}
}
}
}
}
});
const serializableAssignments = assignments.map(item => ({
...item,
startTime: item.shift.startTime.toISOString(),
endTime: item.shift.endTime.toISOString(),
// name: item.shift.publishers.map(p => p.firstName + " " + p.lastName).join(", "),
//name: item.shift.assignments.map(a => a.publisher.firstName[0] + " " + a.publisher.lastName).join(", "),
name: common.getTimeFomatted(new Date(item.shift.startTime)) + "-" + common.getTimeFomatted(new Date(item.shift.endTime)),
type: 'assignment',
//delete shift object
shift: null,
publisher: { id: userId }
}));
serializableItems.push(...serializableAssignments);
return serializableItems;
}
async function filterPublishersNew(selectFields, filterDate, isExactTime = false, isForTheMonth = false, isNoEndDateFilter = false, isWithStats = true, includeOldAvailabilities = false) {
filterDate = new Date(filterDate); // Convert to date object if not already
// Only attempt to split if selectFields is a string; otherwise, use it as it is.
selectFields = typeof selectFields === 'string' ? selectFields.split(",") : selectFields;
let selectBase = selectFields.reduce((acc, curr) => {
acc[curr] = true;
return acc;
}, {});
selectBase.assignments = {
select: {
id: true,
shift: {
select: {
id: true,
startTime: true,
endTime: true
}
}
},
where: {
shift: {
startTime: {
gte: filterDate,
}
}
}
};
var monthInfo = common.getMonthDatesInfo(filterDate);
var weekNr = common.getWeekOfMonth(filterDate); //getWeekNumber
let dayOfWeekEnum = common.getDayOfWeekNameEnEnumForDate(filterDate);
if (!isExactTime) {
filterDate.setHours(0, 0, 0, 0); // Set to midnight
}
const filterDateEnd = new Date(filterDate);
filterDateEnd.setHours(23, 59, 59, 999);
let whereClause = {};
//if full day, match by date only
if (!isExactTime) { // Check only by date without considering time ( Assignments on specific days without time)
whereClause["availabilities"] = {
some: {
OR: [
{
startTime: { gte: filterDate },
endTime: { lte: filterDateEnd },
}
,
// Check if dayOfMonth is null and match by day of week using the enum (Assigments every week)
// This includes availabilities from previous assignments but not with preference
{
dayOfMonth: null, // includes monthly and weekly repeats
dayofweek: dayOfWeekEnum,
// ToDo: and weekOfMonth
startTime: { lte: filterDate },
AND: [
{
OR: [ // OR condition for repeatUntil to handle events that either end after filterDate or repeat forever
{ endDate: { gte: filterDate } },
{ endDate: null }
]
}
]
}
]
}
};
}
//if not full day, match by date and time
else {
//match exact time (should be same as data.findPublisherAvailability())
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 },
AND: [
{
OR: [ // OR condition for repeatUntil to handle events that either end after filterDate or repeat forever
{ endDate: { gte: filterDate } },
{ endDate: null }
]
}
]
}
]
}
};
}
if (isForTheMonth) {
// If no filter date, return all publishers's availabilities for currentMonthStart
whereClause["availabilities"] = {
some: {
OR: [
// Check if dayOfMonth is not null and startTime is after monthInfo.firstMonday (Assignments on specific days AND time)
{
dayOfMonth: { not: null },
startTime: { gte: monthInfo.firstMonday },
// endTime: { lte: monthInfo.lastSunday }
},
// Check if dayOfMonth is null and match by day of week using the enum (Assigments every week)
{
dayOfMonth: null,
AND: [
{
OR: [ // OR condition for repeatUntil to handle events that either end after filterDate or repeat forever
{ endDate: { gte: filterDate } },
{ endDate: null }
]
}
]
}
]
}
};
if (!isNoEndDateFilter) { // Check if we need to apply the endTime filter
whereClause["availabilities"].some.OR[0].endTime = { lte: monthInfo.lastSunday };
}
}
console.log(`getting publishers for date: ${filterDate}, isExactTime: ${isExactTime}, isForTheMonth: ${isForTheMonth}`);
//include availabilities if flag is true
const prisma = common.getPrismaClient(); //why we need to get it again?
let publishers = await prisma.publisher.findMany({
where: whereClause,
select: {
...selectBase,
availabilities: true
}
});
console.log(`publishers: ${publishers.length}, WhereClause: ${JSON.stringify(whereClause)}`);
// convert matching weekly availabilities to availabilities for the day to make furter processing easier on the client.
// we trust that the filtering was OK, so we use the dateFilter as date.
publishers.forEach(pub => {
pub.availabilities = pub.availabilities.map(avail => {
if (avail.dayOfMonth == null) {
let newStart = new Date(filterDate);
newStart.setHours(avail.startTime.getHours(), avail.startTime.getMinutes(), 0, 0);
let newEnd = new Date(filterDate);
newEnd.setHours(avail.endTime.getHours(), avail.endTime.getMinutes(), 0, 0);
return {
...avail,
startTime: newStart,
endTime: newEnd
}
}
return avail;
});
});
let currentWeekStart, currentWeekEnd,
currentMonthStart, currentMonthEnd,
previousMonthStart, previousMonthEnd;
if (isWithStats) {
currentWeekStart = common.getStartOfWeek(filterDate);
currentWeekEnd = common.getEndOfWeek(filterDate);
currentMonthStart = monthInfo.firstMonday;
currentMonthEnd = monthInfo.lastSunday;
let prevMnt = new Date(filterDate)
prevMnt.setMonth(prevMnt.getMonth() - 1);
monthInfo = common.getMonthDatesInfo(prevMnt);
previousMonthStart = monthInfo.firstMonday;
previousMonthEnd = monthInfo.lastSunday;
//get if publisher has assignments for current weekday, week, current month, previous month
publishers.forEach(pub => {
// Filter assignments for current day
pub.currentDayAssignments = pub.assignments?.filter(assignment => {
return assignment.shift.startTime >= filterDate && assignment.shift.startTime <= filterDateEnd;
}).length;
// Filter assignments for current week
pub.currentWeekAssignments = pub.assignments?.filter(assignment => {
return assignment.shift.startTime >= currentWeekStart && assignment.shift.startTime <= currentWeekEnd;
}).length;
// Filter assignments for current month
pub.currentMonthAssignments = pub.assignments?.filter(assignment => {
return assignment.shift.startTime >= currentMonthStart && assignment.shift.startTime <= currentMonthEnd;
}).length;
// Filter assignments for previous month
pub.previousMonthAssignments = pub.assignments?.filter(assignment => {
return assignment.shift.startTime >= previousMonthStart && assignment.shift.startTime <= previousMonthEnd;
}).length;
});
}
//get the availabilities for the day. Calcullate:
//1. how many days the publisher is available for the current month - only with dayOfMonth
//2. how many days the publisher is available without dayOfMonth (previous months count)
//3. how many hours in total the publisher is available for the current month
publishers.forEach(pub => {
if (isWithStats) {
pub.currentMonthAvailability = pub.availabilities?.filter(avail => {
// return avail.dayOfMonth != null && avail.startTime >= currentMonthStart && avail.startTime <= currentMonthEnd;
return avail.startTime >= currentMonthStart && avail.startTime <= currentMonthEnd;
})
pub.currentMonthAvailabilityDaysCount = pub.currentMonthAvailability.length || 0;
// pub.currentMonthAvailabilityDaysCount += pub.availabilities.filter(avail => {
// return avail.dayOfMonth == null;
// }).length;
pub.currentMonthAvailabilityHoursCount = pub.currentMonthAvailability.reduce((acc, curr) => {
return acc + (curr.endTime.getTime() - curr.startTime.getTime()) / (1000 * 60 * 60);
}, 0);
//if pub has up-to-date availabilities (with dayOfMonth) for the current month
pub.hasUpToDateAvailabilities = pub.availabilities?.some(avail => {
return avail.dayOfMonth != null && avail.startTime >= currentMonthStart; // && avail.startTime <= currentMonthEnd;
});
}
//if pub has ever filled the form - if has availabilities which are not from previous assignments
pub.hasEverFilledForm = pub.availabilities?.some(avail => {
return avail.isFromPreviousAssignments == false;
});
//if pub has availabilities for the current day
pub.hasAvailabilityForCurrentDay = pub.availabilities?.some(avail => {
return avail.startTime >= filterDate && avail.startTime <= filterDateEnd;
});
});
if (isExactTime) {
// Post filter for time if dayOfMonth is null as we can't only by time for multiple dates in SQL
// Modify the availabilities array of the filtered publishers
publishers.forEach(pub => {
pub.availabilities = pub.availabilities?.filter(avail => matchesAvailability(avail, filterDate));
});
}
return publishers;
}
function matchesAvailability(avail, filterDate) {
// Setting the start and end time of the filterDate
filterDate.setHours(0, 0, 0, 0);
const filterDateEnd = new Date(filterDate);
filterDateEnd.setHours(23, 59, 59, 999);
// Return true if avail.startTime is between filterDate and filterDateEnd
return avail.startTime >= filterDate && avail.startTime <= filterDateEnd;
}
const fs = require('fs');
const path = require('path');
async function runSqlFile(filePath) {
const prisma = common.getPrismaClient();
// Seed the database with some sample data
try {
// Read the SQL file
const sql = fs.readFileSync(filePath, { encoding: 'utf-8' });
// Split the file content by semicolon and filter out empty statements
const statements = sql.split(';').map(s => s.trim()).filter(s => s.length);
// Execute each statement
for (const statement of statements) {
await prisma.$executeRawUnsafe(statement);
}
console.log('SQL script executed successfully.');
} catch (error) {
console.error('Error executing SQL script:', error);
}
}
module.exports = {
findPublisher,
findPublisherAvailability,
runSqlFile,
getAvailabilities,
filterPublishersNew
};