Passed
Pull Request — 1.11.x (#4473)
by Angel Fernando Quiroz
09:07
created

findRequirementForResource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
namespace Chamilo\CoreBundle\Entity\Repository;
6
7
use Category;
8
use Chamilo\CoreBundle\Entity\Course;
9
use Chamilo\CoreBundle\Entity\GradebookCategory;
10
use Chamilo\CoreBundle\Entity\SequenceResource;
11
use Chamilo\CoreBundle\Entity\Session;
12
use Chamilo\CoreBundle\Entity\SessionRelUser;
13
use Doctrine\ORM\EntityRepository;
14
use Doctrine\ORM\OptimisticLockException;
15
use Doctrine\ORM\ORMException;
16
use Fhaculty\Graph\Set\Vertices;
17
use Fhaculty\Graph\Vertex;
18
use SessionManager;
19
20
/**
21
 * Class SequenceResourceRepository.
22
 */
23
class SequenceResourceRepository extends EntityRepository
24
{
25
    public const VERTICES_TYPE_REQ = 'requirements';
26
    public const VERTICES_TYPE_DEP = 'dependents';
27
28
    /**
29
     * Find the SequenceResource based in the resourceId and type.
30
     *
31
     * @param int $resourceId
32
     * @param int $type
33
     */
34
    public function findRequirementForResource($resourceId, $type): ?SequenceResource
35
    {
36
        return $this->findOneBy(['resourceId' => $resourceId, 'type' => $type]);
37
    }
38
39
    /**
40
     * @todo implement for all types only work for sessions
41
     *
42
     * @param int $resourceId
43
     * @param int $type
44
     */
45
    public function getRequirementAndDependencies($resourceId, $type): array
46
    {
47
        $sequence = $this->findRequirementForResource($resourceId, $type);
48
        $result = ['requirements' => [], 'dependencies' => []];
49
        if ($sequence && $sequence->hasGraph()) {
50
            $graph = $sequence->getSequence()->getUnSerializeGraph();
51
            $vertex = $graph->getVertex($resourceId);
52
            $from = $vertex->getVerticesEdgeFrom();
53
54
            foreach ($from as $subVertex) {
55
                $vertexId = $subVertex->getId();
56
                $sessionInfo = api_get_session_info($vertexId);
57
                $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

57
                $sessionInfo['admin_link'] = '<a href="'./** @scrutinizer ignore-type */ SessionManager::getAdminPath($vertexId).'">'.$sessionInfo['name'].'</a>';
Loading history...
58
                $result['requirements'][] = $sessionInfo;
59
            }
60
61
            $to = $vertex->getVerticesEdgeTo();
62
            foreach ($to as $subVertex) {
63
                $vertexId = $subVertex->getId();
64
                $sessionInfo = api_get_session_info($vertexId);
65
                $sessionInfo['admin_link'] = '<a href="'.SessionManager::getAdminPath($vertexId).'">'.$sessionInfo['name'].'</a>';
66
                $result['dependencies'][] = $sessionInfo;
67
            }
68
        }
69
70
        return $result;
71
    }
72
73
    /**
74
     * Deletes a node and check in all the dependencies if the node exists in
75
     * order to delete.
76
     *
77
     * @param int $resourceId
78
     * @param int $type
79
     *
80
     * @throws ORMException
81
     * @throws OptimisticLockException
82
     */
83
    public function deleteResource($resourceId, $type)
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 ($sequence && $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
    public function getRequirements($resourceId, $type): array
118
    {
119
        return $this->getRequirementsOrDependents($resourceId, $type, self::VERTICES_TYPE_REQ);
120
    }
121
122
    /**
123
     * Get the requirements for a resource only.
124
     */
125
    public function getDependents(int $resourceId, int $type): array
126
    {
127
        return $this->getRequirementsOrDependents($resourceId, $type, self::VERTICES_TYPE_DEP);
128
    }
129
130
    /**
131
     * Get the requirements and dependencies within a sequence for a resource.
132
     */
133
    public function getRequirementsAndDependenciesWithinSequences(int $resourceId, int $type): array
134
    {
135
        $sequencesResource = $this->findBy([
136
            'resourceId' => $resourceId,
137
            'type' => $type,
138
        ]);
139
140
        $result = [];
141
142
        /** @var SequenceResource $sequenceResource */
143
        foreach ($sequencesResource as $sequenceResource) {
144
            if (!$sequenceResource->hasGraph()) {
145
                continue;
146
            }
147
148
            $sequence = $sequenceResource->getSequence();
149
            $graph = $sequence->getUnSerializeGraph();
150
            $vertex = $graph->getVertex($resourceId);
151
            $from = $vertex->getVerticesEdgeFrom();
152
            $to = $vertex->getVerticesEdgeTo();
153
154
            $requirements = $this->findVerticesEdges($from, $type);
155
            $dependencies = $this->findVerticesEdges($to, $type);
156
157
            $result[$sequence->getId()] = [
158
                'name' => $sequence->getName(),
159
                'requirements' => $requirements,
160
                'dependencies' => $dependencies,
161
            ];
162
        }
163
164
        return $result;
165
    }
166
167
    /**
168
     * Check if the ser has completed the requirements for the sequences.
169
     *
170
     * @param array $sequences The sequences
171
     * @param int   $type      The type of sequence resource
172
     * @param int   $userId
173
     * @param int   $sessionId
174
     */
175
    public function checkRequirementsForUser(array $sequences, int $type, $userId, $sessionId = 0): array
176
    {
177
        return $this->checkRequirementsOrDependentsForUser(
178
            $sequences,
179
            $type,
180
            self::VERTICES_TYPE_REQ,
181
            $userId,
182
            $sessionId
183
        );
184
    }
185
186
    /**
187
     * Check if the ser has completed the requirements for the sequences.
188
     *
189
     * @param array $sequences The sequences
190
     * @param int   $type      The type of sequence resource
191
     * @param int   $userId
192
     * @param int   $sessionId
193
     */
194
    public function checkDependentsForUser(array $sequences, int $type, $userId, $sessionId = 0): array
195
    {
196
        return $this->checkRequirementsOrDependentsForUser(
197
            $sequences,
198
            $type,
199
            self::VERTICES_TYPE_DEP,
200
            $userId,
201
            $sessionId
202
        );
203
    }
204
205
    public function checkCourseRequirements($userId, Course $course, $sessionId): bool
206
    {
207
        $em = $this->getEntityManager();
208
        $sessionId = (int) $sessionId;
209
210
        $gradebookCategoryRepo = $em->getRepository('ChamiloCoreBundle:GradebookCategory');
211
        $gradebooks = $gradebookCategoryRepo->findBy(
212
            [
213
                'courseCode' => $course->getCode(),
214
                'sessionId' => $sessionId,
215
                'isRequirement' => true,
216
            ]
217
        );
218
219
        if (empty($gradebooks)) {
220
            return false;
221
        }
222
223
        $status = true;
224
        foreach ($gradebooks as $gradebook) {
225
            $category = Category::createCategoryObjectFromEntity($gradebook);
226
            $userFinishedCourse = Category::userFinishedCourse(
227
                $userId,
228
                $category,
229
                true
230
            );
231
            if (0 === $sessionId) {
232
                if (false === $userFinishedCourse) {
233
                    $status = false;
234
                    break;
235
                }
236
            } else {
237
                if (false === $userFinishedCourse) {
238
                    $status = false;
239
                    break;
240
                }
241
            }
242
        }
243
244
        return $status;
245
    }
246
247
    /**
248
     * Check if at least one sequence are completed.
249
     */
250
    public function checkSequenceAreCompleted(array $sequences, $itemType = self::VERTICES_TYPE_REQ): bool
251
    {
252
        foreach ($sequences as $sequence) {
253
            $status = true;
254
255
            foreach ($sequence[$itemType] as $item) {
256
                $status = $status && $item['status'];
257
            }
258
259
            if ($status) {
260
                return true;
261
            }
262
        }
263
264
        return false;
265
    }
266
267
    /**
268
     * Get sessions from vertices.
269
     */
270
    protected function findVerticesEdges(Vertices $verticesEdges, int $type): array
271
    {
272
        $sessionVertices = [];
273
        $em = $this->getEntityManager();
274
275
        foreach ($verticesEdges as $supVertex) {
276
            $vertexId = $supVertex->getId();
277
            switch ($type) {
278
                case SequenceResource::SESSION_TYPE:
279
                    $resource = $em->getRepository('ChamiloCoreBundle:Session')->find($vertexId);
280
                    break;
281
                case SequenceResource::COURSE_TYPE:
282
                    $resource = $em->getRepository('ChamiloCoreBundle:Course')->find($vertexId);
283
                    break;
284
            }
285
286
            if (empty($resource)) {
287
                continue;
288
            }
289
290
            $sessionVertices[$vertexId] = $resource;
291
        }
292
293
        return $sessionVertices;
294
    }
295
296
    private function checkRequirementsOrDependentsForUser(
297
        array $sequences,
298
        int $resourceType,
299
        string $itemType,
300
        int $userId,
301
        int $sessionId = 0
302
    ): array {
303
        $sequenceList = [];
304
        $em = $this->getEntityManager();
305
        $gradebookCategoryRepo = $em->getRepository(GradebookCategory::class);
306
307
        $sessionUserList = [];
308
        $checkOnlySameSession = api_get_configuration_value('course_sequence_valid_only_in_same_session');
309
        if (SequenceResource::COURSE_TYPE === $resourceType) {
310
            if ($checkOnlySameSession) {
311
                $sessionUserList = [$sessionId];
312
            } else {
313
                $criteria = ['user' => $userId];
314
                $sessions = $em->getRepository('ChamiloCoreBundle:SessionRelUser')->findBy($criteria);
315
                if ($sessions) {
316
                    /** @var SessionRelUser $sessionRelUser */
317
                    foreach ($sessions as $sessionRelUser) {
318
                        $sessionUserList[] = $sessionRelUser->getSession()->getId();
319
                    }
320
                }
321
            }
322
        }
323
324
        foreach ($sequences as $sequenceId => $sequence) {
325
            $item = [
326
                'name' => $sequence['name'],
327
                $itemType => [],
328
            ];
329
            $resourceItem = null;
330
331
            foreach ($sequence[$itemType] as $resource) {
332
                switch ($resourceType) {
333
                    case SequenceResource::SESSION_TYPE:
334
                        /** @var Session $resource */
335
                        $id = $resource->getId();
336
                        $resourceItem = [
337
                            'name' => $resource->getName(),
338
                            'status' => true,
339
                        ];
340
341
                        $sessionsCourses = $resource->getCourses();
342
                        foreach ($sessionsCourses as $sessionCourse) {
343
                            $course = $sessionCourse->getCourse();
344
                            $gradebooks = $gradebookCategoryRepo->findBy(
345
                                [
346
                                    'courseCode' => $course->getCode(),
347
                                    'sessionId' => $resource->getId(),
348
                                    'isRequirement' => true,
349
                                ]
350
                            );
351
352
                            foreach ($gradebooks as $gradebook) {
353
                                $category = Category::createCategoryObjectFromEntity($gradebook);
354
                                if (!empty($userId)) {
355
                                    $resourceItem['status'] = $resourceItem['status'] && Category::userFinishedCourse(
356
                                        $userId,
357
                                        $category
358
                                    );
359
                                }
360
                            }
361
                        }
362
                        break;
363
                    case SequenceResource::COURSE_TYPE:
364
                        $id = $resource->getId();
365
                        $checkSessionId = 0;
366
                        if ($checkOnlySameSession) {
367
                            $checkSessionId = $sessionId;
368
                        }
369
                        $status = $this->checkCourseRequirements($userId, $resource, $checkSessionId);
370
371
                        if (false === $status) {
372
                            $sessionsInCourse = SessionManager::get_session_by_course($id);
373
                            foreach ($sessionsInCourse as $session) {
374
                                if (in_array($session['id'], $sessionUserList)) {
375
                                    $status = $this->checkCourseRequirements($userId, $resource, $session['id']);
376
                                    if (true === $status) {
377
                                        break;
378
                                    }
379
                                }
380
                            }
381
                        }
382
383
                        $resourceItem = [
384
                            'name' => $resource->getTitle(),
385
                            'code' => $resource->getCode(),
386
                            'status' => $status,
387
                        ];
388
389
                        break;
390
                }
391
392
                if (empty($id)) {
393
                    continue;
394
                }
395
396
                $item[$itemType][$id] = $resourceItem;
397
            }
398
            $sequenceList[$sequenceId] = $item;
399
        }
400
401
        return $sequenceList;
402
    }
403
404
    /**
405
     * Get the requirements or dependants for a resource only.
406
     */
407
    private function getRequirementsOrDependents($resourceId, int $resourceType, string $itemType): array
408
    {
409
        $em = $this->getEntityManager();
410
411
        $sequencesResource = $this->findBy(['resourceId' => $resourceId, 'type' => $resourceType]);
412
        $result = [];
413
414
        /** @var SequenceResource $sequenceResource */
415
        foreach ($sequencesResource as $sequenceResource) {
416
            if (!$sequenceResource->hasGraph()) {
417
                continue;
418
            }
419
420
            $sequence = $sequenceResource->getSequence();
421
            $graph = $sequence->getUnSerializeGraph();
422
            $vertex = $graph->getVertex($resourceId);
423
424
            if (self::VERTICES_TYPE_REQ === $itemType) {
425
                $edges = $vertex->getVerticesEdgeFrom();
426
            } else {
427
                $edges = $vertex->getVerticesEdgeTo();
428
            }
429
430
            $sequenceInfo = [
431
                'name' => $sequence->getName(),
432
                $itemType => [],
433
            ];
434
435
            foreach ($edges as $edge) {
436
                $vertexId = $edge->getId();
437
                $resource = null;
438
                switch ($resourceType) {
439
                    case SequenceResource::SESSION_TYPE:
440
                        $repo = $em->getRepository(Session::class);
441
                        $resource = $repo->find($vertexId);
442
443
                        break;
444
                    case SequenceResource::COURSE_TYPE:
445
                        $repo = $em->getRepository(Course::class);
446
                        $resource = $repo->find($vertexId);
447
448
                        break;
449
                }
450
451
                if (null === $resource) {
452
                    continue;
453
                }
454
455
                $sequenceInfo[$itemType][$vertexId] = $resource;
456
            }
457
458
            $result[$sequence->getId()] = $sequenceInfo;
459
        }
460
461
        return $result;
462
    }
463
}
464