Skip to content

Commit 9db8b2f

Browse files
authored
associated renamed courses correctly in grades (#1130)
1 parent 4b9e36a commit 9db8b2f

9 files changed

Lines changed: 58 additions & 49 deletions

File tree

apps/backend/src/modules/class/resolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ const resolvers: ClassModule.Resolvers = {
252252
parent.semester,
253253
parent.sessionId,
254254
parent.subject,
255-
parent.courseNumber,
255+
parent.courseId,
256256
parent.number
257257
);
258258

apps/backend/src/modules/course/resolver.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,10 @@ const resolvers: CourseModule.Resolvers = {
123123
: undefined;
124124

125125
// Cross-listed courses share a courseId, so filter to only classes
126-
// matching this specific course's subject and number.
127-
const matchesCourse = (courseClass: {
128-
subject?: string | null;
129-
courseNumber?: string | null;
130-
}) =>
131-
courseClass.subject === parent.subject &&
132-
courseClass.courseNumber === parent.number;
126+
// matching this specific course's subject. We don't filter by number
127+
// to allow renamed courses (same subject, different number) to match.
128+
const matchesCourse = (courseClass: { subject?: string | null }) =>
129+
courseClass.subject === parent.subject;
133130

134131
if (parent.classes) {
135132
let classes = [...parent.classes];
@@ -247,7 +244,7 @@ const resolvers: CourseModule.Resolvers = {
247244

248245
const gradeDistribution = await getGradeDistributionByCourse(
249246
parent.subject,
250-
parent.number
247+
parent.courseId
251248
);
252249

253250
return gradeDistribution;

apps/backend/src/modules/grade-distribution/controller.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,13 @@ export const getGradeDistributionsByCourseIds = async (courseIds: string[]) => {
165165

166166
export const getGradeDistributionByCourse = async (
167167
subject: string,
168-
number: string
168+
courseId: string
169169
) => {
170170
const subjectQuery = buildSubjectQuery(subject);
171171

172172
const sections = await SectionModel.find({
173173
subject: subjectQuery,
174-
courseNumber: number,
174+
courseId,
175175
primary: true,
176176
})
177177
.select({ sectionId: 1, termId: 1, sessionId: 1 })
@@ -185,7 +185,7 @@ export const getGradeDistributionByClass = async (
185185
semester: string,
186186
sessionId: string,
187187
subject: string,
188-
courseNumber: string,
188+
courseId: string,
189189
sectionNumber: string
190190
) => {
191191
const subjectQuery = buildSubjectQuery(subject);
@@ -195,7 +195,7 @@ export const getGradeDistributionByClass = async (
195195
semester,
196196
sessionId,
197197
subject: subjectQuery,
198-
courseNumber,
198+
courseId,
199199
number: sectionNumber,
200200
primary: true,
201201
})
@@ -212,7 +212,7 @@ export const getGradeDistributionBySemester = async (
212212
semester: string,
213213
sessionId: string,
214214
subject: string,
215-
courseNumber: string
215+
courseId: string
216216
) => {
217217
const subjectQuery = buildSubjectQuery(subject);
218218

@@ -221,7 +221,7 @@ export const getGradeDistributionBySemester = async (
221221
semester,
222222
sessionId,
223223
subject: subjectQuery,
224-
courseNumber,
224+
courseId,
225225
primary: true,
226226
})
227227
.select({ sectionId: 1, termId: 1, sessionId: 1 })
@@ -232,15 +232,15 @@ export const getGradeDistributionBySemester = async (
232232

233233
export const getGradeDistributionByInstructor = async (
234234
subject: string,
235-
courseNumber: string,
235+
courseId: string,
236236
familyName: string,
237237
givenName: string
238238
) => {
239239
const subjectQuery = buildSubjectQuery(subject);
240240

241241
const sections = await SectionModel.find({
242242
subject: subjectQuery,
243-
courseNumber,
243+
courseId,
244244
"meetings.instructors.familyName": familyName,
245245
"meetings.instructors.givenName": givenName,
246246
primary: true,
@@ -258,7 +258,7 @@ export const getGradeDistributionByInstructorAndSemester = async (
258258
semester: string,
259259
sessionId: string,
260260
subject: string,
261-
courseNumber: string,
261+
courseId: string,
262262
familyName: string,
263263
givenName: string
264264
) => {
@@ -269,7 +269,7 @@ export const getGradeDistributionByInstructorAndSemester = async (
269269
semester,
270270
sessionId,
271271
subject: subjectQuery,
272-
courseNumber,
272+
courseId,
273273
"meetings.instructors.familyName": familyName,
274274
"meetings.instructors.givenName": givenName,
275275
primary: true,

apps/backend/src/modules/grade-distribution/resolver.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const resolvers: GradeDistributionModule.Resolvers = {
1616
semester,
1717
sessionId,
1818
subject,
19-
courseNumber,
19+
courseId,
2020
classNumber,
2121
familyName,
2222
givenName,
@@ -31,7 +31,7 @@ const resolvers: GradeDistributionModule.Resolvers = {
3131
semester,
3232
sessionId,
3333
subject,
34-
courseNumber,
34+
courseId,
3535
familyName,
3636
givenName
3737
);
@@ -43,15 +43,15 @@ const resolvers: GradeDistributionModule.Resolvers = {
4343
semester,
4444
sessionId,
4545
subject,
46-
courseNumber,
46+
courseId,
4747
classNumber
4848
);
4949
}
5050

5151
if (givenName && familyName) {
5252
return await getGradeDistributionByInstructor(
5353
subject,
54-
courseNumber,
54+
courseId,
5555
familyName,
5656
givenName
5757
);
@@ -63,11 +63,11 @@ const resolvers: GradeDistributionModule.Resolvers = {
6363
semester,
6464
sessionId,
6565
subject,
66-
courseNumber
66+
courseId
6767
);
6868
}
6969

70-
return await getGradeDistributionByCourse(subject, courseNumber);
70+
return await getGradeDistributionByCourse(subject, courseId);
7171
},
7272
},
7373
};

apps/frontend/src/app/Grades/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ function FilterPanel({
388388
if (!hasInstructor && !hasSemester) {
389389
return {
390390
subject: selectedCourse.subject,
391+
courseId: selectedCourse.courseId,
391392
courseNumber: selectedCourse.number,
392393
};
393394
}
@@ -398,6 +399,7 @@ function FilterPanel({
398399
if (selectedType === InputType.Term) {
399400
return {
400401
subject: selectedCourse.subject,
402+
courseId: selectedCourse.courseId,
401403
courseNumber: selectedCourse.number,
402404
type: InputType.Term,
403405
year: parsedTerm.year,
@@ -409,6 +411,7 @@ function FilterPanel({
409411
}
410412
return {
411413
subject: selectedCourse.subject,
414+
courseId: selectedCourse.courseId,
412415
courseNumber: selectedCourse.number,
413416
type: InputType.Instructor,
414417
familyName,
@@ -423,6 +426,7 @@ function FilterPanel({
423426
if (hasSemester) {
424427
return {
425428
subject: selectedCourse.subject,
429+
courseId: selectedCourse.courseId,
426430
courseNumber: selectedCourse.number,
427431
type: InputType.Term,
428432
year: parsedTerm.year,
@@ -435,6 +439,7 @@ function FilterPanel({
435439
const [familyName, givenName] = instructor.split(", ");
436440
return {
437441
subject: selectedCourse.subject,
442+
courseId: selectedCourse.courseId,
438443
courseNumber: selectedCourse.number,
439444
type: InputType.Instructor,
440445
familyName,
@@ -936,7 +941,7 @@ export default function Grades() {
936941
setEditDraft({
937942
subject: input.subject,
938943
courseNumber: input.courseNumber,
939-
courseId: `${input.subject}-${input.courseNumber}`,
944+
courseId: input.courseId,
940945
type: input.type,
941946
givenName: "givenName" in input ? input.givenName : undefined,
942947
familyName: "familyName" in input ? input.familyName : undefined,

apps/frontend/src/components/CourseAnalytics/types.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export enum InputType {
77

88
interface BaseInput {
99
subject: string;
10-
courseNumber: string;
10+
courseId: string;
11+
courseNumber: string; // For display purposes only
1112
type?: InputType;
1213
}
1314

@@ -86,38 +87,38 @@ export const MAX_COURSES = 6;
8687

8788
export const getInputSearchParam = (input: Input) => {
8889
// Course input
89-
if (!input.type) return `${input.subject};${input.courseNumber}`;
90+
if (!input.type) return `${input.subject};${input.courseId}`;
9091

9192
// Term input
9293
if (input.type === InputType.Term) {
9394
const termSegment = `${input.year}:${input.semester}:${input.sessionId}`;
9495
if (!input.givenName && !input.familyName) {
95-
return `${input.subject};${input.courseNumber};T;${termSegment}`;
96+
return `${input.subject};${input.courseId};T;${termSegment}`;
9697
}
9798

98-
return `${input.subject};${input.courseNumber};T;${termSegment};${input.givenName}:${input.familyName}`;
99+
return `${input.subject};${input.courseId};T;${termSegment};${input.givenName}:${input.familyName}`;
99100
}
100101

101102
// Instructor input
102103
if (!input.year && !input.semester) {
103-
return `${input.subject};${input.courseNumber};P;${input.givenName}:${input.familyName}`;
104+
return `${input.subject};${input.courseId};P;${input.givenName}:${input.familyName}`;
104105
}
105106

106107
const termSegment = `${input.year}:${input.semester}:${input.sessionId}`;
107108

108-
return `${input.subject};${input.courseNumber};P;${input.givenName}:${input.familyName};${termSegment}`;
109+
return `${input.subject};${input.courseId};P;${input.givenName}:${input.familyName};${termSegment}`;
109110
};
110111

111112
export const isInputEqual = (a: Input, b: Input) => {
112113
if (!a.type && !b.type)
113-
return b.courseNumber === a.courseNumber && b.subject === a.subject;
114+
return b.courseId === a.courseId && b.subject === a.subject;
114115

115116
if (a.type !== b.type) return false;
116117

117118
if (a.type === InputType.Term && b.type === InputType.Term) {
118119
return (
119120
b.subject === a.subject &&
120-
b.courseNumber === a.courseNumber &&
121+
b.courseId === a.courseId &&
121122
b.givenName === a.givenName &&
122123
b.familyName === a.familyName &&
123124
b.year === a.year &&
@@ -129,7 +130,7 @@ export const isInputEqual = (a: Input, b: Input) => {
129130
if (a.type === InputType.Instructor && b.type === InputType.Instructor) {
130131
const baseMatch =
131132
b.subject === a.subject &&
132-
b.courseNumber === a.courseNumber &&
133+
b.courseId === a.courseId &&
133134
b.givenName === a.givenName &&
134135
b.familyName === a.familyName;
135136
if (a.year && a.semester && b.year && b.semester) {

apps/frontend/src/lib/api/grade-distributions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const READ_GRADE_DISTRIBUTION = gql`
1414
$semester: Semester
1515
$sessionId: SessionIdentifier
1616
$subject: String!
17-
$courseNumber: CourseNumber!
17+
$courseId: String!
1818
$classNumber: ClassNumber
1919
$familyName: String
2020
$givenName: String
@@ -24,7 +24,7 @@ export const READ_GRADE_DISTRIBUTION = gql`
2424
semester: $semester
2525
sessionId: $sessionId
2626
subject: $subject
27-
courseNumber: $courseNumber
27+
courseId: $courseId
2828
classNumber: $classNumber
2929
familyName: $familyName
3030
givenName: $givenName

apps/frontend/src/utils/url-course-parser.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,23 +60,27 @@ function parseInstructor(
6060
* Parses a single input string from URL parameters
6161
*
6262
* Format examples:
63-
* - Basic: "COMPSCI;61B"
64-
* - Term: "COMPSCI;61B;T;2024:Spring:001"
65-
* - Instructor: "COMPSCI;61B;P;John:DeNero"
66-
* - Full: "COMPSCI;61B;T;2024:Spring:001;John:DeNero"
63+
* - Basic: "COMPSCI;courseId123" (subject;courseId)
64+
* - Term: "COMPSCI;courseId123;T;2024:Spring:001"
65+
* - Instructor: "COMPSCI;courseId123;P;John:DeNero"
66+
* - Full: "COMPSCI;courseId123;T;2024:Spring:001;John:DeNero"
67+
*
68+
* Note: courseId is used instead of courseNumber to support renamed courses.
69+
* courseNumber is derived from the course lookup.
6770
*/
6871
function parseInputString(inputString: string): Input | null {
6972
const parts = inputString.split(";");
7073

71-
// Minimum required: subject and course number
74+
// Minimum required: subject and courseId
7275
if (parts.length < 2) return null;
7376

74-
const [subject, courseNumber] = parts;
75-
if (!subject || !courseNumber) return null;
77+
const [subject, courseId] = parts;
78+
if (!subject || !courseId) return null;
7679

77-
// Basic format: just subject and course number
80+
// Basic format: just subject and courseId
81+
// courseNumber is set to empty string - will be resolved by the component
7882
if (parts.length < 4) {
79-
return { subject, courseNumber };
83+
return { subject, courseId, courseNumber: "" };
8084
}
8185

8286
const typeToken = parts[2];
@@ -97,7 +101,8 @@ function parseInputString(inputString: string): Input | null {
97101

98102
const baseInput: Input = {
99103
subject,
100-
courseNumber,
104+
courseId,
105+
courseNumber: "",
101106
type: InputType.Term,
102107
...term,
103108
};
@@ -115,7 +120,8 @@ function parseInputString(inputString: string): Input | null {
115120

116121
const baseInput: Input = {
117122
subject,
118-
courseNumber,
123+
courseId,
124+
courseNumber: "",
119125
type: InputType.Instructor,
120126
...instructor,
121127
};

packages/gql-typedefs/grade-distribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const gradeDistributionTypeDef = gql`
1919
semester: Semester
2020
sessionId: SessionIdentifier
2121
subject: String!
22-
courseNumber: CourseNumber!
22+
courseId: String!
2323
classNumber: ClassNumber
2424
familyName: String
2525
givenName: String

0 commit comments

Comments
 (0)