wongjiahau/ttap-web

View on GitHub
src/ts/redux/actions/findTimetablesBasedOnChosenSlots.ts

Summary

Maintainability
A
3 hrs
Test Coverage
const find = require("lodash.find");
const sortBy = require("lodash.sortby");
import { IStringDicionary } from "../../interfaces/dictionary";
import { IGroupedTimetable } from "../../model/groupedTimetable";
import { RawSlot } from "../../model/rawSlot";
import {
  DiffReport,
  GenerateSubjectSchema,
  GetDiff,
  SubjectSchema,
} from "../../model/subjectSchema";
import { Timetable } from "../../model/timetable";
import { NullFindTimetableVisualizer } from "../../permutator/findTimetableVisualizer";
import { GroupSimilarTimetables } from "../../permutator/groupSimilarTimetables";
import { PartitionizeByKey } from "../../permutator/partitionize";
import { NewTimetableListState } from "../reducers/timetableListState";
import { IMasterState, MasterStateAction } from "./../reducers/masterState";

export class FindTimetablesBasedOnChosenSlots extends MasterStateAction {
  public constructor() {
    super();
  }
  public TypeName(): string {
    return "find timetables based on chosen slots";
  }
  protected GenerateNewState(state: IMasterState): IMasterState {
    const slotStore =
      state.DataState.RawSlotDataRouter.GetDataFrom("ungeneralized");
    const slotTableState = state.SlotTableState;
    const slotNumbersOfSelectedSlots = GetSlotNumbers(
      slotTableState.SlotStates
    );
    let currentSubjectSchemas: SubjectSchema[] = [];
    let newTimetables: IGroupedTimetable[] = [];
    let selectedSlots: RawSlot[] = [];
    if (slotNumbersOfSelectedSlots.length > 0) {
      selectedSlots = GetSlotsFromSlotNumbers(
        slotStore.GetAll(),
        slotNumbersOfSelectedSlots
      );
      newTimetables = GroupSimilarTimetables(
        state.SettingsState.TimetableFinder(
          selectedSlots,
          state.SettingsState.DisableClashChecking,
          new NullFindTimetableVisualizer()
        )
      );
      const slotsOfSubjects = PartitionizeByKey(selectedSlots, "SubjectCode");
      currentSubjectSchemas = slotsOfSubjects.map((x) =>
        GenerateSubjectSchema(x)
      );
      sortBy(currentSubjectSchemas, [(o: SubjectSchema) => o.SubjectCode]);
    }
    const selectedSubjects = state.SubjectListState.Subjects.filter(
      (s) => s.IsSelected
    );
    const correctSubjectSchemas = selectedSubjects.map((s) =>
      GenerateSubjectSchema(slotStore.GetBunch(s.SlotUids))
    );
    sortBy(correctSubjectSchemas, [(o: SubjectSchema) => o.SubjectCode]);

    let errorMessages: DiffReport[] = [];
    correctSubjectSchemas.forEach((s) => {
      let matchingSchema = find(currentSubjectSchemas, {
        SubjectCode: s.SubjectCode,
      });
      if (matchingSchema === undefined) {
        matchingSchema = new SubjectSchema(false, false, false, s.SubjectCode);
      }
      const diff = GetDiff(s, matchingSchema);
      if (diff.length > 0) {
        errorMessages = errorMessages.concat(diff);
      }
    });
    if (errorMessages.length === 0) {
      // if schema is tolerated
      // check if any timetables can be found based on currently selected slots
      if (newTimetables.length === 0) {
        errorMessages.push(new DiffReport("", "no possible timetables found"));
      }
    }
    if (errorMessages.length > 0) {
      return {
        ...state,
        SlotTableState: {
          ...state.SlotTableState,
          ErrorMessages: errorMessages,
        },
      };
    }
    return {
      ...state,
      SnackbarState: {
        IsOpen: true,
        Message: newTimetables.length + " possible timetables found.",
      },
      SlotTableState: {
        ...state.SlotTableState,
        IsOpen: false,
        ErrorMessages: null,
      },
      TimetableListState: NewTimetableListState(newTimetables, selectedSlots),
    };
  }
}
export function GetSlotsFromSlotNumbers(
  allSlots: RawSlot[],
  slotNumbers: string[]
): RawSlot[] {
  let result: RawSlot[] = [];
  slotNumbers.forEach((num) => {
    result = result.concat(allSlots.filter((x) => x.Number === num));
  });
  return result;
}
export function GetSlotNumbers(slotState: IStringDicionary<boolean>): string[] {
  const result: string[] = [];
  for (const key in slotState) {
    if (slotState.hasOwnProperty(key)) {
      const current = slotState[key];
      if (current === true) {
        result.push(key);
      }
    }
  }
  return result;
}