php js css php js css

Changes

Changes to Script: Inspect AF Courses & Course Schedules Units components

235812
Today 4:53am
McNaught, Scott
Modified script
235813
Today 4:53am
McNaught, Scott
Modified script
Last Run:
Id:
SRiug5aJoEIbXx1y
SRiug5aJoEIbXx1y
Title:
Inspect AF Courses & Course Schedules Units components
Inspect AF Courses & Course Schedules Units components
Code:
<?php
/**
* Deep inspection + demo data generator: AF Courses, Schedules, Units.
* Creates 50 realistic courses with future schedules (Term 1-4 2027),
* products, schedules and per-week units across levels/age groups.
*/

$iCoursesTarget = 50;

// ---------- 1. Inspect existing data ----------
echo "=== INSPECTION ===\n";
echo "Courses total: " . \Components\AF\Courses::getCount() . "\n";
echo "Schedules total: " . \Components\AF\Courses\Schedules::getCount() . "\n";
echo "Units total: " . \Components\AF\Courses\Units::getCount() . "\n";
echo "Sessions: " . \Components\AF\Courses\Configuration\Sessions::getCount() . "\n";
echo "Levels: " . \Components\AF\Courses\Configuration\Levels::getCount() . "\n";
echo "Products: " . \Components\Commerce\Products::getCount() . "\n\n";

// ---------- 2. Ensure future sessions exist (Term 2-4 2027) ----------
$arrNewSessions = [
['2027', 'Term 2 2027', '2027-04-19', '2027-06-19'],
['2027', 'Term 3 2027', '2027-07-12', '2027-09-11'],
['2027', 'Term 4 2027', '2027-10-04', '2027-12-04'],
];
$arrSessionIds = [51 => ['2027-02-01', '2027-03-28']]; // existing Term 1 2027
foreach ($arrNewSessions as $arrS)
{
$arrExisting = \Components\AF\Courses\Configuration\Sessions::getAll([
'filter' => [['session_name', '=', $arrS[1]], 'AND', ['session_year', '=', $arrS[0]]],
'limit' => 1,
]);
if (count($arrExisting))
{
$iSid = (int)$arrExisting[0]['session_id'];
}
else
{
$iSid = (int)\Components\AF\Courses\Configuration\Sessions::save([
'session_year' => $arrS[0],
'session_name' => $arrS[1],
'session_start_date' => $arrS[2],
'session_end_date' => $arrS[3],
'session_visible_public' => 1,
]);
}
$arrSessionIds[$iSid] = [$arrS[2], $arrS[3]];
}
echo "Sessions available: " . implode(', ', array_keys($arrSessionIds)) . "\n";

// ---------- 3. Course blueprint builder ----------
$arrTeachers = [1351, 1181, 1052, 1180, 1182];
// Rooms: CBD Campus(2): 7,8,9,23,24 | St Kilda(1): 3,4,5,6 | Online Zooms(17): 19,20,21
$arrRooms = [
'cbd' => [7, 8, 9, 23, 24],
'stkilda' => [3, 4, 5, 6],
'online' => [19, 20, 21],
];
$arrDayNames = [1 => 'Mon', 2 => 'Tue', 3 => 'Wed', 4 => 'Thu', 5 => 'Fri', 6 => 'Sat', 7 => 'Sun'];
$arrFullDayNames = [1 => 'monday', 2 => 'tuesday', 3 => 'wednesday', 4 => 'thursday', 5 => 'friday', 6 => 'saturday', 7 => 'sunday'];

$fnCourse = function($arrDef) {
return $arrDef;
};

// Build 50 course definitions: [group, level, levelName, ageGroup, type, pace, hoursPerClass, timeStart, timeEnd, days, campusKey]
$arrCourses = [];

