Passed
Pull Request — master (#5831)
by
unknown
07:35
created

SessionRepetitionCommand::execute()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 26
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 11
nc 6
nop 2
dl 0
loc 26
rs 9.9
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($duration)
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
            $newSession->addCourse($course);
127
        }
128
129
        // Copy the general coaches from the original session
130
        foreach ($session->getGeneralCoaches() as $coach) {
131
            $newSession->addGeneralCoach($coach);
132
        }
133
134
        // Save the new session
135
        $this->entityManager->persist($newSession);
136
        $this->entityManager->flush();
137
138
        if ($debug) {
139
            $output->writeln(sprintf('New session %d created successfully.', $newSession->getId()));
140
        }
141
142
        return $newSession;
143
    }
144
145
    /**
146
     * Retrieves or creates a default AccessUrl for sessions.
147
     */
148
    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...
149
    {
150
        return $this->entityManager->getRepository(AccessUrl::class)->findOneBy([]);
151
    }
152
153
154
    /**
155
     * Notifies the general coach of the session about the new repetition.
156
     */
157
    private function notifyGeneralCoach(Session $newSession, bool $debug, OutputInterface $output): void
158
    {
159
        $generalCoach = $newSession->getGeneralCoaches()->first();
160
        if ($generalCoach) {
161
            $message = sprintf(
162
                'A new repetition of the session "%s" has been created. Please review the details: %s',
163
                $newSession->getTitle(),
164
                $this->generateSessionSummaryLink($newSession)
165
            );
166
167
            if ($debug) {
168
                $output->writeln(sprintf('Notifying coach (ID: %d) for session %d', $generalCoach->getId(), $newSession->getId()));
169
            }
170
171
            // Send message to the general coach
172
            $this->sendMessage($generalCoach->getEmail(), $message);
173
174
            if ($debug) {
175
                $output->writeln('Notification sent.');
176
            }
177
        } else {
178
            if ($debug) {
179
                $output->writeln('No general coach found for session ' . $newSession->getId());
180
            }
181
        }
182
    }
183
184
    /**
185
     * Sends an email message to a user.
186
     */
187
    private function sendMessage(string $recipientEmail, string $message): void
188
    {
189
        $subject = $this->translator->trans('New Session Repetition Created');
190
191
        $email = (new Email())
192
            ->from('[email protected]')
193
            ->to($recipientEmail)
194
            ->subject($subject)
195
            ->html('<p>' . $message . '</p>');
196
197
        $this->mailer->send($email);
198
    }
199
200
    /**
201
     * Generates a link to the session summary page.
202
     */
203
    private function generateSessionSummaryLink(Session $session): string
204
    {
205
        return '/main/session/resume_session.php?id_session=' . $session->getId();
206
    }
207
}
208