<commit_before>{% extends "userreports/base_report_builder.html" %}
{% load i18n %}
{% load hq_shared_tags %}

{% block js %}{{ block.super }}
    <script src="{% new_static 'style/js/bootstrap3/main.js' %}"></script>
    <script src="{% new_static 'app_manager/js/case-knockout-bindings.js' %}"></script>
{% endblock %}

{% block page_title %}{% trans "Step 2 of 2 - Configure Report Settings" %}{% endblock page_title %}

{% block js-inline %}{{ block.super }}
    <script type="text/javascript">
        /**
         * Knockout view model representing a row in the filter property list
         * @constructor
         */
        var PropertyListItem = function() {
            var that = this;
            this.existsInCurrentVersion = ko.observable(true);
            this.property = ko.observable("");
            this.displayText = ko.observable("");
            this.userModifiedDisplayText = false;
            this.format = ko.observable("");
            this.calculation = ko.observable("Count per Choice");
            this.dataSourceField = ko.observable("");

            /**
             * Return a "plain" javascript object representing this view model
             * suitable for sending to the server.
             */
            this.toJS = function(){
                return {
                    property: this.property(),
                    display_text: this.displayText(),
                    format: this.format(),
                    calculation: this.calculation()
                };
            };

            this.isNotBlank = ko.computed(function(){
               return !!that.property();
            });

            this.isValid= ko.computed(function(){
                return that.isNotBlank() && that.existsInCurrentVersion();
            });

            this.property.subscribe(function(newValue) {
                if (! that.userModifiedDisplayText){
                    that.displayText(newValue);
                }
            });
        };
        PropertyListItem.wrap = function(o){
            var i = new PropertyListItem();
            i.existsInCurrentVersion(o.exists_in_current_version);
            i.property(o.property !== undefined ? o.property : "");
            i.dataSourceField(o.data_source_field !== undefined ? o.data_source_field : null);
            i.displayText(o.display_text);
            i.userModifiedDisplayText = true;
            i.calculation(o.calculation);
            i.format(o.format);
            return i;
        };

        /**
         * Knockout view model controlling the filter property list.
         */
        var propertyList = function(options) {
            var that = this;
            options = options || {};

            this.buttonText = options.buttonText !== undefined ? options.buttonText : "Add property";
            this.requireColumns = options.requireColumns !== undefined ? options.requireColumns : false;
            this.requireColumnsText = options.requireColumnsText !== undefined ? options.requireColumnsText: "Please select at least one property";
            this.propertyHelpText = options.propertyHelpText !== undefined ? options.propertyHelpText : null;
            this.displayHelpText = options.displayHelpText !== undefined ? options.displayHelpText : null;
            this.formatHelpText = options.formatHelpText !== undefined ? options.formatHelpText : null;
            this.calcHelpText = options.calcHelpText !== undefined ? options.calcHelpText : null;
            this.analyticsAction = options.analyticsAction !== undefined ? options.analyticsAction : null;
            this.analyticsLabel = options.analyticsLabel !== undefined ? options.analyticsLabel : '{{ report_type }}';

            this.hasFormatCol = ko.observable(options.hasFormatCol !== undefined ? options.hasFormatCol : true);
            this.hasCalculationCol = ko.observable(options.hasCalculationCol !== undefined ? options.hasCalculationCol : false);
            this.formatOptions = ko.observableArray(["Choice", "Date"]);
            this.calculationOptions = ko.observableArray(["Count per Choice", "Sum", "Average"]);
            this.propertyOptions = ko.observableArray({{ property_options|JSON }});
            var rawOptions = {{ property_options|JSON }};
            this.optionsContainQuestions = _.any(rawOptions, function (o) {
                return o.type == 'question';
            });
            if (!this.optionsContainQuestions){
                this.propertyOptions = ko.observableArray(rawOptions);
            } else {
                // Munge the property_options into the form expected by the questionsSelect binding.
                this.propertyOptions = ko.observableArray(
                    _.compact(_.map(that.propertyOptions(), function (o) {
                        if (o.type === 'question') {
                            return o.source;
                        } else if (o.type == 'meta') {
                            return {
                                value: o.source[0],
                                label: o.text
                            }
                        }
                    }))
                )
            }


            this.columns = ko.observableArray(options.initialCols !== undefined ? options.initialCols : []);
            this.serializedProperties = ko.computed(function(){
                return JSON.stringify(
                    _.map(that.columns(), function(c){return c.toJS()})
                );
            });
            this.showWarnings = ko.observable(false);
            this.validate = function(){
                that.showWarnings(true);
                var columnsValid = !_.contains(_.map(that.columns(), function(c){return c.isValid()}), false);
                var columnLengthValid = !(that.requireColumns && !that.columns().length);
                return columnsValid && columnLengthValid;
            };
            this.buttonHandler = function(){
                this.columns.push(new PropertyListItem());
                if (!_.isEmpty(this.analyticsAction) && !_.isEmpty(this.analyticsLabel)){
                    window.analytics.usage("Report Builder", this.analyticsAction, this.analyticsLabel);
                    window.analytics.workflow("Clicked " + this.analyticsAction + " in Report Builder");
                }
            }
        };

        var configForm = function(){
            var that = this;
            var initialFilters = _.map({{ initial_filters|JSON }}, function(i){
                return PropertyListItem.wrap(i);
            });
            var initialColumns = _.map({{ initial_columns|JSON }}, function(i){
                return PropertyListItem.wrap(i);
            });
            // TODO: Pass help texts from template so that they can be translated.
            this.filtersList = new propertyList({
                hasFormatCol: {% if form.source_type == "case" %}true{% else %}false{% endif %},
                hasCalculationCol: false,
                initialCols: initialFilters,
                buttonText: 'Add Filter',
                analyticsAction: 'Add Filter',
                propertyHelpText: '{{ filter_property_help_text|escapejs}}',
                displayHelpText: '{{ filter_display_help_text|escapejs }}',
                formatHelpText: '{{ filter_format_help_text|escapejs }}'
            });
            this.columnsList = new propertyList({
                hasFormatCol: false,
                hasCalculationCol: {% if report_type == "table" or report_type == "worker" %}true{% else %}false{% endif %},
                initialCols: initialColumns,
                buttonText: 'Add Column',
                analyticsAction: 'Add Column',
                calcHelpText: '{{ calculation_help_text|escapejs }}',
                requireColumns: true,
                requireColumnsText: "At least one column is required"
            });
            this.submitHandler = function(formElement){
                var isValid = true;
                isValid = that.filtersList.validate() && isValid;
                isValid = that.columnsList.validate() && isValid;
                if (!isValid){
                    alert('Invalid report configuration. Please fix the issues and try again.');
                    // The event handler that disables the button is triggered
                    // after this handler is invoked. Therefore, we use _.defer()
                    // to re-enable it immediately after the call stack clears.
                    _.defer(function(el){
                        $(el).find('.disable-on-submit').enableButton();
                    }, formElement);
                }
                return isValid;
            }
        };
        var formViewModel = new configForm();
        $("#report-config-form").koApplyBindings(formViewModel);

        $(function () {
            {% for event in report_builder_events %}
                {# Track a usage events that were produced on the server #}
                window.analytics.usage.apply(this, {{event|JSON}});
            {% endfor %}
            gaTrackLink(
                "#delete-report-button", "Report Builder", "Delete a Repot", '{{ report_type }}'
            );
        });

    </script>
{% endblock %}<commit_msg>Track 'submit form with no columns' event<commit_after>{% extends "userreports/base_report_builder.html" %}
{% load i18n %}
{% load hq_shared_tags %}

{% block js %}{{ block.super }}
    <script src="{% new_static 'style/js/bootstrap3/main.js' %}"></script>
    <script src="{% new_static 'app_manager/js/case-knockout-bindings.js' %}"></script>
{% endblock %}

{% block page_title %}{% trans "Step 2 of 2 - Configure Report Settings" %}{% endblock page_title %}

{% block js-inline %}{{ block.super }}
    <script type="text/javascript">
        /**
         * Knockout view model representing a row in the filter property list
         * @constructor
         */
        var PropertyListItem = function() {
            var that = this;
            this.existsInCurrentVersion = ko.observable(true);
            this.property = ko.observable("");
            this.displayText = ko.observable("");
            this.userModifiedDisplayText = false;
            this.format = ko.observable("");
            this.calculation = ko.observable("Count per Choice");
            this.dataSourceField = ko.observable("");

            /**
             * Return a "plain" javascript object representing this view model
             * suitable for sending to the server.
             */
            this.toJS = function(){
                return {
                    property: this.property(),
                    display_text: this.displayText(),
                    format: this.format(),
                    calculation: this.calculation()
                };
            };

            this.isNotBlank = ko.computed(function(){
               return !!that.property();
            });

            this.isValid= ko.computed(function(){
                return that.isNotBlank() && that.existsInCurrentVersion();
            });

            this.property.subscribe(function(newValue) {
                if (! that.userModifiedDisplayText){
                    that.displayText(newValue);
                }
            });
        };
        PropertyListItem.wrap = function(o){
            var i = new PropertyListItem();
            i.existsInCurrentVersion(o.exists_in_current_version);
            i.property(o.property !== undefined ? o.property : "");
            i.dataSourceField(o.data_source_field !== undefined ? o.data_source_field : null);
            i.displayText(o.display_text);
            i.userModifiedDisplayText = true;
            i.calculation(o.calculation);
            i.format(o.format);
            return i;
        };

        /**
         * Knockout view model controlling the filter property list.
         */
        var propertyList = function(options) {
            var that = this;
            options = options || {};

            this.buttonText = options.buttonText !== undefined ? options.buttonText : "Add property";
            // True if at least one column is required.
            this.requireColumns = options.requireColumns !== undefined ? options.requireColumns : false;
            this.requireColumnsText = options.requireColumnsText !== undefined ? options.requireColumnsText: "Please select at least one property";
            // This function will be called if a user tries to submit the form with no columns.
            this.noColumnsValidationCallback = options.noColumnsValidationCallback !== undefined ? options.noColumnsValidationCallback : null;
            this.propertyHelpText = options.propertyHelpText !== undefined ? options.propertyHelpText : null;
            this.displayHelpText = options.displayHelpText !== undefined ? options.displayHelpText : null;
            this.formatHelpText = options.formatHelpText !== undefined ? options.formatHelpText : null;
            this.calcHelpText = options.calcHelpText !== undefined ? options.calcHelpText : null;
            this.analyticsAction = options.analyticsAction !== undefined ? options.analyticsAction : null;
            this.analyticsLabel = options.analyticsLabel !== undefined ? options.analyticsLabel : '{{ report_type }}';

            this.hasFormatCol = ko.observable(options.hasFormatCol !== undefined ? options.hasFormatCol : true);
            this.hasCalculationCol = ko.observable(options.hasCalculationCol !== undefined ? options.hasCalculationCol : false);
            this.formatOptions = ko.observableArray(["Choice", "Date"]);
            this.calculationOptions = ko.observableArray(["Count per Choice", "Sum", "Average"]);
            this.propertyOptions = ko.observableArray({{ property_options|JSON }});
            var rawOptions = {{ property_options|JSON }};
            this.optionsContainQuestions = _.any(rawOptions, function (o) {
                return o.type == 'question';
            });
            if (!this.optionsContainQuestions){
                this.propertyOptions = ko.observableArray(rawOptions);
            } else {
                // Munge the property_options into the form expected by the questionsSelect binding.
                this.propertyOptions = ko.observableArray(
                    _.compact(_.map(that.propertyOptions(), function (o) {
                        if (o.type === 'question') {
                            return o.source;
                        } else if (o.type == 'meta') {
                            return {
                                value: o.source[0],
                                label: o.text
                            }
                        }
                    }))
                )
            }


            this.columns = ko.observableArray(options.initialCols !== undefined ? options.initialCols : []);
            this.serializedProperties = ko.computed(function(){
                return JSON.stringify(
                    _.map(that.columns(), function(c){return c.toJS()})
                );
            });
            this.showWarnings = ko.observable(false);
            this.validate = function(){
                that.showWarnings(true);
                var columnsValid = !_.contains(_.map(that.columns(), function(c){return c.isValid()}), false);
                var columnLengthValid = !(that.requireColumns && !that.columns().length);
                if (that.noColumnsValidationCallback && !columnLengthValid){
                    that.noColumnsValidationCallback();
                }
                return columnsValid && columnLengthValid;
            };
            this.buttonHandler = function(){
                this.columns.push(new PropertyListItem());
                if (!_.isEmpty(this.analyticsAction) && !_.isEmpty(this.analyticsLabel)){
                    window.analytics.usage("Report Builder", this.analyticsAction, this.analyticsLabel);
                    window.analytics.workflow("Clicked " + this.analyticsAction + " in Report Builder");
                }
            }
        };

        var configForm = function(){
            var that = this;
            var initialFilters = _.map({{ initial_filters|JSON }}, function(i){
                return PropertyListItem.wrap(i);
            });
            var initialColumns = _.map({{ initial_columns|JSON }}, function(i){
                return PropertyListItem.wrap(i);
            });
            // TODO: Pass help texts from template so that they can be translated.
            this.filtersList = new propertyList({
                hasFormatCol: {% if form.source_type == "case" %}true{% else %}false{% endif %},
                hasCalculationCol: false,
                initialCols: initialFilters,
                buttonText: 'Add Filter',
                analyticsAction: 'Add Filter',
                propertyHelpText: '{{ filter_property_help_text|escapejs}}',
                displayHelpText: '{{ filter_display_help_text|escapejs }}',
                formatHelpText: '{{ filter_format_help_text|escapejs }}'
            });
            this.columnsList = new propertyList({
                hasFormatCol: false,
                hasCalculationCol: {% if report_type == "table" or report_type == "worker" %}true{% else %}false{% endif %},
                initialCols: initialColumns,
                buttonText: 'Add Column',
                analyticsAction: 'Add Column',
                calcHelpText: '{{ calculation_help_text|escapejs }}',
                requireColumns: {% if report_type != "chart"%}true{% else %}false{% endif %},
                requireColumnsText: "At least one column is required",
                noColumnsValidationCallback: function(){
                    window.analytics.usage(
                        'Report Builder',
                        'Click On Done (No Columns)',
                        '{{ report_type }}'
                    );
                }
            });
            this.submitHandler = function(formElement){
                var isValid = true;
                isValid = that.filtersList.validate() && isValid;
                isValid = that.columnsList.validate() && isValid;
                if (!isValid){
                    alert('Invalid report configuration. Please fix the issues and try again.');
                    // The event handler that disables the button is triggered
                    // after this handler is invoked. Therefore, we use _.defer()
                    // to re-enable it immediately after the call stack clears.
                    _.defer(function(el){
                        $(el).find('.disable-on-submit').enableButton();
                    }, formElement);
                }
                return isValid;
            };
        };
        var formViewModel = new configForm();
        $("#report-config-form").koApplyBindings(formViewModel);

        $(function () {
            {% for event in report_builder_events %}
                {# Track a usage events that were produced on the server #}
                window.analytics.usage.apply(this, {{event|JSON}});
            {% endfor %}
            gaTrackLink(
                "#delete-report-button", "Report Builder", "Delete a Repot", '{{ report_type }}'
            );
        });

    </script>
{% endblock %}
{'fraction_non_alphanumeric': 0.113511837072263, 'fraction_numerical': 0.0006202832626899617, 'mean_word_length': 1.7757532281205164, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, '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': '1699482', 'n_tokens_mistral': 4783, 'n_tokens_neox': 4527, 'n_words': 1041}