Skip to content
This repository has been archived by the owner on Oct 18, 2024. It is now read-only.

Commit

Permalink
feat: ✨ add filtering by GE and additional aggregation operations (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
ecxyzzy authored Oct 2, 2023
1 parent f278c17 commit 874872a
Show file tree
Hide file tree
Showing 12 changed files with 686 additions and 25 deletions.
1 change: 1 addition & 0 deletions apps/api/v1/graphql/src/graphql/enum.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum Quarter {
}
"The set of valid GE category codes."
enum GE {
ANY
GE_1A
GE_1B
GE_2
Expand Down
85 changes: 85 additions & 0 deletions apps/api/v1/graphql/src/graphql/grades.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type GradesSection {
courseNumber: String!
"The numeric part of the course number."
courseNumeric: Int!
"What GE categor(y/ies) this section satisfies (if any)."
geCategories: [GE!]!
"The shortened name(s) of the instructor(s) who taught the section."
instructors: [String!]!
}
Expand Down Expand Up @@ -50,6 +52,8 @@ type RawGrade {
courseNumber: String!
"The numeric part of the course number."
courseNumeric: Int!
"What GE categor(y/ies) this section satisfies (if any)."
geCategories: [GE!]!
"How many students attained an A+/A/A-."
gradeACount: Int!
"How many students attained a B+/B/B-."
Expand Down Expand Up @@ -91,6 +95,58 @@ type GradesOptions {
"The list of instructors that matched the given filters."
instructors: [String!]!
}
"An object that represents aggregate grades statistics for a course taught by an instructor."
type AggregateGradeByCourse {
"The department code."
department: String!
"The course number the section belongs to."
courseNumber: String!
"How many students attained an A+/A/A-."
gradeACount: Int!
"How many students attained a B+/B/B-."
gradeBCount: Int!
"How many students attained a C+/C/C-."
gradeCCount: Int!
"How many students attained a D+/D/D-."
gradeDCount: Int!
"How many students attained an F."
gradeFCount: Int!
"How many students attained a P."
gradePCount: Int!
"How many students attained an NP."
gradeNPCount: Int!
"How many students attained a W."
gradeWCount: Int!
"The average GPA of all assigned grades in the course."
averageGPA: Float!
}
"An object that represents aggregate grades statistics for a course taught by an instructor."
type AggregateGradeByOffering {
"The department code."
department: String!
"The course number the section belongs to."
courseNumber: String!
"The shortened name of the instructor who taught the section."
instructor: String!
"How many students attained an A+/A/A-."
gradeACount: Int!
"How many students attained a B+/B/B-."
gradeBCount: Int!
"How many students attained a C+/C/C-."
gradeCCount: Int!
"How many students attained a D+/D/D-."
gradeDCount: Int!
"How many students attained an F."
gradeFCount: Int!
"How many students attained a P."
gradePCount: Int!
"How many students attained an NP."
gradeNPCount: Int!
"How many students attained a W."
gradeWCount: Int!
"The average GPA of all assigned grades in the course."
averageGPA: Float!
}

extend type Query {
"Get the raw grade info for the given parameters."
Expand All @@ -113,6 +169,7 @@ extend type Query {
courseNumber: String
sectionCode: String
division: Division
ge: GE
excludePNP: Boolean
): AggregateGrades!
"Get the available options for the given constraints."
Expand All @@ -124,6 +181,34 @@ extend type Query {
courseNumber: String
sectionCode: String
division: Division
ge: GE
excludePNP: Boolean
): GradesOptions!
"Get the aggregate grade info, grouped by course, for the given parameters."
aggregateByCourse(
year: String
quarter: Quarter
instructor: String
department: String
courseNumber: String
sectionCode: String
division: Division
ge: GE
excludePNP: Boolean
): [AggregateGradeByCourse!]!
"""
Get the aggregate grade info, grouped by offering (course and instructor),
for the given parameters.
"""
aggregateByOffering(
year: String
quarter: Quarter
instructor: String
department: String
courseNumber: String
sectionCode: String
division: Division
ge: GE
excludePNP: Boolean
): [AggregateGradeByOffering!]!
}
6 changes: 6 additions & 0 deletions apps/api/v1/graphql/src/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export const resolvers: ApolloServerOptions<BaseContext>["resolvers"] = {
rawGrades: proxyRestApi("v1/rest/grades/raw"),
aggregateGrades: proxyRestApi("v1/rest/grades/aggregate"),
gradesOptions: proxyRestApi("v1/rest/grades/options"),
aggregateByCourse: proxyRestApi("v1/rest/grades/aggregateByCourse", {
argsTransform: geTransform,
}),
aggregateByOffering: proxyRestApi("v1/rest/grades/aggregateByOffering", {
argsTransform: geTransform,
}),
instructor: proxyRestApi("v1/rest/instructors", { pathArg: "ucinetid" }),
instructors: proxyRestApi("v1/rest/instructors"),
allInstructors: proxyRestApi("v1/rest/instructors/all"),
Expand Down
51 changes: 44 additions & 7 deletions apps/api/v1/rest/grades/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { PrismaClient } from "@libs/db";
import { createErrorResult, createOKResult, logger } from "ant-stack";
import type { InternalHandler } from "ant-stack";
import type { GradesOptions, GradesRaw } from "peterportal-api-next-types";
import type {
AggregateGradesByCourse,
AggregateGradesByOffering,
GradesOptions,
RawGrades,
} from "peterportal-api-next-types";
import { ZodError } from "zod";

import { aggregateGrades, constructPrismaQuery, lexOrd } from "./lib";
import {
aggregateGrades,
aggregateByOffering,
constructPrismaQuery,
lexOrd,
transformRow,
aggregateByCourse,
} from "./lib";
import { QuerySchema } from "./schema";

let prisma: PrismaClient;
Expand Down Expand Up @@ -33,13 +45,10 @@ export const GET: InternalHandler = async (request) => {
where: constructPrismaQuery(parsedQuery),
include: { instructors: true },
})
).map((section) => ({
...section,
instructors: section.instructors.map((instructor) => instructor.name),
}));
).map(transformRow);
switch (params.id) {
case "raw":
return createOKResult<GradesRaw>(res, headers, requestId);
return createOKResult<RawGrades>(res, headers, requestId);
case "aggregate":
return createOKResult(aggregateGrades(res), headers, requestId);
}
Expand Down Expand Up @@ -97,6 +106,34 @@ export const GET: InternalHandler = async (request) => {
requestId,
);
}
case "aggregateByCourse": {
return createOKResult<AggregateGradesByCourse>(
aggregateByCourse(
(
await prisma.gradesSection.findMany({
where: constructPrismaQuery(parsedQuery),
include: { instructors: true },
})
).map(transformRow),
),
headers,
requestId,
);
}
case "aggregateByOffering": {
return createOKResult<AggregateGradesByOffering>(
aggregateByOffering(
(
await prisma.gradesSection.findMany({
where: constructPrismaQuery(parsedQuery),
include: { instructors: true },
})
).map(transformRow),
),
headers,
requestId,
);
}
}
return createErrorResult(
400,
Expand Down
Loading

0 comments on commit 874872a

Please sign in to comment.