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

import Icon from "metabase/components/Icon.jsx";
import LoadingSpinner from 'metabase/components/LoadingSpinner.jsx';
import QueryVisualizationTable from './QueryVisualizationTable.jsx';
import QueryVisualizationChart from './QueryVisualizationChart.jsx';
import QueryVisualizationObjectDetailTable from './QueryVisualizationObjectDetailTable.jsx';
import RunButton from './RunButton.jsx';
import VisualizationSettings from './VisualizationSettings.jsx';

import Query from "metabase/lib/query";

import cx from "classnames";

export default class QueryVisualization extends Component {
    constructor(props, context) {
        super(props, context);
        this.runQuery = this.runQuery.bind(this);

        this.state = {
            origQuery: JSON.stringify(props.card.dataset_query)
        };
    }

    static propTypes = {
        visualizationSettingsApi: PropTypes.object.isRequired,
        card: PropTypes.object.isRequired,
        result: PropTypes.object,
        downloadLink: PropTypes.string,
        setDisplayFn: PropTypes.func.isRequired,
        setChartColorFn: PropTypes.func.isRequired,
        setSortFn: PropTypes.func.isRequired,
        cellIsClickableFn: PropTypes.func,
        cellClickedFn: PropTypes.func,
        isRunning: PropTypes.bool.isRequired,
        runQueryFn: PropTypes.func.isRequired
    };

    static defaultProps = {
        // NOTE: this should be more dynamic from the backend, it's set based on the query lang
        maxTableRows: 2000
    };

    componentWillReceiveProps(nextProps) {
        // whenever we are told that we are running a query lets update our understanding of the "current" query
        if (nextProps.isRunning) {
            this.setState({
                origQuery: JSON.stringify(nextProps.card.dataset_query)
            });
        }
    }

    queryIsDirty() {
        // a query is considered dirty if ANY part of it has been changed
        return (JSON.stringify(this.props.card.dataset_query) !== this.state.origQuery);
    }

    isChartDisplay(display) {
        return (display !== "table" && display !== "scalar");
    }

    runQuery() {
        this.props.runQueryFn();
    }

    canRun() {
        var query = this.props.card.dataset_query;
        if (query.query) {
            return Query.canRun(query.query);
        } else {
            return (query.database != undefined && query.native.query !== "");
        }
    }

    renderHeader() {
        var visualizationSettings = false;
        if (!this.props.isObjectDetail) {
            visualizationSettings = (<VisualizationSettings {...this.props}/>);
        }

        return (
            <div className="relative flex full mt3 mb1">
                <span className="relative z3">{visualizationSettings}</span>
                <div className="absolute flex layout-centered left right z2">
                    <RunButton
                        canRun={this.canRun()}
                        isDirty={this.queryIsDirty()}
                        isRunning={this.props.isRunning}
                        runFn={this.runQuery}
                    />
                </div>
                <div className="absolute right z3 flex align-center">
                    {!this.queryIsDirty() && this.renderCount()}
                    {this.renderDownloadButton()}
                </div>
            </div>
        );
    }

    hasTooManyRows() {
        const dataset_query = this.props.card.dataset_query,
              rows = this.props.result.data.rows;

        if (this.props.result.data.rows_truncated ||
            (dataset_query.type === "query" &&
             dataset_query.query.aggregation[0] === "rows" &&
             rows.length === 2000))
        {
            return true;
        } else {
            return false;
        }
    }

    renderCount() {
        if (this.props.result && !this.props.isObjectDetail && this.props.card.display === "table") {
            return (
                <div>
                    { this.hasTooManyRows() ? ("Showing max of ") : ("Showing ")}
                    <b>{this.props.result.row_count}</b>
                    { (this.props.result.data.rows.length !== 1) ? (" rows") : (" row")}.
                </div>
            );
        }
    }

