php js css php js css

Changes

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

235802
Today 4:53am
McNaught, Scott
Modified script
235803
Today 4:53am
McNaught, Scott
Modified script
Modified:
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
$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_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
$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_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";
}
Last Run:

															

														
Created:
2026-09-04 20:56:27
2026-09-04 20:56:27
Is Ai Generated:
Is Readonly:
Summary:
This script fills your course booking system with sample (fake) classes for 2027. Nothing existing is deleted or changed — it only adds new things. Here's what it will create:

**1. Three new school terms**
- It adds three new terms to the calendar for 2027 (Term 2, Term 3, and Term 4), each with start and end dates. If a term already exists, it reuses it instead of making a duplicate.

**2. About 50 new courses**
- These are French classes covering every age group: adults (beginner through advanced, plus conversation, exam prep, and travel classes), teenagers (school years 7–12 plus some special classes), children (Prep to Year 3 plus holiday programs), toddlers (play groups, story time), one corporate class, and one intensive adult class.
- Each course gets a start date spread across the 2027 terms, a weekly day and time, a teacher (chosen from a list of 5 existing teachers), and a room (in the CBD, St Kilda, or online).

**3. About 50 new shop items (products)**
- Each course gets a matching product so people can buy it, with a price worked out from the number of teaching hours (about $55 per hour, rounded to the nearest $10).

**4. About 50 new class timetables**
- Each course gets a timetable showing when and where it runs and which teacher takes it.

**5. About 400 individual class sessions**
- Each course gets 8 weekly lessons listed one by one, with dates, times, room, and teacher. The first lesson of each course is labelled as an introduction.

**In short:** running this adds roughly 50 courses, 50 timetables, 50 purchasable products, and 400 individual lessons to your system, all dated in 2027. It does not remove or alter any existing records, and if you run it twice it will reuse the same course codes rather than creating exact duplicates for products, but it may still add extra courses.
This script fills your course booking system with sample (fake) classes for 2027. Nothing existing is deleted or changed — it only adds new things. Here's what it will create:

**1. Three new school terms**
- It adds three new terms to the calendar for 2027 (Term 2, Term 3, and Term 4), each with start and end dates. If a term already exists, it reuses it instead of making a duplicate.

**2. About 50 new courses**
- These are French classes covering every age group: adults (beginner through advanced, plus conversation, exam prep, and travel classes), teenagers (school years 7–12 plus some special classes), children (Prep to Year 3 plus holiday programs), toddlers (play groups, story time), one corporate class, and one intensive adult class.
- Each course gets a start date spread across the 2027 terms, a weekly day and time, a teacher (chosen from a list of 5 existing teachers), and a room (in the CBD, St Kilda, or online).

**3. About 50 new shop items (products)**
- Each course gets a matching product so people can buy it, with a price worked out from the number of teaching hours (about $55 per hour, rounded to the nearest $10).

**4. About 50 new class timetables**
- Each course gets a timetable showing when and where it runs and which teacher takes it.

**5. About 400 individual class sessions**
- Each course gets 8 weekly lessons listed one by one, with dates, times, room, and teacher. The first lesson of each course is labelled as an introduction.

**In short:** running this adds roughly 50 courses, 50 timetables, 50 purchasable products, and 400 individual lessons to your system, all dated in 2027. It does not remove or alter any existing records, and if you run it twice it will reuse the same course codes rather than creating exact duplicates for products, but it may still add extra courses.
Contact Id:
-2
-2