From 309bbab18e7f5ce4d830f3dd3d0f1d6fa224990c Mon Sep 17 00:00:00 2001 From: ievavold <ievavold@wisc.edu> Date: Fri, 8 Feb 2019 14:09:03 -0600 Subject: [PATCH] ROENROLL-1352 load subject mapping before degree plan terms are loaded --- src/app/core/models/course.ts | 6 +- src/app/core/models/saved-for-later-course.ts | 2 +- .../degree-planner/services/api.service.ts | 26 ++-- .../store/effects/plan.effects.ts | 114 +++++++++++++----- src/app/degree-planner/store/state.ts | 3 +- 5 files changed, 107 insertions(+), 44 deletions(-) diff --git a/src/app/core/models/course.ts b/src/app/core/models/course.ts index 6185a41..a4e5c5c 100644 --- a/src/app/core/models/course.ts +++ b/src/app/core/models/course.ts @@ -1,4 +1,4 @@ -interface CourseBase { +export interface CourseBase { id: number | null; courseId: string; termCode: string | null; @@ -31,3 +31,7 @@ interface CourseBase { export interface Course extends CourseBase { subject: string; } + +export interface SubjectMapping { + [subjectCode: number]: string; +} diff --git a/src/app/core/models/saved-for-later-course.ts b/src/app/core/models/saved-for-later-course.ts index a9927d1..aaee440 100644 --- a/src/app/core/models/saved-for-later-course.ts +++ b/src/app/core/models/saved-for-later-course.ts @@ -1,4 +1,4 @@ -interface SavedForLaterCourseBase { +export interface SavedForLaterCourseBase { id: number | null; courseId: string; termCode: string; diff --git a/src/app/degree-planner/services/api.service.ts b/src/app/degree-planner/services/api.service.ts index d1fbfde..34eb004 100644 --- a/src/app/degree-planner/services/api.service.ts +++ b/src/app/degree-planner/services/api.service.ts @@ -12,9 +12,9 @@ import { ConfigService } from '@app/core/config.service'; // Models import { Note } from '@app/core/models/note'; import { Term } from '@app/core/models/term'; -import { Course } from '@app/core/models/course'; +import { Course, CourseBase, SubjectMapping } from '@app/core/models/course'; import { DegreePlan } from '@app/core/models/degree-plan'; -import { SavedForLaterCourse } from '@app/core/models/saved-for-later-course'; +import { SavedForLaterCourseBase } from '@app/core/models/saved-for-later-course'; const HTTP_OPTIONS = { headers: new HttpHeaders({ @@ -26,8 +26,8 @@ const HTTP_OPTIONS = { export class DegreePlannerApiService { constructor(private http: HttpClient, private config: ConfigService) {} - public getSavedForLaterCourses(): Observable<SavedForLaterCourse[]> { - return this.http.get<SavedForLaterCourse[]>( + public getSavedForLaterCourses(): Observable<SavedForLaterCourseBase[]> { + return this.http.get<SavedForLaterCourseBase[]>( `${this.config.apiPlannerUrl}/favorites`, ); } @@ -40,8 +40,10 @@ export class DegreePlannerApiService { return this.http.get<DegreePlan>(this.degreePlanEndpoint(roadmapId)); } - public getAllSubjects(): Observable<Object> { - return this.http.get<Object>(this.searchEndpoint('subjectsMap/0000')); + public getAllSubjects(): Observable<SubjectMapping> { + return this.http.get<SubjectMapping>( + this.searchEndpoint('subjectsMap/0000'), + ); } public getActiveTerms(): Observable<Term[]> { @@ -64,8 +66,8 @@ export class DegreePlannerApiService { public getAllTermCourses( roadmapId: number, - ): Observable<{ termCode: string; courses: Course[] }[]> { - return this.http.get<{ termCode: string; courses: Course[] }[]>( + ): Observable<{ termCode: string; courses: CourseBase[] }[]> { + return this.http.get<{ termCode: string; courses: CourseBase[] }[]>( this.degreePlanEndpoint(roadmapId, 'termcourses'), ); } @@ -110,8 +112,8 @@ export class DegreePlannerApiService { public saveForLater( subjectCode: string, courseId: string, - ): Observable<SavedForLaterCourse> { - return this.http.post<SavedForLaterCourse>( + ): Observable<SavedForLaterCourseBase> { + return this.http.post<SavedForLaterCourseBase>( this.config.apiPlannerUrl + '/favorites/' + subjectCode + '/' + courseId, HTTP_OPTIONS, ); @@ -120,8 +122,8 @@ export class DegreePlannerApiService { public removeSavedForLater( subjectCode: string, courseId: string, - ): Observable<SavedForLaterCourse> { - return this.http.delete<SavedForLaterCourse>( + ): Observable<SavedForLaterCourseBase> { + return this.http.delete<SavedForLaterCourseBase>( this.config.apiPlannerUrl + '/favorites/' + subjectCode + '/' + courseId, HTTP_OPTIONS, ); diff --git a/src/app/degree-planner/store/effects/plan.effects.ts b/src/app/degree-planner/store/effects/plan.effects.ts index 094d1bb..be19b72 100644 --- a/src/app/degree-planner/store/effects/plan.effects.ts +++ b/src/app/degree-planner/store/effects/plan.effects.ts @@ -26,6 +26,8 @@ import { // Models import { DegreePlan } from '@app/core/models/degree-plan'; import { PlannedTerm } from '@app/core/models/planned-term'; +import { SubjectMapping } from '@app/core/models/course'; +import { SavedForLaterCourse } from '@app/core/models/saved-for-later-course'; @Injectable() export class DegreePlanEffects { @@ -39,38 +41,56 @@ export class DegreePlanEffects { init$: Observable<InitialPlanLoadResponse> = this.actions$.pipe( ofType(ROOT_EFFECTS_INIT), - // Load all plans available to the user. - flatMap(() => this.api.getAllDegreePlans()), + // Load the data that is not specific to a particular degree plan. Also pick + // the primary degree plan as the first visible degree plan. + flatMap(() => { + return forkJoin( + this.api.getAllDegreePlans(), + this.api.getAllSubjects(), + this.api.getActiveTerms(), + ).pipe( + map(([allDegreePlans, subjects, activeTerms]) => { + const visibleDegreePlan = + allDegreePlans.find(plan => plan.primary) || allDegreePlans[0]; - // Pick one of the plans to use as the visible plan. - map(allDegreePlans => { - return { - visibleDegreePlan: - allDegreePlans.find(plan => plan.primary) || allDegreePlans[0], - allDegreePlans, - }; + return { + allDegreePlans, + subjects, + activeTermCodes: activeTerms.map(term => term.termCode), + visibleDegreePlan, + }; + }), + ); }), // Get term data for the degree plan specified by the roadmap ID and any // courses that were 'saved for later' by the user. flatMap(stdin => { return forkJoin( - this.loadTermsForPlan(stdin), - this.api.getSavedForLaterCourses(), - this.api.getAllSubjects(), + this.loadPlanTerms( + stdin.visibleDegreePlan, + stdin.subjects, + stdin.activeTermCodes, + ), + this.loadSavedForLaterCourses(stdin.subjects), ).pipe( - map(([planDetails, savedForLater, subjects]) => { + map(([visibleTerms, savedForLater]) => { const savedForLaterCourses = savedForLater.map(course => { - course.subject = subjects[course.subjectCode]; + course.subject = stdin.subjects[course.subjectCode]; return course; }); - return { ...planDetails, savedForLaterCourses, subjects }; + + return new InitialPlanLoadResponse({ + visibleDegreePlan: stdin.visibleDegreePlan, + visibleTerms, + savedForLaterCourses, + activeTermCodes: stdin.activeTermCodes, + allDegreePlans: stdin.allDegreePlans, + subjects: stdin.subjects, + }); }), ); }), - - // Wrap data in an Action for dispatch - map(stdin => new InitialPlanLoadResponse(stdin)), ); @Effect() @@ -85,17 +105,37 @@ export class DegreePlanEffects { // corresponding degree plan object. map(([action, state]) => { return { + subjects: state.subjects, visibleDegreePlan: state.allDegreePlans.find(plan => { return plan.roadmapId === action.payload.newVisibleRoadmapId; }) as DegreePlan, + activeTermCodes: state.activeTermCodes, }; }), // Get term data for the degree plan specified by the roadmap ID. - flatMap(stdin => this.loadTermsForPlan(stdin)), + flatMap(oldData => { + return this.loadPlanTerms( + oldData.visibleDegreePlan, + oldData.subjects, + oldData.activeTermCodes, + ).pipe( + map(visibleTerms => { + return { + visibleTerms, + visibleDegreePlan: oldData.visibleDegreePlan, + }; + }), + ); + }), // Wrap data in an Action for dispatch - map(stdin => new ChangeVisiblePlanResponse(stdin)), + map(data => { + return new ChangeVisiblePlanResponse({ + visibleDegreePlan: data.visibleDegreePlan, + visibleTerms: data.visibleTerms, + }); + }), ); @Effect() @@ -305,19 +345,32 @@ export class DegreePlanEffects { }), ); - private loadTermsForPlan<T extends { visibleDegreePlan: DegreePlan }>( - stdin: T, + private loadSavedForLaterCourses(subjects: SubjectMapping) { + return this.api.getSavedForLaterCourses().pipe( + map(courseBases => { + return courseBases.map<SavedForLaterCourse>(base => { + return { + ...base, + subject: subjects[base.subjectCode] as string, + }; + }); + }), + ); + } + + private loadPlanTerms( + visibleDegreePlan: DegreePlan, + subjects: SubjectMapping, + activeTermCodes: string[], ) { return forkJoin( - this.api.getAllNotes(stdin.visibleDegreePlan.roadmapId), - this.api.getAllTermCourses(stdin.visibleDegreePlan.roadmapId), - this.api.getActiveTerms(), + this.api.getAllNotes(visibleDegreePlan.roadmapId), + this.api.getAllTermCourses(visibleDegreePlan.roadmapId), ).pipe( // Combine courses and notes by term. - map(([notes, termCourses, activeTerms]) => { + map(([notes, termCourses]) => { const noteTermCodes = notes.map(note => note.termCode); const courseTermCodes = termCourses.map(term => term.termCode); - const activeTermCodes = activeTerms.map(term => term.termCode); /** * Using the notes & courses relevant to the current degree plan and @@ -335,11 +388,14 @@ export class DegreePlanEffects { const visibleTerms: PlannedTerm[] = uniqueTermCodes.map(termCode => { const note = notes.find(matchesTermCode(termCode)); const termCourse = termCourses.find(matchesTermCode(termCode)); - const courses = termCourse ? termCourse.courses : []; + const courses = (termCourse ? termCourse.courses : []).map(base => ({ + ...base, + subject: subjects[base.subjectCode], + })); return { termCode, note, courses }; }); - return { ...stdin, visibleTerms, activeTermCodes }; + return visibleTerms; }), ); } diff --git a/src/app/degree-planner/store/state.ts b/src/app/degree-planner/store/state.ts index fc13fc4..9cc6a2a 100644 --- a/src/app/degree-planner/store/state.ts +++ b/src/app/degree-planner/store/state.ts @@ -2,6 +2,7 @@ import { PlannedTerm } from '@app/core/models/planned-term'; import { DegreePlan } from '@app/core/models/degree-plan'; import { SavedForLaterCourse } from '@app/core/models/saved-for-later-course'; +import { SubjectMapping } from '@app/core/models/course'; export interface DegreePlannerState { visibleDegreePlan: DegreePlan | undefined; @@ -9,7 +10,7 @@ export interface DegreePlannerState { savedForLaterCourses: SavedForLaterCourse[]; activeTermCodes: string[]; allDegreePlans: DegreePlan[]; - subjects: Object; + subjects: SubjectMapping; } export const INITIAL_DEGREE_PLANNER_STATE: DegreePlannerState = { -- GitLab