<commit_before>const coreApp = require('kolibri');
const logging = require('kolibri.lib.logging');

const ClassroomResource = coreApp.resources.ClassroomResource;
const FacilityResource = coreApp.resources.FacilityResource;
const MembershipResource = coreApp.resources.MembershipResource;
const FacilityUserResource = coreApp.resources.FacilityUserResource;
const TaskResource = coreApp.resources.TaskResource;
const RoleResource = coreApp.resources.RoleResource;

const coreActions = require('kolibri.coreVue.vuex.actions');
const ConditionalPromise = require('kolibri.lib.conditionalPromise');
const constants = require('./state/constants');
const UserKinds = require('kolibri.coreVue.vuex.constants').UserKinds;

const PageNames = constants.PageNames;
const ContentWizardPages = constants.ContentWizardPages;
const samePageCheckGenerator = require('kolibri.coreVue.vuex.actions').samePageCheckGenerator;


/**
 * Vuex State Mappers
 *
 * The methods below help map data from
 * the API to state in the Vuex store
 */
function _classState(data) {
  const state = {
    id: data.id,
    name: data.name,
    parent: data.parent,
    learner_count: data.learner_count,
    coach_count: data.coach_count,
    admin_count: data.admin_count,
  };
  return state;
}

function _facilityState(data) {
  const state = {
    id: data.id,
    name: data.name,
  };
  return state;
}


function _userState(apiUserData) {
  function calcUserKind() {
    if (apiUserData.roles) {
      // array of strings, where each string represents a role object
      const roleKinds = apiUserData.roles.map((roleObj) => roleObj.kind);
      if (roleKinds.includes(UserKinds.ADMIN || UserKinds.SUPERUSER)) {
        return UserKinds.ADMIN;
      } else if (roleKinds.includes(UserKinds.COACH)) {
        return UserKinds.COACH;
      }
    }
    return UserKinds.LEARNER;
  }

  return {
    id: apiUserData.id,
    facility_id: apiUserData.facility,
    username: apiUserData.username,
    full_name: apiUserData.full_name,
    kind: calcUserKind(apiUserData.roles),
  };
}


function _taskState(data) {
  const state = {
    id: data.id,
    type: data.type,
    status: data.status,
    metadata: data.metadata,
    percentage: data.percentage,
  };
  return state;
}

/**
 * Title Helper
 */

function _managePageTitle(title) {
  return `Manage ${title}`;
}


/**
 * Actions
 *
 * These methods are used to update client-side state
 */


function displayModal(store, modalName) {
  store.dispatch('SET_MODAL', modalName);
}


// ================================
// CLASSES MANAGEMENT ACTIONS

/**
 * Do a POST to create new class
 * @param {object} stateClassData
 *  Needed: name
 */