// Adults - Standard progression (16)
foreach ([
['A1.1', 2], ['A1.2', 7], ['A1.3', 8], ['A1.4', 9],
['A2.1', 10], ['A2.2', 11], ['A2.3', 12], ['A2.4', 131],
['B1.1', 14], ['B1.2', 15], ['B1.3', 16],
['B2.1', 17], ['B2.2', 18], ['C1', 6], ['C2', 132], ['A2.5', 27],
] as $i => $arrLvl)
{
$iDay = ($i % 5) + 1;
$bEvening = ($i % 2 === 0);
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 1, 'type' => 2,
'pace' => 38, 'hpc' => 2.0, 'start' => $bEvening ? '18:00:00' : '09:30:00', 'end' => $bEvening ? '20:00:00' : '11:30:00',
'days' => [$iDay], 'campus' => ($i % 4 === 3) ? 'online' : 'cbd', 'teacher' => $arrTeachers[$i % 5],
];
}
// Adults - speciality (8)
$arrSpecial = [
['Conversation B1', 14, 29, 39, 1.5, '18:00:00', '19:30:00', 2],
['Conversation B2', 17, 29, 39, 1.5, '12:00:00', '13:30:00', 4],
['French for Travel A1', 2, 27, 39, 1.5, '17:30:00', '19:00:00', 5],
['Easy Going A2', 10, 30, 39, 1.5, '10:00:00', '11:30:00', 3],
['DELF B2 Prep', 17, 24, 38, 2.0, '17:00:00', '19:00:00', 6],
['Grammar Workshop B1', 14, 16, 44, 6.0, '09:30:00', '15:30:00', 6],
['Pronunciation Workshop A2', 10, 16, 44, 6.0, '09:30:00', '15:30:00', 6],
['TCF Exam Prep', 130, 24, 38, 2.0, '18:00:00', '20:00:00', 1],
];
foreach ($arrSpecial as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 1, 'type' => $a[2],
'pace' => $a[3], 'hpc' => $a[4], 'start' => $a[5], 'end' => $a[6],
'days' => [$a[7]], 'campus' => ($i === 7) ? 'online' : 'stkilda', 'teacher' => $arrTeachers[($i + 2) % 5],
];
}
// Teens (10)
foreach ([['Year 7', 22], ['Year 8', 23], ['Year 9', 24], ['Year 10', 133], ['Year 11', 134], ['Year 12', 135]] as $i => $arrLvl)
{
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 2, 'type' => ($i % 3 === 0 ? 23 : 2),
'pace' => 38, 'hpc' => 2.0, 'start' => '16:30:00', 'end' => '18:30:00',
'days' => [($i % 4) + 1], 'campus' => ($i % 3 === 2) ? 'online' : 'cbd', 'teacher' => $arrTeachers[($i + 1) % 5],
];
}
foreach ([['Teens Conversation A2', 10, 29, 5], ['DELF Junior A2 Prep', 10, 24, 6], ['Taste of French Y9', 24, 23, 3], ['Taste of French Y11', 134, 23, 4]] as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 2, 'type' => $a[2],
'pace' => 39, 'hpc' => 1.5, 'start' => '16:45:00', 'end' => '18:15:00',
'days' => [$a[3]], 'campus' => 'stkilda', 'teacher' => $arrTeachers[($i + 3) % 5],
];
}
// Children (10)
foreach ([['Prep', 136], ['Year 1', 137], ['Year 2', 138], ['Year 3', 139]] as $i => $arrLvl)
{
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 3, 'type' => 2,
'pace' => 39, 'hpc' => 1.5, 'start' => '10:00:00', 'end' => '11:30:00',
'days' => [6], 'campus' => 'stkilda', 'teacher' => $arrTeachers[$i % 5],
];
}
foreach ([['Taste of French Kids 1', 136, 22, 6], ['Taste of French Kids 2', 137, 22, 6], ['Kids Holiday Immersion A1', 2, 25, 2], ['Kids Holiday Immersion A2', 130, 25, 2], ['Kids Conversation B1', 14, 29, 3]] as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 3, 'type' => $a[2],
'pace' => 39, 'hpc' => 1.5, 'start' => '13:00:00', 'end' => '14:30:00',
'days' => [$a[3]], 'campus' => ($i > 1 ? 'cbd' : 'stkilda'), 'teacher' => $arrTeachers[($i + 4) % 5],
];
}
// Toddlers (4)
foreach ([['French Play Group', 21, '09:30:00', '10:15:00'], ['Mum & Me', 19, '10:30:00', '11:15:00'], ['Story Time', 20, '11:30:00', '12:15:00'], ['French Play Group 2', 21, '09:30:00', '10:15:00']] as $i => $a)
{
$arrCourses[] = [
'level' => 'Pre-School', 'level_id' => 1, 'age' => 12, 'type' => $a[1],
'pace' => 37, 'hpc' => 0.75, 'start' => $a[2], 'end' => $a[3],
'days' => [($i % 2) + 5], 'campus' => 'stkilda', 'teacher' => $arrTeachers[$i % 5],
];
}
// Corporate + Intensive (2)
$arrCourses[] = [
'level' => 'A1.1 Corporate', 'level_id' => 2, 'age' => 4, 'type' => 28,
'pace' => 38, 'hpc' => 2.0, 'start' => '08:00:00', 'end' => '10:00:00',
'days' => [2, 4], 'campus' => 'cbd', 'teacher' => $arrTeachers[0],
];
$arrCourses[] = [
'level' => 'A1 Intensive', 'level_id' => 1, 'age' => 1, 'type' => 3,
'pace' => 45, 'hpc' => 4.0, 'start' => '09:00:00', 'end' => '13:00:00',
'days' => [1, 2, 3, 4, 5], 'campus' => 'online', 'teacher' => $arrTeachers[1],
];

