Files
UT-Registration-Plus/src/views/lib/database/queryDistribution.ts
doprz 8a6e9070e0 chore: lint-format-docs-tests-bugfixes (#105)
* docs: add jsdoc

* feat: change enums to as const objects

* chore(test): add themeColors.test.ts

* fix: fix tests and bugs with strings.ts util

* fix: path alias imports and tsconfig file bug

* fix: remove --max-warnings 0
2024-03-06 15:11:27 -06:00

125 lines
4.0 KiB
TypeScript

import type { Course, Semester } from '@shared/types/Course';
import type { CourseSQLRow, Distribution } from '@shared/types/Distribution';
import { initializeDB } from './initializeDB';
/**
* fetches the aggregate distribution of grades for a given course from the course db, and the semesters that we have data for
* @param course the course to fetch the distribution for
* @returns a Distribution object containing the distribution of grades for the course, and
* an array of semesters that we have the distribution for
*/
export async function queryAggregateDistribution(course: Course): Promise<[Distribution, Semester[]]> {
const db = await initializeDB();
const query = generateQuery(course, null);
const res = db.exec(query)?.[0];
if (!res?.columns?.length) {
throw new NoDataError(course);
}
let row: Required<CourseSQLRow> = {} as Required<CourseSQLRow>;
res.columns.forEach((col, i) => {
row[res.columns[i]] = res.values[0][i];
});
const distribution: Distribution = {
A: row.a2,
'A-': row.a3,
'B+': row.b1,
B: row.b2,
'B-': row.b3,
'C+': row.c1,
C: row.c2,
'C-': row.c3,
'D+': row.d1,
D: row.d2,
'D-': row.d3,
F: row.f,
};
// the db file for some reason has duplicate semesters, so we use a set to remove duplicates
const rawSemesters = new Set<string>();
row.semesters.split(',').forEach((sem: string) => {
rawSemesters.add(sem);
});
const semesters: Semester[] = [];
rawSemesters.forEach((sem: string) => {
const [season, year] = sem.split(' ');
semesters.push({ year: parseInt(year, 10), season: season as Semester['season'] });
});
return [distribution, semesters];
}
/**
* Creates a SQL query that we can execute on the database to get the distribution of grades for a given course in a given semester
* @param course the course to fetch the distribution for
* @param semester the semester to fetch the distribution for OR null if we want the aggregate distribution
* @returns a SQL query string
*/
function generateQuery(course: Course, semester: Semester | null): string {
const profName = course.instructors[0]?.fullName;
const query = `
select * from ${semester ? 'grades' : 'agg'}
where dept like '%${course.department}%'
${profName ? `and prof like '%${profName}%'` : ''}
and course_nbr like '%${course.number}%'
${semester ? `and sem like '%${semester.season} ${semester.year}%'` : ''}
order by a1+a2+a3+b1+b2+b3+c1+c2+c3+d1+d2+d3+f desc
`;
return query;
}
/**
* fetches the distribution of grades for a semester for a given course from the course db
* @param course the course to fetch the distribution for
* @param semester the semester to fetch the distribution for
* @returns a Distribution object containing the distribution of grades for the course
*/
export async function querySemesterDistribution(course: Course, semester: Semester): Promise<Distribution> {
const db = await initializeDB();
const query = generateQuery(course, semester);
const res = db.exec(query)?.[0];
if (!res?.columns?.length) {
throw new NoDataError(course);
}
let row: Required<CourseSQLRow> = {} as Required<CourseSQLRow>;
res.columns.forEach((col, i) => {
row[res.columns[i]] = res.values[0][i];
});
const distribution: Distribution = {
A: row.a2,
'A-': row.a3,
'B+': row.b1,
B: row.b2,
'B-': row.b3,
'C+': row.c1,
C: row.c2,
'C-': row.c3,
'D+': row.d1,
D: row.d2,
'D-': row.d3,
F: row.f,
};
return distribution;
}
/**
* A custom error class for when we don't have data for a course
*/
export class NoDataError extends Error {
constructor(course: Course) {
super(`No data for #${course.uniqueId} ${course.department} ${course.number}`);
this.name = 'NoDataError';
}
}