From 2c78bd116108cbf6843d75fa2570fb457918df20 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Mon, 10 Aug 2026 14:20:42 +0200 Subject: graduation: support change deletion, coach assignment Also reset points after a level change. --- src/graduation-common.ts | 180 ++++++++++++++++++++++++++++++----------------- 1 file changed, 117 insertions(+), 63 deletions(-) (limited to 'src/graduation-common.ts') diff --git a/src/graduation-common.ts b/src/graduation-common.ts index b43b4af..ca4efde 100644 --- a/src/graduation-common.ts +++ b/src/graduation-common.ts @@ -15,7 +15,14 @@ const LEVEL_IMAGES = [ new URL(PioneerPng), ]; +interface CoachMapping { + [studentId: number]: string; +} + class GraduationChange { + static COACH_ASSIGNMENT_ID = -2; + + changeId: number; date: Date; studentId: number; change: string; @@ -24,6 +31,7 @@ class GraduationChange { context: any; constructor( + changeId: number, date: Date, studentId: number, change: string, @@ -31,6 +39,7 @@ class GraduationChange { coach: string, context?: any, ) { + this.changeId = changeId; this.date = date; this.studentId = studentId; this.change = change; @@ -49,9 +58,10 @@ class GraduationChange { } static fromDBEntry(e: DatabaseEntry) { - const { date, coach, json } = e; + const { changeId, date, coach, json } = e; const change = JSON.parse(json); return new GraduationChange( + Number(changeId), new Date(date), Number(change.studentId), change.change, @@ -63,100 +73,131 @@ class GraduationChange { static newLevelChange(level: number, reason?: string, coach?: string) { const ctx = { level: level }; - return new GraduationChange(new Date(), -1, "LevelChange", reason || "", coach || "", ctx); + 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(new Date(), -1, "Grün", reason || "", coach || "", ctx); + 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(new Date(), -1, "Orange", reason || "", coach || "", ctx); + 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(new Date(), -1, "Rot", reason || "", coach || "", ctx); + 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; - green: number; - orange: number; - red: number; - mandatory: number; - level: number; - changes: Array; - - constructor( - student: Student, - green: number, - orange: number, - red: number, - mandatory: number, - level: number, - changes: Array, - ) { + coach: string = ""; + green: number = 0; + orange: number = 0; + red: number = 0; + mandatory: number = 0; + level: number = 1; + changes: Array = []; + + constructor(student: Student) { this.student = student; - this.green = green; - this.orange = orange; - this.red = red; - this.level = level; - this.mandatory = mandatory; - this.changes = changes; student.graduation = this; } static from(student: Student, changes: Array) { - let green = 0; - let orange = 0; - let red = 0; - let mandatory = 0; - let level = 1; + 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) { - switch (change.change) { - case "Grün": - green++; - break; - - case "Orange": - orange++; - break; - - case "Rot": - red++; - break; - - case "LevelChange": - level = Number(change.context.level); - break; - - default: - throw new Error(`Unexpected change: ${change}`); - } - - if (change.context.mandatory) { - mandatory++; - } + grad.addChange(change); } - return new Graduation(student, green, orange, red, mandatory, level, changes); + return grad; } addChange(change: GraduationChange) { change.studentId = this.student.id; - // TODO(muhq): Implement incremental addition - let changes = this.changes; - changes.push(change); - // This updates the student.graduation reference as well - Graduation.from(this.student, changes); + + 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.coach = 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.coach = ""; + 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); + } } /** @@ -187,4 +228,17 @@ async function submitGraduationChange(graduation: Graduation, change: Graduation return db.saveEntry(JSON.stringify(change.dbFields())); } -export { LEVEL_NAMES, LEVEL_IMAGES, GraduationChange, Graduation, submitGraduationChange }; +async function deleteGraduationChange(graduation: Graduation, change: GraduationChange) { + graduation.removeChange(change); + const db = await getGraduationDb(); + return db.deleteEntries([change.changeId]); +} + +export { + LEVEL_NAMES, + LEVEL_IMAGES, + GraduationChange, + Graduation, + submitGraduationChange, + deleteGraduationChange, +}; -- cgit v1.2.3