query grade distributions working, and filtering by semesters working

This commit is contained in:
Sriram Hariharan
2023-03-09 16:11:42 -06:00
parent 5be0cbbbf1
commit e60242198a
19 changed files with 1123 additions and 73 deletions

View File

@@ -0,0 +1,115 @@
import { Course, Semester } from 'src/shared/types/Course';
import { CourseSQLRow, Distribution } from 'src/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,
};
const semesters: Semester[] = row.semesters.split(',').map((sem: string) => {
const [season, year] = sem.split(' ');
return { 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';
}
}