Passed
Pull Request — master (#5831)
by
unknown
08:20
created

SessionRepository::getPastSessionsOfUserInUrl()   A

Complexity

Conditions 6
Paths 1

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 15
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 32
rs 9.2222
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 Chamilo\CoreBundle\Entity\AccessUrl;
10
use Chamilo\CoreBundle\Entity\Course;
11
use Chamilo\CoreBundle\Entity\Session;
12
use Chamilo\CoreBundle\Entity\SessionRelCourse;
13
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
14
use Chamilo\CoreBundle\Entity\SessionRelUser;
15
use Chamilo\CoreBundle\Entity\User;
16
use Chamilo\CoreBundle\Settings\SettingsManager;
17
use DateTime;
18
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
19
use Doctrine\Common\Collections\ArrayCollection;
20
use Doctrine\ORM\Query\Expr\Join;
21
use Doctrine\ORM\QueryBuilder;
22
use Doctrine\Persistence\ManagerRegistry;
23
use Exception;
24
25
/**
26
 * @author Julio Montoya <[email protected]>
27
 */
28
class SessionRepository extends ServiceEntityRepository
29
{
30
    public function __construct(
31
        ManagerRegistry $registry,
32
        private readonly SettingsManager $settingsManager,
33
    ) {
34
        parent::__construct($registry, Session::class);
35
    }
36
37
    public function create(): ?Session
38
    {
39
        return new Session();
40
    }
41
42
    public function update(Session $session): void
43
    {
44
        $this->getEntityManager()->persist($session);
45
        $this->getEntityManager()->flush();
46
    }
47
48
    /**
49
     * @return array<SessionRelUser>
50
     */
51
    public function getUsersByAccessUrl(Session $session, AccessUrl $url, array $relationTypeList = []): array
52
    {
53
        if (0 === $session->getUsers()->count()) {
54
            return [];
55
        }
56
57
        $qb = $this->addSessionRelUserFilterByUrl($session, $url);
58
        $qb->orderBy('sru.relationType');
59
60
        if ($relationTypeList) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $relationTypeList of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
61
            $qb->andWhere(
62
                $qb->expr()->in('sru.relationType', $relationTypeList)
63
            );
64
        }
65
66
        return $qb->getQuery()->getResult();
67
    }
68
69
    public function getSessionsByUser(User $user, AccessUrl $url): QueryBuilder
70
    {
71
        $qb = $this->createQueryBuilder('s');
72
        $qb
73
            ->innerJoin('s.users', 'sru')
74
            ->leftJoin('s.urls', 'urls')
75
            ->where($qb->expr()->eq('sru.user', ':user'))
76
            ->andWhere($qb->expr()->eq('urls.url', ':url'))
77
            ->setParameters([
78
                'user' => $user,
79
                'url' => $url,
80
            ])
81
            ->orderBy('s.displayStartDate', 'ASC')
82
            ->addOrderBy('s.title', 'ASC')
83
            ->addOrderBy('s.position', 'ASC')
84
        ;
85
86
        return $qb;
87
    }
88
89
    /**
90
     * @return array<int, Session>
91
     *
92
     * @throws Exception
93
     */
94
    public function getPastSessionsOfUserInUrl(User $user, AccessUrl $url): array
95
    {
96
        $sessions = $this->getSubscribedSessionsOfUserInUrl($user, $url);
97
98
        $filterPastSessions = function (Session $session) use ($user) {
99
            $now = new DateTime();
100
            // Determine if the user is a coach
101
            $userIsCoach = $session->hasCoach($user);
102
103
            // Check if the session has a duration
104
            if ($session->getDuration() > 0) {
105
                $daysLeft = $session->getDaysLeftByUser($user);
106
                $session->setTitle($session->getTitle().'<-'.$daysLeft);
107
108
                return $daysLeft < 0 && !$userIsCoach;
109
            }
110
111
            // Get the appropriate end date based on whether the user is a coach
112
            $sessionEndDate = $userIsCoach && $session->getCoachAccessEndDate()
113
                ? $session->getCoachAccessEndDate()
114
                : $session->getAccessEndDate();
115
116
            // If there's no end date, the session is not considered past
117
            if (!$sessionEndDate) {
118
                return false;
119
            }
120
121
            // Check if the current date is after the end date
122
            return $now > $sessionEndDate;
123
        };
124
125
        return array_filter($sessions, $filterPastSessions);
126
    }
127
128
    /**
129
     * @return array<int, Session>
130
     *
131
     * @throws Exception
132
     */
133
    public function getCurrentSessionsOfUserInUrl(User $user, AccessUrl $url): array
134
    {
135
        $sessions = $this->getSubscribedSessionsOfUserInUrl($user, $url);
136
137
        $filterCurrentSessions = function (Session $session) use ($user, $url) {
138
            $userIsGeneralCoach = $session->hasUserAsGeneralCoach($user);
139
            if (!$userIsGeneralCoach) {
140
                $coursesAsCoach = $this->getSessionCoursesByStatusInCourseSubscription($user, $session, Session::COURSE_COACH, $url);
141
                $coursesAsStudent = $this->getSessionCoursesByStatusInCourseSubscription($user, $session, Session::STUDENT, $url);
142
                $validCourses = array_merge($coursesAsCoach, $coursesAsStudent);
143
144
                if (empty($validCourses)) {
145
                    return false;
146
                }
147
                $session->setCourses(new ArrayCollection($validCourses));
148
            }
149
150
            $userIsCoach = $session->hasCoach($user);
151
152
            // Check if session has a duration
153
            if ($session->getDuration() > 0) {
154
                $daysLeft = $session->getDaysLeftByUser($user);
155
156
                return $daysLeft >= 0 || $userIsCoach;
157
            }
158
159
            // Determine the start date based on whether the user is a coach
160
            $sessionStartDate = $userIsCoach && $session->getCoachAccessStartDate()
161
                ? $session->getCoachAccessStartDate()
162
                : $session->getAccessStartDate();
163
164
            // If there is no start date, consider the session current
165
            if (!$sessionStartDate) {
166
                return true;
167
            }
168
169
            // Get the current date and time
170
            $now = new DateTime();
171
172
            // Determine the end date based on whether the user is a coach
173
            $sessionEndDate = $userIsCoach && $session->getCoachAccessEndDate()
174
                ? $session->getCoachAccessEndDate()
175
                : $session->getAccessEndDate();
176
177
            // Check if the current date is within the start and end dates
178
            return $now >= $sessionStartDate && (!$sessionEndDate || $now <= $sessionEndDate);
179
        };
180
181
        return array_filter($sessions, $filterCurrentSessions);
182
    }
183
184
    /**
185
     * @return array<int, Session>
186
     *
187
     * @throws Exception
188
     */
189
    public function getUpcomingSessionsOfUserInUrl(User $user, AccessUrl $url): array
190
    {
191
        $sessions = $this->getSubscribedSessionsOfUserInUrl($user, $url);
192
193
        $filterUpcomingSessions = function (Session $session) use ($user) {
194
            $now = new DateTime();
195
196
            // All session with access by duration call be either current or past
197
            if ($session->getDuration() > 0) {
198
                return false;
199
            }
200
201
            // Determine if the user is a coach
202
            $userIsCoach = $session->hasCoach($user);
203
204
            // Get the appropriate start date based on whether the user is a coach
205
            $sessionStartDate = $userIsCoach && $session->getCoachAccessStartDate()
206
                ? $session->getCoachAccessStartDate()
207
                : $session->getAccessStartDate();
208
209
            // If there's no start date, the session is not considered future
210
            if (!$sessionStartDate) {
211
                return false;
212
            }
213
214
            // Check if the current date is before the start date
215
            return $now < $sessionStartDate;
216
        };
217
218
        return array_filter($sessions, $filterUpcomingSessions);
219
    }
220
221
    public function addUserInCourse(int $relationType, User $user, Course $course, Session $session): void
222
    {
223
        if (!$user->isActive()) {
224
            throw new Exception('User not active');
225
        }
226
227
        if (!$session->hasCourse($course)) {
228
            $msg = \sprintf('Course %s is not subscribed to the session %s', $course->getTitle(), $session->getTitle());
229
230
            throw new Exception($msg);
231
        }
232
233
        if (!\in_array($relationType, Session::getRelationTypeList(), true)) {
234
            throw new Exception(\sprintf('Cannot handle relationType %s', $relationType));
235
        }
236
237
        $entityManager = $this->getEntityManager();
238
        $existingRecord = $entityManager->getRepository(SessionRelUser::class)->findOneBy([
239
            'session' => $session,
240
            'user' => $user,
241
            'relationType' => $relationType,
242
        ]);
243
244
        if ($existingRecord) {
245
            $entityManager->remove($existingRecord);
246
            $entityManager->flush();
247
        }
248
249
        switch ($relationType) {
250
            case Session::DRH:
251
                if ($user->hasRole('ROLE_HR')) {
252
                    $session->addUserInSession(Session::DRH, $user);
253
                }
254
255
                break;
256
257
            case Session::STUDENT:
258
                $session
259
                    ->addUserInSession(Session::STUDENT, $user)
260
                    ->addUserInCourse(Session::STUDENT, $user, $course)
261
                ;
262
263
                break;
264
265
            case Session::COURSE_COACH:
266
                if ($user->hasRole('ROLE_TEACHER')) {
267
                    $session
268
                        ->addUserInSession(Session::COURSE_COACH, $user)
269
                        ->addUserInCourse(
270
                            Session::COURSE_COACH,
271
                            $user,
272
                            $course
273
                        )
274
                    ;
275
                }
276
277
                break;
278
        }
279
280
        $entityManager->persist($session);
281
        $entityManager->flush();
282
    }
283
284
    /**
285
     * @return array<SessionRelCourse>
286
     */
287
    public function getSessionCoursesByStatusInUserSubscription(User $user, Session $session, int $relationType, ?AccessUrl $url = null): array
288
    {
289
        $qb = $this->getEntityManager()->createQueryBuilder();
290
291
        $qb->select('src')
292
            ->from(SessionRelCourse::class, 'src')
293
            ->innerJoin(
294
                SessionRelUser::class,
295
                'sru',
296
                Join::WITH,
297
                'src.session = sru.session'
298
            )
299
            ->innerJoin('src.session', 'session')
300
            ->where(
301
                $qb->expr()->eq('session', ':session')
302
            )
303
            ->andWhere(
304
                $qb->expr()->eq('sru.user', ':user')
305
            )
306
            ->andWhere(
307
                $qb->expr()->eq('sru.relationType', ':relation_type')
308
            )
309
        ;
310
311
        $parameters = [
312
            'session' => $session,
313
            'user' => $user,
314
            'relation_type' => $relationType,
315
        ];
316
317
        if ($url) {
318
            $qb->innerJoin('session.urls', 'urls')
319
                ->andWhere(
320
                    $qb->expr()->eq('urls.url', ':url')
321
                )
322
            ;
323
324
            $parameters['url'] = $url;
325
        }
326
327
        $qb->setParameters($parameters);
328
329
        return $qb->getQuery()->getResult();
330
    }
331
332
    /**
333
     * @return array<SessionRelCourse>
334
     */
335
    public function getSessionCoursesByStatusInCourseSubscription(User $user, Session $session, int $status, ?AccessUrl $url = null): array
336
    {
337
        $qb = $this->getEntityManager()->createQueryBuilder();
338
339
        $qb->select('src')
340
            ->from(SessionRelCourse::class, 'src')
341
            ->innerJoin(
342
                SessionRelCourseRelUser::class,
343
                'srcru',
344
                Join::WITH,
345
                'src.session = srcru.session AND src.course = srcru.course'
346
            )
347
            ->innerJoin('srcru.session', 'session')
348
            ->where(
349
                $qb->expr()->eq('session', ':session')
350
            )
351
            ->andWhere(
352
                $qb->expr()->eq('srcru.user', ':user')
353
            )
354
            ->andWhere(
355
                $qb->expr()->eq('srcru.status', ':status')
356
            )
357
        ;
358
359
        $parameters = [
360
            'session' => $session,
361
            'user' => $user,
362
            'status' => $status,
363
        ];
364
365
        if ($url) {
366
            $qb->innerJoin('session.urls', 'urls')
367
                ->andWhere(
368
                    $qb->expr()->eq('urls.url', ':url')
369
                )
370
            ;
371
372
            $parameters['url'] = $url;
373
        }
374
375
        $qb->setParameters($parameters);
376
377
        return $qb->getQuery()->getResult();
378
    }
379
380
    private function addSessionRelUserFilterByUrl(Session $session, AccessUrl $url): QueryBuilder
381
    {
382
        $qb = $this->getEntityManager()->createQueryBuilder();
383
        $qb
384
            ->select('sru')
385
            ->from(SessionRelUser::class, 'sru')
386
            ->innerJoin('sru.user', 'u')
387
            ->innerJoin('u.portals', 'p')
388
            ->andWhere('sru.session = :session AND p.url = :url')
389
            ->setParameters([
390
                'session' => $session,
391
                'url' => $url,
392
            ])
393
        ;
394
395
        return $qb;
396
    }
397
398
    public function getUserFollowedSessionsInAccessUrl(User $user, AccessUrl $url): QueryBuilder
399
    {
400
        $callback = fn (Session $session) => $session->getId();
401
402
        if ($user->isHRM()) {
403
            $idList = array_map($callback, $user->getDRHSessions());
404
        } elseif ($user->isTeacher() || COURSEMANAGER === $user->getStatus()) {
405
            $idListAsCoach = $user
406
                ->getSessionsByStatusInCourseSubscription(Session::COURSE_COACH)
407
                ->map($callback)
408
                ->getValues()
409
            ;
410
            $idListAsGeneralCoach = array_map($callback, $user->getSessionsAsGeneralCoach());
411
            $idList = array_merge($idListAsCoach, $idListAsGeneralCoach);
412
        } elseif ($user->isSessionAdmin()) {
413
            $idList = array_map($callback, $user->getSessionsAsAdmin());
414
        } else {
415
            $idList = array_map($callback, $user->getSessionsAsStudent());
416
        }
417
418
        $qb = $this->createQueryBuilder('s');
419
        $qb
420
            ->innerJoin('s.urls', 'u')
421
            ->where($qb->expr()->eq('u.url', $url->getId()))
422
            ->andWhere($qb->expr()->in('s.id', ':id_list'))
423
            ->setParameter('id_list', $idList)
424
        ;
425
426
        return $qb;
427
    }
428
429
    /**
430
     * @return array<int, Session>
431
     *
432
     * @throws Exception
433
     */
434
    public function getSubscribedSessionsOfUserInUrl(
435
        User $user,
436
        AccessUrl $url,
437
        bool $ignoreVisibilityForAdmins = false,
438
    ): array {
439
        $sessions = $this->getSessionsByUser($user, $url)->getQuery()->getResult();
440
441
        $filterSessions = function (Session $session) use ($user, $ignoreVisibilityForAdmins) {
442
            $visibility = $session->setAccessVisibilityByUser($user, $ignoreVisibilityForAdmins);
443
444
            if (Session::VISIBLE !== $visibility) {
445
                $closedOrHiddenCourses = $session->getClosedOrHiddenCourses();
446
447
                if ($closedOrHiddenCourses->count() === $session->getCourses()->count()) {
448
                    $visibility = Session::INVISIBLE;
449
                }
450
            }
451
452
            switch ($visibility) {
453
                case Session::READ_ONLY:
454
                case Session::VISIBLE:
455
                case Session::AVAILABLE:
456
                    break;
457
458
                case Session::INVISIBLE:
459
                    if (!$ignoreVisibilityForAdmins) {
460
                        return false;
461
                    }
462
            }
463
464
            return true;
465
        };
466
467
        return array_filter($sessions, $filterSessions);
468
    }
469
470
    /**
471
     * Finds a valid child session based on access dates and reinscription days.
472
     *
473
     * @param Session $session
474
     * @return Session|null
475
     */
476
    public function findValidChildSession(Session $session): ?Session
477
    {
478
        $childSessions = $this->findChildSessions($session);
479
        $now = new \DateTime();
480
481
        foreach ($childSessions as $childSession) {
482
            $startDate = $childSession->getAccessStartDate();
483
            $endDate = $childSession->getAccessEndDate();
484
            $daysToReinscription = $childSession->getDaysToReinscription();
485
486
            if (empty($daysToReinscription) || $daysToReinscription <= 0) {
487
                continue;
488
            }
489
490
            $adjustedEndDate = (clone $endDate)->modify('-' . $daysToReinscription . ' days');
491
492
            if ($startDate <= $now && $adjustedEndDate >= $now) {
493
                return $childSession;
494
            }
495
        }
496
        return null;
497
    }
498
499
    /**
500
     * Finds a valid parent session based on access dates and reinscription days.
501
     */
502
    public function findValidParentSession(Session $session): ?Session
503
    {
504
        $parentSession = $this->findParentSession($session);
505
        if ($parentSession) {
506
            $now = new \DateTime();
507
            $startDate = $parentSession->getAccessStartDate();
508
            $endDate = $parentSession->getAccessEndDate();
509
            $daysToReinscription = $parentSession->getDaysToReinscription();
510
511
            // Return null if days to reinscription is not set
512
            if ($daysToReinscription === null || $daysToReinscription === '') {
513
                return null;
514
            }
515
516
            // Adjust the end date by days to reinscription
517
            $endDate = $endDate->modify('-' . $daysToReinscription . ' days');
518
519
            // Check if the current date falls within the session's validity period
520
            if ($startDate <= $now && $endDate >= $now) {
521
                return $parentSession;
522
            }
523
        }
524
        return null;
525
    }
526
527
    /**
528
     * Finds child sessions based on the parent session.
529
     */
530
    public function findChildSessions(Session $parentSession): array
531
    {
532
        return $this->createQueryBuilder('s')
533
            ->where('s.parentId = :parentId')
534
            ->setParameter('parentId', $parentSession->getId())
535
            ->getQuery()
536
            ->getResult();
537
    }
538
539
    /**
540
     * Finds the parent session for a given session.
541
     */
542
    public function findParentSession(Session $session): ?Session
543
    {
544
        if ($session->getParentId()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $session->getParentId() of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
545
            return $this->find($session->getParentId());
546
        }
547
548
        return null;
549
    }
550
551
    /**
552
     * Find sessions without child and ready for repetition.
553
     *
554
     * @return Session[]
555
     */
556
    public function findSessionsWithoutChildAndReadyForRepetition()
557
    {
558
        $currentDate = new \DateTime();
559
560
        $qb = $this->createQueryBuilder('s')
561
            ->where('s.daysToNewRepetition IS NOT NULL')
562
            ->andWhere('s.lastRepetition = :false')
563
            ->andWhere(':currentDate BETWEEN DATE_SUB(s.accessEndDate, s.daysToNewRepetition, \'DAY\') AND s.accessEndDate')
564
            ->andWhere('NOT EXISTS (
565
                SELECT 1
566
                FROM Chamilo\CoreBundle\Entity\Session child
567
                WHERE child.parentId = s.id
568
                AND child.accessEndDate >= :currentDate
569
            )')
570
            ->setParameter('false', false)
571
            ->setParameter('currentDate', $currentDate);
572
573
        return $qb->getQuery()->getResult();
574
    }
575
576
    public function countUsersBySession(int $sessionId, int $relationType = Session::STUDENT): int
577
    {
578
        $qb = $this->createQueryBuilder('s');
579
        $qb->select('COUNT(sru.id)')
580
            ->innerJoin('s.users', 'sru')
581
            ->where('s.id = :sessionId')
582
            ->andWhere('sru.relationType = :relationType')
583
            ->setParameter('sessionId', $sessionId)
584
            ->setParameter('relationType', $relationType)
585
        ;
586
587
        return (int) $qb->getQuery()->getSingleScalarResult();
588
    }
589
}
590