Lambda-School-Labs/Labs26-StorySquad-BE-TeamB

View on GitHub
api/mod/modHelpers/clusterGeneration.js

Summary

Maintainability
A
55 mins
Test Coverage
A
93%
const db = require('../../../data/db-config');
const { dsApi, dbOps, formatCohortSubmissions } = require('../../../lib');

/**
 * This function runs a series of transactional requests to:
 *   1. pull and format submission data from our database
 *   2. send the formatted data to the DSAPI for clustering
 *   3. store the generated "squads" in our databse and put the members into teams
 * @returns {Object} returns an object containing the new clusters (not needed)
 */
const clusterGeneration = () => {
  return db.transaction(async (trx) => {
    try {
      const dsReq = {};
      // Pull a list of all cohorts
      const cohorts = await trx('Cohorts');
      // Iterate over the cohorts
      for (let { ID } of cohorts) {
        // Get all submissions for every cohort
        const unformatted = await dbOps.getAllSubmissionsByCohort(trx, ID);
        // Store each cohorts' submissions in the dsReq hash table with the key being the cohort id
        dsReq[ID] = formatCohortSubmissions(unformatted);
      }
      // Send the submissions to data science for clustering
      const { data } = await dsApi.getClusters(dsReq);

      // Parse the JSON response
      const clusters = JSON.parse(data);

      // Add the generated clusters to the database
      let members = [];
      // Iterate over cohorts again
      for (let { ID } of cohorts) {
        // For every squad returned in each cohort from data science, where squad is an array of 4 submission IDs
        for (let squad of clusters[ID]) {
          // Create a squad in the database for the current cohort
          const [SquadID] = await addSquad(trx, ID);
          // Create two teams for the newly created squad
          const [t1, t2] = await addTeams(trx, SquadID);
          // Create 4 new team member entries for the database
          const newMembers = await addMembers(trx, t1, t2, squad);
          // [[1], [2], [3], [4]] => [1, 2, 3, 4]
          members.push([].concat.apply([], newMembers));
        }
      }

      // This line flattens the array of arrays so that this function
      // returns a simple 1D array of integers
      return members;
    } catch (err) {
      console.log(err.message);
      throw new Error(err.message);
    }
  });
};

// Helper functions for storing clusters in the database

/**
 * Adds a new cluster (squad) to the database for foreign key references.
 * @param {Object} conn a knex client instance
 * @param {number} CohortID the integer representation of the cohort
 * @returns {Promise} returns a promise resolving to an array with the newly generated squad ID inside
 */
const addSquad = (conn, CohortID) => {
  return conn('Squads').insert({ CohortID }).returning('ID');
};

/**
 * Adds two new teams to the database for the same squad
 * @param {Object} conn a knex client instance
 * @param {number} SquadID the integer representation of the squad
 * @param {string} team1Name name of the given team (defaults to "Team #")
 * @param {string} team2Name name of the given team (defaults to "Team #")
 * @returns {Promise} returns a promise resolving to an array with the two new team IDs inside
 */
const addTeams = (
  conn,
  SquadID,
  team1Name = 'Team 1',
  team2Name = 'Team 2'
) => {
  return conn('Teams')
    .insert([
      { SquadID, Name: team1Name, Num: 1 },
      { SquadID, Name: team2Name, Num: 2 },
    ])
    .returning('ID');
};

/**
 * Adds a group of team members to the database
 * @param {Object} conn a knex client instance
 * @param {number} team1ID the integer representation of the first team
 * @param {number} team2ID the integer representation of the second team
 * @param {Array} subIDs an array of 4 IDs, each pointing to the submissions for the given team
 * @returns {Promise} returns a promise resolving to an array containing all of the new members' IDs
 */
const addMembers = (conn, team1ID, team2ID, subIDs) => {
  return Promise.all(
    subIDs.map((id, i) => addSingleMember(conn, id, i < 2 ? team1ID : team2ID))
  );
};

/**
 * Adds a new team member to the database, should only be called by addMembers()
 * @param {Object} conn a knex client instance
 * @param {number} SubmissionID the integer representation of the submission
 * @param {number} TeamID the integer representation of the team
 * @returns {Promise} returns a promise resolving to an array with the new member's ID in it
 */
const addSingleMember = (conn, SubmissionID, TeamID) => {
  return conn('Members').insert({ SubmissionID, TeamID }).returning('ID');
};

module.exports = clusterGeneration;