$arrCourses = array_slice($arrCourses, 0, $iCoursesTarget);
echo "Course definitions: " . count($arrCourses) . "\n\n";

// ---------- 4. Create courses ----------
$arrSessionKeys = array_keys($arrSessionIds);
$iCreated = 0;
$arrErrors = [];

foreach ($arrCourses as $i => $a)
{
// Assign sessions round-robin, then a start date inside the session window
$iSessionId = $arrSessionKeys[$i % count($arrSessionKeys)];
list($sSessStart, $sSessEnd) = $arrSessionIds[$iSessionId];
$iDayNum = $a['days'][0];
$iOffset = ($i * 7) % 28; // vary start week
$oStart = new DateTime($sSessStart);
$oStart->modify('+' . $iOffset . ' days');
while ((int)$oStart->format('N') !== $iDayNum)
{
$oStart->modify('+1 day');
}
// 8 weekly classes per schedule
$oEnd = clone $oStart;
$oEnd->modify('+' . (count($a['days']) * 7) . ' days');

$sCourseCode = $a['level'] . '-' . $arrDayNames[$iDayNum] . '-' . substr(str_replace('-', '', $sSessStart), 0, 8) . '-' . ($i + 1);

// Product
if (!isset($iBrandId))
{
$arrBrand = \Components\Commerce\Products\Brands::getAll(['limit' => 1]);
if (count($arrBrand))
{
$iBrandId = (int)$arrBrand[0]['brand_id'];
}
else
{
$iBrandId = (int)\Components\Commerce\Products\Brands::save(['brand_title' => 'Alliance Française']);
}
echo "Using brand: {$iBrandId}\n";
}
$fHoursTotal = $a['hpc'] * 8;
$fPrice = round(($fHoursTotal * 55) / 10) * 10;
$iProductId = 0;
$arrProd = \Components\Commerce\Products::getAll(['filter' => [['product_code', '=', $sCourseCode]], 'limit' => 1]);
if (count($arrProd))
{
$iProductId = (int)$arrProd[0]['product_id'];
}
else
{
$iProductId = (int)\Components\Commerce\Products::save([
'product_title' => $a['level'] . ' ' . $arrDayNames[$iDayNum] . ' ' . ($iSessionId === 51 ? 'Term 1 2027' : '2027'),
'product_price_base' => $fPrice,
'product_code' => $sCourseCode,
'product_brand_id' => $iBrandId,
'product_is_offline' => 1,
]);
}

$arrLocationIds = $arrRooms[$a['campus']];
$iLocationId = $arrLocationIds[$i % count($arrLocationIds)];
$sLocationType = ($a['campus'] === 'online') ? 'remote' : 'internal';

// Course
$arrCourse = [
'course_code' => $sCourseCode,
'course_type' => 'collective',
'course_start_date' => $oStart->format('Y-m-d'),
'course_start_time' => $a['start'],
'course_end_date' => $oEnd->format('Y-m-d'),
'course_end_time' => $a['end'],
'course_total_hours' => number_format($fHoursTotal, 2, '.', ''),
'course_objectives' => $a['level'] . ' objectives: develop listening and speaking skills, build core vocabulary and consolidate grammar through interactive activities.',
'session_id' => $iSessionId,
'age_group_id' => $a['age'],
'type_id' => $a['type'],
'pace_id' => $a['pace'],
'product_id' => $iProductId,
'location_ids_cache' => (string)$iLocationId,
'level_ids_cache' => (string)$a['level_id'],
'course_is_day_' . $arrFullDayNames[$iDayNum] => 1,
'course_hidden_in_selector' => 0,
'course_membership_option' => 'hidden',
];
$iCourseId = (int)\Components\AF\Courses::save($arrCourse);
if (!$iCourseId)
{
$arrErrors[] = "Course save failed: {$sCourseCode}";
continue;
}

// Level link
\Components\AF\Courses\Levels::save(['course_id' => $iCourseId, 'level_id' => $a['level_id']]);

// Schedule
$arrSchedule = [
'schedule_start_date' => $oStart->format('Y-m-d'),
'schedule_end_date' => $oEnd->format('Y-m-d'),
'schedule_start_time' => $a['start'],
'schedule_end_time' => $a['end'],
'schedule_location_type' => $sLocationType,
'location_id' => $iLocationId,
'teacher_id' => $a['teacher'],
'course_id' => $iCourseId,
'schedule_day' => $a['days'],
];
$iScheduleId = (int)\Components\AF\Courses\Schedules::save($arrSchedule);

// Units - one per class occurrence
$iUnits = 0;
$oUnitDate = clone $oStart;
for ($u = 0; $u < 8; $u++)
{
// step to the next configured weekday
while (!in_array((int)$oUnitDate->format('N'), $a['days']))
{
$oUnitDate->modify('+1 day');
}
\Components\AF\Courses\Units::save([
'unit_date' => $oUnitDate->format('Y-m-d'),
'unit_start_time' => $a['start'],
'unit_end_time' => $a['end'],
'unit_comment' => ($u === 0 ? 'Introduction and placement' : null),
'unit_billing_status' => 'unpaid',
'unit_location_type' => $sLocationType,
'location_id' => $iLocationId,
'teacher_id' => $a['teacher'],
'schedule_id' => $iScheduleId,
'course_id' => $iCourseId,
]);
$iUnits++;
$oUnitDate->modify('+1 day');
}

$iCreated++;
echo sprintf("#%02d course_id=%d sched=%d units=%d %s | %s %s-%s | session %d | lvl %s | product %d \$%.2f\n",
$iCreated, $iCourseId, $iScheduleId, $iUnits, $sCourseCode,
$oStart->format('d M Y'), $a['start'], substr($a['end'], 0, 5), $iSessionId, $a['level'], $iProductId, $fPrice);

\Components\Settings\Developer\Threads::setCurrentProgressStatus("Created {$iCreated} / " . count($arrCourses) . " courses…");
}

