Passed
Pull Request — master (#6330)
by
unknown
08:09
created

SequenceResourceRepository::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Repository;
8
9
use Category;
10
use Chamilo\CoreBundle\Entity\Course;
11
use Chamilo\CoreBundle\Entity\GradebookCategory;
12
use Chamilo\CoreBundle\Entity\SequenceResource;
13
use Chamilo\CoreBundle\Entity\Session;
14
use Chamilo\CoreBundle\Entity\SessionRelCourse;
15
use Chamilo\CoreBundle\Entity\SessionRelUser;
16
use Chamilo\CoreBundle\Settings\SettingsManager;
17
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
18
use Doctrine\Persistence\ManagerRegistry;
19
use Fhaculty\Graph\Set\Vertices;
20
use Fhaculty\Graph\Vertex;
21
use SessionManager;
22
23
class SequenceResourceRepository extends ServiceEntityRepository
24
{
25
    public function __construct(
26
        ManagerRegistry $registry,
27
        private readonly SettingsManager $settingsManager
28
    ) {
29
        parent::__construct($registry, SequenceResource::class);
30
    }
31
32
    /**
33
     * Find the SequenceResource based in the resourceId and type.
34
     */
35
    public function findRequirementForResource(int $resourceId, int $type): ?SequenceResource
36
    {
37
        return $this->findOneBy([
38
            'resourceId' => $resourceId,
39
            'type' => $type,
40
        ]);
41
    }
42
43
    /**
44
     * @todo implement for all types only work for sessions
45
     *
46
     * @return array
47
     */
48
    public function getRequirementAndDependencies(int $resourceId, int $type)
49
    {
50
        $sequence = $this->findRequirementForResource($resourceId, $type);
51
        $result = [
52
            'requirements' => [],
53
            'dependencies' => [],
54
        ];
55
        if ($sequence && $sequence->hasGraph()) {
56
            $graph = $sequence->getSequence()->getUnSerializeGraph();
57
            $vertex = $graph->getVertex($resourceId);
58
            $from = $vertex->getVerticesEdgeFrom();
59
60
            foreach ($from as $subVertex) {
61
                $vertexId = $subVertex->getId();
62
                $sessionInfo = api_get_session_info($vertexId);
63
                $sessionInfo['admin_link'] = '<a href="'.SessionManager::getAdminPath($vertexId).'">'.$sessionInfo['name'].'</a>';
0 ignored issues
show
Bug introduced by
Are you sure SessionManager::getAdminPath($vertexId) of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

63
                $sessionInfo['admin_link'] = '<a href="'./** @scrutinizer ignore-type */ SessionManager::getAdminPath($vertexId).'">'.$sessionInfo['name'].'</a>';
Loading history...
64
                $result['requirements'][] = $sessionInfo;
65
            }
66
67
            $to = $vertex->getVerticesEdgeTo();
68
            foreach ($to as $subVertex) {
69
                $vertexId = $subVertex->getId();
70
                $sessionInfo = api_get_session_info($vertexId);
71
                $sessionInfo['admin_link'] = '<a href="'.SessionManager::getAdminPath($vertexId).'">'.$sessionInfo['name'].'</a>';
72
                $result['dependencies'][] = $sessionInfo;
73
            }
74
        }
75
76
        return $result;
77
    }
78
79
    /**
80
     * Deletes a node and check in all the dependencies if the node exists in
81
     * order to deleted.
82
     */
83
    public function deleteSequenceResource(int $resourceId, int $type): void
84
    {
85
        $sequence = $this->findRequirementForResource($resourceId, $type);
86
        if ($sequence && $sequence->hasGraph()) {
87
            $em = $this->getEntityManager();
88
            $graph = $sequence->getSequence()->getUnSerializeGraph();
89
            $mainVertex = $graph->getVertex($resourceId);
90
            $vertices = $graph->getVertices();
91
92
            /** @var Vertex $vertex */
93
            foreach ($vertices as $vertex) {
94
                $subResourceId = $vertex->getId();
95
                $subSequence = $this->findRequirementForResource($subResourceId, $type);
96
                if ($subSequence->hasGraph()) {
97
                    $graph = $subSequence->getSequence()->getUnSerializeGraph();
98
                    $subMainVertex = $graph->getVertex($resourceId);
99
                    $subMainVertex->destroy();
100
                    $subSequence->getSequence()->setGraphAndSerialize($graph);
101
                    $em->persist($subSequence);
102
                }
103
            }
104
105
            $mainVertex->destroy();
106
            $em->remove($sequence);
107
            $em->flush();
108
        }
109
    }
110
111
    /**
112
     * Get the requirements for a resource only.
113
     *
114
     * @param int $resourceId The resource ID
115
     * @param int $type       The type of sequence resource
116
     *
117
     * @return array
118
     */
119
    public function getRequirements(int $resourceId, int $type)
120
    {
121
        $sequencesResource = $this->findBy([
122
            'resourceId' => $resourceId,
123
            'type' => $type,
124
        ]);
125
        $em = $this->getEntityManager();
126
        $result = [];
127
128
        /** @var SequenceResource $sequenceResource */
129
        foreach ($sequencesResource as $sequenceResource) {
130
            if (!$sequenceResource->hasGraph()) {
131
                continue;
132
            }
133
134
            $sequence = $sequenceResource->getSequence();
135
            $graph = $sequence->getUnSerializeGraph();
136
            $vertex = $graph->getVertex($resourceId);
137
            $from = $vertex->getVerticesEdgeFrom();
138
139
            $sequenceInfo = [
140
                'name' => $sequence->getTitle(),
141
                'requirements' => [],
142
            ];
143
144
            foreach ($from as $subVertex) {
145
                $vertexId = $subVertex->getId();
146
                $resource = null;
147
148
                switch ($type) {
149
                    case SequenceResource::SESSION_TYPE:
150
                        $repo = $em->getRepository(Session::class);
151
                        $resource = $repo->find($vertexId);
152
153
                        break;
154
155
                    case SequenceResource::COURSE_TYPE:
156
                        $repo = $em->getRepository(Course::class);
157
                        $resource = $repo->find($vertexId);
158
159
                        break;
160
                }
161
162
                if (null === $resource) {
163
                    continue;
164
                }
165
166
                $sequenceInfo['requirements'][$vertexId] = $resource;
167
            }
168
169
            $result[$sequence->getId()] = $sequenceInfo;
170
        }
171
172
        return $result;
173
    }
174
175
    /**
176
     * Get the requirements and dependencies within a sequence for a resource.
177
     *
178
     * @param int $resourceId The resource ID
179
     * @param int $type       The type of sequence resource
180
     *
181
     * @return array
182
     */
183
    public function getRequirementsAndDependenciesWithinSequences(int $resourceId, int $type)
184
    {
185
        $sequencesResource = $this->findBy([
186
            'resourceId' => $resourceId,
187
            'type' => $type,
188
        ]);
189
190
        $result = [];
191
192
        /** @var SequenceResource $sequenceResource */
193
        foreach ($sequencesResource as $sequenceResource) {
194
            if (!$sequenceResource->hasGraph()) {
195
                continue;
196
            }
197
198
            $sequence = $sequenceResource->getSequence();
199
            $graph = $sequence->getUnSerializeGraph();
200
            $vertex = $graph->getVertex($resourceId);
201
            $from = $vertex->getVerticesEdgeFrom();
202
            $to = $vertex->getVerticesEdgeTo();
203
204
            $requirements = $this->findVerticesEdges($from, $type);
205
            $dependencies = $this->findVerticesEdges($to, $type);
206
207
            $result[$sequence->getId()] = [
208
                'name' => $sequence->getTitle(),
209
                'requirements' => $requirements,
210
                'dependencies' => $dependencies,
211
            ];
212
        }
213
214
        return $result;
215
    }
216
217
    /**
218
     * Check if the ser has completed the requirements for the sequences.
219
     *
220
     * @param array $sequences The sequences
221
     * @param int   $type      The type of sequence resource
222
     *
223
     * @return array
224
     */
225
    public function checkRequirementsForUser(array $sequences, int $type, int $userId)
226
    {
227
        $sequenceList = [];
228
        $em = $this->getEntityManager();
229
        $gradebookCategoryRepo = $em->getRepository(GradebookCategory::class);
230
231
        $sessionUserList = [];
232
        if (SequenceResource::COURSE_TYPE === $type) {
233
            $criteria = [
234
                'user' => $userId,
235
            ];
236
            $sessions = $em->getRepository(SessionRelUser::class)->findBy($criteria);
237
            if ([] !== $sessions) {
238
                foreach ($sessions as $sessionRelUser) {
239
                    $sessionUserList[] = $sessionRelUser->getSession()->getId();
240
                }
241
            }
242
        }
243
244
        foreach ($sequences as $sequenceId => $sequence) {
245
            $item = [
246
                'name' => $sequence['name'],
247
                'requirements' => [],
248
            ];
249
            $resourceItem = null;
250
251
            foreach ($sequence['requirements'] as $resource) {
252
                switch ($type) {
253
                    case SequenceResource::SESSION_TYPE:
254
                        $id = $resource->getId();
255
                        $resourceItem = [
256
                            'name' => $resource->getTitle(),
257
                            'status' => true,
258
                        ];
259
260
                        $sessionsCourses = $resource->getCourses();
261
262
                        foreach ($sessionsCourses as $sessionCourse) {
263
                            $course = $sessionCourse->getCourse();
264
                            $categories = $gradebookCategoryRepo->findBy(
265
                                [
266
                                    'course' => $course,
267
                                    'session' => $resource,
268
                                    'isRequirement' => true,
269
                                ]
270
                            );
271
272
                            foreach ($categories as $category) {
273
                                if (!empty($userId)) {
274
                                    $resourceItem['status'] = $resourceItem['status'] && Category::userFinishedCourse(
275
                                        $userId,
276
                                        $category
277
                                    );
278
                                }
279
                            }
280
                        }
281
282
                        break;
283
284
                    case SequenceResource::COURSE_TYPE:
285
                        $id = $resource->getId();
286
                        $status = $this->checkCourseRequirements($userId, $resource, 0);
287
288
                        if (!$status) {
289
                            $sessionsInCourse = SessionManager::get_session_by_course($id);
290
                            foreach ($sessionsInCourse as $session) {
291
                                if (\in_array($session['id'], $sessionUserList, true)) {
292
                                    $status = $this->checkCourseRequirements($userId, $resource, $session['id']);
293
                                    if ($status) {
294
                                        break;
295
                                    }
296
                                }
297
                            }
298
                        }
299
300
                        $resourceItem = [
301
                            'name' => $resource->getTitle(),
302
                            'status' => $status,
303
                        ];
304
305
                        break;
306
                }
307
308
                if (empty($id)) {
309
                    continue;
310
                }
311
312
                $item['requirements'][$id] = $resourceItem;
313
            }
314
            $sequenceList[$sequenceId] = $item;
315
        }
316
317
        return $sequenceList;
318
    }
319
320
    public function checkCourseRequirements(int $userId, Course $course, int $sessionId = 0): bool
321
    {
322
        $em = $this->getEntityManager();
323
        $session = $sessionId > 0
324
            ? $em->getRepository(Session::class)->find($sessionId)
325
            : null;
326
        $gradebookCategoryRepo = $em->getRepository(GradebookCategory::class);
327
        $categories = $gradebookCategoryRepo->findBy(
328
            [
329
                'course' => $course,
330
                'session' => $session,
331
                'isRequirement' => true,
332
            ]
333
        );
334
335
        if (empty($categories)) {
336
            return false;
337
        }
338
339
        $status = true;
340
        foreach ($categories as $category) {
341
            $userFinishedCourse = Category::userFinishedCourse(
342
                $userId,
343
                $category,
344
                true
345
            );
346
347
            if (0 === $sessionId) {
348
                if (!$userFinishedCourse) {
349
                    $status = false;
350
351
                    break;
352
                }
353
            } elseif (!$userFinishedCourse) {
354
                $status = false;
355
356
                break;
357
            }
358
        }
359
360
        return $status;
361
    }
362
363
    /**
364
     * Check if at least one sequence are completed.
365
     *
366
     * @param array $sequences The sequences
367
     *
368
     * @return bool
369
     */
370
    public function checkSequenceAreCompleted(array $sequences)
371
    {
372
        foreach ($sequences as $sequence) {
373
374
            if (!isset($sequence['requirements'])) {
375
                continue;
376
            }
377
378
            $status = true;
379
380
            foreach ($sequence['requirements'] as $item) {
381
                $status = $status && $item['status'];
382
            }
383
384
            if ($status) {
385
                return true;
386
            }
387
        }
388
389
        return false;
390
    }
391
392
    /**
393
     * Get sessions from vertices.
394
     *
395
     * @param Vertices $verticesEdges The vertices
396
     *
397
     * @return array
398
     */
399
    protected function findVerticesEdges(Vertices $verticesEdges, int $type)
400
    {
401
        $sessionVertices = [];
402
        $em = $this->getEntityManager();
403
404
        foreach ($verticesEdges as $supVertex) {
405
            $vertexId = $supVertex->getId();
406
407
            switch ($type) {
408
                case SequenceResource::SESSION_TYPE:
409
                    $resource = $em->getRepository(Session::class)->find($vertexId);
410
411
                    break;
412
413
                case SequenceResource::COURSE_TYPE:
414
                    $resource = $em->getRepository(Course::class)->find($vertexId);
415
416
                    break;
417
            }
418
419
            if (empty($resource)) {
420
                continue;
421
            }
422
423
            $sessionVertices[$vertexId] = $resource;
424
        }
425
426
        return $sessionVertices;
427
    }
428
429
    public function getDependents(int $resourceId, int $type): array
430
    {
431
        return $this->getRequirementsOrDependents($resourceId, $type, 'dependents');
432
    }
433
434
    public function checkDependentsForUser(array $sequences, int $type, int $userId, int $sessionId = 0): array
435
    {
436
        return $this->checkRequirementsOrDependentsForUser(
437
            $sequences,
438
            $type,
439
            'dependents',
440
            $userId,
441
            $sessionId
442
        );
443
    }
444
445
    private function getRequirementsOrDependents(int $resourceId, int $resourceType, string $itemType): array
446
    {
447
        $em = $this->getEntityManager();
448
449
        $sequencesResource = $this->findBy(['resourceId' => $resourceId, 'type' => $resourceType]);
450
        $result = [];
451
452
        foreach ($sequencesResource as $sequenceResource) {
453
            if (!$sequenceResource->hasGraph()) {
454
                continue;
455
            }
456
457
            $sequence = $sequenceResource->getSequence();
458
            $graph = $sequence->getUnSerializeGraph();
459
            $vertex = $graph->getVertex($resourceId);
460
461
            $edges = $itemType === 'requirements'
462
                ? $vertex->getVerticesEdgeFrom()
463
                : $vertex->getVerticesEdgeTo();
464
465
            $sequenceInfo = [
466
                'name' => $sequence->getTitle(),
467
                $itemType => [],
468
            ];
469
470
            foreach ($edges as $edge) {
471
                $vertexId = $edge->getId();
472
                $resource = null;
473
474
                switch ($resourceType) {
475
                    case SequenceResource::SESSION_TYPE:
476
                        $resource = $em->getRepository(Session::class)->find($vertexId);
477
                        break;
478
                    case SequenceResource::COURSE_TYPE:
479
                        $resource = $em->getRepository(Course::class)->find($vertexId);
480
                        break;
481
                }
482
483
                if (null === $resource) {
484
                    continue;
485
                }
486
487
                $sequenceInfo[$itemType][$vertexId] = $resource;
488
            }
489
490
            $result[$sequence->getId()] = $sequenceInfo;
491
        }
492
493
        return $result;
494
    }
495
496
    private function checkRequirementsOrDependentsForUser(
497
        array $sequences,
498
        int $resourceType,
499
        string $itemType,
500
        int $userId,
501
        int $sessionId = 0
502
    ): array {
503
        $sequenceList = [];
504
        $em = $this->getEntityManager();
505
        $gradebookCategoryRepo = $em->getRepository(GradebookCategory::class);
506
507
        $sessionUserList = [];
508
        $checkOnlySameSession = $this->settingsManager->getSetting('course.course_sequence_valid_only_in_same_session', true);
509
510
        if (SequenceResource::COURSE_TYPE === $resourceType) {
511
            if ($checkOnlySameSession) {
512
                $sessionUserList = [$sessionId];
513
            } else {
514
                $sessions = $em->getRepository(SessionRelUser::class)->findBy(['user' => $userId]);
515
                foreach ($sessions as $sessionRelUser) {
516
                    $sessionUserList[] = $sessionRelUser->getSession()->getId();
517
                }
518
            }
519
        }
520
521
        foreach ($sequences as $sequenceId => $sequence) {
522
            $item = ['name' => $sequence['name'], $itemType => []];
523
524
            foreach ($sequence[$itemType] as $resource) {
525
                switch ($resourceType) {
526
                    case SequenceResource::SESSION_TYPE:
527
                        $id = $resource->getId();
528
                        $resourceItem = ['name' => $resource->getName(), 'status' => true];
529
530
                        /* @var SessionRelCourse $sessionCourse */
531
                        foreach ($resource->getCourses() as $sessionCourse) {
532
                            $course = $sessionCourse->getCourse();
533
                            $session = $sessionCourse->getSession();
534
                            $categories = $gradebookCategoryRepo->findBy([
535
                                'course' => $course,
536
                                'session' => $session,
537
                                'isRequirement' => true,
538
                            ]);
539
540
                            foreach ($categories as $category) {
541
                                $resourceItem['status'] = $resourceItem['status'] && Category::userFinishedCourse(
542
                                        $userId,
543
                                        $category
544
                                    );
545
                            }
546
                        }
547
                        break;
548
549
                    case SequenceResource::COURSE_TYPE:
550
                        $id = $resource->getId();
551
                        $status = $this->checkCourseRequirements($userId, $resource, $sessionId);
552
553
                        if (!$status) {
554
                            foreach (SessionManager::get_session_by_course($id) as $session) {
555
                                if (in_array($session['id'], $sessionUserList)) {
556
                                    $status = $this->checkCourseRequirements($userId, $resource, $session['id']);
557
                                    if ($status) break;
558
                                }
559
                            }
560
                        }
561
562
                        $resourceItem = [
563
                            'name' => $resource->getTitle(),
564
                            'code' => $resource->getCode(),
565
                            'status' => $status,
566
                        ];
567
                        break;
568
                }
569
570
                if (!empty($id)) {
571
                    $item[$itemType][$id] = $resourceItem;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $resourceItem does not seem to be defined for all execution paths leading up to this point.
Loading history...
572
                }
573
            }
574
575
            $sequenceList[$sequenceId] = $item;
576
        }
577
578
        return $sequenceList;
579
    }
580
}
581