Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions database-diagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ title: Database prototype
erDiagram
USER {
int id PK
int fieldOfStudyId FK
string firstName
string lastName
int fieldOfStudyId FK
}
EXPENSE {
int id PK
Expand Down Expand Up @@ -58,7 +58,7 @@ erDiagram
int id PK
int userId FK
int interviewId FK
int admissionPeriodId FK
int semesterId FK
string yearOfStudy
int substitute
int doublePosition
Expand All @@ -71,7 +71,6 @@ erDiagram
string specialNeeds
string language
string preferredSchool

}
PRIORITY_DAY{
int id PK
Expand All @@ -81,6 +80,35 @@ erDiagram
int thursday
int friday
}
SEMESTER{
int id PK
int lastSemesterId FK
int departmentId FK
date semesterStartDate
date semesterEndDate
date recruitmentStartDate
date recruitmentEndDate
string name
}
SCHOOL{
int id PK
int departmentId FK
string name
string contactPersonName
string contactPersonPhoneNumber
string contactPersonEmail
boolean isInternational
}
SCHOOL_SEMESTER_ASSISTANT{
int school FK
int semester FK
int user FK
}
TEAM_SEMESTER_USER{
int team FK
int semester FK
int user FK
}
USER ||--o{ EXPENSE : "pays"
USER ||--o{ FIELD_OF_STUDY : studys
VEKTOR_DEPARTMENT ||--o{ FIELD_OF_STUDY : oversee
Expand All @@ -91,4 +119,13 @@ erDiagram
USER ||--|| PRIORITY_DAY : wants
TEAM }o--o{ ASSISTENT_APPLICATION : interest
VEKTOR_DEPARTMENT ||--O{ TEAM : belongsTo
SEMESTER }|--|| VEKTOR_DEPARTMENT : department
SCHOOL }|--|| VEKTOR_DEPARTMENT : department
SEMESTER }o--|| ASSISTENT_APPLICATION : admissionPeriod
SCHOOL_SEMESTER_ASSISTANT ||--o{ SCHOOL : schoolSemesterAssistant
SCHOOL_SEMESTER_ASSISTANT ||--o{ SEMESTER : schoolSemesterAssistant
SCHOOL_SEMESTER_ASSISTANT ||--o{ USER : shcoolSemesterAssistant
TEAM_SEMESTER_USER ||--o{ TEAM : teamSemesterUser
TEAM_SEMESTER_USER ||--o{ SEMESTER : teamSemesterUser
TEAM_SEMESTER_USER ||--o{ USER : teamSemesterUser
```
2 changes: 2 additions & 0 deletions db/tables/departments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { mainSchema } from "@/db/tables/schema";
import { teamsTable } from "@/db/tables/teams";
import { relations } from "drizzle-orm";
import { serial } from "drizzle-orm/pg-core";
import { semestersTable } from "./semesters";

export const citiesEnum = mainSchema.enum("city", [
"Trondheim",
Expand All @@ -19,4 +20,5 @@ export const departmentsTable = mainSchema.table("departments", {
export const departmentsRelations = relations(departmentsTable, ({ many }) => ({
fieldsOfStudy: many(fieldsOfStudyTable),
teams: many(teamsTable),
semesters: many(semestersTable),
}));
33 changes: 33 additions & 0 deletions db/tables/school-semester-assistant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { relations } from "drizzle-orm";
import { integer } from "drizzle-orm/pg-core";
import { mainSchema } from "./schema";
import { schoolsTable } from "./schools";
import { semestersTable } from "./semesters";
import { assistantUsersTable } from "./users";

export const schoolSemesterAssistantsTable = mainSchema.table(
"schoolSemesterAssistant",
{
schoolId: integer("schoolId").references(() => schoolsTable.id),
semesterId: integer("semesterId").references(() => semestersTable.id),
assistantUserId: integer("userId").references(() => assistantUsersTable.id),
},
);

export const schoolSemesterAssistantsRelations = relations(
schoolSemesterAssistantsTable,
({ one }) => ({
school: one(schoolsTable, {
fields: [schoolSemesterAssistantsTable.schoolId],
references: [schoolsTable.id],
}),
semester: one(semestersTable, {
fields: [schoolSemesterAssistantsTable.semesterId],
references: [semestersTable.id],
}),
assistantUser: one(assistantUsersTable, {
fields: [schoolSemesterAssistantsTable.assistantUserId],
references: [assistantUsersTable.id],
}),
}),
);
23 changes: 23 additions & 0 deletions db/tables/schools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { relations } from "drizzle-orm";
import { boolean, integer, serial, text } from "drizzle-orm/pg-core";
import { departmentsTable } from "./departments";
import { mainSchema } from "./schema";
import { schoolSemesterAssistantsTable } from "./school-semester-assistant";

export const schoolsTable = mainSchema.table("schools", {
id: serial("id").primaryKey(),
departmentId: integer("departmentId").references(() => departmentsTable.id),
name: text("name").notNull(),
contactPersonName: text("contactPersonName").notNull(),
contactPersonPhoneNumber: text("contactPersonPhoneNumber").notNull(),
contactPersonEmail: text("contactpersonEmail").notNull(),
isInternational: boolean("isInternational").notNull(),
});

export const schoolsRelations = relations(schoolsTable, ({ one, many }) => ({
department: one(departmentsTable, {
fields: [schoolsTable.departmentId],
references: [departmentsTable.id],
}),
semesterAssistants: many(schoolSemesterAssistantsTable),
}));
47 changes: 47 additions & 0 deletions db/tables/semesters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { relations } from "drizzle-orm";
import {
type AnyPgColumn,
date,
integer,
serial,
text,
} from "drizzle-orm/pg-core";
import { departmentsTable } from "./departments";
import { mainSchema } from "./schema";
import { schoolSemesterAssistantsTable } from "./school-semester-assistant";
import { teamSemesterUsersTable } from "./team-semester-user";

export const semestersTable = mainSchema.table("semesters", {
id: serial("id").primaryKey(),
lastSemesterId: integer("lastSemesterId").references(
(): AnyPgColumn => semestersTable.id,
),
semesterStartDate: date("semesterStartDate").notNull(),
semesterEndDate: date("semesterEndDate").notNull(),
recruitmentStartDate: date("recruitmentStartDate").notNull(),
recruitmentEndDate: date("recruitmentEndDate").notNull(),
departmentId: integer("departmentId")
.notNull()
.references(() => departmentsTable.id),
name: text("name").notNull(),
});

export const semestersRelations = relations(
semestersTable,
({ one, many }) => ({
department: one(departmentsTable, {
fields: [semestersTable.departmentId],
references: [departmentsTable.id],
}),
lastSemester: one(semestersTable, {
fields: [semestersTable.lastSemesterId],
references: [semestersTable.id],
}),
nextSemester: one(semestersTable, {
fields: [semestersTable.id],
references: [semestersTable.lastSemesterId],
}),
schoolAssistants: many(schoolSemesterAssistantsTable),
teamUsers: many(teamSemesterUsersTable),
}),
);
30 changes: 30 additions & 0 deletions db/tables/team-semester-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { relations } from "drizzle-orm";
import { integer } from "drizzle-orm/pg-core";
import { mainSchema } from "./schema";
import { semestersTable } from "./semesters";
import { teamsTable } from "./teams";
import { teamUsersTable } from "./users";

export const teamSemesterUsersTable = mainSchema.table("teamSemesterUser", {
teamId: integer("teamId").references(() => teamsTable.id),
semesterId: integer("semesterId").references(() => semestersTable.id),
teamUserId: integer("teamUserId").references(() => teamUsersTable.id),
});

export const teamSemesterUsersRelations = relations(
teamSemesterUsersTable,
({ one }) => ({
team: one(teamsTable, {
fields: [teamSemesterUsersTable.teamId],
references: [teamsTable.id],
}),
semester: one(semestersTable, {
fields: [teamSemesterUsersTable.semesterId],
references: [semestersTable.id],
}),
teamUser: one(teamUsersTable, {
fields: [teamSemesterUsersTable.teamUserId],
references: [teamUsersTable.id],
}),
}),
);
4 changes: 2 additions & 2 deletions db/tables/teams.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { teamApplicationsTable } from "@/db/tables/applications";
import { departmentsTable } from "@/db/tables/departments";
import { mainSchema } from "@/db/tables/schema";
import { teamUsersTable } from "@/db/tables/users";
import { relations } from "drizzle-orm";
import { boolean, serial, text, timestamp } from "drizzle-orm/pg-core";
import { integer } from "drizzle-orm/pg-core";
import { teamSemesterUsersTable } from "./team-semester-user";

export const teamsTable = mainSchema.table("teams", {
id: serial("id").primaryKey(),
Expand All @@ -26,5 +26,5 @@ export const teamRelations = relations(teamsTable, ({ one, many }) => ({
references: [departmentsTable.id],
}),
teamApplications: many(teamApplicationsTable),
teamMembers: many(teamUsersTable),
teamUsersInSemester: many(teamSemesterUsersTable),
}));
27 changes: 17 additions & 10 deletions db/tables/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { integer, serial, text } from "drizzle-orm/pg-core";
import { expensesTable } from "@/db/tables/expenses";
import { fieldsOfStudyTable } from "@/db/tables/fields-of-study";
import { teamsTable } from "@/db/tables/teams";
import { schoolSemesterAssistantsTable } from "./school-semester-assistant";
import { teamSemesterUsersTable } from "./team-semester-user";

export const usersTable = mainSchema.table("users", {
id: serial("id").primaryKey(),
Expand Down Expand Up @@ -36,16 +38,20 @@ export const teamUsersTable = mainSchema.table("teamUsers", {
.references(() => teamsTable.id),
username: text("username").notNull().unique(),
});
export const teamUsersRelations = relations(teamUsersTable, ({ one }) => ({
superUser: one(usersTable, {
fields: [teamUsersTable.id],
references: [usersTable.id],
}),
team: one(teamsTable, {
fields: [teamUsersTable.teamId],
references: [teamsTable.id],
export const teamUsersRelations = relations(
teamUsersTable,
({ one, many }) => ({
superUser: one(usersTable, {
fields: [teamUsersTable.id],
references: [usersTable.id],
}),
team: one(teamsTable, {
fields: [teamUsersTable.teamId],
references: [teamsTable.id],
}),
teamSemesters: many(teamSemesterUsersTable),
}),
}));
);

export const assistantUsersTable = mainSchema.table("assistantUsers", {
id: integer("id")
Expand All @@ -54,10 +60,11 @@ export const assistantUsersTable = mainSchema.table("assistantUsers", {
});
export const assistantUsersRelation = relations(
assistantUsersTable,
({ one }) => ({
({ one, many }) => ({
superUser: one(usersTable, {
fields: [assistantUsersTable.id],
references: [usersTable.id],
}),
schoolSemesters: many(schoolSemesterAssistantsTable),
}),
);
4 changes: 4 additions & 0 deletions lib/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ export function zodEnumFromObjKeys<K extends string>(
const [firstKey, ...otherKeys] = Object.keys(obj) as K[];
return z.enum([firstKey, ...otherKeys]);
}
const phoneNumberRegex = /^\d{8}$/;
export const phoneNumberParser = z
.string()
.regex(phoneNumberRegex, "Phone number must be 8 digits");
31 changes: 31 additions & 0 deletions src/request-handling/schools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { schoolsTable } from "@/db/tables/schools";
import { phoneNumberParser } from "@/lib/lib";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { serialIdParser } from "./common";

export const schoolRequestParser = z
.object({
id: serialIdParser.describe("Id of school"),
departmentId: serialIdParser.describe("Id of corresponding department"),
name: z.string().describe("Name of school"),
contactPersonName: z.string().describe("Name of contact person on school"),
contactPersonPhoneNumber: phoneNumberParser.describe(
"Phone number of contact person",
),
contactPersonEmail: z.string().describe("Email of contact person"),
isInternational: z
.boolean()
.describe("Whether the school is international or not"),
})
.strict();

export const schoolRequestToInsertParser = schoolRequestParser
.extend({
name: schoolRequestParser.shape.name.trim(),
contactPersonName: schoolRequestParser.shape.contactPersonName.trim(),
contactPersonEmail: schoolRequestParser.shape.contactPersonEmail.trim(),
})
.pipe(createInsertSchema(schoolsTable).strict().readonly());

export type NewSchool = z.infer<typeof schoolRequestToInsertParser>;
42 changes: 42 additions & 0 deletions src/request-handling/semesters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { semestersTable } from "@/db/tables/semesters";
import { timeStringParser } from "@/lib/time-parsers";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { serialIdParser } from "./common";

export const semesterRequestParser = z
.object({
id: serialIdParser.describe("Id of semester"),
lastSemesterId: serialIdParser.describe("Id of last semester"),
semesterStartDate: timeStringParser.describe("Date of semester start"),
semesterEndDate: timeStringParser.describe("Date of semester end"),
recruitmentStartDate: timeStringParser.describe(
"Date of recruitment period start",
),
recruitmentEndDate: timeStringParser.describe(
"Date of recruitment period end",
),
departmentId: serialIdParser.describe("Id of corresponding department"),
name: z.string().describe("Name of semester"),
})
.strict();

export const semesterRequestToInsertParser = semesterRequestParser
.extend({
name: semesterRequestParser.shape.name.trim(),
semesterStartDate: semesterRequestParser.shape.semesterStartDate.pipe(
z.coerce.date(),
),
semesterEndDate: semesterRequestParser.shape.semesterEndDate.pipe(
z.coerce.date(),
),
recruitmentStartDate: semesterRequestParser.shape.recruitmentStartDate.pipe(
z.coerce.date(),
),
recruitmentEndDate: semesterRequestParser.shape.recruitmentEndDate.pipe(
z.coerce.date(),
),
})
.pipe(createInsertSchema(semestersTable).strict().readonly());

export type NewSemester = z.infer<typeof semesterRequestToInsertParser>;
10 changes: 10 additions & 0 deletions src/response-handling/schools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { schoolsTable } from "@/db/tables/schools";
import { createSelectSchema } from "drizzle-zod";
import type { z } from "zod";

export const schoolsSelectSchema = createSelectSchema(schoolsTable)
.strict()
.readonly();

export type School = z.infer<typeof schoolsSelectSchema>;
export type SchoolsKey = School["id"];
Loading