echo "\n=== SUMMARY ===\n";
echo "Created courses: {$iCreated}\n";
echo "Courses total now: " . \Components\AF\Courses::getCount() . "\n";
echo "Schedules total now: " . \Components\AF\Courses\Schedules::getCount() . "\n";
echo "Units total now: " . \Components\AF\Courses\Units::getCount() . "\n";
if (count($arrErrors))
{
echo "Errors:\n" . implode("\n", $arrErrors) . "\n";
}
else
{
echo "No errors.\n";
}
<?php
/**
* Deep inspection + demo data generator: AF Courses, Schedules, Units.
* Creates 50 realistic courses with future schedules (Term 1-4 2027),
* products, schedules and per-week units across levels/age groups.
*/

$iCoursesTarget = 50;

// ---------- 1. Inspect existing data ----------
echo "=== INSPECTION ===\n";
echo "Courses total: " . \Components\AF\Courses::getCount() . "\n";
echo "Schedules total: " . \Components\AF\Courses\Schedules::getCount() . "\n";
echo "Units total: " . \Components\AF\Courses\Units::getCount() . "\n";
echo "Sessions: " . \Components\AF\Courses\Configuration\Sessions::getCount() . "\n";
echo "Levels: " . \Components\AF\Courses\Configuration\Levels::getCount() . "\n";
echo "Products: " . \Components\Commerce\Products::getCount() . "\n\n";

// ---------- 2. Ensure future sessions exist (Term 2-4 2027) ----------
$arrNewSessions = [
['2027', 'Term 2 2027', '2027-04-19', '2027-06-19'],
['2027', 'Term 3 2027', '2027-07-12', '2027-09-11'],
['2027', 'Term 4 2027', '2027-10-04', '2027-12-04'],
];
$arrSessionIds = [51 => ['2027-02-01', '2027-03-28']]; // existing Term 1 2027
foreach ($arrNewSessions as $arrS)
{
$arrExisting = \Components\AF\Courses\Configuration\Sessions::getAll([
'filter' => [['session_name', '=', $arrS[1]], 'AND', ['session_year', '=', $arrS[0]]],
'limit' => 1,
]);
if (count($arrExisting))
{
$iSid = (int)$arrExisting[0]['session_id'];
}
else
{
$iSid = (int)\Components\AF\Courses\Configuration\Sessions::save([
'session_year' => $arrS[0],
'session_name' => $arrS[1],
'session_start_date' => $arrS[2],
'session_end_date' => $arrS[3],
'session_visible_public' => 1,
]);
}
$arrSessionIds[$iSid] = [$arrS[2], $arrS[3]];
}
echo "Sessions available: " . implode(', ', array_keys($arrSessionIds)) . "\n";

