Passed
Pull Request — master (#5832)
by
unknown
06:59
created

LpProgressReminderCommand::execute()   B

Complexity

Conditions 7
Paths 22

Size

Total Lines 47
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 24
c 1
b 0
f 0
nc 22
nop 2
dl 0
loc 47
rs 8.6026
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
    public function __construct(
30
        private CourseRepository $courseRepository,
31
        private CourseRelUserRepository $courseRelUserRepository,
32
        private ExtraFieldValuesRepository $extraFieldValuesRepository,
33
        private TrackEDefaultRepository $trackEDefaultRepository,
34
        private UserRepository $userRepository,
35
        private MailerInterface $mailer,
36
        private Environment $twig,
37
        private TranslatorInterface $translator
38
    ) {
39
        parent::__construct();
40
    }
41
42
43
    protected function configure()
44
    {
45
        $this
46
            ->setDescription('Send LP progress reminders to users based on "number_of_days_for_completion".')
47
            ->addOption(
48
                'debug',
49
                null,
50
                InputOption::VALUE_NONE,
51
                'If set, will output detailed debug information'
52
            );
53
    }
54
55
    protected function execute(InputInterface $input, OutputInterface $output): int
56
    {
57
        $debugMode = $input->getOption('debug');
58
        $output->writeln('Starting the LP progress reminder process...');
59
60
        // Retrieve LPs with completion days
61
        $lpItems = $this->extraFieldValuesRepository->getLpIdWithDaysForCompletion();
62
        if ($debugMode) {
63
            $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

63
            $output->writeln('LP Items retrieved: ' . /** @scrutinizer ignore-type */ print_r($lpItems, true));
Loading history...
64
        }
65
66
        if (empty($lpItems)) {
67
            $output->writeln('No learning paths with days for completion found.');
68
            return Command::SUCCESS;
69
        }
70
71
        // Retrieve all courses from the CourseRepository
72
        $courses = $this->courseRepository->findAll();
73
        if ($debugMode) {
74
            $output->writeln('Courses retrieved: ' . count($courses));
75
        }
76
77
        foreach ($courses as $course) {
78
            $courseId = $course->getId();
79
80
            if ($debugMode) {
81
                $output->writeln('Processing course ID: ' . $courseId);
82
            }
83
84
            // Retrieve users for the course and session
85
            $courseUsers = $this->courseRelUserRepository->getCourseUsers($courseId, array_keys($lpItems), false);
86
            $sessionCourseUsers = $this->courseRelUserRepository->getCourseUsers($courseId, array_keys($lpItems), true);
87
88
            if ($debugMode) {
89
                $output->writeln('Course users retrieved: ' . count($courseUsers));
90
                $output->writeln('Session users retrieved: ' . count($sessionCourseUsers));
91
            }
92
93
            // Process users from the main course
94
            $this->processCourseUsers($courseUsers, $lpItems, $courseId, $debugMode);
95
96
            // Process users from the course session
97
            $this->processCourseUsers($sessionCourseUsers, $lpItems, $courseId, $debugMode, true);
98
        }
99
100
        $output->writeln('LP progress reminder process finished.');
101
        return Command::SUCCESS;
102
    }
103
104
    /**
105
     * Processes users from a course or session to check if a reminder needs to be sent.
106
     */
107
    private function processCourseUsers(array $users, array $lpItems, int $courseId, bool $checkSession = false): void
108
    {
109
        foreach ($users as $user) {
110
            $userId = $user['userId'];
111
            $courseTitle = $user['courseTitle'];
112
            $lpId = $user['lpId'];
113
            $progress = (int) $user['progress'];
114
            $nbDaysForLpCompletion = (int) $lpItems[$lpId]['ndays'];
115
            $sessionId = $checkSession && isset($user['session_id']) ? $user['session_id'] : null;
116
            $registrationDate = $this->trackEDefaultRepository->getUserCourseRegistrationAt($courseId, $userId, $sessionId);
117
118
            if ($registrationDate && $this->isTimeToRemindUser($registrationDate, $nbDaysForLpCompletion)) {
119
                $nbRemind = $this->getNbReminder($registrationDate, $nbDaysForLpCompletion);
120
                $this->sendLpReminder($userId, $courseTitle, $progress, $registrationDate, $nbRemind);
121
            }
122
        }
123
    }
124
125
    /**
126
     * Calculates the number of reminders to be sent based on registration date and days for completion.
127
     */
128
    private function getNbReminder(DateTime $registrationDate, int $nbDaysForLpCompletion): int
129
    {
130
        $date1 = clone $registrationDate;
131
        $date1->modify("+$nbDaysForLpCompletion day");
132
133
        $date2 = new DateTime('now', new DateTimeZone('UTC'));
134
135
        $interval = $date1->diff($date2);
136
        $diffDays = (int) $interval->format('%a');
137
138
        return (int) ceil($diffDays / 3) + 1;
139
    }
140
141
    /**
142
     * Checks if it is time to remind the user based on their registration date and LP completion days.
143
     */
144
    private function isTimeToRemindUser(DateTime $registrationDate, int $nbDaysForLpCompletion): bool
145
    {
146
        $date1 = clone $registrationDate;
147
        $date1->modify("+$nbDaysForLpCompletion day");
148
        $startDate = $date1->format('Y-m-d');
149
150
        $date2 = new DateTime('now', new DateTimeZone('UTC'));
151
        $now = $date2->format('Y-m-d');
152
153
        if ($startDate < $now) {
154
            $interval = $date1->diff($date2);
155
            $diffDays = (int) $interval->format('%a');
156
            return (0 === $diffDays % 3);
157
        }
158
159
        return $startDate === $now;
160
    }
161
162
    /**
163
     * Sends a reminder email to the user regarding their LP progress.
164
     */
165
    private function sendLpReminder(int $toUserId, string $courseName, int $lpProgress, DateTime $registrationDate, int $nbRemind): bool
166
    {
167
        $user = $this->userRepository->find($toUserId);
168
        if (!$user) {
169
            throw new \Exception("User not found");
170
        }
171
172
        $hello = $this->translator->trans('HelloX');
173
        $youAreRegCourse = $this->translator->trans('YouAreRegCourseXFromDateX');
174
        $thisMessageIsAbout = $this->translator->trans('ThisMessageIsAboutX');
175
        $stepsToRemind = $this->translator->trans('StepsToRemindX');
176
        $lpRemindFooter = $this->translator->trans('LpRemindFooterX');
177
178
        $hello = sprintf($hello, $user->getFullName());
179
        $youAreRegCourse = sprintf($youAreRegCourse, $courseName, $registrationDate->format('Y-m-d'));
180
        $thisMessageIsAbout = sprintf($thisMessageIsAbout, $lpProgress);
181
        $stepsToRemind = sprintf($stepsToRemind, '', $user->getUsername(), '');
182
        $lpRemindFooter = sprintf($lpRemindFooter, '', 'm');
183
184
        $body = $this->twig->render('@ChamiloCore/Mailer/Legacy/lp_progress_reminder_body.html.twig', [
185
            'HelloX' => $hello,
186
            'YouAreRegCourseXFromDateX' => $youAreRegCourse,
187
            'ThisMessageIsAboutX' => $thisMessageIsAbout,
188
            'StepsToRemindX' => $stepsToRemind,
189
            'LpRemindFooterX' => $lpRemindFooter,
190
        ]);
191
192
        $email = (new Email())
193
            ->from('[email protected]')
194
            ->to($user->getEmail())
195
            ->subject(sprintf("Reminder number %d for the course %s", $nbRemind, $courseName))
196
            ->html($body);
197
198
        try {
199
            $this->mailer->send($email);
200
            return true;
201
        } catch (\Exception $e) {
202
            throw new \Exception('Error to send email: ' . $e->getMessage());
203
        }
204
    }
205
}
206