query grade distributions working, and filtering by semesters working
This commit is contained in:
115
src/views/lib/database/queryDistribution.ts
Normal file
115
src/views/lib/database/queryDistribution.ts
Normal 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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user