diff --git a/go.mod b/go.mod index 9880e76..24713d7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.0 require ( github.com/PuerkitoBio/goquery v1.8.1 - github.com/UTDNebula/nebula-api/api v0.0.0-20260226052950-dbe2edfa3cfe //points to the compound-key branch of the nebula-api. + github.com/UTDNebula/nebula-api/api v0.0.0-20260302171502-90e1baaaf4e1 //points to the compound-key branch of the nebula-api. github.com/chromedp/cdproto v0.0.0-20250120090109-d38428e4d9c8 github.com/chromedp/chromedp v0.12.1 github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index 05c2c37..658d07c 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapp github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= -github.com/UTDNebula/nebula-api/api v0.0.0-20260226052950-dbe2edfa3cfe h1:QUcltcZLKB+kbP3xa7h+sbOwf7vxofRGPKXHo+B35Q4= -github.com/UTDNebula/nebula-api/api v0.0.0-20260226052950-dbe2edfa3cfe/go.mod h1:vWwnuoXFE/Lo9yW6Z6DJguCtAHu0xMym+6r2IEru1v0= +github.com/UTDNebula/nebula-api/api v0.0.0-20260302171502-90e1baaaf4e1 h1:xdceHH3Y2AiC0DV0bp47zN9FvM7JdzcpJrod7SIOR5w= +github.com/UTDNebula/nebula-api/api v0.0.0-20260302171502-90e1baaaf4e1/go.mod h1:vWwnuoXFE/Lo9yW6Z6DJguCtAHu0xMym+6r2IEru1v0= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= diff --git a/parser/courseParser.go b/parser/courseParser.go index 6a102ea..b927cf5 100644 --- a/parser/courseParser.go +++ b/parser/courseParser.go @@ -53,15 +53,8 @@ func getCourse(internalCourseNumber string, session schema.AcademicSession, rowI CoursePrefix, CourseNumber := getPrefixAndNumber(classInfo) catalogYear := getCatalogYear(session) - courseKey := schema.CourseKey{ - Subject_prefix: CoursePrefix, - Course_number: CourseNumber, - Catalog_year: catalogYear, - } - course := schema.Course{ Id: primitive.NewObjectID(), - Key: courseKey, Course_number: CourseNumber, Subject_prefix: CoursePrefix, Title: utils.TrimWhitespace(rowInfo["Course Title:"].Text()), @@ -72,7 +65,7 @@ func getCourse(internalCourseNumber string, session schema.AcademicSession, rowI Activity_type: classInfo["Activity Type:"], Grading: classInfo["Grading:"], Internal_course_number: internalCourseNumber, - Catalog_year: getCatalogYear(session), + Catalog_year: catalogYear, } // Try to get lecture/lab contact hours and offering frequency from course description diff --git a/parser/courseParser_test.go b/parser/courseParser_test.go index f722735..a72ede8 100644 --- a/parser/courseParser_test.go +++ b/parser/courseParser_test.go @@ -19,7 +19,7 @@ func TestGetCourse(t *testing.T) { output := *getCourse(courseNum, testCase.Section.Academic_session, testCase.RowInfo, testCase.ClassInfo) expected := testCase.Course - diff := cmp.Diff(expected, output, cmpopts.IgnoreFields(schema.Course{}, "Id", "Enrollment_reqs", "Prerequisites", "Key", "Section_keys")) + diff := cmp.Diff(expected, output, cmpopts.IgnoreFields(schema.Course{}, "Id", "Sections", "Enrollment_reqs", "Prerequisites")) if diff != "" { t.Errorf("Failed (-expected +got)\n %s", diff) diff --git a/parser/parser_test.go b/parser/parser_test.go index 7ffca8e..1ac7bf4 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -316,7 +316,7 @@ func TestParse(t *testing.T) { t.Run(key, func(t *testing.T) { if outputCourse, ok := CoursesByKey[key]; ok { diff := cmp.Diff(expectedCourse, outputCourse, - cmpopts.IgnoreFields(schema.Course{}, "Id", "Key", "Section_keys"), + cmpopts.IgnoreFields(schema.Course{}, "Id"), ) if diff != "" { @@ -361,7 +361,7 @@ func TestParse(t *testing.T) { if outputProfessor, ok := ProfessorsByKey[key]; ok { diff := cmp.Diff(expectedProfessor, outputProfessor, - cmpopts.IgnoreFields(schema.Professor{}, "Id", "Key", "Section_keys"), + cmpopts.IgnoreFields(schema.Professor{}, "Id"), cmp.Transformer("Sections", func(sections []primitive.ObjectID) []string { result := make([]string, 0, len(sections)) for _, id := range sections { @@ -411,7 +411,7 @@ func TestParse(t *testing.T) { t.Run(key, func(t *testing.T) { if outputSection, ok := SectionsByClass[key]; ok { diff := cmp.Diff(expectedSection, outputSection, - cmpopts.IgnoreFields(schema.Section{}, "Id", "Key", "Course_key", "Professor_keys"), + cmpopts.IgnoreFields(schema.Section{}, "Id"), cmp.Transformer("Professors", func(profIds []primitive.ObjectID) []string { result := make([]string, 0, len(profIds)) for _, id := range profIds { diff --git a/parser/professorParser.go b/parser/professorParser.go index 0360ada..b0aa6e2 100644 --- a/parser/professorParser.go +++ b/parser/professorParser.go @@ -10,7 +10,7 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" ) -func parseProfessors(sectionId schema.SectionKey, rowInfo map[string]*goquery.Selection) []schema.ProfessorKey { +func parseProfessors(sectionKey schema.ProfSectionKey, rowInfo map[string]*goquery.Selection) []schema.ProfessorKey { professorText := utils.TrimWhitespace(rowInfo["Instructor(s):"].Text()) professorMatches := personRegexp.FindAllStringSubmatch(professorText, -1) var profRefs []schema.ProfessorKey = make([]schema.ProfessorKey, 0, len(professorMatches)) @@ -36,7 +36,7 @@ func parseProfessors(sectionId schema.SectionKey, rowInfo map[string]*goquery.Se prof, profExists := Professors[profKey] if profExists { - prof.Section_keys = append(prof.Section_keys, sectionId) + prof.Sections = append(prof.Sections, sectionKey) profRefs = append(profRefs, professorKey) continue } @@ -45,10 +45,9 @@ func parseProfessors(sectionId schema.SectionKey, rowInfo map[string]*goquery.Se prof.Id = primitive.NewObjectID() prof.First_name = firstName prof.Last_name = lastName - prof.Key = professorKey prof.Titles = []string{utils.TrimWhitespace(match[2])} prof.Email = utils.TrimWhitespace(match[3]) - prof.Section_keys = []schema.SectionKey{sectionId} + prof.Sections = []schema.ProfSectionKey{sectionKey} profRefs = append(profRefs, professorKey) Professors[profKey] = prof ProfessorIDMap[prof.Id] = profKey diff --git a/parser/profileLoader.go b/parser/profileLoader.go index b218cc6..4e85407 100644 --- a/parser/profileLoader.go +++ b/parser/profileLoader.go @@ -37,13 +37,6 @@ func loadProfiles(inDir string) { } profKey := prof.First_name + prof.Last_name - - professorKey := schema.ProfessorKey { - First_name: prof.First_name, - Last_name: prof.Last_name, - } - - prof.Key = professorKey Professors[profKey] = &prof ProfessorIDMap[prof.Id] = profKey } diff --git a/parser/sectionParser.go b/parser/sectionParser.go index 14bc835..58ab0cd 100644 --- a/parser/sectionParser.go +++ b/parser/sectionParser.go @@ -42,36 +42,35 @@ func parseSection(rowInfo map[string]*goquery.Selection, classInfo map[string]st classNum, courseNum := getInternalClassAndCourseNum(classInfo) session := getAcademicSession(rowInfo) courseRef := parseCourse(courseNum, session, rowInfo, classInfo) - sectionNumber := getSectionNumber(classInfo) id := primitive.NewObjectID() // Build compound keys - courseKey := courseRef.Key + courseKey := schema.CourseKey{ + Subject_prefix: courseRef.Subject_prefix, + Course_number: courseRef.Course_number, + Catalog_year: courseRef.Catalog_year, + } - if (courseKey == schema.CourseKey{}) { - courseKey = schema.CourseKey{ - Subject_prefix: courseRef.Subject_prefix, - Course_number: courseRef.Course_number, - Catalog_year: courseRef.Catalog_year, - } + courseSectionKey := schema.CourseSectionKey{ + Section_number: sectionNumber, + Term: session.Name, } - sectionKey := schema.SectionKey{ + + profSectionKey := schema.ProfSectionKey{ Subject_prefix: courseRef.Subject_prefix, Course_number: courseRef.Course_number, - Catalog_year: courseRef.Catalog_year, - Term: session.Name, Section_number: sectionNumber, + Term: session.Name, } section := schema.Section{ Id: id, - Key: sectionKey, Section_number: sectionNumber, - Course_key: courseKey, + Course: courseKey, Academic_session: session, - Professor_keys: parseProfessors(sectionKey, rowInfo), + Professors: parseProfessors(profSectionKey, rowInfo), Teaching_assistants: getTeachingAssistants(rowInfo), Internal_class_number: classNum, Instruction_mode: getInstructionMode(classInfo), @@ -85,7 +84,7 @@ func parseSection(rowInfo map[string]*goquery.Selection, classInfo map[string]st Sections[section.Id] = §ion // Append new section to course's section listing - courseRef.Section_keys = append(courseRef.Section_keys, sectionKey) + courseRef.Sections = append(courseRef.Sections, courseSectionKey) } // getInternalClassAndCourseNum returns a sections internal course and class number, diff --git a/parser/testdata/case_000/course.json b/parser/testdata/case_000/course.json index 5e342be..e401ae5 100644 --- a/parser/testdata/case_000/course.json +++ b/parser/testdata/case_000/course.json @@ -15,7 +15,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23bd8" + { + "section_number": "003", + "term": "25S" + } ], "lecture_contact_hours": "3", "laboratory_contact_hours": "0", diff --git a/parser/testdata/case_000/professors.json b/parser/testdata/case_000/professors.json index 207c908..3ca3aa2 100644 --- a/parser/testdata/case_000/professors.json +++ b/parser/testdata/case_000/professors.json @@ -17,7 +17,12 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23bd8" + { + "subject_prefix": "ACCT", + "course_number": "2301", + "section_number": "003", + "term": "25S" + } ] }, { @@ -38,7 +43,12 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23bd8" + { + "subject_prefix": "ACCT", + "course_number": "2301", + "section_number": "003", + "term": "25S" + } ] } ] diff --git a/parser/testdata/case_000/section.json b/parser/testdata/case_000/section.json index a67c0f5..a9a3c4b 100644 --- a/parser/testdata/case_000/section.json +++ b/parser/testdata/case_000/section.json @@ -1,7 +1,11 @@ { "_id": "67d07ee0c972c18731e23bd8", "section_number": "003", - "course_reference": "67d07ee0c972c18731e23bd7", + "course_key": { + "subject_prefix": "ACCT", + "course_number": "2301", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -9,8 +13,14 @@ "end_date": "2025-05-16T00:00:00-05:00" }, "professors": [ - "67d07ee0c972c18731e23bd9", - "67d07ee0c972c18731e23bda" + { + "first_name": "Naim Bugra", + "last_name": "Ozel" + }, + { + "first_name": "Jieying", + "last_name": "Zhang" + } ], "teaching_assistants": [ { diff --git a/parser/testdata/case_001/course.json b/parser/testdata/case_001/course.json index 24dcf8b..22cdf0a 100644 --- a/parser/testdata/case_001/course.json +++ b/parser/testdata/case_001/course.json @@ -15,7 +15,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23bdc" + { + "section_number": "001", + "term": "25S" + } ], "lecture_contact_hours": "3", "laboratory_contact_hours": "0", diff --git a/parser/testdata/case_001/professors.json b/parser/testdata/case_001/professors.json index 8bf8c6b..a3dcd36 100644 --- a/parser/testdata/case_001/professors.json +++ b/parser/testdata/case_001/professors.json @@ -17,7 +17,12 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23bdc" + { + "subject_prefix": "ACCT", + "course_number": "2301", + "section_number": "001", + "term": "25S" + } ] }, { @@ -38,7 +43,12 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23bdc" + { + "subject_prefix": "ACCT", + "course_number": "2301", + "section_number": "001", + "term": "25S" + } ] } ] diff --git a/parser/testdata/case_001/section.json b/parser/testdata/case_001/section.json index eeb9360..2dcdec2 100644 --- a/parser/testdata/case_001/section.json +++ b/parser/testdata/case_001/section.json @@ -1,7 +1,11 @@ { "_id": "67d07ee0c972c18731e23bdc", "section_number": "001", - "course_reference": "67d07ee0c972c18731e23bdb", + "course_key": { + "subject_prefix": "ACCT", + "course_number": "2301", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -9,8 +13,14 @@ "end_date": "2025-05-16T00:00:00-05:00" }, "professors": [ - "67d07ee0c972c18731e23bdd", - "67d07ee0c972c18731e23bde" + { + "first_name": "Jieying", + "last_name": "Zhang" + }, + { + "first_name": "Naim Bugra", + "last_name": "Ozel" + } ], "teaching_assistants": [ { diff --git a/parser/testdata/case_002/course.json b/parser/testdata/case_002/course.json index 141ff6b..47e9cec 100644 --- a/parser/testdata/case_002/course.json +++ b/parser/testdata/case_002/course.json @@ -15,7 +15,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23be0" + { + "section_number": "501", + "term": "25S" + } ], "lecture_contact_hours": "3", "laboratory_contact_hours": "0", diff --git a/parser/testdata/case_002/professors.json b/parser/testdata/case_002/professors.json index c6913f6..31aae13 100644 --- a/parser/testdata/case_002/professors.json +++ b/parser/testdata/case_002/professors.json @@ -17,7 +17,12 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23be0" + { + "subject_prefix": "BA", + "course_number": "1320", + "section_number": "501", + "term": "25S" + } ] } ] diff --git a/parser/testdata/case_002/section.json b/parser/testdata/case_002/section.json index 6eb44f5..1ecfbfd 100644 --- a/parser/testdata/case_002/section.json +++ b/parser/testdata/case_002/section.json @@ -1,7 +1,11 @@ { "_id": "67d07ee0c972c18731e23be0", "section_number": "501", - "course_reference": "67d07ee0c972c18731e23bdf", + "course_key": { + "subject_prefix": "BA", + "course_number": "1320", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -9,7 +13,10 @@ "end_date": "2025-05-16T00:00:00-05:00" }, "professors": [ - "67d07ee0c972c18731e23be1" + { + "first_name": "Peter", + "last_name": "Lewin" + } ], "teaching_assistants": [ { diff --git a/parser/testdata/case_003/course.json b/parser/testdata/case_003/course.json index 94219f8..509f336 100644 --- a/parser/testdata/case_003/course.json +++ b/parser/testdata/case_003/course.json @@ -15,7 +15,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23be3" + { + "section_number": "016", + "term": "25S" + } ], "lecture_contact_hours": "1", "laboratory_contact_hours": "0", diff --git a/parser/testdata/case_003/professors.json b/parser/testdata/case_003/professors.json index 3cb4a51..f68298c 100644 --- a/parser/testdata/case_003/professors.json +++ b/parser/testdata/case_003/professors.json @@ -17,7 +17,12 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23be3" + { + "subject_prefix": "BIOL", + "course_number": "6111", + "section_number": "016", + "term": "25S" + } ] } ] diff --git a/parser/testdata/case_003/section.json b/parser/testdata/case_003/section.json index fff4105..deacfc1 100644 --- a/parser/testdata/case_003/section.json +++ b/parser/testdata/case_003/section.json @@ -1,7 +1,11 @@ { "_id": "67d07ee0c972c18731e23be3", "section_number": "016", - "course_reference": "67d07ee0c972c18731e23be2", + "course_key": { + "subject_prefix": "BIOL", + "course_number": "6111", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -9,7 +13,10 @@ "end_date": "2025-05-16T00:00:00-05:00" }, "professors": [ - "67d07ee0c972c18731e23be4" + { + "first_name": "Tian", + "last_name": "Hong" + } ], "teaching_assistants": [], "internal_class_number": "29611", diff --git a/parser/testdata/case_004/course.json b/parser/testdata/case_004/course.json index d8c5383..491f7f5 100644 --- a/parser/testdata/case_004/course.json +++ b/parser/testdata/case_004/course.json @@ -15,7 +15,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23be6" + { + "section_number": "201", + "term": "25S" + } ], "lecture_contact_hours": "", "laboratory_contact_hours": "", diff --git a/parser/testdata/case_004/section.json b/parser/testdata/case_004/section.json index 2481524..f68422f 100644 --- a/parser/testdata/case_004/section.json +++ b/parser/testdata/case_004/section.json @@ -1,7 +1,11 @@ { "_id": "67d07ee0c972c18731e23be6", "section_number": "201", - "course_reference": "67d07ee0c972c18731e23be5", + "course_key": { + "subject_prefix": "AERO", + "course_number": "3320", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", diff --git a/parser/testdata/case_005/course.json b/parser/testdata/case_005/course.json index 9095afc..6a83d19 100644 --- a/parser/testdata/case_005/course.json +++ b/parser/testdata/case_005/course.json @@ -15,7 +15,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23be8" + { + "section_number": "002", + "term": "25S" + } ], "lecture_contact_hours": "", "laboratory_contact_hours": "", diff --git a/parser/testdata/case_005/section.json b/parser/testdata/case_005/section.json index 712c972..f3e5db8 100644 --- a/parser/testdata/case_005/section.json +++ b/parser/testdata/case_005/section.json @@ -1,7 +1,11 @@ { "_id": "67d07ee0c972c18731e23be8", "section_number": "002", - "course_reference": "67d07ee0c972c18731e23be7", + "course_key": { + "subject_prefix": "AERO", + "course_number": "4320", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", diff --git a/parser/testdata/courses.json b/parser/testdata/courses.json index 78ae4d4..954e3ea 100644 --- a/parser/testdata/courses.json +++ b/parser/testdata/courses.json @@ -16,7 +16,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23bef" + { + "section_number": "501", + "term": "25S" + } ], "lecture_contact_hours": "3", "laboratory_contact_hours": "0", @@ -41,7 +44,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23bf2" + { + "section_number": "016", + "term": "25S" + } ], "lecture_contact_hours": "1", "laboratory_contact_hours": "0", @@ -66,7 +72,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23bf5" + { + "section_number": "201", + "term": "25S" + } ], "lecture_contact_hours": "", "laboratory_contact_hours": "", @@ -91,7 +100,10 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23bf7" + { + "section_number": "002", + "term": "25S" + } ], "lecture_contact_hours": "", "laboratory_contact_hours": "", @@ -116,8 +128,14 @@ "corequisites": null, "co_or_pre_requisites": null, "sections": [ - "67d07ee0c972c18731e23bea", - "67d07ee0c972c18731e23bed" + { + "section_number": "003", + "term": "25S" + }, + { + "section_number": "001", + "term": "25S" + } ], "lecture_contact_hours": "3", "laboratory_contact_hours": "0", diff --git a/parser/testdata/professors.json b/parser/testdata/professors.json index 2a931c4..60fc1d2 100644 --- a/parser/testdata/professors.json +++ b/parser/testdata/professors.json @@ -17,8 +17,18 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23bea", - "67d07ee0c972c18731e23bed" + { + "subject_prefix": "ACCT", + "course_number": "2301", + "section_number": "003", + "term": "25S" + }, + { + "subject_prefix": "ACCT", + "course_number": "2301", + "section_number": "001", + "term": "25S" + } ] }, { @@ -39,8 +49,18 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23bea", - "67d07ee0c972c18731e23bed" + { + "subject_prefix": "ACCT", + "course_number": "2301", + "section_number": "003", + "term": "25S" + }, + { + "subject_prefix": "ACCT", + "course_number": "2301", + "section_number": "001", + "term": "25S" + } ] }, { @@ -61,7 +81,12 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23bef" + { + "subject_prefix": "BA", + "course_number": "1320", + "section_number": "501", + "term": "25S" + } ] }, { @@ -82,7 +107,12 @@ "image_uri": "", "office_hours": null, "sections": [ - "67d07ee0c972c18731e23bf2" + { + "subject_prefix": "BIOL", + "course_number": "6111", + "section_number": "016", + "term": "25S" + } ] } ] diff --git a/parser/testdata/sections.json b/parser/testdata/sections.json index 92319de..629e54a 100644 --- a/parser/testdata/sections.json +++ b/parser/testdata/sections.json @@ -2,7 +2,11 @@ { "_id": "67d07ee0c972c18731e23bef", "section_number": "501", - "course_reference": "67d07ee0c972c18731e23bee", + "course_key": { + "subject_prefix": "BA", + "course_number": "1320", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -10,7 +14,10 @@ "end_date": "2025-05-16T00:00:00-05:00" }, "professors": [ - "67d07ee0c972c18731e23bf0" + { + "first_name": "Peter", + "last_name": "Lewin" + } ], "teaching_assistants": [ { @@ -51,7 +58,11 @@ { "_id": "67d07ee0c972c18731e23bf2", "section_number": "016", - "course_reference": "67d07ee0c972c18731e23bf1", + "course_key": { + "subject_prefix": "BIOL", + "course_number": "6111", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -59,7 +70,10 @@ "end_date": "2025-05-16T00:00:00-05:00" }, "professors": [ - "67d07ee0c972c18731e23bf3" + { + "first_name": "Tian", + "last_name": "Hong" + } ], "teaching_assistants": [], "internal_class_number": "29611", @@ -89,7 +103,11 @@ { "_id": "67d07ee0c972c18731e23bf5", "section_number": "201", - "course_reference": "67d07ee0c972c18731e23bf4", + "course_key": { + "subject_prefix": "AERO", + "course_number": "3320", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -125,7 +143,11 @@ { "_id": "67d07ee0c972c18731e23bf7", "section_number": "002", - "course_reference": "67d07ee0c972c18731e23bf6", + "course_key": { + "subject_prefix": "AERO", + "course_number": "4320", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -176,7 +198,11 @@ { "_id": "67d07ee0c972c18731e23bea", "section_number": "003", - "course_reference": "67d07ee0c972c18731e23be9", + "course_key": { + "subject_prefix": "ACCT", + "course_number": "2301", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -184,8 +210,14 @@ "end_date": "2025-05-16T00:00:00-05:00" }, "professors": [ - "67d07ee0c972c18731e23beb", - "67d07ee0c972c18731e23bec" + { + "first_name": "Naim Bugra", + "last_name": "Ozel" + }, + { + "first_name": "Jieying", + "last_name": "Zhang" + } ], "teaching_assistants": [ { @@ -229,7 +261,11 @@ { "_id": "67d07ee0c972c18731e23bed", "section_number": "001", - "course_reference": "67d07ee0c972c18731e23be9", + "course_key": { + "subject_prefix": "ACCT", + "course_number": "2301", + "catalog_year": "24" + }, "section_corequisites": null, "academic_session": { "name": "25S", @@ -237,8 +273,14 @@ "end_date": "2025-05-16T00:00:00-05:00" }, "professors": [ - "67d07ee0c972c18731e23bec", - "67d07ee0c972c18731e23beb" + { + "first_name": "Jieying", + "last_name": "Zhang" + }, + { + "first_name": "Naim Bugra", + "last_name": "Ozel" + } ], "teaching_assistants": [ { diff --git a/parser/validator.go b/parser/validator.go index 254da42..dc03561 100644 --- a/parser/validator.go +++ b/parser/validator.go @@ -18,19 +18,37 @@ func validate() { }() // Build maps for quick lookup by key - sectionsByKey := make(map[schema.SectionKey]*schema.Section) + courseSectionsByKey := make(map[schema.CourseKey]map[schema.CourseSectionKey]*schema.Section) for _, section := range Sections { - sectionsByKey[section.Key] = section + courseKey := section.Course + if courseSectionsByKey[courseKey] == nil { + courseSectionsByKey[courseKey] = make(map[schema.CourseSectionKey]*schema.Section) + } + + sectionKey := schema.CourseSectionKey{ + Section_number: section.Section_number, + Term: section.Academic_session.Name, + } + courseSectionsByKey[courseKey][sectionKey] = section } courseByKey := make(map[schema.CourseKey]*schema.Course) for _, course := range Courses { - courseByKey[course.Key] = course + courseKey := schema.CourseKey{ + Subject_prefix: course.Subject_prefix, + Course_number: course.Course_number, + Catalog_year: course.Catalog_year, + } + courseByKey[courseKey] = course } profByKey := make(map[schema.ProfessorKey]*schema.Professor) for _, professor := range Professors { - profByKey[professor.Key] = professor + professorKey := schema.ProfessorKey{ + First_name: professor.First_name, + Last_name: professor.Last_name, + } + profByKey[professorKey] = professor } log.Printf("\nValidating courses...") @@ -43,7 +61,7 @@ func validate() { valDuplicateCourses(course1, course2) } // Make sure course isn't referencing any nonexistent sections, and that course-section references are consistent both ways - valCourseReference(course1, sectionsByKey) + valCourseReference(course1, courseSectionsByKey) } courseKeys = nil log.Print("No invalid courses!") @@ -81,7 +99,9 @@ func validate() { // Validate if the courses are duplicate func valDuplicateCourses(course1 *schema.Course, course2 *schema.Course) { - if course1.Key == course2.Key { + if course1.Subject_prefix == course2.Subject_prefix && + course1.Course_number == course2.Course_number && + course1.Catalog_year == course2.Catalog_year { log.Printf("Duplicate course found for %s%s!", course1.Subject_prefix, course1.Course_number) log.Printf("Course 1: %v\n\nCourse 2: %v", course1, course2) log.Panic("Courses failed to validate!") @@ -89,28 +109,38 @@ func valDuplicateCourses(course1 *schema.Course, course2 *schema.Course) { } // Validate course reference to sections -func valCourseReference(course *schema.Course, sections map[schema.SectionKey]*schema.Section) { - for _, sectionKey := range course.Section_keys { - section, exists := sections[sectionKey] - // validate if course references to some section not in the parsed sections - if !exists { - log.Printf("Nonexistent section reference found for %s%s!", course.Subject_prefix, course.Course_number) - log.Printf("Referenced section ID: %s\nCourse ID: %s", sectionKey, course.Key) - log.Panic("Courses failed to validate!") - } +func valCourseReference(course *schema.Course, courseSections map[schema.CourseKey]map[schema.CourseSectionKey]*schema.Section) { + courseKey := schema.CourseKey{ + Subject_prefix: course.Subject_prefix, + Course_number: course.Course_number, + Catalog_year: course.Catalog_year, + } - // validate if the ref sections references back to the course - if section.Course_key != course.Key { - log.Printf("Inconsistent section reference found for %s%s! The course references the section, but not vice-versa!", course.Subject_prefix, course.Course_number) - log.Printf("Referenced section key: %+v\nCourse key: %+v\nSection course key: %+v", sectionKey, course.Key, section.Course_key) - log.Panic("Courses failed to validate!") + if sections, sectionsExist := courseSections[courseKey]; sectionsExist { + for _, sectionKey := range course.Sections { + section, exists := sections[sectionKey] + // validate if course references to some section not in the parsed sections + if !exists { + log.Printf("Nonexistent section reference found for %s%s!", course.Subject_prefix, course.Course_number) + log.Printf("Referenced section key: %s\nCourse key: %s", sectionKey, courseKey) + log.Panic("Courses failed to validate!") + } + + // validate if the ref sections references back to the course + if section.Course != courseKey { + log.Printf("Inconsistent section reference found for %s%s! The course references the section, but not vice-versa!", course.Subject_prefix, course.Course_number) + log.Printf("Referenced CourseSection key: %+v\nCourse key: %+v\nSection's course key: %+v", sectionKey, courseKey, section.Course) + log.Panic("Courses failed to validate!") + } } } } // Validate if the sections are duplicate func valDuplicateSections(section1 *schema.Section, section2 *schema.Section) { - if section1.Key == section2.Key { + if section1.Section_number == section2.Section_number && + section1.Course == section2.Course && + section1.Academic_session == section2.Academic_session { log.Print("Duplicate section found!") log.Printf("Section 1: %v\n\nSection 2: %v", section1, section2) log.Panic("Sections failed to validate!") @@ -119,19 +149,26 @@ func valDuplicateSections(section1 *schema.Section, section2 *schema.Section) { // Validate section reference to professor func valSectionReferenceProf(section *schema.Section, profs map[schema.ProfessorKey]*schema.Professor) { - for _, profKey := range section.Professor_keys { + for _, profKey := range section.Professors { + profSectionKey := schema.ProfSectionKey{ + Subject_prefix: section.Course.Subject_prefix, + Course_number: section.Course.Course_number, + Section_number: section.Section_number, + Term: section.Academic_session.Name, + } + professor, exists := profs[profKey] // validate if the section references to some prof not in the parsed professors if !exists { - log.Printf("Nonexistent professor reference found for section key %s!", section.Key) + log.Printf("Nonexistent professor reference found for section ID %s!", section.Id) log.Printf("Referenced professor key: %v", profKey) log.Panic("Sections failed to validate!") } // validate if the referenced professor references back to section - if !slices.Contains(professor.Section_keys, section.Key) { + if !slices.Contains(professor.Sections, profSectionKey) { log.Printf("Inconsistent professor reference found for section ID %s! The section references the professor, but not vice-versa!", section.Id) - log.Printf("Referenced professor key: %v", professor.Key) + log.Printf("Referenced professor key: %v", profKey) log.Panic("Sections failed to validate!") } } @@ -139,18 +176,20 @@ func valSectionReferenceProf(section *schema.Section, profs map[schema.Professor // Validate section reference to course func valSectionReferenceCourse(section *schema.Section, coursesByKey map[schema.CourseKey]*schema.Course) { - _, exists := coursesByKey[section.Course_key] + _, exists := coursesByKey[section.Course] // validate if section reference some course not in parsed courses if !exists { log.Printf("Nonexistent course reference found for section ID %s!", section.Id) - log.Printf("Referenced course key: %+v", section.Course_key) + log.Printf("Referenced course key: %+v", section.Course) log.Panic("Sections failed to validate!") } } // Validate if the professors are duplicate func valDuplicateProfs(prof1 *schema.Professor, prof2 *schema.Professor) { - if prof1.Key == prof2.Key && prof1.Profile_uri == prof2.Profile_uri { + if prof1.First_name == prof2.First_name && + prof1.Last_name == prof2.Last_name && + prof1.Profile_uri == prof2.Profile_uri { log.Printf("Duplicate professor found!") log.Printf("Professor 1: %v\n\nProfessor 2: %v", prof1, prof2) log.Panic("Professors failed to validate!") diff --git a/parser/validator_test.go b/parser/validator_test.go index 6dba575..ddf8c95 100644 --- a/parser/validator_test.go +++ b/parser/validator_test.go @@ -20,15 +20,6 @@ var testProfessors []*schema.Professor // Map index of test sections to test courses var sectionCourseMap map[int]int -// Map professor to sections -var profSectionMap map[int][]int - -// Map sections to professor -var sectionProfMap map[int][]int - -// Map courses to sections -var courseSectionMap map[int][]int - func init() { // Parse the test courses data, err := os.ReadFile("./testdata/courses.json") @@ -62,92 +53,6 @@ func init() { // The correct mapping between sections and courses sectionCourseMap = map[int]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 4} - - // Mapping between professor and sections - profSectionMap = map[int][]int{0: {4, 5}, 1: {4, 5}, 2:{0}, 3:{1}} - - // Reverse mappings - courseSectionMap = map[int][]int{} - for sectionIndex, courseIndex := range sectionCourseMap { - courseSectionMap[courseIndex] = append(courseSectionMap[courseIndex], sectionIndex) - } - - sectionProfMap = map[int][]int{} - for profIndex, sections := range profSectionMap { - for _, sectionIndex := range sections { - sectionProfMap[sectionIndex] = append(sectionProfMap[sectionIndex], profIndex) - } - } - - // Set up keys for courses - for i := range testCourses { - testCourses[i].Key = schema.CourseKey { - Course_number: testCourses[i].Course_number, - Catalog_year: testCourses[i].Catalog_year, - Subject_prefix: testCourses[i].Subject_prefix, - } - - sectionKeys := []schema.SectionKey{} - for _, sectionIndex := range courseSectionMap[i] { - sectionKey := schema.SectionKey { - Section_number: testSections[sectionIndex].Section_number, - Term: testSections[sectionIndex].Academic_session.Name, - Course_number: testCourses[i].Course_number, - Catalog_year: testCourses[i].Catalog_year, - Subject_prefix: testCourses[i].Subject_prefix, - } - sectionKeys = append(sectionKeys, sectionKey) - } - testCourses[i].Section_keys = sectionKeys - } - - // Set up keys for sections - for i := range testSections { - testSections[i].Key = schema.SectionKey { - Section_number: testSections[i].Section_number, - Term: testSections[i].Academic_session.Name, - Course_number: testCourses[sectionCourseMap[i]].Course_number, - Catalog_year: testCourses[sectionCourseMap[i]].Catalog_year, - Subject_prefix: testCourses[sectionCourseMap[i]].Subject_prefix, - } - - testSections[i].Course_key = schema.CourseKey { - Course_number: testCourses[sectionCourseMap[i]].Course_number, - Catalog_year: testCourses[sectionCourseMap[i]].Catalog_year, - Subject_prefix: testCourses[sectionCourseMap[i]].Subject_prefix, - } - - professorKeys := []schema.ProfessorKey{} - for _, professorIndex := range sectionProfMap[i] { - professorKey := schema.ProfessorKey { - First_name: testProfessors[professorIndex].First_name, - Last_name: testProfessors[professorIndex].Last_name, - } - professorKeys = append(professorKeys, professorKey) - } - testSections[i].Professor_keys = professorKeys - } - - // Set up keys for professors - for i := range testProfessors { - testProfessors[i].Key = schema.ProfessorKey { - First_name: testProfessors[i].First_name, - Last_name: testProfessors[i].Last_name, - } - - sectionKeys := []schema.SectionKey{} - for _, sectionIndex := range profSectionMap[i] { - sectionKey := schema.SectionKey { - Section_number: testSections[sectionIndex].Section_number, - Term: testSections[sectionIndex].Academic_session.Name, - Course_number: testCourses[sectionCourseMap[sectionIndex]].Course_number, - Catalog_year: testCourses[sectionCourseMap[sectionIndex]].Catalog_year, - Subject_prefix: testCourses[sectionCourseMap[sectionIndex]].Subject_prefix, - } - sectionKeys = append(sectionKeys, sectionKey) - } - testProfessors[i].Section_keys = sectionKeys - } } // Test duplicate courses. Designed for fail cases @@ -213,9 +118,20 @@ func TestDuplicateProfPass(t *testing.T) { // Test if course references to anything nonexistent. Designed for pass case // TestCourseReferencePass ensures section references to courses succeed. func TestCourseReferencePass(t *testing.T) { - sectionMap := make(map[schema.SectionKey]*schema.Section) + courseSectionMap := make(map[schema.CourseKey]map[schema.CourseSectionKey]*schema.Section) for _, section := range testSections { - sectionMap[section.Key] = section + courseKey := section.Course + + if courseSectionMap[courseKey] == nil { + courseSectionMap[courseKey] = make(map[schema.CourseSectionKey]*schema.Section) + } + + sectionKey := schema.CourseSectionKey{ + Section_number: section.Section_number, + Term: section.Academic_session.Name, + } + + courseSectionMap[courseKey][sectionKey] = section } // Buffer to capture the output @@ -235,7 +151,7 @@ func TestCourseReferencePass(t *testing.T) { // Run func for _, course := range testCourses { - valCourseReference(course, sectionMap) + valCourseReference(course, courseSectionMap) } } @@ -245,26 +161,24 @@ func TestCourseReferencePass(t *testing.T) { // - Section doesn't reference back to same course // // This is fail: missing -// Legacy test, no longer needed? // TestCourseReferenceFail1 detects missing course references during validation. -// func TestCourseReferenceFail1(t *testing.T) { -// for key, value := range indexMap { -// t.Run(fmt.Sprintf("Section %v & course %v", key, value), func(t *testing.T) { -// testCourseReferenceFail("missing", value, key, t) -// }) -// } -// } +func TestCourseReferenceFail1(t *testing.T) { + for key, value := range sectionCourseMap { + t.Run(fmt.Sprintf("Section %v & course %v", key, value), func(t *testing.T) { + testCourseReferenceFail("missing", value, key, t) + }) + } +} // This is fail: modified // TestCourseReferenceFail2 detects mismatched section-course references. -//Legacy test, no longer needed? -// func TestCourseReferenceFail2(t *testing.T) { -// for key, value := range indexMap { -// t.Run(fmt.Sprintf("Section %v & course %v", key, value), func(t *testing.T) { -// testCourseReferenceFail("modified", value, key, t) -// }) -// } -// } +func TestCourseReferenceFail2(t *testing.T) { + for key, value := range sectionCourseMap { + t.Run(fmt.Sprintf("Section %v & course %v", key, value), func(t *testing.T) { + testCourseReferenceFail("modified", value, key, t) + }) + } +} // Test section reference to professor, designed for pass case // TestSectionReferenceProfPass ensures section professor references are mutual. @@ -319,7 +233,7 @@ func TestSectionReferenceProfFail(t *testing.T) { defer func() { logOutput := logBuffer.String() for _, msg := range []string{ - "Nonexistent professor reference found for section key {ACCT 2301 24 25S 003}!", + "Nonexistent professor reference found for section ID ObjectID(\"67d07ee0c972c18731e23bea\")!", "Referenced professor key: {Naim Bugra Ozel}", } { if !strings.Contains(logOutput, msg) { @@ -346,7 +260,12 @@ func TestSectionReferenceProfFail(t *testing.T) { func TestSectionReferenceCourse(t *testing.T) { coursesByKey := make(map[schema.CourseKey]*schema.Course) for _, course := range testCourses { - coursesByKey[course.Key] = course + courseKey := schema.CourseKey{ + Subject_prefix: course.Subject_prefix, + Course_number: course.Course_number, + Catalog_year: course.Catalog_year, + } + coursesByKey[courseKey] = course } var logBuffer bytes.Buffer @@ -414,7 +333,7 @@ func testDuplicateFail(objType string, ix int, t *testing.T) { // Log output needs to contain lines in the list for _, msg := range expectedMsgs { if !strings.Contains(logOutput, msg) { - t.Errorf("Exptected the message for %v: %v", objType, msg) + t.Errorf("Expected the message for %v: %v", objType, msg) } } @@ -471,92 +390,124 @@ func testDuplicatePass(objType string, ix1 int, ix2 int, t *testing.T) { // fail = "missing" means it lacks one sections // fail = "modified" means one section's course reference has been modified -// For course-section, no longer needed -// func testCourseReferenceFail(fail string, courseIx int, sectionIx int, t *testing.T) { -// sectionMap := make(map[primitive.ObjectID]*schema.Section) - -// var sectionID, originalID primitive.ObjectID // used to store IDs of modified sections - -// // Build the failed section map based on fail type -// switch fail { -// case "missing": -// // Misses a section -// for i, section := range testSections { -// if sectionIx != i { -// sectionMap[section.Id] = section -// } else { -// sectionID = section.Id // Nonexistent ID referenced by course -// } -// } -// case "modified": -// // One section doesn't reference to correct courses -// for i, section := range testSections { -// sectionMap[section.Id] = section -// if sectionIx == i { -// // Save the section ID and original course reference to be restored later on -// sectionID = section.Id -// originalID = section.Course_reference - -// // Modified part -// sectionMap[section.Id].Course_reference = primitive.NewObjectID() -// } -// } -// } - -// // Expected msgs -// var expectedMsgs []string - -// // The course that references nonexistent stuff -// var failCourse *schema.Course - -// if fail == "missing" { -// failCourse = testCourses[courseIx] - -// expectedMsgs = []string{ -// fmt.Sprintf("Nonexistent section reference found for %v%v!", failCourse.Subject_prefix, failCourse.Course_number), -// fmt.Sprintf("Referenced section ID: %s\nCourse ID: %s", sectionID, failCourse.Id), -// } -// } else { -// failCourse = testCourses[courseIx] -// failSection := testSections[sectionIx] - -// expectedMsgs = []string{ -// fmt.Sprintf("Inconsistent section reference found for %v%v! The course references the section, but not vice-versa!", -// failCourse.Subject_prefix, failCourse.Course_number), -// fmt.Sprintf("Referenced section ID: %s\nCourse ID: %s\nSection course reference: %s", -// failSection.Id, failCourse.Id, failSection.Course_reference), -// } -// } - -// // Buffer to capture the output -// var logBuffer bytes.Buffer -// log.SetOutput(&logBuffer) - -// defer func() { -// logOutput := logBuffer.String() - -// for _, msg := range expectedMsgs { -// if !strings.Contains(logOutput, msg) { -// t.Errorf("The function didn't log correct message. Expected \"%v\"", msg) -// } -// } - -// // Restore to original course reference of modified section (if needed) -// if fail == "modified" { -// sectionMap[sectionID].Course_reference = originalID -// } - -// if r := recover(); r == nil { -// t.Errorf("The function didn't panic") -// } else { -// if r != "Courses failed to validate!" { -// t.Errorf("The function panic the wrong message") -// } -// } -// }() - -// // Run func -// for _, course := range testCourses { -// valCourseReference(course, sectionMap) -// } -// } \ No newline at end of file +func testCourseReferenceFail(fail string, courseIx int, sectionIx int, t *testing.T) { + courseSectionMap := make(map[schema.CourseKey]map[schema.CourseSectionKey]*schema.Section) + + // Used to store keys of modified sections + var sectionRef schema.CourseSectionKey + var sectionCourseRef, originalCourse schema.CourseKey + + // Build the failed section map based on fail type + switch fail { + case "missing": + // Misses a section + for i, section := range testSections { + courseKey := section.Course + if courseSectionMap[courseKey] == nil { + courseSectionMap[courseKey] = make(map[schema.CourseSectionKey]*schema.Section) + } + + sectionKey := schema.CourseSectionKey{ + Section_number: section.Section_number, + Term: section.Academic_session.Name, + } + + if sectionIx != i { + courseSectionMap[courseKey][sectionKey] = section + } else { + sectionRef = sectionKey // Nonexistent key referenced by course + } + } + case "modified": + // One section doesn't reference to correct courses + for i, section := range testSections { + courseKey := section.Course + if courseSectionMap[courseKey] == nil { + courseSectionMap[courseKey] = make(map[schema.CourseSectionKey]*schema.Section) + } + + sectionKey := schema.CourseSectionKey{ + Section_number: section.Section_number, + Term: section.Academic_session.Name, + } + courseSectionMap[courseKey][sectionKey] = section + + if sectionIx == i { + // Save the section ID and original course reference to be restored later on + sectionRef = sectionKey + sectionCourseRef = section.Course + originalCourse = courseKey + + // Modified part + courseSectionMap[courseKey][sectionKey].Course = schema.CourseKey{} + } + } + } + + // Expected msgs + var expectedMsgs []string + + // The course that references nonexistent stuff + var failCourse *schema.Course + + if fail == "missing" { + failCourse = testCourses[courseIx] + failCourseKey := schema.CourseKey{ + Subject_prefix: failCourse.Subject_prefix, + Course_number: failCourse.Course_number, + Catalog_year: failCourse.Catalog_year, + } + + expectedMsgs = []string{ + fmt.Sprintf("Nonexistent section reference found for %s%s!", failCourse.Subject_prefix, failCourse.Course_number), + fmt.Sprintf("Referenced section key: %s\nCourse key: %s", sectionRef, failCourseKey), + } + } else { + failCourse = testCourses[courseIx] + failCourseKey := schema.CourseKey{ + Subject_prefix: failCourse.Subject_prefix, + Course_number: failCourse.Course_number, + Catalog_year: failCourse.Catalog_year, + } + failSection := testSections[sectionIx] + + expectedMsgs = []string{ + fmt.Sprintf("Inconsistent section reference found for %s%s! The course references the section, but not vice-versa!", + failCourse.Subject_prefix, failCourse.Course_number), + fmt.Sprintf("Referenced CourseSection key: %+v\nCourse key: %+v\nSection's course key: %+v", + sectionRef, failCourseKey, failSection.Course), + } + } + + // Buffer to capture the output + var logBuffer bytes.Buffer + log.SetOutput(&logBuffer) + + defer func() { + logOutput := logBuffer.String() + + for _, msg := range expectedMsgs { + if !strings.Contains(logOutput, msg) { + t.Errorf("The function didn't log correct message. Expected \"%v\"", msg) + } + } + + // Restore to original course reference of modified section (if needed) + if fail == "modified" { + courseSectionMap[originalCourse][sectionRef].Course = sectionCourseRef + } + + if r := recover(); r == nil { + t.Errorf("The function didn't panic") + } else { + if r != "Courses failed to validate!" { + t.Errorf("The function panic the wrong message") + } + } + }() + + // Run func + for _, course := range testCourses { + valCourseReference(course, courseSectionMap) + } +} \ No newline at end of file diff --git a/scrapers/profiles.go b/scrapers/profiles.go index 0844e2f..4e12744 100644 --- a/scrapers/profiles.go +++ b/scrapers/profiles.go @@ -287,7 +287,7 @@ func ScrapeProfiles(outDir string) { Profile_uri: link, Image_uri: imageUri, Office_hours: []schema.Meeting{}, - Section_keys: []schema.SectionKey{}, + Sections: []schema.ProfSectionKey{}, }) utils.VPrintf("Scraped profile for %s %s!", firstName, lastName)