diff --git a/backend/src/generated-types/graphql.ts b/backend/src/generated-types/graphql.ts index 41cc72cd9..b88243e75 100644 --- a/backend/src/generated-types/graphql.ts +++ b/backend/src/generated-types/graphql.ts @@ -35,7 +35,7 @@ export type CatalogClass = { export type CatalogItem = { __typename?: 'CatalogItem'; - classes: Array>; + classes: Array; description: Scalars['String']; gradeAverage?: Maybe; lastUpdated: Scalars['ISODate']; @@ -55,7 +55,7 @@ export type Class = { number: Scalars['String']; primarySection: Section; raw: Scalars['JSONObject']; - sections: Array>; + sections: Array
; semester: Semester; session: Scalars['String']; status: Scalars['String']; @@ -70,8 +70,8 @@ export type Class = { /** Info shared between Classes within and across semesters. */ export type Course = { __typename?: 'Course'; - classes: Array>; - crossListing?: Maybe>>; + classes: Array; + crossListing?: Maybe>; description: Scalars['String']; fromDate: Scalars['String']; gradeAverage?: Maybe; @@ -81,7 +81,7 @@ export type Course = { number: Scalars['String']; prereqs?: Maybe; raw: Scalars['JSONObject']; - sections: Array>; + sections: Array
; subject: Scalars['String']; subjectName: Scalars['String']; title: Scalars['String']; @@ -202,7 +202,7 @@ export type Query = { * * Used primarily in the catalog page. */ - catalog?: Maybe>>; + catalog?: Maybe>; class?: Maybe; course?: Maybe; /** @@ -210,7 +210,7 @@ export type Query = { * * Useful for searching for courses. */ - courseList?: Maybe>>; + courseList?: Maybe>; grade?: Maybe; /** @deprecated test */ ping: Scalars['String']; @@ -310,8 +310,8 @@ export type Section = { days?: Maybe>; enrollCount: Scalars['Int']; enrollMax: Scalars['Int']; - enrollmentHistory?: Maybe>>; - instructors?: Maybe>>; + enrollmentHistory?: Maybe>; + instructors?: Maybe>; kind: Scalars['String']; lastUpdated: Scalars['ISODate']; location?: Maybe; @@ -550,7 +550,7 @@ export type CatalogClassResolvers = { - classes?: Resolver>, ParentType, ContextType>; + classes?: Resolver, ParentType, ContextType>; description?: Resolver; gradeAverage?: Resolver, ParentType, ContextType>; lastUpdated?: Resolver; @@ -569,7 +569,7 @@ export type ClassResolvers; primarySection?: Resolver; raw?: Resolver; - sections?: Resolver>, ParentType, ContextType>; + sections?: Resolver, ParentType, ContextType>; semester?: Resolver; session?: Resolver; status?: Resolver; @@ -583,8 +583,8 @@ export type ClassResolvers = { - classes?: Resolver>, ParentType, ContextType, Partial>; - crossListing?: Resolver>>, ParentType, ContextType>; + classes?: Resolver, ParentType, ContextType, Partial>; + crossListing?: Resolver>, ParentType, ContextType>; description?: Resolver; fromDate?: Resolver; gradeAverage?: Resolver, ParentType, ContextType>; @@ -594,7 +594,7 @@ export type CourseResolvers; prereqs?: Resolver, ParentType, ContextType>; raw?: Resolver; - sections?: Resolver>, ParentType, ContextType, Partial>; + sections?: Resolver, ParentType, ContextType, Partial>; subject?: Resolver; subjectName?: Resolver; title?: Resolver; @@ -666,10 +666,10 @@ export type MutationResolvers = { - catalog?: Resolver>>, ParentType, ContextType, RequireFields>; + catalog?: Resolver>, ParentType, ContextType, RequireFields>; class?: Resolver, ParentType, ContextType, RequireFields>; course?: Resolver, ParentType, ContextType, RequireFields>; - courseList?: Resolver>>, ParentType, ContextType>; + courseList?: Resolver>, ParentType, ContextType>; grade?: Resolver, ParentType, ContextType, RequireFields>; ping?: Resolver; scheduleByID?: Resolver, ParentType, ContextType, RequireFields>; @@ -700,8 +700,8 @@ export type SectionResolvers>, ParentType, ContextType>; enrollCount?: Resolver; enrollMax?: Resolver; - enrollmentHistory?: Resolver>>, ParentType, ContextType>; - instructors?: Resolver>>, ParentType, ContextType>; + enrollmentHistory?: Resolver>, ParentType, ContextType>; + instructors?: Resolver>, ParentType, ContextType>; kind?: Resolver; lastUpdated?: Resolver; location?: Resolver, ParentType, ContextType>; diff --git a/backend/src/modules/catalog/controller.ts b/backend/src/modules/catalog/controller.ts index e9247a9d6..6c5b7ac3b 100644 --- a/backend/src/modules/catalog/controller.ts +++ b/backend/src/modules/catalog/controller.ts @@ -160,17 +160,46 @@ export function getPrimarySection(id: string, term: TermInput, classNumber: stri } export function getClassSections(id: string, term: TermInput, classNumber: string) { + // for associated sections with a lecture. Lecture assumed to be of form 00x return SectionModel .find({ "class.course.identifiers": matchCsCourseId(id), "class.session.term.name": termToString(term), - "class.number": classNumber + $or: [ + {"class.number": {$regex: new RegExp("^"+classNumber[classNumber.length-1])}}, + {"class.number": "999"} + ] }) .lean() .then(s => s.map(formatSection)) } -export function getCourse(subject: string, courseNumber: string, term?: TermInput | null) { +async function getCourseFromFilter(filter: any, term?: TermInput | null, info?: GraphQLResolveInfo | null) { + + const course = await CourseModel + .findOne(filter) + .sort({ fromDate: -1 }) + .lean() + .then(c => formatCourse(c, term)) + + if (info && getChildren(info).includes("gradeAverage")) { + + const grades : GradeType[] = [] + const gradesQuery = await GradeModel.find({ + "CourseSubjectShortNm": course.raw.classSubjectArea?.description ?? course.raw.subjectArea?.description, + "CourseNumber": course.raw.catalogNumber?.formatted + }, {GradeNm: 1, EnrollmentCnt: 1}) + for (const grade of gradesQuery) { + grades.push(grade) + } + course.gradeAverage = await getAverage(grades) + + } + + return course +} + +export function getCourse(subject: string, courseNumber: string, term?: TermInput | null, info?: GraphQLResolveInfo) { const filter: any = { "classSubjectArea.code": subject, "catalogNumber.formatted": courseNumber @@ -180,11 +209,7 @@ export function getCourse(subject: string, courseNumber: string, term?: TermInpu filter.fromDate = { $lte: getTermStartMonth(term) } } - return CourseModel - .findOne(filter) - .sort({ fromDate: -1 }) - .lean() - .then(c => formatCourse(c, term)) + return getCourseFromFilter(filter, term, info) } export function getCourseById(id: string, term?: TermInput | null) { @@ -196,11 +221,7 @@ export function getCourseById(id: string, term?: TermInput | null) { filter.fromDate = { $lte: getTermStartMonth(term) } } - return CourseModel - .findOne(filter) - .sort({ fromDate: -1 }) - .lean() - .then(c => formatCourse(c, term)) + return getCourseFromFilter(filter, term) } export function getCourseClasses(id: string, term?: TermInput | null) { @@ -218,6 +239,20 @@ export function getCourseClasses(id: string, term?: TermInput | null) { .then(c => c.map(formatClass)) } +export function getCourseSections(id: string, term?: TermInput | null) { + const filter: any = { + "class.course.identifiers": matchCsCourseId(id), + } + + if (!isNil(term)) { + filter["class.session.term.name"] = termToString(term) + } + return SectionModel + .find(filter) + .lean() + .then(s => s.map(formatSection)) +} + export function getCrossListings(displayNames: string[], term?: TermInput | null) { return displayNames.map(name => { const filter: any = { diff --git a/backend/src/modules/catalog/resolver.ts b/backend/src/modules/catalog/resolver.ts index c46593a8d..875f31250 100644 --- a/backend/src/modules/catalog/resolver.ts +++ b/backend/src/modules/catalog/resolver.ts @@ -1,10 +1,10 @@ import { CatalogModule } from "./generated-types/module-types"; -import { getCatalog, getClass, getClassById, getClassSections, getCourse, getCourseById, getCourseClasses, getCourseList, getCrossListings, getPrimarySection, getSection } from "./controller" +import { getCatalog, getClass, getClassById, getClassSections, getCourse, getCourseById, getCourseClasses, getCourseList, getCourseSections, getCrossListings, getPrimarySection, getSection } from "./controller" const resolvers: CatalogModule.Resolvers = { Query: { catalog: (_, args, __, info) => getCatalog(args.term, info), - course: (_, args) => getCourse(args.subject, args.courseNumber, args.term), + course: (_, args, __, info) => getCourse(args.subject, args.courseNumber, args.term, info), class: (_, args) => getClass(args.subject, args.courseNumber, args.term, args.classNumber), section: (_, args) => getSection(args.subject, args.courseNumber, args.term, args.classNumber, args.sectionNumber), courseList: getCourseList, @@ -49,6 +49,10 @@ const resolvers: CatalogModule.Resolvers = { crossListing: (parent) => { const cl = parent.crossListing as any return getCrossListings(cl.displayNames, cl.term) + }, + sections: (parent, { term }) => { + const c = parent.classes as any + return getCourseSections(c, term) } } } diff --git a/backend/src/modules/catalog/typedefs/catalog.ts b/backend/src/modules/catalog/typedefs/catalog.ts index c37859582..96886a54d 100644 --- a/backend/src/modules/catalog/typedefs/catalog.ts +++ b/backend/src/modules/catalog/typedefs/catalog.ts @@ -11,24 +11,23 @@ type Query { Used primarily in the catalog page. """ - catalog(term: TermInput!): [CatalogItem] + catalog(term: TermInput!): [CatalogItem!] """ Get a list of all course names across all semesters. Useful for searching for courses. """ - courseList: [CourseListItem] + courseList: [CourseListItem!] } """ Info shared between Classes within and across semesters. """ type Course @cacheControl(maxAge: 1) { - classes(term: TermInput): [Class]! - crossListing: [Course] - sections(term: TermInput, primary: Boolean): [Section]! - + classes(term: TermInput): [Class!]! + crossListing: [Course!] + sections(term: TermInput, primary: Boolean): [Section!]! description: String! fromDate: String! gradeAverage: Float @@ -51,7 +50,7 @@ Data for a specific class in a specific semester. There may be more than one Cla type Class @cacheControl(maxAge: 1) { course: Course! primarySection: Section! - sections: [Section]! + sections: [Section!]! description: String enrollCount: Int! @@ -77,7 +76,7 @@ Sections are each associated with one Class. type Section @cacheControl(maxAge: 1) { class: Class! course: Course! - enrollmentHistory: [EnrollmentDay] + enrollmentHistory: [EnrollmentDay!] ccn: Int! dateEnd: String @@ -85,7 +84,7 @@ type Section @cacheControl(maxAge: 1) { days: [Boolean!] enrollCount: Int! enrollMax: Int! - instructors: [Instructor] + instructors: [Instructor!] kind: String! location: String notes: String @@ -117,7 +116,7 @@ type CatalogItem @cacheControl(maxAge: 1) { number: String! title: String! description: String! - classes: [CatalogClass]! + classes: [CatalogClass!]! gradeAverage: Float lastUpdated: ISODate!