Skip to content

Commit fdb7308

Browse files
Optimize email certificate task by reducing database reads/writes
and introduce configurable settings for task efficiency
1 parent baaa1a0 commit fdb7308

File tree

6 files changed

+161
-14
lines changed

6 files changed

+161
-14
lines changed

classes/task/email_certificate_task.php

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,79 @@ public function get_name() {
4949
public function execute() {
5050
global $DB;
5151

52+
// Get the certificatesPerRun, includeinnotvisiblecourses and certificateexecutionperiod configurations.
53+
$certificatesPerRun = (int)get_config('customcert', 'certificatesperrun');
54+
$includeInNotVisibleCourses = (bool)get_config('customcert', 'includeinnotvisiblecourses');
55+
$certificateExecutionPeriod = (int)get_config('customcert', 'certificateexecutionperiod');
56+
57+
58+
// Get the last processed batch and total certificates to process.
59+
$taskProgress = $DB->get_record('customcert_task_progress', ['taskname' => 'email_certificate_task']);
60+
$lastProcessedBatch = $taskProgress->last_processed;
61+
62+
5263
// Get all the certificates that have requested someone get emailed.
5364
$emailotherslengthsql = $DB->sql_length('c.emailothers');
5465
$sql = "SELECT c.*, ct.id as templateid, ct.name as templatename, ct.contextid, co.id as courseid,
55-
co.fullname as coursefullname, co.shortname as courseshortname
56-
FROM {customcert} c
57-
JOIN {customcert_templates} ct
66+
co.fullname as coursefullname, co.shortname as courseshortname
67+
FROM {customcert} c
68+
JOIN {customcert_templates} ct
5869
ON c.templateid = ct.id
59-
JOIN {course} co
60-
ON c.course = co.id
61-
WHERE (c.emailstudents = :emailstudents
62-
OR c.emailteachers = :emailteachers
63-
OR $emailotherslengthsql >= 3)";
64-
if (!$customcerts = $DB->get_records_sql($sql, ['emailstudents' => 1, 'emailteachers' => 1])) {
70+
JOIN {course} co
71+
ON c.course = co.id";
72+
73+
// Add JOIN with mdl_course_categories to exclude certificates from hidden courses.
74+
$sql .= " JOIN {course_categories} cat ON co.category = cat.id";
75+
76+
// Add conditions to exclude certificates from hidden courses.
77+
$sql .= " WHERE (c.emailstudents = :emailstudents
78+
OR c.emailteachers = :emailteachers
79+
OR $emailotherslengthsql >= 3)";
80+
81+
// Check the includeinnotvisiblecourses configuration.
82+
if (!$includeInNotVisibleCourses) {
83+
// Exclude certificates from hidden courses.
84+
$sql .= " AND co.visible = 1 AND cat.visible = 1";
85+
}
86+
87+
// Add condition based on certificate execution period.
88+
if ($certificateExecutionPeriod > 0) {
89+
// Include courses with no end date or end date greater than the specified period.
90+
$sql .= " AND (co.enddate = 0 OR co.enddate > :enddate)";
91+
$params['enddate'] = time() - $certificateExecutionPeriod;
92+
}
93+
94+
// Execute the SQL query.
95+
if (!$customcerts = $DB->get_records_sql($sql, ['emailstudents' => 1, 'emailteachers' => 1] + $params)) {
6596
return;
6697
}
6798

6899
// The renderers used for sending emails.
69100
$page = new \moodle_page();
70101
$htmlrenderer = $page->get_renderer('mod_customcert', 'email', 'htmlemail');
71102
$textrenderer = $page->get_renderer('mod_customcert', 'email', 'textemail');
72-
foreach ($customcerts as $customcert) {
103+
104+
// Store the total count of certificates in the database.
105+
$totalCertificatesToProcess = count($customcerts);
106+
$DB->set_field('customcert_task_progress', 'total_certificate_to_process', $totalCertificatesToProcess, [
107+
'taskname' => 'email_certificate_task',
108+
]);
109+
110+
111+
// Check if we need to reset and start from the beginning.
112+
if ($lastProcessedBatch >= count($customcerts)) {
113+
$lastProcessedBatch = 0; // Reset to the beginning.
114+
}
115+
116+
if ($certificatesPerRun <= 0) {
117+
// Process all certificates in a single run.
118+
$certificates = $customcerts;
119+
} else {
120+
// Process certificates in batches, starting from the last processed batch.
121+
$certificates = array_slice($customcerts, $lastProcessedBatch, $certificatesPerRun);
122+
}
123+
124+
foreach ($certificates as $customcert) {
73125
// Do not process an empty certificate.
74126
$sql = "SELECT ce.*
75127
FROM {customcert_elements} ce
@@ -175,6 +227,7 @@ public function execute() {
175227
return;
176228
}
177229

230+
$issueIds = array();
178231
// Now, email the people we need to.
179232
foreach ($issuedusers as $user) {
180233
// Set up the user.
@@ -248,8 +301,17 @@ public function execute() {
248301
}
249302

250303
// Set the field so that it is emailed.
251-
$DB->set_field('customcert_issues', 'emailed', 1, ['id' => $user->issueid]);
304+
$issueIds[] = $user->issueid;
305+
//$DB->set_field('customcert_issues', 'emailed', 1, ['id' => $user->issueid]);
306+
}
307+
if (!empty($issueIds)) {
308+
$DB->set_field_select('customcert_issues', 'emailed', 1 , 'id IN (' . implode(',', $issueIds) . ')');
252309
}
253310
}
311+
// Update the last processed batch.
312+
$newLastProcessedBatch = $lastProcessedBatch + count($certificates);
313+
$DB->set_field('customcert_task_progress', 'last_processed', $newLastProcessedBatch, [
314+
'taskname' => 'email_certificate_task',
315+
]);
254316
}
255317
}

db/install.xml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8" ?>
2-
<XMLDB PATH="mod/customcert/db" VERSION="20220613" COMMENT="XMLDB file for Moodle mod/customcert"
2+
<XMLDB PATH="mod/customcert/db" VERSION="20240306" COMMENT="XMLDB file for Moodle mod/customcert"
33
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
44
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
55
>
@@ -100,5 +100,20 @@
100100
<KEY NAME="page" TYPE="foreign" FIELDS="pageid" REFTABLE="customcert_pages" REFFIELDS="id"/>
101101
</KEYS>
102102
</TABLE>
103+
<TABLE NAME="customcert_task_progress" COMMENT="to track email task progress">
104+
<FIELDS>
105+
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
106+
<FIELD NAME="taskname" TYPE="char" LENGTH="255" NOTNULL="true" DEFAULT="email_certificate_task" SEQUENCE="false"/>
107+
<FIELD NAME="last_processed" TYPE="int" LENGTH="20" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
108+
<FIELD NAME="total_certificate_to_process" TYPE="int" LENGTH="20" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="to store the total count of certificates that should be processed"/>
109+
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
110+
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
111+
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
112+
</FIELDS>
113+
<KEYS>
114+
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
115+
<KEY NAME="usermodified" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id"/>
116+
</KEYS>
117+
</TABLE>
103118
</TABLES>
104119
</XMLDB>

db/upgrade.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,5 +235,47 @@ function xmldb_customcert_upgrade($oldversion) {
235235
upgrade_mod_savepoint(true, 2023042405, 'customcert');
236236
}
237237

238+
if ($oldversion < 2023042408) {
239+
240+
// Define table customcert_task_progress to be created.
241+
$table = new xmldb_table('customcert_task_progress');
242+
243+
// Adding fields to table customcert_task_progress.
244+
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
245+
$table->add_field('taskname', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'email_certificate_task');
246+
$table->add_field('last_processed', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, '0');
247+
$table->add_field('total_certificate_to_process', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, '0');
248+
$table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
249+
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
250+
$table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
251+
252+
// Adding keys to table customcert_task_progress.
253+
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
254+
$table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']);
255+
256+
// Conditionally launch create table for customcert_task_progress.
257+
if (!$dbman->table_exists($table)) {
258+
$dbman->create_table($table);
259+
// Add a default row to the customcert_task_progress table.
260+
$defaultData = new stdClass();
261+
$defaultData->taskname = 'email_certificate_task';
262+
$defaultData->last_processed = 0;
263+
$defaultData->total_certificate_to_process = 0;
264+
$defaultData->usermodified = get_admin();;
265+
$defaultData->timecreated = time();
266+
$defaultData->timemodified = time();
267+
268+
// Write close to ensure the transaction is committed.
269+
\core\session\manager::write_close();
270+
271+
// Insert the default data into the table.
272+
$DB->insert_record('customcert_task_progress', $defaultData);
273+
}
274+
275+
// Customcert savepoint reached.
276+
upgrade_mod_savepoint(true, 2023042408, 'customcert');
277+
}
278+
279+
238280
return true;
239281
}

