api_url = $savvyext->cfgReadChar('sendgrid.api_url'); $this->key = $savvyext->cfgReadChar('sendgrid.api_key'); // connect replica database $db_host = $savvyext->cfgReadChar('database_replica.host'); $db_name = $savvyext->cfgReadChar('database_replica.name'); $db_user = $savvyext->cfgReadChar('database_replica.user'); $db_pass = $savvyext->cfgReadChar('database_replica.pass'); $db_port = $savvyext->cfgReadLong('database_replica.port'); $db_conn = "host=${db_host} port=${db_port} dbname=${db_name} user=${db_user} password=${db_pass}"; $this->replica_conn = pg_connect($db_conn); // get range of days to retrieve data $this->_2week = self::getDaysOfWeek( $week = -2 ); $this->_1week = self::getDaysOfWeek( $week = -1 ); // sendgrid api $this->sendgrid_api = $savvyext->cfgReadChar('mailsend.api_key'); $this->sendgrid_from = $savvyext->cfgReadChar('mailsend.from'); $this->sendgrid_name = $savvyext->cfgReadChar('mailsend.name'); } /** * Write errors log * @param string $action action name * @param string $message error message * @return */ private static function logs( $message ) { $log = 'Date: ' .date("F j, Y, g:i a").PHP_EOL. 'Attempt: '.PHP_EOL. sprintf('%s', is_array($message) ? json_encode($message): $message).PHP_EOL. '-------------------------'.PHP_EOL; file_put_contents( './logs/log_'.date('j.n.Y').'log', $log, FILE_APPEND ); } /** * Return range days from monday to sunday of week * @param number $weekOffset number of week * @return array */ private static function getDaysOfWeek( $weekOffset ) { $date = new DateTime(); $date->setIsoDate( $date->format('o'), $date->format('W') + $weekOffset ); return array( 'monday' => $date->format('Y-m-d'), 'sunday' => $date->modify('+6 day')->format('Y-m-d') ); } /** * Separation array by range of dates * @param array $datas list data need to be separation * @param string $start start date * @param string $end end date * @return array new arrays */ private static function separationByDates( $datas, $start, $end ) { $new_array = array(); foreach ( $datas as $key => $data ) { if ( $start <= $data['dates'] and $data['dates'] <= $end ) { array_push( $new_array, $data ); unset( $datas[$key] ); } } return array( 'old_array' => $datas, 'new_array' => $new_array ); } /** * Check the time to run the cronjob * @return bool */ private static function check_weekly_time() { $timestamp = time(); if ( date( 'D', $timestamp ) === 'Mon' ) { return true; } exit(); } /** * Prepare cURL to call SendGrid API * @param string|null $endpoint api endpoint * @param array $params api params * @return mixed */ protected function getSendgrid(string $endpoint = null, $params = array()) { // prepare url api $api_call = sprintf( '%s%s?%s', $this->api_url, $endpoint, http_build_query($params) ); $ch = curl_init($api_call); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_VERBOSE, false); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Authorization: ' . $this->key )); // check cURL error $error = curl_error($ch); if ($error) { throw new Exception('Response with errors: ' . is_array($error) ? json_decode($error) : $error ); } $response = json_decode( curl_exec($ch), true ); if (isset($response['errors'])) { $message = is_array($response['errors']) ? json_decode($response['errors']) : response['errors']; throw new Exception('Response with errors: ' . $message); } if (isset($results['results']) && !count($results['results'])) { throw new Exception('Response with empty data'); } return $response; } /** * execute pg query * @param string $query sql query * @return array results */ protected function execute_pg_query( $query ) { try { $do = pg_query( $this->replica_conn, $query ); if ( $errors = pg_last_error( $this->replica_conn ) ) { throw new Exception( $errors ); } $results = pg_fetch_all( $do ); if ( ! $results ) { throw new Exception( 'Results not found!' ); } // Separation results by dates $results = self::separationByDates( $results, $this->_1week['monday'], $this->_1week['sunday'] ); return $results; } catch (Exception $e) { self::logs('Errors', $e->getMessage()); return; } } /** * Generate chart image * @return string image src */ protected function generate_chart_images( $chartLabels, $chartValues ) { $chartConfig = array( 'type' => 'line', 'backgroundColor' => 'transparent', 'data' => array( 'labels' => $chartLabels, 'datasets' => array( array( 'label' => sprintf( 'from %s - to %s', $this->_2week['monday'], $this->_2week['sunday'] ), 'backgroundColor' => '#36A2EB', 'borderColor' => '#36A2EB', 'borderDash' => [5, 5], 'fill' => false, 'data' => array_values( $chartValues['old_value'] ) ), array( 'label' => sprintf( 'from %s - to %s', $this->_1week['monday'], $this->_1week['sunday'] ), 'backgroundColor' => '#F93784', 'borderColor' => '#F93784', 'fill' => false, 'data' => array_values( $chartValues['new_value'] ) ) ) ), 'options' => array( 'responsive' => true, 'plugins' => array( 'datalabels' => array( 'display' => true, 'align' => 'top', 'backgroundColor' => '#FFFFFF', 'borderRadius' => 3, 'borderWidth' => 1, 'borderColor' => '#B8B8B8', 'font' => array( 'size' => 8 ) ) ) ) ); $chartConfig = json_encode( $chartConfig ); $result = preg_replace( '/(")(function.*?})(")/m', '$2', $chartConfig); $imageUrl = 'https://quickchart.io/chart?w=500&h=200&c=' . urlencode($result); return $imageUrl; } /** * Get tracking installed os * @return array */ public function get_tracking_installed() { // prepare sql to retrieve data // by range days $query = sprintf(" SELECT md.created as dates, SUM(case when mdp.platform = 'Android' then 1 else 0 end) as Android, SUM(case when mdp.platform = 'iOS' then 1 else 0 end) as iOS FROM members_devices md LEFT JOIN members_device_platforms mdp ON md.platform_id = mdp.id WHERE md.created BETWEEN '%s' AND '%s' AND md.member_id IN (SELECT id FROM members WHERE status = 1) GROUP BY dates ", $this->_2week['monday'], $this->_1week['sunday']); // run query $results = $this->execute_pg_query( $query ); return $results; } /** * Get number users who subscribed the deals * @return int numbers */ public function get_subscribe_deals() { $query = sprintf(" SELECT mca.subscribe as dates, count(DISTINCT m.email) FROM members_card_assign mca LEFT JOIN main_cards mc ON mca.card_id = mc.id LEFT JOIN members m ON mca.member_id = m.id WHERE mca.subscribe IS NOT NULL AND mc.button1_action IN ('GOOFFERS') AND mca.subscribe BETWEEN '%s' AND '%s' GROUP BY dates ", $this->_2week['monday'], $this->_1week['sunday']); // run query $results = $this->execute_pg_query( $query ); return $results; } /** * Get number user who filled profile * @return int numbers */ public function get_user_filled_profile() { $query = sprintf(" SELECT greatest(m.added, mba.added, ot.created, md.created) as Dates, count(DISTINCT m.email) FROM members m INNER JOIN members_bank_accounts mba ON m.id = mba.member_id INNER JOIN oauth2_tokens ot ON m.id = ot.member_id INNER JOIN members_devices md on m.id = md.member_id WHERE ((m.max_budget <> 0) IS NOT FALSE AND mba.status = 1 AND md.player_id is not null) AND (greatest(m.added, mba.added, ot.created, md.created) BETWEEN '%s' AND '%s') GROUP BY Dates ", $this->_2week['monday'], $this->_1week['sunday'] ); // run query $results = $this->execute_pg_query( $query ); return $results; } /** * Get sendgrid tracking data * @return array properties of data */ public function get_sendgrid_tracking() { try { $params = array( 'aggregated_by' => 'week', 'start_date' => $this->_2week['monday'], 'end_date' => $this->_1week['sunday'] ); $results = $this->getSendgrid( $endpoint = 'stats', $params ); // check error api call if ( isset( $results['errors'] ) ) { $message = $results['errors'][0]['message']; throw new Exception( $message ); } $combineResults = array(); foreach ( $results as $key => $value ) { $combineResults[] = array( 'dates' => $value['date'], 'data' => $value['stats'][0]['metrics'] ); } $results = self::separationByDates( $combineResults, $this->_1week['monday'], $this->_1week['sunday'] ); return $results; } catch (Exception $e) { self::logs($action_name, $e->getMessage()); return; } } /** * BKO tracking data * @return string chart image */ public function bko_tracking() { // tracking install device $install = $this->get_tracking_installed(); // tracking user subscribe deals $subscribe = $this->get_subscribe_deals(); // tracking user filled profile $profile = $this->get_user_filled_profile(); $data = array( 'old_value' => array( 'android' => count( array_filter( $install['old_array'], function($e) {return $e['android'] > 0; } ) ), 'ios' => count( array_filter( $install['old_array'], function($e) {return $e['ios'] > 0; } ) ), 'subs' => count( $subscribe['old_array'] ), 'profile' => count( $profile['old_array'] ) ), 'new_value' => array( 'android' => count( array_filter( $install['new_array'], function($e) {return $e['android'] > 0; } ) ), 'ios' => count( array_filter( $install['new_array'], function($e) {return $e['ios'] > 0; } ) ), 'subs' => count( $subscribe['new_array'] ), 'profile' => count( $profile['new_array'] ) ) ); $labels = array( 'Android install', 'iOS install', 'Subscribe deals', 'Filled profile' ); // get chart image $imgSrc = $this->generate_chart_images( $labels, $data ); return $imgSrc; } /** * SendGrid tracking data * @return string chart image */ public function sendgrid_tracking() { $tracking = $this->get_sendgrid_tracking(); if ( ! $tracking ) { return; } $data = array(); foreach ( $tracking as $key => $item ) { $data[ $key ] = array( 'delivered' => array_sum( array_column( $item[0], 'delivered') ), 'processed' => array_sum( array_column( $item[0], 'processed') ), 'unique_clicks' => array_sum( array_column( $item[0], 'unique_clicks') ), 'unique_opens' => array_sum( array_column( $item[0], 'unique_opens') ), ); } $data = array( 'old_value' => $data['old_array'], 'new_value' => $data['new_array'] ); $labels = array( 'Delivered', 'Processed', 'Unique Clicks', 'Unique Opens' ); // get chart image $imgSrc = $this->generate_chart_images( $labels, $data ); return $imgSrc; } /** * Send custom email report * @return mixed */ public function sendEmail() { // check weekly report time self::check_weekly_time(); $bko_tracking = $this->bko_tracking(); $sendgrid_tracking = $this->sendgrid_tracking(); // include email template ob_start(); require_once('./email/custom-report.php'); $mail_html = ob_get_clean(); try { $email = new \SendGrid\Mail\Mail(); $email->setFrom( $this->sendgrid_from, $this->sendgrid_name ); $email->setSubject( 'Float Weekly Report' ); $toEmails = array( 'stew@float.sg' => 'Stew', 'olu@float.sg' => 'Olu', 'valeriy@float.sg' => 'Valeriy', 'tokslaw@float.sg' => 'Tokslaw', 'jeff@float.sg' => 'Jeff', 'chelsea@float.sg' => 'Chelsea' ); $email->addTos( $toEmails ); $email->addContent( 'text/html', $mail_html ); $sendgrid = new \SendGrid( $this->sendgrid_api ); $response = $sendgrid->send( $email ); if ( $response->statusCode() !== 202 ) { throw new Exception( 'Send custom report get errors' ); } echo 'Success'; } catch (Exception $e) { echo 'Custom report could not be sent. Mailer Error: ', $e->getMessage(); self::logs( 'Custom report could not be sent. Mailer Error: ', $e->getMessage() ); exit(); } } } $api = new Custom_Report(); $api->sendEmail();