function createClass(store, stateClassData) {
  const classData = {
    name: stateClassData.name,
    parent: stateClassData.facilityId,
  };

  ClassroomResource.createModel(classData).save().then(
    cl => {
      // dispatch newly created class
      store.dispatch('ADD_CLASS', _classState(cl));
      displayModal(store, false);
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

/**
 * Do a DELETE to delete the class.
 * @param {string or Integer} id
 */
function deleteClass(store, id) {
  if (!id) {
    // if no id passed, abort the function
    return;
  }
  ClassroomResource.getModel(id).delete().then(
    cl => {
      store.dispatch('DELETE_CLASS', id);
      displayModal(store, false);
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

/**
 * Do a PATCH to update the class.
 * @param {string} id - class id.
 * @param {object} updateData.
 */
function updateClass(store, id, updateData) {
  if (!id || Object.keys(updateData).length === 0) {
    // if no id or empty updateData passed, abort the function
    return;
  }
  const classModel = ClassroomResource.getModel(id);

  classModel.save(updateData).then(
    response => {
      store.dispatch('UPDATE_CLASS', id, response);
      displayModal(store, false);
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

function removeClassUser(store, classId, userId) {
  if (!classId || !userId) {
    // if no id passed, abort the function
    return;
  }
  // fetch the membership model with this classId and userId.
  const MembershipCollection = MembershipResource.getCollection({
    user_id: userId,
    collection_id: classId,
  });

  MembershipCollection.fetch().then(
    (membership) => {
      const membershipId = membership[0].id; // will always only have one item in the array.
      MembershipResource.getModel(membershipId).delete().then(
        response => {
          store.dispatch('DELETE_USER', userId);
          displayModal(store, false);
        },
        error => { coreActions.handleApiError(store, error); }
      );
    }
  );
}

function showClassesPage(store) {
  store.dispatch('CORE_SET_PAGE_LOADING', true);
  store.dispatch('SET_PAGE_NAME', PageNames.CLASS_MGMT_PAGE);
  const classCollection = ClassroomResource.getCollection();
  const classPromise = classCollection.fetch({}, true);
  const facilityCollection = FacilityResource.getCollection();
  const facilityPromise = facilityCollection.fetch();

  const promises = [facilityPromise, classPromise];

  ConditionalPromise.all(promises).only(
    samePageCheckGenerator(store),
    ([facility, classes]) => {
      const pageState = {
        modalShown: null,
        facility: _facilityState(facility[0]), // for mvp, we assume only one facility exists
        classes: classes.map(_classState),
      };

      store.dispatch('SET_PAGE_STATE', pageState);
      store.dispatch('CORE_SET_PAGE_LOADING', false);
      store.dispatch('CORE_SET_ERROR', null);
      store.dispatch('CORE_SET_TITLE', _managePageTitle('Classes'));
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

function showClassEditPage(store, classId) {
  store.dispatch('CORE_SET_PAGE_LOADING', true);
  store.dispatch('SET_PAGE_NAME', PageNames.CLASS_EDIT_MGMT_PAGE);

  const classModel = ClassroomResource.getModel(classId);
  const classPromise = classModel.fetch();
  // fetch the users under this class.
  const classUsersCollection = FacilityUserResource.getCollection({ member_of: classId });
  const classUsersPromise = classUsersCollection.fetch();

  const promises = [classUsersPromise, classPromise];

  ConditionalPromise.all(promises).only(
    samePageCheckGenerator(store),
    ([users, cl]) => {
      const pageState = {
        modalShown: null,
        classes: [cl],
        users: users.map(_userState),
      };
      store.dispatch('SET_PAGE_STATE', pageState);
      store.dispatch('CORE_SET_PAGE_LOADING', false);
      store.dispatch('CORE_SET_ERROR', null);
      store.dispatch('CORE_SET_TITLE', _managePageTitle('Classes'));
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

function showClassEnrollPage(store, classId) {
  store.dispatch('CORE_SET_PAGE_LOADING', true);
  store.dispatch('SET_PAGE_NAME', PageNames.CLASS_ENROLL_MGMT_PAGE);
  store.dispatch('CORE_SET_TITLE', _managePageTitle('Classes'));
  store.dispatch('CORE_SET_ERROR', null);
  const facilityIdPromise = FacilityUserResource.getCurrentFacility();
  const userPromise = FacilityUserResource.getCollection().fetch();
  const classPromise = ClassroomResource.getCollection().fetch({}, true);

  ConditionalPromise.all([facilityIdPromise, userPromise, classPromise]).only(
    samePageCheckGenerator(store),
    ([facilityId, users, usersInClass]) => {
      store.dispatch('SET_FACILITY', facilityId[0]); // for mvp, we assume only one facility exists
      const pageState = { classId, users: users.map(_userState) };
      store.dispatch('SET_PAGE_STATE', pageState);
      store.dispatch('CORE_SET_PAGE_LOADING', false);
    },
    error => {
      coreActions.handleApiError(store, error);
    }
  );<commit_msg>Get class users.<commit_after>const coreApp = require('kolibri');
const logging = require('kolibri.lib.logging');

const ClassroomResource = coreApp.resources.ClassroomResource;
const FacilityResource = coreApp.resources.FacilityResource;
const MembershipResource = coreApp.resources.MembershipResource;
const FacilityUserResource = coreApp.resources.FacilityUserResource;
const TaskResource = coreApp.resources.TaskResource;
const RoleResource = coreApp.resources.RoleResource;

const coreActions = require('kolibri.coreVue.vuex.actions');
const ConditionalPromise = require('kolibri.lib.conditionalPromise');
const constants = require('./state/constants');
const UserKinds = require('kolibri.coreVue.vuex.constants').UserKinds;

const PageNames = constants.PageNames;
const ContentWizardPages = constants.ContentWizardPages;
const samePageCheckGenerator = require('kolibri.coreVue.vuex.actions').samePageCheckGenerator;


/**
 * Vuex State Mappers
 *
 * The methods below help map data from
 * the API to state in the Vuex store
 */
function _classState(data) {
  const state = {
    id: data.id,
    name: data.name,
    parent: data.parent,
    learner_count: data.learner_count,
    coach_count: data.coach_count,
    admin_count: data.admin_count,
  };
  return state;
}

function _facilityState(data) {
  const state = {
    id: data.id,
    name: data.name,
  };
  return state;
}


function _userState(apiUserData) {
  function calcUserKind() {
    if (apiUserData.roles) {
      // array of strings, where each string represents a role object
      const roleKinds = apiUserData.roles.map((roleObj) => roleObj.kind);
      if (roleKinds.includes(UserKinds.ADMIN || UserKinds.SUPERUSER)) {
        return UserKinds.ADMIN;
      } else if (roleKinds.includes(UserKinds.COACH)) {
        return UserKinds.COACH;
      }
    }
    return UserKinds.LEARNER;
  }

  return {
    id: apiUserData.id,
    facility_id: apiUserData.facility,
    username: apiUserData.username,
    full_name: apiUserData.full_name,
    kind: calcUserKind(apiUserData.roles),
  };
}


function _taskState(data) {
  const state = {
    id: data.id,
    type: data.type,
    status: data.status,
    metadata: data.metadata,
    percentage: data.percentage,
  };
  return state;
}

/**
 * Title Helper
 */

function _managePageTitle(title) {
  return `Manage ${title}`;
}


/**
 * Actions
 *
 * These methods are used to update client-side state
 */


function displayModal(store, modalName) {
  store.dispatch('SET_MODAL', modalName);
}


// ================================
// CLASSES MANAGEMENT ACTIONS

/**
 * Do a POST to create new class
 * @param {object} stateClassData
 *  Needed: name
 */
function createClass(store, stateClassData) {
  const classData = {
    name: stateClassData.name,
    parent: stateClassData.facilityId,
  };

  ClassroomResource.createModel(classData).save().then(
    cl => {
      // dispatch newly created class
      store.dispatch('ADD_CLASS', _classState(cl));
      displayModal(store, false);
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

/**
 * Do a DELETE to delete the class.
 * @param {string or Integer} id
 */
function deleteClass(store, id) {
  if (!id) {
    // if no id passed, abort the function
    return;
  }
  ClassroomResource.getModel(id).delete().then(
    cl => {
      store.dispatch('DELETE_CLASS', id);
      displayModal(store, false);
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

/**
 * Do a PATCH to update the class.
 * @param {string} id - class id.
 * @param {object} updateData.
 */
function updateClass(store, id, updateData) {
  if (!id || Object.keys(updateData).length === 0) {
    // if no id or empty updateData passed, abort the function
    return;
  }
  const classModel = ClassroomResource.getModel(id);

  classModel.save(updateData).then(
    response => {
      store.dispatch('UPDATE_CLASS', id, response);
      displayModal(store, false);
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

function removeClassUser(store, classId, userId) {
  if (!classId || !userId) {
    // if no id passed, abort the function
    return;
  }
  // fetch the membership model with this classId and userId.
  const MembershipCollection = MembershipResource.getCollection({
    user_id: userId,
    collection_id: classId,
  });

  MembershipCollection.fetch().then(
    (membership) => {
      const membershipId = membership[0].id; // will always only have one item in the array.
      MembershipResource.getModel(membershipId).delete().then(
        response => {
          store.dispatch('DELETE_USER', userId);
          displayModal(store, false);
        },
        error => { coreActions.handleApiError(store, error); }
      );
    }
  );
}

function showClassesPage(store) {
  store.dispatch('CORE_SET_PAGE_LOADING', true);
  store.dispatch('SET_PAGE_NAME', PageNames.CLASS_MGMT_PAGE);
  const classCollection = ClassroomResource.getCollection();
  const classPromise = classCollection.fetch({}, true);
  const facilityCollection = FacilityResource.getCollection();
  const facilityPromise = facilityCollection.fetch();

  const promises = [facilityPromise, classPromise];

  ConditionalPromise.all(promises).only(
    samePageCheckGenerator(store),
    ([facility, classes]) => {
      const pageState = {
        modalShown: null,
        facility: _facilityState(facility[0]), // for mvp, we assume only one facility exists
        classes: classes.map(_classState),
      };

      store.dispatch('SET_PAGE_STATE', pageState);
      store.dispatch('CORE_SET_PAGE_LOADING', false);
      store.dispatch('CORE_SET_ERROR', null);
      store.dispatch('CORE_SET_TITLE', _managePageTitle('Classes'));
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

function showClassEditPage(store, classId) {
  store.dispatch('CORE_SET_PAGE_LOADING', true);
  store.dispatch('SET_PAGE_NAME', PageNames.CLASS_EDIT_MGMT_PAGE);

  const classModel = ClassroomResource.getModel(classId);
  const classPromise = classModel.fetch();
  // fetch the users under this class.
  const classUsersCollection = FacilityUserResource.getCollection({ member_of: classId });
  const classUsersPromise = classUsersCollection.fetch();

  const promises = [classUsersPromise, classPromise];

  ConditionalPromise.all(promises).only(
    samePageCheckGenerator(store),
    ([users, cl]) => {
      const pageState = {
        modalShown: null,
        classes: [cl],
        users: users.map(_userState),
      };
      store.dispatch('SET_PAGE_STATE', pageState);
      store.dispatch('CORE_SET_PAGE_LOADING', false);
      store.dispatch('CORE_SET_ERROR', null);
      store.dispatch('CORE_SET_TITLE', _managePageTitle('Classes'));
    },
    error => { coreActions.handleApiError(store, error); }
  );
}

function showClassEnrollPage(store, classId) {
  store.dispatch('CORE_SET_PAGE_LOADING', true);
  store.dispatch('SET_PAGE_NAME', PageNames.CLASS_ENROLL_MGMT_PAGE);
  store.dispatch('CORE_SET_TITLE', _managePageTitle('Classes'));
  store.dispatch('CORE_SET_ERROR', null);
  const userPromise = FacilityUserResource.getCollection().fetch({}, true);
  const classUsersPromise = FacilityUserResource.getCollection({ member_of: classId }).fetch();

  ConditionalPromise.all([userPromise, classUsersPromise]).only(
    samePageCheckGenerator(store),
    ([users, usersInClass]) => {
      console.log(users, usersInClass);
      const pageState = { classId, users: users.map(_userState) };
      store.dispatch('SET_PAGE_STATE', pageState);
      store.dispatch('CORE_SET_PAGE_LOADING', false);
    },
    error => {
      coreActions.handleApiError(store, error);
    }
  );
{'fraction_non_alphanumeric': 0.12705973223480949, 'fraction_numerical': 0.0004505664263645726, 'mean_word_length': 3.9245641838351824, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7143115', 'n_tokens_mistral': 4734, 'n_tokens_neox': 4690, 'n_words': 994}