222 lines
7.1 KiB
TypeScript
222 lines
7.1 KiB
TypeScript
import { PrismaClient } from '@/prisma/generated/client';
|
|
import { faker } from '@faker-js/faker';
|
|
import { addDays, format, addWeeks, subDays } from 'date-fns';
|
|
import {
|
|
ScheduleWithRelations,
|
|
PostWithRelations,
|
|
TimeslotData,
|
|
} from '../types/seeding';
|
|
|
|
export const seedSchedules = async (
|
|
prisma: PrismaClient,
|
|
posts: PostWithRelations[]
|
|
): Promise<ScheduleWithRelations[]> => {
|
|
// Clean up existing data
|
|
await prisma.schedule.deleteMany({});
|
|
await prisma.timeslot.deleteMany({});
|
|
|
|
const schedules: ScheduleWithRelations[] = [];
|
|
const now = new Date();
|
|
const publishedPosts = posts.filter((post) => post.published === true);
|
|
|
|
console.log(
|
|
`Creating schedules for ${publishedPosts.length} published posts`
|
|
);
|
|
|
|
// Common time slots patterns
|
|
const timeSlotPatterns = [
|
|
// Morning, Afternoon, Evening pattern
|
|
[
|
|
{ start: '06:00', end: '09:00' },
|
|
{ start: '12:00', end: '15:00' },
|
|
{ start: '18:00', end: '21:00' },
|
|
],
|
|
// Morning and Afternoon pattern
|
|
[
|
|
{ start: '08:00', end: '11:00' },
|
|
{ start: '13:00', end: '16:00' },
|
|
],
|
|
// Full day pattern
|
|
[
|
|
{ start: '09:00', end: '12:00' },
|
|
{ start: '14:00', end: '17:00' },
|
|
{ start: '19:00', end: '21:00' },
|
|
],
|
|
];
|
|
|
|
for (const post of publishedPosts) {
|
|
try {
|
|
// Number of schedules per post (1-2)
|
|
const numSchedules = faker.helpers.rangeToNumber({ min: 1, max: 2 });
|
|
|
|
const postSchedules = [];
|
|
|
|
for (let i = 0; i < numSchedules; i++) {
|
|
// Schedule start date logic
|
|
let scheduleStartDate: Date;
|
|
let scheduleEndDate: Date;
|
|
|
|
const scheduleType = faker.helpers.weightedArrayElement([
|
|
{ value: 'past', weight: 20 },
|
|
{ value: 'current', weight: 40 },
|
|
{ value: 'future', weight: 40 },
|
|
]);
|
|
|
|
if (scheduleType === 'past') {
|
|
// Past schedule (20-60 days ago)
|
|
const daysAgo = faker.helpers.rangeToNumber({ min: 20, max: 60 });
|
|
scheduleStartDate = subDays(now, daysAgo);
|
|
scheduleEndDate = addDays(
|
|
scheduleStartDate,
|
|
faker.helpers.rangeToNumber({ min: 2, max: 5 })
|
|
);
|
|
} else if (scheduleType === 'current') {
|
|
// Current schedule (started recently, ends in near future)
|
|
const daysAgo = faker.helpers.rangeToNumber({ min: 1, max: 3 });
|
|
scheduleStartDate = subDays(now, daysAgo);
|
|
scheduleEndDate = addDays(
|
|
now,
|
|
faker.helpers.rangeToNumber({ min: 2, max: 7 })
|
|
);
|
|
} else {
|
|
// Future schedule (starting soon)
|
|
const daysAhead = faker.helpers.rangeToNumber({ min: 1, max: 14 });
|
|
scheduleStartDate = addDays(now, daysAhead);
|
|
scheduleEndDate = addDays(
|
|
scheduleStartDate,
|
|
faker.helpers.rangeToNumber({ min: 2, max: 5 })
|
|
);
|
|
}
|
|
|
|
// Create the schedule
|
|
const schedule = await prisma.schedule.create({
|
|
data: {
|
|
postId: post.id,
|
|
dateStart: scheduleStartDate,
|
|
dateEnd: scheduleEndDate,
|
|
},
|
|
});
|
|
|
|
// Select a time slot pattern
|
|
const selectedPattern = faker.helpers.arrayElement(timeSlotPatterns);
|
|
let totalCurrentParticipants = 0;
|
|
const timeslots: TimeslotData[] = [];
|
|
|
|
// Create timeslots for this schedule
|
|
for (const slot of selectedPattern) {
|
|
const maxParticipants = faker.helpers.rangeToNumber({
|
|
min: 1,
|
|
max: 5,
|
|
});
|
|
|
|
// For completed or near-completed schedules, add some participants
|
|
let currentParticipants = 0;
|
|
let isAvailable = true;
|
|
|
|
if (
|
|
scheduleType === 'past' ||
|
|
(scheduleType === 'current' && post.status === 'Closed')
|
|
) {
|
|
// For past or closed schedules, potentially fill slots
|
|
currentParticipants = faker.helpers.rangeToNumber({
|
|
min: 0,
|
|
max: maxParticipants,
|
|
});
|
|
|
|
// If closed, at least one slot should be full
|
|
if (post.status === 'Closed' && slot === selectedPattern[0]) {
|
|
currentParticipants = maxParticipants;
|
|
}
|
|
|
|
isAvailable = currentParticipants < maxParticipants;
|
|
}
|
|
|
|
totalCurrentParticipants += currentParticipants;
|
|
|
|
const timeslot = await prisma.timeslot.create({
|
|
data: {
|
|
scheduleId: schedule.id,
|
|
startTime: slot.start,
|
|
endTime: slot.end,
|
|
maxParticipants: maxParticipants,
|
|
currentParticipants: currentParticipants,
|
|
isAvailable: isAvailable,
|
|
},
|
|
});
|
|
|
|
timeslots.push(timeslot as TimeslotData);
|
|
}
|
|
|
|
// Create the enhanced schedule object with timeslots
|
|
const scheduleWithTimeslots: ScheduleWithRelations = {
|
|
...schedule,
|
|
timeslot: timeslots,
|
|
totalCurrentParticipants,
|
|
};
|
|
|
|
schedules.push(scheduleWithTimeslots);
|
|
postSchedules.push(scheduleWithTimeslots);
|
|
}
|
|
|
|
// Update the post with its schedules
|
|
if (post.Schedule) {
|
|
post.Schedule = postSchedules;
|
|
} else {
|
|
post.Schedule = postSchedules;
|
|
}
|
|
|
|
// If post status is Closed, ensure total participants meets or exceeds requiredParticipant
|
|
if (post.status === 'Closed' && postSchedules.length > 0) {
|
|
const totalParticipantsForPost = postSchedules.reduce(
|
|
(total, schedule) => total + (schedule.totalCurrentParticipants || 0),
|
|
0
|
|
);
|
|
|
|
if (totalParticipantsForPost < post.requiredParticipant) {
|
|
// Get first timeslot
|
|
const firstSchedule = postSchedules[0];
|
|
if (
|
|
firstSchedule &&
|
|
firstSchedule.timeslot &&
|
|
firstSchedule.timeslot.length > 0
|
|
) {
|
|
const firstTimeslot = firstSchedule.timeslot[0];
|
|
const participantsNeeded =
|
|
post.requiredParticipant - totalParticipantsForPost;
|
|
const newParticipantCount =
|
|
firstTimeslot.currentParticipants + participantsNeeded;
|
|
|
|
// Update the timeslot
|
|
await prisma.timeslot.update({
|
|
where: { id: firstTimeslot.id },
|
|
data: {
|
|
currentParticipants: newParticipantCount,
|
|
maxParticipants: Math.max(
|
|
firstTimeslot.maxParticipants || 0,
|
|
newParticipantCount
|
|
),
|
|
isAvailable: false,
|
|
},
|
|
});
|
|
|
|
// Update our local copy
|
|
firstTimeslot.currentParticipants = newParticipantCount;
|
|
firstTimeslot.isAvailable = false;
|
|
if (firstSchedule.totalCurrentParticipants !== undefined) {
|
|
firstSchedule.totalCurrentParticipants += participantsNeeded;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error creating schedule for post ${post.id}:`, error);
|
|
console.error(error instanceof Error ? error.message : 'Unknown error');
|
|
}
|
|
}
|
|
|
|
console.log(
|
|
`Successfully created ${schedules.length} schedules with timeslots`
|
|
);
|
|
return schedules;
|
|
};
|