Skip to content
Snippets Groups Projects
Forked from an inaccessible project.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
api.service.ts 7.74 KiB
import { CourseDetails } from './../../core/models/course-details';
// Libraries
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Location } from '@angular/common';
import { Observable } from 'rxjs';
import { map, filter } from 'rxjs/operators';

// Services
import { ConfigService } from '@app/core/config.service';

// Models
import { Note } from '@app/core/models/note';
import { Term } from '@app/core/models/term';
import { CourseBase, SubjectMapping } from '@app/core/models/course';
import { DegreePlan } from '@app/core/models/degree-plan';
import { SavedForLaterCourseBase } from '@app/core/models/saved-for-later-course';

const HTTP_OPTIONS = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json',
  }),
};

@Injectable({ providedIn: 'root' })
export class DegreePlannerApiService {
  constructor(private http: HttpClient, private config: ConfigService) {}

  public getSavedForLaterCourses(): Observable<SavedForLaterCourseBase[]> {
    return this.http.get<SavedForLaterCourseBase[]>(
      `${this.config.apiPlannerUrl}/favorites`,
    );
  }

  public createDegreePlan(name: string, primary: boolean = false) {
    const url = `${this.config.apiPlannerUrl}/degreePlan`;
    const payload = { name, primary };
    return this.http.post<DegreePlan>(url, payload);
  }

  public deleteDegreePlan(roadmapId: number) {
    const url = `${this.config.apiPlannerUrl}/degreePlan/${roadmapId}`;
    return this.http.delete<void>(url);
  }

  public getAllDegreePlans(): Observable<DegreePlan[]> {
    return this.http.get<DegreePlan[]>(this.degreePlanEndpoint());
  }

  public getPlan(roadmapId: number): Observable<DegreePlan> {
    return this.http.get<DegreePlan>(this.degreePlanEndpoint(roadmapId));
  }

  public getAllSubjects(): Observable<SubjectMapping> {
    return this.http.get<SubjectMapping>(
      this.searchEndpoint('subjectsMap/0000'),
    );
  }

  public getActiveTerms(): Observable<Term[]> {
    return this.http.get<Term[]>(this.searchEndpoint('terms'));
  }

  public getAllNotes(roadmapId: number): Observable<Note[]> {
    return this.http.get<Note[]>(this.degreePlanEndpoint(roadmapId, 'notes'));
  }

  getCourseDetails(
    subjectCode: string,
    courseId: string,
  ): Observable<CourseDetails[]> {
    return this.http.get<CourseDetails[]>(
      this.config.apiSearchUrl + '/course/0000/' + subjectCode + '/' + courseId,
      HTTP_OPTIONS,
    );
  }

  public getAllTermCourses(
    roadmapId: number,
  ): Observable<{ termCode: string; courses: CourseBase[] }[]> {
    return this.http.get<{ termCode: string; courses: CourseBase[] }[]>(
      this.degreePlanEndpoint(roadmapId, 'termcourses'),
    );
  }

  public updateCourseTerm(roadmapId, recordId, termCode): Observable<any> {
    return this.http.put<CourseBase>(
      this.config.apiPlannerUrl +
        '/degreePlan/' +
        roadmapId +
        '/courses/' +
        recordId +
        '?termCode=' +
        termCode,
      HTTP_OPTIONS,
    );
  }

  public autocomplete(search: string) {
    const data = {
      subjectCode: '104',
      termCode: '0000',
      matchText: search,
    };

    return this.http.post('/api/search/v1/autocomplete', data, HTTP_OPTIONS);
  }

