mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 06:57:32 -04:00 
			
		
		
		
	working on email stats
This commit is contained in:
		
							parent
							
								
									3e81fc3925
								
							
						
					
					
						commit
						e97a35f916
					
				| @ -3,6 +3,7 @@ | ||||
| namespace App\Http\Controllers; | ||||
| 
 | ||||
| use App\Jobs\ExportReportResults; | ||||
| use App\Jobs\LoadPostmarkStats; | ||||
| use App\Jobs\RunReport; | ||||
| use App\Models\Account; | ||||
| use App\Models\ScheduledReport; | ||||
| @ -12,9 +13,6 @@ use Utils; | ||||
| use View; | ||||
| use Carbon; | ||||
| use Validator; | ||||
| use stdClass; | ||||
| use DateInterval; | ||||
| use DatePeriod; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
| @ -189,67 +187,8 @@ class ReportController extends BaseController | ||||
| 
 | ||||
|     public function loadEmailReport($startDate, $endDate) | ||||
|     { | ||||
|         $account = auth()->user()->account; | ||||
|         $startDate = date_create($startDate); | ||||
|         $endDate = date_create($endDate); | ||||
|         $postmark = new \Postmark\PostmarkClient(config('services.postmark')); | ||||
|         $obj = new stdClass; | ||||
|         $data = dispatch(new LoadPostmarkStats($startDate, $endDate)); | ||||
| 
 | ||||
|         $eventTypes = ['sent', 'opened']; | ||||
| 
 | ||||
|         foreach ($eventTypes as $eventType) { | ||||
|             $data = []; | ||||
|             $endDate->modify('+1 day'); | ||||
|             $interval = new DateInterval('P1D'); | ||||
|             $period = new DatePeriod($startDate, $interval, $endDate); | ||||
|             $endDate->modify('-1 day'); | ||||
|             $records = []; | ||||
| 
 | ||||
|             if ($eventType == 'sent') { | ||||
|                 $response = $postmark->getOutboundSendStatistics(null, request()->start_date, request()->end_date); | ||||
|             } else { | ||||
|                 $response = $postmark->getOutboundOpenStatistics(null, request()->start_date, request()->end_date); | ||||
|             } | ||||
| 
 | ||||
|             foreach ($response->days as $key => $val) { | ||||
|                 $field = $eventType == 'opened' ? 'unique' : $eventType; | ||||
|                 $data[$val['date']] = $val[$field]; | ||||
|             } | ||||
| 
 | ||||
|             foreach ($period as $day) { | ||||
|                 $date = $day->format('Y-m-d'); | ||||
|                 $records[] = isset($data[$date]) ? $data[$date] : 0; | ||||
| 
 | ||||
|                 if ($eventType == 'sent') { | ||||
|                     $labels[] = $day->format('m/d/Y'); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if ($eventType == 'sent') { | ||||
|                 $color = '51,122,183'; | ||||
|             } elseif ($eventType == 'opened') { | ||||
|                 $color = '54,193,87'; | ||||
|             } elseif ($eventType == 'bounced') { | ||||
|                 $color = '128,128,128'; | ||||
|             } | ||||
| 
 | ||||
|             $group = new stdClass(); | ||||
|             $group->data = $records; | ||||
|             $group->label = trans("texts.{$eventType}"); | ||||
|             $group->lineTension = 0; | ||||
|             $group->borderWidth = 4; | ||||
|             $group->borderColor = "rgba({$color}, 1)"; | ||||
|             $group->backgroundColor = "rgba({$color}, 0.1)"; | ||||
|             $datasets[] = $group; | ||||
|         } | ||||
| 
 | ||||
|         $data = new stdClass(); | ||||
|         $data->labels = $labels; | ||||
|         $data->datasets = $datasets; | ||||
| 
 | ||||
|         $response = new stdClass(); | ||||
|         $response->data = $data; | ||||
| 
 | ||||
|         return response()->json($response); | ||||
|         return response()->json($data); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										151
									
								
								app/Jobs/LoadPostmarkStats.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								app/Jobs/LoadPostmarkStats.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Jobs; | ||||
| 
 | ||||
| use App\Jobs\Job; | ||||
| use Postmark\PostmarkClient; | ||||
| use stdClass; | ||||
| use DateInterval; | ||||
| use DatePeriod; | ||||
| 
 | ||||
| class LoadPostmarkStats extends Job | ||||
| { | ||||
|     public function __construct($startDate, $endDate) | ||||
|     { | ||||
|         $this->startDate = $startDate; | ||||
|         $this->endDate = $endDate; | ||||
| 
 | ||||
|         $this->response = new stdClass(); | ||||
|         $this->postmark = new \Postmark\PostmarkClient(config('services.postmark')); | ||||
|         $this->account = auth()->user()->account; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Execute the job. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         $this->loadOverallStats(); | ||||
|         $this->loadSentStats(); | ||||
|         $this->loadPlatformStats(); | ||||
|         $this->loadEmailClientStats(); | ||||
| 
 | ||||
|         return $this->response; | ||||
|     } | ||||
| 
 | ||||
|     private function loadOverallStats() { | ||||
|         $startDate = date_create($this->startDate); | ||||
|         $endDate = date_create($this->endDate); | ||||
| 
 | ||||
|         $eventTypes = ['sent', 'opened']; | ||||
| 
 | ||||
|         foreach ($eventTypes as $eventType) { | ||||
|             $data = []; | ||||
|             $endDate->modify('+1 day'); | ||||
|             $interval = new DateInterval('P1D'); | ||||
|             $period = new DatePeriod($startDate, $interval, $endDate); | ||||
|             $endDate->modify('-1 day'); | ||||
|             $records = []; | ||||
| 
 | ||||
|             if ($eventType == 'sent') { | ||||
|                 $response = $this->postmark->getOutboundSendStatistics(null, request()->start_date, request()->end_date); | ||||
|             } else { | ||||
|                 $response = $this->postmark->getOutboundOpenStatistics(null, request()->start_date, request()->end_date); | ||||
|             } | ||||
| 
 | ||||
|             foreach ($response->days as $key => $val) { | ||||
|                 $field = $eventType == 'opened' ? 'unique' : $eventType; | ||||
|                 $data[$val['date']] = $val[$field]; | ||||
|             } | ||||
| 
 | ||||
|             foreach ($period as $day) { | ||||
|                 $date = $day->format('Y-m-d'); | ||||
|                 $records[] = isset($data[$date]) ? $data[$date] : 0; | ||||
| 
 | ||||
|                 if ($eventType == 'sent') { | ||||
|                     $labels[] = $day->format('m/d/Y'); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if ($eventType == 'sent') { | ||||
|                 $color = '51,122,183'; | ||||
|             } elseif ($eventType == 'opened') { | ||||
|                 $color = '54,193,87'; | ||||
|             } elseif ($eventType == 'bounced') { | ||||
|                 $color = '128,128,128'; | ||||
|             } | ||||
| 
 | ||||
|             $group = new stdClass(); | ||||
|             $group->data = $records; | ||||
|             $group->label = trans("texts.{$eventType}"); | ||||
|             $group->lineTension = 0; | ||||
|             $group->borderWidth = 4; | ||||
|             $group->borderColor = "rgba({$color}, 1)"; | ||||
|             $group->backgroundColor = "rgba({$color}, 0.1)"; | ||||
|             $datasets[] = $group; | ||||
|         } | ||||
| 
 | ||||
|         $data = new stdClass(); | ||||
|         $data->labels = $labels; | ||||
|         $data->datasets = $datasets; | ||||
|         $this->response->data = $data; | ||||
|     } | ||||
| 
 | ||||
|     private function loadSentStats() { | ||||
|         $account = $this->account; | ||||
|         $data = $this->postmark->getOutboundOverviewStatistics(null, request()->start_date, request()->end_date); | ||||
|         $this->response->totals = [ | ||||
|             'sent' => $account->formatNumber($data->sent), | ||||
|             'opened' => sprintf('%s | %s%%', $account->formatNumber($data->uniqueopens), $account->formatNumber($data->uniqueopens / $data->sent * 100)), | ||||
|             'bounced' => sprintf('%s | %s%%', $account->formatNumber($data->bounced), $account->formatNumber($data->bouncerate, 3)), | ||||
|             //'spam' => sprintf('%s | %s%%', $account->formatNumber($data->spamcomplaints), $account->formatNumber($data->spamcomplaintsrate, 3))
 | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     private function loadPlatformStats() { | ||||
|         $data = $this->postmark->getOutboundPlatformStatistics(null, request()->start_date, request()->end_date); | ||||
|         $account = $this->account; | ||||
|         $str = ''; | ||||
|         $total = 0; | ||||
| 
 | ||||
|         $total = $data['desktop'] + $data['mobile'] + $data['webmail']; | ||||
| 
 | ||||
|         foreach (['mobile', 'desktop', 'webmail'] as $platform) { | ||||
|             $str .= sprintf('<tr><td>%s</td><td>%s%%</td></tr>', trans('texts.' . $platform), $account->formatNumber($data[$platform] / $total * 100)); | ||||
|         } | ||||
| 
 | ||||
|         $this->response->platforms = $str; | ||||
|     } | ||||
| 
 | ||||
|     private function loadEmailClientStats() { | ||||
|         $data = $this->postmark->getOutboundEmailClientStatistics(null, request()->start_date, request()->end_date); | ||||
|         $account = $this->account; | ||||
|         $str = ''; | ||||
|         $total = 0; | ||||
|         $clients = []; | ||||
| 
 | ||||
|         foreach ($data as $key => $val) { | ||||
|             if ($key == 'days') { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             $total += $val; | ||||
|             $clients[$key] = $val; | ||||
|         } | ||||
| 
 | ||||
|         arsort($clients); | ||||
| 
 | ||||
|         foreach ($clients as $key => $val) { | ||||
|             $percent = $val / $total * 100; | ||||
|             if ($percent < 0.5) { | ||||
|                 continue; | ||||
|             } | ||||
|             $str .= sprintf('<tr><td>%s</td><td>%s%%</td></tr>', ucwords($key), $account->formatNumber($percent)); | ||||
|         } | ||||
| 
 | ||||
|         $this->response->emailClients = $str; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -500,6 +500,21 @@ class Utils | ||||
|         return $data->first(); | ||||
|     } | ||||
| 
 | ||||
|     public static function formatNumber($value, $currencyId = false, $precision = 0) | ||||
|     { | ||||
|         $value = floatval($value); | ||||
| 
 | ||||
|         if (! $currencyId) { | ||||
|             $currencyId = Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY); | ||||
|         } | ||||
| 
 | ||||
|         $currency = self::getFromCache($currencyId, 'currencies'); | ||||
|         $thousand = $currency->thousand_separator; | ||||
|         $decimal = $currency->decimal_separator; | ||||
| 
 | ||||
|         return number_format($value, $precision, $decimal, $thousand); | ||||
|     } | ||||
| 
 | ||||
|     public static function formatMoney($value, $currencyId = false, $countryId = false, $decorator = false) | ||||
|     { | ||||
|         $value = floatval($value); | ||||
|  | ||||
| @ -682,6 +682,17 @@ class Account extends Eloquent | ||||
|         return Utils::formatMoney($amount, $currencyId, $countryId, $decorator); | ||||
|     } | ||||
| 
 | ||||
|     public function formatNumber($amount, $precision = 0) | ||||
|     { | ||||
|         if ($this->currency_id) { | ||||
|             $currencyId = $this->currency_id; | ||||
|         } else { | ||||
|             $currencyId = DEFAULT_CURRENCY; | ||||
|         } | ||||
| 
 | ||||
|         return Utils::formatNumber($amount, $currencyId, $precision); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return mixed | ||||
|      */ | ||||
|  | ||||
| @ -2755,6 +2755,15 @@ $LANG = array( | ||||
|     'emails' => 'Emails', | ||||
|     'opened' => 'Opened', | ||||
|     'bounced' => 'Bounced', | ||||
|     'total_sent' => 'Total Sent', | ||||
|     'total_opened' => 'Total Opened', | ||||
|     'total_bounced' => 'Total Bounced', | ||||
|     'total_spam' => 'Total Spam', | ||||
|     'platforms' => 'Platforms', | ||||
|     'email_clients' => 'Email Clients', | ||||
|     'mobile' => 'Mobile', | ||||
|     'desktop' => 'Desktop', | ||||
|     'webmail' => 'Webmail', | ||||
| 
 | ||||
| ); | ||||
| 
 | ||||
|  | ||||
| @ -66,11 +66,6 @@ | ||||
| 							} | ||||
| 						} | ||||
| 					}, | ||||
| 					title: { | ||||
| 						display: false, | ||||
| 						fontSize: 18, | ||||
| 						text: '{{ trans('texts.total_revenue') }}' | ||||
| 					}, | ||||
| 					scales: { | ||||
| 						xAxes: [{ | ||||
| 							type: 'time', | ||||
| @ -164,8 +159,14 @@ | ||||
| 		function loadData() { | ||||
| 			var url = "{!! url('/reports/emails_report') !!}/" + chartStartDate.format('YYYY-MM-DD') + '/' + chartEndDate.format('YYYY-MM-DD'); | ||||
| 			$.get(url, function(response) { | ||||
| 				//response = JSON.parse(response);
 | ||||
| 				loadChart(response.data); | ||||
| 				$('#totalSentDiv').html(response.totals['sent']); | ||||
| 				$('#totalOpenedDiv').html(response.totals['opened']); | ||||
| 				$('#totalBouncedDiv').html(response.totals['bounced']); | ||||
| 				//$('#totalSpamDiv').html(response.totals['spam']);
 | ||||
| 
 | ||||
| 				$('#platformsTable').html(response.platforms); | ||||
| 				$('#emailClientsTable').html(response.emailClients); | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
| @ -174,6 +175,68 @@ | ||||
| 
 | ||||
| 	</script> | ||||
| 
 | ||||
| 
 | ||||
| 	<div class="row"> | ||||
| 	    <div class="col-md-4"> | ||||
| 	        <div class="panel panel-default"> | ||||
| 	            <div class="panel-body"> | ||||
| 	                <div style="overflow:hidden"> | ||||
| 	                    <div class="in-thin"> | ||||
| 	                        {{ trans('texts.total_sent') }} | ||||
| 	                    </div> | ||||
| 	                    <div class="in-bold" id="totalSentDiv"> | ||||
| 							  | ||||
| 						</div> | ||||
| 	                </div> | ||||
| 	            </div> | ||||
| 	        </div> | ||||
| 	    </div> | ||||
| 	    <div class="col-md-4"> | ||||
| 	        <div class="panel panel-default"> | ||||
| 				<div class="panel-body"> | ||||
| 	                <div style="overflow:hidden"> | ||||
| 	                    <div class="in-thin"> | ||||
| 	                        {{ trans('texts.total_opened') }} | ||||
| 	                    </div> | ||||
| 	                    <div class="in-bold" id="totalOpenedDiv"> | ||||
| 							  | ||||
| 						</div> | ||||
| 	                </div> | ||||
| 	            </div> | ||||
| 	        </div> | ||||
| 	    </div> | ||||
| 		<div class="col-md-4"> | ||||
| 	        <div class="panel panel-default"> | ||||
| 	            <div class="panel-body"> | ||||
| 	                <div style="overflow:hidden"> | ||||
| 	                    <div class="in-thin"> | ||||
| 	                        {{ trans('texts.total_bounced') }} | ||||
| 	                    </div> | ||||
| 	                    <div class="in-bold" id="totalBouncedDiv"> | ||||
| 							  | ||||
| 						</div> | ||||
| 	                </div> | ||||
| 	            </div> | ||||
| 	        </div> | ||||
| 	    </div> | ||||
| 		<!-- | ||||
| 		<div class="col-md-3"> | ||||
| 	        <div class="panel panel-default"> | ||||
| 	            <div class="panel-body outstanding-panel"> | ||||
| 	                <div style="overflow:hidden"> | ||||
| 	                    <div class="in-thin"> | ||||
| 	                        {{ trans('texts.total_spam') }} | ||||
| 	                    </div> | ||||
| 	                    <div class="in-bold" id="totalSpamDiv"> | ||||
| 							  | ||||
| 						</div> | ||||
| 	                </div> | ||||
| 	            </div> | ||||
| 	        </div> | ||||
| 	    </div> | ||||
| 		--> | ||||
| 	</div> | ||||
| 
 | ||||
| 	<div class="row"> | ||||
| 	    <div class="col-md-12"> | ||||
| 	        <div id="progress-div" class="progress"> | ||||
| @ -184,4 +247,35 @@ | ||||
| 	    </div> | ||||
| 	</div> | ||||
| 
 | ||||
| 	<p> </p> | ||||
| 
 | ||||
| 	<div class="row"> | ||||
| 	    <div class="col-md-6"> | ||||
| 	        <div class="panel panel-default"> | ||||
| 				<div class="panel-heading" style="background-color:#777 !important"> | ||||
| 	                <h3 class="panel-title in-bold-white"> | ||||
| 	                    <i class="glyphicon glyphicon-phone"></i> {{ trans('texts.platforms') }} | ||||
| 	                </h3> | ||||
| 	            </div> | ||||
| 				<div class="panel-body" style="height:260px;overflow-y:auto;"> | ||||
| 	                <table class="table table-striped" id="platformsTable"> | ||||
| 					</table> | ||||
| 				</div> | ||||
| 	        </div> | ||||
| 	    </div> | ||||
| 	    <div class="col-md-6"> | ||||
| 	        <div class="panel panel-default"> | ||||
| 				<div class="panel-heading" style="margin:0; background-color: #f5f5f5 !important;"> | ||||
| 	                <h3 class="panel-title" style="color: black !important"> | ||||
| 	                    <i class="glyphicon glyphicon-inbox"></i> {{ trans('texts.email_clients') }} | ||||
| 	                </h3> | ||||
| 	            </div> | ||||
| 				<div class="panel-body" style="height:260px;overflow-y:auto;"> | ||||
| 	                <table class="table table-striped" id="emailClientsTable"> | ||||
| 					</table> | ||||
| 				</div> | ||||
| 	        </div> | ||||
| 	    </div> | ||||
| 	</div> | ||||
| 
 | ||||
| @stop | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user