// ---------- 3. Course blueprint builder ----------
$arrTeachers = [1351, 1181, 1052, 1180, 1182];
// Rooms: CBD Campus(2): 7,8,9,23,24 | St Kilda(1): 3,4,5,6 | Online Zooms(17): 19,20,21
$arrRooms = [
'cbd' => [7, 8, 9, 23, 24],
'stkilda' => [3, 4, 5, 6],
'online' => [19, 20, 21],
];
$arrDayNames = [1 => 'Mon', 2 => 'Tue', 3 => 'Wed', 4 => 'Thu', 5 => 'Fri', 6 => 'Sat', 7 => 'Sun'];
$arrFullDayNames = [1 => 'monday', 2 => 'tuesday', 3 => 'wednesday', 4 => 'thursday', 5 => 'friday', 6 => 'saturday', 7 => 'sunday'];

$fnCourse = function($arrDef) {
return $arrDef;
};

// Build 50 course definitions: [group, level, levelName, ageGroup, type, pace, hoursPerClass, timeStart, timeEnd, days, campusKey]
$arrCourses = [];

// Adults - Standard progression (16)
foreach ([
['A1.1', 2], ['A1.2', 7], ['A1.3', 8], ['A1.4', 9],
['A2.1', 10], ['A2.2', 11], ['A2.3', 12], ['A2.4', 131],
['B1.1', 14], ['B1.2', 15], ['B1.3', 16],
['B2.1', 17], ['B2.2', 18], ['C1', 6], ['C2', 132], ['A2.5', 27],
] as $i => $arrLvl)
{
$iDay = ($i % 5) + 1;
$bEvening = ($i % 2 === 0);
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 1, 'type' => 2,
'pace' => 38, 'hpc' => 2.0, 'start' => $bEvening ? '18:00:00' : '09:30:00', 'end' => $bEvening ? '20:00:00' : '11:30:00',
'days' => [$iDay], 'campus' => ($i % 4 === 3) ? 'online' : 'cbd', 'teacher' => $arrTeachers[$i % 5],
];
}
// Adults - speciality (8)
$arrSpecial = [
['Conversation B1', 14, 29, 39, 1.5, '18:00:00', '19:30:00', 2],
['Conversation B2', 17, 29, 39, 1.5, '12:00:00', '13:30:00', 4],
['French for Travel A1', 2, 27, 39, 1.5, '17:30:00', '19:00:00', 5],
['Easy Going A2', 10, 30, 39, 1.5, '10:00:00', '11:30:00', 3],
['DELF B2 Prep', 17, 24, 38, 2.0, '17:00:00', '19:00:00', 6],
['Grammar Workshop B1', 14, 16, 44, 6.0, '09:30:00', '15:30:00', 6],
['Pronunciation Workshop A2', 10, 16, 44, 6.0, '09:30:00', '15:30:00', 6],
['TCF Exam Prep', 130, 24, 38, 2.0, '18:00:00', '20:00:00', 1],
];
foreach ($arrSpecial as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 1, 'type' => $a[2],
'pace' => $a[3], 'hpc' => $a[4], 'start' => $a[5], 'end' => $a[6],
'days' => [$a[7]], 'campus' => ($i === 7) ? 'online' : 'stkilda', 'teacher' => $arrTeachers[($i + 2) % 5],
];
}
// Teens (10)
foreach ([['Year 7', 22], ['Year 8', 23], ['Year 9', 24], ['Year 10', 133], ['Year 11', 134], ['Year 12', 135]] as $i => $arrLvl)
{
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 2, 'type' => ($i % 3 === 0 ? 23 : 2),
'pace' => 38, 'hpc' => 2.0, 'start' => '16:30:00', 'end' => '18:30:00',
'days' => [($i % 4) + 1], 'campus' => ($i % 3 === 2) ? 'online' : 'cbd', 'teacher' => $arrTeachers[($i + 1) % 5],
];
}
foreach ([['Teens Conversation A2', 10, 29, 5], ['DELF Junior A2 Prep', 10, 24, 6], ['Taste of French Y9', 24, 23, 3], ['Taste of French Y11', 134, 23, 4]] as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 2, 'type' => $a[2],
'pace' => 39, 'hpc' => 1.5, 'start' => '16:45:00', 'end' => '18:15:00',
'days' => [$a[3]], 'campus' => 'stkilda', 'teacher' => $arrTeachers[($i + 3) % 5],
];
}
// Children (10)
foreach ([['Prep', 136], ['Year 1', 137], ['Year 2', 138], ['Year 3', 139]] as $i => $arrLvl)
{
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 3, 'type' => 2,
'pace' => 39, 'hpc' => 1.5, 'start' => '10:00:00', 'end' => '11:30:00',
'days' => [6], 'campus' => 'stkilda', 'teacher' => $arrTeachers[$i % 5],
];
}
foreach ([['Taste of French Kids 1', 136, 22, 6], ['Taste of French Kids 2', 137, 22, 6], ['Kids Holiday Immersion A1', 2, 25, 2], ['Kids Holiday Immersion A2', 130, 25, 2], ['Kids Conversation B1', 14, 29, 3]] as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 3, 'type' => $a[2],
'pace' => 39, 'hpc' => 1.5, 'start' => '13:00:00', 'end' => '14:30:00',
'days' => [$a[3]], 'campus' => ($i > 1 ? 'cbd' : 'stkilda'), 'teacher' => $arrTeachers[($i + 4) % 5],
];
}
// Toddlers (4)
foreach ([['French Play Group', 21, '09:30:00', '10:15:00'], ['Mum & Me', 19, '10:30:00', '11:15:00'], ['Story Time', 20, '11:30:00', '12:15:00'], ['French Play Group 2', 21, '09:30:00', '10:15:00']] as $i => $a)
{
$arrCourses[] = [
'level' => 'Pre-School', 'level_id' => 1, 'age' => 12, 'type' => $a[1],
'pace' => 37, 'hpc' => 0.75, 'start' => $a[2], 'end' => $a[3],
'days' => [($i % 2) + 5], 'campus' => 'stkilda', 'teacher' => $arrTeachers[$i % 5],
];
}
// Corporate + Intensive (2)
$arrCourses[] = [
'level' => 'A1.1 Corporate', 'level_id' => 2, 'age' => 4, 'type' => 28,
'pace' => 38, 'hpc' => 2.0, 'start' => '08:00:00', 'end' => '10:00:00',
'days' => [2, 4], 'campus' => 'cbd', 'teacher' => $arrTeachers[0],
];
$arrCourses[] = [
'level' => 'A1 Intensive', 'level_id' => 1, 'age' => 1, 'type' => 3,
'pace' => 45, 'hpc' => 4.0, 'start' => '09:00:00', 'end' => '13:00:00',
'days' => [1, 2, 3, 4, 5], 'campus' => 'online', 'teacher' => $arrTeachers[1],
];

