Skip to content
Snippets Groups Projects
Commit 11170592 authored by Isaac Evavold's avatar Isaac Evavold
Browse files

streamline plan effects

parent aa6d1863
No related branches found
No related tags found
No related merge requests found
// Libraries // Libraries
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ROOT_EFFECTS_INIT, Actions, Effect, ofType } from '@ngrx/effects'; import { ROOT_EFFECTS_INIT, Actions, Effect, ofType } from '@ngrx/effects';
import { Observable, forkJoin } from 'rxjs'; import { Observable, forkJoin, of } from 'rxjs';
import { map, flatMap, tap, withLatestFrom, filter } from 'rxjs/operators'; import { map, flatMap, withLatestFrom, filter } from 'rxjs/operators';
import { GlobalState } from '@app/core/state'; import { GlobalState } from '@app/core/state';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
...@@ -41,101 +41,68 @@ export class DegreePlanEffects { ...@@ -41,101 +41,68 @@ export class DegreePlanEffects {
init$: Observable<InitialPlanLoadResponse> = this.actions$.pipe( init$: Observable<InitialPlanLoadResponse> = this.actions$.pipe(
ofType(ROOT_EFFECTS_INIT), ofType(ROOT_EFFECTS_INIT),
// Load the data that is not specific to a particular degree plan. Also pick // Load the list of degree plans and data used by all degree plans.
// the primary degree plan as the first visible degree plan.
flatMap(() => { flatMap(() => {
return forkJoin( const activeTermCodes = this.api
this.api.getAllDegreePlans(), .getActiveTerms()
this.api.getAllSubjects(), .pipe(map(terms => terms.map(term => term.termCode)));
this.api.getActiveTerms(),
).pipe( return forkJoinWithKeys({
map(([allDegreePlans, subjects, activeTerms]) => { allDegreePlans: this.api.getAllDegreePlans(),
const visibleDegreePlan = subjects: this.api.getAllSubjects(),
allDegreePlans.find(plan => plan.primary) || allDegreePlans[0]; activeTermCodes,
});
return {
allDegreePlans,
subjects,
activeTermCodes: activeTerms.map(term => term.termCode),
visibleDegreePlan,
};
}),
);
}), }),
// Get term data for the degree plan specified by the roadmap ID and any // Load data specific to the primary degree plan.
// courses that were 'saved for later' by the user. flatMap(({ allDegreePlans, subjects, activeTermCodes }) => {
flatMap(stdin => { const savedForLaterCourses = this.loadSavedForLaterCourses(subjects);
return forkJoin( const visibleDegreePlan = pickPrimaryDegreePlan(allDegreePlans);
this.loadPlanTerms( const visibleTerms = loadPlanTerms(
stdin.visibleDegreePlan, this.api,
stdin.subjects, visibleDegreePlan,
stdin.activeTermCodes, subjects,
), activeTermCodes,
this.loadSavedForLaterCourses(stdin.subjects),
).pipe(
map(([visibleTerms, savedForLater]) => {
const savedForLaterCourses = savedForLater.map(course => {
course.subject = stdin.subjects[course.subjectCode];
return course;
});
return new InitialPlanLoadResponse({
visibleDegreePlan: stdin.visibleDegreePlan,
visibleTerms,
savedForLaterCourses,
activeTermCodes: stdin.activeTermCodes,
allDegreePlans: stdin.allDegreePlans,
subjects: stdin.subjects,
});
}),
); );
return forkJoinWithKeys({
visibleDegreePlan: of(visibleDegreePlan),
visibleTerms,
savedForLaterCourses,
activeTermCodes: of(activeTermCodes),
allDegreePlans: of(allDegreePlans),
subjects: of(subjects),
});
}), }),
map(payload => new InitialPlanLoadResponse(payload)),
); );
@Effect() @Effect()
switch$: Observable<ChangeVisiblePlanResponse> = this.actions$.pipe( switch$: Observable<ChangeVisiblePlanResponse> = this.actions$.pipe(
ofType<ChangeVisiblePlanRequest>(PlanActionTypes.ChangeVisiblePlanRequest), ofType<ChangeVisiblePlanRequest>(PlanActionTypes.ChangeVisiblePlanRequest),
// Get the most recent Degree Planner state object from the store. This is
// used to decide to fire either the `updateNote` API or `createNote` API.
withLatestFrom(this.store$.select(getDegreePlannerState)), withLatestFrom(this.store$.select(getDegreePlannerState)),
// Get the roadmap ID from the action and use that ID to lookup the flatMap(([action, state]) => {
// corresponding degree plan object. const visibleDegreePlan = state.allDegreePlans.find(plan => {
map(([action, state]) => { return plan.roadmapId === action.payload.newVisibleRoadmapId;
return { }) as DegreePlan;
subjects: state.subjects,
visibleDegreePlan: state.allDegreePlans.find(plan => { const visibleTerms = loadPlanTerms(
return plan.roadmapId === action.payload.newVisibleRoadmapId; this.api,
}) as DegreePlan, visibleDegreePlan,
activeTermCodes: state.activeTermCodes, state.subjects,
}; state.activeTermCodes,
}),
// Get term data for the degree plan specified by the roadmap ID.
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 return forkJoinWithKeys({
map(data => { visibleDegreePlan: of(visibleDegreePlan),
return new ChangeVisiblePlanResponse({ visibleTerms,
visibleDegreePlan: data.visibleDegreePlan,
visibleTerms: data.visibleTerms,
}); });
}), }),
map(payload => new ChangeVisiblePlanResponse(payload)),
); );
@Effect() @Effect()
...@@ -145,12 +112,6 @@ export class DegreePlanEffects { ...@@ -145,12 +112,6 @@ export class DegreePlanEffects {
withLatestFrom(this.store$.select(getDegreePlannerState)), withLatestFrom(this.store$.select(getDegreePlannerState)),
filter(([_, state]) => typeof state.visibleDegreePlan !== undefined), filter(([_, state]) => typeof state.visibleDegreePlan !== undefined),
// Get the roadmap ID from the action.
tap(([action, state]) => {
console.log(action);
console.log(state);
}),
// Get term data for the degree plan specified by the roadmap ID. // Get term data for the degree plan specified by the roadmap ID.
flatMap(([action, state]) => { flatMap(([action, state]) => {
// TODO error handle the API calls // TODO error handle the API calls
...@@ -190,12 +151,6 @@ export class DegreePlanEffects { ...@@ -190,12 +151,6 @@ export class DegreePlanEffects {
withLatestFrom(this.store$.select(getDegreePlannerState)), withLatestFrom(this.store$.select(getDegreePlannerState)),
filter(([_, state]) => state.visibleDegreePlan !== undefined), filter(([_, state]) => state.visibleDegreePlan !== undefined),
// Get the roadmap ID from the action.
tap(([action, state]) => {
console.log(action);
console.log(state);
}),
// Get term data for the degree plan specified by the roadmap ID. // Get term data for the degree plan specified by the roadmap ID.
flatMap(([action, state]) => { flatMap(([action, state]) => {
// TODO error handle the API calls // TODO error handle the API calls
...@@ -227,12 +182,6 @@ export class DegreePlanEffects { ...@@ -227,12 +182,6 @@ export class DegreePlanEffects {
withLatestFrom(this.store$.select(getDegreePlannerState)), withLatestFrom(this.store$.select(getDegreePlannerState)),
filter(([_, state]) => state.visibleDegreePlan !== undefined), filter(([_, state]) => state.visibleDegreePlan !== undefined),
// Get the roadmap ID from the action.
// tap(([action, state]) => {
// console.log(action);
// console.log(state);
// }),
// Get term data for the degree plan specified by the roadmap ID. // Get term data for the degree plan specified by the roadmap ID.
flatMap(([action, state]) => { flatMap(([action, state]) => {
// TODO error handle the API calls // TODO error handle the API calls
...@@ -264,15 +213,6 @@ export class DegreePlanEffects { ...@@ -264,15 +213,6 @@ export class DegreePlanEffects {
withLatestFrom(this.store$.select(getDegreePlannerState)), withLatestFrom(this.store$.select(getDegreePlannerState)),
filter(([_, state]) => state.visibleDegreePlan !== undefined), filter(([_, state]) => state.visibleDegreePlan !== undefined),
// tap(([action, state]) => {
// console.log('REMOVE SAVED FOR LATER ----------');
// console.log(action);
// console.log(state);
// console.log('---------------------------------');
// }),
// Get term data for the degree plan specified by the roadmap ID. // Get term data for the degree plan specified by the roadmap ID.
flatMap(([action, state]) => { flatMap(([action, state]) => {
// TODO error handle the API calls // TODO error handle the API calls
...@@ -309,16 +249,6 @@ export class DegreePlanEffects { ...@@ -309,16 +249,6 @@ export class DegreePlanEffects {
withLatestFrom(this.store$.select(getDegreePlannerState)), withLatestFrom(this.store$.select(getDegreePlannerState)),
filter(([_, state]) => state.visibleDegreePlan !== undefined), filter(([_, state]) => state.visibleDegreePlan !== undefined),
// Get the roadmap ID from the action.
// tap(([action, state]) => {
// console.log('ADD SAVED FOR LATER ----------');
// console.log(action);
// console.log(state);
// console.log('---------------------------------');
// }),
// Get term data for the degree plan specified by the roadmap ID. // Get term data for the degree plan specified by the roadmap ID.
flatMap(([action, state]) => { flatMap(([action, state]) => {
// TODO error handle the API calls // TODO error handle the API calls
...@@ -357,49 +287,69 @@ export class DegreePlanEffects { ...@@ -357,49 +287,69 @@ export class DegreePlanEffects {
}), }),
); );
} }
}
private loadPlanTerms( const loadPlanTerms = (
visibleDegreePlan: DegreePlan, api: DegreePlannerApiService,
subjects: SubjectMapping, visibleDegreePlan: DegreePlan,
activeTermCodes: string[], subjects: SubjectMapping,
) { activeTermCodes: string[],
return forkJoin( ): Observable<PlannedTerm[]> => {
this.api.getAllNotes(visibleDegreePlan.roadmapId), const notesAndTerms$ = forkJoinWithKeys({
this.api.getAllTermCourses(visibleDegreePlan.roadmapId), notes: api.getAllNotes(visibleDegreePlan.roadmapId),
).pipe( terms: api.getAllTermCourses(visibleDegreePlan.roadmapId),
// Combine courses and notes by term. });
map(([notes, termCourses]) => {
const noteTermCodes = notes.map(note => note.termCode); const uniqueTerms$ = notesAndTerms$.pipe(
const courseTermCodes = termCourses.map(term => term.termCode); map(({ notes, terms }) => {
const noteTermCodes = notes.map(note => note.termCode);
/** const courseTermCodes = terms.map(term => term.termCode);
* Using the notes & courses relevant to the current degree plan and const uniqueTermCodes = unique([
* the active terms, generate a sorted list of all unqiue term codes. ...noteTermCodes,
*/ ...courseTermCodes,
const uniqueTermCodes = unique([ ...activeTermCodes,
...noteTermCodes, ]);
...courseTermCodes,
...activeTermCodes, return uniqueTermCodes.sort();
]).sort(); }),
);
/**
* Group the notes and courses into a list of visible terms.
*/
const visibleTerms: PlannedTerm[] = uniqueTermCodes.map(termCode => {
const note = notes.find(matchesTermCode(termCode));
const termCourse = termCourses.find(matchesTermCode(termCode));
const courses = (termCourse ? termCourse.courses : []).map(base => ({
...base,
subject: subjects[base.subjectCode],
}));
return { termCode, note, courses };
});
return visibleTerms; const visibleTerms$ = forkJoin(uniqueTerms$, notesAndTerms$).pipe(
}), map(([uniqueTerms, { notes, terms }]) => {
); return uniqueTerms.map(termCode => {
} const note = notes.find(matchesTermCode(termCode));
} const term = terms.find(matchesTermCode(termCode));
const courses = (term ? term.courses : []).map(course => ({
...course,
subject: subjects[course.subjectCode],
}));
return { termCode, note, courses } as PlannedTerm;
});
}),
);
return visibleTerms$;
};
type SimpleMap = { [name: string]: any };
type ObservableMap<T = SimpleMap> = { [K in keyof T]: Observable<T[K]> };
const forkJoinWithKeys = <T = SimpleMap>(pairs: ObservableMap<T>) => {
const keys = Object.keys(pairs);
const observables = keys.map(key => pairs[key]);
return forkJoin(observables).pipe(
map<any[], T>(values => {
const valueMapping = {} as T;
keys.forEach((key, index) => {
valueMapping[key] = values[index];
});
return valueMapping;
}),
);
};
const unique = <T>(things: T[]): T[] => { const unique = <T>(things: T[]): T[] => {
return things.filter((thing, index, all) => all.indexOf(thing) === index); return things.filter((thing, index, all) => all.indexOf(thing) === index);
...@@ -408,3 +358,8 @@ const unique = <T>(things: T[]): T[] => { ...@@ -408,3 +358,8 @@ const unique = <T>(things: T[]): T[] => {
const matchesTermCode = (termCode: string) => (thing: { termCode: string }) => { const matchesTermCode = (termCode: string) => (thing: { termCode: string }) => {
return thing.termCode === termCode; return thing.termCode === termCode;
}; };
const pickPrimaryDegreePlan = (plans: DegreePlan[]): DegreePlan => {
const primary = plans.find(plan => plan.primary);
return primary ? primary : plans[0];
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment