<commit_before>// npm modules
import moment from 'moment';
import chalk from 'chalk';
import currencyFormatter from 'currency-formatter';
import ora from 'ora';
// our modules
import {api, config, formatPeriods, ReviewsStats} from '../utils';

function print(report) {
  let output = `${chalk.blue(`\nEarnings Report for ${report.startDate} to ${report.endDate}:`)}\n`;

  Object.keys(report.projects).forEach((key) => {
    const {name, id, ungradeable, passed, failed, earned, avgTurnaroundTime, totalAssigned} = report.projects[key];
    const projectInfo = `Project: ${name} (${id}):`;

    output += `
    ${projectInfo}
    ${new Array(projectInfo.length + 1).join('-')}
        Total Assigned: ${totalAssigned}
            Reviewed: ${passed + failed}
            Ungradeable: ${ungradeable}
        Earned: ${currencyFormatter.format(earned, {code: 'USD'})}
        Average Turnaround Time: ${moment(avgTurnaroundTime).format('HH:mm')}\n`;
  });

  const totalEarned = currencyFormatter.format(report.totalEarned, {code: 'USD'});
  output += `\nTotal Earned: ${totalEarned}\n`;

  if (report.numberOfDays) {
    const dailyAverage = currencyFormatter.format(report.dailyAverage, {code: 'USD'});
    output += `Daily Average: ${dailyAverage}\n`;
  }
  output += '=============================================';
  return output;
}

function formatUserInput(args, options) {
  const userInput = args;
  // Check if the user is using the --from and --to flags.
  if (options.from || options.to) {
    userInput.push({
      from: options.from || config.startDate,
      to: options.to || moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
    });
  }
  // If the user didn't input any args, we create a period that represents
  // the total time the reviewer has been actively reviewing projects.
  if (!args.length) {
    userInput.push({
      from: config.startDate,
      to: moment().endOf('day').format('YYYY-MM-DDTHH:mm:ss.SSS'),
    });
  }
  return userInput;
}

export const revenueCmd = (args, options) => {
  const spinner = ora('Getting data...').start();
  let output = '';
  const userInput = formatUserInput(args, options);
  const periods = formatPeriods(userInput, (err, res) => {
    if (err) throw err;
    return res;
  });
  // Output reports for each period
  const resolvedReports = periods.map(async (period) => {
    const completedReviews = await api({
      task: 'completed',
      body: {
        start_date: period[0].format('YYYY-MM-DDTHH:mm:ss.SSS'),
        end_date: period[1].format('YYYY-MM-DDTHH:mm:ss.SSS'),
      },
    });
    const report = new ReviewsStats(completedReviews.body, period);
    output += print(report);
    return Promise.resolve();
  });
  Promise.all(resolvedReports).then(() => {
    spinner.succeed('Reports completed:');
    console.log(output);
    process.exit(0);
  });
};<commit_msg>Add error handling for API errors<commit_after>// npm modules
import moment from 'moment';
import chalk from 'chalk';
import currencyFormatter from 'currency-formatter';
import ora from 'ora';
// our modules
import {api, config, formatPeriods, ReviewsStats} from '../utils';

function print(report) {
  let output = `${chalk.blue(`\nEarnings Report for ${report.startDate} to ${report.endDate}:`)}\n`;

  Object.keys(report.projects).forEach((key) => {
    const {name, id, ungradeable, passed, failed, earned, avgTurnaroundTime, totalAssigned} = report.projects[key];
    const projectInfo = `Project: ${name} (${id}):`;

    output += `
    ${projectInfo}
    ${new Array(projectInfo.length + 1).join('-')}
        Total Assigned: ${totalAssigned}
            Reviewed: ${passed + failed}
            Ungradeable: ${ungradeable}
        Earned: ${currencyFormatter.format(earned, {code: 'USD'})}
        Average Turnaround Time: ${moment(avgTurnaroundTime).format('HH:mm')}\n`;
  });

  const totalEarned = currencyFormatter.format(report.totalEarned, {code: 'USD'});
  output += `\nTotal Earned: ${totalEarned}\n`;

  if (report.numberOfDays) {
    const dailyAverage = currencyFormatter.format(report.dailyAverage, {code: 'USD'});
    output += `Daily Average: ${dailyAverage}\n`;
  }
  output += '=============================================';
  return output;
}

function formatUserInput(args, options) {
  const userInput = args;
  // Check if the user is using the --from and --to flags.
  if (options.from || options.to) {
    userInput.push({
      from: options.from || config.startDate,
      to: options.to || moment.utc().format('YYYY-MM-DDTHH:mm:ss.SSS'),
    });
  }
  // If the user didn't input any args, we create a period that represents
  // the total time the reviewer has been actively reviewing projects.
  if (!args.length) {
    userInput.push({
      from: config.startDate,
      to: moment().endOf('day').format('YYYY-MM-DDTHH:mm:ss.SSS'),
    });
  }
  return userInput;
}

export const revenueCmd = (args, options) => {
  const spinner = ora('Getting data...').start();
  let output = '';
  const userInput = formatUserInput(args, options);
  const periods = formatPeriods(userInput, (err, res) => {
    if (err) throw err;
    return res;
  });
  // Output reports for each period
  const resolvedReports = periods.map(async (period) => {
    try {
      const completedReviews = await api({
        task: 'completed',
        body: {
          start_date: period[0].format('YYYY-MM-DDTHH:mm:ss.SSS'),
          end_date: period[1].format('YYYY-MM-DDTHH:mm:ss.SSS'),
        },
      })
      const report = new ReviewsStats(completedReviews.body, period);
      output += print(report);
      return Promise.resolve();
    } catch (e) {
      console.log(chalk.red(`\n\n  The API is returning the following error: "${e.error}"`));
      process.exit(1);
    }
  });
  Promise.all(resolvedReports).then(() => {
    spinner.succeed('Reports completed:');
    console.log(output);
    process.exit(0);
  });
};
{'fraction_non_alphanumeric': 0.17396469789545146, 'fraction_numerical': 0.0015274949083503055, 'mean_word_length': 3.519171779141104, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, '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': '2096969', 'n_tokens_mistral': 1898, 'n_tokens_neox': 1839, 'n_words': 452}