1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
import { getGraduationDb, DatabaseEntry } from "./database-ENV.ts";
import type { Student } from "./student-common.ts";
import NeulingPng from "../assets/Neuling.png";
import EntdeckerPng from "../assets/Entdecker.png";
import NavigatorPng from "../assets/Navigator.png";
import PioneerPng from "../assets/Pioneer.png";
const LEVEL_NAMES = ["Neuling", "Entdecker", "Navigator", "Pionieer"];
const LEVEL_IMAGES = [
new URL(NeulingPng),
new URL(EntdeckerPng),
new URL(NavigatorPng),
new URL(PioneerPng),
];
class GraduationChange {
date: Date;
studentId: number;
change: string;
reason: string;
coach: string;
context: any;
constructor(
date: Date,
studentId: number,
change: string,
reason: string,
coach: string,
context?: any,
) {
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 { date, coach, json } = e;
const change = JSON.parse(json);
return new GraduationChange(
new Date(date),
change.change,
change.student,
change.reason,
coach,
change.context,
);
}
static newLevelChange(level: number, reason?: string, coach?: string) {
const ctx = { level: level };
return new GraduationChange(new Date(), -1, "LevelChange", 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);
}
static newOrangePointChange(mandatory: boolean, reason?: string, coach?: string) {
const ctx = { mandatory: mandatory };
return new GraduationChange(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);
}
}
class Graduation {
student: Student;
green: number;
orange: number;
red: number;
mandatory: number;
level: number;
changes: Array<GraduationChange>;
constructor(
student: Student,
green: number,
orange: number,
red: number,
mandatory: number,
level: number,
changes: Array<GraduationChange>,
) {
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<GraduationChange>) {
let green = 0;
let orange = 0;
let red = 0;
let mandatory = 0;
let level = 1;
// 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++;
}
}
return new Graduation(student, green, orange, red, mandatory, level, changes);
}
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);
}
/**
* 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(graduation: Graduation, change: GraduationChange) {
graduation.addChange(change);
const db = await getGraduationDb();
return db.saveEntry(JSON.stringify(change.dbFields()));
}
export { LEVEL_NAMES, LEVEL_IMAGES, GraduationChange, Graduation, submitGraduationChange };
|