Coursemology/coursemology2

View on GitHub
client/app/bundles/course/lesson-plan/reducers/utils.js

Summary

Maintainability
A
2 hrs
Test Coverage
import moment from 'lib/moment';

/**
 * Adds a new attribute itemTypeKey to the lesson plan item.
 * itemTypeKey has two functions:
 * 1. It serves as key for the visibilityByType hash
 * 2. It is used as the display string for the 'type' of the item.
 */
export function generateTypeKey(item) {
  return {
    ...item,
    itemTypeKey: item.lesson_plan_item_type.join(': '),
  };
}

/**
 * Converts the visibility settings received from the backend into a hash keyed by
 * itemTypeKey (as generated by generateTypeKey above).
 *
 * Example:
 *
 * Each setting is a hash like { setting_key: ['Standard Assessment', 'Tab 2'], visible: false }.
 *
 * This becomes the visibilitySetting hash { 'Standard Assessment: Tab 2': false } where the key
 * is in the same format as itemTypeKey.
 */
export function generateVisibilitySettings(visibilitySettings) {
  const newVisibilitySettings = {};
  visibilitySettings.forEach((setting) => {
    newVisibilitySettings[setting.setting_key.join(': ')] = setting.visible;
  });
  return newVisibilitySettings;
}

function sortByStartAt(a, b) {
  const aStartAt = moment(a.start_at);
  if (aStartAt.isAfter(b.start_at)) {
    return 1;
  }
  if (aStartAt.isBefore(b.start_at)) {
    return -1;
  }
  return 0;
}

/**
 * Groups lesson plan items under their respective milestones.
 * An item falls under a milestone if the milestone is the latest milestone
 * to have an earlier start_at date-time than the item.
 * Items that precedes all milestones are grouped with an empty milestone.
 * Items are sorted by startAt, then itemTypeKey, then title.
 *
 * @param {Array} items
 * @param {Array} milestones
 * @return {Array.<{ milestone: Object, items: Array }>}
 */
export function groupItemsUnderMilestones(items, milestones) {
  const sortedMilestones = [...milestones].sort(sortByStartAt);
  const sortedItems = [...items].sort((a, b) => {
    const startAtSortResult = sortByStartAt(a, b);
    if (startAtSortResult !== 0) {
      return startAtSortResult;
    }
    const itemTypeSortResult = a.itemTypeKey.localeCompare(b.itemTypeKey);
    if (itemTypeSortResult !== 0) {
      return itemTypeSortResult;
    }
    return a.title.localeCompare(b.title);
  });

  const groups = [];
  const group = { id: null, milestone: null, items: [] };

  // Adds current group to groups and resets group
  const addGroup = () => {
    if (group.items.length > 0 || group.milestone) {
      const milestoneId = group.milestone ? group.milestone.id : 'ungrouped';
      group.id = `milestone-group-${milestoneId}`;
      groups.push({ ...group });

      group.id = null;
      group.milestone = null;
      group.items = [];
    }
  };

  sortedMilestones.forEach((milestone) => {
    // Group items that come before the current milestone under the previous milestone
    while (
      sortedItems.length > 0 &&
      moment(sortedItems[0].start_at).isBefore(milestone.start_at)
    ) {
      group.items.push(sortedItems.shift());
    }
    // Finalize the group, then start a new group with the current milestone
    addGroup();
    group.milestone = milestone;
  });
  // The remaining items belong with the last milestone
  group.items = group.items.concat(sortedItems);
  addGroup();

  return groups;
}

/**
 * Generates a hash that indicates the visibility of each item type, e.g. :
 *   { "Training: Extra": false, "Recitation": true }
 * as read from the given visibilitySettings.
 *
 * All other items are visible by default.
 *
 * @param {Array} items
 * @param {{itemTypeKey: Boolean}} visibilitySettings keyed by itemTypeKey
 * @return {Object}
 */
export function initializeVisibility(items, visibilitySettings) {
  const itemTypes = new Set(items.map((item) => item.itemTypeKey));
  const visibility = {};
  itemTypes.forEach((itemType) => {
    const hasVisibilitySetting = Object.prototype.hasOwnProperty.call(
      visibilitySettings,
      itemType,
    );
    visibility[itemType] = hasVisibilitySetting
      ? visibilitySettings[itemType]
      : true;
  });
  return visibility;
}