awsmug/torro-forms

View on GitHub
src/modules/evaluators/participation.php

Summary

Maintainability
C
1 day
Test Coverage
<?php
/**
 * Participation evaluator class
 *
 * @package TorroForms
 * @since 1.0.0
 */

namespace awsmug\Torro_Forms\Modules\Evaluators;

use awsmug\Torro_Forms\DB_Objects\Forms\Form;
use awsmug\Torro_Forms\DB_Objects\Submissions\Submission;
use awsmug\Torro_Forms\Modules\Assets_Submodule_Interface;
use WP_Error;

/**
 * Class for an evaluator that measures form participation over time.
 *
 * @since 1.0.0
 */
class Participation extends Evaluator implements Assets_Submodule_Interface {

    /**
     * Bootstraps the submodule by setting properties.
     *
     * @since 1.0.0
     */
    protected function bootstrap() {
        $this->slug        = 'participation';
        $this->title       = __( 'Participation', 'torro-forms' );
        $this->description = __( 'Evaluates participation for a form over time.', 'torro-forms' );
    }

    /**
     * Evaluates a specific form submission.
     *
     * This method is run whenever a submission is completed to update the aggregate calculations.
     * Aggregate calculations are stored so that forms with a very high number of submissions do
     * not need to be calculated live.
     *
     * @since 1.0.0
     *
     * @param array      $aggregate_results Aggregate results to update.
     * @param Submission $submission        Submission to evaluate.
     * @param Form       $form              Form the submission applies to.
     * @return array Updated aggregate evaluation results.
     */
    public function evaluate_single( $aggregate_results, $submission, $form ) {
        if ( ! isset( $aggregate_results['total'] ) ) {
            $aggregate_results['total'] = 1;
        } else {
            $aggregate_results['total']++;
        }

        $year  = (string) $submission->format_datetime( 'Y' );
        $month = (string) $submission->format_datetime( 'm' );

        if ( ! isset( $aggregate_results[ $year ] ) ) {
            $aggregate_results[ $year ] = array();
        }

        if ( ! isset( $aggregate_results[ $year ]['total'] ) ) {
            $aggregate_results[ $year ]['total'] = 1;
        } else {
            $aggregate_results[ $year ]['total']++;
        }

        if ( ! isset( $aggregate_results[ $year ][ $month ] ) ) {
            $aggregate_results[ $year ][ $month ] = 1;
        } else {
            $aggregate_results[ $year ][ $month ]++;
        }

        return $aggregate_results;
    }

    /**
     * Renders evaluation results for a specific form.
     *
     * @since 1.0.0
     *
     * @param array $results Results to show.
     * @param Form  $form    Form the results belong to.
     * @param array $args    {
     *     Optional. Additional arguments for displaying the results.
     *
     *     @type bool             $total Whether to show total results. Default true.
     *     @type int|string|array $year  One or more years to only display results for those. Otherwise
     *                                   results are displayed for all relevant years. Default none.
     * }
     */
    public function show_results( $results, $form, $args = array() ) {
        $args = wp_parse_args(
            $args,
            array(
                'total' => true,
                'year'  => 0,
            )
        );

        $args['total'] = rest_sanitize_boolean( $args['total'] );

        $total_count = 0;
        if ( isset( $results['total'] ) ) {
            $total_count = $results['total'];
            unset( $results['total'] );
        }

        if ( ! empty( $results ) ) {
            ksort( $results );
            $keys = array_keys( $results );

            $min_year = $keys[0];
            $max_year = $keys[ count( $keys ) - 1 ];

            $years = array_map( 'strval', range( (int) $min_year, (int) $max_year, 1 ) );
        } else {
            $years = array( (string) current_time( 'Y' ) );
        }

        $tabs = array();

        if ( $args['total'] ) {
            $tabs['total'] = array(
                'label'    => _x( 'Total', 'submission count', 'torro-forms' ),
                'callback' => function() use ( $results, $years, $total_count, $form ) {
                    $year_results = array();
                    foreach ( $years as $year ) {
                        if ( isset( $results[ $year ]['total'] ) ) {
                            $year_results[] = (int) $results[ $year ]['total'];
                        } else {
                            $year_results[] = 0;
                        }
                    }

                    ?>
                    <p>
                        <strong>
                            <?php esc_html_e( 'Number of completed submissions:', 'torro-forms' ); ?>
                            <?php echo absint( $total_count ); ?>
                        </strong>
                    </p>
                    <div id="<?php echo esc_attr( $this->slug . '-chart-total' ); ?>"></div>
                    <script type="application/json" class="c3-chart-data">
                        <?php
                        echo wp_json_encode( $this->get_chart_json( $form, esc_attr( $this->slug . '-chart-total' ), $years, $year_results, __( 'Years', 'torro-forms' ), __( 'Submission Count', 'torro-forms' ) ) );
                        ?>
                    </script>
                    <?php
                },
            );
        }

        if ( ! empty( $args['year'] ) ) {
            if ( is_int( $args['year'] ) ) {
                $years = array( $args['year'] );
            } else {
                $years = wp_parse_id_list( $args['year'] );
            }
        }

        foreach ( $years as $year ) {
            $tabs[ $year ] = array(
                'label'    => $year,
                'callback' => function() use ( $results, $year, $form ) {
                    global $wp_locale;

                    $total_count = isset( $results[ $year ]['total'] ) ? $results[ $year ]['total'] : 0;

                    $months = $wp_locale->month;

                    $month_results = array();
                    foreach ( $months as $index => $month ) {
                        if ( isset( $results[ $year ][ $index ] ) ) {
                            $month_results[] = (int) $results[ $year ][ $index ];
                        } else {
                            $month_results[] = 0;
                        }
                    }

                    ?>
                    <p>
                        <strong>
                            <?php
                            /* translators: %s: a year */
                            echo esc_html( sprintf( __( 'Number of completed submissions in %s:', 'torro-forms' ), $year ) );
                            ?>
                            <?php echo absint( $total_count ); ?>
                        </strong>
                    </p>
                    <div id="<?php echo esc_attr( $this->slug . '-chart-' . $year ); ?>"></div>
                    <script type="application/json" class="c3-chart-data">
                        <?php
                        echo wp_json_encode( $this->get_chart_json( $form, esc_attr( $this->slug . '-chart-' . $year ), array_values( $months ), $month_results, __( 'Months', 'torro-forms' ), __( 'Submission Count', 'torro-forms' ) ) );
                        ?>
                    </script>
                    <?php
                },
            );
        }

        $this->display_tabs( $tabs );
    }

