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