Passed
Pull Request — master (#5832)
by
unknown
07:53
created

LpProgressReminderCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 10
dl 0
loc 13
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 Chamilo\CoreBundle\ServiceHelper\MessageHelper;
16
use DateTime;
17
use DateTimeZone;
18
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
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 readonly CourseRepository $courseRepository,
34
        private readonly CourseRelUserRepository $courseRelUserRepository,
35
        private readonly SessionRelCourseRelUserRepository $sessionRelCourseRelUserRepository,
36
        private readonly ExtraFieldValuesRepository $extraFieldValuesRepository,
37
        private readonly TrackEDefaultRepository $trackEDefaultRepository,
38
        private readonly UserRepository $userRepository,
39
        private readonly Environment $twig,
40
        private readonly TranslatorInterface $translator,
41
        private readonly MessageHelper $messageHelper,
42
        private readonly UrlGeneratorInterface $urlGenerator
43
    ) {
44
        parent::__construct();
45
    }
46
47
48
    protected function configure()
49
    {
50
        $this
51
            ->setDescription('Send LP progress reminders to users based on "number_of_days_for_completion".')
52
            ->addOption(
53
                'debug',
54
                null,
55
                InputOption::VALUE_NONE,
56
                'If set, will output detailed debug information'
57
            );
58
    }
59
60
    protected function execute(InputInterface $input, OutputInterface $output): int
61
    {
62
        $debugMode = $input->getOption('debug');
63
        $output->writeln('Starting the LP progress reminder process...');
64
65
        // Retrieve LPs with completion days
66
        $lpItems = $this->extraFieldValuesRepository->getLpIdWithDaysForCompletion();
67
        if ($debugMode && !empty($lpItems)) {
68
            $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

68
            $output->writeln('LP Items retrieved: ' . /** @scrutinizer ignore-type */ print_r($lpItems, true));
Loading history...
69
        }
70
71
        if (empty($lpItems)) {
72
            $output->writeln('No learning paths with days for completion found.');
73
            return Command::SUCCESS;
74
        }
75
76
        $lpMap = [];
77
        foreach ($lpItems as $lpItem) {
78
            $lpMap[$lpItem['lp_id']] = $lpItem['ndays'];
79
        }
80
        $lpIds = array_keys($lpMap);
81
82
        // Retrieve all courses from the CourseRepository
83
        $courses = $this->courseRepository->findAll();
84
        if ($debugMode && !empty($courses)) {
85
            $output->writeln('Courses retrieved: ' . count($courses));
86
        }
87
88
        foreach ($courses as $course) {
89
            $courseId = $course->getId();
90
91
            // Retrieve users for the course (without session)
92
            $courseUsers = $this->courseRelUserRepository->getCourseUsers($courseId, $lpIds);
93
            // Retrieve users for the course session
94
            $sessionCourseUsers = $this->sessionRelCourseRelUserRepository->getSessionCourseUsers($courseId, $lpIds);
95
96
            if ($debugMode && (!empty($courseUsers) || !empty($sessionCourseUsers))) {
97
                $output->writeln('Processing course ID: ' . $courseId);
98
                if (!empty($courseUsers)) {
99
                    $output->writeln('Course users retrieved: ' . count($courseUsers));
100
                    //$output->writeln('Course retrieved: ' . print_r($courseUsers, true));
101
                }
102
                if (!empty($sessionCourseUsers)) {
103
                    $output->writeln('Session users retrieved: ' . count($sessionCourseUsers));
104
                    //$output->writeln('Session retrieved: ' . print_r($sessionCourseUsers, true));
105
                }
106
            }
107
108
            // Process users from the main course (sessionId = 0 or null)
109
            $this->processCourseUsers($courseUsers, $lpMap, $courseId, $debugMode);
110
111
            // Process users from the course session (sessionId > 0)
112
            $this->processCourseUsers($sessionCourseUsers, $lpMap, $courseId, $debugMode, true);
113
        }
114
115
        $output->writeln('LP progress reminder process finished.');
116
        return Command::SUCCESS;
117
    }
118
119
    /**
120
     * Processes users from a course or session to check if a reminder needs to be sent.
121
     */
122
    private function processCourseUsers(array $users, array $lpItems, int $courseId, bool $debugMode = false, bool $checkSession = false): void
123
    {
124
        foreach ($users as $user) {
125
            $userId = $user['userId'];
126
            $courseTitle = $user['courseTitle'];
127
            $lpId = $user['lpId'];
128
            $progress = (int) $user['progress'];
129
130
            if (!isset($lpItems[$lpId])) {
131
                continue;
132
            }
133
134
            $sessionId = $checkSession && isset($user['sessionId']) && $user['sessionId'] > 0 ? $user['sessionId'] : 0;
135
136
            $registrationDate = $this->trackEDefaultRepository->getUserCourseRegistrationAt($courseId, $userId, $sessionId);
137
            $nbDaysForLpCompletion = (int) $lpItems[$lpId];
138
139
            if ($registrationDate) {
140
                if ($debugMode) {
141
                    $sessionInfo = $sessionId > 0 ? "in session ID $sessionId" : "without a session";
142
                    echo "Registration date: {$registrationDate->format('Y-m-d H:i:s')}, Days for completion: $nbDaysForLpCompletion, LP ID: $lpId, $sessionInfo\n";
143
                }
144
                if ($this->isTimeToRemindUser($registrationDate, $nbDaysForLpCompletion)) {
145
                    $nbRemind = $this->getNbReminder($registrationDate, $nbDaysForLpCompletion);
146
                    if ($debugMode) {
147
                        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...
148
                        $this->logReminderSent($userId, $courseTitle, $nbRemind, $debugMode, $lpId, $sessionId);
149
                    }
150
                    $this->sendLpReminder($userId, $courseTitle, $progress, $registrationDate, $nbRemind);
151
                }
152
            }
153
        }
154
    }
