<commit_before>import React, { Component, PropTypes } from "react";

import AccordianList from "metabase/components/AccordianList.jsx";
import FieldList from './FieldList.jsx';
import QueryDefinitionTooltip from "./QueryDefinitionTooltip.jsx";

import Icon from "metabase/components/Icon.jsx";
import Tooltip from "metabase/components/Tooltip.jsx";
import Button from "metabase/components/Button.jsx";

import Query from "metabase/lib/query";
import { AggregationClause } from "metabase/lib/query";

import _ from "underscore";

import ExpressionEditorTextfield from "./expressions/ExpressionEditorTextfield.jsx"

export default class AggregationPopover extends Component {
    constructor(props, context) {
        super(props, context);

        this.state = {
            aggregation: (props.isNew ? [] : props.aggregation),
            choosingField: (props.aggregation && props.aggregation.length > 1 && AggregationClause.isStandard(props.aggregation)),
            editingAggregation: (props.aggregation && props.aggregation.length > 1 && AggregationClause.isCustom(props.aggregation))
        };

        _.bindAll(this, "commitAggregation", "onPickAggregation", "onPickField", "onClearAggregation");
    }

    static propTypes = {
        isNew: PropTypes.bool,
        aggregation: PropTypes.array,
        availableAggregations: PropTypes.array.isRequired,
        onCommitAggregation: PropTypes.func.isRequired,
        onClose: PropTypes.func.isRequired,
        tableMetadata: PropTypes.object.isRequired,
        customFields: PropTypes.object
    };


    commitAggregation(aggregation) {
        this.props.onCommitAggregation(aggregation);
        this.props.onClose();
    }

    onPickAggregation(agg) {
        // check if this aggregation requires a field, if so then force user to pick that now, otherwise we are done
        if (agg.custom) {
            this.setState({
                aggregation: agg.value,
                editingAggregation: true
            });
        } else if (agg.aggregation && agg.aggregation.requiresField) {
            this.setState({
                aggregation: agg.value,
                choosingField: true
            });
        } else {
            // this includse picking a METRIC or picking an aggregation which doesn't require a field
            this.commitAggregation(agg.value);
        }
    }

    onPickField(fieldId) {
        this.commitAggregation(AggregationClause.setField(this.state.aggregation, fieldId));
    }

    onClearAggregation() {
        this.setState({
            choosingField: false,
            editingAggregation: false
        });
    }

    getAggregationFieldOptions(aggOperator) {
        // NOTE: we don't use getAggregator() here because availableAggregations has the table.fields populated on the aggregation options
        const aggOptions = this.props.availableAggregations.filter((o) => o.short === aggOperator);
        if (aggOptions && aggOptions.length > 0) {
            return Query.getFieldOptions(this.props.tableMetadata.fields, true, aggOptions[0].validFieldsFilters[0])
        }
    }

    itemIsSelected(item) {
        const { aggregation } = this.props;
        return item.isSelected(aggregation);
    }

    renderItemExtra(item, itemIndex) {
        if (item.aggregation && item.aggregation.description) {
            return (
                <div className="p1">
                    <Tooltip tooltip={item.aggregation.description}>
                        <span className="QuestionTooltipTarget" />
                    </Tooltip>
                </div>
            );
        } else if (item.metric) {
            return this.renderMetricTooltip(item.metric);
        }
    }

    renderMetricTooltip(metric) {
        let { tableMetadata } = this.props;
        return (
            <div className="p1">
                <Tooltip tooltip={<QueryDefinitionTooltip type="metric" object={metric} tableMetadata={tableMetadata} />}>
                    <span className="QuestionTooltipTarget" />
                </Tooltip>
            </div>
        );
    }

