Skip to main content
OpenEduCat logo
Push Sync

Moodle Course & Enrolment Sync

When you create a course program in OEC and assign subjects to it, the integration automatically provisions a 4-level hierarchy in Moodle: department category → program sub-category → one course per subject. When students are enrolled in OEC, they are enrolled in the corresponding Moodle courses. Your Moodle admin does not need to touch anything.

OEC is authoritative for this data. Changes made directly in Moodle's course catalog (title, description, dates) will be overwritten on the next push sync. All structural changes go through OEC.

4-Level Academic Hierarchy

OEC's academic structure maps to Moodle's course hierarchy. Each level must be synced before the level below it, the cron enforces this dependency order.

1

Department

op.departmentCourse Category
idnumber: oec_dept_{id}e.g. oec_dept_42

Topologically sorted, parent departments always synced before children.

2

Program

op.courseCourse Sub-Category
idnumber: oec_course_{id}e.g. oec_course_7

Placed under the department's Moodle category. Skipped if department not yet mapped.

3

Subject

op.subjectMoodle Course
idnumber: oec_subj_{subj_id}_course_{course_id}e.g. oec_subj_15_course_7

One Moodle course per (subject, program) pair. Shortname = {subject.code}_{course.code}.

4

Batch

op.batchMoodle Group (within course)
idnumber: oec_batch_{id}e.g. oec_batch_3

Groups are course-scoped in Moodle. Created in each course corresponding to the batch's program.

The idnumber Cross-Reference Key

Every entity synced to Moodle carries a deterministic idnumber that encodes the OEC database ID. This idnumber is set in Moodle's own idnumber field on courses and categories, and also stored in moodle.mapping.moodle_idnumber in OEC.

This design means the mapping table can be rebuilt from scratch by querying Moodle for records by idnumber, no custom Moodle database columns, no plugins needed. If the mapping table is ever lost, the Initial Import Wizard reads idnumbers from Moodle and reconstructs all mappings.

Subject-as-Course design decision

Each OEC subject maps to a separate Moodle course, not a section or group within a course. A subject shared across three programs creates three Moodle courses, each with a distinct idnumber encoding both the subject ID and the program ID (e.g., oec_subj_15_course_7, oec_subj_15_course_12).

This was a deliberate design choice: each Moodle course has an independent gradebook, so grade mapping is unambiguous, one Moodle course ID maps to exactly one OEC subject in one program context. The alternative (subject-as-section) would require pulling section-level grades, which Moodle's API does not cleanly expose.

What Triggers a Sync

Syncs are triggered both in real-time (on record save) and by the hourly cron.

Action in OECTriggerMoodle result
New subject added to a program in OECop.course.write() on subject_ids changeSyncSubjects job enqueued → Moodle course created
Subject name or code updated in OECop.subject.write() on name/code/department_idSyncSubjects update job enqueued → Moodle course shortname/fullname updated
Student enrolled in a course in OECHourly cron (SyncEnrollment)Student enrolled in all Moodle courses for their subject_ids
Student withdraws from a course in OECManual unenrol action or cronenrol_manual_unenrol_users called; op.student.course.enrol mappings removed
Faculty assignment updated in OECop.faculty.write() on faculty_subject_idsFaculty enrolled as teacher in all relevant Moodle courses
Full push cronHourly _cron_full_push_syncAll entities synced in dependency order (dept → prog → subj → users → batches → enrolments)

Enrolment Sync: Critical Detail

The SyncEnrollment service uses enrollment.subject_ids, the specific subjects a student has chosen within their enrollment, not course.subject_ids (all subjects in the program).

This means a student enrolled in a program but taking only 4 of 6 available subjects is enrolled in exactly those 4 Moodle courses, not all 6. If a student's subject_ids is empty (e.g., a new enrollment where subject selection is pending), the service falls back to course.subject_ids.

The enrolment call uses enrol_manual_enrol_users and is idempotent, the service checks for an existing op.student.course.enrol mapping before calling the API. Calling enrol twice for the same student-course pair is safe in Moodle but the mapping check prevents the unnecessary API call.

Course Sync, Frequently Asked Questions

Technical questions about course and enrolment provisioning.

Yes, through the write() hook on op.course and op.subject. When a subject is added to an OEC course program, the system enqueues a SyncSubjects job via queue_job with_delay(channel='root.moodle'). A Moodle course is created with shortname = {subject.code}_{course.code} and idnumber = oec_subj_{subj_id}_course_{course_id}. The Moodle course is placed in the sub-category for that OEC program.

Ready to Transform Your Course Sync?

See how OpenEduCat frees up time so every student gets the attention they deserve.

Try it free for 15 days. No credit card required.