[CourseStats][Attempts] Count attempts properly finally

This commit is contained in:
Aaro Koivukangas
2026-08-24 17:09:21 +03:00
parent fa0f88b9af
commit d1c3baeac4
2 changed files with 61 additions and 26 deletions
@@ -94,7 +94,32 @@ export class CourseYearlyStatsCounter {
}
}
public markCreditToGroup(
/** Add student's credit to attempts, attempt-stats include possibly duplicate students */
public markAttemptToGroup(
studentNumber: string,
passed: boolean,
grade: string,
groupCode: number,
groupName: string | Name,
courseCode: string,
yearCode: number
) {
this.initGroup(groupCode, groupName, courseCode, yearCode)
const { grades, categories } = this.groups[groupCode].attempts
grades[grade] ??= []
grades[grade].push(studentNumber)
if (passed) {
categories.passed.push(studentNumber)
} else {
categories.failed.push(studentNumber)
}
}
/** Add student's credit to student-stats, student-stats include only unique students */
public markStudentGradeToGroup(
studentNumber: string,
passed: boolean,
grade: string,
@@ -106,24 +131,11 @@ export class CourseYearlyStatsCounter {
) {
this.initGroup(groupCode, groupName, courseCode, yearCode)
// mark credit to attempts
const { grades, categories } = this.groups[groupCode].attempts
grades[grade] ??= []
grades[grade].push(studentNumber)
if (passed) {
categories.passed.push(studentNumber)
} else {
categories.failed.push(studentNumber)
}
// mark best effort grade
const current = this.groups[groupCode].students.grades[studentNumber]
// Don't add students to student stats based on improved grades
if (creditTypeCode === CreditTypeCode.IMPROVED) return
const current = this.groups[groupCode].students.grades[studentNumber]
if (current?.passed && !passed) return
if (current?.passed && Number(grade) <= Number(current?.grade)) return
this.groups[groupCode].students.grades[studentNumber] = { grade, passed }
+33 -10
View File
@@ -220,9 +220,18 @@ export const getYearlyStatsOfNew = async (
const counter = new CourseYearlyStatsCounter()
// A single-course credit group can contain more than one real attempt for the same course code
// (e.g. failed once and passed on a retry). parseCredit collapses a group into a single
// attainment for student-stats (no student is counted twice), but attempt-stats (include all attempts for a course)
// need every attempt counted in its own year/semester.
// Substitution groups are left untouched
const splitIntoAttempts = (group: Credit[]): Credit[][] =>
isSingleCourse(group) && group.length > 1 ? group.map(credit => [credit]) : [group]
for (const creditGroup of creditGroups) {
if (!creditGroup?.length) continue
// if (creditGroup.every(credit => CreditModel.improved(credit))) continue
const studyRightElements = studentNumberToSrElementsMap[creditGroup.at(0)?.student_studentnumber ?? 0] ?? []
const {
studentNumber,
@@ -237,12 +246,7 @@ export const getYearlyStatsOfNew = async (
courseCode: creditCourseCode,
credits,
creditTypeCode,
} = parseCredit(
creditGroup,
anonymizationSalt,
courseCode,
studentNumberToSrElementsMap[creditGroup.at(0)?.student_studentnumber ?? 0] ?? []
)
} = parseCredit(creditGroup, anonymizationSalt, courseCode, studyRightElements)
counter.markStudyProgramme(
studentNumber,
@@ -258,7 +262,7 @@ export const getYearlyStatsOfNew = async (
// Credits/attainments have quaranteed matching attainment_date and semesters/years
const groupCode = separate ? semesterCode : yearCode
const groupName = separate ? semesterName : yearName
counter.markCreditToGroup(
counter.markStudentGradeToGroup(
studentNumber,
passed,
grade,
@@ -270,8 +274,27 @@ export const getYearlyStatsOfNew = async (
)
// Don't add students to student stats based on improved grades
if (creditTypeCode === CreditTypeCode.IMPROVED) continue
counter.markCreditToStudentCategories(studentNumber, attainmentDate, groupCode)
if (creditTypeCode !== CreditTypeCode.IMPROVED) {
counter.markCreditToStudentCategories(studentNumber, attainmentDate, groupCode)
}
// The same group can include a failed and a passed credit in which case the parseCredit
// would return only the passed credit. Hence we split the failed and passed credit into
// two separate groups
for (const attemptCredits of splitIntoAttempts(creditGroup)) {
const attempt = parseCredit(attemptCredits, anonymizationSalt, courseCode, studyRightElements)
const attemptGroupCode = separate ? attempt.semesterCode : attempt.yearCode
const attemptGroupName = separate ? attempt.semesterName : attempt.yearName
counter.markAttemptToGroup(
attempt.studentNumber,
attempt.passed,
attempt.grade,
attemptGroupCode,
attemptGroupName,
attempt.courseCode,
attempt.yearCode
)
}
}
for (const enrollments of enrollmentGroups) {