Passed
Pull Request — master (#5832)
by
unknown
09:44
created

LpProgressReminderCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 9
dl 0
loc 12
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\Repository\CourseRelUserRepository;
10
use Chamilo\CoreBundle\Repository\ExtraFieldValuesRepository;
11
use Chamilo\CoreBundle\Repository\Node\CourseRepository;
12
use Chamilo\CoreBundle\Repository\Node\UserRepository;
13
use Chamilo\CoreBundle\Repository\SessionRelCourseRelUserRepository;
14
use Chamilo\CoreBundle\Repository\TrackEDefaultRepository;
15
use DateTime;
16
use DateTimeZone;
17
use Symfony\Component\Mailer\MailerInterface;
18
use Symfony\Component\Mime\Email;
19
use Symfony\Contracts\Translation\TranslatorInterface;
20
use Twig\Environment;
21
use Symfony\Component\Console\Command\Command;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Input\InputOption;
24
use Symfony\Component\Console\Output\OutputInterface;
25
26
class LpProgressReminderCommand extends Command
27
{
28
    protected static $defaultName = 'app:lp-progress-reminder';
29
30
    private const NUMBER_OF_DAYS_TO_RESEND_NOTIFICATION = 3;
31
32
    public function __construct(
33
        private CourseRepository $courseRepository,
34
        private CourseRelUserRepository $courseRelUserRepository,
35
        private SessionRelCourseRelUserRepository $sessionRelCourseRelUserRepository,
36
        private ExtraFieldValuesRepository $extraFieldValuesRepository,
37
        private TrackEDefaultRepository $trackEDefaultRepository,
38
        private UserRepository $userRepository,
39
        private MailerInterface $mailer,
40
        private Environment $twig,
41
        private TranslatorInterface $translator
42
    ) {
43
        parent::__construct();
44
    }
45
46
47
    protected function configure()
48
    {
49
        $this
50
            ->setDescription('Send LP progress reminders to users based on "number_of_days_for_completion".')
51
            ->addOption(
52
                'debug',
53
                null,
54
                InputOption::VALUE_NONE,
55
                'If set, will output detailed debug information'
56
            );
57
    }
58
59
    protected function execute(InputInterface $input, OutputInterface $output): int
60
    {
61
        $debugMode = $input->getOption('debug');
62
        $output->writeln('Starting the LP progress reminder process...');
63
64
        // Retrieve LPs with completion days
65
        $lpItems = $this->extraFieldValuesRepository->getLpIdWithDaysForCompletion();
66
        if ($debugMode && !empty($lpItems)) {
67
            $output->writeln('LP Items retrieved: ' . print_r($lpItems, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($lpItems, true) of type string|true 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

67
            $output->writeln('LP Items retrieved: ' . /** @scrutinizer ignore-type */ print_r($lpItems, true));
Loading history...
68
        }
69
70
        if (empty($lpItems)) {
71
            $output->writeln('No learning paths with days for completion found.');
72
            return Command::SUCCESS;
73
        }
74
75
        $lpMap = [];
76
        foreach ($lpItems as $lpItem) {
77
            $lpMap[$lpItem['lp_id']] = $lpItem['ndays'];
78
        }
79
        $lpIds = array_keys($lpMap);
80
81
        // Retrieve all courses from the CourseRepository
82
        $courses = $this->courseRepository->findAll();
83
        if ($debugMode && !empty($courses)) {
84
            $output->writeln('Courses retrieved: ' . count($courses));
85
        }
86
87
        foreach ($courses as $course) {
88
            $courseId = $course->getId();
89
90
            // Retrieve users for the course (without session)
91
            $courseUsers = $this->courseRelUserRepository->getCourseUsers($courseId, $lpIds);
92
            // Retrieve users for the course session
93
            $sessionCourseUsers = $this->sessionRelCourseRelUserRepository->getSessionCourseUsers($courseId, $lpIds);
94
95
            if ($debugMode && (!empty($courseUsers) || !empty($sessionCourseUsers))) {
96
                $output->writeln('Processing course ID: ' . $courseId);
97
                if (!empty($courseUsers)) {
98
                    $output->writeln('Course users retrieved: ' . count($courseUsers));
99
                    //$output->writeln('Course retrieved: ' . print_r($courseUsers, true));
100
                }
101
                if (!empty($sessionCourseUsers)) {
102
                    $output->writeln('Session users retrieved: ' . count($sessionCourseUsers));
103
                    //$output->writeln('Session retrieved: ' . print_r($sessionCourseUsers, true));
104
                }
105
            }
106
107
            // Process users from the main course (sessionId = 0 or null)
108
            $this->processCourseUsers($courseUsers, $lpMap, $courseId, $debugMode);
109
110
            // Process users from the course session (sessionId > 0)
111
            $this->processCourseUsers($sessionCourseUsers, $lpMap, $courseId, $debugMode, true);
112
        }
113
114
        $output->writeln('LP progress reminder process finished.');
115
        return Command::SUCCESS;
116
    }
117
118
    /**
119
     * Processes users from a course or session to check if a reminder needs to be sent.
120
     */
121
    private function processCourseUsers(array $users, array $lpItems, int $courseId, bool $debugMode = false, bool $checkSession = false): void
122
    {
123
        foreach ($users as $user) {
124
            $userId = $user['userId'];
125
            $courseTitle = $user['courseTitle'];
126
            $lpId = $user['lpId'];
127
            $progress = (int) $user['progress'];
128
129
            if (!isset($lpItems[$lpId])) {
130
                continue;
131
            }
132
133
            $sessionId = 0;
134
            if ($checkSession && isset($user['sessionId']) && $user['sessionId'] > 0) {
135
                $sessionId = $user['sessionId'];
136
            }
137
138
            $registrationDate = $this->trackEDefaultRepository->getUserCourseRegistrationAt($courseId, $userId, $sessionId);
139
            $nbDaysForLpCompletion = (int) $lpItems[$lpId];
140
141
            if ($registrationDate) {
142
                if ($debugMode) {
143
                    $sessionInfo = $sessionId > 0 ? "in session ID $sessionId" : "without a session";
144
                    echo "Registration date: {$registrationDate->format('Y-m-d H:i:s')}, Days for completion: $nbDaysForLpCompletion, LP ID: $lpId, $sessionInfo\n";
145
                }
146
                if ($this->isTimeToRemindUser($registrationDate, $nbDaysForLpCompletion)) {
147
                    $nbRemind = $this->getNbReminder($registrationDate, $nbDaysForLpCompletion);
148
                    if ($debugMode) {
149
                        echo "Sending reminder to user $userId for course $courseTitle (LP ID: $lpId) $sessionInfo\n";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sessionInfo does not seem to be defined for all execution paths leading up to this point.
Loading history...
150
                        $this->logReminderSent($userId, $courseTitle, $nbRemind, $debugMode, $sessionId, $lpId);
151
                    }
152
                    $this->sendLpReminder($userId, $courseTitle, $progress, $registrationDate, $nbRemind);
153
                }
154
            }
155
        }
156
    }
157
158
    /**
159
     * Logs the reminder details if debug mode is enabled.
160
     */
161
    private function logReminderSent(int $userId, string $courseTitle, int $nbRemind, bool $debugMode, int $sessionId = 0, int $lpId): void
162
    {
163
        if ($debugMode) {
164
            $sessionInfo = $sessionId > 0 ? sprintf("in session ID %d", $sessionId) : "without a session";
165
            echo sprintf(
166
                "Reminder number %d sent to user ID %d for the course %s (LP ID: %d) %s.\n",
167
                $nbRemind,
168
                $userId,
169
                $courseTitle,
170
                $lpId,
171
                $sessionInfo
172
            );
173
        }
174
    }
175
176
    /**
177
     * Calculates the number of reminders to be sent based on registration date and days for completion.
178
     */
179
    private function getNbReminder(DateTime $registrationDate, int $nbDaysForLpCompletion): int
180
    {
181
        $date1 = clone $registrationDate;
182
        $date1->modify("+$nbDaysForLpCompletion day");
183
184
        $date2 = new DateTime('now', new DateTimeZone('UTC'));
185
186
        $interval = $date1->diff($date2);
187
        $diffDays = (int) $interval->format('%a');
188
189
        return (int) ceil($diffDays / self::NUMBER_OF_DAYS_TO_RESEND_NOTIFICATION) + 1;
190
    }
191
192
    /**
193
     * Checks if it is time to remind the user based on their registration date and LP completion days.
194
     */
195
    private function isTimeToRemindUser(DateTime $registrationDate, int $nbDaysForLpCompletion): bool
196
    {
197
        $date1 = clone $registrationDate;
198
        $date1->modify("+$nbDaysForLpCompletion day");
199
        $startDate = $date1->format('Y-m-d');
200
201
        $date2 = new DateTime('now', new DateTimeZone('UTC'));
202
        $now = $date2->format('Y-m-d');
203
204
        if ($startDate < $now) {
205
            $interval = $date1->diff($date2);
206
            $diffDays = (int) $interval->format('%a');
207
            return (0 === $diffDays % self::NUMBER_OF_DAYS_TO_RESEND_NOTIFICATION);
208
        }
209
210
        return $startDate === $now;
211
    }
212
213
    /**
214
     * Sends a reminder email to the user regarding their LP progress.
215
     */
216
    private function sendLpReminder(int $toUserId, string $courseName, int $lpProgress, DateTime $registrationDate, int $nbRemind): bool
217
    {
218
        $user = $this->userRepository->find($toUserId);
219
        if (!$user) {
220
            throw new \Exception("User not found");
221
        }
222
223
        $hello = $this->translator->trans('HelloX');
224
        $youAreRegCourse = $this->translator->trans('YouAreRegCourseXFromDateX');
225
        $thisMessageIsAbout = $this->translator->trans('ThisMessageIsAboutX');
226
        $stepsToRemind = $this->translator->trans('StepsToRemindX');
227
        $lpRemindFooter = $this->translator->trans('LpRemindFooterX');
228
229
        $hello = sprintf($hello, $user->getFullName());
230
        $youAreRegCourse = sprintf($youAreRegCourse, $courseName, $registrationDate->format('Y-m-d'));
231
        $thisMessageIsAbout = sprintf($thisMessageIsAbout, $lpProgress);
232
        $stepsToRemind = sprintf($stepsToRemind, '', $user->getUsername(), '');
233
        $lpRemindFooter = sprintf($lpRemindFooter, '', 'm');
234
235
        $body = $this->twig->render('@ChamiloCore/Mailer/Legacy/lp_progress_reminder_body.html.twig', [
236
            'HelloX' => $hello,
237
            'YouAreRegCourseXFromDateX' => $youAreRegCourse,
238
            'ThisMessageIsAboutX' => $thisMessageIsAbout,
239
            'StepsToRemindX' => $stepsToRemind,
240
            'LpRemindFooterX' => $lpRemindFooter,
241
        ]);
242
243
        $email = (new Email())
244
            ->from('[email protected]')
245
            ->to($user->getEmail())
246
            ->subject(sprintf("Reminder number %d for the course %s", $nbRemind, $courseName))
247
            ->html($body);
248
249
        try {
250
            $this->mailer->send($email);
251
            return true;
252
        } catch (\Exception $e) {
253
            throw new \Exception('Error to send email: ' . $e->getMessage());
254
        }
255
    }
256
}
257