Passed
Push — master ( 9d97a9...43700c )
by Yannick
10:26 queued 01:53
created

SequenceResourceRepository::findVerticesEdges()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 15
c 0
b 0
f 0
nc 7
nop 2
dl 0
loc 28
rs 9.4555
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
                                            true,
278
                                            $course->getId(),
279
                                            $resource->getId()
280
                                    );
281
                                }
282
                            }
283
                        }
284
285
                        break;
286
287
                    case SequenceResource::COURSE_TYPE:
288
                        $id = $resource->getId();
289
                        $status = $this->checkCourseRequirements($userId, $resource, 0);
290
291
                        if (!$status) {
292
                            $sessionsInCourse = SessionManager::get_session_by_course($id);
293
                            foreach ($sessionsInCourse as $session) {
294
                                if (\in_array($session['id'], $sessionUserList, true)) {
295
                                    $status = $this->checkCourseRequirements($userId, $resource, $session['id']);
296
                                    if ($status) {
297
                                        break;
298
                                    }
299
                                }
300
                            }
301
                        }
302
303
                        $resourceItem = [
304
                            'name' => $resource->getTitle(),
305
                            'status' => $status,
306
                        ];
307
308
                        break;
309
                }
310
311
                if (empty($id)) {
312
                    continue;
313
                }
314
315
                $item['requirements'][$id] = $resourceItem;
316
            }
317
            $sequenceList[$sequenceId] = $item;
318
        }
319
320
        return $sequenceList;
321
    }
322
323
    public function checkCourseRequirements(int $userId, Course $course, int $sessionId = 0): bool
324
    {
325
        $em = $this->getEntityManager();
326
        $session = $sessionId > 0
327
            ? $em->getRepository(Session::class)->find($sessionId)
328
            : null;
329
        $gradebookCategoryRepo = $em->getRepository(GradebookCategory::class);
330
        $categories = $gradebookCategoryRepo->findBy([
331
            'course' => $course,
332
            'session' => $session,
333
            'isRequirement' => true,
334
        ]);
335
336
        if (empty($categories) && $sessionId > 0) {
337
            $categories = $gradebookCategoryRepo->findBy([
338
                'course' => $course,
339
                'session' => null,
340
                'isRequirement' => true,
341
            ]);
342
        }
343
344
        if (empty($categories)) {
345
            return false;
346
        }
347
348
        $status = true;
349
        foreach ($categories as $category) {
350
            $userFinishedCourse = Category::userFinishedCourse(
351
                $userId,
352
                $category,
353
                true,
354
                $course->getId(),
355
                $sessionId
356
            );
357
358
            if (!$userFinishedCourse) {
359
                $status = false;
360
361
                break;
362
            }
363
        }
364
365
        return $status;
366
    }
367
368
    /**
369
     * Check if at least one sequence are completed.
370
     *
371
     * @param array $sequences The sequences
372
     *
373
     * @return bool
374
     */
375
    public function checkSequenceAreCompleted(array $sequences)
376
    {
377
        foreach ($sequences as $sequence) {
378
379
            if (!isset($sequence['requirements'])) {
380
                continue;
381
            }
382
383
            $status = true;
384
385
            foreach ($sequence['requirements'] as $item) {
386
                $status = $status && $item['status'];
387
            }
388
389
            if ($status) {
390
                return true;
391
            }
392
        }
393
394
        return false;
395
    }
396
397
    /**
398
     * Get sessions from vertices.
399
     *
400
     * @param Vertices $verticesEdges The vertices
401
     *
402
     * @return array
403
     */
404
    protected function findVerticesEdges(Vertices $verticesEdges, int $type)
405
    {
406
        $sessionVertices = [];
407
        $em = $this->getEntityManager();
408
409
        foreach ($verticesEdges as $supVertex) {
410
            $vertexId = $supVertex->getId();
411
412
            switch ($type) {
413
                case SequenceResource::SESSION_TYPE:
414
                    $resource = $em->getRepository(Session::class)->find($vertexId);
415
416
                    break;
417
418
                case SequenceResource::COURSE_TYPE:
419
                    $resource = $em->getRepository(Course::class)->find($vertexId);
420
421
                    break;
422
            }
423
424
            if (empty($resource)) {
425
                continue;
426
            }
427
428
            $sessionVertices[$vertexId] = $resource;
429
        }
430
431
        return $sessionVertices;
432
    }
433
434
    public function getDependents(int $resourceId, int $type): array
435
    {
436
        return $this->getRequirementsOrDependents($resourceId, $type, 'dependents');
437
    }
438
439
    public function checkDependentsForUser(array $sequences, int $type, int $userId, int $sessionId = 0): array
440
    {
441
        return $this->checkRequirementsOrDependentsForUser(
442
            $sequences,
443
            $type,
444
            'dependents',
445
            $userId,
446
            $sessionId
447
        );
448
    }