$arrCourses = array_slice($arrCourses, 0, $iCoursesTarget);
echo "Course definitions: " . count($arrCourses) . "\n\n";

// ---------- 4. Create courses ----------
$arrSessionKeys = array_keys($arrSessionIds);
$iCreated = 0;
$arrErrors = [];

foreach ($arrCourses as $i => $a)
{
// Assign sessions round-robin, then a start date inside the session window
$iSessionId = $arrSessionKeys[$i % count($arrSessionKeys)];
list($sSessStart, $sSessEnd) = $arrSessionIds[$iSessionId];
$iDayNum = $a['days'][0];
$iOffset = ($i * 7) % 28; // vary start week
$oStart = new DateTime($sSessStart);
$oStart->modify('+' . $iOffset . ' days');
while ((int)$oStart->format('N') !== $iDayNum)
{
$oStart->modify('+1 day');
}
// 8 weekly classes per schedule
$oEnd = clone $oStart;
$oEnd->modify('+' . (count($a['days']) * 7) . ' days');

$sCourseCode = $a['level'] . '-' . $arrDayNames[$iDayNum] . '-' . substr(str_replace('-', '', $sSessStart), 0, 8) . '-' . ($i + 1);

// Product
if (!isset($iBrandId))
{
$arrBrand = \Components\Commerce\Products\Brands::getAll(['limit' => 1]);
if (count($arrBrand))
{
$iBrandId = (int)$arrBrand[0]['brand_id'];
}
else
{
$iBrandId = (int)\Components\Commerce\Products\Brands::save(['brand_title' => 'Alliance Française']);
}
echo "Using brand: {$iBrandId}\n";
}
$fHoursTotal = $a['hpc'] * 8;
$fPrice = round(($fHoursTotal * 55) / 10) * 10;
$iProductId = 0;
$arrProd = \Components\Commerce\Products::getAll(['filter' => [['product_code', '=', $sCourseCode]], 'limit' => 1]);
if (count($arrProd))
{
$iProductId = (int)$arrProd[0]['product_id'];
}
else
{
$iProductId = (int)\Components\Commerce\Products::save([
'product_title' => $a['level'] . ' ' . $arrDayNames[$iDayNum] . ' ' . ($iSessionId === 51 ? 'Term 1 2027' : '2027'),
'product_price_base' => $fPrice,
'product_code' => $sCourseCode,
'product_brand_id' => $iBrandId,
'product_is_offline' => 1,
]);
}

$arrLocationIds = $arrRooms[$a['campus']];
$iLocationId = $arrLocationIds[$i % count($arrLocationIds)];
$sLocationType = ($a['campus'] === 'online') ? 'remote' : 'internal';

// Course
$arrCourse = [
'course_code' => $sCourseCode,
'course_type' => 'collective',
'course_start_date' => $oStart->format('Y-m-d'),
'course_start_time' => $a['start'],
'course_end_date' => $oEnd->format('Y-m-d'),
'course_end_time' => $a['end'],
'course_total_hours' => number_format($fHoursTotal, 2, '.', ''),
'course_objectives' => $a['level'] . ' objectives: develop listening and speaking skills, build core vocabulary and consolidate grammar through interactive activities.',
'session_id' => $iSessionId,
'age_group_id' => $a['age'],
'type_id' => $a['type'],
'pace_id' => $a['pace'],
'product_id' => $iProductId,
'location_ids_cache' => (string)$iLocationId,
'level_ids_cache' => (string)$a['level_id'],
'course_is_day_' . $arrFullDayNames[$iDayNum] => 1,
'course_hidden_in_selector' => 0,
'course_membership_option' => 'hidden',
];
$iCourseId = (int)\Components\AF\Courses::save($arrCourse);
if (!$iCourseId)
{
$arrErrors[] = "Course save failed: {$sCourseCode}";
continue;
}

// Level link
\Components\AF\Courses\Levels::save(['course_id' => $iCourseId, 'level_id' => $a['level_id']]);

// Schedule
$arrSchedule = [
'schedule_start_date' => $oStart->format('Y-m-d'),
'schedule_end_date' => $oEnd->format('Y-m-d'),
'schedule_start_time' => $a['start'],
'schedule_end_time' => $a['end'],
'schedule_location_type' => $sLocationType,
'location_id' => $iLocationId,
'teacher_id' => $a['teacher'],
'course_id' => $iCourseId,
'schedule_day' => $a['days'],
];
$iScheduleId = (int)\Components\AF\Courses\Schedules::save($arrSchedule);

// Units - one per class occurrence
$iUnits = 0;
$oUnitDate = clone $oStart;
for ($u = 0; $u < 8; $u++)
{
// step to the next configured weekday
while (!in_array((int)$oUnitDate->format('N'), $a['days']))
{
$oUnitDate->modify('+1 day');
}
\Components\AF\Courses\Units::save([
'unit_date' => $oUnitDate->format('Y-m-d'),
'unit_start_time' => $a['start'],
'unit_end_time' => $a['end'],
'unit_comment' => ($u === 0 ? 'Introduction and placement' : null),
'unit_billing_status' => 'unpaid',
'unit_location_type' => $sLocationType,
'location_id' => $iLocationId,
'teacher_id' => $a['teacher'],
'schedule_id' => $iScheduleId,
'course_id' => $iCourseId,
]);
$iUnits++;
$oUnitDate->modify('+1 day');
}

$iCreated++;
echo sprintf("#%02d course_id=%d sched=%d units=%d %s | %s %s-%s | session %d | lvl %s | product %d \$%.2f\n",
$iCreated, $iCourseId, $iScheduleId, $iUnits, $sCourseCode,
$oStart->format('d M Y'), $a['start'], substr($a['end'], 0, 5), $iSessionId, $a['level'], $iProductId, $fPrice);

\Components\Settings\Developer\Threads::setCurrentProgressStatus("Created {$iCreated} / " . count($arrCourses) . " courses…");
}

