WPD Data Warehouse

Alpha Insights Documentation

WPD_Data_Warehouse( $filter = array() )

Overview

This class will become our core class for fetching any data, this includes core post types, custom post types & custom tables.

The way this class works fundamentally is by passing a filter in (array), which will be used for any requests that are made.

You can then call any data you would like using the relevant method (ref below) which will load the data into the object.

The called method will return the relevant data, as well as store the data in a property which can be fetched using get_data();

All fetch functions are responsible for calling raw data from the DB and all organise functions are responsible for formatting the data into the relevant array keys.

This class is a work in progress, in future version of Alpha Insights all data called by Alpha Insights will go through this class so that any stored data can be accessed from the same place.

Example Usage

				
					// Init the class
$data_warehouse = new WPD_Data_Warehouse();

// Call a method to fetch for data
$analytics_data = $data_warehouse->fetch_analytics_data();
				
			

WPD_Data_Warehouse

				
					<?php
/**
 *
 * High level class that fetches all data, mainly used for reports but can be accessed directly
 * 
 * @package Alpha Insights
 * @version 2.0.0
 * @author WPDavies
 * @link https://wpdavies.dev/
 *
 */
defined( 'ABSPATH' ) || exit;

class WPD_Data_Warehouse {

    /**
     *
     *  The filter used for filtering data
     * 
     *  ['date_format_display'] = Holds the date format for grouping
     *  ['start_date] = Holds the start date to filter by
     *  ['end_date] = Holds the end date to filter by
     *   
     */
    private array $filter = array();

    /**
     *
     *  Data by date containers
     *
     */
    private array $data_by_date_containers = array();

    /**
     * 
     *  Stores any errors in request, useful for debugging
     * 
     **/
    private array $errors = array();

    /**
     *
     *  All data that has been fetched will be stored in this array
     * 
     *  Associative array with keys for each data segment and a breakdown of totals, data & data_by_date
     *  Only store what we need so that we don't overdo the memory
     * 
     */
    private array $data = array(
        'analytics' => array(
            'totals' => array(),
            'raw_data' => array(),
            'organised_data' => array(),
            'data_by_date' => array(),
            'total_db_records' => 0,
        ),
        'product_impressions' => array(
            'totals' => array(),
            'raw_data' => array(),
            'organised_data' => array(),
            'data_by_date' => array(),
            'total_db_records' => 0
        ),
        'anonymous_queries' => array(), // If we make arbitrary SQL queries, they can go in here
        'total_db_records' => 0 // Stores all records fetched
    );

    /** 
     *   Initialize the constructor for the data warehouse. Will load the filters & date containers on initialization
     * 
     *   @param array $filter The filter to be used on the data, expected to be passed as an array at this point
     *   @param array $data_by_date_containers An array of dates with empty values to use for reports
     *
     **/
    public function __construct( $filter = array() ) {

        // Configure Filter
        $this->set_filter( $filter );

        // Setup data by date
        $this->set_data_by_date_containers();

    }

    /**
     *
     *  Setup props, loads filter and date containers in if they are set
     * 
     * @param array $filter
     * @param array $data_by_date_containers
     * @return null
     *
     */
    private function set_filter( $filter ) {

        $this->filter = $filter;

    }

    /**
     *
     *  Setup the date shells
     *
     */
    private function set_data_by_date_containers() {

        // Fetch details
        $max_date       = $this->get_selected_date_range('end_date');    // date in the past
        $min_date       = $this->get_selected_date_range('start_date');  // current date
        $n_days_period  = $this->get_n_days_range();

        // Set Default Display Format
        if ( ! isset($this->filter['date_format_display']) ) $this->filter['date_format_display'] = 'day';

        /**
         *
         *  Construct date data
         *
         */
        if ( $this->filter['date_format_display'] == 'day' ) {

            $date_range = $this->get_date_range_array($min_date, $max_date, '+1 day', 'Y-m-d' );
            $date_format = 'Y-m-d';

        } elseif ( $this->filter['date_format_display'] == 'month' ) {

            $date_format = 'M Y';
            $date_range = $this->get_date_range_array($min_date, $max_date, '+1 day', $date_format );

        } elseif ( $this->filter['date_format_display'] == 'quarter' ) {

            $date_format = 'M Y';
            $date_range = $this->get_date_range_array($min_date, $max_date, '+3 months', $date_format ); // Changed step

        } elseif ( $this->filter['date_format_display'] == 'year' ) {

            $date_format = 'Y';
            $date_range = $this->get_date_range_array($min_date, $max_date, '+1 year', $date_format );

        } else {

            $date_format = 'Y-m-d';
            $date_range = $this->get_date_range_array($min_date, $max_date, '+1 day', $date_format );

        }

        $calculations_by_day = array(
            'Mon' => 0,
            'Tue' => 0,
            'Wed' => 0,
            'Thu' => 0,
            'Fri' => 0,
            'Sat' => 0,
            'Sun' => 0,
        );

        $calculations_by_time = array(
            '12am' => 0,
            '1am' => 0,
            '2am' => 0,
            '3am' => 0,
            '4am' => 0,
            '5am' => 0,
            '6am' => 0,
            '7am' => 0,
            '8am' => 0,
            '9am' => 0,
            '10am' => 0,
            '11am' => 0,
            '12pm' => 0,
            '1pm' => 0,
            '2pm' => 0,
            '3pm' => 0,
            '4pm' => 0,
            '5pm' => 0,
            '6pm' => 0,
            '7pm' => 0,
            '8pm' => 0,
            '9pm' => 0,
            '10pm' => 0,
            '11pm' => 0,
        );

        $date_formatting_container = array();
        foreach ( $date_range as $date_array_val ) {

            $date_formatting_container[$date_array_val] = array(
                'x' =>  $date_array_val,
                'y' =>  0           
            );

        }

        // Return data
        $data_by_date_containers = array(

            'n_days_period' => $n_days_period,
            'date_format' => $date_format,
            'date_from' => $min_date,
            'date_to' => $max_date,
            'date_range_container' => $date_formatting_container,
            'calculations_by_day' => $calculations_by_day,
            'calculations_by_time' => $calculations_by_time

        );

        // Set data by date containers
        $this->data_by_date_containers = $data_by_date_containers;

        // Return data by date containers
        return $data_by_date_containers;

    }

    /**
     * 
     *  Stores data in the data prop
     *  
     *  @param string $data_type The relevant data type, i.e. expense, orders, etc etc (see init $data prop for available types)
     *  @param array $data The data to pass in, must be one of: totals, raw_data, organised_data, data_by_date
     * 
     **/
    private function set_data( $data_type, $data ) {

        // Data type passed is incorrect
        if ( ! is_string($data_type) ) {
            $this->set_error( 'Trying to set data, ' . $data_type . ' is not a string.' );
            return false;
        }

        // Data payload is incorrect
        if ( ! is_array($data) ) {
            $this->set_error( 'Trying to set data, ' . $data . ' is not an array.' );
            return false;
        }

        // Allowed keys to be set into array
        $allowed_data_keys = array( 'totals', 'raw_data', 'organised_data', 'data_by_date', 'total_db_records' );

        // Store the data
        foreach( $data as $key => $data ) {

            // Only store allowed keys
            if ( ! in_array( $key, $allowed_data_keys ) ) {
                $this->set_error( 'Trying to set data, ' . $key . ' is not an allowed key.' );
                continue;
            }
            
            // Store the key
            $this->data[$data_type][$key] = $data;

        }

        // Update the total record count
        $this->set_total_db_records();

    }

