Passed
Pull Request — master (#5831)
by
unknown
47:54 queued 24:36
created

SessionRepetitionCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
nc 1
nop 4
dl 0
loc 11
rs 10
c 1
b 0
f 0
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\Entity\AccessUrl;
10
use Chamilo\CoreBundle\Entity\Session;
11
use Chamilo\CoreBundle\Repository\SessionRepository;
12
use Doctrine\ORM\EntityManagerInterface;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Symfony\Component\Console\Input\InputOption;
17
use Symfony\Component\Mailer\MailerInterface;
18
use Symfony\Component\Mime\Email;
19
use Symfony\Contracts\Translation\TranslatorInterface;
20
21
class SessionRepetitionCommand extends Command
22
{
23
    protected static $defaultName = 'app:session-repetition';
24
25
    private SessionRepository $sessionRepository;
26
    private EntityManagerInterface $entityManager;
27
    private MailerInterface $mailer;
28
    private TranslatorInterface $translator;
29
30
    public function __construct(
31
        SessionRepository $sessionRepository,
32
        EntityManagerInterface $entityManager,
33
        MailerInterface $mailer,
34
        TranslatorInterface $translator
35
    ) {
36
        parent::__construct();
37
        $this->sessionRepository = $sessionRepository;
38
        $this->entityManager = $entityManager;
39
        $this->mailer = $mailer;
40
        $this->translator = $translator;
41
    }
42
43
    protected function configure(): void
44
    {
45
        $this
46
            ->setDescription('Automatically duplicates sessions that meet the repetition criteria.')
47
            ->addOption('debug', null, InputOption::VALUE_NONE, 'Enable debug mode');
48
    }
49
50
    protected function execute(InputInterface $input, OutputInterface $output): int
51
    {
52
        $debug = $input->getOption('debug');
53
54
        // Find sessions that meet the repetition criteria
55
        $sessions = $this->sessionRepository->findSessionsWithoutChildAndReadyForRepetition();
56
57
        if ($debug) {
58
            $output->writeln(sprintf('Found %d session(s) ready for repetition.', count($sessions)));
59
        }
60
61
        foreach ($sessions as $session) {
62
            if ($debug) {
63
                $output->writeln(sprintf('Processing session: %d', $session->getId()));
64
            }
65
66
            // Duplicate session
67
            $newSession = $this->duplicateSession($session, $debug, $output);
68
69
            // Notify general coach of the new session
70
            $this->notifyGeneralCoach($newSession, $debug, $output);
71
72
            $output->writeln('Created new session: ' . $newSession->getId() . ' from session: ' . $session->getId());
73
        }
74
75
        return Command::SUCCESS;
76
    }
77
78
    /**
79
     * Duplicates a session and creates a new session with adjusted dates.
80
     */
81
    private function duplicateSession(Session $session, bool $debug, OutputInterface $output): Session
82
    {
83
        // Calculate new session dates based on the duration of the original session
84
        $duration = $session->getAccessEndDate()->diff($session->getAccessStartDate())->days;
0 ignored issues
show
Documentation Bug introduced by
It seems like $session->getAccessEndDa...ccessStartDate())->days can also be of type boolean. However, the property $days is declared as type false|integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
85
        $newStartDate = (clone $session->getAccessEndDate())->modify('+1 day');
86
        $newEndDate = (clone $newStartDate)->modify("+{$duration} days");
87
88
        if ($debug) {
89
            $output->writeln(sprintf(
90
                'Duplicating session %d. New start date: %s, New end date: %s',
91
                $session->getId(),
92
                $newStartDate->format('Y-m-d H:i:s'),
93
                $newEndDate->format('Y-m-d H:i:s')
94
            ));
95
        }
96
97
        // Create a new session with the same details as the original session
98
        $newSession = new Session();
99
        $newSession
100
            ->setTitle($session->getTitle() . ' (Repetition ' . $session->getId() . ' - ' . time() . ')')
101
            ->setAccessStartDate($newStartDate)
102
            ->setAccessEndDate($newEndDate)
103
            ->setDisplayStartDate($newStartDate)
104
            ->setDisplayEndDate($newEndDate)
105
            ->setCoachAccessStartDate($newStartDate)
106
            ->setCoachAccessEndDate($newEndDate)
107
            ->setVisibility($session->getVisibility())
108
            ->setDuration(0)
109
            ->setDescription($session->getDescription() ?? '')
110
            ->setShowDescription($session->getShowDescription() ?? false)
111
            ->setCategory($session->getCategory())
112
            ->setPromotion($session->getPromotion())
113
            ->setDaysToReinscription($session->getDaysToReinscription())
114
            ->setDaysToNewRepetition($session->getDaysToNewRepetition())
115
            ->setParentId($session->getId())
116
            ->setLastRepetition(false);
117
118
        // Copy the AccessUrls from the original session
119
        foreach ($session->getUrls() as $accessUrl) {
120
            $newSession->addAccessUrl($accessUrl->getUrl());
121
        }
122
123
        // Copy the courses from the original session
124
        foreach ($session->getCourses() as $sessionRelCourse) {
125
            $course = $sessionRelCourse->getCourse();
126
            if ($course) {
127
                $newSession->addCourse($course);
128
            }
129
        }
130
131
        // Copy the general coaches from the original session
132
        foreach ($session->getGeneralCoaches() as $coach) {
133
            $newSession->addGeneralCoach($coach);
134
        }
135
136
        // Save the new session
137
        $this->entityManager->persist($newSession);
138
        $this->entityManager->flush();
139
140
        if ($debug) {
141
            $output->writeln(sprintf('New session %d created successfully.', $newSession->getId()));
142
        }
143
144
        return $newSession;
145
    }
146
147
    /**
148
     * Retrieves or creates a default AccessUrl for sessions.
149
     */
150
    private function getDefaultAccessUrl()
0 ignored issues
show
Unused Code introduced by
The method getDefaultAccessUrl() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
151
    {
152
        return $this->entityManager->getRepository(AccessUrl::class)->findOneBy([]);
153
    }
154
155
156
    /**
157
     * Notifies the general coach of the session about the new repetition.
158
     */
159
    private function notifyGeneralCoach(Session $newSession, bool $debug, OutputInterface $output): void
160
    {
161
        $generalCoach = $newSession->getGeneralCoaches()->first();
162
        if ($generalCoach) {
163
            $message = sprintf(
164
                'A new repetition of the session "%s" has been created. Please review the details: %s',
165
                $newSession->getTitle(),
166
                $this->generateSessionSummaryLink($newSession)
167
            );
168
169
            if ($debug) {
170
                $output->writeln(sprintf('Notifying coach (ID: %d) for session %d', $generalCoach->getId(), $newSession->getId()));
171
            }
172
173
            // Send message to the general coach
174
            $this->sendMessage($generalCoach->getEmail(), $message);
175
176
            if ($debug) {
177
                $output->writeln('Notification sent.');
178
            }
179
        } else {
180
            if ($debug) {
181
                $output->writeln('No general coach found for session ' . $newSession->getId());
182
            }
183
        }
184
    }
185
186
    /**
187
     * Sends an email message to a user.
188
     */
189
    private function sendMessage(string $recipientEmail, string $message): void
190
    {
191
        $subject = $this->translator->trans('New Session Repetition Created');
192
193
        $email = (new Email())
194
            ->from('[email protected]')
195
            ->to($recipientEmail)
196
            ->subject($subject)
197
            ->html('<p>' . $message . '</p>');
198
199
        $this->mailer->send($email);
200
    }
201
202
    /**
203
     * Generates a link to the session summary page.
204
     */
205
    private function generateSessionSummaryLink(Session $session): string
206
    {
207
        return '/main/session/resume_session.php?id_session=' . $session->getId();
208
    }
209
}
210