echo "\n=== SUMMARY ===\n";
echo "Created courses: {$iCreated}\n";
echo "Courses total now: " . \Components\AF\Courses::getCount() . "\n";
echo "Schedules total now: " . \Components\AF\Courses\Schedules::getCount() . "\n";
echo "Units total now: " . \Components\AF\Courses\Units::getCount() . "\n";
if (count($arrErrors))
{
echo "Errors:\n" . implode("\n", $arrErrors) . "\n";
}
else
{
echo "No errors.\n";
}
Created:
2026-09-04 20:56:27
2026-09-04 20:56:27
Is Ai Generated:
1
1
Is Readonly:
Summary:
This script fills your course booking system with sample (fake) classes for 2027. Here's what it actually changes:

**New term dates added (up to 3):**
It makes sure the system has term periods for 2027 — Terms 2, 3 and 4 of 2027. If a term already exists, it reuses it; otherwise it adds it. Term 1 2027 is assumed to already exist.

**New courses added (about 50):**
It creates 50 French classes covering all age groups:
- 24 adult classes (beginner through advanced, plus special ones like conversation classes, exam preparation, and workshops)
- 10 teen classes (school years 7–12, plus a few extras like conversation and exam prep)
- 9 children's classes (Prep to Year 3, plus holiday and conversation classes)
- 4 toddler/playgroup classes
- 1 corporate class and 1 intensive adult class

