@@ -18,6 +18,8 @@ import {
1818 findActiveMembership ,
1919 getAcademicStart ,
2020 getNextAcademicStart ,
21+ getNextSemesterStart ,
22+ getCurrentSemesterStart ,
2123} from "@dotkomonline/types"
2224import { createS3PresignedPost , getCurrentUTC , slugify } from "@dotkomonline/utils"
2325import { trace } from "@opentelemetry/api"
@@ -120,15 +122,31 @@ export function getUserService(
120122 }
121123
122124 // Master degree always takes precedence over bachelor every single time.
123- const isMasterStudent = masterProgramme !== undefined
124- const distanceFromStartInYears = isMasterStudent
125- ? membershipService . findMasterStartYearDelta ( courses )
126- : membershipService . findBachelorStartYearDelta ( courses )
127- const estimatedStudyStart = subYears ( getAcademicStart ( getCurrentUTC ( ) ) , distanceFromStartInYears )
128-
129- // NOTE: We grant memberships for at most one year at a time. If you are granted membership after new-years, you
130- // will only keep the membership until the start of the next school year.
131- const endDate = getNextAcademicStart ( )
125+ const study = masterProgramme !== undefined ? "MASTER" : "BACHELOR"
126+ const estimatedStudyStart = membershipService . findEstimatedStudyStart ( study , courses )
127+
128+ // We grant memberships for one semester at a time. This has some trade-offs, and natural alternative end dates are:
129+ // 1. One semester (what we use)
130+ // 2. School year (one or two semesters--until next Autumn semester, earlier referred to as the next
131+ // "academic start")
132+ // 3. Entire degree (three years for Bachelor's and two years for Master's)
133+ //
134+ // The longer each membership lasts, the fewer times you need to calculate the grade and other information. This
135+ // reduces the number of opportunities for wrong calculations, but also make the system less flexible. Sometimes
136+ // students take a Bachelor's degree over a span of two years. Other times they change study. We choose the tradeoff
137+ // where you have this flexibility, even though it costs us an increase in manual adjustments. You most often need
138+ // to manually adjust someone's membership if someone:
139+ // a) Failed at least one of their courses a semester.
140+ // b) Has a very weird study plan due to previous studies.
141+ // c) Have been an exchange student and therefore not have done all their courses in the "correct" order
142+ // (according to our system anyway), where they have a "hole" in their course list exposed to us.
143+ //
144+ // We have decided it is best to manually adjust the membership in any nonlinear case, versus trying to correct for
145+ // fairly common cases like exchange students automatically. We never want this heuristic to overestimate someone's
146+ // grade. This is because we deem it generally less beneficial to be in a lower grade (because in practice the older
147+ // students usually have priority for attendance), increasing their chances of reaching out to correct the error.
148+ const endDate = getNextSemesterStart ( )
149+ const startDate = getCurrentSemesterStart ( )
132150
133151 // Master's programme takes precedence over bachelor's programme.
134152 if ( masterProgramme !== undefined ) {
@@ -148,18 +166,22 @@ export function getUserService(
148166 logger . info ( "Estimated end date for the master's programme to be %s" , endDate . toUTCString ( ) )
149167 return {
150168 type : "MASTER_STUDENT" ,
151- start : estimatedStudyStart ,
169+ start : startDate ,
152170 end : endDate ,
153171 specialization : code ,
172+ grade : estimatedStudyStart . grade ,
173+ semester : estimatedStudyStart . semester ,
154174 }
155175 }
156176
157177 logger . info ( "Estimated end date for the bachelor's programme to be %s" , endDate . toUTCString ( ) )
158178 return {
159179 type : "BACHELOR_STUDENT" ,
160- start : estimatedStudyStart ,
180+ start : startDate ,
161181 end : endDate ,
162182 specialization : null ,
183+ grade : estimatedStudyStart . grade ,
184+ semester : estimatedStudyStart . semester ,
163185 }
164186 }
165187
0 commit comments