449
450
    private function getRequirementsOrDependents(int $resourceId, int $resourceType, string $itemType): array
451
    {
452
        $em = $this->getEntityManager();
453
454
        $sequencesResource = $this->findBy(['resourceId' => $resourceId, 'type' => $resourceType]);
455
        $result = [];
456
457
        foreach ($sequencesResource as $sequenceResource) {
458
            if (!$sequenceResource->hasGraph()) {
459
                continue;
460
            }
461
462
            $sequence = $sequenceResource->getSequence();
463
            $graph = $sequence->getUnSerializeGraph();
464
            $vertex = $graph->getVertex($resourceId);
465
466
            $edges = $itemType === 'requirements'
467
                ? $vertex->getVerticesEdgeFrom()
468
                : $vertex->getVerticesEdgeTo();
469
470
            $sequenceInfo = [
471
                'name' => $sequence->getTitle(),
472
                $itemType => [],
473
            ];
474
475
            foreach ($edges as $edge) {
476
                $vertexId = $edge->getId();
477
                $resource = null;
478
479
                switch ($resourceType) {
480
                    case SequenceResource::SESSION_TYPE:
481
                        $resource = $em->getRepository(Session::class)->find($vertexId);
482
                        break;
483
                    case SequenceResource::COURSE_TYPE:
484
                        $resource = $em->getRepository(Course::class)->find($vertexId);
485
                        break;
486
                }
487
488
                if (null === $resource) {
489
                    continue;
490
                }
491
492
                $sequenceInfo[$itemType][$vertexId] = $resource;
493
            }
494
495
            $result[$sequence->getId()] = $sequenceInfo;
496
        }
497
498
        return $result;
499
    }
500
501
    private function checkRequirementsOrDependentsForUser(
502
        array $sequences,
503
        int $resourceType,
504
        string $itemType,
505
        int $userId,
506
        int $sessionId = 0
507
    ): array {
508
        $sequenceList = [];
509
        $em = $this->getEntityManager();
510
        $gradebookCategoryRepo = $em->getRepository(GradebookCategory::class);
511
512
        $sessionUserList = [];
513
        $checkOnlySameSession = $this->settingsManager->getSetting('course.course_sequence_valid_only_in_same_session', true);
514
515
        if (SequenceResource::COURSE_TYPE === $resourceType) {
516
            if ($checkOnlySameSession) {
517
                $sessionUserList = [$sessionId];
518
            } else {
519
                $sessions = $em->getRepository(SessionRelUser::class)->findBy(['user' => $userId]);
520
                foreach ($sessions as $sessionRelUser) {
521
                    $sessionUserList[] = $sessionRelUser->getSession()->getId();
522
                }
523
            }
524
        }
525
526
        foreach ($sequences as $sequenceId => $sequence) {
527
            $item = ['name' => $sequence['name'], $itemType => []];
528
529
            foreach ($sequence[$itemType] as $resource) {
530
                switch ($resourceType) {
531
                    case SequenceResource::SESSION_TYPE:
532
                        $id = $resource->getId();
533
                        $resourceItem = ['name' => $resource->getName(), 'status' => true];
534
535
                        /* @var SessionRelCourse $sessionCourse */
536
                        foreach ($resource->getCourses() as $sessionCourse) {
537
                            $course = $sessionCourse->getCourse();
538
                            $session = $sessionCourse->getSession();
539
                            $categories = $gradebookCategoryRepo->findBy([
540
                                'course' => $course,
541
                                'session' => $session,
542
                                'isRequirement' => true,
543
                            ]);
544
545
                            foreach ($categories as $category) {
546
                                $resourceItem['status'] = $resourceItem['status'] && Category::userFinishedCourse(
547
                                        $userId,
548
                                        $category,
549
                                        true,
550
                                        $course->getId(),
551
                                        $sessionId
552
                                    );
553
                            }
554
                        }
555
                        break;
556
557
                    case SequenceResource::COURSE_TYPE:
558
                        $id = $resource->getId();
559
                        $status = $this->checkCourseRequirements($userId, $resource, $sessionId);
560
561
                        if (!$status) {
562
                            foreach (SessionManager::get_session_by_course($id) as $session) {
563
                                if (in_array($session['id'], $sessionUserList)) {
564
                                    $status = $this->checkCourseRequirements($userId, $resource, $session['id']);
565
                                    if ($status) break;
566
                                }
567
                            }
568
                        }
569
570
                        $resourceItem = [
571
                            'id' => $resource->getId(),
572
                            'name' => $resource->getTitle(),
573
                            'code' => $resource->getCode(),
574
                            'status' => $status,
575
                        ];
576
                        break;
577
                }
578
579
                if (!empty($id)) {
580
                    $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...
581
                }
582
            }
583
584
            $sequenceList[$sequenceId] = $item;
585
        }
586
587
        return $sequenceList;
588
    }
589
}
590