input.js

import 'babel-polyfill';
import * as XLSX from 'xlsx';
import parser from 'xml2json-light';
import _ from 'lodash';
import Utility from './utility';


/**
 * 차트 데이터 직접입력 이외의 방식을 모두 담당하는 클래스
 * @alias Input
 */
export default class Input {
  /**
   * @description spreadsheet 로 부터 데이터를 가져옴
   * <p>static 함수
   * @param { object } option spreadsheet 로 부터 데이터를 가져올 때 적용시킬 옵션
   * @memberof Input
   * @instance
   */
  static async getDataFromSpreadSheet(option) {
    const defaultOption = {
      sheet: 1,
      datasetDirection: 'horizontal',
      datasets: [],
    };

    option = { ...defaultOption, ...option };
    option.sheet -= 1;

    if (option.file === undefined) {
      Utility.error('No filename in spreadsheet');
      return;
    }

    // eslint-disable-next-line consistent-return
    return new Promise(((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.responseType = 'arraybuffer';

      xhr.onload = () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          const arraybuffer = xhr.response;
          const preData = new Uint8Array(arraybuffer);
          const arr = [];

          for (let i = 0; i !== preData.length; ++i) { arr[i] = String.fromCharCode(preData[i]); }

          const bstr = arr.join(''); // bstr [의미불명확]
          const workbook = XLSX.read(bstr, {
            type: 'binary',
          });
          const sheetName = workbook.SheetNames[option.sheet];
          const workSheet = workbook.Sheets[sheetName];
          const data = XLSX.utils.sheet_to_json(workSheet, {
            raw: true,
          });

          const chartData = {
            labels: [],
            datasets: [],
          };

          const firstLineData = Object.keys(data[0]);
          const datasetsIndex = [];

          if (option.datasetDirection === 'vertical') {
            for (let i = 1; i < firstLineData.length; i++) {
              const isIncludeExclude = _.some(option.excludeLabel, (el) => {
                if (_.includes(firstLineData[i], el)) {
                  return el;
                }

                return null;
              });

              if (!isIncludeExclude) {
                chartData.labels.push(firstLineData[i]);
              }
            }

            if (option.datasets.length === 0) {
              for (let i = 0; i < data.length; i++) {
                datasetsIndex.push(i);
              }
            } else {
              for (let i = 0; i < data.length; i++) {
                const datasetLabel = data[i][firstLineData[0]];
                if (option.datasets.indexOf(datasetLabel) !== -1) { datasetsIndex.push(i); }
              }
            }

            // make each datasets
            for (let i = 0; i < datasetsIndex.length; i++) {
              const dataset = {
                label: data[datasetsIndex[i]][firstLineData[0]],
              };
              dataset.data = [];
              for (let j = 1; j < firstLineData.length; j++) {
                const isIncludeExclude = _.some(option.excludeLabel, (el) => {
                  if (_.includes(firstLineData[j], el)) {
                    return el;
                  }

                  return null;
                });
                if (!isIncludeExclude) {
                  dataset.data.push(data[datasetsIndex[i]][firstLineData[j]]);
                }
              }
              chartData.datasets.push(dataset);
            }
          } else {
            const labelKey = firstLineData[0];
            for (let i = 0; i < data.length; i++) {
              const isIncludeExclude = _.some(option.excludeLabel, (el) => {
                if (_.includes(data[i][labelKey], el)) {
                  return el;
                }

                return null;
              });

              if (!isIncludeExclude) {
                chartData.labels.push(data[i][labelKey]);
              }
            }

            if (option.datasets.length === 0) {
              for (let i = 1; i < firstLineData.length; i++) {
                datasetsIndex.push(i);
              }
            } else {
              for (let i = 0; i < option.datasets.length; i++) {
                const datasetLabel = option.datasets[i];
                const arrayIndex = firstLineData.indexOf(datasetLabel);
                if (arrayIndex !== -1) { datasetsIndex.push(arrayIndex); }
              }
            }

            // make each datasets
            for (let i = 0; i < datasetsIndex.length; i++) {
              const dataset = {
                label: firstLineData[datasetsIndex[i]],
              };
              dataset.data = [];
              for (let j = 0; j < data.length; j++) {
                const isIncludeExclude = _.some(option.excludeLabel, (el) => {
                  if (_.includes(data[j][dataset.label], el)) {
                    return el;
                  }

                  return null;
                });

                if (!isIncludeExclude) {
                  dataset.data.push(data[j][dataset.label]);
                }
              }
              chartData.datasets.push(dataset);
            }
          }
          resolve(chartData);
        }
      };

      xhr.onerror = () => {
        reject(new Error(xhr.statusText));
      };

      xhr.open('GET', option.file, true);
      xhr.send();
    }));
  }

  /**
   * @description API 로 부터 데이터를 가져옴
   * <p>static 함수
   * @param { string } url API URL
   * @param { object } params API Parameters
   * @instance
   * @memberof Input
   */
  static async getDataFromAPI(url, params) {
    if (url === undefined) {
      Utility.error('API Url is not defined');
      return;
    }
    let finalUrl = url;
    if (params !== undefined && Object.keys(params).length !== 0) {
      finalUrl += '?';

      Object.keys(params).forEach((key) => {
        finalUrl += `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
      });
    }

    // eslint-disable-next-line consistent-return
    return new Promise(((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.onload = () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          let object;
          if (xhr.response[0] === '<') {
            object = parser.xml2json(xhr.response);
          } else {
            object = JSON.parse(xhr.response);
          }
          resolve(object);
        } else {
          reject(new Error(xhr.statusText));
        }
      };
      xhr.onerror = () => {
        reject(new Error(xhr.statusText));
      };

      xhr.open('GET', finalUrl);
      xhr.send();
    }));
  }
}