Skip to content

Commit

Permalink
feat: Relationships, start grade distribution chart
Browse files Browse the repository at this point in the history
  • Loading branch information
mathhulk committed Oct 30, 2024
1 parent 58a11f8 commit 9ca2455
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 121 deletions.
10 changes: 5 additions & 5 deletions apps/backend/src/modules/class/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import {
} from "../../generated-types/graphql";
import { ClassModule } from "./generated-types/module-types";

export type IntermediateClass = Omit<
ClassModule.Class,
"course" | "term" | "primarySection" | "sections"
> & {
interface Relationships {
course: null;
term: null;
primarySection: null;
sections: null;
gradeDistribution: null;
};
}

export type IntermediateClass = Omit<ClassModule.Class, keyof Relationships> &
Relationships;

export const formatDate = (date?: string | number | Date | null) => {
if (!date) return date;
Expand Down
13 changes: 8 additions & 5 deletions apps/backend/src/modules/course/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import {
import { formatDate } from "../class/formatter";
import { CourseModule } from "./generated-types/module-types";

export type IntermediateCourse = Omit<
CourseModule.Course,
"classes" | "crossListing" | "requiredCourses" | "gradeDistribution"
> & {
interface Relationships {
classes: null;
crossListing: string[];
requiredCourses: string[];
gradeDistribution: null;
};
}

export type IntermediateCourse = Omit<
CourseModule.Course,
keyof Relationships
> &
Relationships;

export function formatCourse(course: CourseType) {
return {
Expand Down
10 changes: 6 additions & 4 deletions apps/backend/src/modules/grade-distribution/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,23 @@ export const points: { [key: string]: number } = {
D: 1,
"D-": 0.7,
"D+": 1.3,
F: 0,
};

export const getAverageGrade = (distribution: Grade[]) => {
const total = distribution.reduce((acc, { letter, count }) => {
if (points[letter]) return acc + count;
if (Object.keys(points).includes(letter)) return acc + count;

// Ignore letters not included in grade point average
// Ignore letters not included in GPA
return acc;
}, 0);

// For distributions without a grade point average, return null
// For distributions without a GPA, return null
if (total === 0) return null;

const weightedTotal = distribution.reduce((acc, { letter, count }) => {
if (points[letter]) return points[letter] * count + acc;
if (Object.keys(points).includes(letter))
return points[letter] * count + acc;

return acc;
}, 0);
Expand Down
13 changes: 8 additions & 5 deletions apps/backend/src/modules/schedule/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import { ScheduleType } from "@repo/common";

import { ScheduleModule } from "./generated-types/module-types";

interface Relationships {
classes: ScheduleModule.SelectedClassInput[];
term: null;
}

export type IntermediateSchedule = Omit<
ScheduleModule.Schedule,
"term" | "classes"
> & {
term: null;
classes: ScheduleModule.SelectedClassInput[];
};
keyof Relationships
> &
Relationships;

export const formatSchedule = async (schedule: ScheduleType) => {
return {
Expand Down
10 changes: 5 additions & 5 deletions apps/backend/src/modules/user/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { UserType } from "@repo/common";

import { UserModule } from "./generated-types/module-types";

export type IntermediateUser = Omit<
UserModule.User,
"bookmarkedClasses" | "bookmarkedCourses"
> & {
interface Relationships {
bookmarkedCourses: UserModule.BookmarkedCourseInput[];
bookmarkedClasses: UserModule.BookmarkedClassInput[];
};
}

export type IntermediateUser = Omit<UserModule.User, keyof Relationships> &
Relationships;

export const formatUser = (user: UserType) => {
return {
Expand Down
138 changes: 82 additions & 56 deletions apps/frontend/src/components/Class/Grades/index.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,104 @@
import { useMemo } from "react";

import {
Bar,
BarChart,
CartesianGrid,
LabelList,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
} from "recharts";

import useClass from "@/hooks/useClass";
import { Grade } from "@/lib/api";

import styles from "./Grades.module.scss";

const data = [
{
grade: "A",
percentage: 20,
average: 25,
},
{
grade: "B",
percentage: 15,
average: 20,
},
{
grade: "C",
percentage: 10,
average: 15,
},
{
grade: "D",
percentage: 5,
average: 10,
},
{
grade: "F",
percentage: 2.5,
average: 5,
},
{
grade: "Pass",
percentage: 35,
average: 20,
},
{
grade: "Not pass",
percentage: 17.5,
average: 5,
},
export const points: { [key: string]: number } = {
A: 4,
"A-": 3.7,
"A+": 4,
B: 3,
"B-": 2.7,
"B+": 3.3,
C: 2,
"C-": 1.7,
"C+": 2.3,
D: 1,
"D-": 0.7,
"D+": 1.3,
F: 0,
};

const letters = [
"A+",
"A",
"A-",
"B+",
"B",
"B-",
"C+",
"C",
"C-",
"D",
"F",
"P",
"NP",
];

export default function Grades() {
const {
class: {
gradeDistribution,
course: { gradeDistribution: courseGradeDistribution },
},
} = useClass();

const data = useMemo(() => {
const getTotal = (distribution: Grade[]) =>
distribution.reduce((acc, grade) => acc + grade.count, 0);

const classTotal = getTotal(gradeDistribution.distribution);
const courseTotal = getTotal(courseGradeDistribution.distribution);

return letters.map((letter) => {
const getCount = (distribution: Grade[]) =>
distribution.find((grade) => grade.letter === letter)?.count || 0;

return {
letter,
class: getCount(gradeDistribution.distribution) / classTotal,
course: getCount(courseGradeDistribution.distribution) / courseTotal,
};
});
}, [gradeDistribution, courseGradeDistribution]);

return (
<div className={styles.root}>
<ResponsiveContainer width="100%" height="100%">
<BarChart width={730} height={250} data={data}>
<CartesianGrid strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="grade" fill="var(--label-color)" tickMargin={8} />
<CartesianGrid
strokeDasharray="3 3"
vertical={false}
stroke="var(--border-color)"
/>
<Legend />
<Bar dataKey="percentage" fill="var(--blue-500)">
<LabelList
dataKey="percentage"
position="insideTop"
offset={16}
fontSize={12}
fill="var(--label-color)"
/>
</Bar>
<Bar dataKey="average" fill="var(--label-color)">
<LabelList
dataKey="average"
position="top"
fontSize={12}
fill="var(--label-color)"
/>
</Bar>
<Tooltip cursor={{ fillOpacity: 0.1 }} />
{gradeDistribution.average && (
<Bar dataKey="class" fill="var(--blue-500)" name="Fall 2024" />
)}
<Bar
dataKey="course"
fill="var(--amber-500)"
name="Fall 2014 - Spring 2024"
/>
<XAxis
dataKey="letter"
tickMargin={8}
tick={{ fill: "var(--paragraph-color)", fontSize: 12 }}
stroke="var(--label-color)"
/>
</BarChart>
</ResponsiveContainer>
</div>
Expand Down
58 changes: 57 additions & 1 deletion apps/frontend/src/lib/api/classes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { gql } from "@apollo/client";

import { ICourse } from ".";
import { GradeDistribution, ICourse } from ".";
import { ITerm, Semester } from "./terms";

export enum InstructionMethod {
Expand Down Expand Up @@ -194,6 +194,7 @@ export interface IClass {
primarySection: ISection;
sections: ISection[];
term: ITerm;
gradeDistribution: GradeDistribution;

// Attributes
session: string;
Expand Down Expand Up @@ -235,11 +236,26 @@ export const READ_CLASS = gql`
unitsMin
gradingBasis
finalExam
gradeDistribution {
average
distribution {
letter
count
}
}
course {
title
description
classes {
year
semester
}
gradeDistribution {
average
distribution {
letter
count
}
}
academicCareer
requirements
Expand Down Expand Up @@ -308,3 +324,43 @@ export const READ_CLASS = gql`
}
}
`;

export interface GetClassesResponse {
catalog: ICourse[];
}

export const GET_CLASSES = gql`
query GetClasses($year: Int!, $semester: Semester!) {
catalog(year: $year, semester: $semester) {
subject
number
title
gradeDistribution {
average
}
academicCareer
classes {
subject
courseNumber
number
title
unitsMax
unitsMin
finalExam
gradingBasis
primarySection {
component
online
open
enrollCount
enrollMax
waitlistCount
waitlistMax
meetings {
days
}
}
}
}
}
`;
Loading

0 comments on commit 9ca2455

Please sign in to comment.