Passed
Pull Request — master (#6396)
by Angel Fernando Quiroz
08:47
created

ScheduledAnnouncementHelper::sendPendingMessages()   F

Complexity

Conditions 29
Paths 8530

Size

Total Lines 147
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 29
eloc 85
nc 8530
nop 2
dl 0
loc 147
rs 0
c 0
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\Helpers;
8
9
use Chamilo\CoreBundle\Entity\Course;
10
use Chamilo\CoreBundle\Entity\Session;
11
use Chamilo\CoreBundle\Entity\User;
12
use Chamilo\CoreBundle\Repository\Node\AccessUrlRepository;
13
use Chamilo\CoreBundle\Repository\ScheduledAnnouncementRepository;
14
use Chamilo\CoreBundle\Repository\SessionRepository;
15
use Chamilo\CoreBundle\Settings\SettingsManager;
16
use DateTime;
17
use Doctrine\ORM\EntityManager;
18
use Exception;
19
use ExtraField;
20
use ExtraFieldValue;
21
use Symfony\Contracts\Translation\TranslatorInterface;
22
use Tracking;
23
24
class ScheduledAnnouncementHelper
25
{
26
    public function __construct(
27
        private readonly ScheduledAnnouncementRepository $announcementRepository,
28
        private readonly AccessUrlRepository $accessUrlRepository,
29
        private readonly EntityManager $em,
30
        private readonly SettingsManager $settingsManager,
31
        private readonly SessionRepository $sessionRepository,
32
        private readonly MessageHelper $messageHelper,
33
        private readonly TranslatorInterface $translator
34
    ) {}
35
36
    /**
37
     * Sends pending announcements to users and coaches.
38
     */
39
    public function sendPendingMessages(int $urlId, bool $debug = false): int
40
    {
41
        if (!$this->allowed()) {
42
            if ($debug) {
43
                error_log('Announcements not allowed.');
44
            }
45
46
            return 0;
47
        }
48
49
        $messagesSent = 0;
50
        $now = new DateTime();
51
52
        if ($debug) {
53
            error_log('Current time: '.$now->format('Y-m-d H:i:s'));
54
        }
55
56
        $announcements = $this->announcementRepository->findBy(['sent' => false]);
57
58
        if ($debug) {
59
            error_log(\count($announcements).' pending announcements found.');
60
        }
61
62
        $extraField = new ExtraField('user');
63
        $extraFields = $extraField->get_all(['filter = ? AND visible_to_self = ?' => [1, 1]]);
64
65
        foreach ($announcements as $announcement) {
66
            if (!$announcement->isSent() && $announcement->getDate() < $now) {
67
                if ($debug) {
68
                    error_log('Processing announcement ID: '.$announcement->getId());
69
                }
70
71
                $sessionId = $announcement->getSessionId();
72
                $session = $this->sessionRepository->find($sessionId);
73
74
                if (!$session) {
75
                    if ($debug) {
76
                        error_log("Session not found for session ID: $sessionId");
77
                    }
78
79
                    continue;
80
                }
81
82
                $accessUrl = $this->accessUrlRepository->find($urlId);
83
                $sessionRelUsers = $this->sessionRepository->getUsersByAccessUrl($session, $accessUrl);
84
                $generalCoaches = $session->getGeneralCoaches();
85
86
                if (empty($sessionRelUsers) || 0 === $generalCoaches->count()) {
87
                    if ($debug) {
88
                        error_log("No users or general coaches found for session ID: $sessionId");
89
                    }
90
91
                    continue;
92
                }
93
94
                $coachId = $generalCoaches->first()->getId();
95
                if ($debug) {
96
                    error_log("Coach ID: $coachId");
97
                }
98
99
                $coachList = [];
100
101
                $sendToCoaches = $this->shouldSendToCoaches($announcement->getId());
102
103
                $courseList = $session->getCourses();
104
105
                if ($debug) {
106
                    error_log('Number of courses in session: '.\count($courseList));
107
                    foreach ($courseList as $sessionRelCourse) {
108
                        $course = $sessionRelCourse->getCourse();
109
                        error_log('Course ID: '.$course->getId().', Course Title: '.$course->getTitle());
110
                    }
111
                }
112
113
                if ($sendToCoaches) {
114
                    foreach ($courseList as $course) {
115
                        $coaches = $session->getGeneralCoaches();
116
                        $coachList = array_merge($coachList, $coaches->toArray());
117
                    }
118
                    $coachList = array_unique($coachList);
119
                }
120
121
                $announcement->setSent(true);
122
                $this->em->persist($announcement);
123
                $this->em->flush();
124
125
                $attachments = '';
126
                $subject = $announcement->getSubject();
127
128
                foreach ($sessionRelUsers as $sessionRelUser) {
129
                    $user = $sessionRelUser->getUser();
130
131
                    if ($debug) {
132
                        error_log('User ::: '.$user->getId());
133
                    }
134
135
                    if ($user->getId() !== $coachId) {
136
                        $courseInfo = $this->getCourseInfo($courseList);
137
                        $progress = $this->getUserProgress($user->getId(), $courseInfo, $session);
138
139
                        $message = $this->buildMessage(
140
                            $announcement,
141
                            $session,
142
                            $user,
143
                            $courseInfo,
144
                            $attachments,
145
                            $progress
146
                        );
147
148
                        if (!empty($extraFields)) {
149
                            $extraFieldValue = new ExtraFieldValue('user');
150
                            foreach ($extraFields as $extraField) {
151
                                $valueExtra = $extraFieldValue->get_values_by_handler_and_field_variable($user->getId(), $extraField['variable'], true);
152
                                $tags['(('.strtolower($extraField['variable']).'))'] = $valueExtra['value'] ?? '';
153
                            }
154
                            $message = str_replace(array_keys($tags), $tags, $message);
155
                        }
156
157
                        if ($debug) {
158
                            error_log('Sending email to user ID: '.$user->getId());
159
                        }
160
161
                        $this->sendEmail($user->getId(), $subject, $message, $coachId);
162
                    }
163
                }
164
165
                $coachMessage = $this->buildCoachMessage($announcement, $generalCoaches, $message);
166
                foreach ($coachList as $courseCoach) {
167
                    if ($debug) {
168
                        error_log('Sending email to coach ID: '.$courseCoach->getId());
169
                    }
170
                    $this->sendEmail($courseCoach->getId(), $subject, $coachMessage, $coachId);
171
                }
172
173
                $messagesSent++;
174
175
                if ($debug) {
176
                    error_log('Messages sent for announcement ID: '.$announcement->getId());
177
                }
178
            }
179
        }
180
181
        if ($debug) {
182
            error_log("Total messages sent: $messagesSent");
183
        }
184
185
        return $messagesSent;
186
    }
187
188
    /**
189
     * Checks if the announcement should be sent to coaches.
190
     */
191
    private function shouldSendToCoaches(int $announcementId): bool
192
    {
193
        $extraFieldValue = new ExtraFieldValue('scheduled_announcement');
194
        $sendToCoaches = $extraFieldValue->get_values_by_handler_and_field_variable($announcementId, 'send_to_coaches');
195
196
        return !empty($sendToCoaches) && !empty($sendToCoaches['value']) && 1 === (int) $sendToCoaches['value'];
197
    }
198
199
    /**
200
     * Verifies if sending scheduled announcements is allowed.
201
     */
202
    private function allowed(): bool
203
    {
204
        return 'true' === $this->settingsManager->getSetting('announcement.allow_scheduled_announcements');
205
    }
206
207
    /**
208
     * Builds the announcement message for the user.
209
     *
210
     * @param mixed $announcement
211
     * @param mixed $courseInfo
212
     * @param mixed $attachments
213
     * @param mixed $progress
214
     */
215
    private function buildMessage($announcement, Session $session, User $user, $courseInfo, $attachments, $progress): string
216
    {
217
        $generalCoaches = $session->getGeneralCoaches();
218
        $generalCoachName = [];
219
        $generalCoachEmail = [];
220
221
        foreach ($generalCoaches as $coach) {
222
            $generalCoachName[] = $coach->getFullname();
223
            $generalCoachEmail[] = $coach->getEmail();
224
        }
225
226
        $startTime = $this->getLocalTime($session->getAccessStartDate());
227
        $endTime = $this->getLocalTime($session->getAccessEndDate());
228
229
        $tags = [
230
            '((session_name))' => $session->getTitle(),
231
            '((session_start_date))' => $startTime,
232
            '((general_coach))' => implode(' - ', $generalCoachName),
233
            '((general_coach_email))' => implode(' - ', $generalCoachEmail),
234
            '((session_end_date))' => $endTime,
235
            '((user_complete_name))' => $user->getFirstname().' '.$user->getLastname(),
236
            '((user_firstname))' => $user->getFirstname(),
237
            '((user_lastname))' => $user->getLastname(),
238
            '((lp_progress))' => $progress,
239
        ];
240
241
        $message = str_replace(array_keys($tags), $tags, $announcement->getMessage());
242
243
        return $message.$attachments;
244
    }
245
246
    /**
247
     * Builds the announcement message for the coach.
248
     *
249
     * @param mixed $announcement
250
     * @param mixed $generalCoaches
251
     * @param mixed $message
252
     */
253
    private function buildCoachMessage($announcement, $generalCoaches, $message): string
254
    {
255
        $coachNames = [];
256
        foreach ($generalCoaches as $coach) {
257
            $coachNames[] = $coach->getFullname();
258
        }
259
260
        $coachMessageIntro = \count($generalCoaches) > 1
261
            ? $this->translator->trans('You are receiving a copy because you are one of the course coaches')
262
            : $this->translator->trans('You are receiving a copy because you are the course coach');
263
264
        $coachMessage = $coachMessageIntro.': '.implode(', ', $coachNames);
265
266
        return $coachMessage.'<br /><br />'.$message;
267
    }
268
269
    /**
270
     * Sends an email with the announcement to a user or coach.
271
     */
272
    private function sendEmail(int $userId, string $subject, string $message, int $coachId): void
273
    {
274
        $user = $this->em->getRepository(User::class)->find($userId);
275
        $coach = $this->em->getRepository(User::class)->find($coachId);
276
277
        if (!$user || !$coach) {
278
            throw new Exception('User or coach not found.');
279
        }
280
281
        $this->messageHelper->sendMessageSimple(
282
            $userId,
283
            $subject,
284
            $message,
285
            $coachId
286
        );
287
    }
288
289
    /**
290
     * Retrieves course information from a list of courses.
291
     *
292
     * @param mixed $courseList
293
     */
294
    private function getCourseInfo($courseList)
295
    {
296
        if (!empty($courseList) && current($courseList) instanceof Course) {
297
            $courseId = current($courseList)->getId();
298
299
            return $this->getCourseInfoById($courseId);
300
        }
301
302
        return [];
303
    }
304
305
    /**
306
     * Retrieves course information by course ID.
307
     */
308
    private function getCourseInfoById(int $courseId)
309
    {
310
        $course = $this->em->getRepository(Course::class)->find($courseId);
311
312
        if ($course) {
313
            return [
314
                'real_id' => $course->getId(),
315
                'title' => $course->getTitle(),
316
                'code' => $course->getCode(),
317
                'description' => $course->getDescription(),
318
                'tutor_name' => $course->getTutorName(),
319
            ];
320
        }
321
322
        return [];
323
    }
324
325
    /**
326
     * Gets the user's progress for a specific course and session.
327
     *
328
     * @param mixed $courseInfo
329
     */
330
    private function getUserProgress(int $userId, $courseInfo, Session $session): string
331
    {
332
        if (!empty($courseInfo) && $session) {
333
            $progress = Tracking::get_avg_student_progress($userId, $courseInfo['real_id'], [], $session->getId());
334
335
            return is_numeric($progress) ? $progress.'%' : '0%';
336
        }
337
338
        return '0%';
339
    }
340
341
    /**
342
     * Formats a DateTime object to a string.
343
     */
344
    private function getLocalTime(?DateTime $datetime): string
345
    {
346
        return $datetime ? $datetime->format('Y-m-d H:i:s') : '';
347
    }
348
}
349