    /**
     * Registers all assets the submodule provides.
     *
     * @since 1.0.0
     *
     * @param Assets $assets The plugin assets instance.
     */
    public function register_assets( $assets ) {
        // Empty method body.
    }

    /**
     * Enqueues scripts and stylesheets on the form editing screen.
     *
     * @since 1.0.0
     *
     * @param Assets $assets The plugin assets instance.
     */
    public function enqueue_form_builder_assets( $assets ) {
        // Empty method body.
    }

    /**
     * Enqueues scripts and stylesheets on the submissions list table view.
     *
     * @since 1.0.0
     *
     * @param Assets $assets The plugin assets instance.
     * @param Form   $form   Form to show results for.
     */
    public function enqueue_submission_results_assets( $assets, $form ) {
        $assets->enqueue_script( 'c3' );
        $assets->enqueue_style( 'c3' );
    }

    /**
     * Checks whether the evaluator is enabled for a specific form.
     *
     * @since 1.0.0
     *
     * @param Form $form Form object to check.
     * @return bool True if the evaluator is enabled, false otherwise.
     */
    public function enabled( $form ) {
        return true;
    }

    /**
     * Returns the available meta fields for the submodule.
     *
     * @since 1.0.0
     *
     * @return array Associative array of `$field_slug => $field_args` pairs.
     */
    public function get_meta_fields() {
        $meta_fields = $this->_get_meta_fields();

        unset( $meta_fields['enabled'] );

        $meta_fields['display_mode'] = array(
            'type'    => 'select',
            'label'   => __( 'Display Mode', 'torro-forms' ),
            'choices' => array(
                'line'   => __( 'Line Chart', 'torro-forms' ),
                'spline' => __( 'Spline Chart', 'torro-forms' ),
                'step'   => __( 'Step Chart', 'torro-forms' ),
                'bar'    => __( 'Bar Chart', 'torro-forms' ),
            ),
            'default' => 'line',
        );

        $meta_fields['data_point_labels'] = array(
            'type'        => 'select',
            'label'       => _x( 'Data Point Labels', 'evaluator', 'torro-forms' ),
            'description' => __( 'Specify whether a label should be shown with each data point and which content it should display.', 'torro-forms' ),
            'choices'     => array(
                'none'       => _x( 'None', 'data point labels', 'torro-forms' ),
                'value'      => _x( 'Values', 'data point labels', 'torro-forms' ),
                'percentage' => _x( 'Percentages', 'data point labels', 'torro-forms' ),
            ),
            'default'     => 'none',
        );

        return $meta_fields;
    }

    /**
     * Returns the JSON data to generate the chart.
     *
     * @since 1.0.0
     *
     * @global WP_Locale $wp_locale WordPress locale object.
     *
     * @param Form   $form     Form object.
     * @param string $id       ID attribute of the element to bind the chart to.
     * @param array  $x_values Values for the 'x' axis.
     * @param array  $y_values Values for the 'y' axis. Must match the number of $x_values passed.
     * @param string $x_label  Label for the 'x' axis.
     * @param string $y_label  Label for the 'y' axis.
     * @return array JSON data for the chart.
     */
    protected function get_chart_json( $form, $id, $x_values, $y_values, $x_label, $y_label ) {
        global $wp_locale;

        $display_mode      = $this->get_form_option( $form->id, 'display_mode', 'line' );
        $data_point_labels = $this->get_form_option( $form->id, 'data_point_labels', 'none' );

        $less_than_10 = array_reduce(
            $y_values,
            function( $carry, $y_value ) {
                if ( $y_value > 10 ) {
                    return false;
                }

                return $carry;
            },
            true
        );

        $labels = false;
        if ( 'value' === $data_point_labels ) {
            $labels = true;
        } elseif ( 'percentage' === $data_point_labels ) {
            $aggregate = array_reduce(
                $y_values,
                function( $carry, $y_value ) {
                    $carry += $y_value;

                    return $carry;
                },
                0
            );

            $labels = array(
                'format' => array(
                    'template'  => '%percentage%%',
                    'aggregate' => $aggregate,
                ),
            );
        }

        array_unshift( $x_values, 'x' );
        array_unshift( $y_values, 'submissionCount' );

        $data = array(
            'bindto' => '#' . $id,
            'data'   => array(
                'x'       => 'x',
                'columns' => array( $x_values, $y_values ),
                'names'   => array(
                    'submissionCount' => $y_label,
                ),
                'type'    => $display_mode,
                'colors'  => array(
                    'submissionCount' => '#0073aa',
                ),
                'labels'  => $labels,
            ),
            'axis'   => array(
                'x' => array(
                    'label' => $x_label,
                    'type'  => 'category',
                ),
                'y' => array(
                    'label' => $y_label,
                    'min'   => 1,
                ),
            ),
            'legend' => array(
                'show' => false,
            ),
        );
        if ( $less_than_10 ) {
            $data['axis']['y']['max'] = 10;
        }

        return $data;
    }
}