    /**
     * 
     *  Checks all stored db records and sets the data property accordingly
     * 
     **/
    public function set_total_db_records() {

		// Get the array keys
		$array_keys = array_keys( $this->get_data() );

		// Set the default count
		$count = 0;

		// Loop through the keys
		foreach( $array_keys as $data_type ) {
			if ( isset($this->data[$data_type]['total_db_records']) ) {
				$count += $this->data[$data_type]['total_db_records'];
			}
		}

        // Update our data
        $this->data['total_db_records'] = $count;

        // Return result
		return $count;

	}

    /**
     * 
     *  Sets an error in the class
     * 
     **/
    private function set_error( $error ) {

        // This method does most of the error handling
        $this->log( $error, true );

    }

    /**
     * 
     *  Returns an associative array of all date meta being used
     * 
     **/
    public function get_date_meta_data() {

        $date_meta = array(
            'start_date' => $this->get_start_date(),
            'end_date' => $this->get_end_date(),
            'n_days_period' => $this->get_n_days_range(),
            'date_format_display' => 'day',
            'time_parser' => 'yyyy-MM-dd'
        );

        // Overrides
        if ( isset($this->filter['date_format_display']) ) {
            $date_meta['date_format_display'] = $this->filter['date_format_display'];
        }

        // Overrides
        if ( isset($this->filter['time_parser']) ) {
            $date_meta['time_parser'] = $this->filter['time_parser'];
        }

        // Return
        return $date_meta;

    }

    /**
     * 
     *  Returns data by date containers
     * 
     **/
    public function get_data_by_date_containers() {

        // Set the containers if not set yet
        if ( empty($this->data_by_date_containers) ) {
            $this->set_data_by_date_containers();
        }

        return $this->data_by_date_containers;

    }

    /**
     * 
     *  Gets the start date we are using for this request
     * 
     **/
    public function get_start_date( $format = 'Y-m-d' ) {

        return $this->get_selected_date_range('start_date', $format);

    }

    /**
     * 
     *  Gets the start date we are using for this request
     * 
     **/
    public function get_end_date( $format = 'Y-m-d' ) {

        return $this->get_selected_date_range('end_date', $format);

    }

    /**
     *
     *  Returns selected date range
     *  Accepts start_date or end_date
     * 
     *  @todo Get rid of this and move it into get_start_date() and get_end_date()
     *
     */
    public function get_selected_date_range( $result = 'start_date', $format = 'Y-m-d' ) {

        $days_in_past   = (string) '-' . $this->get_n_days_range() . ' days';
        $wp_timestamp   = current_time( 'timestamp' );

        if ( $result == 'start_date' ) {

            $start = date($format, strtotime( $days_in_past, $wp_timestamp ) ); // this needs to be based on wp time as below

            if ( isset( $this->filter['start_date'] ) && ! empty($this->filter['start_date']) ) {
                $start = date( $format, strtotime($this->filter['start_date']) );
            }

            return $start;

        } elseif ( $result == 'end_date' ) {

            $end = current_time( $format ); 

            if ( isset($this->filter['end_date']) && ! empty($this->filter['end_date']) ) {
                $end = date( $format, strtotime($this->filter['end_date']));
            }

            return $end;

        }

    }

    /**
     *
     *  @return array $dates Returns array of dates within defined range
     *
     */
    public function get_date_range_array( $first, $last, $step = '+1 day', $output_format = 'Y-m-d' ) {

        $dates              = array();
        $current_date       = strtotime($first);
        $end_date           = strtotime($last);

        while( $current_date <= $end_date ) {

            $dates[] = date($output_format, $current_date);
            $current_date = strtotime($step, $current_date);

        }

        return array_values( array_unique( $dates ) );

    }

    /**
     *
     *  Check if we are limiting range by X days
     *
     */
    public function get_n_days_range() {

        // Defaults
        $days = 30;

        if ( isset( $this->filter['start_date'] ) && isset( $this->filter['end_date'] ) ) {

            $start  = new DateTime( $this->filter['start_date'] );
            $end    = new DateTime( $this->filter['end_date'] );

            // Only execute this if we're sure these are now objects
            if ( is_a($start, 'DateTime') && is_a($end, 'DateTime') ) {

                $days = $end->diff( $start )->format('%a') + 1;
                return $days;

            }

        }

        return $days;

    }

    /**
     * 
     *  Returns the currently set filter
     * 
     *  @return array Returns all active filters on this instance of the class
     * 
     **/
    public function get_filter( $key = null ) {

        // Check if they've passed a key in
        if ( ! is_null($key) ) {

            // Return their key if found
            if ( isset($this->filter[$key]) ) {
                return $this->filter[$key];
            }

            // Otherwise false, we couldn't find it
            return false;

        }

        // Return all filters
        return $this->filter;

    }

    /**
     * 
     *  Data Return Function
     * 
     *  If no params are set, it will return all data
     *  If only the data_type is set, it will return all found data for that data_type
     *  If the data_type and data_key are set and found, will return that payload
     *  
     *  @param string $data_type The data type, i.e. analytics, orders etc..
     *  @param string $data_key The data_key to fetch for, will return that specific key's data if found with the data_type. Accepts totals, raw_data, organised_data, data_by_date & total_db_records
     * 
     *  @return array|bool Returns the requested data or false if we couldn't resolve the request
     * 
     **/
    public function get_data( $data_type = false, $data_key = false ) {

        // Insufficient parameters passed, return all data
        if ( $data_type === false && $data_key === false ) {
            return $this->data;
        }

        // Return all data for the data type if found and no data_key is set
        if ( isset( $this->data[$data_type] ) && $data_key === false ) {
            return $this->data[$data_type];
        }

        // Try and return their target key if found
        if ( isset( $this->data[$data_type] ) && isset($this->data[$data_type][$data_key]) ) {
            return $this->data[$data_type][$data_key];
        }

        // Incorrect query
        return false;

    }

    /**
     * 
     *  Gets all set errors
     * 
     **/
    public function get_errors() {

        return $this->errors;

    }

    /**
     * 
     *  Gets the total record count
     * 
     **/
    public function get_total_db_records() {

        // Found it
        if ( isset($this->data['total_db_records']) ) {
            return $this->data['total_db_records'];
        }

        // Doesnt exist?
        return false;

    }

