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

LpProgressReminderCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

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

65
            $output->writeln('LP Items retrieved: ' . /** @scrutinizer ignore-type */ print_r($lpItems, true));
Loading history...
66
        }
67
68
        if (empty($lpItems)) {
69
            $output->writeln('No learning paths with days for completion found.');
70
            return Command::SUCCESS;
71
        }
72
73
        $lpMap = [];
74
        foreach ($lpItems as $lpItem) {
75
            $lpMap[$lpItem['lp_id']] = $lpItem['ndays'];
76
        }
77
        $lpIds = array_keys($lpMap);
78
79
        // Retrieve all courses from the CourseRepository
80
        $courses = $this->courseRepository->findAll();
81
        if ($debugMode && !empty($courses)) {
82
            $output->writeln('Courses retrieved: ' . count($courses));
83
        }
84
85
        foreach ($courses as $course) {
86
            $courseId = $course->getId();
87
88
            // Retrieve users for the course (without session)
89
            $courseUsers = $this->courseRelUserRepository->getCourseUsers($courseId, $lpIds);
90
            // Retrieve users for the course session
91
            $sessionCourseUsers = $this->courseRelUserRepository->getCourseUsers($courseId, $lpIds, true);
92
93
            if ($debugMode && (!empty($courseUsers) || !empty($sessionCourseUsers))) {
94
                $output->writeln('Processing course ID: ' . $courseId);
95
                if (!empty($courseUsers)) {
96
                    $output->writeln('Course users retrieved: ' . count($courseUsers));
97
                }
98
                if (!empty($sessionCourseUsers)) {
99
                    $output->writeln('Session users retrieved: ' . count($sessionCourseUsers));
100
                    $output->writeln('Session retrieved: ' . print_r($sessionCourseUsers, true));
0 ignored issues
show
Bug introduced by
Are you sure print_r($sessionCourseUsers, 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

100
                    $output->writeln('Session retrieved: ' . /** @scrutinizer ignore-type */ print_r($sessionCourseUsers, true));
Loading history...
101
                }
102
            }
103
104
            // Process users from the main course (sessionId = 0 or null)
105
            $this->processCourseUsers($courseUsers, $lpMap, $courseId, $debugMode);
106
107
            // Process users from the course session (sessionId > 0)
108
            $this->processCourseUsers($sessionCourseUsers, $lpMap, $courseId, $debugMode, true);
109
        }
110
111
        $output->writeln('LP progress reminder process finished.');
112
        return Command::SUCCESS;
113
    }
114
115
    /**
116
     * Processes users from a course or session to check if a reminder needs to be sent.
117
     */
118
    private function processCourseUsers(array $users, array $lpItems, int $courseId, bool $debugMode = false, bool $checkSession = false): void
119
    {
120
        foreach ($users as $user) {
121
            $userId = $user['userId'];
122
            $courseTitle = $user['courseTitle'];
123
            $lpId = $user['lpId'];
124
            $progress = (int) $user['progress'];
125
126
            if (!isset($lpItems[$lpId])) {
127
                continue;
128
            }
129
130
            $sessionId = 0;
131
            if ($checkSession && isset($user['sessionId']) && $user['sessionId'] > 0) {
132
                $sessionId = $user['sessionId'];
133
            }
134
135
            $registrationDate = $this->trackEDefaultRepository->getUserCourseRegistrationAt($courseId, $userId, $sessionId);
136
            $nbDaysForLpCompletion = $lpItems[$lpId];
137
138
            if ($registrationDate) {
139
                if ($debugMode) {
140
                    $sessionInfo = $sessionId > 0 ? "in session ID $sessionId" : "without a session";
141
                    echo "Registration date: {$registrationDate->format('Y-m-d H:i:s')}, Days for completion: $nbDaysForLpCompletion, $sessionInfo\n";
142
                }
143
                if ($this->isTimeToRemindUser($registrationDate, $nbDaysForLpCompletion)) {
144
                    $nbRemind = $this->getNbReminder($registrationDate, $nbDaysForLpCompletion);
145
                    if ($debugMode) {
146
                        echo "Sending reminder to user $userId for course $courseTitle $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...
147
                        $this->logReminderSent($userId, $courseTitle, $nbRemind, $debugMode, $sessionId);
148
                    }
149
                    $this->sendLpReminder($userId, $courseTitle, $progress, $registrationDate, $nbRemind);
150
                }
151
            } elseif ($debugMode) {
152
                $sessionInfo = $sessionId > 0 ? "in session ID $sessionId" : "without a session";
153
                echo "No registration date found for user $userId in course $courseTitle $sessionInfo\n";
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): 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 %s.\n",
167
                $nbRemind,
168
                $userId,
169
                $courseTitle,
170
                $sessionInfo
171
            );
172
        }
