Skip to content

Commit 60fa716

Browse files
authored
Merge pull request #4275 from Northeastern-Electric-Racing/#4241-scheduling-infrastructure
Scheduling Process
2 parents 4378e5c + 555aabf commit 60fa716

6 files changed

Lines changed: 362 additions & 2 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"prisma:seed": "cd src/backend; npx prisma db seed",
1919
"prisma:reset:force": "yarn workspace shared build; cd src/backend; npx prisma migrate reset --force",
2020
"prisma:reset": "yarn workspace shared build; cd src/backend; npx prisma migrate reset",
21+
"prisma:reset:no-seed": "yarn workspace shared build; cd src/backend; npx prisma migrate reset --skip-seed",
2122
"docker:prisma:reset": "cd devContainerization && docker compose -f docker-compose.dev.yml exec -T backend sh -c \"cd /src/backend && npx prisma migrate reset --force\"",
2223
"prisma:migrate": "yarn prisma:migrate:dev",
2324
"docker:prisma:migrate": "cd devContainerization && docker compose -f docker-compose.dev.yml exec -it -w /src/backend backend npx prisma migrate dev",

src/backend/src/prisma/dev-seed.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,21 @@ import { OrganizationProcess } from './seed/organization.process.js';
1212
import { CarProcess } from './seed/car.process.js';
1313
import { ConfigDataProcess } from './seed/config-data.process.js';
1414
import { TeamProcess } from './seed/team.process.js';
15+
import { SchedulingProcess } from './seed/scheduling.process.js';
1516

1617
const prisma = new PrismaClient();
1718

1819
// ORDER MATTERS AT THE MOMENT. I am still looking into topological sort so that order won't matter here.
1920
await new SeedRunner()
2021
.withPrisma(prisma)
21-
.register(new OrganizationProcess(), new CarProcess(), new UsersProcess(), new ConfigDataProcess(), new TeamProcess())
22+
.register(
23+
new OrganizationProcess(),
24+
new CarProcess(),
25+
new UsersProcess(),
26+
new ConfigDataProcess(),
27+
new SchedulingProcess(),
28+
new TeamProcess()
29+
)
2230
.run();
2331

2432
await prisma.$disconnect();