    /**
     *
     *  Fetches Analytics data from DB and stores it, raw data only no processing.
     *  @see get_gmt_from_date() for converting input date
     *  @link https://developer.wordpress.org/reference/functions/get_gmt_from_date/
     *
     */
    public function fetch_analytics_data() {

        global $wpdb;

        // Collect Vars
        $wpd_db = new WPD_Database_Interactor();
        $woo_events_table = $wpd_db->events_table;
        $session_data_table = $wpd_db->session_data_table;

        // Apply filters if required
        $where_clause = '';
        $filters = $this->get_filter();

        if ( isset($filters['start_date']) && isset($filters['end_date']) ) {

            $start_date = get_gmt_from_date( $filters['start_date'] );
            $end_date = get_gmt_from_date( $filters['end_date'] );

            // Add 1 day to end date, so that it's inclusive of actual date
            $end_date = date("Y-m-d H:i:s", strtotime('+24 hours', strtotime($end_date)));

            $where_clause .= ' AND events.date_created_gmt >= \'' . $start_date . '\'';
            $where_clause .= ' AND events.date_created_gmt <= \'' . $end_date . '\'';

        }

        // Single session ID defined
        if ( isset($filters['session_id']) && ! empty($filters['session_id']) && is_string($filters['session_id']) ) {
            $where_clause .= ' AND session_data.session_id = \'' . $filters["session_id"] . '\'';
        }

        // Prepare query
        $sql_query = 
            "SELECT 
            events.session_id,
            events.date_created_gmt AS event_date_created_gmt,
            events.event_type, 
            events.event_quantity,
            events.event_value,
            events.product_id,
            events.variation_id,
            events.page_href, 
            events.object_type, 
            events.object_id, 
            session_data.user_id, 
            session_data.landing_page,
            session_data.referral_url,
            session_data.date_created_gmt AS session_date_created_gmt,
            session_data.date_updated_gmt AS session_date_updated_gmt,
            session_data.device,
            session_data.browser,
            session_data.ip_address
            FROM $woo_events_table AS events
            LEFT JOIN $session_data_table AS session_data ON events.session_id = session_data.session_id
            WHERE 1=1
            $where_clause
            ORDER BY events.date_created_gmt ASC";

        // Fetch Results
        $results = $wpdb->get_results( $sql_query, 'ARRAY_A' );

        // DB Error
        if ( $wpdb->last_error  ) {
            wpd_write_log( 'Error capturing analytics data from DB, dumping the error and query.', 'db_error' );
            wpd_write_log( $wpdb->last_error, 'db_error' );
            wpd_write_log( $wpdb->last_query, 'db_error' );
            $this->set_error( $wpdb->last_error );
            return false;
        }

        // Get count of records from query
        $record_count = (is_array($results)) ? count($results) : 0;

        // Store results
        $this->set_data( 'analytics', array('raw_data' => $results) );
        $this->set_data( 'analytics', array('total_db_records' => $record_count) );

        // Organise data (This will also update the stored props)
        $this->organise_analytics_data();

        // Return results (will return all relevant props also stored by organised_analytics_data)
        return $this->get_data( 'analytics' );

    }

