From a3b753a5f792fc92e51a28dacadc4ba14be0b4bf Mon Sep 17 00:00:00 2001 From: toni Date: Thu, 19 Mar 2026 23:13:00 +0100 Subject: [PATCH] exams setup --- packages/api/prisma/schema/exam.prisma | 6 ++ packages/api/prisma/schema/member.prisma | 25 ++++---- packages/api/prisma/schema/memberExam.prisma | 13 +++++ .../api/src/controllers/examsController.ts | 39 +++++++++++++ packages/api/src/dtos/examDto.ts | 40 +++++++++++++ packages/api/src/services/examsService.ts | 57 +++++++++++++++++++ 6 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 packages/api/prisma/schema/exam.prisma create mode 100644 packages/api/prisma/schema/memberExam.prisma create mode 100644 packages/api/src/controllers/examsController.ts create mode 100644 packages/api/src/dtos/examDto.ts create mode 100644 packages/api/src/services/examsService.ts diff --git a/packages/api/prisma/schema/exam.prisma b/packages/api/prisma/schema/exam.prisma new file mode 100644 index 0000000..b0d1b16 --- /dev/null +++ b/packages/api/prisma/schema/exam.prisma @@ -0,0 +1,6 @@ +model Exam { + id String @id @default(dbgenerated("uuidv7()")) + date DateTime @db.Date + title String @db.VarChar + memberExams MemberExam[] +} diff --git a/packages/api/prisma/schema/member.prisma b/packages/api/prisma/schema/member.prisma index ab09884..5232538 100644 --- a/packages/api/prisma/schema/member.prisma +++ b/packages/api/prisma/schema/member.prisma @@ -1,16 +1,17 @@ model Member { - id String @id @default(dbgenerated("uuidv7()")) @db.Uuid - vorname String? @db.VarChar - nachname String? @db.VarChar - geburtsdatum DateTime? @db.Date - geburtsort String? @db.VarChar - geschlecht String? @db.VarChar - passnummer String? @db.VarChar + id String @id @default(dbgenerated("uuidv7()")) @db.Uuid + vorname String? @db.VarChar + nachname String? @db.VarChar + geburtsdatum DateTime? @db.Date + geburtsort String? @db.VarChar + geschlecht String? @db.VarChar + passnummer String? @db.VarChar verein Boolean? - graduierung String? @db.VarChar - letztePruefung DateTime? @db.Date - kontakt String? @db.VarChar - kuendigungzum DateTime? @db.Date + graduierung String? @db.VarChar + letztePruefung DateTime? @db.Date + kontakt String? @db.VarChar + kuendigungzum DateTime? @db.Date marker Boolean? - creationDate DateTime? @default(now()) @db.Date + creationDate DateTime? @default(now()) @db.Date + memberExams MemberExam[] } diff --git a/packages/api/prisma/schema/memberExam.prisma b/packages/api/prisma/schema/memberExam.prisma new file mode 100644 index 0000000..4387202 --- /dev/null +++ b/packages/api/prisma/schema/memberExam.prisma @@ -0,0 +1,13 @@ +model MemberExam { + id String @id @default(dbgenerated("uuidv7()")) @db.Uuid + targetGrade String @db.VarChar + + memberId String @db.Uuid + member Member @relation(fields: [memberId], references: [id]) + + examId String @db.Uuid + exam Exam @relation(fields: [examId], references: [id]) + + passed Boolean? + comment String? @db.VarChar +} diff --git a/packages/api/src/controllers/examsController.ts b/packages/api/src/controllers/examsController.ts new file mode 100644 index 0000000..1c1e2cf --- /dev/null +++ b/packages/api/src/controllers/examsController.ts @@ -0,0 +1,39 @@ +import express from "express"; +import { Body, Controller, Get, Path, Post, Query, Request, Route } from "tsoa"; +import type { + ExamCreationDto, + ExamDto, + ExamWithMembersDto, + MemberExamCreationDto, + MemberExamDto, +} from "../dtos/examDto.js"; +import { inject } from "../infrastructure/di/index.js"; +import { ExamsService } from "../services/examsService.js"; + +@Route("exams") +export class ExamsController extends Controller { + private readonly examsService = inject(ExamsService); + + @Post() + public async createExam( + @Request() request: express.Request, + @Body() newExam: ExamCreationDto, + ): Promise { + return await this.examsService.createExam(newExam, request); + } + + @Get("latest") + public async getLatestExam( + @Request() request: express.Request, + ): Promise { + return await this.examsService.getLatest(request); + } + + @Post("register/{examId}") + public async registerForExam( + @Request() request: express.Request, + @Body() newRegistration: MemberExamCreationDto + ): Promise { + return await this.examsService.register(newRegistration, request); + } +} diff --git a/packages/api/src/dtos/examDto.ts b/packages/api/src/dtos/examDto.ts new file mode 100644 index 0000000..544dcc6 --- /dev/null +++ b/packages/api/src/dtos/examDto.ts @@ -0,0 +1,40 @@ +import { + type ExamModel, + type MemberExamModel, +} from "../generated/prisma/models.js"; + +export class ExamCreationDto implements Omit { + date!: Date; + title!: string; +} + +export class ExamDto implements ExamModel { + id!: string; + date!: Date; + title!: string; +} + +export class MemberExamDto implements MemberExamModel { + id!: string; + targetGrade!: string; + memberId!: string; + examId!: string; + passed!: boolean | null; + comment!: string | null; +} + +export class ExamWithMembersDto implements ExamModel { + id!: string; + date!: Date; + title!: string; + memberExams!: MemberExamDto[]; +} + +export class MemberExamCreationDto implements Omit< + MemberExamModel, + "id" | "passed" | "comment" +> { + targetGrade!: string; + memberId!: string; + examId!: string; +} diff --git a/packages/api/src/services/examsService.ts b/packages/api/src/services/examsService.ts new file mode 100644 index 0000000..2c9aba8 --- /dev/null +++ b/packages/api/src/services/examsService.ts @@ -0,0 +1,57 @@ +import type { Request } from "express"; +import type { + ExamCreationDto, + ExamDto, + ExamWithMembersDto, + MemberExamCreationDto, + MemberExamDto, +} from "../dtos/examDto.js"; +import { inject } from "../infrastructure/di/index.js"; +import { Injectable } from "../infrastructure/di/injectable-decorator.js"; +import { DatabaseService } from "./db/prisma.js"; + +@Injectable() +export class ExamsService { + private readonly db = inject(DatabaseService); + public async createExam( + newExam: ExamCreationDto, + request: Request, + ): Promise { + return await this.db.doRequest( + async prisma => + prisma.exam.create({ + data: newExam, + }), + request, + ); + } + public async getLatest( + request: Request, + ): Promise { + return await this.db.doRequest( + async prisma => + prisma.exam.findFirst({ + orderBy: { date: "desc" }, + include: { memberExams: true }, + }), + request, + ); + } + + public async register( + creationDto: MemberExamCreationDto, + request: Request, + ): Promise { + return await this.db.doRequest( + async prisma => + prisma.memberExam.create({ + data: { + examId: creationDto.examId, + memberId: creationDto.memberId, + targetGrade: creationDto.targetGrade, + }, + }), + request, + ); + } +}