Each class gets a start date spread across the 2027 terms, a set day and time, a teacher (chosen from 5 existing teachers), and a room (in the CBD, St Kilda, or online).

**New shop items added (about 50):**
For each class, it also creates a matching product in the online shop so people could buy a spot in it. Prices are worked out from the number of teaching hours (about $55 per hour, rounded). If a product with the same code already exists, it reuses it instead of making a duplicate.

**New class timetables added (about 50):**
Each course gets one timetable entry showing when and where it runs and which teacher takes it.

**New individual lessons added (about 400):**
Each course gets 8 weekly lesson entries — one per week of the term — each with its own date, time, room, and teacher. The first lesson of each course is labelled as an introduction session.

**Nothing is deleted or changed:**
The script only adds new records. It doesn't remove or edit any existing courses, products, or lessons. If you ran it twice, it would mostly reuse the shop items but would likely create duplicate courses and lessons, so it's meant to be run once on a system that needs filling with demo data.
This script fills your course booking system with sample (fake) classes for 2027. Here's what it actually changes:

**New term dates added (up to 3):**
It makes sure the system has term periods for 2027 — Terms 2, 3 and 4 of 2027. If a term already exists, it reuses it; otherwise it adds it. Term 1 2027 is assumed to already exist.

**New courses added (about 50):**
It creates 50 French classes covering all age groups:
- 24 adult classes (beginner through advanced, plus special ones like conversation classes, exam preparation, and workshops)
- 10 teen classes (school years 7–12, plus a few extras like conversation and exam prep)
- 9 children's classes (Prep to Year 3, plus holiday and conversation classes)
- 4 toddler/playgroup classes
- 1 corporate class and 1 intensive adult class

Each class gets a start date spread across the 2027 terms, a set day and time, a teacher (chosen from 5 existing teachers), and a room (in the CBD, St Kilda, or online).

**New shop items added (about 50):**
For each class, it also creates a matching product in the online shop so people could buy a spot in it. Prices are worked out from the number of teaching hours (about $55 per hour, rounded). If a product with the same code already exists, it reuses it instead of making a duplicate.

**New class timetables added (about 50):**
Each course gets one timetable entry showing when and where it runs and which teacher takes it.

**New individual lessons added (about 400):**
Each course gets 8 weekly lesson entries — one per week of the term — each with its own date, time, room, and teacher. The first lesson of each course is labelled as an introduction session.

**Nothing is deleted or changed:**
The script only adds new records. It doesn't remove or edit any existing courses, products, or lessons. If you ran it twice, it would mostly reuse the shop items but would likely create duplicate courses and lessons, so it's meant to be run once on a system that needs filling with demo data.
Modified:
2026-09-04 20:58:30
2026-09-04 20:58:30
Contact Id:
-2
-2