    /**
     *
     *  Prepare Analytics Data, reforms it into a more usable array
     *  @todo object not being passed into product page views
     *  @todo method of tracking products purchased is okay, but doesnt give me an order value. Need to create an event for order_created or whatever
     *  
     */
    private function organise_analytics_data() {

        // Start prep
        $raw_analytics_data = $this->get_data( 'analytics', 'raw_data' );
        $organised_data = array();
        $session_summary_container = array(
            'page_views'                    => 0,
            'non_page_view_events'          => 0, // All non-page view events
            'category_page_views'           => 0,
            'product_clicks'                => 0,
            'product_page_views'            => 0,
            'add_to_carts'                  => 0,
            'add_to_cart_value'             => 0,
            'initiate_checkouts'            => 0,
            'transactions'                  => 0,
            'transaction_value'             => 0,
            'product_transaction_value'     => 0,
            'unique_products_purchased'     => 0, // Unique line items
            'total_products_purchased'      => 0, // Quantity of products purchased
        );
        $session_container = array(
            'summary'       => $session_summary_container,
            'session'       => array(),
            'events'        => array()
        );
        $page_view_container = array(
            'session_count' => array(),
            'user_count'    => array(),
            'views'         => 0,
            'transactions'  => 0,
            'revenue'       => 0
        );
        $totals = array(
            'total_records'                         => 0,
            'sessions'                              => 0,
            'users'                                 => 0, // (count of unique IP's)
            'page_views'                            => 0,
            'non_page_view_events'                  => 0, // All non-page view events
            'session_duration'                      => 0,
            'average_session_duration'              => 0,
            'category_page_views'                   => 0,
            'sessions_with_category_page_views'     => 0,
            'product_clicks'                        => 0,
            'product_page_views'                    => 0,
            'sessions_with_product_page_views'      => 0,
            'add_to_carts'                          => 0,
            'add_to_cart_value'                     => 0,
            'sessions_with_add_to_cart'             => 0,
            'initiate_checkouts'                    => 0,
            'sessions_with_initiate_checkout'       => 0,
            'transactions'                          => 0,
            'sessions_with_transaction'             => 0,
            'transaction_value'                     => 0,
            'product_transaction_value'             => 0,
            'unique_products_purchased'             => 0, // Unique line items
            'total_products_purchased'              => 0, // Quantity of products purchased
            'event_summary'                         => array(),
            'campaign_summary'                      => array(),
            'acquisition_summary'                   => array(),
            'product_summary'                       => array(),
            'page_view_summary'                     => array(),
            'landing_page_summary'                  => array(),
            'referral_url_summary'                  => array(),
            'calculations'                          => array(
                'sessions_per_day'                          => 0,
                'users_per_day'                             => 0,
                'page_views_per_session'                    => 0,
                'events_per_session'                        => 0,
                'percent_sessions_with_category_view'       => 0.00,
                'percent_sessions_with_product_page_view'   => 0.00,
                'percent_sessions_with_add_to_cart'         => 0.00,
                'percent_sessions_with_initiate_checkout'   => 0.00,
                'conversion_rate'                           => 0.00
            )
        );

        $temp_counter = array(
            'session_id' => array(),
            'ip_address' => array(),
            'sessions_by_date' => array(),
            'ip_address_by_date' => array()
        );

        $data_by_date = array(

            'sessions_by_date' => $this->data_by_date_containers['date_range_container'], // Unique Per Day
            'users_by_date' => $this->data_by_date_containers['date_range_container'], // Unique Per Day
            'page_views_by_date' => $this->data_by_date_containers['date_range_container'],
            'events_by_date' => $this->data_by_date_containers['date_range_container'],
            'category_page_views_by_date' => $this->data_by_date_containers['date_range_container'],
            'product_clicks_by_date' => $this->data_by_date_containers['date_range_container'],
            'product_page_views_by_date' => $this->data_by_date_containers['date_range_container'],
            'add_to_carts_by_date' => $this->data_by_date_containers['date_range_container'],
            'transactions_by_date' => $this->data_by_date_containers['date_range_container'],

        );

        // Check for unique events per session -> store sessions ID and do unique count at the end
        $session_unique_array = array(
            'sessions_with_category_page_view' => array(),
            'sessions_with_product_page_view' => array(),
            'sessions_with_add_to_cart' => array(),
            'sessions_with_initiate_checkout' => array(),
            'sessions_with_transaction' => array()
        );

        // If we're empty, return defaults.
        if ( empty( $raw_analytics_data ) || ! is_array( $raw_analytics_data ) ) {

            $analytics_data = array(
                'totals' => $totals,
                'data_by_date' => $data_by_date,
                'organised_data' => $organised_data
            );

            // Store data
            $this->set_data( 'analytics', $analytics_data );

            // Basically returns an empty / default array
            return $analytics_data;

        }

        /**
         *
         *  Perform all calculations
         *
         */
        foreach( $raw_analytics_data as $event ) {

            // Event Data
            $session_id                 = $event['session_id'];
            $event_date_created_gmt     = $event['event_date_created_gmt'];
            $event_type                 = $event['event_type'];
            $event_quantity             = $event['event_quantity'];
            $event_value                = $event['event_value'];
            $product_id                 = $event['product_id'];
            $variation_id               = $event['variation_id'];
            $page_href                  = $event['page_href'];
            $object_type                = $event['object_type'];
            $object_id                  = $event['object_id'];

            // @todo this should likely be done on submission of data
            if ($event_type == 'add_to_cart') $event_value = $event_value * $event_quantity;

            // Session Data
            $user_id                    = $event['user_id'];
            $ip_address                 = $event['ip_address'];
            $landing_page               = $event['landing_page'];
            $referral_url               = $event['referral_url'];
            $session_date_created_gmt   = $event['session_date_created_gmt'];
            $session_date_updated_gmt   = $event['session_date_updated_gmt'];
            $device                     = $event['device'];
            $browser                    = $event['browser'];

            // Variable cleaning
            $session_duration               = $this->calculate_difference_in_seconds( $session_date_updated_gmt, $session_date_created_gmt );
            $event_timestamp_in_local       = get_date_from_gmt( $event_date_created_gmt );
            $session_date_created_local     = get_date_from_gmt( $session_date_created_gmt );
            $session_date_updated_local     = get_date_from_gmt( $session_date_updated_gmt );
            $landing_page_url_components    = $this->get_url_components( $landing_page );
            $landing_page_path              = $landing_page_url_components['path'];
            $landing_page_query_parameters  = $landing_page_url_components['query_parameters'];
            $session_traffic_source         = $this->determine_traffic_source( $referral_url, $landing_page_query_parameters );
            $event_page_url_components      = $this->get_url_components( $page_href );
            $event_page_path                = $event_page_url_components['path'];
            $event_formatted_date           = $this->reformat_date_to_date_format($event_timestamp_in_local); // Formatted for date date
            (isset($landing_page_query_parameters['utm_campaign'])) ? $utm_campaign = $landing_page_query_parameters['utm_campaign'] : $utm_campaign = null;

            // Setup session container
            if ( ! isset($organised_data[$session_id]) ) $organised_data[$session_id] = $session_container;

            // Store Events
            $organised_data[$session_id]['events'][] = array(
                'event_type' => $event_type,
                'event_timestamp_in_local' => $event_timestamp_in_local, // need to create
                'event_quantity' => $event_quantity,
                'event_value' => $event_value,
                'event_product_id' => $product_id,
                'event_variation_id' => $variation_id,
                'event_page_href' => $page_href,
                'event_page_path' => $event_page_path,
                'object_type' => $object_type,
                'object_id' => $object_id
            );

            // Store Session Meta
            $organised_data[$session_id]['session'] = array(
                'session_id' => $session_id,
                'ip_address' => $ip_address,
                'user_id' => $user_id,
                'session_start_in_local' => $session_date_created_local,
                'session_end_in_local' => $session_date_updated_local,
                'session_duration' => $session_duration,
                'landing_page' => $landing_page,
                'landing_page_path' => $landing_page_path,
                'landing_page_query_parameters' => $landing_page_query_parameters,
                'landing_page_campaign' => $utm_campaign,
                'referral_url' => $referral_url,
                'referral_source' => $session_traffic_source,
                'device' => $device,
                'browser' => $browser,
            );

            /**
             *
             *  Calculate session totals
             *
             */
            // Data Totals
            $totals['total_records']++;

            /**
             *
             *  Calculate Totals
             * 
             */
            // Total Sessions
            if ( ! isset($temp_counter['session_id'][$session_id]) ) {
                $temp_counter['session_id'][$session_id] = true;
                $totals['sessions']++;
                $totals['session_duration'] += $session_duration;
            }

            // Total Users
            if ( ! isset($temp_counter['ip_address'][$ip_address]) ) {
                $temp_counter['ip_address'][$ip_address] = 1;
                $totals['users']++;
            }

            // Sessions by date
            if ( ! isset($temp_counter['sessions_by_date'][$event_formatted_date][$session_id]) ) {

                // Temp Counter for unique sessions by date
                $temp_counter['sessions_by_date'][$event_formatted_date][$session_id] = 1;

                // If this array key has not been setup, set it up
                if ( ! isset($data_by_date['sessions_by_date'][$event_formatted_date]['y']) ) $data_by_date['sessions_by_date'][$event_formatted_date]['y'] = 0;

                // Increment the event count
                $data_by_date['sessions_by_date'][$event_formatted_date]['y']++;

            }

            // Users by date
            if ( ! isset($temp_counter['ip_address_by_date'][$event_formatted_date][$ip_address]) ) {

                // Temp counter for unique users per date
                $temp_counter['ip_address_by_date'][$event_formatted_date][$ip_address] = 1;

                // Setup user count by date if it hasn't been setup
                if ( ! isset($data_by_date['users_by_date'][$event_formatted_date]['y']) ) $data_by_date['users_by_date'][$event_formatted_date]['y'] = 0;

                // Increment the users by date count
                $data_by_date['users_by_date'][$event_formatted_date]['y']++;

            }

            // Landing Page Data
            if ( ! empty($landing_page_path) ) {
                if ( ! isset($totals['landing_page_summary'][$landing_page_path]) ) {
                    $totals['landing_page_summary'][$landing_page_path] = $page_view_container;
                }
                $totals['landing_page_summary'][$landing_page_path]['session_count'][$session_id] = 1;
                $totals['landing_page_summary'][$landing_page_path]['user_count'][$ip_address] = 1;
                $totals['landing_page_summary'][$landing_page_path]['views']++;
            }
            
            // Referral Page Data
            if ( ! empty($referral_url) ) {
                if ( ! isset($totals['referral_url_summary'][$referral_url]) ) {
                    $totals['referral_url_summary'][$referral_url] = $page_view_container;
                }
                $totals['referral_url_summary'][$referral_url]['session_count'][$session_id] = 1;
                $totals['referral_url_summary'][$referral_url]['user_count'][$ip_address] = 1;
                $totals['referral_url_summary'][$referral_url]['views']++;
            }

            // Page Views
            if ($event_type == 'page_view') {
                $organised_data[$session_id]['summary']['page_views']++;
                $totals['page_views']++;

                if ( ! isset($data_by_date['page_views_by_date'][$event_formatted_date]['y']) ) $data_by_date['page_views_by_date'][$event_formatted_date]['y'] = 0;
                $data_by_date['page_views_by_date'][$event_formatted_date]['y']++;

                // Handle total page view data
                if ( ! isset($totals['page_view_summary'][$event_page_path]) ) {
                    $totals['page_view_summary'][$event_page_path] = $page_view_container;
                }
                $totals['page_view_summary'][$event_page_path]['session_count'][$session_id] = 1;
                $totals['page_view_summary'][$event_page_path]['user_count'][$ip_address] = 1;
                $totals['page_view_summary'][$event_page_path]['views']++;
            }

            // Events
            if ($event_type != 'page_view') {
                $organised_data[$session_id]['summary']['non_page_view_events']++;
                $totals['non_page_view_events']++;

                if ( ! isset($data_by_date['events_by_date'][$event_formatted_date]['y']) ) $data_by_date['events_by_date'][$event_formatted_date]['y'] = 0;
                $data_by_date['events_by_date'][$event_formatted_date]['y']++;
            }

            // Product Click
            if ($event_type == 'product_click') {
                $organised_data[$session_id]['summary']['product_clicks']++;
                $totals['product_clicks']++;
                $data_by_date['product_clicks_by_date'][$event_formatted_date]['y']++;
            }

            // Product Category Page Views
            if ($event_type == 'page_view' && $object_type == 'product_cat') {
                $organised_data[$session_id]['summary']['category_page_views']++;
                $totals['category_page_views']++;
                $data_by_date['category_page_views_by_date'][$event_formatted_date]['y']++;
                if ( ! in_array($session_id, $session_unique_array['sessions_with_category_page_view']) ) {
                    $session_unique_array['sessions_with_category_page_view'][] = $session_id;
                    $totals['sessions_with_category_page_views']++;
                }
            }

            // Product Page Views
            if ($event_type == 'page_view' && $object_type == 'product') {
                $organised_data[$session_id]['summary']['product_page_views']++;
                $totals['product_page_views']++;
                if ( ! isset($data_by_date['product_page_views_by_date'][$event_formatted_date]['y']) ) $data_by_date['product_page_views_by_date'][$event_formatted_date]['y'] = 0;
                $data_by_date['product_page_views_by_date'][$event_formatted_date]['y']++;
                if ( ! in_array($session_id, $session_unique_array['sessions_with_product_page_view']) ) {
                    $session_unique_array['sessions_with_product_page_view'][] = $session_id;
                    $totals['sessions_with_product_page_views']++;
                }
            }

            // Add to cart
            if ($event_type == 'add_to_cart') {
                $organised_data[$session_id]['summary']['add_to_carts']++;
                $organised_data[$session_id]['summary']['add_to_cart_value'] += $event_value;
                $totals['add_to_carts']++;
                $totals['add_to_cart_value'] += $event_value;
                if ( ! isset($data_by_date['add_to_carts_by_date'][$event_formatted_date]['y']) ) $data_by_date['add_to_carts_by_date'][$event_formatted_date]['y'] = 0;
                $data_by_date['add_to_carts_by_date'][$event_formatted_date]['y']++;
                if ( ! in_array($session_id, $session_unique_array['sessions_with_add_to_cart']) ) {
                    $session_unique_array['sessions_with_add_to_cart'][] = $session_id;
                    $totals['sessions_with_add_to_cart']++;
                }
            }

            // Initiate Checkout
            if ($event_type == 'init_checkout') {
                $organised_data[$session_id]['summary']['initiate_checkouts']++;
                $totals['initiate_checkouts']++;
                if ( ! in_array($session_id, $session_unique_array['sessions_with_initiate_checkout']) ) {
                    $session_unique_array['sessions_with_initiate_checkout'][] = $session_id;
                    $totals['sessions_with_initiate_checkout']++;
                }
            }

            // Purchase - Product Line Items
            if ($event_type == 'product_purchase') {
                $organised_data[$session_id]['summary']['unique_products_purchased']++;
                $organised_data[$session_id]['summary']['total_products_purchased'] += $event_quantity;
                $totals['unique_products_purchased']++;
                $totals['total_products_purchased'] += $event_quantity;
            }

            // Transaction
            if ($event_type == 'transaction') {
                $organised_data[$session_id]['summary']['transactions']++;
                $organised_data[$session_id]['summary']['transaction_value'] += $event_value;
                $totals['transactions']++;
                $totals['transaction_value'] += $event_value;
                if ( ! isset($data_by_date['transactions_by_date'][$event_formatted_date]['y']) ) $data_by_date['transactions_by_date'][$event_formatted_date]['y'] = 0;
                $data_by_date['transactions_by_date'][$event_formatted_date]['y']++;
                if ( ! in_array($session_id, $session_unique_array['sessions_with_transaction']) ) {
                    $session_unique_array['sessions_with_transaction'][] = $session_id;
                    $totals['sessions_with_transaction']++;
                }
                if (! empty($landing_page_path)) {
                    $totals['landing_page_summary'][$landing_page_path]['revenue'] += $event_value;
                    $totals['landing_page_summary'][$landing_page_path]['transactions']++;
                }
                if (! empty($referral_url)) {
                    $totals['referral_url_summary'][$referral_url]['revenue'] += $event_value;
                    $totals['referral_url_summary'][$referral_url]['transactions']++;
                } 
            }

            /**
             *
             *  Log all events here
             *
             */
            // Setup container if not exists
            if ( ! isset($totals['event_summary'][$event_type]) ) {
                $totals['event_summary'][$event_type] = array('total_count' => 0, 'user_count' => array(), 'session_count' => array(), 'total_value' => 0);
            }
            $totals['event_summary'][$event_type]['total_count']++;
            $totals['event_summary'][$event_type]['user_count'][$ip_address] = 1;
            $totals['event_summary'][$event_type]['session_count'][$session_id] = 1;
            $totals['event_summary'][$event_type]['total_value'] += $event_value;

            /**
             * 
             *  Campaign Performance
             * 
             */
            if ( ! is_null($utm_campaign) ) {
                if ( ! isset($totals['campaign_summary'][$utm_campaign]) ) {
                    $totals['campaign_summary'][$utm_campaign] = array('session_count' => array(), 'user_count' => array(), 'transactions' => 0, 'total_value' => 0);
                }
                $totals['campaign_summary'][$utm_campaign]['user_count'][$ip_address] = 1;
                $totals['campaign_summary'][$utm_campaign]['session_count'][$session_id] = 1;
                if ( $event_type == 'transaction' ) {
                    $totals['campaign_summary'][$utm_campaign]['transactions']++;
                    $totals['campaign_summary'][$utm_campaign]['total_value'] += $event_value;
                }
            }

            /**
             * 
             *  Acquisition Performance
             * 
             */
            if ( ! isset($totals['acquisition_summary'][$session_traffic_source]) ) {
                $totals['acquisition_summary'][$session_traffic_source] = array('session_count' => array(), 'channel_percent' => 0.00, 'user_count' => array(), 'transactions' => 0, 'total_value' => 0);
            }
            $totals['acquisition_summary'][$session_traffic_source]['user_count'][$ip_address] = 1;
            $totals['acquisition_summary'][$session_traffic_source]['session_count'][$session_id] = 1;
            if ( $event_type == 'transaction' ) {
                $totals['acquisition_summary'][$session_traffic_source]['transactions']++;
                $totals['acquisition_summary'][$session_traffic_source]['total_value'] += $event_value;
            }

            /**
             * 
             *  Product Performance
             * 
             */
            if ( $product_id > 0 ) {

                if ( ! isset($totals['product_summary'][$product_id]) ) {
                    $totals['product_summary'][$product_id] = array(
                        'user_count'            => array(), 
                        'session_count'         => array(), 
                        'category_impressions'  => 0, 
                        'product_clicks'        => 0, 
                        'ctr_percent'           => 0, 
                        'product_page_views'    => 0, 
                        'add_to_cart'           => 0, 
                        'add_to_cart_per_session'   => 0, 
                        'transactions'          => 0, 
                        'qty_purchased'         => 0, 
                        'total_value'           => 0
                    );
                }

                $totals['product_summary'][$product_id]['user_count'][$ip_address] = 1;
                $totals['product_summary'][$product_id]['session_count'][$session_id] = 1;
                if ( $event_type == 'product_click' ) $totals['product_summary'][$product_id]['product_clicks']++;
                if ( $event_type == 'add_to_cart' ) $totals['product_summary'][$product_id]['add_to_cart']++;
                if ( $event_type == 'page_view' && $object_type == 'product' ) $totals['product_summary'][$product_id]['product_page_views']++;
                if ( $event_type == 'product_purchase' ) {
                    $totals['product_summary'][$product_id]['transactions']++;
                    $totals['product_summary'][$product_id]['total_value'] += $event_value;
                    $totals['product_summary'][$product_id]['qty_purchased'] += $event_quantity;                
                }

            }


        }

        // Some cleaning - All Events
        if ( is_array($totals['event_summary']) && ! empty($totals['event_summary']) ) {
            foreach( $totals['event_summary'] as $event_key => $event_data ) {
                $totals['event_summary'][$event_key]['user_count'] = count($event_data['user_count']);
                $totals['event_summary'][$event_key]['session_count'] = count($event_data['session_count']);
            }
        }
        // Some cleaning - Campaigns
        if ( is_array($totals['campaign_summary']) && ! empty($totals['campaign_summary']) ) {
            foreach( $totals['campaign_summary'] as $campaign_name => $campaign_data ) {
                $totals['campaign_summary'][$campaign_name]['user_count'] = count($campaign_data['user_count']);
                $totals['campaign_summary'][$campaign_name]['session_count'] = count($campaign_data['session_count']);
                $totals['campaign_summary'][$campaign_name]['conversion_rate'] = wpd_calculate_percentage( $totals['campaign_summary'][$campaign_name]['transactions'], $totals['campaign_summary'][$campaign_name]['session_count'], 2 );
            }
        }
        // Some cleaning - Campaigns
        if ( is_array($totals['acquisition_summary']) && ! empty($totals['acquisition_summary']) ) {
            foreach( $totals['acquisition_summary'] as $acquisition_channel => $acquisition_data ) {
                $totals['acquisition_summary'][$acquisition_channel]['user_count'] = count($acquisition_data['user_count']);
                $totals['acquisition_summary'][$acquisition_channel]['session_count'] = count($acquisition_data['session_count']);
                $totals['acquisition_summary'][$acquisition_channel]['channel_percent'] = wpd_calculate_percentage( $totals['acquisition_summary'][$acquisition_channel]['session_count'], $totals['sessions'], 2 );
                $totals['acquisition_summary'][$acquisition_channel]['conversion_rate'] = wpd_calculate_percentage( $totals['acquisition_summary'][$acquisition_channel]['transactions'], $totals['sessions'], 2 );
            }
        }
        // Some cleaning - Products
        if ( is_array($totals['product_summary']) && ! empty($totals['product_summary']) ) {
            $product_impression_data = $this->fetch_product_impressions();
            $product_impression_data = $product_impression_data['organised_data'];
            foreach( $totals['product_summary'] as $product_id => $product_data ) {
                if ( isset($product_impression_data[$product_id]) ) $totals['product_summary'][$product_id]['category_impressions'] = $product_impression_data[$product_id];
                $totals['product_summary'][$product_id]['user_count'] = count($product_data['user_count']);
                $totals['product_summary'][$product_id]['session_count'] = count($product_data['session_count']);
                $totals['product_summary'][$product_id]['ctr_percent'] = wpd_calculate_percentage( $totals['product_summary'][$product_id]['product_clicks'], $totals['product_summary'][$product_id]['category_impressions'], 2 );
                $totals['product_summary'][$product_id]['conversion_rate'] = wpd_calculate_percentage( $totals['product_summary'][$product_id]['transactions'], $totals['sessions'], 2 );
                $totals['product_summary'][$product_id]['add_to_cart_per_session'] = wpd_calculate_percentage( $totals['product_summary'][$product_id]['add_to_cart'], $totals['sessions'], 2 );
            }
        }
        // Some cleaning - Page View Data
        if ( is_array($totals['page_view_summary']) && ! empty($totals['page_view_summary']) ) {
            foreach($totals['page_view_summary'] as $page_view_href => $page_data) {
                $totals['page_view_summary'][$page_view_href]['session_count'] = count( $page_data['session_count'] );
                $totals['page_view_summary'][$page_view_href]['user_count'] = count( $page_data['user_count'] );
            }
            $totals['page_view_summary'] = wpd_sort_multi_level_array($totals['page_view_summary'], 'session_count');
        }
        // Some cleaning - Landing Page Data
        if ( is_array($totals['landing_page_summary']) && ! empty($totals['landing_page_summary']) ) {
            foreach($totals['landing_page_summary'] as $page_view_href => $page_data) {
                $totals['landing_page_summary'][$page_view_href]['session_count'] = count( $page_data['session_count'] );
                $totals['landing_page_summary'][$page_view_href]['user_count'] = count( $page_data['user_count'] );
            }
            $totals['landing_page_summary'] = wpd_sort_multi_level_array($totals['landing_page_summary'], 'session_count');
        }
        // Some cleaning - Referral URL Data
        if ( is_array($totals['referral_url_summary']) && ! empty($totals['referral_url_summary']) ) {
            foreach($totals['referral_url_summary'] as $page_view_href => $page_data) {
                $totals['referral_url_summary'][$page_view_href]['session_count'] = count( $page_data['session_count'] );
                $totals['referral_url_summary'][$page_view_href]['user_count'] = count( $page_data['user_count'] );
            }
            $totals['referral_url_summary'] = wpd_sort_multi_level_array($totals['referral_url_summary'], 'session_count');
        }

        // Do total calculations
        $number_of_days = $this->data_by_date_containers['n_days_period'];
        $totals['average_session_duration'] = wpd_divide( $totals['session_duration'], $totals['sessions'], 2 );
        $totals['calculations']['sessions_per_day'] = wpd_divide( $totals['sessions'], $number_of_days, 2 );
        $totals['calculations']['users_per_day'] = wpd_divide( $totals['users'], $number_of_days, 2 );
        $totals['calculations']['page_views_per_session'] = wpd_divide( $totals['page_views'], $totals['sessions'], 2 );
        $totals['calculations']['events_per_session'] = wpd_divide( $totals['non_page_view_events'], $totals['sessions'], 2 );
        $totals['calculations']['percent_sessions_with_category_view'] = wpd_calculate_percentage( $totals['sessions_with_category_page_views'], $totals['sessions'], 2 );
        $totals['calculations']['percent_sessions_with_product_page_view'] = wpd_calculate_percentage( $totals['sessions_with_product_page_views'], $totals['sessions'], 2 );
        $totals['calculations']['percent_sessions_with_add_to_cart'] = wpd_calculate_percentage( $totals['sessions_with_add_to_cart'], $totals['sessions'], 2 );
        $totals['calculations']['percent_sessions_with_initiate_checkout'] = wpd_calculate_percentage( $totals['sessions_with_initiate_checkout'], $totals['sessions'], 2 );
        $totals['calculations']['conversion_rate'] = wpd_calculate_percentage( $totals['transactions'], $totals['sessions'], 2 );

        // Configure return object
        $analytics_data = array(
            'totals' => $totals,
            'data_by_date' => $data_by_date,
            'organised_data' => $organised_data
        );

        // Store the data into the prop
        $this->set_data( 'analytics', $analytics_data );

        return $analytics_data;

    }

