Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 263
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
MesurementGenerator
0.00% covered (danger)
0.00%
0 / 263
0.00% covered (danger)
0.00%
0 / 11
2862
0.00% covered (danger)
0.00%
0 / 1
 addGlobalOverview
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
90
 ActionTypeTable
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
110
 ProtectionActionAppliedAnnexeTable
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 ProtectionActionAppliedTable
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 ActionPlanTable
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
12
 ColorCellPriority
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 addSyntheticView
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 addActionPlanSyntheticView
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 addDetailedView
0.00% covered (danger)
0.00%
0 / 78
0.00% covered (danger)
0.00%
0 / 1
90
 sortMesurementByDate
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 sortMesurementByPriority
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3/**
4 * This file is part of the MADIS - RGPD Management application.
5 *
6 * @copyright Copyright (c) 2018-2019 Soluris - Solutions Numériques Territoriales Innovantes
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22declare(strict_types=1);
23
24namespace App\Domain\Reporting\Generator\Word;
25
26use App\Domain\Registry\Dictionary\MesurementPriorityDictionary;
27use App\Domain\Registry\Dictionary\MesurementStatusDictionary;
28use App\Domain\Registry\Model\Mesurement;
29use PhpOffice\PhpWord\Element\Section;
30use PhpOffice\PhpWord\Element\Table;
31use PhpOffice\PhpWord\SimpleType\TblWidth;
32
33class MesurementGenerator extends AbstractGenerator implements ImpressionGeneratorInterface
34{
35    protected $tableStyleConformite;
36
37    /**
38     * Global overview : Information to display for mesurement in overview report.
39     */
40    public function addGlobalOverview(Section $section, array $data): void
41    {
42        if ($this->collectivity) {
43            $collectivity = $this->collectivity;
44        } else {
45            $collectivity = $this->userProvider->getAuthenticatedUser()->getCollectivity();
46        }
47
48        $actionPlan = [
49            [
50                'Priorité',
51                'Action',
52                'Date',
53                'Responsable de l\'action',
54                'Observations',
55            ],
56        ];
57
58        $this->tableStyleConformite = [
59            'borderColor' => '006699',
60            'borderSize'  => 6,
61            'cellMargin'  => 100,
62            'unit'        => TblWidth::PERCENT,
63            'width'       => 100 * 50,
64        ];
65
66        uasort($data, [$this, 'sortMesurementByDate']);
67
68        $appliedMesurement = [];
69        $actionPlan        = [];
70        foreach ($data as $mesurement) {
71            if (MesurementStatusDictionary::STATUS_APPLIED === $mesurement->getStatus()) {
72                $appliedMesurement[] = [
73                    $mesurement->getPlanificationDate() ? $mesurement->getPlanificationDate()->format('d/m/Y') : '',
74                    $mesurement->getName(),
75                ];
76            } elseif (!\is_null($mesurement->getPlanificationDate()) && MesurementStatusDictionary::STATUS_NOT_APPLIED === $mesurement->getStatus()) {
77                $actionPlan[] = [
78                    'data' => [
79                        $mesurement->getPriority(),
80                        $mesurement->getName(),
81                        $mesurement->getPlanificationDate() ? $mesurement->getPlanificationDate()->format(self::DATE_FORMAT) : '',
82                        $mesurement->getManager(),
83                        $mesurement->getComment(),
84                    ],
85                    'style' => [
86                        'bgColor' => $mesurement->getPriority() ? $this->ColorCellPriority($mesurement->getPriority()) : '',
87                    ],
88                ];
89            }
90        }
91
92        $section->addTitle('Actions de protection mises en place', 2);
93        $textrun = $section->addTextRun();
94        $textrun->addText('Des actions de protection ont été mises en place afin de protéger les données à caractère personnel. Une ');
95        $textrun->addLink('ActionsImplemented', 'liste exhaustive des actions de protection mises en place', ['underline' => 'single'], [], true);
96        $textrun->addText(' figure en annexe. Ci-dessous, les 20 dernières actions :');
97
98        $this->ProtectionActionAppliedTable($section, $appliedMesurement, false);
99
100        $section->addTitle("Plan d'actions", 2);
101        $section->addText('Un plan d’action a été établi comme suit.');
102        $this->ActionPlanTable($section, $actionPlan);
103
104        $section->addTextBreak();
105
106        $section->addText('Le tableau ci-dessous représente le nombre d’actions de protection affectées par type de registre :');
107        $this->ActionTypeTable($section, $data);
108    }
109
110    private function ActionTypeTable($section, $data)
111    {
112        $tableActionType = $section->addTable($this->tableStyleConformite);
113        $tableActionType->addRow(null, ['tblHeader' => true, 'cantSplit' => true]);
114        $cell = $tableActionType->addCell(6000, $this->cellHeadStyle);
115        $cell->addText('Type de registre', $this->textHeadStyle);
116        $cell = $tableActionType->addCell(3000, $this->cellHeadStyle);
117        $cell->addText('Nombre d’actions de protection affectées', $this->textHeadStyle);
118
119        $cntTreatment   = 0;
120        $cntContractors = 0;
121        $cntTools       = 0;
122        $cntViolations  = 0;
123        $cntRequests    = 0;
124        foreach ($data as $item) {
125            $cntTreatment   = $item->getTreatments() ? $cntTreatment + count($item->getTreatments()) : $cntTreatment;
126            $cntContractors = $item->getContractors() ? $cntContractors + count($item->getContractors()) : $cntContractors;
127            $cntTools       = $item->getTools() ? $cntTools + count($item->getTools()) : $cntTools;
128            $cntViolations  = $item->getViolations() ? $cntViolations + count($item->getViolations()) : $cntViolations;
129            $cntRequests    = $item->getRequests() ? $cntRequests + count($item->getRequests()) : $cntRequests;
130        }
131
132        if ($this->collectivity) {
133            $collectivity = $this->collectivity;
134        } else {
135            $collectivity = $this->userProvider->getAuthenticatedUser()->getCollectivity();
136        }
137        $arrayTypes = $collectivity->isHasModuleTools() ?
138            [['type' => 'Traitements', 'count' => $cntTreatment], ['type' => 'Sous-traitants', 'count' => $cntContractors], ['type' => 'Logiciels ou supports', 'count' => $cntTools], ['type' => 'Violations de données', 'count' => $cntViolations], ['type' => 'Demandes', 'count' => $cntRequests]] :
139            [['type' => 'Traitements', 'count' => $cntTreatment], ['type' => 'Sous-traitants', 'count' => $cntContractors], ['type' => 'Violations de données', 'count' => $cntViolations], ['type' => 'Demandes', 'count' => $cntRequests]];
140
141        foreach ($arrayTypes as $item) {
142            $tableActionType->addRow();
143            $cell = $tableActionType->addCell(6000);
144            $cell->addText($item['type']);
145            $cell = $tableActionType->addCell(3000);
146            $cell->addText($item['count']);
147        }
148    }
149
150    public function ProtectionActionAppliedAnnexeTable($section, $data)
151    {
152        $section->addBookmark('ActionsImplemented');
153        $section->addTitle('Liste des actions de protection mises en place', 2);
154
155        uasort($data, [$this, 'sortMesurementByDate']);
156        $appliedMesurement = [];
157        /** @var Mesurement $mesurement */
158        foreach ($data as $mesurement) {
159            if (MesurementStatusDictionary::STATUS_APPLIED === $mesurement->getStatus()) {
160                $appliedMesurement[] = [
161                    $mesurement->getPlanificationDate() ? $mesurement->getPlanificationDate()->format('d/m/Y') : '',
162                    $mesurement->getName(),
163                ];
164            }
165        }
166        $this->ProtectionActionAppliedTable($section, $appliedMesurement, true);
167    }
168
169    public function ProtectionActionAppliedTable($section, $appliedMesurement, $isAnnexe)
170    {
171        $tableProtectionActionApplied = $section->addTable($this->tableStyleConformite);
172        $tableProtectionActionApplied->addRow(null, ['tblHeader' => true, 'cantSplit' => true]);
173        $cell = $tableProtectionActionApplied->addCell(1500, $this->cellHeadStyle);
174        $cell->addText('Date', $this->textHeadStyle);
175        $cell = $tableProtectionActionApplied->addCell(7500, $this->cellHeadStyle);
176        $cell->addText('Action', $this->textHeadStyle);
177
178        if ($appliedMesurement) {
179            $limit = $isAnnexe ? count($appliedMesurement) : 20;
180
181            foreach (array_slice($appliedMesurement, 0, $limit) as $line) {
182                $tableProtectionActionApplied->addRow(400, ['exactHeight' => true, 'cantsplit' => true]);
183                $cell1 = $tableProtectionActionApplied->addCell(1500);
184                $cell1->addText($line[0], [], ['alignment' => 'center']);
185                $cell2 = $tableProtectionActionApplied->addCell(7500);
186                $cell2->addText($line[1]);
187            }
188        }
189    }
190
191    private function ActionPlanTable($section, $actionPlan)
192    {
193        $tableActionPlan = $section->addTable($this->tableStyleConformite);
194        $tableActionPlan->addRow(null, ['tblHeader' => true, 'cantSplit' => true]);
195        $cell = $tableActionPlan->addCell(1000, $this->cellHeadStyle);
196        $cell->addText('Priorité', $this->textHeadStyle);
197        $cell = $tableActionPlan->addCell(2000, $this->cellHeadStyle);
198        $cell->addText('Action', $this->textHeadStyle);
199        $cell = $tableActionPlan->addCell(1000, $this->cellHeadStyle);
200        $cell->addText('Date', $this->textHeadStyle);
201        $cell = $tableActionPlan->addCell(1500, $this->cellHeadStyle);
202        $cell->addText('Responsable de l\'action', $this->textHeadStyle);
203        $cell = $tableActionPlan->addCell(1500, $this->cellHeadStyle);
204        $cell->addText('Observations', $this->textHeadStyle);
205
206        if ($actionPlan) {
207            foreach ($actionPlan as $line) {
208                $tableActionPlan->addRow(null, ['cantSplit' => true]);
209                $cell1    = $tableActionPlan->addCell(1000, ['bgColor' => $line['style']['bgColor']]);
210                $priority = match ($line['data'][0]) {
211                    'low'    => 'Basse',
212                    'normal' => 'Normale',
213                    'high'   => 'Haute',
214                    null     => 'Aucune',
215                };
216                $cell1->addText($priority, ['bold' => true]);
217                $cell2 = $tableActionPlan->addCell(2000);
218                $cell2->addText($line['data'][1]);
219                $cell1 = $tableActionPlan->addCell(1000);
220                $cell1->addText($line['data'][2]);
221                $cell2 = $tableActionPlan->addCell(1500);
222                $cell2->addText($line['data'][3]);
223                $cell2 = $tableActionPlan->addCell(1500);
224                $cell2->addText($line['data'][4]);
225            }
226        }
227    }
228
229    private function ColorCellPriority($priority)
230    {
231        $returned_value = match ($priority) {
232            'low'    => 'ffff80',
233            'normal' => 'fac9ad',
234            'high'   => 'ffa7a7',
235            ''       => 'ffffff',
236        };
237
238        return $returned_value;
239    }
240
241    public function addSyntheticView(Section $section, array $data): void
242    {
243        $section->addTitle('Liste des actions de protection', 1);
244
245        // Table data
246        // Add header
247        $tableData = [
248            [
249                'Nom',
250                'Statut',
251                'Priorité',
252            ],
253        ];
254
255        uasort($data, [$this, 'sortMesurementByPriority']);
256
257        // Add content
258        foreach ($data as $mesurement) {
259            $tableData[] = [
260                $mesurement->getName(),
261                MesurementStatusDictionary::getStatus()[$mesurement->getStatus()],
262                $mesurement->getPriority() ? MesurementPriorityDictionary::getPriorities()[$mesurement->getPriority()] : '',
263            ];
264        }
265
266        $this->addTable($section, $tableData, true, self::TABLE_ORIENTATION_HORIZONTAL);
267    }
268
269    public function addActionPlanSyntheticView(Section $section, array $data): void
270    {
271        $section->addTitle('Liste des actions de protection planifiées', 1);
272
273        // Table data
274        // Add header
275        $tableData = [
276            [
277                'Nom',
278                'Échéance',
279                "Responsable d'action",
280            ],
281        ];
282
283        uasort($data, [$this, 'sortMesurementByPriority']);
284
285        // Add content
286        foreach ($data as $mesurement) {
287            $tableData[] = [
288                $mesurement->getName(),
289                $this->getDate($mesurement->getPlanificationDate()),
290                $mesurement->getManager(),
291            ];
292        }
293
294        $this->addTable($section, $tableData, true, self::TABLE_ORIENTATION_HORIZONTAL);
295    }
296
297    public function addDetailedView(Section $section, array $data, $actionPlan = false): void
298    {
299        if ($actionPlan) {
300            $section->addTitle('Détail des actions de protection planifiées', 1);
301        } else {
302            $section->addTitle('Détail des actions de protection', 1);
303        }
304
305        foreach ($data as $key => $mesurement) {
306            if (0 != $key) {
307                $section->addPageBreak();
308            }
309            $section->addTitle($mesurement->getName(), 2);
310
311            $generalInformationsData = [
312                [
313                    'Nom',
314                    $mesurement->getName(),
315                ],
316            ];
317
318            // Ajouter les services si le module est actif
319            if ($mesurement->getCollectivity()->getIsServicesEnabled()) {
320                $generalInformationsData[] = [
321                    'Service',
322                    $mesurement->getService(),
323                ];
324            }
325
326            $generalInformationsData = array_merge($generalInformationsData, [
327                [
328                    'Description',
329                    $mesurement->getDescription() ? \preg_split('/\R/', $mesurement->getDescription()) : null,
330                ],
331                [
332                    'Responsable d\'action',
333                    $mesurement->getManager(),
334                ],
335                [
336                    'Priorité',
337                    !\is_null($mesurement->getPriority()) ? MesurementPriorityDictionary::getPriorities()[$mesurement->getPriority()] : '',
338                ],
339                [
340                    'Coût',
341                    $mesurement->getCost(),
342                ],
343                [
344                    'Charge',
345                    $mesurement->getCharge(),
346                ],
347            ]);
348
349            $applicationData = [
350                [
351                    'Statut',
352                    MesurementStatusDictionary::getStatus()[$mesurement->getStatus()],
353                ],
354                [
355                    'Échéance',
356                    MesurementStatusDictionary::STATUS_NOT_APPLICABLE !== $mesurement->getStatus()
357                        ? ($mesurement->getPlanificationDate() ? $this->getDate($mesurement->getPlanificationDate(), 'd/m/Y') : null)
358                        : 'Non applicable',
359                ],
360                [
361                    'Observations',
362                    $mesurement->getComment(),
363                ],
364            ];
365
366            $historyData = [
367                [
368                    'Date de création',
369                    $this->getDate($mesurement->getCreatedAt()),
370                ],
371                [
372                    'Date de modification',
373                    $this->getDate($mesurement->getUpdatedAt()),
374                ],
375                [
376                    'Modifié par',
377                    $mesurement->getUpdatedBy(),
378                ],
379            ];
380
381            $section->addTitle('Informations générales', 3);
382            $this->addTable($section, $generalInformationsData, false, self::TABLE_ORIENTATION_VERTICAL);
383
384            $section->addTitle('Application', 3);
385            $this->addTable($section, $applicationData, false, self::TABLE_ORIENTATION_VERTICAL);
386
387            $section->addTitle('Éléments associés', 3);
388            $this->addLinkedData($section, $mesurement);
389
390            $section->addTitle('Historique', 3);
391            $this->addTable($section, $historyData, false, self::TABLE_ORIENTATION_VERTICAL);
392        }
393    }
394
395    private function sortMesurementByDate(Mesurement $a, Mesurement $b)
396    {
397        if ($a->getPlanificationDate() === $b->getPlanificationDate()) {
398            return strcmp($b->getName(), $a->getName());
399        }
400
401        return ($a->getPlanificationDate() < $b->getPlanificationDate()) ? 1 : -1;
402    }
403
404    private function sortMesurementByPriority(Mesurement $a, Mesurement $b)
405    {
406        $weightA = \is_null($a->getPriority()) ? 0 : MesurementPriorityDictionary::getWeightPriorities()[$a->getPriority()];
407        $weightB = \is_null($b->getPriority()) ? 0 : MesurementPriorityDictionary::getWeightPriorities()[$b->getPriority()];
408
409        if ($weightA === $weightB) {
410            return 0;
411        }
412
413        return ($weightA < $weightB) ? 1 : -1;
414    }
415}