src/backend/src/prisma/factories/config-data.factory.ts

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,246 @@ export const otherReimbursementReasonCreateInput = (
312312
connect: accountCodeIds.map((accountCodeId) => ({ accountCodeId }))
313313
}
314314
});
315+
316+
export const calendarCreateInputs = (userCreatedId: string, organizationId: string): Prisma.CalendarCreateInput[] => [
317+
{
318+
name: 'Design Reviews',
319+
description: 'Tracks all design review events and deadlines.',
320+
colorHexCode: '#4caf50',
321+
userCreated: connectUser(userCreatedId),
322+
organization: connectOrganization(organizationId)
323+
},
324+
{
325+
name: 'Organization',
326+
description: 'Tracks all organization-wide events and meetings.',
327+
colorHexCode: '#f44336',
328+
userCreated: connectUser(userCreatedId),
329+
organization: connectOrganization(organizationId)
330+
},
331+
{
332+
name: 'Manufacturing',
333+
description: 'Tracks all manufacturing and bay time events.',
334+
colorHexCode: '#ff9800',
335+
userCreated: connectUser(userCreatedId),
336+
organization: connectOrganization(organizationId)
337+
},
338+
{
339+
name: 'Miscellaneous',
340+
description: 'Tracks miscellaneous events.',
341+
colorHexCode: '#2196f3',
342+
userCreated: connectUser(userCreatedId),
343+
organization: connectOrganization(organizationId)
344+
},
345+
{
346+
name: 'New Member Events',
347+
description: 'Tracks all new member onboarding events.',
348+
colorHexCode: '#5c6bc0',
349+
userCreated: connectUser(userCreatedId),
350+
organization: connectOrganization(organizationId)
351+
},
352+
{
353+
name: 'FHE Comp',
354+
description: 'Tracks all FHE competition events.',
355+
colorHexCode: '#9c27b0',
356+
userCreated: connectUser(userCreatedId),
357+
organization: connectOrganization(organizationId)
358+
},
359+
{
360+
name: 'FSAE Comp',
361+
description: 'Tracks all FSAE competition events.',
362+
colorHexCode: '#9c27b0',
363+
userCreated: connectUser(userCreatedId),
364+
organization: connectOrganization(organizationId)
365+
}
366+
];
367+
368+
type EventTypeConfig = {
369+
name: string;
370+
calendarNames: string[];
371+
requiredMembers: boolean;
372+
optionalMembers: boolean;
373+
teams: boolean;
374+
teamType: boolean;
375+
location: boolean;
376+
zoomLink: boolean;
377+
shop: boolean;
378+
machinery: boolean;
379+
workPackage: boolean;
380+
questionDocument: boolean;
381+
documents: boolean;
382+
description: boolean;
383+
onlyHeadsOrAboveForEventCreation: boolean;
384+
requiresConfirmation: boolean;
385+
sendSlackNotifications: boolean;
386+
};
387+
388+
export const eventTypeConfigs: EventTypeConfig[] = [
389+
{
390+
name: 'Manufacturing',
391+
calendarNames: ['Manufacturing'],
392+
requiredMembers: true,
393+
optionalMembers: true,
394+
teams: true,
395+
teamType: true,
396+
location: false,
397+
zoomLink: false,
398+
shop: true,
399+
machinery: true,
400+
workPackage: true,
401+
questionDocument: false,
402+
documents: false,
403+
description: false,
404+
onlyHeadsOrAboveForEventCreation: false,
405+
requiresConfirmation: false,
406+
sendSlackNotifications: false
407+
},
408+
{
409+
name: 'Educational',
410+
calendarNames: ['Organization'],
411+
requiredMembers: false,
412+
optionalMembers: true,
413+
teams: true,
414+
teamType: false,
415+
location: true,
416+
zoomLink: true,
417+
shop: false,
418+
machinery: false,
419+
workPackage: false,
420+
questionDocument: false,
421+
documents: true,
422+
description: true,
423+
onlyHeadsOrAboveForEventCreation: false,
424+
requiresConfirmation: false,
425+
sendSlackNotifications: true
426+
},
427+
{
428+
name: 'FSAE',
429+
calendarNames: ['FSAE Comp'],
430+
requiredMembers: true,
431+
optionalMembers: true,
432+
teams: true,
433+
teamType: true,
434+
location: true,
435+
zoomLink: false,
436+
shop: false,
437+
machinery: false,
438+
workPackage: false,
439+
questionDocument: false,
440+
documents: true,
441+
description: true,
442+
onlyHeadsOrAboveForEventCreation: true,
443+
requiresConfirmation: true,
444+
sendSlackNotifications: true
445+
},
446+
{
447+
name: 'FHE',
448+
calendarNames: ['FHE Comp'],
449+
requiredMembers: true,
450+
optionalMembers: true,
451+
teams: true,
452+
teamType: true,
453+
location: true,
454+
zoomLink: false,
455+
shop: false,
456+
machinery: false,
457+
workPackage: false,
458+
questionDocument: false,
459+
documents: true,
460+
description: true,
461+
onlyHeadsOrAboveForEventCreation: true,
462+
requiresConfirmation: true,
463+
sendSlackNotifications: true
464+
},
465+
{
466+
name: 'Misc. Event (When2Meet)',
467+
calendarNames: ['Miscellaneous'],
468+
requiredMembers: true,
469+
optionalMembers: true,
470+
teams: true,
471+
teamType: false,
472+
location: false,
473+
zoomLink: false,
474+
shop: false,
475+
machinery: false,
476+
workPackage: false,
477+
questionDocument: false,
478+
documents: false,
479+
description: true,
480+
onlyHeadsOrAboveForEventCreation: false,
481+
requiresConfirmation: true,
482+
sendSlackNotifications: true
483+
},
484+
{
485+
name: 'Deadline/Heads Up',
486+
calendarNames: ['Organization'],
487+
requiredMembers: false,
488+
optionalMembers: false,
489+
teams: true,
490+
teamType: false,
491+
location: false,
492+
zoomLink: false,
493+
shop: false,
494+
machinery: false,
495+
workPackage: true,
496+
questionDocument: false,
497+
documents: false,
498+
description: true,
499+
onlyHeadsOrAboveForEventCreation: false,
500+
requiresConfirmation: false,
501+
sendSlackNotifications: true
502+
},
503+
{
504+
name: 'Misc. Event (Standard)',
505+
calendarNames: ['Miscellaneous'],
506+
requiredMembers: false,
507+
optionalMembers: true,
508+
teams: true,
509+
teamType: false,
510+
location: true,
511+
zoomLink: true,
512+
shop: false,
513+
machinery: false,
514+
workPackage: false,
515+
questionDocument: false,
516+
documents: false,
517+
description: true,
518+
onlyHeadsOrAboveForEventCreation: false,
519+
requiresConfirmation: false,
520+
sendSlackNotifications: true
521+
}
522+
];
523+
524+
export const eventTypeCreateInput = (
525+
userCreatedId: string,
526+
organizationId: string,
527+
config: EventTypeConfig,
528+
calendarIdsByName: Record<string, string>
529+
): Prisma.Event_TypeCreateInput => ({
530+
name: config.name,
531+
userCreated: connectUser(userCreatedId),
532+
organization: connectOrganization(organizationId),
533+
requiredMembers: config.requiredMembers,
534+
optionalMembers: config.optionalMembers,
535+
teams: config.teams,
536+
teamType: config.teamType,
537+
location: config.location,
538+
zoomLink: config.zoomLink,
539+
shop: config.shop,
540+
machinery: config.machinery,
541+
workPackage: config.workPackage,
542+
questionDocument: config.questionDocument,
543+
documents: config.documents,
544+
description: config.description,
545+
onlyHeadsOrAboveForEventCreation: config.onlyHeadsOrAboveForEventCreation,
546+
requiresConfirmation: config.requiresConfirmation,
547+
sendSlackNotifications: config.sendSlackNotifications,
548+
calendars: {
549+
connect: config.calendarNames.map((name) => {
550+
const calendarId = calendarIdsByName[name];
551+
if (!calendarId) {
552+
throw new Error(`Missing calendar for event type config: ${name}`);
553+
}
554+
return { calendarId };
555+
})
556+
}
557+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Prisma } from '@prisma/client';
2+
import { Faker } from '@faker-js/faker';
3+
4+
export const scheduleSettingsCreateInput = (faker: Faker, userId: string): Prisma.Schedule_SettingsCreateInput => ({
5+
personalGmail: faker.internet.email({ provider: 'gmail.com' }),
6+
personalZoomLink: `https://zoom.us/j/${faker.string.numeric(10)}`,
7+
User: { connect: { userId } }
8+
});
9+
10+
const AVAILABILITY_OVER_WEEKENDS = {
11+
min: 0,
12+
max: 4
13+
};
14+
15+
const AVAILABILITY_GENERAL = {
16+
min: 4,
17+
max: 10
18+
};
19+
20+
export const availabilityCreateInput = (
21+
faker: Faker,
22+
scheduleSettingsId: string,
23+
date: Date
24+
): Prisma.AvailabilityCreateInput => {
25+
const dayOfWeek = date.getUTCDay();
26+
const availability = dayOfWeek === 0 || dayOfWeek === 6 ? AVAILABILITY_OVER_WEEKENDS : AVAILABILITY_GENERAL;
27+
return {
28+
availability: faker.helpers.arrayElements(
29+
Array.from({ length: 12 }, (_, i) => i),
30+
{ min: availability.min, max: availability.max }
31+
),
32+
dateSet: date,
33+
scheduleSettings: { connect: { drScheduleSettingsId: scheduleSettingsId } }
34+
};
35+
};

src/backend/src/prisma/seed/config-data.process.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
22
Account_Code,
3+
Calendar,
34
Description_Bullet_Type,
5+
Event_Type,
46
Index_Code,
57
Link_Type,
68
Manufacturer,
@@ -15,7 +17,10 @@ import { UsersOutput, UsersProcess } from './user.process.js';
1517
import { SeedProcess } from '../processes/seed-process.js';
1618
import {
1719
accountCodeCreateInputs,
20+
calendarCreateInputs,
1821
descriptionBulletTypeCreateInputs,
22+
eventTypeConfigs,
23+
eventTypeCreateInput,
1924
indexCodeCreateInputs,
2025
linkTypeCreateInputs,
2126
manufacturerCreateInputs,
@@ -40,6 +45,8 @@ export type ConfigDataOutput = {
4045
indexCodes: Index_Code[];
4146
vendors: Vendor[];
4247
reimbursementProductOtherReasons: Reimbursement_Product_Other_Reason[];
48+
calendars: Calendar[];
49+
eventTypes: Event_Type[];
4350
};
4451

4552
export class ConfigDataProcess extends SeedProcess<ConfigDataInput, ConfigDataOutput> {
@@ -129,6 +136,23 @@ export class ConfigDataProcess extends SeedProcess<ConfigDataInput, ConfigDataOu
129136
})
130137
);
131138

139+
const calendars = await Promise.all(
140+
calendarCreateInputs(creator.userId, organizationId).map((data) => this.prisma.calendar.create({ data }))
141+
);
142+
143+
const calendarIdsByName = calendars.reduce<Record<string, string>>((acc, calendar) => {
144+
acc[calendar.name] = calendar.calendarId;
145+
return acc;
146+
}, {});
147+
148+
const eventTypes = await Promise.all(
149+
eventTypeConfigs.map((config) =>
150+
this.prisma.event_Type.create({
151+
data: eventTypeCreateInput(creator.userId, organizationId, config, calendarIdsByName)
152+
})
153+
)
154+
);
155+
132156
return {
133157
teamTypes,
134158
linkTypes,
@@ -139,7 +163,9 @@ export class ConfigDataProcess extends SeedProcess<ConfigDataInput, ConfigDataOu
139163
accountCodes,
140164
indexCodes,
141165
vendors,
142-
reimbursementProductOtherReasons
166+
reimbursementProductOtherReasons,
167+
calendars,
168+
eventTypes
143169
};
144170
}
145171
}

0 commit comments

Comments
 (0)