    /**
     *
     *  Fetches product impressions from category page, used to supplement the analytics data.
     *  @see get_gmt_from_date() for converting input date
     *  @link https://developer.wordpress.org/reference/functions/get_gmt_from_date/
     *
     */
    public function fetch_product_impressions() {

        global $wpdb;

        // Collect Vars
        $wpd_db = new WPD_Database_Interactor();
        $product_impressions_table = $wpd_db->product_impressions_table;

        $return_array = array();

        // Apply filters if required
        $where_clause = '';
        $filters = $this->filter;
        
        if ( isset($filters['start_date']) && isset($filters['end_date']) ) {

            $start_date = get_gmt_from_date( $filters['start_date'] );
            $end_date = get_gmt_from_date( $filters['end_date'] );

            // Add 1 day to end date, so that it's inclusive of actual date
            $end_date = date("Y-m-d H:i:s", strtotime('+24 hours', strtotime($end_date)));

            $where_clause .= ' AND product_impressions.date_created_gmt >= \'' . $start_date . '\'';
            $where_clause .= ' AND product_impressions.date_created_gmt <= \'' . $end_date . '\'';

        }

        // Prepare query
        $sql_query =
            "SELECT 
            product_impressions.product_id as product_id,
            count(product_id) as impressions
            FROM $product_impressions_table AS product_impressions
            WHERE 1=1
            $where_clause
            GROUP BY product_id";

        // Fetch Results
        $results = $wpdb->get_results( $sql_query, 'ARRAY_A' );

        // Log an error
        if ( $wpdb->last_error  ) {
            wpd_write_log( 'Error capturing product impressions data from DB, dumping the error and query.', 'db_error' );
            wpd_write_log( $wpdb->last_error, 'db_error' );
            wpd_write_log( $wpdb->last_query, 'db_error' );
            $this->set_error( $wpdb->last_error );
            return false;
        }

        // Get count of records from query
        $record_count = (is_array($results)) ? count($results) : 0;

        // Store the found data
        $this->set_data( 'product_impressions', array('raw_data' => $results) );
        $this->set_data( 'product_impressions', array('total_db_records' => $record_count) );

        // Re Organise the array
        if ( is_array($results) && ! empty($results) ) {

            // Cleanup
            foreach( $results as $row ) {
                $return_array[$row['product_id']] = $row['impressions'];
            }

            // Store the organised data
            $this->set_data( 'product_impressions', array('organised_data' => $return_array) );
        }

        // Return results
        return $this->get_data( 'product_impressions' );

    }