lang/en/customcert.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,12 @@
228228

229229
// Acess API.
230230
$string['customcert:managelanguages'] = 'Manage language on edit form';
231+
232+
$string['certificatesperrun'] = 'Certificates Per Run';
233+
$string['certificatesperrun_desc'] = 'Enter the number of certificates to process per scheduled task run where 0 means it will process all certifiate.';
234+
$string['includeinnotvisiblecourses'] = 'Include Certificates in Not Visible Courses';
235+
$string['includeinnotvisiblecourses_desc'] = 'Check this box to include certificates in courses that are not visible to the user.';
236+
$string['certificateexecutionperiod'] = 'Certificate Execution Period';
237+
$string['certificateexecutionperiod_desc'] = 'Specify the period for which certificates should be executed based on their end date. Set to 0 to execute all certificates, regardless of their age.';
238+
$string['scheduledtaskconfigheading'] = 'Scheduled Task Configuration';
239+
$string['scheduledtaskconfigdesc'] = 'Configure the settings for the scheduled task that processes certificates.';

settings.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,33 @@
5858
new moodle_url('/mod/customcert/download_all_certificates.php'), ''));
5959
}
6060

61+
62+
$settings->add(new admin_setting_heading('scheduledtaskconfig',
63+
get_string('scheduledtaskconfigheading', 'customcert'),
64+
get_string('scheduledtaskconfigdesc', 'customcert')));
65+
66+
$settings->add(new admin_setting_configtext('customcert/certificatesperrun', get_string('certificatesperrun', 'customcert'),
67+
get_string('certificatesperrun_desc', 'customcert'),
68+
50, PARAM_INT));
69+
$settings->add(new admin_setting_configcheckbox('customcert/includeinnotvisiblecourses',
70+
get_string('includeinnotvisiblecourses', 'customcert'),
71+
get_string('includeinnotvisiblecourses_desc', 'customcert'), 0));
72+
$settings->add(
73+
new admin_setting_configduration(
74+
'customcert/certificateexecutionperiod',
75+
new \lang_string('certificateexecutionperiod', 'customcert'),
76+
new \lang_string('certificateexecutionperiod_desc', 'customcert'),
77+
365 * DAYSECS
78+
)
79+
);
80+
6181
$settings->add(new admin_setting_heading('defaults',
6282
get_string('modeditdefaults', 'admin'), get_string('condifmodeditdefaults', 'admin')));
6383

6484
$yesnooptions = [
6585
0 => get_string('no'),
6686
1 => get_string('yes'),
6787
];
68-
6988
$settings->add(new admin_setting_configselect('customcert/emailstudents',
7089
get_string('emailstudents', 'customcert'), get_string('emailstudents_help', 'customcert'), 0, $yesnooptions));
7190
$settings->add(new admin_setting_configselect('customcert/emailteachers',

version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
2626

27-
$plugin->version = 2023042407; // The current module version (Date: YYYYMMDDXX).
27+
$plugin->version = 2023042408; // The current module version (Date: YYYYMMDDXX).
2828
$plugin->requires = 2023042400; // Requires this Moodle version (4.2).
2929
$plugin->cron = 0; // Period for cron to check this module (secs).
3030
$plugin->component = 'mod_customcert';

0 commit comments

Comments
 (0)