import { getGraduationDb, DatabaseEntry } from "./database-ENV.ts"; import type { Student } from "./student-common.ts"; import AnwärterPng from "../assets/Anwärter.png"; import EntdeckerPng from "../assets/Entdecker.png"; import NavigatorPng from "../assets/Navigator.png"; import PioneerPng from "../assets/Pioneer.png"; const LEVEL_NAMES = ["Anwärter", "Entdecker", "Navigator", "Pionieer"]; const LEVEL_IMAGES = [ new URL(AnwärterPng), new URL(EntdeckerPng), new URL(NavigatorPng), new URL(PioneerPng), ]; interface CoachMapping { [studentId: number]: Array; } class GraduationChange { static COACH_ASSIGNMENT_ID = -2; changeId: number; date: Date; studentId: number; change: string; reason: string; coach: string; context: any; constructor( changeId: number, date: Date, studentId: number, change: string, reason: string, coach: string, context?: any, ) { this.changeId = changeId; this.date = date; this.studentId = studentId; this.change = change; this.reason = reason; this.coach = coach; this.context = context || {}; } dbFields() { return { studentId: this.studentId, change: this.change, reason: this.reason, context: this.context, }; } static fromDBEntry(e: DatabaseEntry) { const { changeId, date, coach, json } = e; const change = JSON.parse(json); return new GraduationChange( Number(changeId), new Date(date), Number(change.studentId), change.change, change.reason, coach, change.context, ); } static newLevelChange(level: number, reason?: string, coach?: string) { const ctx = { level: level }; return new GraduationChange( -1, new Date(), -1, "LevelÄnderung", reason || "", coach || "", ctx, ); } static newGreenPointChange(mandatory: boolean, reason?: string, coach?: string) { const ctx = { mandatory: mandatory }; return new GraduationChange(-1, new Date(), -1, "Grün", reason || "", coach || "", ctx); } static newOrangePointChange(mandatory: boolean, reason?: string, coach?: string) { const ctx = { mandatory: mandatory }; return new GraduationChange(-1, new Date(), -1, "Orange", reason || "", coach || "", ctx); } static newRedPointChange(mandatory: boolean, reason?: string, coach?: string) { const ctx = { mandatory: mandatory }; return new GraduationChange(-1, new Date(), -1, "Rot", reason || "", coach || "", ctx); } static newCoachAssignmentChange(assignments: CoachMapping, coach?: string) { const ctx = { zuweisungen: assignments }; return new GraduationChange( GraduationChange.COACH_ASSIGNMENT_ID, new Date(), -1, "CoachZuweisung", "", coach || "", ctx, ); } } class Graduation { student: Student; coaches: Array = []; green: number = 0; orange: number = 0; red: number = 0; mandatory: number = 0; level: number = 1; changes: Array = []; constructor(student: Student) { this.student = student; student.graduation = this; } static from(student: Student, changes: Array) { const grad = new Graduation(student); // The parsing logic assumes that changes are ordered chronologically changes.sort((a, b) => a.date.valueOf() - b.date.valueOf()); for (const change of changes) { grad.addChange(change); } return grad; } addChange(change: GraduationChange) { change.studentId = this.student.id; switch (change.change) { case "Grün": this.green++; break; case "Orange": this.orange++; break; case "Rot": this.red++; break; case "LevelÄnderung": this.level = Number(change.context.level); // Reset the effective marks this.green = 0; this.orange = 0; this.red = 0; break; case "CoachZuweisung": // Reset the effective marks if (this.student.id in change.context.zuweisungen) this.coaches = change.context.zuweisungen[this.student.id]; break; default: throw new Error(`Unexpected change: ${change}`); } if (change.context.mandatory) { this.mandatory++; } this.changes.push(change); } removeChange(change: GraduationChange) { let changes: Array = this.changes; // Reset the graduation this.coaches = []; this.green = 0; this.orange = 0; this.red = 0; this.mandatory = 0; this.level = 1; this.changes = []; for (const toAdd of changes.filter(function (el) { return el.changeId !== -1 ? el.changeId != change.changeId : el.date != change.date; })) { this.addChange(toAdd); } } /** * Return the URI of the icon representing the graduation level. */ getIcon() { return LEVEL_IMAGES[this.level - 1]; } /** * Return the name of the current graduation level. */ getLevelName() { return LEVEL_NAMES[this.level - 1]; } /** * Return the combined value of all points. */ getNettoPoints(): number { return this.green - this.orange - this.red * 2; } } async function submitGraduationChange(change: GraduationChange, graduation?: Graduation) { if (typeof graduation !== "undefined") graduation.addChange(change); const db = await getGraduationDb(); return await db.saveEntry(JSON.stringify(change.dbFields())); } async function deleteGraduationChange(graduation: Graduation, change: GraduationChange) { graduation.removeChange(change); const db = await getGraduationDb(); return await db.deleteEntries([change.changeId]); } export { LEVEL_NAMES, LEVEL_IMAGES, GraduationChange, Graduation, submitGraduationChange, deleteGraduationChange, };