    /**
     * 
     *  Collects count of sessions
     * 
     *  Will take on board filters for session_id, ip_address, user_id and dates 
     * 
     **/
    public function fetch_session_count() {

        global $wpdb;

        // Collect Vars
        $wpd_db = new WPD_Database_Interactor();
        $session_data_table = $wpd_db->session_data_table;

        // Apply filters if required
        $where_clause = '';
        $filters = $this->filter;

        if ( isset($filters['start_date']) && isset($filters['end_date']) ) {

            $start_date = get_gmt_from_date( $filters['start_date'] );
            $end_date = get_gmt_from_date( $filters['end_date'] );

            // Add 1 day to end date, so that it's inclusive of actual date
            $end_date = date("Y-m-d H:i:s", strtotime('+24 hours', strtotime($end_date)));

            $where_clause .= ' AND date_created_gmt >= \'' . $start_date . '\'';
            $where_clause .= ' AND date_created_gmt <= \'' . $end_date . '\'';

        }

        // Single session ID defined
        if ( isset($filters['session_id']) && ! empty($filters['session_id']) && is_string($filters['session_id']) ) {
            $where_clause .= ' AND session_id = \'' . $filters["session_id"] . '\'';
        }

        // Single user_id
        if ( isset($filters['user_id']) && ! empty($filters['user_id']) && is_numeric($filters['user_id']) ) {
            $where_clause .= ' AND user_id = ' . $filters["user_id"] . '';
        }

        // Single ip_address
        if ( isset($filters['ip_address']) && ! empty($filters['ip_address']) && is_string($filters['ip_address']) ) {
            $where_clause .= ' AND ip_address = \'' . $filters["ip_address"] . '\'';
        }

        // Prepare query
        $sql_query = 
            "SELECT COUNT(*)
            FROM $session_data_table
            WHERE 1=1
            $where_clause";

        // Fetch Results
        $results = $wpdb->get_var( $sql_query );

        // Error
        if ( $wpdb->last_error  ) {
            wpd_write_log( 'Error capturing analytics data from DB, dumping the error and query.', 'db_error' );
            wpd_write_log( $wpdb->last_error, 'db_error' );
            wpd_write_log( $wpdb->last_query, 'db_error' );
            return 0;
        }
        
        // Return results
        if ( is_numeric($results) ) {
            return $results;
        } else {
            return 0;
        }

    }