  public searchCourses(config: {
    subjectCode: string;
    searchText?: string;
    termCode?: string;
  }): Observable<any> {
    // const data = {
    //   filters: [
    //     { term: { 'subject.subjectCode': '266' } },
    //     {
    //       has_child: {
    //         type: 'enrollmentPackage',
    //         query: {
    //           match: {
    //             'packageEnrollmentStatus.status': 'OPEN WAITLISTED CLOSED',
    //           },
    //         },
    //       },
    //     },
    //   ],
    //   page: 1,
    //   pageSize: 500,
    //   queryString: 'programing',
    //   selectedTerm: '1194',
    //   sortOrder: 'SCORE',
    // };

    const { subjectCode, termCode, searchText } = config;

    const payload: any = {
      selectedTerm: termCode,
      queryString: searchText === '' ? '*' : searchText,
      // Filters are use to build the elastic search query
      filters: [],

      // These options control how much data we get back
      page: 1,
      pageSize: 25,
      sortOrder: 'SCORE',
    };

    // If we have a specific subject code, add a fitler for it
    if (subjectCode !== 'all') {
      payload.filters.push({ term: { 'subject.subjectCode': subjectCode } });
    }

    // 0000 is used to search all courses
    // Any other term code we can assuem is an active term
    if (termCode !== '0000') {
      // Used to search a specific term
      payload.filters.push({
        has_child: {
          type: 'enrollmentPackage',
          query: {
            // We want to make sure we search for ALL classes regardless of status
            match: {
              'packageEnrollmentStatus.status': 'OPEN WAITLISTED CLOSED',
            },
          },
        },
      });
    }

    return this.http.post('/api/search/v1', payload, HTTP_OPTIONS);
  }

  public addCourse(
    planId: number,
    subjectCode: string,
    courseId: string,
    termCode: string,
  ): Observable<CourseBase> {
    return this.http.post<CourseBase>(
      this.config.apiPlannerUrl + '/degreePlan/' + planId + '/courses',
      { subjectCode, courseId, termCode },
      HTTP_OPTIONS,
    );
  }

  public removeCourse(planId: number, recordId: number): Observable<void> {
    return this.http.delete<void>(
      this.config.apiPlannerUrl +
        '/degreePlan/' +
        planId +
        '/courses/' +
        recordId,
      HTTP_OPTIONS,
    );
  }

  public saveForLater(
    subjectCode: string,
    courseId: string,
  ): Observable<SavedForLaterCourseBase> {
    return this.http.post<SavedForLaterCourseBase>(
      this.config.apiPlannerUrl + '/favorites/' + subjectCode + '/' + courseId,
      HTTP_OPTIONS,
    );
  }

  public removeSavedForLater(
    subjectCode: string,
    courseId: string,
  ): Observable<SavedForLaterCourseBase> {
    return this.http.delete<SavedForLaterCourseBase>(
      this.config.apiPlannerUrl + '/favorites/' + subjectCode + '/' + courseId,
      HTTP_OPTIONS,
    );
  }

  public createNote(
    planId: number,
    termCode: string,
    noteText: string,
  ): Observable<Note> {
    const payload = {
      termCode: termCode,
      note: noteText,
    };

    return this.http.post<Note>(
      this.degreePlanEndpoint(planId, 'notes'),
      payload,
      HTTP_OPTIONS,
    );
  }

  public updateNote(
    planId: number,
    termCode: string,
    noteText: string,
    noteId: number,
  ): Observable<Note> {
    const payload = {
      termCode: termCode,
      note: noteText,
      id: noteId,
    };

    return this.http
      .put<null>(
        this.degreePlanEndpoint(planId, 'notes', noteId),
        payload,
        HTTP_OPTIONS,
      )
      .pipe(map(() => payload));
  }

  public deleteNote(planId: number, noteId: number): Observable<null> {
    return this.http.delete<null>(
      this.degreePlanEndpoint(planId, 'notes', noteId),
      HTTP_OPTIONS,
    );
  }

  public updatePlan(
    planId: number,
    name: string,
    primary: boolean,
  ): Observable<1> {
    return this.http.put<1>(
      this.degreePlanEndpoint(planId),
      { name, primary },
      HTTP_OPTIONS,
    );
  }

  /**
   * Helper function for building API endpoint URLs
   */
  private degreePlanEndpoint(...parts: any[]): string {
    return ['degreePlan']
      .concat(parts.map(part => part.toString()))
      .reduce((soFar, next) => {
        return Location.joinWithSlash(soFar, next);
      }, this.config.apiPlannerUrl);
  }

  private searchEndpoint(...parts: any[]): string {
    return parts
      .map(part => part.toString())
      .reduce((soFar, next) => {
        return Location.joinWithSlash(soFar, next);
      }, this.config.apiSearchUrl);
  }
}