    renderDownloadButton() {
        // NOTE: we expect our component provider set this to something falsey if download not available
        if (this.props.downloadLink) {
            if (window.OSX) {
                const downloadLink = this.props.downloadLink;
                return (
                    <a classname="mx1" href="#" title="Download this data" onClick={function() {
                        window.OSX.saveCSV(downloadLink);
                    }}>
                        <Icon name='download' width="16px" height="16px" />
                    </a>
                );
            } else {
                return (
                    <a className="mx1" href={this.props.downloadLink} title="Download this data" target="_blank">
                        <Icon name='download' width="16px" height="16px" />
                    </a>
                );
            }
        }
    }

    render() {
        var loading,
            viz;

        if(this.props.isRunning) {
            loading = (
                <div className="Loading absolute top left bottom right flex flex-column layout-centered text-brand">
                    <LoadingSpinner />
                    <h2 className="Loading-message text-brand text-uppercase mt3">Doing science...</h2>
                </div>
            );
        }

        if (!this.props.result) {
            viz = (
                <div className="flex full layout-centered text-grey-1">
                    <h1>If you give me some data I can show you something cool.  Run a Query!</h1>
                </div>
            );
        } else {
            if (this.props.result.error) {
                if (this.props.card.dataset_query && this.props.card.dataset_query.type === 'native') {
                    viz = (
                        <div className="QueryError flex full align-center text-error">
                            <div className="QueryError-iconWrapper">
                                <svg className="QueryError-icon" viewBox="0 0 32 32" width="64" height="64" fill="currentcolor">
                                    <path d="M4 8 L8 4 L16 12 L24 4 L28 8 L20 16 L28 24 L24 28 L16 20 L8 28 L4 24 L12 16 z "></path>
                                </svg>
                            </div>
                            <span className="QueryError-message">{this.props.result.error}</span>
                        </div><commit_msg>asking for data is presumptuous.... spurious promises of peer approval not withstanding<commit_after>import React, { Component, PropTypes } from "react";

import Icon from "metabase/components/Icon.jsx";
import LoadingSpinner from 'metabase/components/LoadingSpinner.jsx';
import QueryVisualizationTable from './QueryVisualizationTable.jsx';
import QueryVisualizationChart from './QueryVisualizationChart.jsx';
import QueryVisualizationObjectDetailTable from './QueryVisualizationObjectDetailTable.jsx';
import RunButton from './RunButton.jsx';
import VisualizationSettings from './VisualizationSettings.jsx';

import Query from "metabase/lib/query";

import cx from "classnames";

export default class QueryVisualization extends Component {
    constructor(props, context) {
        super(props, context);
        this.runQuery = this.runQuery.bind(this);

        this.state = {
            origQuery: JSON.stringify(props.card.dataset_query)
        };
    }

    static propTypes = {
        visualizationSettingsApi: PropTypes.object.isRequired,
        card: PropTypes.object.isRequired,
        result: PropTypes.object,
        downloadLink: PropTypes.string,
        setDisplayFn: PropTypes.func.isRequired,
        setChartColorFn: PropTypes.func.isRequired,
        setSortFn: PropTypes.func.isRequired,
        cellIsClickableFn: PropTypes.func,
        cellClickedFn: PropTypes.func,
        isRunning: PropTypes.bool.isRequired,
        runQueryFn: PropTypes.func.isRequired
    };

    static defaultProps = {
        // NOTE: this should be more dynamic from the backend, it's set based on the query lang
        maxTableRows: 2000
    };

    componentWillReceiveProps(nextProps) {
        // whenever we are told that we are running a query lets update our understanding of the "current" query
        if (nextProps.isRunning) {
            this.setState({
                origQuery: JSON.stringify(nextProps.card.dataset_query)
            });
        }
    }

    queryIsDirty() {
        // a query is considered dirty if ANY part of it has been changed
        return (JSON.stringify(this.props.card.dataset_query) !== this.state.origQuery);
    }

    isChartDisplay(display) {
        return (display !== "table" && display !== "scalar");
    }

    runQuery() {
        this.props.runQueryFn();
    }

    canRun() {
        var query = this.props.card.dataset_query;
        if (query.query) {
            return Query.canRun(query.query);
        } else {
            return (query.database != undefined && query.native.query !== "");
        }
    }

    renderHeader() {
        var visualizationSettings = false;
        if (!this.props.isObjectDetail) {
            visualizationSettings = (<VisualizationSettings {...this.props}/>);
        }

        return (
            <div className="relative flex full mt3 mb1">
                <span className="relative z3">{visualizationSettings}</span>
                <div className="absolute flex layout-centered left right z2">
                    <RunButton
                        canRun={this.canRun()}
                        isDirty={this.queryIsDirty()}
                        isRunning={this.props.isRunning}
                        runFn={this.runQuery}
                    />
                </div>
                <div className="absolute right z3 flex align-center">
                    {!this.queryIsDirty() && this.renderCount()}
                    {this.renderDownloadButton()}
                </div>
            </div>
        );
    }

    hasTooManyRows() {
        const dataset_query = this.props.card.dataset_query,
              rows = this.props.result.data.rows;

        if (this.props.result.data.rows_truncated ||
            (dataset_query.type === "query" &&
             dataset_query.query.aggregation[0] === "rows" &&
             rows.length === 2000))
        {
            return true;
        } else {
            return false;
        }
    }

    renderCount() {
        if (this.props.result && !this.props.isObjectDetail && this.props.card.display === "table") {
            return (
                <div>
                    { this.hasTooManyRows() ? ("Showing max of ") : ("Showing ")}
                    <b>{this.props.result.row_count}</b>
                    { (this.props.result.data.rows.length !== 1) ? (" rows") : (" row")}.
                </div>
            );
        }
    }

    renderDownloadButton() {
        // NOTE: we expect our component provider set this to something falsey if download not available
        if (this.props.downloadLink) {
            if (window.OSX) {
                const downloadLink = this.props.downloadLink;
                return (
                    <a classname="mx1" href="#" title="Download this data" onClick={function() {
                        window.OSX.saveCSV(downloadLink);
                    }}>
                        <Icon name='download' width="16px" height="16px" />
                    </a>
                );
            } else {
                return (
                    <a className="mx1" href={this.props.downloadLink} title="Download this data" target="_blank">
                        <Icon name='download' width="16px" height="16px" />
                    </a>
                );
            }
        }
    }

    render() {
        var loading,
            viz;

        if(this.props.isRunning) {
            loading = (
                <div className="Loading absolute top left bottom right flex flex-column layout-centered text-brand">
                    <LoadingSpinner />
                    <h2 className="Loading-message text-brand text-uppercase mt3">Doing science...</h2>
                </div>
            );
        }

        if (!this.props.result) {
            viz = (
                <div className="flex full layout-centered text-grey-1">
                    <h1>If you're stuck, you can always just pick a table and hit "Run Query" to get started.</h1>
                </div>
            );
        } else {
            if (this.props.result.error) {
                if (this.props.card.dataset_query && this.props.card.dataset_query.type === 'native') {
                    viz = (
                        <div className="QueryError flex full align-center text-error">
                            <div className="QueryError-iconWrapper">
                                <svg className="QueryError-icon" viewBox="0 0 32 32" width="64" height="64" fill="currentcolor">
                                    <path d="M4 8 L8 4 L16 12 L24 4 L28 8 L20 16 L28 24 L24 28 L16 20 L8 28 L4 24 L12 16 z "></path>
                                </svg>
                            </div>
                            <span className="QueryError-message">{this.props.result.error}</span>
                        </div>
{'fraction_non_alphanumeric': 0.1112012987012987, 'fraction_numerical': 0.011953955135773318, 'mean_word_length': 1.6642421859642225, 'pattern_counts': {'":': 0, '<': 81, '<?xml version=': 0, '>': 81, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '408486', 'n_tokens_mistral': 3659, 'n_tokens_neox': 3450, 'n_words': 814}