    /**
     * 
     * Collects all product analytics for a given product id(s)
     * 
     * @param int $product_id = Product ID to search for in query
     * @return int $product_analytics
     * 
     */
    public function fetch_product_statistic( $product_statistic = null, $product_id = 0 ) {

        // First, search for filter's product id
        if ( $product_id === 0 ) {
            // Product ID not set, lets check the filter
            if ( isset($this->filter['product_id']) && is_int($this->filter['product_id']) && $this->filter['product_id'] > 0 ) {
                $product_id = $this->filter['product_id'];
            } else {
                return 0; // Return the default
            }
        }

        // First, search for filter's product id
        if ( $product_statistic === null ) {
            // Product ID not set, lets check the filter
            if ( isset($this->filter['fetch_product_stat'])  && ! empty( $this->filter['fetch_product_stat'] ) ) {
                $product_statistic = $this->filter['fetch_product_stat'];
            } else {
                return 0; // Return the default
            }
        }

        global $wpdb;

        // Collect Vars
        $wpd_db = new WPD_Database_Interactor();
        $product_impressions_table = $wpd_db->product_impressions_table;
        $events_table = $wpd_db->events_table;

        $return_array = array();

        // Apply filters if required
        $where_clause = '';
        $filters = $this->filter;
        
        if ( isset($filters['start_date']) && isset($filters['end_date']) ) {

            $start_date = get_gmt_from_date( $filters['start_date'] );
            $end_date = get_gmt_from_date( $filters['end_date'] );

            // Add 1 day to end date, so that it's inclusive of actual date
            $end_date = date("Y-m-d H:i:s", strtotime('+24 hours', strtotime($end_date)));

            $where_clause .= ' AND date_created_gmt >= \'' . $start_date . '\'';
            $where_clause .= ' AND date_created_gmt <= \'' . $end_date . '\'';

        }

        // Already checked to make sure theres a product id > 0
        $where_clause .= ' AND product_id = \'' . $product_id . '\'';

        if ( $product_statistic == 'product_impression' ) {

            // Prepare query
            $sql_query = 
                "SELECT count(*)
                FROM $product_impressions_table
                WHERE 1=1
                $where_clause";

            // Fetch Results
            $results = (int) $wpdb->get_var( $sql_query );

            if ( $wpdb->last_error  ) {
                wpd_write_log( 'Error collecting product statistic from DB, dumping the error and query.', 'db_error' );
                wpd_write_log( $wpdb->last_error, 'db_error' );
                wpd_write_log( $wpdb->last_query, 'db_error' );
            }

            return $results;

        } else { // Assuming all other events are in the events table

            $where_clause .= ' AND event_type = \'' . $product_statistic . '\'';

            // Prepare query
            $sql_query = 
                "SELECT count(*)
                FROM $events_table
                WHERE 1=1
                $where_clause";

            // Fetch Results
            $results = (int) $wpdb->get_var( $sql_query );

            if ( $wpdb->last_error  ) {
                wpd_write_log( 'Error collecting product statistic from DB, dumping the error and query.', 'db_error' );
                wpd_write_log( $wpdb->last_error, 'db_error' );
                wpd_write_log( $wpdb->last_query, 'db_error' );
            }

            return $results;

        }

    }

