PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 1.10.1
Fluent Support – Helpdesk & Customer Support Ticket System v1.10.1
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
fluent-support / app / Modules / Reporting / Reporting.php

Reporting.php in Fluent Support – Helpdesk & Customer Support Ticket System 1.10.1, at app/Modules/Reporting/Reporting.php

764 lines 26.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Modules\Reporting;
4
5 use FluentSupport\App\Models\Agent;
6 use FluentSupport\App\Models\Conversation;
7 use FluentSupport\App\Models\MailBox;
8 use FluentSupport\App\Models\Meta;
9 use FluentSupport\App\Models\Person;
10 use FluentSupport\App\Models\Product;
11 use FluentSupport\App\Models\Ticket;
12 use FluentSupport\App\Services\Helper;
13 use FluentSupport\Framework\Database\Orm\Builder;
14 use FluentSupport\Framework\Support\Arr;
15 use FluentSupport\Framework\Support\DateTime;
16
17 /**
18 * Reporting class is responsible for getting data related to report
19 * @package FluentSupport\App\Modules\Reporting
20 *
21 * @version 1.0.0
22 */
23 class Reporting
24 {
25 use ReportingHelperTrait;
26
27 /**
28 * getTicketsGrowth will generate tickets statistics and return
29 * @param false $from
30 * @param false $to
31 * @param array $filters
32 * @return array
33 */
34 public function getTicketsGrowth($from = false, $to = false, $filters = [])
35 {
36 //Generate report period
37 $period = $this->makeDatePeriod(
38 $from = $this->makeFromDate($from),//Date from
39 $to = $this->makeToDate($to),//Date to
40 $frequency = $this->getFrequency($from, $to)// frequency P1D, P1W, P1M
41 );
42
43 //Get group by and order by i.e date,week, month
44 list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
45
46 //get all tickets statistics within the date range
47 $query = $this->db()->table('fs_tickets')
48 ->select($this->prepareSelect($frequency))
49 ->whereBetween('created_at', $this->prepareBetween($frequency, $from, $to))
50 ->groupBy($groupBy)
51 ->oldest($orderBy);
52
53 //If filter by product or agent or status selected
54 if ($filters) {
55 if (!empty($filters['statuses'])) {
56 $query->whereIn('status', $filters['statuses']);
57 }
58
59 if (!empty($filters['product_id'])) {
60 $query->where('product_id', $filters['product_id']);
61 }
62
63 if (!empty($filters['agent_id'])) {
64 $query->where('agent_id', $filters['agent_id']);
65 }
66
67 if (!empty($filters['mailbox_id'])) {
68 $query->where('mailbox_id', $filters['mailbox_id']);
69 }
70 }
71
72 $items = $query->get();
73
74 return $this->getResult($period, $items);
75 }
76
77 /**
78 * getTicketResolveGrowth method will get the statistics for resolved/closed tickets
79 * @param false $from
80 * @param false $to
81 * @param array $filters
82 * @return array
83 */
84 public function getTicketResolveGrowth($from = false, $to = false, $filters = [], $type = '')
85 {
86 $period = $this->makeDatePeriod(
87 $from = $this->makeFromDate($from),//Date from
88 $to = $this->makeToDate($to),//date to
89 $frequency = $this->getFrequency($from, $to)// frequency P1D, P1W, P1M
90 );
91
92 list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);//Get group by and order by i.e date,week, month
93
94 $filterColumn = (!empty($type)) ? $type.'_id' : 'id';
95
96 //get the closed ticket statistics within the date range
97 $query = $this->db()->table('fs_tickets')
98 ->select($this->prepareSelect($frequency, 'resolved_at'))
99 ->whereBetween('resolved_at', $this->prepareBetween($frequency, $from, $to))
100 ->where('status', 'closed')
101 ->where($filterColumn, '>', 0)
102 ->groupBy($groupBy)
103 ->oldest($orderBy);
104
105 //If filter by product or agent is selected
106 if ($filters) {
107 if (!empty($filters['product_id'])) {
108 $query->where('product_id', $filters['product_id']);
109 }
110
111 if (!empty($filters['agent_id'])) {
112 $query->where('agent_id', $filters['agent_id']);
113 }
114
115 if (!empty($filters['mailbox_id'])) {
116 $query->where('mailbox_id', $filters['mailbox_id']);
117 }
118 }
119
120 $items = $query->get();
121
122 return $this->getResult($period, $items);
123 }
124
125 /**
126 * getResponseGrowth method will generate the statistics for response
127 * @param false $from
128 * @param false $to
129 * @param array $filters
130 * @return array
131 */
132 public function getResponseGrowth($from = false, $to = false, $filters = [])
133 {
134 $period = $this->makeDatePeriod(
135 $from = $this->makeFromDate($from),
136 $to = $this->makeToDate($to),
137 $frequency = $this->getFrequency($from, $to)
138 );
139
140 list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
141
142 $query = Conversation::query()
143 ->select($this->prepareSelect($frequency))
144 ->whereBetween('created_at', $this->prepareBetween($frequency, $from, $to))
145 ->where('conversation_type', 'response')
146 ->whereHas('person', function ($q) {
147 $q->where('person_type', 'agent');
148 })
149 ->groupBy($groupBy)
150 ->oldest($orderBy);
151
152 if ($filters) {
153 if (!empty($filters['person_id'])) {
154 $query->where('person_id', $filters['person_id']);
155 }
156
157 if (!empty($filters['product_id'])) {
158 $query->where('product_id', $filters['product_id']);
159 }
160 }
161
162 $items = $query->get();
163
164 return $this->getResult($period, $items);
165 }
166
167 public function getResponseGrowthChart($from = false, $to = false, $filters = [], $type = ''): array
168 {
169 $period = $this->makeDatePeriod(
170 $from = $this->makeFromDate($from),
171 $to = $this->makeToDate($to),
172 $frequency = $this->getFrequency($from, $to)
173 );
174
175 list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
176
177 $filterColumn = $type."_id";
178
179 $query = $this->db()->table('fs_tickets')
180 ->select($this->prepareSelect($frequency,'created_at','response_count'))
181 ->whereBetween('created_at', $this->prepareBetween($frequency, $from, $to))
182 ->havingRaw('COUNT(response_count)> 0')
183 ->where($filterColumn, '>', 0)
184 ->groupBy($groupBy)
185 ->oldest($orderBy);
186
187 if ($filters) {
188 if (!empty($filters['product_id'])) {
189 $query->where('product_id', $filters['product_id']);
190 }
191
192 if (!empty($filters['mailbox_id'])) {
193 $query->where('mailbox_id', $filters['mailbox_id']);
194 }
195 }
196
197 $items = $query->get();
198
199 return $this->getResult($period, $items);
200 }
201
202 /**
203 * agentSummary method will prepare ticket summary with responses by agent
204 * @param false $from
205 * @param false $to
206 * @param false $agent
207 * @return mixed
208 */
209 public function agentSummary($from = false, $to = false, $agent = false)
210 {
211 if(!$from) {
212 $from = current_time('Y-m-d');
213 }
214
215 if(!$to) {
216 $to = current_time('Y-m-d');
217 }
218
219 $from .= ' 00:00:00';
220 $to .= ' 23:59:59';
221 $reports = [];
222
223 //Get tickets statistics that are closed
224 $resolves = $this->db()->table('fs_tickets')
225 ->select([
226 $this->db()->raw('COUNT(id) AS count'),
227 'agent_id',
228 ])
229 ->groupBy('agent_id')
230 ->where('status', 'closed')
231 ->whereBetween('resolved_at', [$from, $to])
232 ->get();
233
234 $reports = $this->pushReportData('closed', $resolves, $reports, 'agent_id');
235
236 //get statistics for all except closed ticket
237 $openTickets = $this->db()->table('fs_tickets')
238 ->select([
239 $this->db()->raw('COUNT(id) AS count'),
240 'agent_id'
241 ])
242 ->groupBy('agent_id')
243 ->where('status', '!=', 'closed')
244 ->get();
245
246 $reports = $this->pushReportData('opens', $openTickets, $reports, 'agent_id');
247 //Get response by agent
248 $responses = Conversation::select([
249 $this->db()->raw('COUNT(id) AS count'),
250 $this->db()->raw('person_id as agent_id'),
251 $this->db()->raw('created_at')
252 ])
253 ->whereHas('person', function ($q) {
254 $q->where('person_type', '=', 'agent');
255 })
256 ->whereBetween('created_at', [$from, $to])
257 ->where('conversation_type', 'response')
258 ->groupBy('agent_id')
259 ->get();
260
261 $reports = $this->pushReportData('responses', $responses, $reports, 'agent_id');
262 //Get interactions/responses by individual agents
263 foreach ($responses as $response) {
264 $reports[$response->agent_id]['interactions'] = Conversation::where('person_id', $response->agent_id)
265 ->where('conversation_type', 'response')
266 ->whereBetween('created_at', [$from, $to])
267 ->groupBy('ticket_id')
268 ->get()
269 ->count();
270 }
271
272 $agentIds = array_keys($reports);
273
274 if ($agent) {
275 $agentIds = array_map('intval', explode(',', $agent));
276 }
277
278 // get agent feedback statistics
279 $agentFeedbackRatingEnabled = Helper::getBusinessSettings('agent_feedback_rating') === 'yes';
280 if (defined('FLUENTSUPPORTPRO_PLUGIN_VERSION') && $agentFeedbackRatingEnabled) {
281 $agentConversations = Conversation::select([
282 $this->db()->raw('person_id as agent_id'),
283 $this->db()->raw('GROUP_CONCAT(id) as conversation_ids')
284 ])
285 ->whereIn('person_id', $agentIds)
286 ->whereHas('person', function ($q) {
287 $q->where('person_type', '=', 'agent');
288 })
289 ->where('conversation_type', 'response')
290 ->groupBy('agent_id')
291 ->get();
292
293 foreach ($agentConversations as $conversation) {
294 $conversationIds = array_map('intval', explode(',', $conversation->conversation_ids));
295
296 $feedbackMeta = Meta::whereIn('object_id', $conversationIds)
297 ->where('key', 'agent_feedback_ratings')
298 ->whereBetween('created_at', [$from, $to])
299 ->get();
300
301 $likeCount = 0;
302 $dislikeCount = 0;
303
304 foreach ($feedbackMeta as $feedback) {
305 $feedbackStatus = $feedback->value;
306
307 if ($feedbackStatus === 'like') {
308 $likeCount++;
309 } elseif ($feedbackStatus === 'dislike') {
310 $dislikeCount++;
311 }
312 }
313
314 $agentId = $conversation->agent_id;
315 $reports[$agentId]['likes'] = $likeCount;
316 $reports[$agentId]['dislikes'] = $dislikeCount;
317 }
318 }
319
320 $agents = Agent::select(['id', 'first_name', 'last_name'])
321 ->whereIn('id', $agentIds)
322 ->get();
323
324 foreach ($agents as $agent) {
325 $report = NULL;
326 if(isset($reports[$agent->id])) {
327 $reportFields = [
328 'interactions' => 0,
329 'responses' => 0,
330 'opens' => 0,
331 'closed' => 0,
332 'waiting_tickets' => 0,
333 ];
334
335 $additionalFields = defined('FLUENTSUPPORTPRO_PLUGIN_VERSION') && $agentFeedbackRatingEnabled ? ['likes' => 0, 'dislikes' => 0] : [];
336 $reportFields = $reportFields + $additionalFields;
337
338 $report = wp_parse_args($reports[$agent->id], $reportFields);
339 }
340 $agent->stats = $report;
341 $agent->active_stat = $this->getActiveStatByAgent($agent->id);
342 }
343 return $agents;
344 }
345
346 public function getSummary($type, $from = null, $to = null)
347 {
348 global $wpdb;
349 $tablePrefix = $wpdb->prefix;
350
351 if (!$from) {
352 $from = current_time('Y-m-d');
353 }
354
355 if (!$to) {
356 $to = current_time('Y-m-d');
357 }
358
359 $from .= ' 00:00:00';
360 $to .= ' 23:59:59';
361 $reports = [];
362
363 $groupByField = $type == 'product' ? 'product_id' : 'mailbox_id';
364
365 $resolves = $this->db()->table('fs_tickets')
366 ->select([
367 $this->db()->raw('COUNT(id) AS count'),
368 $groupByField,
369 ])
370 ->groupBy($groupByField)
371 ->where('status', 'closed')
372 ->whereBetween('resolved_at', [$from, $to])
373 ->get();
374
375 $reports = $this->pushReportData('closed', $resolves, $reports, $groupByField);
376
377 $openTickets = $this->db()->table('fs_tickets')
378 ->select([
379 $this->db()->raw('COUNT(id) AS count'),
380 $groupByField
381 ])
382 ->groupBy($groupByField)
383 ->where('status', '!=', 'closed')
384 ->whereBetween('created_at', [$from, $to])
385 ->get();
386
387 $reports = $this->pushReportData('opens', $openTickets, $reports, $groupByField);
388
389 $responses = $this->db()->table('fs_conversations')
390 ->join('fs_tickets', 'fs_tickets.id', '=', 'fs_conversations.ticket_id')
391 ->select([
392 $this->db()->raw('COUNT(' . $tablePrefix . 'fs_conversations.id) AS count'),
393 'fs_tickets.' . $groupByField,
394 ])
395 ->groupBy('fs_tickets.' . $groupByField)
396 ->whereBetween('fs_conversations.created_at', [$from, $to])
397 ->get();
398
399 $reports = $this->pushReportData('responses', $responses, $reports, $groupByField);
400
401 $ticketIds = $this->db()->table('fs_tickets')
402 ->select([
403 $groupByField,
404 $this->db()->raw('GROUP_CONCAT(id) AS ticket_ids'),
405 ])
406 ->where($groupByField, '!=', 0)
407 ->groupBy($groupByField)
408 ->get();
409
410 $result = [];
411 foreach ($ticketIds as $item) {
412 $result[$item->{$groupByField}] = explode(',', $item->ticket_ids);
413 }
414
415 foreach ($result as $id => $ticketIds) {
416 $interactions = Conversation::whereIn('ticket_id', $ticketIds)
417 ->where('conversation_type', 'response')
418 ->whereBetween('created_at', [$from, $to])
419 ->groupBy('ticket_id')
420 ->get()
421 ->count();
422
423 $reports[$id]['interactions'] = $interactions;
424 }
425
426 $ids = array_keys($reports);
427
428 $types = [
429 'product' => [
430 'model' => Product::class,
431 'fields' => ['id', 'title'],
432 ],
433 'mailbox' => [
434 'model' => MailBox::class,
435 'fields' => ['id', 'name'],
436 ],
437 ];
438
439 $model = $types[$type]['model'];
440 $fields = $types[$type]['fields'];
441
442 $items = $model::select($fields)
443 ->whereIn('id', $ids)
444 ->get();
445
446 foreach ($items as $item) {
447 $report = isset($reports[$item->id]) ? $reports[$item->id] : [];
448
449 $report = wp_parse_args($report, [
450 'responses' => 0,
451 'opens' => 0,
452 'closed' => 0,
453 'interactions' => 0
454 ]);
455 $item->stats = $report;
456 $item->active_stat = '';
457 }
458
459 return $items;
460 }
461
462 /**
463 * pushReportData method will format the ticket summary report
464 * @param $type
465 * @param $tickets
466 * @param $reports
467 * @param $groupByField
468 * @return array
469 */
470 private function pushReportData($type, $tickets, $reports, $groupByField): array
471 {
472 foreach ($tickets as $ticket) {
473 $groupKey = $ticket->{$groupByField};
474
475 if (!$groupKey) {
476 continue;
477 }
478
479 if (!isset($reports[$groupKey])) {
480 $reports[$groupKey] = [];
481 }
482
483 $reports[$groupKey][$type] = $ticket->count;
484 }
485
486 return $reports;
487 }
488
489 /**
490 * getActiveStats method will return the statistics for active tickets
491 * This method will get the list of open tickets calculate the wait times and return results
492 * @return array|false
493 */
494 public function getActiveStats()
495 {
496 // We will calculate the wait times for open waiting tickets
497 $waitStat = Ticket::waitingOnly()
498 ->where('status', '!=', 'closed')
499 ->whereNotNull('waiting_since')
500 ->select([
501 $this->db()->raw('avg(UNIX_TIMESTAMP(waiting_since)) as avg_waiting'),
502 $this->db()->raw('MIN(UNIX_TIMESTAMP(waiting_since)) as max_waiting'),
503 $this->db()->raw('COUNT(*) as total_tickets')
504 ])
505 ->first();
506
507 if(!$waitStat) {
508 return false;
509 }
510
511 $waitStat->avg_waiting = intval($waitStat->avg_waiting);
512 if($waitStat->avg_waiting > 0) {
513 $waitSeconds = time() - $waitStat->avg_waiting;
514 if( $waitSeconds < 172800 && $waitSeconds > 7200) {
515 $avgWait = ceil($waitSeconds / 3600) . ' hours';
516 } else {
517 $avgWait = human_time_diff($waitStat->avg_waiting, time());
518 }
519 } else {
520 $avgWait = 0;
521 }
522
523 return [
524 'average_waiting' => $avgWait,
525 'max_waiting' => (intval($waitStat->max_waiting)) ? human_time_diff(intval($waitStat->max_waiting), time()) : 0,
526 'waiting_tickets' => $waitStat->total_tickets
527 ];
528 }
529
530 /**
531 * getActiveStatByAgent method will return the statistics of active tickets for an agent
532 * This method will get agent id as parameter, fetch the list of open tickets by agent id, calculate the wait times and return results
533 * @param $agentId
534 * @return array|false
535 */
536 public function getActiveStatByAgent($agentId)
537 {
538 $waitStat = Ticket::waitingOnly()
539 ->where('status', '!=', 'closed')
540 ->whereNotNull('waiting_since')
541 ->where('agent_id', $agentId)
542 ->select([
543 $this->db()->raw('avg(UNIX_TIMESTAMP(waiting_since)) as avg_waiting'),
544 $this->db()->raw('MIN(UNIX_TIMESTAMP(waiting_since)) as max_waiting'),
545 $this->db()->raw('COUNT(*) as total_tickets')
546 ])
547 ->first();
548
549 if(!$waitStat) {
550 return false;
551 }
552
553 $waitStat->avg_waiting = intval($waitStat->avg_waiting);
554 if($waitStat->avg_waiting > 0) {
555 $waitSeconds = time() - $waitStat->avg_waiting;
556 if( $waitSeconds < 172800 && $waitSeconds > 7200) {
557 $avgWait = ceil($waitSeconds / 3600) . ' hours';
558 } else {
559 $avgWait = human_time_diff($waitStat->avg_waiting, time());
560 }
561 } else {
562 $avgWait = 0;
563 }
564
565 return [
566 'average_waiting' => $avgWait,
567 'max_waiting' => (intval($waitStat->max_waiting)) ? human_time_diff(intval($waitStat->max_waiting), time()) : 0,
568 'waiting_tickets' => $waitStat->total_tickets
569 ];
570 }
571
572 public function getQueryResults($from, $to, $filter)
573 {
574 switch ($filter['report_type']) {
575 case 'ticket':
576 return $this->getTicketStats($from, $to);
577 case 'agent_response':
578 return $this->getResponseStats($from, $to, 'agent', $filter['agent_id'] ?? null);
579 case 'customer_response':
580 return $this->getResponseStats($from, $to, 'customer');
581 default:
582 return [];
583 }
584 }
585
586 public function getTicketStats($from, $to)
587 {
588 $whereClause = '';
589 if ($from && $to) {
590 // Ensure the dates are in the correct format (Y-m-d H:i:s)
591 $start_date = date('Y-m-d 00:00:00', strtotime($from));
592 $end_date = date('Y-m-d 23:59:59', strtotime($to));
593 $whereClause = "WHERE created_at BETWEEN '$start_date' AND '$end_date'";
594 }
595
596 global $wpdb;
597
598 // SQL query to count tickets by day of week and hour within the specified date range
599 $query = "SELECT DAYNAME(created_at) AS weekday, HOUR(created_at) AS hour, COUNT(*) AS count
600 FROM {$wpdb->prefix}fs_tickets
601 {$whereClause}
602 GROUP BY DAYNAME(created_at), HOUR(created_at)
603 ORDER BY FIELD(DAYNAME(created_at), 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), HOUR(created_at)";
604
605 // Execute the query
606 $results = $wpdb->get_results($query);
607
608 $fillData = array_fill(0, 24, 0);
609 // add :00 on the suffix
610 $fillData = array_combine(array_map(function ($hour) {
611 return sprintf('%1d:00', $hour);
612 }, array_keys($fillData)), $fillData);
613
614 // Prepare the report
615 // Prepare the report
616 $report = [
617 'Mon' => $fillData,
618 'Tue' => $fillData,
619 'Wed' => $fillData,
620 'Thu' => $fillData,
621 'Fri' => $fillData,
622 'Sat' => $fillData,
623 'Sun' => $fillData
624 ];
625
626 foreach ($results as $result) {
627 $weekdayName = substr($result->weekday, 0, 3);
628 if (!isset($report[$weekdayName])) {
629 $report[$weekdayName] = $fillData;
630 }
631 $report[$weekdayName][sprintf('%1d:00', $result->hour)] = (int)$result->count;
632 }
633
634 return $report;
635 }
636
637 public function getResponseStats($from, $to, $reportType, $agentId = null)
638 {
639
640 global $wpdb;
641
642 $whereClause = " AND p.person_type = '" . $reportType . "'";
643
644 if ($from && $to) {
645 // Ensure the dates are in the correct format (Y-m-d H:i:s)
646 $start_date = date('Y-m-d 00:00:00', strtotime($from));
647 $end_date = date('Y-m-d 23:59:59', strtotime($to));
648 $whereClause .= " AND c.created_at BETWEEN '$start_date' AND '$end_date'";
649 }
650
651 if ($agentId) {
652 $whereClause .= " AND c.person_id = $agentId";
653 }
654
655 // SQL query to count customer responses by day of week and hour within the specified date range
656 $query = "SELECT DAYNAME(c.created_at) AS weekday, HOUR(c.created_at) AS hour, COUNT(*) AS count
657 FROM {$wpdb->prefix}fs_conversations AS c
658 JOIN {$wpdb->prefix}fs_persons AS p ON c.person_id = p.id
659 WHERE c.conversation_type = 'response'
660 {$whereClause}
661 GROUP BY DAYNAME(c.created_at), HOUR(c.created_at)
662 ORDER BY FIELD(DAYNAME(c.created_at), 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), HOUR(c.created_at)";
663
664 // Execute the query
665 $results = $wpdb->get_results($query);
666
667 $fillData = array_fill(0, 24, 0);
668 // add :00 on the suffix
669 $fillData = array_combine(array_map(function ($hour) {
670 return sprintf('%1d:00', $hour);
671 }, array_keys($fillData)), $fillData);
672
673 // Prepare the report
674 $report = [
675 'Mon' => $fillData,
676 'Tue' => $fillData,
677 'Wed' => $fillData,
678 'Thu' => $fillData,
679 'Fri' => $fillData,
680 'Sat' => $fillData,
681 'Sun' => $fillData
682 ];
683
684 foreach ($results as $result) {
685 $weekdayName = substr($result->weekday, 0, 3);
686 if (!isset($report[$weekdayName])) {
687 $report[$weekdayName] = $fillData;
688 }
689 $report[$weekdayName][sprintf('%01d:00', $result->hour)] = (int)$result->count;
690 }
691
692 return $report;
693 }
694
695 public function getTicketResponseStats($from, $to, $filter)
696 {
697 $query = Conversation::query()
698 ->select('id', 'ticket_id', 'person_id', 'created_at', 'content')
699 ->addSelect([
700 'person_type' => Person::select('person_type')
701 ->whereColumn('id', 'fs_conversations.person_id'),
702 'full_name' => Person::selectRaw("CONCAT(first_name, ' ', last_name)")
703 ->whereColumn('id', 'fs_conversations.person_id')
704 ])
705 ->where('conversation_type', 'response')
706 ->when(Arr::get($filter, 'person_type'), function (Builder $q, $personType) {
707 return $q->whereHas('person', function ($q) use ($personType) {
708 return $q->where('person_type', $personType);
709 });
710 })
711 ->when(($from && $to), function ($q) use ($from, $to) {
712 return $q->whereBetween('created_at', [
713 DateTime::parse($from)->startOfDay(),
714 DateTime::parse($to)->endOfDay()
715 ]);
716 })
717 ->when(Arr::get($filter, 'person_id'), function ($q, $personId) {
718 return $q->where('person_id', $personId);
719 });
720
721 return $query->get();
722 }
723
724 public function applyDateFilter($query, $from, $to)
725 {
726 if ($from && $to) {
727 $from .= ' 00:00:00';
728 $to .= ' 23:59:59';
729
730 $query->whereBetween('created_at', [$from, $to]);
731 }
732 }
733
734 public function finalizeQuery($query)
735 {
736 return $query->groupByRaw('DAYNAME(created_at), HOUR(created_at)')
737 ->orderByRaw("FIELD(DAYNAME(created_at), 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), HOUR(created_at)")
738 ->get();
739 }
740
741 public function formatResults($results)
742 {
743 $dataItems = [
744 'Mon' => [], 'Tue' => [], 'Wed' => [], 'Thu' => [], 'Fri' => [], 'Sat' => [], 'Sun' => []
745 ];
746
747 $hours = array_map(function ($hour) {
748 return $hour . ":00";
749 }, range(0, 23));
750
751 foreach ($dataItems as $day => $data) {
752 $dataItems[$day] = array_fill_keys($hours, 0);
753 }
754
755 foreach ($results as $row) {
756 $day = substr($row['day_of_week'], 0, 3);
757 $hour = $row['hour_of_day'] . ":00";
758 $dataItems[$day][$hour] = (int) $row['count'];
759 }
760
761 return $dataItems;
762 }
763 }
764