    render() {
        const { availableAggregations, tableMetadata, isNew } = this.props;
        const { aggregation, choosingField, editingAggregation } = this.state;

        let selectedAggregation;
        if (AggregationClause.isMetric(aggregation)) {
            selectedAggregation = _.findWhere(tableMetadata.metrics, { id: AggregationClause.getMetric(aggregation) });
        } else if (AggregationClause.getOperator(aggregation)) {
            selectedAggregation = _.findWhere(availableAggregations, { short: AggregationClause.getOperator(aggregation) });
        }

        let sections = [];

        if (availableAggregations.length > 0) {
            sections.push({
                name: "Metabasics",
                items: availableAggregations.map(aggregation => ({
                    name: aggregation.name,
                    value: [aggregation.short].concat(aggregation.fields.map(field => null)),
                    isSelected: (agg) => !AggregationClause.isCustom(agg) && AggregationClause.getAggregation(agg) === aggregation.short,
                    aggregation: aggregation
                })),
                icon: "table2"
            });
        }

        // we only want to consider active metrics, with the ONE exception that if the currently selected aggregation is a
        // retired metric then we include it in the list to maintain continuity
        let metrics = tableMetadata.metrics && tableMetadata.metrics.filter((mtrc) => mtrc.is_active === true || (selectedAggregation && selectedAggregation.id === mtrc.id));
        if (metrics && metrics.length > 0) {
            sections.push({
                name: "Common Metrics",
                items: metrics.map(metric => ({
                    name: metric.name,
                    value: ["METRIC", metric.id],
                    isSelected: (aggregation) => AggregationClause.getMetric(aggregation) === metric.id,
                    metric: metric
                })),
                icon: "star-outline"
            });
        }

        sections[0].items.push({
            name: "Custom Aggregation",
            value: aggregation,
            custom: true,
            isSelected: (aggregation) => AggregationClause.isCustom(aggregation)<commit_msg>Don't try to convert 'rows' to custom expression<commit_after>import React, { Component, PropTypes } from "react";

import AccordianList from "metabase/components/AccordianList.jsx";
import FieldList from './FieldList.jsx';
import QueryDefinitionTooltip from "./QueryDefinitionTooltip.jsx";

import Icon from "metabase/components/Icon.jsx";
import Tooltip from "metabase/components/Tooltip.jsx";
import Button from "metabase/components/Button.jsx";

import Query from "metabase/lib/query";
import { AggregationClause } from "metabase/lib/query";

import _ from "underscore";

import ExpressionEditorTextfield from "./expressions/ExpressionEditorTextfield.jsx"

export default class AggregationPopover extends Component {
    constructor(props, context) {
        super(props, context);

        this.state = {
            aggregation: (props.isNew ? [] : props.aggregation),
            choosingField: (props.aggregation && props.aggregation.length > 1 && AggregationClause.isStandard(props.aggregation)),
            editingAggregation: (props.aggregation && props.aggregation.length > 1 && AggregationClause.isCustom(props.aggregation))
        };

        _.bindAll(this, "commitAggregation", "onPickAggregation", "onPickField", "onClearAggregation");
    }

    static propTypes = {
        isNew: PropTypes.bool,
        aggregation: PropTypes.array,
        availableAggregations: PropTypes.array.isRequired,
        onCommitAggregation: PropTypes.func.isRequired,
        onClose: PropTypes.func.isRequired,
        tableMetadata: PropTypes.object.isRequired,
        customFields: PropTypes.object
    };


    commitAggregation(aggregation) {
        this.props.onCommitAggregation(aggregation);
        this.props.onClose();
    }

    onPickAggregation(agg) {
        // check if this aggregation requires a field, if so then force user to pick that now, otherwise we are done
        if (agg.custom) {
            this.setState({
                aggregation: agg.value,
                editingAggregation: true
            });
        } else if (agg.aggregation && agg.aggregation.requiresField) {
            this.setState({
                aggregation: agg.value,
                choosingField: true
            });
        } else {
            // this includse picking a METRIC or picking an aggregation which doesn't require a field
            this.commitAggregation(agg.value);
        }
    }

    onPickField(fieldId) {
        this.commitAggregation(AggregationClause.setField(this.state.aggregation, fieldId));
    }

    onClearAggregation() {
        this.setState({
            choosingField: false,
            editingAggregation: false
        });
    }

    getAggregationFieldOptions(aggOperator) {
        // NOTE: we don't use getAggregator() here because availableAggregations has the table.fields populated on the aggregation options
        const aggOptions = this.props.availableAggregations.filter((o) => o.short === aggOperator);
        if (aggOptions && aggOptions.length > 0) {
            return Query.getFieldOptions(this.props.tableMetadata.fields, true, aggOptions[0].validFieldsFilters[0])
        }
    }

    itemIsSelected(item) {
        const { aggregation } = this.props;
        return item.isSelected(aggregation);
    }

    renderItemExtra(item, itemIndex) {
        if (item.aggregation && item.aggregation.description) {
            return (
                <div className="p1">
                    <Tooltip tooltip={item.aggregation.description}>
                        <span className="QuestionTooltipTarget" />
                    </Tooltip>
                </div>
            );
        } else if (item.metric) {
            return this.renderMetricTooltip(item.metric);
        }
    }

    renderMetricTooltip(metric) {
        let { tableMetadata } = this.props;
        return (
            <div className="p1">
                <Tooltip tooltip={<QueryDefinitionTooltip type="metric" object={metric} tableMetadata={tableMetadata} />}>
                    <span className="QuestionTooltipTarget" />
                </Tooltip>
            </div>
        );
    }

    render() {
        const { availableAggregations, tableMetadata, isNew } = this.props;
        const { aggregation, choosingField, editingAggregation } = this.state;

        let selectedAggregation;
        if (AggregationClause.isMetric(aggregation)) {
            selectedAggregation = _.findWhere(tableMetadata.metrics, { id: AggregationClause.getMetric(aggregation) });
        } else if (AggregationClause.getOperator(aggregation)) {
            selectedAggregation = _.findWhere(availableAggregations, { short: AggregationClause.getOperator(aggregation) });
        }

        let sections = [];

        if (availableAggregations.length > 0) {
            sections.push({
                name: "Metabasics",
                items: availableAggregations.map(aggregation => ({
                    name: aggregation.name,
                    value: [aggregation.short].concat(aggregation.fields.map(field => null)),
                    isSelected: (agg) => !AggregationClause.isCustom(agg) && AggregationClause.getAggregation(agg) === aggregation.short,
                    aggregation: aggregation
                })),
                icon: "table2"
            });
        }

        // we only want to consider active metrics, with the ONE exception that if the currently selected aggregation is a
        // retired metric then we include it in the list to maintain continuity
        let metrics = tableMetadata.metrics && tableMetadata.metrics.filter((mtrc) => mtrc.is_active === true || (selectedAggregation && selectedAggregation.id === mtrc.id));
        if (metrics && metrics.length > 0) {
            sections.push({
                name: "Common Metrics",
                items: metrics.map(metric => ({
                    name: metric.name,
                    value: ["METRIC", metric.id],
                    isSelected: (aggregation) => AggregationClause.getMetric(aggregation) === metric.id,
                    metric: metric
                })),
                icon: "star-outline"
            });
        }

        sections[0].items.push({
            name: "Custom Aggregation",
            value: aggregation !== "rows" && !_.isEqual(aggregation, ["rows"]) ? aggregation : null,
            custom: true,
            isSelected: (aggregation) => AggregationClause.isCustom(aggregation)
{'fraction_non_alphanumeric': 0.10451844903064415, 'fraction_numerical': 0.0017198248905565979, 'mean_word_length': 2.285310734463277, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 51, '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': '2144325', 'n_tokens_mistral': 3325, 'n_tokens_neox': 3249, 'n_words': 711}