<commit_before>import React from 'react';
import autoBind from 'react-autobind';
import Select from 'react-select';
import alertify from 'alertifyjs';
import MultiCheckbox from 'js/components/common/multiCheckbox';
import Checkbox from 'js/components/common/checkbox';
import TextBox from 'js/components/common/textBox';
import ToggleSwitch from 'js/components/common/toggleSwitch';
import {bem} from 'js/bem';
import {actions} from 'js/actions';
import {formatTimeDate} from 'js/utils';
import mixins from 'js/mixins';
import {
  ADDITIONAL_SUBMISSION_PROPS,
  PERMISSIONS_CODENAMES,
} from 'js/constants';
import {
  EXPORT_TYPES,
  DEFAULT_EXPORT_SETTINGS,
  EXPORT_FORMATS,
  EXPORT_MULTIPLE_OPTIONS,
} from './exportsConstants';
import assetUtils from 'js/assetUtils';
import exportsStore from 'js/components/projectDownloads/exportsStore';

const NAMELESS_EXPORT_NAME = t('Latest unsaved settings');

/**
 * @prop {object} asset
 *
 * NOTE: we use a nameless export setting to keep last used export settings that
 * weren't saved as named custom setting.
 */
export default class ProjectExportsCreator extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isComponentReady: false,
      isPending: false, // is either saving setting or creating export
      // selectedExportType is being handled by exportsStore to allow other
      // components to know it changed
      selectedExportType: exportsStore.getExportType(),
      selectedExportFormat: this.getContextualDefaultExportFormat(),
      groupSeparator: DEFAULT_EXPORT_SETTINGS.GROUP_SEPARATOR,
      selectedExportMultiple: DEFAULT_EXPORT_SETTINGS.EXPORT_MULTIPLE,
      isIncludeGroupsEnabled: DEFAULT_EXPORT_SETTINGS.INCLUDE_GROUPS,
      isIncludeAllVersionsEnabled: DEFAULT_EXPORT_SETTINGS.INCLUDE_ALL_VERSIONS,
      isAdvancedViewVisible: false,
      isSaveCustomExportEnabled: DEFAULT_EXPORT_SETTINGS.SAVE_CUSTOM_EXPORT,
      customExportName: DEFAULT_EXPORT_SETTINGS.CUSTOM_EXPORT_NAME,
      isCustomSelectionEnabled: DEFAULT_EXPORT_SETTINGS.CUSTOM_SELECTION,
      isFlattenGeoJsonEnabled: DEFAULT_EXPORT_SETTINGS.FLATTEN_GEO_JSON,
      selectedRows: new Set(),
      selectableRowsCount: 0,
      selectedDefinedExport: null,
      definedExports: [],
      isUpdatingDefinedExportsList: false,
    };

    this.unlisteners = [];

    const allSelectableRows = this.getAllSelectableRows();
    if (allSelectableRows) {
      this.state.selectedRows = new Set(allSelectableRows);
      this.state.selectableRowsCount = this.state.selectedRows.size;
    }

    autoBind(this);
  }

  componentDidMount() {
    this.unlisteners.push(
      exportsStore.listen(this.onExportsStoreChange),
      actions.exports.getExportSettings.completed.listen(this.onGetExportSettingsCompleted),
      actions.exports.updateExportSetting.completed.listen(this.fetchExportSettings.bind(this, true)),
      actions.exports.createExportSetting.completed.listen(this.fetchExportSettings.bind(this, true)),
      actions.exports.deleteExportSetting.completed.listen(this.onDeleteExportSettingCompleted),
    );

    this.fetchExportSettings(true);
  }

  componentWillUnmount() {
    this.unlisteners.forEach((clb) => {clb();});
  }

  setDefaultExportSettings() {
    exportsStore.setExportType(DEFAULT_EXPORT_SETTINGS.EXPORT_TYPE);
    this.setState({
      selectedExportFormat: this.getContextualDefaultExportFormat(),
      groupSeparator: DEFAULT_EXPORT_SETTINGS.GROUP_SEPARATOR,
      selectedExportMultiple: DEFAULT_EXPORT_SETTINGS.EXPORT_MULTIPLE,
      isIncludeGroupsEnabled: DEFAULT_EXPORT_SETTINGS.INCLUDE_GROUPS,
      isIncludeAllVersionsEnabled: DEFAULT_EXPORT_SETTINGS.INCLUDE_ALL_VERSIONS,
      isSaveCustomExportEnabled: DEFAULT_EXPORT_SETTINGS.SAVE_CUSTOM_EXPORT,
      customExportName: DEFAULT_EXPORT_SETTINGS.CUSTOM_EXPORT_NAME,
      isCustomSelectionEnabled: DEFAULT_EXPORT_SETTINGS.CUSTOM_SELECTION,
      isFlattenGeoJsonEnabled: DEFAULT_EXPORT_SETTINGS.FLATTEN_GEO_JSON,
      selectedRows: new Set(this.getAllSelectableRows()),
    });
  }

  onExportsStoreChange() {
    const newExportType = exportsStore.getExportType();
    if (newExportType.value !== this.state.selectedExportType.value) {
      const newStateObj = {
        selectedExportType: newExportType,
        // when export type changes, make sure the custom export is cleared
        // to avoid users saving unwanted changes (the custom export name is not
        // visible unless Advanced View is toggled)
        isSaveCustomExportEnabled: false,
        customExportName: '',
      };

      this.setState(newStateObj);
    }
  }

  onGetExportSettingsCompleted(response, passData) {
    // we need to prepare the results to be displayed in Select
    const definedExports = [];
    response.results.forEach((result, index) => {
      definedExports.push({
        value: index,
        label: result.name ? result.name : NAMELESS_EXPORT_NAME,
        data: result,
      });
    });

    this.setState({
      isUpdatingDefinedExportsList: false,
      definedExports: definedExports,
    });

    // load last saved settings
    if (response.count >= 1 && passData?.preselectLastSettings) {
      this.applyExportSettingToState(response.results[0]);
    }

    if (!this.state.isComponentReady) {
      this.setState({isComponentReady: true});
    }
  }

  onDeleteExportSettingCompleted() {
    this.clearSelectedDefinedExport();
    this.fetchExportSettings();
  }

  getExportFormatOptions() {
    if (this.props.asset.summary?.languages.length >= 2) {
      const options = [];
      // all defined languages should go first
      // and they replace `_default` option
      this.props.asset.summary.languages.forEach((language, index) => {
        options.push({
          value: language,
          label: language,
          langIndex: index, // needed for later
        });
      });
      options.push(EXPORT_FORMATS._xml);
      return options;
    } else {
      return [
        // `_default` should be first
        EXPORT_FORMATS._default,
        EXPORT_FORMATS._xml,
      ];
    }
  }

  /**
   * @returns one of export format options, either the asset's default language
   * or `_default` (or more precisely: the first option)
   */
  getContextualDefaultExportFormat() {
    const exportFormatOptions = this.getExportFormatOptions();
    const defaultAssetLanguage = this.props.asset.summary?.default_translation;
    const defaultAssetLanguageOption = exportFormatOptions.find((option) => {
      return defaultAssetLanguage === option.value;
    });
    return defaultAssetLanguageOption || exportFormatOptions[0];
  }

  getAllSelectableRows() {
    const allRows = new Set();
    if (this.props.asset?.content?.survey) {
      const flatPaths = assetUtils.getSurveyFlatPaths(
        this.props.asset.content.survey,
        false,
        true
      );
      Object.values(flatPaths).forEach((path) => {allRows.add(path);});
      Object.keys(ADDITIONAL_SUBMISSION_PROPS).forEach((submissionProp) => {
        allRows.add(submissionProp);
      });<commit_msg>rewrite format options list to work with single named language and unnamed language aside named ones<commit_after>import React from 'react';
import autoBind from 'react-autobind';
import Select from 'react-select';
import alertify from 'alertifyjs';
import MultiCheckbox from 'js/components/common/multiCheckbox';
import Checkbox from 'js/components/common/checkbox';
import TextBox from 'js/components/common/textBox';
import ToggleSwitch from 'js/components/common/toggleSwitch';
import {bem} from 'js/bem';
import {actions} from 'js/actions';
import {formatTimeDate} from 'js/utils';
import mixins from 'js/mixins';
import {
  ADDITIONAL_SUBMISSION_PROPS,
  PERMISSIONS_CODENAMES,
} from 'js/constants';
import {
  EXPORT_TYPES,
  DEFAULT_EXPORT_SETTINGS,
  EXPORT_FORMATS,
  EXPORT_MULTIPLE_OPTIONS,
} from './exportsConstants';
import assetUtils from 'js/assetUtils';
import exportsStore from 'js/components/projectDownloads/exportsStore';

const NAMELESS_EXPORT_NAME = t('Latest unsaved settings');

/**
 * @prop {object} asset
 *
 * NOTE: we use a nameless export setting to keep last used export settings that
 * weren't saved as named custom setting.
 */
export default class ProjectExportsCreator extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isComponentReady: false,
      isPending: false, // is either saving setting or creating export
      // selectedExportType is being handled by exportsStore to allow other
      // components to know it changed
      selectedExportType: exportsStore.getExportType(),
      selectedExportFormat: this.getContextualDefaultExportFormat(),
      groupSeparator: DEFAULT_EXPORT_SETTINGS.GROUP_SEPARATOR,
      selectedExportMultiple: DEFAULT_EXPORT_SETTINGS.EXPORT_MULTIPLE,
      isIncludeGroupsEnabled: DEFAULT_EXPORT_SETTINGS.INCLUDE_GROUPS,
      isIncludeAllVersionsEnabled: DEFAULT_EXPORT_SETTINGS.INCLUDE_ALL_VERSIONS,
      isAdvancedViewVisible: false,
      isSaveCustomExportEnabled: DEFAULT_EXPORT_SETTINGS.SAVE_CUSTOM_EXPORT,
      customExportName: DEFAULT_EXPORT_SETTINGS.CUSTOM_EXPORT_NAME,
      isCustomSelectionEnabled: DEFAULT_EXPORT_SETTINGS.CUSTOM_SELECTION,
      isFlattenGeoJsonEnabled: DEFAULT_EXPORT_SETTINGS.FLATTEN_GEO_JSON,
      selectedRows: new Set(),
      selectableRowsCount: 0,
      selectedDefinedExport: null,
      definedExports: [],
      isUpdatingDefinedExportsList: false,
    };

    this.unlisteners = [];

    const allSelectableRows = this.getAllSelectableRows();
    if (allSelectableRows) {
      this.state.selectedRows = new Set(allSelectableRows);
      this.state.selectableRowsCount = this.state.selectedRows.size;
    }

    autoBind(this);
  }

  componentDidMount() {
    this.unlisteners.push(
      exportsStore.listen(this.onExportsStoreChange),
      actions.exports.getExportSettings.completed.listen(this.onGetExportSettingsCompleted),
      actions.exports.updateExportSetting.completed.listen(this.fetchExportSettings.bind(this, true)),
      actions.exports.createExportSetting.completed.listen(this.fetchExportSettings.bind(this, true)),
      actions.exports.deleteExportSetting.completed.listen(this.onDeleteExportSettingCompleted),
    );

    this.fetchExportSettings(true);
  }

  componentWillUnmount() {
    this.unlisteners.forEach((clb) => {clb();});
  }

  setDefaultExportSettings() {
    exportsStore.setExportType(DEFAULT_EXPORT_SETTINGS.EXPORT_TYPE);
    this.setState({
      selectedExportFormat: this.getContextualDefaultExportFormat(),
      groupSeparator: DEFAULT_EXPORT_SETTINGS.GROUP_SEPARATOR,
      selectedExportMultiple: DEFAULT_EXPORT_SETTINGS.EXPORT_MULTIPLE,
      isIncludeGroupsEnabled: DEFAULT_EXPORT_SETTINGS.INCLUDE_GROUPS,
      isIncludeAllVersionsEnabled: DEFAULT_EXPORT_SETTINGS.INCLUDE_ALL_VERSIONS,
      isSaveCustomExportEnabled: DEFAULT_EXPORT_SETTINGS.SAVE_CUSTOM_EXPORT,
      customExportName: DEFAULT_EXPORT_SETTINGS.CUSTOM_EXPORT_NAME,
      isCustomSelectionEnabled: DEFAULT_EXPORT_SETTINGS.CUSTOM_SELECTION,
      isFlattenGeoJsonEnabled: DEFAULT_EXPORT_SETTINGS.FLATTEN_GEO_JSON,
      selectedRows: new Set(this.getAllSelectableRows()),
    });
  }

  onExportsStoreChange() {
    const newExportType = exportsStore.getExportType();
    if (newExportType.value !== this.state.selectedExportType.value) {
      const newStateObj = {
        selectedExportType: newExportType,
        // when export type changes, make sure the custom export is cleared
        // to avoid users saving unwanted changes (the custom export name is not
        // visible unless Advanced View is toggled)
        isSaveCustomExportEnabled: false,
        customExportName: '',
      };

      this.setState(newStateObj);
    }
  }

  onGetExportSettingsCompleted(response, passData) {
    // we need to prepare the results to be displayed in Select
    const definedExports = [];
    response.results.forEach((result, index) => {
      definedExports.push({
        value: index,
        label: result.name ? result.name : NAMELESS_EXPORT_NAME,
        data: result,
      });
    });

    this.setState({
      isUpdatingDefinedExportsList: false,
      definedExports: definedExports,
    });

    // load last saved settings
    if (response.count >= 1 && passData?.preselectLastSettings) {
      this.applyExportSettingToState(response.results[0]);
    }

    if (!this.state.isComponentReady) {
      this.setState({isComponentReady: true});
    }
  }

  onDeleteExportSettingCompleted() {
    this.clearSelectedDefinedExport();
    this.fetchExportSettings();
  }

  getExportFormatOptions() {
    const options = [];

    // Step 1: add all defined languages as options (both named and unnamed)
    if (this.props.asset.summary?.languages.length >= 1) {
      this.props.asset.summary.languages.forEach((language, index) => {
        // unnamed language gives the `_default` option
        if (language === null) {
          options.push(EXPORT_FORMATS._default);
        } else {
          options.push({
            value: language,
            label: language,
            langIndex: index, // needed for later
          });
        }
      });
    }

    // Step 2: if for some reason nothing was added yet, add `_default`
    if (options.length === 0) {
      options.push(EXPORT_FORMATS._default);
    }

    // Step 3: `_xml` is always available and always last
    options.push(EXPORT_FORMATS._xml);

    return options;
  }

  /**
   * @returns one of export format options, either the asset's default language
   * or `_default` (or more precisely: the first option)
   */
  getContextualDefaultExportFormat() {
    const exportFormatOptions = this.getExportFormatOptions();
    const defaultAssetLanguage = this.props.asset.summary?.default_translation;
    const defaultAssetLanguageOption = exportFormatOptions.find((option) => {
      return defaultAssetLanguage === option.value;
    });
    return defaultAssetLanguageOption || exportFormatOptions[0];
  }

  getAllSelectableRows() {
    const allRows = new Set();
    if (this.props.asset?.content?.survey) {
      const flatPaths = assetUtils.getSurveyFlatPaths(
        this.props.asset.content.survey,
        false,
        true
      );
      Object.values(flatPaths).forEach((path) => {allRows.add(path);});
      Object.keys(ADDITIONAL_SUBMISSION_PROPS).forEach((submissionProp) => {
        allRows.add(submissionProp);
      });
{'fraction_non_alphanumeric': 0.0945479641131815, 'fraction_numerical': 0.000966183574879227, 'mean_word_length': 3.7999337528983106, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1371318', 'n_tokens_mistral': 4189, 'n_tokens_neox': 4103, 'n_words': 832}