aboutsummaryrefslogtreecommitdiff
path: root/lib/cronjobs
diff options
context:
space:
mode:
authorThomas Hackl <hackl@data-quest.de>2022-12-07 07:17:21 +0000
committerThomas Hackl <hackl@data-quest.de>2022-12-07 07:17:21 +0000
commit3d8cb20aef2c55cadd38dccc3f51128d7e357ecf (patch)
treea144f9afa0c2b2bcb2622f61d89455fd84a85614 /lib/cronjobs
parentd22c048e14a346feac17aa8983c0b89bf9010490 (diff)
Resolve "Erweiterung Courseware: Zertifikate, Erinnerungen und Rücksetzen des Fortschritts"
Closes #1660 Merge request studip/studip!1172
Diffstat (limited to 'lib/cronjobs')
-rw-r--r--lib/cronjobs/courseware.php341
1 files changed, 341 insertions, 0 deletions
diff --git a/lib/cronjobs/courseware.php b/lib/cronjobs/courseware.php
new file mode 100644
index 0000000..16e0ad9
--- /dev/null
+++ b/lib/cronjobs/courseware.php
@@ -0,0 +1,341 @@
+<?php
+/**
+ * courseware.php
+ *
+ * @author Thomas Hackl <hackl@data-quest.de>
+ * @access public
+ * @since 5.3
+ */
+
+class CoursewareCronjob extends CronJob
+{
+ public static function getName()
+ {
+ return _('Courseware-Erinnerungen und -zertifikate verschicken sowie Fortschritt zurücksetzen');
+ }
+
+ public static function getDescription()
+ {
+ return _('Versendet Erinnerungen, Zertifikate bei Erreichen eines bestimmten Fortschritts und setzt ' .
+ 'Fortschritt bei derartig konfigurierten Coursewares zurück.');
+ }
+
+ public static function getParameters()
+ {
+ return [
+ 'verbose' => [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'status' => 'optional',
+ 'description' => _('Sollen Ausgaben erzeugt werden (sind später im Log des Cronjobs sichtbar)'),
+ ]
+ ];
+ }
+
+ public function setUp()
+ {
+ }
+
+ public function execute($last_result, $parameters = [])
+ {
+ $verbose = $parameters['verbose'];
+
+ /*
+ * Fetch all courses that have some relevant settings.
+ */
+ $todo = DBManager::get()->fetchAll(
+ "SELECT c.`range_id`, c. `field`, c.`value`
+ FROM `config_values` c
+ JOIN `seminare` s ON (s.`Seminar_id` = c.`range_id`)
+ WHERE c.`field` IN (:fields)",
+ ['fields' => [
+ // Send certificate when this progress is reached
+ 'COURSEWARE_CERTIFICATE_SETTINGS',
+ // Remind all users about courseware
+ 'COURSEWARE_REMINDER_SETTINGS',
+ // Reset user progress to 0
+ 'COURSEWARE_RESET_PROGRESS_SETTINGS'
+ ]
+ ]
+ );
+
+ if (count($todo) > 0) {
+
+ if ($verbose) {
+ echo sprintf("Found %u courses to process.\n", count($todo));
+ }
+
+ $timezone = Config::get()->DEFAULT_TIMEZONE;
+
+ // Process all found entries...
+ foreach ($todo as $one) {
+
+ // Fetch all courseware blocks belonging to the current course.
+ $blocks = DBManager::get()->fetchFirst(
+ "SELECT DISTINCT b.`id`
+ FROM `cw_blocks` b
+ JOIN `cw_containers` c ON (c.`id` = b.`container_id`)
+ JOIN `cw_structural_elements` e ON (e.`id` = c.`structural_element_id`)
+ WHERE e.`range_id` = :course",
+ ['course' => $one['range_id']]
+ );
+
+ // extract details from JSON
+ $settings = json_decode($one['value'], true);
+
+ // differentiate by setting type
+ switch ($one['field']) {
+ // Send certificates to those who have progressed far enough and have not yet gotten one.
+ case 'COURSEWARE_CERTIFICATE_SETTINGS':
+
+ if ($verbose) {
+ echo sprintf("Generating certificates for course %s.\n",
+ $one['range_id']);
+ }
+
+ // Fetch accumulated progress values for all users in this course.
+ $progresses = DBManager::get()->fetchAll(
+ "SELECT DISTINCT p.`user_id`, SUM(p.`grade`) AS progress
+ FROM `cw_user_progresses` p
+ WHERE `block_id` IN (:blocks)
+ AND NOT EXISTS (
+ SELECT `id` FROM `cw_certificates` WHERE `user_id` = p.`user_id` AND `course_id` = :course
+ )
+ GROUP BY `user_id`",
+ ['blocks' => $blocks, 'course' => $one['range_id']]
+ );
+
+ // Calculate percentual progress and send certificates if necessary.
+ foreach ($progresses as $progress) {
+ $percent = ($progress['progress'] / count($blocks)) * 100;
+ if ($percent >= $settings['threshold']) {
+ if ($verbose) {
+ echo sprintf("User %s will get a certificate for course %s.\n",
+ $progress['user_id'], $one['range_id']);
+ }
+
+ $this->sendCertificate($one['range_id'], $progress['user_id'],
+ $percent, $settings);
+
+ /*
+ * Insert a new entry into database for tracking who already got a certificate.
+ * This can be useful if certificates get a validity time or such.
+ */
+ $entry = new Courseware\Certificate();
+ $entry->user_id = $progress['user_id'];
+ $entry->course_id = $one['range_id'];
+ $entry->store();
+ }
+ }
+
+ break;
+
+ // Send reminders to all course participants.
+ case 'COURSEWARE_REMINDER_SETTINGS':
+
+ // Check when the last reminder was sent...
+ $now = new DateTime('', new DateTimeZone($timezone));
+
+ // What would be the minimum date for the last reminder?
+ $minReminder = clone $now;
+
+ // The last reminder has been sent at?
+ $lastReminder = new DateTime('', new DateTimeZone($timezone));
+ $lastReminder->setTimestamp(
+ UserConfig::get($one['range_id'])->COURSEWARE_LAST_REMINDER ?: 0
+ );
+
+ // Check if the settings specify a start and/or end date for reminders
+ $start = new DateTime($settings['startDate'] ?: '1970-01-01',
+ new DateTimeZone($timezone));
+ $end = new DateTime($settings['endDate'] ?: '2199-12-31',
+ new DateTimeZone($timezone));
+
+ $interval = new DateInterval('P1D');
+ switch ($settings['interval']) {
+ case 7:
+ $interval = new DateInterval('P7D');
+ break;
+ case 14:
+ $interval = new DateInterval('P14D');
+ break;
+ case 30:
+ $interval = new DateInterval('P1M');
+ break;
+ case 90:
+ $interval = new DateInterval('P3M');
+ break;
+ case 180:
+ $interval = new DateInterval('P6M');
+ break;
+ case 365:
+ $interval = new DateInterval('P1Y');
+ break;
+ }
+ $minReminder->sub($interval);
+
+ // ... and send a new one if necessary.
+ if ($lastReminder <= $minReminder && $now >= $start && $now <= $end) {
+ if ($verbose) {
+ echo sprintf("Sending reminders for course %s.\n",
+ $one['range_id']);
+ }
+
+ if ($this->sendReminders($one['range_id'], $settings)) {
+ UserConfig::get($one['range_id'])->store('COURSEWARE_LAST_REMINDER',
+ $now->getTimestamp()
+ );
+ }
+ }
+
+ break;
+
+ // Reset courseware progress to 0 for all course participants.
+ case 'COURSEWARE_RESET_PROGRESS_SETTINGS':
+
+ // Check when the last reset was performed...
+ $now = new DateTime('', new DateTimeZone($timezone));
+ $checkLast = clone $now;
+ $lastReset = new DateTime('', new DateTimeZone($timezone));
+ $lastReset->setTimestamp(
+ UserConfig::get($one['range_id'])->COURSEWARE_LAST_PROGRESS_RESET ?: 0
+ );
+
+ $interval = new DateInterval('P1D');
+ switch ($one['value']) {
+ case 14:
+ $interval = new DateInterval('P14D');
+ break;
+ case 30:
+ $interval = new DateInterval('P1M');
+ break;
+ case 90:
+ $interval = new DateInterval('P3M');
+ break;
+ case 180:
+ $interval = new DateInterval('P6M');
+ break;
+ case 365:
+ $interval = new DateInterval('P1Y');
+ break;
+ }
+
+ // ... and reset again if necessary.
+ if ($lastReset <= $checkLast->sub($interval)) {
+ if ($verbose) {
+ echo sprintf("Resetting all progress for courseware in course %s.\n",
+ $one['range_id']);
+ }
+
+ // Remove all progress in the given blocks.
+ $this->resetProgress($one['range_id'], $blocks, $settings);
+
+ UserConfig::get($one['range_id'])->store('COURSEWARE_LAST_PROGRESS_RESET',
+ $now->getTimestamp()
+ );
+ }
+ }
+ }
+
+ } else if ($verbose) {
+ echo "Nothing to do.\n";
+ }
+ }
+
+ private function sendCertificate($course_id, $user_id, $progress, $settings)
+ {
+ $user = User::find($user_id);
+ $course = Course::find($course_id);
+
+ $template = $GLOBALS['template_factory']->open('courseware/mails/certificate');
+ $html = $template->render(
+ compact('user', 'course')
+ );
+
+ // Generate the PDF.
+ $pdf = new CoursewarePDFCertificate($settings['image']);
+ $pdf->AddPage();
+ $pdf->writeHTML($html, true, false, true, false, '');
+ $pdf_file_name = $user->nachname . '_' . $course->name . '_' . _('Zertifikat') . '.pdf';
+ $filename = $GLOBALS['TMP_PATH'] . '/' . $pdf_file_name;
+ $pdf->Output($filename, 'F');
+
+ // Send the mail with PDF attached.
+ $mail = new StudipMail();
+
+ $message = sprintf(
+ _('Anbei erhalten Sie Ihr Courseware-Zertifikat zur Veranstaltung %1$s, in der Sie einen Fortschritt ' .
+ 'von %2$u %% erreicht haben.'), $course->getFullname(), $progress);
+ $message .= "\n\n" . _('Über folgenden Link kommen Sie direkt zur Courseware') . ': ' .
+ URLHelper::getURL('seminar_main.php', ['auswahl' => $course->id,
+ 'redirect_to' => 'dispatch.php/course/courseware']);
+
+ $mail->addRecipient($user->email, $user->getFullname())
+ ->setSubject(_('Courseware: Zertifikat') . ' - ' . $course->getFullname())
+ ->setBodyText($message)
+ ->addFileAttachment($filename, $pdf_file_name)
+ ->send();
+
+ @unlink($filename);
+
+ // Add database entry for the certificate.
+
+ }
+
+ private function sendReminders($course_id, $settings)
+ {
+ $course = Course::find($course_id);
+
+ $recipients = $course->getMembersWithStatus('autor', true);
+
+ $mail = new StudipMail();
+
+ foreach ($recipients as $rec) {
+ $mail->addRecipient(
+ $rec->email,
+ $rec->getUserFullname(),
+ 'bcc'
+ );
+ }
+
+ $message = $settings['mailText'] . "\n\n" . _('Über folgenden Link kommen Sie direkt zur Courseware') . ': ' .
+ URLHelper::getURL('seminar_main.php', ['auswahl' => $course->id,
+ 'redirect_to' => 'dispatch.php/course/courseware']);
+
+ $mail->setSubject(_('Courseware: Erinnerung') . ' - ' . $course->getFullname())
+ ->setBodyText($message);
+
+ return $mail->send();
+ }
+
+ private function resetProgress($course_id, $block_ids, $settings)
+ {
+ $course = Course::find($course_id);
+
+ DBManager::get()->execute(
+ "DELETE FROM `cw_user_progresses` WHERE `block_id` IN (:blocks)",
+ ['blocks' => $block_ids]
+ );
+
+ $recipients = $course->getMembersWithStatus('autor', true);
+
+ $mail = new StudipMail();
+
+ foreach ($recipients as $rec) {
+ $mail->addRecipient(
+ $rec->email,
+ $rec->getUserFullname(),
+ 'bcc'
+ );
+ }
+
+ $message = $settings['mailText'] . "\n\n" . _('Über folgenden Link kommen Sie direkt zur Courseware') . ': ' .
+ URLHelper::getURL('seminar_main.php', ['auswahl' => $course->id,
+ 'redirect_to' => 'dispatch.php/course/courseware']);
+
+ $mail->setSubject(_('Courseware: Fortschritt zurückgesetzt') . ' - ' . $course->getFullname())
+ ->setBodyText($message);
+
+ return $mail->send();
+ }
+}