155
156
    /**
157
     * Logs the reminder details if debug mode is enabled.
158
     */
159
    private function logReminderSent(int $userId, string $courseTitle, int $nbRemind, bool $debugMode, int $lpId, int $sessionId = 0): void
160
    {
161
        if ($debugMode) {
162
            $sessionInfo = $sessionId > 0 ? sprintf("in session ID %d", $sessionId) : "without a session";
163
            echo sprintf(
164
                "Reminder number %d sent to user ID %d for the course %s (LP ID: %d) %s.\n",
165
                $nbRemind,
166
                $userId,
167
                $courseTitle,
168
                $lpId,
169
                $sessionInfo
170
            );
171
        }
172
    }
173
174
    /**
175
     * Calculates the number of reminders to be sent based on registration date and days for completion.
176
     */
177
    private function getNbReminder(DateTime $registrationDate, int $nbDaysForLpCompletion): int
178
    {
179
        $date1 = clone $registrationDate;
180
        $date1->modify("+$nbDaysForLpCompletion day");
181
182
        $date2 = new DateTime('now', new DateTimeZone('UTC'));
183
184
        $interval = $date1->diff($date2);
185
        $diffDays = (int) $interval->format('%a');
186
187
        return (int) ceil($diffDays / self::NUMBER_OF_DAYS_TO_RESEND_NOTIFICATION) + 1;
188
    }
189
190
    /**
191
     * Checks if it is time to remind the user based on their registration date and LP completion days.
192
     */
193
    private function isTimeToRemindUser(DateTime $registrationDate, int $nbDaysForLpCompletion): bool
194
    {
195
        $date1 = clone $registrationDate;
196
        $date1->modify("+$nbDaysForLpCompletion day");
197
        $startDate = $date1->format('Y-m-d');
198
199
        $date2 = new DateTime('now', new DateTimeZone('UTC'));
200
        $now = $date2->format('Y-m-d');
201
202
        if ($startDate < $now) {
203
            $interval = $date1->diff($date2);
204
            $diffDays = (int) $interval->format('%a');
205
            return (0 === $diffDays % self::NUMBER_OF_DAYS_TO_RESEND_NOTIFICATION);
206
        }
207
208
        return $startDate === $now;
209
    }
210
211
    /**
212
     * Sends a reminder email to the user regarding their LP progress.
213
     */
214
    private function sendLpReminder(int $toUserId, string $courseName, int $lpProgress, DateTime $registrationDate, int $nbRemind): bool
215
    {
216
        $user = $this->userRepository->find($toUserId);
217
        if (!$user) {
218
            throw new \Exception("User not found");
219
        }
220
221
        $platformUrl = $this->urlGenerator->generate('index', [], UrlGeneratorInterface::ABSOLUTE_URL);
222
        $recoverPasswordUrl = $platformUrl.'/main/auth/lostPassword.php';
223
224
        $trainingCenterName = 'Your Training Center';
225
        $trainers = 'Trainer Name';
226
227
        $hello = $this->translator->trans("Hello %s");
228
        $youAreRegCourse = $this->translator->trans("You are registered in the training %s since the %s");
229
        $thisMessageIsAbout = $this->translator->trans("You are receiving this message because you have completed a learning path with a %s progress of your training.<br/>Your progress must be 100 to consider that your training was carried out.<br/>If you have the slightest problem, you should contact with your trainer.");
230
        $stepsToRemind = $this->translator->trans("As a reminder, to access the training platform:<br/>1. Connect to the platform at the address: %s <br/>2. Then enter: <br/>Your username: %s <br/>Your password: This was emailed to you.<br/>if you forgot it and can't find it, you can retrieve it by going to %s <br/><br/>Thank you for doing what is necessary.");
231
        $lpRemindFooter = $this->translator->trans("The training center<p>%s</p>Trainers:<br/>%s");
232
233
        $hello = sprintf($hello, $user->getFullName());
234
        $youAreRegCourse = sprintf($youAreRegCourse, $courseName, $registrationDate->format('Y-m-d'));
235
        $thisMessageIsAbout = sprintf($thisMessageIsAbout, $lpProgress);
236
        $stepsToRemind = sprintf($stepsToRemind, $platformUrl, $user->getUsername(), $recoverPasswordUrl);
237
        $lpRemindFooter = sprintf($lpRemindFooter, $trainingCenterName, $trainers);
238
239
        $messageContent = $this->twig->render('@ChamiloCore/Mailer/Legacy/lp_progress_reminder_body.html.twig', [
240
            'HelloX' => $hello,
241
            'YouAreRegCourseXFromDateX' => $youAreRegCourse,
242
            'ThisMessageIsAboutX' => $thisMessageIsAbout,
243
            'StepsToRemindX' => $stepsToRemind,
244
            'LpRemindFooterX' => $lpRemindFooter,
245
        ]);
246
247
        try {
248
            $this->messageHelper->sendMessageSimple(
249
                $toUserId,
250
                sprintf("Reminder number %d for the course %s", $nbRemind, $courseName),
251
                $messageContent
252
            );
253
254
            return true;
255
        } catch (\Exception $e) {
256
            throw new \Exception('Error sending reminder: ' . $e->getMessage());
257
        }
258
    }
259
}
260