    /**
     *
     *  Returns date in format for the date container
     *
     */
    private function reformat_date_to_date_format( $date ) {

        $date_container_date_format = $this->data_by_date_containers['date_format'];
        $formatted_date = date( $date_container_date_format, strtotime($date) );

        return $formatted_date;

    }

    /**
     *
     *  Calculates difference in seconds between two dates - used for session duration
     *
     */
    private function calculate_difference_in_seconds( $recent_date, $old_date ) {

        if ( is_null($recent_date) || is_null($old_date) ) {
            return 0;
        }

        $recent_date_string = strtotime($recent_date);
        $old_date_string    = strtotime($old_date);
        (int) $difference_in_seconds = $recent_date_string - $old_date_string;

        return $difference_in_seconds;

    }

    /**
     *
     *  Checks for Query Parameters and returns as associated array if found
     *
     */
    private function get_url_components( $url ) {

        $result = array(
            'url' => $url,
            'path' => null,
            'query_parameters' => array()
        );

        if ( is_null($url) ) return $result;

        // Prevents issues with 038;
        $url = htmlspecialchars_decode( $url );
        $result['url'] = $url;

        $parsed_url = parse_url( $url );

        if ( isset($parsed_url['path']) && ! empty($parsed_url['path']) ) {
            $result['path'] = $parsed_url['path'];
        }

        // Only collect query params
        $query_parameters = parse_url( $url, PHP_URL_QUERY );

        if ( ! empty($query_parameters) ) {

            $result['decoded_qp'] = $query_parameters;
            parse_str( $query_parameters, $result['query_parameters'] );

        }

        return $result;

    }

    /**
     *
     *  Calculate traffic source type
     *
     */
    private function determine_traffic_source( $referral_url, $query_parameters = null ) {

        $traffic_type = new WPD_Traffic_Type( $referral_url, $query_parameters );
        return $traffic_type->determine_traffic_source();

    }

    /**
     * 
     *  Logs general messages and optionally errors to:
     *  wpd_google_ads_api_log.txt and wpd_google_ads_api_error_log.txt
     * 
     *  @param string|array|WP_Error $message the content to print to the log
     *  @param bool $error Set to true if you want to log this to the error log in addition to the general api log
     *  @return void
     * 
     **/
    private function log( $message, $error = false ) {

        // Setup backtrace
        $backtrace = debug_backtrace();
        $last_call = $backtrace[1]['function'];
        if ( $last_call == 'set_error' ) $last_call = $backtrace[2]['function'];

        if ( $error ) {

            // WP Error
            if ( is_a( $message, 'WP_Error' ) ) {
                $error_message = (string) $message->get_error_message();
            }

            // Array
            if ( is_array($message) || is_object($message) ) {
                $error_message = (string) json_encode( $message );
            }

            // Confirm string
            if ( is_string($message) ) {
                $error_message = (string) $message;
            }

            // Save the errors to instance
            if ( $error_message ) {
                $this->errors[] = $error_message;
            }

            // Log errors
            wpd_write_log( 'Backtrace function: ' . $last_call, 'data_warehouse_error' );
            wpd_write_log( $message, 'data_warehouse_error' );
            
        }

        // Log the message
        wpd_write_log( $message, 'data_warehouse' );

        return $message;

    }

}
				
			

Got A Question?

Subscribe
Notify of
guest
0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Alpha Insights

Alpha Insights

The World's Most Advanced WooCommerce Reporting Plugin.

WooCommerce Cost Of Goods Order Profit Reports