__( 'Form segments — who fills in a form', 'ai-eshop-optimizer' ), 'description' => __( 'READ-ONLY, no AI credits. Turns a Gravity Form\'s choice fields (checkbox, radio, select, multiselect) into an audience breakdown: the ranked top segments that are useful for targeting, plus the full per-field distribution with counts and percentages. Answers "who is actually filling in my enquiry form?" and grounds ad copy, keywords and social targeting in what leads declared about themselves. Params: form (name, matched case-insensitively — partial names are fine) or form_id; limit (top segments, default 8); include_distributions (default true). Call it with NO form to list the forms available with their entry counts. Percentages are of RESPONDENTS to each field, not of entries, so on multi-select fields the shares sum past 100%. Returns nothing when Gravity Forms is not active. It never edits a form or an entry.', 'ai-eshop-optimizer' ), 'category' => \AIEO\Claude\Bootstrap::CATEGORY, 'input_schema' => self::input_schema(), 'output_schema' => array( 'type' => 'object' ), 'execute_callback' => array( __CLASS__, 'execute' ), 'permission_callback' => array( __CLASS__, 'permission_check' ), 'meta' => array( 'annotations' => array( 'readonly' => true, 'idempotent' => true, ), 'show_in_rest' => true, ), ) ); } public static function permission_check( $input = null ) { unset( $input ); return current_user_can( 'manage_woocommerce' ) || current_user_can( 'manage_options' ); } private static function input_schema() { return array( 'default' => array(), 'type' => 'object', 'properties' => array( 'form' => array( 'type' => 'string', 'description' => 'Form name. Matched case-insensitively and partially, so "enquiry" finds "Contact enquiry form".', ), 'form_id' => array( 'type' => 'integer', 'minimum' => 1, 'description' => 'Gravity Forms form id, when known. Takes precedence over `form`.', ), 'limit' => array( 'type' => 'integer', 'default' => 8, 'minimum' => 1, 'maximum' => 50, 'description' => 'How many top segments to return. Defaults to 8.', ), 'include_distributions' => array( 'type' => 'boolean', 'default' => true, 'description' => 'Include the full per-field breakdown as well as the ranked segments.', ), ), ); } public static function execute( $input = null ) { if ( ! class_exists( 'AIEO_Form_Insights' ) ) { return array( 'available' => false, 'forms' => array(), 'note' => __( 'Form insights are not available on this site.', 'ai-eshop-optimizer' ), ); } if ( ! \AIEO_Form_Insights::available() ) { return array( 'available' => false, 'forms' => array(), 'note' => __( 'Gravity Forms is not active, so there are no form answers to report on.', 'ai-eshop-optimizer' ), ); } $input = is_array( $input ) ? $input : array(); $limit = isset( $input['limit'] ) ? (int) $input['limit'] : 8; $limit = max( 1, min( 50, $limit ) ); $with_d = ! isset( $input['include_distributions'] ) || (bool) $input['include_distributions']; $forms = (array) \AIEO_Form_Insights::forms(); if ( ! $forms ) { return array( 'available' => true, 'forms' => array(), 'note' => __( 'There are no Gravity Forms on this site to report on.', 'ai-eshop-optimizer' ), ); } $form_id = isset( $input['form_id'] ) ? (int) $input['form_id'] : 0; $wanted = isset( $input['form'] ) ? trim( (string) $input['form'] ) : ''; $matched = array(); if ( $form_id <= 0 && '' !== $wanted ) { $matched = self::match_forms( $forms, $wanted ); if ( ! $matched ) { return array( 'available' => true, 'forms' => self::form_list( $forms ), 'note' => __( 'No form matched that name. Pick one of the forms listed and ask again.', 'ai-eshop-optimizer' ), ); } $form_id = (int) $matched[0]['id']; } if ( $form_id <= 0 ) { return array( 'available' => true, 'forms' => self::form_list( $forms ), 'note' => __( 'Name one of these forms (or pass its form_id) to get its audience breakdown.', 'ai-eshop-optimizer' ), ); } $report = \AIEO_Form_Insights::distributions( $form_id ); if ( is_wp_error( $report ) ) { return array( 'available' => true, 'form_id' => $form_id, 'forms' => self::form_list( $forms ), 'note' => $report->get_error_message(), ); } $out = array( 'available' => true, 'form_id' => (int) ( isset( $report['form_id'] ) ? $report['form_id'] : $form_id ), 'form_title' => (string) ( isset( $report['form_title'] ) ? $report['form_title'] : '' ), 'total_entries' => (int) ( isset( $report['total_entries'] ) ? $report['total_entries'] : 0 ), 'top_segments' => array_values( (array) \AIEO_Form_Insights::top_segments( $form_id, $limit ) ), 'computed_at' => (string) ( isset( $report['computed_at'] ) ? $report['computed_at'] : '' ), 'note' => __( 'Percentages are of RESPONDENTS to each field, not of entries — on multi-select fields a person ticking three boxes counts once in the denominator, so shares sum past 100%. Top segments are ranked by how much they SEGMENT the audience: fields offering three or more live answers outrank yes/no ones, because the majority answer to a yes/no question is the largest group on the form and the least useful thing on it.', 'ai-eshop-optimizer' ), ); if ( $with_d ) { $out['fields'] = isset( $report['fields'] ) && is_array( $report['fields'] ) ? $report['fields'] : array(); } if ( count( $matched ) > 1 ) { $out['matched_forms'] = $matched; } return $out; } private static function match_forms( array $forms, $needle ) { $has_mb = function_exists( 'mb_strtolower' ); $lc = $has_mb ? mb_strtolower( $needle ) : strtolower( $needle ); $hits = array(); foreach ( $forms as $f ) { $title = (string) ( isset( $f['title'] ) ? $f['title'] : '' ); $t_lc = $has_mb ? mb_strtolower( $title ) : strtolower( $title ); if ( '' === $t_lc || false === strpos( $t_lc, $lc ) ) { continue; } $hits[] = array( 'id' => (int) ( isset( $f['id'] ) ? $f['id'] : 0 ), 'title' => $title, 'entries' => (int) ( isset( $f['entries'] ) ? $f['entries'] : 0 ), 'exact' => ( $t_lc === $lc ), ); } usort( $hits, static function ( $a, $b ) { if ( $a['exact'] !== $b['exact'] ) { return $a['exact'] ? -1 : 1; } return $b['entries'] <=> $a['entries']; } ); foreach ( $hits as $i => $h ) { unset( $hits[ $i ]['exact'] ); } return array_values( $hits ); } private static function form_list( array $forms ) { $out = array(); foreach ( $forms as $f ) { $out[] = array( 'id' => (int) ( isset( $f['id'] ) ? $f['id'] : 0 ), 'title' => (string) ( isset( $f['title'] ) ? $f['title'] : '' ), 'entries' => (int) ( isset( $f['entries'] ) ? $f['entries'] : 0 ), ); } return $out; } }