init backend presensi

This commit is contained in:
mwpn
2026-03-05 14:37:36 +07:00
commit b4fda6b9c9
319 changed files with 27261 additions and 0 deletions

View File

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateDevicesTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'device_code' => [
'type' => 'VARCHAR',
'constraint' => '100',
'unique' => true,
],
'device_name' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'api_key' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
],
'last_seen_at' => [
'type' => 'DATETIME',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('devices');
}
public function down()
{
$this->forge->dropTable('devices');
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateClassesTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '100',
],
'grade' => [
'type' => 'VARCHAR',
'constraint' => '50',
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('classes');
}
public function down()
{
$this->forge->dropTable('classes');
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateStudentsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'nis' => [
'type' => 'VARCHAR',
'constraint' => '50',
'unique' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('class_id');
$this->forge->addForeignKey('class_id', 'classes', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('students');
}
public function down()
{
$this->forge->dropTable('students');
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateSubjectsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('subjects');
}
public function down()
{
$this->forge->dropTable('subjects');
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateSchedulesTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'subject_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'teacher_name' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'day_of_week' => [
'type' => 'TINYINT',
'constraint' => 1,
'comment' => '1=Monday, 7=Sunday',
],
'start_time' => [
'type' => 'TIME',
],
'end_time' => [
'type' => 'TIME',
],
'room' => [
'type' => 'VARCHAR',
'constraint' => '100',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['class_id', 'day_of_week']);
$this->forge->addForeignKey('class_id', 'classes', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('subject_id', 'subjects', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('schedules');
}
public function down()
{
$this->forge->dropTable('schedules');
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateZonesTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'zone_code' => [
'type' => 'VARCHAR',
'constraint' => '100',
'unique' => true,
],
'zone_name' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'latitude' => [
'type' => 'DECIMAL',
'constraint' => '10,8',
],
'longitude' => [
'type' => 'DECIMAL',
'constraint' => '11,8',
],
'radius_meters' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('zones');
}
public function down()
{
$this->forge->dropTable('zones');
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateAttendanceSessionsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'student_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'schedule_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'device_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'checkin_at' => [
'type' => 'DATETIME',
],
'latitude' => [
'type' => 'DECIMAL',
'constraint' => '10,8',
],
'longitude' => [
'type' => 'DECIMAL',
'constraint' => '11,8',
],
'confidence' => [
'type' => 'DECIMAL',
'constraint' => '5,2',
'null' => true,
],
'status' => [
'type' => 'ENUM',
'constraint' => ['PRESENT', 'LATE', 'OUTSIDE_ZONE', 'NO_SCHEDULE', 'INVALID_DEVICE'],
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['student_id', 'checkin_at']);
$this->forge->addKey('schedule_id');
$this->forge->addKey('device_id');
$this->forge->addForeignKey('student_id', 'students', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('schedule_id', 'schedules', 'id', 'SET NULL', 'CASCADE');
$this->forge->addForeignKey('device_id', 'devices', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('attendance_sessions');
}
public function down()
{
$this->forge->dropTable('attendance_sessions');
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateParentsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'phone_number' => [
'type' => 'VARCHAR',
'constraint' => '20',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('parents');
}
public function down()
{
$this->forge->dropTable('parents');
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateTelegramAccountsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'telegram_user_id' => [
'type' => 'BIGINT',
'constraint' => 20,
'unsigned' => true,
'unique' => true,
],
'username' => [
'type' => 'VARCHAR',
'constraint' => '255',
'null' => true,
],
'first_name' => [
'type' => 'VARCHAR',
'constraint' => '255',
'null' => true,
],
'last_name' => [
'type' => 'VARCHAR',
'constraint' => '255',
'null' => true,
],
'is_verified' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('telegram_accounts');
}
public function down()
{
$this->forge->dropTable('telegram_accounts');
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateStudentParentsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'student_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'parent_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'relationship' => [
'type' => 'ENUM',
'constraint' => ['AYAH', 'IBU', 'WALI'],
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['student_id', 'parent_id']);
$this->forge->addForeignKey('student_id', 'students', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('parent_id', 'parents', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('student_parents');
}
public function down()
{
$this->forge->dropTable('student_parents');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddParentLinkCodeToStudentsTable extends Migration
{
public function up()
{
$fields = [
'parent_link_code' => [
'type' => 'VARCHAR',
'constraint' => '16',
'null' => true,
'unique' => true,
'after' => 'class_id',
],
];
$this->forge->addColumn('students', $fields);
}
public function down()
{
$this->forge->dropColumn('students', 'parent_link_code');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddParentIdToTelegramAccountsTable extends Migration
{
public function up()
{
$this->forge->addColumn('telegram_accounts', [
'parent_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'is_verified',
],
]);
$this->forge->addForeignKey('parent_id', 'parents', 'id', 'CASCADE', 'SET NULL', 'telegram_accounts_parent_id_fk');
}
public function down()
{
$this->forge->dropForeignKey('telegram_accounts', 'telegram_accounts_parent_id_fk');
$this->forge->dropColumn('telegram_accounts', 'parent_id');
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateUsersTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'email' => [
'type' => 'VARCHAR',
'constraint' => '255',
'unique' => true,
],
'password_hash' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('users');
}
public function down()
{
$this->forge->dropTable('users');
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateRolesTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'role_code' => [
'type' => 'VARCHAR',
'constraint' => '50',
'unique' => true,
],
'role_name' => [
'type' => 'VARCHAR',
'constraint' => '100',
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('roles');
}
public function down()
{
$this->forge->dropTable('roles');
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateUserRolesTable extends Migration
{
public function up()
{
$this->forge->addField([
'user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'role_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
]);
$this->forge->addKey(['user_id', 'role_id'], true);
$this->forge->addForeignKey('user_id', 'users', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('role_id', 'roles', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('user_roles');
}
public function down()
{
$this->forge->dropTable('user_roles');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddUserIdToParentsTable extends Migration
{
public function up()
{
$this->forge->addColumn('parents', [
'user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'id',
],
]);
$this->forge->addForeignKey('user_id', 'users', 'id', 'SET NULL', 'CASCADE', 'parents_user_id_fk');
}
public function down()
{
$this->forge->dropForeignKey('parents', 'parents_user_id_fk');
$this->forge->dropColumn('parents', 'user_id');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddWaliUserIdToClassesTable extends Migration
{
public function up()
{
$this->forge->addColumn('classes', [
'wali_user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'grade',
],
]);
$this->forge->addForeignKey('wali_user_id', 'users', 'id', 'SET NULL', 'CASCADE', 'classes_wali_user_id_fk');
}
public function down()
{
$this->forge->dropForeignKey('classes', 'classes_wali_user_id_fk');
$this->forge->dropColumn('classes', 'wali_user_id');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddTeacherUserIdToSchedulesTable extends Migration
{
public function up()
{
$this->forge->addColumn('schedules', [
'teacher_user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'teacher_name',
],
]);
$this->forge->addForeignKey('teacher_user_id', 'users', 'id', 'SET NULL', 'CASCADE', 'schedules_teacher_user_id_fk');
}
public function down()
{
$this->forge->dropForeignKey('schedules', 'schedules_teacher_user_id_fk');
$this->forge->dropColumn('schedules', 'teacher_user_id');
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddAttendanceDateAndUniqueToAttendanceSessionsTable extends Migration
{
public function up()
{
$this->forge->addColumn('attendance_sessions', [
'attendance_date' => [
'type' => 'DATE',
'null' => true,
'after' => 'schedule_id',
],
]);
$this->db->query('UPDATE attendance_sessions SET attendance_date = DATE(checkin_at) WHERE attendance_date IS NULL');
$this->forge->modifyColumn('attendance_sessions', [
'attendance_date' => [
'type' => 'DATE',
'null' => false,
'after' => 'schedule_id',
],
]);
$prefix = $this->db->DBPrefix;
$table = $this->db->escapeIdentifiers($prefix . 'attendance_sessions');
$this->db->query('ALTER TABLE ' . $table . ' ADD UNIQUE KEY unique_student_schedule_date (student_id, schedule_id, attendance_date)');
}
public function down()
{
$prefix = $this->db->DBPrefix;
$table = $this->db->escapeIdentifiers($prefix . 'attendance_sessions');
$this->db->query('ALTER TABLE ' . $table . ' DROP KEY unique_student_schedule_date');
$this->forge->dropColumn('attendance_sessions', 'attendance_date');
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateLessonSlotsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'slot_number' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'start_time' => [
'type' => 'TIME',
],
'end_time' => [
'type' => 'TIME',
],
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('slot_number');
$this->forge->createTable('lesson_slots');
}
public function down()
{
$this->forge->dropTable('lesson_slots');
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddLessonSlotIdAndUniqueToSchedulesTable extends Migration
{
public function up()
{
$this->forge->addColumn('schedules', [
'lesson_slot_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'teacher_user_id',
],
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
'after' => 'room',
],
]);
$this->forge->addForeignKey('lesson_slot_id', 'lesson_slots', 'id', 'SET NULL', 'CASCADE', 'schedules_lesson_slot_id_fk');
$this->forge->addUniqueKey(['class_id', 'lesson_slot_id', 'day_of_week'], 'schedules_class_slot_day_unique');
}
public function down()
{
$this->forge->dropForeignKey('schedules', 'schedules_lesson_slot_id_fk');
$this->forge->dropKey('schedules', 'schedules_class_slot_day_unique');
$this->forge->dropColumn('schedules', 'lesson_slot_id');
$this->forge->dropColumn('schedules', 'is_active');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddMajorToClassesTable extends Migration
{
public function up()
{
$this->forge->addColumn('classes', [
'major' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => false,
'default' => 'IPA',
'after' => 'grade',
],
]);
}
public function down()
{
$this->forge->dropColumn('classes', 'major');
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddCodeToSubjectsTable extends Migration
{
public function up()
{
$this->forge->addColumn('subjects', [
'code' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => true,
'after' => 'name',
],
]);
}
public function down()
{
$this->forge->dropColumn('subjects', 'code');
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddStudentFieldsForManagement extends Migration
{
public function up()
{
// Rename nis -> nisn (Dapodik compatible; unique index follows column rename)
$this->db->query('ALTER TABLE ' . $this->db->prefixTable('students') . ' CHANGE nis nisn VARCHAR(50) NOT NULL');
// Add gender (L/P)
$this->forge->addColumn('students', [
'gender' => [
'type' => 'VARCHAR',
'constraint' => 1,
'null' => true,
'after' => 'name',
],
]);
// Add is_active (default 1)
$this->forge->addColumn('students', [
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
'after' => 'class_id',
],
]);
// Make class_id nullable (for unmapped / Dapodik sync)
$this->forge->dropForeignKey('students', 'students_class_id_foreign');
$this->forge->modifyColumn('students', [
'class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
]);
$this->forge->addForeignKey('class_id', 'classes', 'id', 'SET NULL', 'CASCADE', 'students_class_id_foreign');
}
public function down()
{
$this->forge->dropForeignKey('students', 'students_class_id_foreign');
$this->forge->modifyColumn('students', [
'class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => false,
],
]);
$this->forge->addForeignKey('class_id', 'classes', 'id', 'CASCADE', 'CASCADE', 'students_class_id_foreign');
$this->forge->dropColumn('students', 'is_active');
$this->forge->dropColumn('students', 'gender');
$this->db->query('ALTER TABLE ' . $this->db->prefixTable('students') . ' CHANGE nisn nis VARCHAR(50) NOT NULL');
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateDapodikRombelMappingsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'dapodik_rombel' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'last_seen_at' => [
'type' => 'DATETIME',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('dapodik_rombel');
$this->forge->addForeignKey('class_id', 'classes', 'id', 'SET NULL', 'CASCADE', 'dapodik_rombel_mappings_class_id_fk');
$this->forge->createTable('dapodik_rombel_mappings');
}
public function down()
{
$this->forge->dropTable('dapodik_rombel_mappings');
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateDapodikSyncJobsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'BIGINT',
'constraint' => 20,
'unsigned' => true,
'auto_increment' => true,
],
'type' => [
'type' => 'VARCHAR',
'constraint' => 50,
'comment' => 'students|classes',
],
'total_rows' => [
'type' => 'INT',
'constraint' => 11,
'default' => 0,
],
'processed_rows' => [
'type' => 'INT',
'constraint' => 11,
'default' => 0,
],
'status' => [
'type' => 'VARCHAR',
'constraint' => 20,
'default' => 'pending',
'comment' => 'pending|running|completed|failed',
],
'message' => [
'type' => 'TEXT',
'null' => true,
],
'started_at' => [
'type' => 'DATETIME',
'null' => true,
],
'finished_at' => [
'type' => 'DATETIME',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('status');
$this->forge->createTable('dapodik_sync_jobs');
}
public function down()
{
$this->forge->dropTable('dapodik_sync_jobs');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddDapodikIdToStudentsTable extends Migration
{
public function up()
{
// Add dapodik_id column (nullable, unique) to students
$this->forge->addColumn('students', [
'dapodik_id' => [
'type' => 'VARCHAR',
'constraint' => 64,
'null' => true,
'after' => 'id',
],
]);
// Add unique index on dapodik_id (for upsert)
$this->db->query('CREATE UNIQUE INDEX students_dapodik_id_unique ON ' . $this->db->prefixTable('students') . ' (dapodik_id)');
}
public function down()
{
// Drop unique index and column
$this->db->query('DROP INDEX students_dapodik_id_unique ON ' . $this->db->prefixTable('students'));
$this->forge->dropColumn('students', 'dapodik_id');
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateTeacherSubjectsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'teacher_user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'subject_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['teacher_user_id', 'subject_id'], 'teacher_subjects_unique');
$this->forge->addForeignKey('teacher_user_id', 'users', 'id', 'CASCADE', 'CASCADE', 'teacher_subjects_teacher_fk');
$this->forge->addForeignKey('subject_id', 'subjects', 'id', 'CASCADE', 'CASCADE', 'teacher_subjects_subject_fk');
$this->forge->createTable('teacher_subjects');
}
public function down()
{
$this->forge->dropForeignKey('teacher_subjects', 'teacher_subjects_teacher_fk');
$this->forge->dropForeignKey('teacher_subjects', 'teacher_subjects_subject_fk');
$this->forge->dropTable('teacher_subjects');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddFaceExternalIdToStudentsTable extends Migration
{
public function up()
{
// Add nullable face_external_id column for linking to external face-recognition ID
$this->forge->addColumn('students', [
'face_external_id' => [
'type' => 'VARCHAR',
'constraint' => 100,
'null' => true,
'after' => 'dapodik_id',
],
]);
// Unique index so one face ID hanya milik satu siswa
$this->forge->addUniqueKey('face_external_id', 'students_face_external_id_unique');
}
public function down()
{
$this->forge->dropKey('students', 'students_face_external_id_unique');
$this->forge->dropColumn('students', 'face_external_id');
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateStudentMobileAccountsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'student_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'pin_hash' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('student_id');
$this->forge->addForeignKey('student_id', 'students', 'id', 'CASCADE', 'CASCADE', 'student_mobile_accounts_student_fk');
$this->forge->createTable('student_mobile_accounts');
}
public function down()
{
$this->forge->dropTable('student_mobile_accounts');
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateViolationCategoriesTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'code' => [
'type' => 'VARCHAR',
'constraint' => 10,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'description' => [
'type' => 'TEXT',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('code', 'violation_categories_code_unique');
$this->forge->createTable('violation_categories');
}
public function down()
{
$this->forge->dropTable('violation_categories');
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateViolationsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'category_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'code' => [
'type' => 'VARCHAR',
'constraint' => 20,
'null' => true,
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'description' => [
'type' => 'TEXT',
'null' => true,
],
'score' => [
'type' => 'INT',
'constraint' => 11,
'default' => 0,
],
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addForeignKey('category_id', 'violation_categories', 'id', 'CASCADE', 'CASCADE', 'violations_category_fk');
$this->forge->createTable('violations');
}
public function down()
{
$this->forge->dropForeignKey('violations', 'violations_category_fk');
$this->forge->dropTable('violations');
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateStudentViolationsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'student_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'violation_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'reported_by_user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'occurred_at' => [
'type' => 'DATETIME',
'null' => true,
],
'notes' => [
'type' => 'TEXT',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('student_id');
$this->forge->addKey('class_id');
$this->forge->addKey('occurred_at');
$this->forge->addForeignKey('student_id', 'students', 'id', 'CASCADE', 'CASCADE', 'student_violations_student_fk');
$this->forge->addForeignKey('class_id', 'classes', 'id', 'SET NULL', 'CASCADE', 'student_violations_class_fk');
$this->forge->addForeignKey('violation_id', 'violations', 'id', 'CASCADE', 'CASCADE', 'student_violations_violation_fk');
$this->forge->addForeignKey('reported_by_user_id', 'users', 'id', 'CASCADE', 'CASCADE', 'student_violations_reporter_fk');
$this->forge->createTable('student_violations');
}
public function down()
{
$this->forge->dropForeignKey('student_violations', 'student_violations_student_fk');
$this->forge->dropForeignKey('student_violations', 'student_violations_class_fk');
$this->forge->dropForeignKey('student_violations', 'student_violations_violation_fk');
$this->forge->dropForeignKey('student_violations', 'student_violations_reporter_fk');
$this->forge->dropTable('student_violations');
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
/**
* Seed awal master pelanggaran berdasarkan tabel tata tertib
* yang kamu kirim (kategori AI + tindak pidana).
*
* Catatan: migration ini hanya menambah data jika belum ada
* (dicek berdasarkan code kategori + title pelanggaran).
*/
class SeedViolationsFromPolicy extends Migration
{
public function up()
{
$db = \Config\Database::connect();
// 1) Pastikan kategori ada
$categories = [
'A' => ['name' => 'KETERLAMBATAN & KEHADIRAN'],
'B' => ['name' => 'PAKAIAN'],
'C' => ['name' => 'KEPRIBADIAN'],
'D' => ['name' => 'KETERTIBAN'],
'E' => ['name' => 'MEROKOK'],
'F' => ['name' => 'PORNOGRAFI'],
'G' => ['name' => 'SENJATA TAJAM'],
'H' => ['name' => 'NAPZA DAN / MINUMAN KERAS'],
'I' => ['name' => 'BERKELAHI / TAWURAN'],
'J' => ['name' => 'TINDAK PIDANA HUKUM'],
];
$catIds = [];
foreach ($categories as $code => $cfg) {
$row = $db->table('violation_categories')->where('code', $code)->get()->getRowArray();
if (! $row) {
$db->table('violation_categories')->insert([
'code' => $code,
'name' => $cfg['name'],
'description' => null,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
$catIds[$code] = (int) $db->insertID();
} else {
$catIds[$code] = (int) $row['id'];
}
}
// Helper untuk insert jika belum ada (berdasarkan title)
$insertViolation = function (string $code, string $title, int $score, ?string $description = null) use ($db, $catIds): void {
if (! isset($catIds[$code])) {
return;
}
$exists = $db->table('violations')
->where('category_id', $catIds[$code])
->where('title', $title)
->countAllResults();
if ($exists > 0) {
return;
}
$db->table('violations')->insert([
'category_id' => $catIds[$code],
'code' => null,
'title' => $title,
'description' => $description,
'score' => $score,
'is_active' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
};
// 2) Seed pelanggaran per kategori (ringkas dari tabel yang kamu kirim)
// A — Keterlambatan & Kehadiran
$insertViolation('A', 'Terlambat (dipulangkan dicatat tidak hadir tanpa keterangan)', 5);
$insertViolation('A', 'Tidak masuk tanpa keterangan', 5);
$insertViolation('A', 'Tidak masuk dengan membuat keterangan palsu', 10);
$insertViolation('A', 'Tidak mengikuti jam pelajaran tertentu', 5);
$insertViolation('A', 'Keluar saat pelajaran berlangsung dan tidak kembali sampai pulang', 5);
$insertViolation('A', 'Tidak ikut upacara bendera', 5);
$insertViolation('A', 'Meninggalkan pelajaran atau sekolah dengan izin palsu', 10);
// B — Pakaian
$insertViolation('B', 'Memakai seragam tidak sesuai aturan (teguran 1)', 2);
$insertViolation('B', 'Memakai seragam tidak sesuai aturan (teguran 2 + surat pernyataan 1)', 2);
$insertViolation('B', 'Memakai seragam tidak sesuai aturan (teguran 3 + sanksi sekolah)', 2);
$insertViolation('B', 'Tidak memakai seragam sekolah sesuai jadwal', 3);
// C — Kepribadian
$insertViolation('C', 'Berhias berlebihan', 5);
$insertViolation('C', 'Siswa putra memakai gelang, kalung, anting', 10);
$insertViolation('C', 'Memakai tindik di wajah atau tubuh lain (siswi)', 10);
$insertViolation('C', 'Memakai tato / atribut tidak wajar bagi pelajar', 20);
$insertViolation('C', 'Berduaan/bermesraan/pacaran (tindak asusila, melanggar kesopanan)', 50);
$insertViolation('C', 'Melawan kepala sekolah/guru/karyawan dengan ancaman', 75);
$insertViolation('C', 'Melawan kepala sekolah/guru/karyawan dengan pemukulan', 100);
$insertViolation('C', 'Intimidasi / bullying dengan kekerasan', 75);
$insertViolation('C', 'Tindakan tidak menyenangkan sesama siswa', 15);
$insertViolation('C', 'Mengaktifkan HP saat KBM berlangsung', 5);
$insertViolation('C', 'Mencemarkan nama kepala sekolah/guru/karyawan/sekolah', 50);
$insertViolation('C', 'Diketahui hamil / menikah', 100);
// D — Ketertiban
$insertViolation('D', 'Mengotori / mencoret benda milik sekolah/guru/karyawan/teman/lingkungan', 15);
$insertViolation('D', 'Merusak atau mengambil barang milik sekolah/guru/karyawan/teman', 50);
$insertViolation('D', 'Membawa/menggunakan benda tak terkait KBM tanpa izin', 15);
$insertViolation('D', 'Memakai sandal/sepatu sandal saat pelajaran tanpa izin', 5);
$insertViolation('D', 'Melompat pagar sekolah untuk keluar/masuk', 20);
$insertViolation('D', 'Tidak membuang sampah pada tempatnya', 3);
$insertViolation('D', 'Merusak tanaman dengan sengaja', 5);
// E — Merokok
$insertViolation('E', 'Membawa rokok ke sekolah', 20);
$insertViolation('E', 'Menghisap rokok di sekolah', 25);
// F — Pornografi
$insertViolation('F', 'Memiliki/membawa/mempergunakan media berkonten pornografi/kekerasan', 65);
$insertViolation('F', 'Memperjualbelikan media berkonten pornografi/kekerasan', 75);
// G — Senjata tajam
$insertViolation('G', 'Membawa senjata tajam/api/bahan peledak tanpa izin', 100);
$insertViolation('G', 'Memperjualbelikan senjata tajam/api/bahan peledak', 100);
$insertViolation('G', 'Menggunakan senjata tajam/api/bahan peledak untuk mengancam/melukai', 100);
// H — NAPZA / minuman keras
$insertViolation('H', 'Menggunakan napza dan/atau minuman terlarang', 100);
$insertViolation('H', 'Membawa napza dan/atau minuman terlarang', 100);
$insertViolation('H', 'Mengedarkan/memperjualbelikan napza/minuman keras', 100);
// I — Berkelahi / tawuran
$insertViolation('I', 'Indikasi / berkelahi / tawuran dengan siswa sekolah lain', 100);
$insertViolation('I', 'Berkelahi antar siswa', 100);
// J — Tindak pidana hukum lain
$insertViolation('J', 'Tersangkut tindak pidana lainnya', 10);
}
public function down()
{
// Tidak perlu menghapus data seed; biarkan tetap ada.
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateDisciplineLevelsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'min_score' => [
'type' => 'INT',
'constraint' => 11,
'default' => 0,
],
'max_score' => [
'type' => 'INT',
'constraint' => 11,
'null' => true, // null = tak terbatas (>= min_score)
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'school_action' => [
'type' => 'TEXT',
'null' => true,
],
'executor' => [
'type' => 'VARCHAR',
'constraint' => 150,
'null' => true,
],
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('discipline_levels');
}
public function down()
{
$this->forge->dropTable('discipline_levels');
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
/**
* Seed awal level disiplin berdasarkan rentang poin dan tindakan sekolah.
*/
class SeedDisciplineLevelsFromPolicy extends Migration
{
public function up()
{
$db = \Config\Database::connect();
$levels = [
[
'min_score' => 1,
'max_score' => 40,
'title' => 'Level 1',
'school_action'=> "Berkomunikasi dengan orang tua/wali siswa.\nTeguran tertulis dan surat perjanjian pertama.",
'executor' => 'Wali kelas',
],
[
'min_score' => 41,
'max_score' => 70,
'title' => 'Level 2',
'school_action'=> "Memberi bimbingan dan perhatian.\nTeguran tertulis dan surat perjanjian kedua.",
'executor' => 'Wali kelas dan BK',
],
[
'min_score' => 71,
'max_score' => 99,
'title' => 'Level 3',
'school_action'=> "Berkomunikasi dengan orang tua/wali siswa.\nMemberi bimbingan dan perhatian.\nSurat perjanjian tertulis bermaterai.\nSkorsing maksimal 3 hari efektif, diketahui Kepala Sekolah.",
'executor' => 'Wali kelas, BK, wakasek kesiswaan, diketahui Kepala Sekolah',
],
[
'min_score' => 100,
'max_score' => null,
'title' => 'Level 4',
'school_action'=> "Berkomunikasi dengan orang tua/wali siswa.\nMemberi bimbingan dan perhatian.\nDikembalikan kepada orang tua secara sepihak.",
'executor' => 'Pleno guru',
],
];
foreach ($levels as $lvl) {
// Cegah duplikat berdasarkan min_score & max_score
$exists = $db->table('discipline_levels')
->where('min_score', $lvl['min_score'])
->where('max_score', $lvl['max_score'])
->countAllResults();
if ($exists > 0) {
continue;
}
$db->table('discipline_levels')->insert([
'min_score' => $lvl['min_score'],
'max_score' => $lvl['max_score'],
'title' => $lvl['title'],
'school_action'=> $lvl['school_action'],
'executor' => $lvl['executor'],
'is_active' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
}
public function down()
{
// Tidak menghapus data; biarkan tetap ada.
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddGeoFenceToDevicesTable extends Migration
{
public function up()
{
$fields = [
'latitude' => [
'type' => 'DECIMAL',
'constraint' => '10,8',
'null' => true,
'after' => 'last_seen_at',
],
'longitude' => [
'type' => 'DECIMAL',
'constraint' => '11,8',
'null' => true,
'after' => 'latitude',
],
'radius_meters' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
'after' => 'longitude',
],
];
$this->forge->addColumn('devices', $fields);
}
public function down()
{
$this->forge->dropColumn('devices', ['latitude', 'longitude', 'radius_meters']);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
/**
* Satu set pengaturan presensi terpusat: jam masuk & jam pulang sekolah.
* Koordinat sekolah tetap di table zones (SMA1-SCHOOL).
*/
class CreateSchoolPresenceSettingsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'time_masuk_start' => [
'type' => 'TIME',
'null' => true,
'comment' => 'Mulai window absen masuk (misal 06:30)',
],
'time_masuk_end' => [
'type' => 'TIME',
'null' => true,
'comment' => 'Akhir window absen masuk (misal 07:00)',
],
'time_pulang_start' => [
'type' => 'TIME',
'null' => true,
'comment' => 'Mulai window absen pulang (misal 14:00)',
],
'time_pulang_end' => [
'type' => 'TIME',
'null' => true,
'comment' => 'Akhir window absen pulang (misal 14:30)',
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('school_presence_settings');
// Satu row default
$this->db->table('school_presence_settings')->insert([
'time_masuk_start' => '06:30:00',
'time_masuk_end' => '07:00:00',
'time_pulang_start' => '14:00:00',
'time_pulang_end' => '14:30:00',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
public function down()
{
$this->forge->dropTable('school_presence_settings');
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
/**
* Token untuk absen mapel via QR: guru generate, siswa scan.
*/
class CreateQrAttendanceTokensTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'schedule_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'token' => [
'type' => 'VARCHAR',
'constraint' => 64,
],
'expires_at' => [
'type' => 'DATETIME',
],
'created_by_user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('token');
$this->forge->addKey('expires_at');
$this->forge->addKey('schedule_id');
$this->forge->addForeignKey('schedule_id', 'schedules', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('qr_attendance_tokens');
}
public function down()
{
$this->forge->dropTable('qr_attendance_tokens');
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
/**
* Menambah checkin_type untuk membedakan absen mapel vs masuk/pulang.
* mapel = absen per jadwal (default); masuk = absen masuk sekolah; pulang = absen pulang.
*/
class AddCheckinTypeToAttendanceSessions extends Migration
{
public function up()
{
$this->forge->addColumn('attendance_sessions', [
'checkin_type' => [
'type' => 'ENUM',
'constraint' => ['mapel', 'masuk', 'pulang'],
'default' => 'mapel',
'null' => false,
'after' => 'schedule_id',
],
]);
}
public function down()
{
$this->forge->dropColumn('attendance_sessions', 'checkin_type');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddFaceHashToStudentsTable extends Migration
{
public function up()
{
$this->forge->addColumn('students', [
'face_hash' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
'after' => 'face_external_id',
'comment' => 'MD5 hash dari file foto wajah referensi',
],
]);
}
public function down()
{
$this->forge->dropColumn('students', 'face_hash');
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateStudentFacesTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'student_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'embedding' => [
'type' => 'LONGTEXT',
'null' => false,
'comment' => 'JSON string float[] embedding wajah',
],
'source' => [
'type' => 'ENUM',
'constraint' => ['formal', 'live', 'live_avg'],
'default' => 'formal',
],
'quality_score' => [
'type' => 'FLOAT',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('student_id');
$this->forge->addForeignKey('student_id', 'students', 'id', 'CASCADE', 'CASCADE', 'student_faces_student_fk');
$this->forge->createTable('student_faces');
}
public function down()
{
$this->forge->dropTable('student_faces');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddPhotoFormalUrlToStudentsTable extends Migration
{
public function up()
{
$this->forge->addColumn('students', [
'photo_formal_url' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
'after' => 'face_hash',
'comment' => 'URL/path foto formal administrasi (mis. dari Drive)',
],
]);
}
public function down()
{
$this->forge->dropColumn('students', 'photo_formal_url');
}
}

View File

View File

@@ -0,0 +1,84 @@
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class AcademicSeeder extends Seeder
{
public function run()
{
$now = date('Y-m-d H:i:s');
$todayDayOfWeek = (int) date('N'); // 1=Monday, 7=Sunday
// Insert 1 class
$classData = [
[
'name' => 'X IPA 1',
'grade' => '10',
'created_at' => $now,
'updated_at' => $now,
],
];
$this->db->table('classes')->insertBatch($classData);
$classId = $this->db->insertID();
// Insert 2 students
$studentData = [
[
'nisn' => '2024001',
'name' => 'Student One',
'gender' => 'L',
'class_id' => $classId,
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'nisn' => '2024002',
'name' => 'Student Two',
'gender' => 'P',
'class_id' => $classId,
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
];
$this->db->table('students')->insertBatch($studentData);
// Insert 2 subjects
$subjectData = [
[
'name' => 'Mathematics',
'created_at' => $now,
'updated_at' => $now,
],
[
'name' => 'Physics',
'created_at' => $now,
'updated_at' => $now,
],
];
$this->db->table('subjects')->insertBatch($subjectData);
$subjectIds = $this->db->query('SELECT id FROM subjects ORDER BY id')->getResultArray();
$mathSubjectId = $subjectIds[0]['id'];
$physicsSubjectId = $subjectIds[1]['id'];
// Insert 1 schedule for today's weekday
// Schedule: 08:00 - 09:30 for Mathematics
$scheduleData = [
[
'class_id' => $classId,
'subject_id' => $mathSubjectId,
'teacher_name' => 'Mr. Mathematics Teacher',
'day_of_week' => $todayDayOfWeek,
'start_time' => '08:00:00',
'end_time' => '09:30:00',
'room' => 'A101',
'created_at' => $now,
'updated_at' => $now,
],
];
$this->db->table('schedules')->insertBatch($scheduleData);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class AuthSeeder extends Seeder
{
public function run()
{
$roles = [
['role_code' => 'ADMIN', 'role_name' => 'Administrator'],
['role_code' => 'WALI_KELAS', 'role_name' => 'Wali Kelas'],
['role_code' => 'GURU_BK', 'role_name' => 'Guru BK'],
['role_code' => 'GURU_MAPEL', 'role_name' => 'Guru Mata Pelajaran'],
['role_code' => 'ORANG_TUA', 'role_name' => 'Orang Tua'],
];
$this->db->table('roles')->insertBatch($roles);
$this->db->table('users')->insert([
'name' => 'Admin',
'email' => 'admin@example.com',
'password_hash' => password_hash('admin123', PASSWORD_DEFAULT),
'is_active' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
$adminId = $this->db->insertID();
$adminRoleId = $this->db->table('roles')->where('role_code', 'ADMIN')->get()->getRow()->id;
$this->db->table('user_roles')->insert([
'user_id' => $adminId,
'role_id' => $adminRoleId,
]);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class DeviceSeeder extends Seeder
{
public function run()
{
$data = [
[
'device_code' => 'SMA1-GATE-01',
'device_name' => 'SMAN1 Gate Device 01',
'api_key' => 'devkey123',
'is_active' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
],
[
'device_code' => 'MOBILE_APP',
'device_name' => 'Aplikasi Mobile (Presensi Siswa)',
'api_key' => 'MOBILE_APP_SECRET',
'is_active' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
],
];
$table = $this->db->table('devices');
foreach ($data as $row) {
$exists = $table->where('device_code', $row['device_code'])->countAllResults();
if ($exists === 0) {
$table->insert($row);
}
}
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class GeoSeeder extends Seeder
{
public function run()
{
$now = date('Y-m-d H:i:s');
// Insert SMA1-SCHOOL zone
// Using dummy coordinates (example: Bandung area)
// Latitude: -6.917464, Longitude: 107.619123
$zoneData = [
[
'zone_code' => 'SMA1-SCHOOL',
'zone_name' => 'SMAN 1 School Zone',
'latitude' => -6.917464,
'longitude' => 107.619123,
'radius_meters' => 150,
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
];
$this->db->table('zones')->insertBatch($zoneData);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class StudentMobileAccountsSeeder extends Seeder
{
public function run()
{
//
}
}