Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
StatsController
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 5
156
0.00% covered (danger)
0.00%
0 / 1
 viewStatsByBrand
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 exportByBrandCsv
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
12
 viewByQuestion
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 exportByQuestionCsv
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
6
 answersByDays
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace App\Http\Controllers\Bo;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\Bo\StatsByBrandRequest;
7use App\Http\Requests\Bo\StatsByQuestionRequest;
8use App\Models\Brand;
9use App\Models\BrandStat;
10use App\Models\CustomerAnswer;
11use App\Models\Question;
12use Illuminate\Support\Carbon;
13use Illuminate\Support\Collection;
14use Illuminate\Support\Facades\Schema;
15use League\Csv\Writer;
16
17class StatsController extends Controller
18{
19    /**
20     * Shows stats by brand.
21     *
22     * @param \App\Http\Requests\Bo\StatsByBrandRequest $request
23     * @return \Illuminate\Http\Response
24     */
25    public function viewStatsByBrand(StatsByBrandRequest $request)
26    {
27        $stats = null;
28        if ($request->post()) {
29            $stats = BrandStat::query()->whereDate('created_at', '>=', $request->start)
30                ->whereDate('created_at', '<=', $request->end)
31                ->whereBrandId($request->brand)->get();
32        }
33        return view('bo.pages.stats.view-stats-by-brands', \compact('stats'));
34    }
35
36    /**
37     * Exports stats by brand as a csv file.
38     *
39     * @param \App\Http\Requests\Bo\StatsByBrandRequest $request
40     * @return \Illuminate\Http\Response
41     */
42    public function exportByBrandCsv(StatsByBrandRequest $request): \Illuminate\Http\Response
43    {
44        $brand = Brand::query()->findOrFail($request->brand);
45        $stats = BrandStat::query()->with('brand')->whereDate('created_at', '>=', $request->start)
46            ->whereDate('created_at', '<=', $request->end)
47            ->whereBrandId($request->brand)->get();
48
49        $columns = collect(Schema::getColumnListing((new BrandStat())->getTable()))
50            ->filter(function ($value) {
51                return !in_array($value, ['created_at', 'brand_id']);
52            })->all();
53
54        $header = '"date";"marque";' . implode(';', $columns);
55
56        $w = Writer::createFromString($header . PHP_EOL);
57        $w->setDelimiter(';');
58        $w->setEnclosure('"');
59        $w->setOutputBOM(Writer::BOM_UTF8);
60
61        foreach ($stats as $stat) {
62            $cols = [
63                $stat->created_at->isoFormat('YYYY-MM-DD'),
64                $stat->brand->name
65            ];
66            foreach ($columns as $colname) {
67                $cols[] = $stat->{$colname};
68            }
69            $w->insertOne($cols);
70        }
71        return response((string)$w, 200, [
72            'Content-Encoding' => 'none',
73            'Content-Type' => 'text/csv; charset=UTF-8',
74            // phpcs:ignore Generic.Files.LineLength.TooLong
75            'Content-Disposition' => "attachment; filename=\"Statistiques du $request->start au $request->end pour $brand->name.csv\"",
76            'Content-Description' => 'File Transfer',
77        ]);
78    }
79
80    /**
81     * Views stats for a specific question.
82     *
83     * @param \App\Http\Requests\Bo\StatsByQuestionRequest $request
84     * @return \Illuminate\Contracts\View\View
85     */
86    public function viewByQuestion(StatsByQuestionRequest $request): \Illuminate\Contracts\View\View
87    {
88        $question      = null;
89        $answers       = null;
90        $answersByDays = null;
91        if ($request->post()) {
92            $question = Question::query()->findOrFail($request->question);
93            $answers  = $question->answers;
94            $start    = Carbon::parse($request->start);
95            $end      = Carbon::parse($request->end);
96
97            $answersByDays = $this->answersByDays($answers, $start, $end);
98        }
99
100        return view('bo.pages.stats.view-stats-by-questions', \compact('question', 'answers', 'answersByDays'));
101    }
102
103    /**
104     * Exports stats for a specific question as csv.
105     *
106     * @param \App\Http\Requests\Bo\StatsByQuestionRequest $request
107     * @return \Illuminate\Http\Response
108     */
109    public function exportByQuestionCsv(StatsByQuestionRequest $request): \Illuminate\Http\Response
110    {
111        $question = Question::query()->findOrFail($request->question);
112        $answers  = $question->answers;
113        $start    = Carbon::parse($request->start);
114        $end      = Carbon::parse($request->end);
115
116        $answersByDays = $this->answersByDays($answers, $start, $end);
117
118        $header = '"date";' . implode(';', $answers->map(function ($answer) {
119            return "\"$answer->answer\"";
120        })->all());
121
122        $w = Writer::createFromString($header . PHP_EOL);
123        $w->setDelimiter(';');
124        $w->setEnclosure('"');
125        $w->setOutputBOM(Writer::BOM_UTF8);
126
127        foreach ($answersByDays as $day => $dayAnswers) {
128            $w->insertOne(array_merge(
129                [
130                    $day
131                ],
132                $dayAnswers->values()->all()
133            ));
134        }
135        return response((string)$w, 200, [
136            'Content-Encoding' => 'none',
137            'Content-Type' => 'text/csv; charset=UTF-8',
138            // phpcs:ignore Generic.Files.LineLength.TooLong
139            'Content-Disposition' => "attachment; filename=\"Statistiques du $request->start au $request->end pour $question->question.csv\"",
140            'Content-Description' => 'File Transfer',
141        ]);
142    }
143
144    /**
145     * Makes a collection of total users answers by day.
146     *
147     * @param \Illuminate\Support\Collection $answers A collection of Answer.
148     * @param \Illuminate\Support\Carbon     $start   The wanted start period.
149     * @param \Illuminate\Support\Carbon     $end     The wanted end period.
150     * @return \Illuminate\Support\Collection A collection with date as Y_m_d index
151     *                                        with array of answers and their total.
152     */
153    private function answersByDays(Collection $answers, Carbon $start, Carbon $end): \Illuminate\Support\Collection
154    {
155        // * Totalize by date all answers.
156        $answersByDays = CustomerAnswer::query()->whereDate('created_at', '>=', $start)
157            ->whereDate('created_at', '<=', $end)
158            ->whereIn('answer_id', $answers->pluck('id'))
159            ->get()->map(function ($custAnswer) {
160                return [
161                    'answer_id' => $custAnswer->answer_id,
162                    'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
163                ];
164            })->groupBy(['created_at', 'answer_id'])->map(function ($day) {
165                return $day->map(function ($custAnswers) {
166                    return $custAnswers->count();
167                });
168            });
169
170        do {
171            $currentDate = $start->isoFormat('YYYY-MM-DD');
172            /** @var \Illuminate\Support\Collection $answersByDays */
173            if (!$answersByDays->has($currentDate)) {
174                $answersByDays->put(
175                    $currentDate,
176                    $answers->mapWithKeys(function ($answer) {
177                        return [$answer->answer => 0];
178                    })
179                );
180            } else {
181                $curentAnswers = $answersByDays->pull($currentDate);
182                $answersByDays->put(
183                    $currentDate,
184                    $answers->mapWithKeys(function ($answer) use ($curentAnswers) {
185                        return [$answer->answer =>
186                        $curentAnswers->has($answer->id) ?
187                            $curentAnswers->get($answer->id) : 0];
188                    })
189                );
190            }
191
192            $start = $start->addDay();
193        } while ($start->lessThanOrEqualTo($end));
194
195        return $answersByDays;
196    }
197}