diff --git a/backend/package.json b/backend/package.json index 85a4d255..376d3416 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,7 +18,8 @@ "@mikro-orm/postgresql": "^6.4.6", "@mikro-orm/reflection": "^6.4.6", "dotenv": "^16.4.7", - "express": "^5.0.1" + "express": "^5.0.1", + "uuid": "^11.1.0" }, "devDependencies": { "@mikro-orm/cli": "^6.4.6", diff --git a/backend/src/entities/assigments/assignment.entity.ts b/backend/src/entities/assigments/assignment.entity.ts new file mode 100644 index 00000000..f3bd6a19 --- /dev/null +++ b/backend/src/entities/assigments/assignment.entity.ts @@ -0,0 +1,25 @@ +import {Entity, ManyToOne, OneToMany, PrimaryKey, Property} from "@mikro-orm/core"; +import {Class} from "../classes/class.entity"; +import {LearningPath} from "../content/learning-path.entity"; +import {Group} from "./group.entity" + +@Entity() +export class Assignment { + @ManyToOne({entity: () => Class, primary: true}) + within!: Class; + + @PrimaryKey({type: "number"}) + id!: number; + + @Property({type: "string"}) + title!: string; + + @Property({type: "longtext"}) + description!: string; + + @ManyToOne({entity: () => LearningPath}) + task!: LearningPath; + + @OneToMany({entity: () => Group}) + groups!: Group[]; +} diff --git a/backend/src/entities/assigments/group.entity.ts b/backend/src/entities/assigments/group.entity.ts new file mode 100644 index 00000000..48ca08d7 --- /dev/null +++ b/backend/src/entities/assigments/group.entity.ts @@ -0,0 +1,15 @@ +import {Entity, ManyToMany, ManyToOne, PrimaryKey} from "@mikro-orm/core"; +import {Assignment} from "./assignment.entity"; +import {Student} from "../users/student.entity"; + +@Entity() +export class Group { + @ManyToOne({entity: () => Assignment, primary: true}) + assignment!: Assignment; + + @PrimaryKey({type: "integer"}) + groupNumber!: number; + + @ManyToMany({entity: Student}) + members!: Student[]; +} diff --git a/backend/src/entities/classes/class-invitation.entity.ts b/backend/src/entities/classes/class-invitation.entity.ts new file mode 100644 index 00000000..c945b97e --- /dev/null +++ b/backend/src/entities/classes/class-invitation.entity.ts @@ -0,0 +1,18 @@ +import {Entity, ManyToOne} from "@mikro-orm/core"; +import {Teacher} from "../users/teacher.entity"; +import {Class} from "./class.entity"; + +/** + * Invitation of a teacher into a class (in order to teach it). + */ +@Entity() +export class TeacherInvitation { + @ManyToOne({entity: () => Teacher, primary: true}) + sender!: Teacher; + + @ManyToOne({entity: () => Teacher, primary: true}) + receiver!: Teacher; + + @ManyToOne({entity: () => Class, primary: true}) + class!: Class; +} diff --git a/backend/src/entities/classes/class-join-request.entity.ts b/backend/src/entities/classes/class-join-request.entity.ts new file mode 100644 index 00000000..1a8485aa --- /dev/null +++ b/backend/src/entities/classes/class-join-request.entity.ts @@ -0,0 +1,21 @@ +import {Entity, Enum, ManyToOne} from "@mikro-orm/core"; +import {Student} from "../users/student.entity"; +import {Class} from "./class.entity"; + +@Entity() +export class ClassJoinRequest { + @ManyToOne({entity: () => Student, primary: true}) + requester!: Student; + + @ManyToOne({entity: () => Class, primary: true}) + class!: Class; + + @Enum(() => ClassJoinRequestStatus) + status!: ClassJoinRequestStatus; +} + +export enum ClassJoinRequestStatus { + Open = "open", + Accepted = "accepted", + Declined = "declined" +} diff --git a/backend/src/entities/classes/class.entity.ts b/backend/src/entities/classes/class.entity.ts new file mode 100644 index 00000000..fa9d3cf8 --- /dev/null +++ b/backend/src/entities/classes/class.entity.ts @@ -0,0 +1,19 @@ +import {Collection, Entity, ManyToMany, PrimaryKey, Property} from "@mikro-orm/core"; +import { v4 } from 'uuid'; +import {Teacher} from "../users/teacher.entity"; +import {Student} from "../users/student.entity"; + +@Entity() +export class Class { + @PrimaryKey() + classId = v4(); + + @Property({type: "string"}) + displayName!: string; + + @ManyToMany(() => Teacher) + teachers!: Collection; + + @ManyToMany(() => Student) + students!: Collection; +} diff --git a/backend/src/entities/content/attachment.entity.ts b/backend/src/entities/content/attachment.entity.ts new file mode 100644 index 00000000..ac6edd93 --- /dev/null +++ b/backend/src/entities/content/attachment.entity.ts @@ -0,0 +1,16 @@ +import {ManyToOne, PrimaryKey, Property} from "@mikro-orm/core"; +import {LearningObject} from "./learning-object.entity"; + +export class Attachment { + @ManyToOne({entity: () => LearningObject, primary: true}) + learningObject!: LearningObject; + + @PrimaryKey({type: "integer"}) + no!: number; + + @Property({type: "string"}) + mimeType!: string; + + @Property({type: "blob"}) + content!: Buffer; +} diff --git a/backend/src/entities/content/language.ts b/backend/src/entities/content/language.ts new file mode 100644 index 00000000..b51e2c95 --- /dev/null +++ b/backend/src/entities/content/language.ts @@ -0,0 +1,6 @@ +export enum Language { + Dutch = "nl", + French = "fr", + English = "en", + Germany = "de" +} diff --git a/backend/src/entities/content/learning-object.entity.ts b/backend/src/entities/content/learning-object.entity.ts new file mode 100644 index 00000000..60db77af --- /dev/null +++ b/backend/src/entities/content/learning-object.entity.ts @@ -0,0 +1,93 @@ +import {Embeddable, Embedded, Entity, Enum, OneToMany, PrimaryKey, Property} from "@mikro-orm/core"; +import {Language} from "./language"; +import {Attachment} from "./attachment.entity"; + +@Entity() +export class LearningObject { + @PrimaryKey({type: "string"}) + hruid!: string; + + @Enum({items: () => Language, primary: true}) + language!: Language; + + @PrimaryKey({type: "string"}) + version: number = "1"; + + @PrimaryKey({type: "string"}) + author!: string; + + @Property({type: "string"}) + title!: string; + + @Property({type: "longtext"}) + description!: string; + + @Property({type: "string"}) + contentType!: string; + + @Property({type: "array"}) + keywords: string[] = []; + + @Property({type: "array", nullable: true}) + targetAges?: number[]; + + @Property({type: "bool"}) + teacherExclusive: boolean = false; + + @Property({type: "array"}) + skosConcepts!: string[]; + + @Embedded({entity: () => EducationalGoal, array: true}) + educationalGoals: EducationalGoal[] = []; + + @Property({type: "string"}) + copyright: string = "" + + @Property({type: "string"}) + license: string = "" + + @Property({type: "smallint", nullable: true}) + difficulty?: number; + + @Property({type: "integer"}) + estimatedTime!: number; + + @Property({type: "bool"}) + available: boolean = true; + + @Property({type: "string", nullable: true}) + contentLocation?: string; + + @OneToMany({entity: Attachment}) + attachments: Attachment[] = []; + + @Property({type: "blob"}) + content: Buffer; +} + +@Embeddable() +export class EducationalGoal { + @Property({type: "string"}) + source: string; + + @Property({type: "string"}) + id: string; +} + +@Embeddable() +export class ReturnValue { + @Property({type: "string"}) + callbackUrl: string; + + @Property({type: "json"}) + callbackSchema: Object; +} + +export enum ContentType { + Markdown = "text/markdown", + Image = "image/image", + Mpeg = "audio/mpeg", + Pdf = "application/pdf", + Extern = "extern", + Blockly = "Blockly" +} diff --git a/backend/src/entities/content/learning-path.entity.ts b/backend/src/entities/content/learning-path.entity.ts new file mode 100644 index 00000000..88346629 --- /dev/null +++ b/backend/src/entities/content/learning-path.entity.ts @@ -0,0 +1,50 @@ +import {Embeddable, Embedded, Entity, Enum, OneToOne, Property} from "@mikro-orm/core"; +import {Language} from "./language"; + +@Entity() +export class LearningPath { + @Enum({items: () => Language}) + language!: Language; + + @Property({type: "string"}) + title!: string; + + @Property({type: "longtext"}) + description!: string; + + @Property({type: "blob"}) + image!: string; + + @Embedded({entity: () => LearningPathNode, array: true}) + nodes: LearningPathNode[]; +} + +@Embeddable() +export class LearningPathNode { + @Property({type: "string"}) + learningObjectHruid!: string; + + @Enum({items: () => Language}) + language!: Language; + + @Property({type: "string"}) + version!: string; + + @Property({type: "longtext"}) + instruction!: string; + + @Property({type: "bool"}) + startNode!: boolean; + + @Embedded({entity: () => LearningPathTransition, array: true}) + transitions!: LearningPathTransition[]; +} + +@Embeddable() +export class LearningPathTransition { + @Property({type: "string"}) + condition!: string; + + @OneToOne({entity: () => LearningPathNode}) + next!: LearningPathNode; +} diff --git a/backend/src/entities/users/student.entity.ts b/backend/src/entities/users/student.entity.ts index ab5a537a..aead3ff6 100644 --- a/backend/src/entities/users/student.entity.ts +++ b/backend/src/entities/users/student.entity.ts @@ -1,6 +1,9 @@ import {User} from "./user.entity"; -import { Entity } from '@mikro-orm/core'; +import {Collection, Entity, ManyToMany} from '@mikro-orm/core'; +import {Class} from "../classes/class.entity"; @Entity() export class Student extends User { + @ManyToMany(() => Class) + classes!: Collection; } diff --git a/backend/src/entities/users/teacher.entity.ts b/backend/src/entities/users/teacher.entity.ts index 0b3e8c18..19aa4a5d 100644 --- a/backend/src/entities/users/teacher.entity.ts +++ b/backend/src/entities/users/teacher.entity.ts @@ -1,7 +1,9 @@ -import { Entity } from '@mikro-orm/core'; +import {Collection, Entity, ManyToMany} from '@mikro-orm/core'; import {User} from "./user.entity"; +import {Class} from "../classes/class.entity"; @Entity() export class Teacher extends User { - + @ManyToMany(() => Class) + classes!: Collection; } diff --git a/backend/src/entities/users/user.entity.ts b/backend/src/entities/users/user.entity.ts index 0da754f5..6f057ba3 100644 --- a/backend/src/entities/users/user.entity.ts +++ b/backend/src/entities/users/user.entity.ts @@ -1,7 +1,7 @@ import { Entity, PrimaryKey, Property } from '@mikro-orm/core'; @Entity({abstract: true}) -export class User { +export abstract class User { @PrimaryKey({type: "string"}) username!: string; diff --git a/package-lock.json b/package-lock.json index a00e4f54..91972f84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "dwengo-1-monorepo", "version": "0.0.1", + "license": "MIT", "workspaces": [ "backend", "frontend" @@ -26,13 +27,13 @@ "backend": { "name": "dwengo-1-backend", "version": "0.0.1", - "license": "MIT", "dependencies": { "@mikro-orm/core": "^6.4.6", "@mikro-orm/postgresql": "^6.4.6", "@mikro-orm/reflection": "^6.4.6", "dotenv": "^16.4.7", - "express": "^5.0.1" + "express": "^5.0.1", + "uuid": "^11.1.0" }, "devDependencies": { "@mikro-orm/cli": "^6.4.6", @@ -8021,6 +8022,18 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",