Yoast_GA_Dashboards_Collector

Dashboards collector.

Defined (1)

The class is defined in the following location(s).

/admin/dashboards/class-admin-dashboards-collector.php  
  1. class Yoast_GA_Dashboards_Collector { 
  2.  
  3. /** 
  4. * @var array $active_metrics Store the active metrics 
  5. */ 
  6. public $active_metrics; 
  7.  
  8. /** 
  9. * Store the dimensions 
  10. * @var array 
  11. */ 
  12. private $dimensions = array(); 
  13.  
  14. /** 
  15. * Store the valid metrics, which should be 
  16. * @var array 
  17. */ 
  18. private $valid_metrics = array(); 
  19.  
  20. /** 
  21. * @var integer $ga_profile_id Store the GA Profile ID 
  22. */ 
  23. public $ga_profile_id; 
  24.  
  25. /** 
  26. * Construct on the dashboards class for GA 
  27. * @param int $ga_profile_id 
  28. * @param array $active_metrics 
  29. * @param array $valid_metrics 
  30. */ 
  31. public function __construct( $ga_profile_id, $active_metrics, $valid_metrics ) { 
  32. $this->ga_profile_id = $ga_profile_id; 
  33.  
  34. $active_metrics = $this->filter_metrics_to_dimensions( $active_metrics ); 
  35. $this->active_metrics = $active_metrics; 
  36.  
  37. add_filter( 'ga_dashboards_dimensions', array( $this, 'filter_dimensions' ), 10, 1 ); 
  38.  
  39. $this->options = Yoast_GA_Dashboards_Api_Options::get_instance(); 
  40.  
  41. $this->init_shutdown_hook(); 
  42.  
  43. /** 
  44. * Fetch the data from Google Analytics and store it 
  45. */ 
  46. public function aggregate_data() { 
  47. if ( is_numeric( $this->ga_profile_id ) ) { 
  48. // ProfileID is set 
  49.  
  50. /** 
  51. * Implement the metric data first 
  52. */ 
  53. if ( is_array( $this->active_metrics ) && count( $this->active_metrics ) >= 1 ) { 
  54. $this->aggregate_metrics( $this->active_metrics ); 
  55.  
  56. /** 
  57. * Now implement the dimensions that are set 
  58. */ 
  59. if ( is_array( $this->dimensions ) && count( $this->dimensions ) >= 1 ) { 
  60. $this->aggregate_dimensions( $this->dimensions ); 
  61. else { 
  62. // Failure on authenticating, please reauthenticate 
  63.  
  64. /** 
  65. * This hook runs on the shutdown to fetch data from GA 
  66. */ 
  67. private function init_shutdown_hook() { 
  68. // Hook the WP cron event 
  69. add_action( 'wp', array( $this, 'setup_wp_cron_aggregate' ) ); 
  70.  
  71. // Hook our function to the WP cron event the fetch data daily 
  72. add_action( 'yst_ga_aggregate_data', array( $this, 'aggregate_data' ) ); 
  73.  
  74. // Check if the WP cron did run on time 
  75. if ( filter_input( INPUT_GET, 'page' ) === 'yst_ga_dashboard' ) { 
  76. add_action( 'shutdown', array( $this, 'check_api_call_hook' ) ); 
  77.  
  78. /** 
  79. * Check if we scheduled the WP cron event, if not, do so. 
  80. */ 
  81. public function setup_wp_cron_aggregate() { 
  82. if ( ! wp_next_scheduled( 'yst_ga_aggregate_data' ) ) { 
  83. // Set the next event of fetching data 
  84. wp_schedule_event( strtotime( date( 'Y-m-d', strtotime( 'tomorrow' ) ) . ' 00:05:00 ' ), 'daily', 'yst_ga_aggregate_data' ); 
  85.  
  86. /** 
  87. * Check if the WP cron did run yesterday. If not, we need to run it form here 
  88. */ 
  89. public function check_api_call_hook() { 
  90. $last_run = $this->get_last_aggregate_run(); 
  91.  
  92.  
  93. /** 
  94. * Transient doesn't exists, so we need to run the 
  95. * hook (This function runs already on Shutdown so 
  96. * we can call it directly from now on) or the last run has ben more than 24 hours 
  97. */ 
  98. if ( $last_run === false || Yoast_GA_Utils::hours_between( strtotime( $last_run ), time() ) >= 24 ) { 
  99. $this->aggregate_data(); 
  100.  
  101.  
  102. /** 
  103. * Get the datetime when the aggregate data function was succesful 
  104. * @return mixed 
  105. */ 
  106. private function get_last_aggregate_run() { 
  107. return get_option( 'yst_ga_last_wp_run' ); 
  108.  
  109. /** 
  110. * Remove metrics and set them as a dimension if needed 
  111. * @param array $metrics 
  112. * @return mixed 
  113. */ 
  114. private function filter_metrics_to_dimensions( $metrics ) { 
  115. $filter_metrics = $this->get_filter_metrics(); 
  116.  
  117. foreach ( $metrics as $key => $metric_name ) { 
  118. if ( isset( $filter_metrics[ $metric_name ] ) ) { 
  119. // Add and set the dimension 
  120. $dimension = array( $filter_metrics[ $metric_name ] ); 
  121. $this->dimensions = array_merge( $this->dimensions, $dimension ); 
  122.  
  123. // Remove it from the metrics after we've added it into dimensions 
  124. unset( $metrics[ $key ] ); 
  125.  
  126. return $metrics; 
  127.  
  128. /** 
  129. * Get array with metrics which we need to filter as a dimension 
  130. * @return array 
  131. */ 
  132. private function get_filter_metrics() { 
  133. return array( 
  134. 'source' => array( 
  135. 'metric' => 'sessions',  
  136. 'dimension' => 'source',  
  137. 'storage_name' => 'source',  
  138. ),  
  139. 'top_pageviews' => array( 
  140. 'metric' => 'pageViews',  
  141. 'dimension' => 'pagePath',  
  142. 'storage_name' => 'top_pageviews',  
  143. ),  
  144. 'top_countries' => array( 
  145. 'metric' => 'sessions',  
  146. 'dimension' => 'country',  
  147. 'storage_name' => 'top_countries',  
  148. ),  
  149. ); 
  150.  
  151. /** 
  152. * Filter function for adding dimensions 
  153. * @filter ga_dashboards_dimensions 
  154. * @param array $dimensions 
  155. * @return array 
  156. */ 
  157. public function filter_dimensions( $dimensions = array() ) { 
  158. if ( is_array( $dimensions ) && count( $dimensions ) >= 1 ) { 
  159. $dimensions = array_merge( $this->dimensions, $dimensions ); 
  160. $this->dimensions = $dimensions; 
  161.  
  162. return $this->dimensions; 
  163.  
  164. /** 
  165. * Get the start and and date for aggregation functionality 
  166. * @return array 
  167. */ 
  168. private function get_date_range() { 
  169. /** 
  170. * Filter: 'yst-ga-filter-api-end-date' - Allow people to change the end date for the dashboard 
  171. * data. Default: yesterday. 
  172. * @api string Date (Y-m-d) 
  173. */ 
  174. return array( 
  175. 'start' => date( 'Y-m-d', strtotime( '-1 month' ) ),  
  176. 'end' => apply_filters( 'yst-ga-filter-api-end-date', date( 'Y-m-d', strtotime( 'yesterday' ) ) ),  
  177. ); 
  178.  
  179. /** 
  180. * Aggregate metrics from GA. This function should be called in the shutdown function. 
  181. * @param array $metrics 
  182. */ 
  183. private function aggregate_metrics( $metrics ) { 
  184. $dates = $this->get_date_range(); 
  185.  
  186. foreach ( $metrics as $metric ) { 
  187. $this->execute_call( $metric, $dates['start'], $dates['end'] ); 
  188.  
  189. /** 
  190. * Aggregate dimensions from GA. This function should be called in the shutdown function. 
  191. * @param array $dimensions 
  192. */ 
  193. private function aggregate_dimensions( $dimensions ) { 
  194. $dates = $this->get_date_range(); 
  195.  
  196. foreach ( $dimensions as $dimension ) { 
  197. if ( isset( $dimension['metric'] ) ) { 
  198. if ( isset( $dimension['id'] ) ) { 
  199. $this->execute_call( $dimension['metric'], $dates['start'], $dates['end'], 'ga:dimension' . $dimension['id'] ); 
  200. elseif ( isset( $dimension['dimension'] ) ) { 
  201. if ( isset( $dimension['storage_name'] ) ) { 
  202. $this->execute_call( $dimension['metric'], $dates['start'], $dates['end'], 'ga:' . $dimension['dimension'], $dimension['storage_name'] ); 
  203. else { 
  204. $this->execute_call( $dimension['metric'], $dates['start'], $dates['end'], 'ga:' . $dimension['dimension'] ); 
  205.  
  206. /** 
  207. * Execute an API call to Google Analytics and store the data in the dashboards data class 
  208. * @param string $metric 
  209. * @param string $start_date 2014-10-16 
  210. * @param string $end_date 2014-11-20 
  211. * @param string $dimensions ga:date 
  212. * @param string $storage_name auto 
  213. * @return bool 
  214. */ 
  215. private function execute_call( $metric, $start_date, $end_date, $dimensions = 'ga:date', $storage_name = 'auto' ) { 
  216. $dimensions = $this->prepare_dimensions( $dimensions, $metric ); 
  217. $params = $this->build_params_for_call( $start_date, $end_date, $dimensions, $metric ); 
  218. $storage_type = $this->get_storage_type( $dimensions ); 
  219.  
  220. $response = Yoast_Google_Analytics::get_instance()->do_request( 'https://www.googleapis.com/analytics/v3/data/ga?' . $params ); 
  221.  
  222. if ( isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) { 
  223.  
  224. // Delete option api_fail because there it's successful now 
  225. delete_option( 'yst_ga_api_call_fail' ); 
  226.  
  227. // Success, set a transient which stores the latest runtime 
  228. update_option( 'yst_ga_last_wp_run', date( 'Y-m-d' ) ); 
  229.  
  230. $response = Yoast_Googleanalytics_Reporting::get_instance()->parse_response( $response, $storage_type, $start_date, $end_date ); 
  231. else { 
  232. // When response is failing, we should count the number of 
  233. $this->save_api_failure(); 
  234.  
  235. return false; 
  236.  
  237. if ( strpos( 'ga:date', $dimensions ) !== false ) { 
  238. return $this->handle_response( $response, $metric, $dimensions, $start_date, $end_date, 'datelist', $storage_name ); 
  239. else { 
  240. return $this->handle_response( $response, $metric, $dimensions, $start_date, $end_date, 'table', $storage_name ); 
  241.  
  242. /** 
  243. * When the API isn't able to get a successful response (code 200), we have to save that the call has failed 
  244. */ 
  245. private function save_api_failure() { 
  246. update_option( 'yst_ga_api_call_fail', true ); 
  247.  
  248. /** 
  249. * Get the storage type from dimensions 
  250. * @param string $dimensions 
  251. * @return string 
  252. */ 
  253. private function get_storage_type( $dimensions ) { 
  254. if ( strpos( 'ga:date', $dimensions ) !== false ) { 
  255. return 'datelist'; 
  256. else { 
  257. return 'table'; 
  258.  
  259. /** 
  260. * Prepare dimensions before adding them as a parameter in a call 
  261. * @param array $dimensions 
  262. * @param array $metric 
  263. * @return array 
  264. */ 
  265. private function prepare_dimensions( $dimensions, $metric ) { 
  266. $filter_metrics = $this->get_filter_metrics(); 
  267.  
  268. // Check if the dimensions param is an array, if so, glue it with implode to a comma separated string. 
  269. if ( is_array( $dimensions ) ) { 
  270. $dimensions = implode( ', ', $dimensions ); 
  271.  
  272. if ( in_array( $metric, $this->valid_metrics ) ) { 
  273. $dimensions = 'ga:date, ' . $dimensions; 
  274. elseif ( isset( $filter_metrics[ str_replace( 'ga:', '', $dimensions ) ] ) ) { 
  275. // Make sure we don't have a ga:date property here 
  276. $dimensions = str_replace( 'ga:date', '', $dimensions ); 
  277.  
  278. return $dimensions; 
  279.  
  280. /** 
  281. * Build the params for a call to Google Analytics, return them prepared for a http query 
  282. * @param string $start_date 
  283. * @param string $end_date 
  284. * @param string $dimensions 
  285. * @param string $metric 
  286. * @return string 
  287. */ 
  288. private function build_params_for_call( $start_date, $end_date, $dimensions, $metric ) { 
  289. /** 
  290. * Filter: 'yst-ga-filter-api-limit' - Allow people to change the max results value in the API 
  291. * calls. Default value is 1000 results per call. 
  292. * @api int 1000 
  293. */ 
  294. $api_call_limit = apply_filters( 'yst-ga-filter-api-limit', 1000 ); 
  295.  
  296. $params = array( 
  297. 'ids' => 'ga:' . $this->ga_profile_id,  
  298. 'start-date' => $start_date,  
  299. 'end-date' => $end_date,  
  300. 'dimensions' => $dimensions,  
  301. 'metrics' => 'ga:' . $metric,  
  302. 'max-results' => $api_call_limit,  
  303. ); 
  304.  
  305. $params = $this->add_sort_direction( $params, $dimensions, $metric ); 
  306. $params = http_build_query( $params ); 
  307.  
  308. return $params; 
  309.  
  310. /** 
  311. * Add a sort direction if we need to (Especially on dimensions which are 
  312. * listed in $this->get_filter_metrics()) 
  313. * @param array $params 
  314. * @param string $dimensions 
  315. * @param string $metric 
  316. * @return array 
  317. */ 
  318. private function add_sort_direction( $params, $dimensions, $metric ) { 
  319. $filter_dimensions = $this->get_filter_metrics(); 
  320.  
  321. foreach ( $filter_dimensions as $dimension ) { 
  322. if ( str_replace( 'ga:', '', $dimensions ) == $dimension['dimension'] && str_replace( 'ga:', '', $metric ) == $dimension['metric'] ) { 
  323. $params['sort'] = '-ga:' . $dimension['metric']; 
  324.  
  325. return $params; 
  326.  
  327. /** 
  328. * Handle the response from the Google Analytics api. 
  329. * @param array|boolean $response 
  330. * @param string $metric 
  331. * @param array $dimensions 
  332. * @param string $start_date 
  333. * @param string $end_date 
  334. * @param string $store_as 
  335. * @param string $storage_name 
  336. * @return bool 
  337. */ 
  338. private function handle_response( $response, $metric, $dimensions, $start_date, $end_date, $store_as = 'table', $storage_name = 'auto' ) { 
  339. if ( is_array( $response ) ) { 
  340. // Success, store this data 
  341. $filter_metrics = $this->get_filter_metrics(); 
  342. $extracted = str_replace( 'ga:', '', str_replace( 'ga:date, ', '', $dimensions ) ); 
  343.  
  344. if ( isset( $filter_metrics[ $extracted ] ) ) { 
  345. $name = $extracted; 
  346. else { 
  347. $name = $metric; 
  348.  
  349. if ( $dimensions !== 'ga:date' && ! isset( $filter_metrics[ $extracted ] ) ) { 
  350. $name = str_replace( 'ga:date, ', '', $dimensions ); 
  351.  
  352. // Overwrite the name if we have a defined one 
  353. if ( $storage_name != 'auto' ) { 
  354. $name = $storage_name; 
  355.  
  356. return Yoast_GA_Dashboards_Data::set( $name, $response, strtotime( $start_date ), strtotime( $end_date ), $store_as ); 
  357. else { 
  358. // Failure on API call try to log it 
  359. $this->log_error( print_r( $response, true ) ); 
  360.  
  361. return false; 
  362.  
  363. /** 
  364. * Log an error while calling the Google Analytics API 
  365. * @param string $error 
  366. */ 
  367. private function log_error( $error ) { 
  368. if ( true == WP_DEBUG ) { 
  369. if ( function_exists( 'error_log' ) ) { 
  370. error_log( 'Google Analytics by MonsterInsights (Dashboard API): ' . $error ); 
  371.