Passed
Pull Request — master (#5831)
by
unknown
07:40
created

ReinscriptionCheckCommand::execute()   C

Complexity

Conditions 17
Paths 10

Size

Total Lines 79
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 17
eloc 41
nc 10
nop 2
dl 0
loc 79
rs 5.2166
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Command;
8
9
use Chamilo\CoreBundle\Entity\Session;
10
use Chamilo\CoreBundle\Entity\SessionRelUser;
11
use Chamilo\CourseBundle\Entity\CLp;
12
use Chamilo\CourseBundle\Repository\CLpRepository;
13
use Chamilo\CoreBundle\Repository\SessionRepository;
14
use Chamilo\CourseBundle\Entity\CLpView;
15
use Doctrine\ORM\EntityManagerInterface;
16
use Symfony\Component\Console\Command\Command;
17
use Symfony\Component\Console\Input\InputInterface;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Input\InputOption;
20
21
class ReinscriptionCheckCommand extends Command
22
{
23
    protected static $defaultName = 'app:reinscription-check';
24
25
    private CLpRepository $lpRepository;
26
    private SessionRepository $sessionRepository;
27
    private EntityManagerInterface $entityManager;
28
29
    public function __construct(
30
        CLpRepository $lpRepository,
31
        SessionRepository $sessionRepository,
32
        EntityManagerInterface $entityManager
33
    ) {
34
        parent::__construct();
35
        $this->lpRepository = $lpRepository;
36
        $this->sessionRepository = $sessionRepository;
37
        $this->entityManager = $entityManager;
38
    }
39
40
    protected function configure(): void
41
    {
42
        $this
43
            ->setDescription('Checks for users whose course completions have expired and reinscribe them into new sessions if needed.')
44
            ->addOption(
45
                'debug',
46
                null,
47
                InputOption::VALUE_NONE,
48
                'If set, debug messages will be shown.'
49
            );
50
    }
51
52
    protected function execute(InputInterface $input, OutputInterface $output): int
53
    {
54
        $debug = $input->getOption('debug');
55
56
        // 1. Find all lessons with "validity_in_days" > 0
57
        $learningPaths = $this->lpRepository->findWithValidity();
58
59
        /* @var CLp $lp */
60
        foreach ($learningPaths as $lp) {
61
            $validityDays = $lp->getValidityInDays();
62
            $sessionId = $this->lpRepository->getLpSessionId($lp->getIid());
63
64
            if (!$sessionId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sessionId of type integer|null is loosely compared to false; 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...
65
                if ($debug) {
66
                    $output->writeln('Session ID not found for Learning Path ID: ' . $lp->getIid());
67
                }
68
                continue;
69
            }
70
71
            // 2. Get the session of the lesson
72
            $session = $this->sessionRepository->find($sessionId);
73
            if (!$session) {
74
                if ($debug) {
75
                    $output->writeln('Session not found for ID: ' . $sessionId);
76
                }
77
                continue;
78
            }
79
80
            // Process only if the session is not the last repetition
81
            if ($session->getLastRepetition()) {
82
                if ($debug) {
83
                    $output->writeln('Session ' . $session->getId() . ' is the last repetition. Skipping...');
84
                }
85
                continue;
86
            }
87
88
            // 3. Find users who completed the lesson and whose validity has expired
89
            $expiredUsers = $this->findExpiredCompletions($lp, $validityDays);
90
91
            if (count($expiredUsers) === 0) {
92
                if ($debug) {
93
                    $output->writeln('No expired users found for Learning Path ID: ' . $lp->getIid());
94
                }
95
                continue;
96
            }
97
98
            foreach ($expiredUsers as $user) {
99
                if ($debug) {
100
                    $output->writeln('User ' . $user->getUser()->getId() . ' has expired completion for LP ' . $lp->getIid());
101
                }
102
103
                // 4. Find the last valid child session
104
                $validChildSession = $this->sessionRepository->findValidChildSession($session);
105
106
                if ($validChildSession) {
107
                    // Reinscribe user in the valid child session
108
                    $this->enrollUserInSession($user->getUser(), $validChildSession);
109
                    if ($debug) {
110
                        $output->writeln('Reinscribed user ' . $user->getUser()->getId() . ' into child session ' . $validChildSession->getId());
111
                    }
112
                } else {
113
                    // 5. If no valid child session, find the valid parent session
114
                    $validParentSession = $this->sessionRepository->findValidParentSession($session);
115
                    if ($validParentSession) {
116
                        // Reinscribe user in the valid parent session
117
                        $this->enrollUserInSession($user->getUser(), $validParentSession);
118
                        if ($debug) {
119
                            $output->writeln('Reinscribed user ' . $user->getUser()->getId() . ' into parent session ' . $validParentSession->getId());
120
                        }
121
                    } else {
122
                        if ($debug) {
123
                            $output->writeln('No valid parent or child session found for user ' . $user->getUser()->getId());
124
                        }
125
                    }
126
                }
127
            }
128
        }
129
130
        return Command::SUCCESS;
131
    }
132
133
    /**
134
     * Find users with expired completion based on "validity_in_days".
135
     */
136
    private function findExpiredCompletions($lp, $validityDays)
137
    {
138
        $now = new \DateTime();
139
        $expirationDate = (clone $now)->modify('-' . $validityDays . ' days');
140
141
        // Find users with 100% completion and whose last access date (start_time) is older than 'validity_in_days'
142
        return $this->entityManager->getRepository(CLpView::class)
143
            ->createQueryBuilder('v')
144
            ->innerJoin('Chamilo\CourseBundle\Entity\CLpItemView', 'iv', 'WITH', 'iv.view = v')
145
            ->where('v.lp = :lp')
146
            ->andWhere('v.progress = 100')
147
            ->andWhere('iv.startTime < :expirationDate')
148
            ->setParameter('lp', $lp)
149
            ->setParameter('expirationDate', $expirationDate->getTimestamp())
150
            ->getQuery()
151
            ->getResult();
152
    }
153
154
    /**
155
     * Enrolls a user into a session.
156
     */
157
    private function enrollUserInSession($user, $session): void
158
    {
159
        // First, check if the user is already enrolled in the session
160
        $existingSubscription = $this->findUserSubscriptionInSession($user, $session);
161
162
        if ($existingSubscription) {
163
            // Remove existing subscription before re-enrolling the user
164
            $session->removeUserSubscription($existingSubscription);
165
            $this->entityManager->persist($session);
166
            $this->entityManager->flush();
167
        }
168
169
        // Add the user into the session as a student
170
        $session->addUserInSession(Session::STUDENT, $user);
171
172
        // Save the changes to the database
173
        $this->entityManager->persist($session);
174
        $this->entityManager->flush();
175
    }
176
177
    private function findUserSubscriptionInSession($user, $session)
178
    {
179
        return $this->entityManager->getRepository(SessionRelUser::class)
180
            ->findOneBy([
181
                'user' => $user,
182
                'session' => $session,
183
            ]);
184
    }
185
}
186