173
    }
174
175
    /**
176
     * Calculates the number of reminders to be sent based on registration date and days for completion.
177
     */
178
    private function getNbReminder(DateTime $registrationDate, int $nbDaysForLpCompletion): int
179
    {
180
        $date1 = clone $registrationDate;
181
        $date1->modify("+$nbDaysForLpCompletion day");
182
183
        $date2 = new DateTime('now', new DateTimeZone('UTC'));
184
185
        $interval = $date1->diff($date2);
186
        $diffDays = (int) $interval->format('%a');
187
188
        return (int) ceil($diffDays / self::NUMBER_OF_DAYS_TO_RESEND_NOTIFICATION) + 1;
189
    }
190
191
    /**
192
     * Checks if it is time to remind the user based on their registration date and LP completion days.
193
     */
194
    private function isTimeToRemindUser(DateTime $registrationDate, int $nbDaysForLpCompletion): bool
195
    {
196
        $date1 = clone $registrationDate;
197
        $date1->modify("+$nbDaysForLpCompletion day");
198
        $startDate = $date1->format('Y-m-d');
199
200
        $date2 = new DateTime('now', new DateTimeZone('UTC'));
201
        $now = $date2->format('Y-m-d');
202
203
        if ($startDate < $now) {
204
            $interval = $date1->diff($date2);
205
            $diffDays = (int) $interval->format('%a');
206
            return (0 === $diffDays % self::NUMBER_OF_DAYS_TO_RESEND_NOTIFICATION);
207
        }
208
209
        return $startDate === $now;
210
    }
211
212
    /**
213
     * Sends a reminder email to the user regarding their LP progress.
214
     */
215
    private function sendLpReminder(int $toUserId, string $courseName, int $lpProgress, DateTime $registrationDate, int $nbRemind): bool
216
    {
217
        $user = $this->userRepository->find($toUserId);
218
        if (!$user) {
219
            throw new \Exception("User not found");
220
        }
221
222
        $hello = $this->translator->trans('HelloX');
223
        $youAreRegCourse = $this->translator->trans('YouAreRegCourseXFromDateX');
224
        $thisMessageIsAbout = $this->translator->trans('ThisMessageIsAboutX');
225
        $stepsToRemind = $this->translator->trans('StepsToRemindX');
226
        $lpRemindFooter = $this->translator->trans('LpRemindFooterX');
227
228
        $hello = sprintf($hello, $user->getFullName());
229
        $youAreRegCourse = sprintf($youAreRegCourse, $courseName, $registrationDate->format('Y-m-d'));
230
        $thisMessageIsAbout = sprintf($thisMessageIsAbout, $lpProgress);
231
        $stepsToRemind = sprintf($stepsToRemind, '', $user->getUsername(), '');
232
        $lpRemindFooter = sprintf($lpRemindFooter, '', 'm');
233
234
        $body = $this->twig->render('@ChamiloCore/Mailer/Legacy/lp_progress_reminder_body.html.twig', [
235
            'HelloX' => $hello,
236
            'YouAreRegCourseXFromDateX' => $youAreRegCourse,
237
            'ThisMessageIsAboutX' => $thisMessageIsAbout,
238
            'StepsToRemindX' => $stepsToRemind,
239
            'LpRemindFooterX' => $lpRemindFooter,
240
        ]);
241
242
        $email = (new Email())
243
            ->from('[email protected]')
244
            ->to($user->getEmail())
245
            ->subject(sprintf("Reminder number %d for the course %s", $nbRemind, $courseName))
246
            ->html($body);
247
248
        try {
249
            $this->mailer->send($email);
250
            return true;
251
        } catch (\Exception $e) {
252
            throw new \Exception('Error to send email: ' . $e->getMessage());
253
        }
254
    }
255
}
256