Showing posts with label SiM. Show all posts
Showing posts with label SiM. Show all posts

Thursday, 6 August 2026

Perintah untuk cek jumlah record inputan hari ini

 SELECT 'Jurnal Mengajar' AS jenis, COUNT(*) AS jumlah

FROM mdl_local_jurnalmengajar

WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


UNION ALL


SELECT 'Pembinaan Mapel', COUNT(*)

FROM mdl_local_jurnalmengajar_pembinaanmapel

WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


UNION ALL


SELECT 'Pembinaan BK', COUNT(*)

FROM mdl_local_jurnalpembinaan

WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


UNION ALL


SELECT 'Layanan BK', COUNT(*)

FROM mdl_local_jurnallayananbk

WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


UNION ALL


SELECT 'Guru Wali', COUNT(*)

FROM mdl_local_jurnalmengajar_guruwali

WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


UNION ALL


SELECT 'Surat Izin Murid', COUNT(*)

FROM mdl_local_jurnalmengajar_suratizin

WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


UNION ALL


SELECT 'Kehadiran Guru', COUNT(*)

FROM mdl_local_jurnalmengajar_kehadiran

WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


UNION ALL


SELECT 'TOTAL', SUM(jumlah)

FROM (

    SELECT COUNT(*) AS jumlah

    FROM mdl_local_jurnalmengajar

    WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


    UNION ALL

    SELECT COUNT(*)

    FROM mdl_local_jurnalmengajar_pembinaanmapel

    WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


    UNION ALL

    SELECT COUNT(*)

    FROM mdl_local_jurnalpembinaan

    WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


    UNION ALL

    SELECT COUNT(*)

    FROM mdl_local_jurnallayananbk

    WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


    UNION ALL

    SELECT COUNT(*)

    FROM mdl_local_jurnalmengajar_guruwali

    WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


    UNION ALL

    SELECT COUNT(*)

    FROM mdl_local_jurnalmengajar_suratizin

    WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()


    UNION ALL

    SELECT COUNT(*)

    FROM mdl_local_jurnalmengajar_kehadiran

    WHERE DATE(FROM_UNIXTIME(timecreated)) = CURDATE()

) x;

Sunday, 12 July 2026

Snapshot riwayat kelas, kelulusan dan murid pindah

 

Menurut saya alur akhirnya menjadi seperti ini:

  • 📸 Snapshot Riwayat Kelas → hanya sekali setiap awal tahun.
  • Sinkron Murid Baru → jika ada siswa masuk setelah tahun ajaran berjalan.
  • 🎓 Kelulusan Massal → akhir tahun untuk kelas XII.

Dengan tiga proses itu, hampir seluruh riwayat akademik akan tercatat otomatis tanpa perlu mengedit satu per sat

Wednesday, 8 July 2026

tahapan migrasi guruwali

 

Menurut saya, keputusan yang paling tepat untuk plugin Anda adalah berhenti menggunakan binaan.csv dan menggantinya dengan satu tabel master Guru Wali.

Saya menyarankan struktur berikut:

CREATE TABLE mdl_local_jurnalmengajar_guruwali (
    id BIGINT(10) NOT NULL AUTO_INCREMENT,
    guruid BIGINT(10) NOT NULL,
    muridid BIGINT(10) NOT NULL,
    timecreated BIGINT(10) NOT NULL,
    timemodified BIGINT(10) NOT NULL,

    PRIMARY KEY (id),

    UNIQUE KEY uniq_murid (muridid),

    KEY idx_guru (guruid),
    KEY idx_murid (muridid)
);

Kenapa saya mantap memilih desain ini?

Karena sekarang saya sudah melihat keseluruhan plugin Anda.

Guru

Semua memakai

mdl_user.id

Murid

Semua modul juga memakai

muridid = mdl_user.id

Misalnya:

  • ✅ Jurnal Guru Wali

  • ✅ Riwayat Individu

  • ✅ Pembinaan BK

  • ✅ Layanan BK

  • ✅ Pembinaan Guru Mapel

  • ✅ Rekap Kehadiran

Semuanya sudah menggunakan muridid.

Jadi Guru Wali tinggal mengikuti arsitektur yang sudah ada, bukan membuat konsep baru.


Tahapan migrasi yang saya sarankan

Tahap 1

Tambahkan tabel SQL.

Belum mengubah halaman apa pun.


Tahap 2

Buat script migrasi sekali saja.

binaan.csv
        ↓
cari userid murid dari NIS
        ↓
INSERT

Setelah selesai tabel akan berisi:

guruidmuridid
1172821
1172845
34902

Tahap 3

Ubah halaman satu per satu.

Urutannya saya sarankan:

  1. guruwali_manage.php

  2. guruwali_add.php

  3. guruwali_delete.php

  4. guruwali_view.php

  5. jurnalguruwali.php

  6. rekap_kehadiran_muridwali.php

  7. lib_notifikasi.php

Semuanya cukup membaca tabel baru.


Tahap 4

Hapus

binaan.csv

beserta semua fungsi:

jw_load_binaan_csv()
jw_find_user_by_nis()
jw_get_murid_options_from_csv()

Karena sudah tidak diperlukan lagi.


Keuntungan jangka panjang

Nanti saat awal tahun ajaran:

  1. Import User

  2. Import Cohort

  3. Guru Wali tetap

Tidak ada lagi edit CSV.

Tidak ada lagi masalah:

  • XII masih muncul

  • kelas lama masih tampil

  • nama murid tidak berubah

Karena setiap halaman tinggal melakukan:

Guru
    ↓
local_jurnalmengajar_guruwali
    ↓
muridid
    ↓
mdl_user
    ↓
cohort aktif

Sehingga nama dan kelas selalu mengikuti kondisi terbaru di Moodle.


Menurut saya, ini adalah waktu yang tepat

Karena Anda memang sedang mengerjakan proses import cohort tahunan, saya akan menganggap ini sebagai versi 2.0 dari fitur Guru Wali.

Perubahan yang dilakukan hanya pada master data Guru Wali. Seluruh tabel jurnal dan riwayat yang sudah ada tetap dipertahankan, sehingga risiko terhadap data lama sangat kecil, sementara manfaatnya besar untuk pemeliharaan plugin di tahun-tahun berikutnya.

fitur guru wali sarankan

 

Saya sudah meninjau struktur fitur Guru Wali pada plugin local_jurnalmengajar. Dari file yang ada, fiturnya sudah cukup lengkap dan membentuk satu alur kerja yang jelas.

Alur fitur Guru Wali saat ini

Import CSV binaan
        │
        ▼
binaan.csv (dataroot)
        │
        ├── guruwali_manage.php
        ├── guruwali_view.php
        ├── jurnalguruwali.php
        ├── rekap_kehadiran_muridwali.php
        └── lib_notifikasi.php

Artinya seluruh fitur Guru Wali bergantung pada satu file binaan.csv, bukan tabel database.


1. Manajemen Murid Binaan

Administrator dapat:

  • Import CSV

  • Download format CSV

  • Tambah murid binaan

  • Update murid binaan

  • Hapus murid binaan

  • Melihat daftar murid binaan setiap guru

File:

  • import_binaan.php

  • download_format_binaan.php

  • guruwali_add.php

  • guruwali_manage.php

  • guruwali_delete.php

  • guruwali_view.php


2. Input Jurnal Guru Wali

Guru wali dapat:

✔ memilih beberapa murid sekaligus

✔ mengisi

  • Topik

  • Tindak lanjut

  • Keterangan

kemudian otomatis dibuat satu record untuk setiap murid.

Misalnya memilih:

  • Ahmad

  • Budi

  • Cici

maka langsung menjadi tiga record jurnal.

Ini sudah cukup efisien.


3. Edit Jurnal

Sudah tersedia.

Guru dapat mengubah:

  • topik

  • tindak lanjut

  • keterangan

tanpa membuat data baru.


4. Riwayat

Di bawah form terdapat:

Riwayat 10 jurnal terakhir.

Bisa diedit kembali.


5. Rekap Kehadiran Murid Binaan

Halaman:

rekap_kehadiran_muridwali.php

Fitur:

  • filter tanggal

  • mode per hari

  • mode per jam

  • rekap

Hadir

Sakit

Ijin

Alpa

Dispensasi

dan bisa membuka detail per murid.


6. Notifikasi WA

Sesudah jurnal disimpan:

Guru wali cukup menginput sekali.

Plugin akan:

  • mengelompokkan berdasarkan kelas

  • membuat pesan

  • mengirim ke tujuan sesuai setting

menggunakan template

template_guru_wali

yang placeholdernya sudah lengkap.


7. Integrasi dengan Template

Sudah tersedia template yang bisa diubah admin.

Placeholder:

  • waktu

  • murid

  • kelas

  • topik

  • tindak lanjut

  • keterangan

  • guru wali

Semua sudah dipakai ketika mengirim WA.


Yang menurut saya masih kurang

Kalau melihat keseluruhan plugin, fitur Guru Wali sebenarnya sudah sekitar 75–80% lengkap. Yang masih bisa dikembangkan agar menjadi lebih berguna bagi wali kelas dan BK adalah:

1. Dashboard Murid Binaan ⭐⭐⭐⭐⭐

Saat guru membuka menu Guru Wali, langsung tampil:

Jumlah murid binaan : 18

Jurnal bulan ini : 24

Belum pernah dibina : 5

Sudah dibina : 13

Tidak perlu membuka rekap dahulu.


2. Riwayat per Murid ⭐⭐⭐⭐⭐

Klik nama murid

Muncul seluruh histori

10 Januari
Topik ...

20 Januari
Topik ...

15 Februari
Topik ...

sehingga perkembangan siswa mudah dipantau.


3. Grafik Intensitas Pembinaan ⭐⭐⭐⭐☆

Misalnya

Ahmad
█████████ 9

Budi
██ 2

Cici
██████ 6

langsung terlihat siapa yang sering dibina.


4. Pencarian Murid ⭐⭐⭐⭐☆

Saat murid binaan sudah banyak (30–40 orang),

lebih nyaman jika ada kotak pencarian nama.


5. Statistik per Topik ⭐⭐⭐⭐☆

Misalnya

Disiplin        14

Terlambat       9

Motivasi        6

Bullying        1

berguna untuk evaluasi.


6. Target Pembinaan ⭐⭐⭐⭐⭐

Misalnya sekolah menentukan

setiap murid minimal mendapat pembinaan 1 kali per semester.

Plugin dapat menampilkan:

Sudah dibina
16

Belum dibina
4

Ini akan sangat membantu monitoring.


7. Integrasi dengan Pembinaan BK ⭐⭐⭐⭐⭐

Saat membuka profil murid:

Riwayat Guru Wali

Riwayat BK

Riwayat Pembinaan Guru Mapel

Riwayat Surat Izin

Semua muncul pada satu halaman sehingga menjadi semacam profil pembinaan siswa yang terintegrasi.


Prioritas pengembangan yang saya sarankan

Jika kita mengembangkan fitur ini secara bertahap, saya menyarankan urutannya:

  1. Riwayat lengkap per murid (paling bermanfaat dan relatif mudah).

  2. Dashboard Guru Wali dengan statistik ringkas.

  3. Monitoring target pembinaan per semester.

  4. Integrasi seluruh riwayat pembinaan (Guru Wali, BK, Guru Mapel, Surat Izin) menjadi satu profil siswa.

Menurut saya, empat pengembangan tersebut akan membuat modul Guru Wali terasa jauh lebih matang dan menjadi pusat pemantauan perkembangan siswa, bukan sekadar tempat mencatat jurnal.

Friday, 3 July 2026

convert ubah hasil fet ke format csv siap import

 Mantap, Guru Duan! 🎉

php tools/fet2csv.php "/home/dhwan/FET SMA2 CONTOH/2026-2027/HASIL/jadwal-sman2kandangan-26juni-0910-6/jadwal-sman2kandangan-26juni-0910_activities_time_horizontal.html"

Menurut saya kita sudah mencapai target awal dengan baik.

Kalau nanti sumbernya pindah folder?

Tidak masalah.

****************************************
Misalnya hasil FET tahun depan ada di:

/home/dhwan/JADWAL/2027/HASIL/

cukup ganti path-nya.


php tools/fet2csv.php \
"/home/dhwan/JADWAL/2027/HASIL/jadwal_2027_activities_time_horizontal.html"

Script tidak perlu diubah sama sekali.

*****************************************

## Yang sudah berhasil dibuat

✅ Membaca file hasil FET:

```text
activities_time_horizontal.html
```

✅ Parsing seluruh aktivitas.

✅ Mengambil:

* Guru
* Hari
* Kelas
* Mata Pelajaran
* Jam

✅ Mengelompokkan aktivitas menjadi satu record per sesi.

✅ Mengekspor ke CSV UTF-8 (dengan BOM) sehingga langsung terbuka rapi di LibreOffice Calc maupun Excel.

Format akhirnya:

```csv
guru,userid,hari,kelas,jamke,mapel
Dwiyani Lestari M.Pd,,Senin,XI-B,"2,3","Bahasa Indonesia"
```

---

## Alur kerja nantinya menjadi sederhana

```text
FET Generate
      │
      ▼
activities_time_horizontal.html
      │
      ▼
php tools/fet2csv.php activities_time_horizontal.html
      │
      ▼
jadwal.csv
      │
      ▼
LibreOffice Calc
      │
      ├── isi userid (VLOOKUP/XLOOKUP)
      ▼
Import ke Moodle Jurnal Mengajar
```

---


Hitung berapa jumlah file php

 

Dari daftar yang Anda tampilkan, terdapat 141 file berekstensi .php di direktori:

/var/www/html/moodle/local/jurnalmengajar

Perhitungan tersebut tidak menghitung:

  • Direktori (amd, assets, classes, cli, css, db, lang, pix, templates, vendor, dll.)

  • File non-PHP seperti:

    • composer.json

    • composer.lock

    • jadwal_pengawas.json

  • File backup seperti:

    • export_berita_acara_pdf.php.save

    • tv.php.save

    • tv.php.save.1

Jika yang dimaksud adalah semua file PHP secara rekursif (termasuk di dalam classes/, cli/, db/, dll.), maka jumlahnya akan lebih banyak. Anda bisa mengeceknya langsung di Debian dengan:

find /var/www/html/moodle/local/jurnalmengajar -type f -name "*.php" | wc -l

atau untuk melihat daftarnya:

find /var/www/html/moodle/local/jurnalmengajar -type f -name "*.php" | sort

Thursday, 2 July 2026

cek wa notif tidak terkirim

tail -100 /var/moodledata/logs/wa_debug.log 

Wednesday, 1 July 2026

Modul baru: Guru Tidak Hadir

 

Menurut saya, pengembangan yang kita lakukan kali ini bukan sekadar menambah fitur, tetapi menambahkan satu modul baru yang terintegrasi dengan sistem reminder jurnal.

1. Modul baru: Guru Tidak Hadir

Dibuat tabel baru (tanpa upgrade plugin), misalnya:

mdl_local_jurnalmengajar_kehadiran

Fungsinya menyimpan data guru yang tidak hadir.

Status yang didukung:

  • Sakit

  • Izin

  • Cuti

  • Tugas Luar

Status Alpa sengaja tidak dibuat, karena guru yang alpa tetap harus masuk ke reminder jurnal.


2. CRUD Guru Tidak Hadir

Dibuat halaman administrasi:

guru_takhadir.php

beserta CRUD:

  • guru_takhadir.php

  • guru_takhadir_add.php

  • guru_takhadir_edit.php

  • guru_takhadir_delete.php

Fitur:

  • pilih guru (role local/jurnalmengajar:submit)

  • status

  • periode mulai

  • periode selesai

  • keterangan


3. Fungsi baru di lib.php

Dibuat fungsi:

jurnalmengajar_get_status_takhadir($userid, $tanggal)

Fungsinya:

  • mengecek apakah guru sedang tidak hadir

  • mengembalikan:

    • false

    • sakit

    • izin

    • cuti

    • tugasluar

Sehingga logika pengecekan cukup dipanggil dari satu fungsi.


4. Integrasi ke notif_jurnal.php

Alur lama:

Jadwal
↓
Jam terlewat
↓
Sudah isi?
↓
Reminder

Sekarang menjadi:

Jadwal
↓
Cutoff
↓
Jam terlewat
↓
Guru Tidak Hadir?
│
├── Ya
│     ↓
│  Rekap Guru Tidak Hadir
│
└── Tidak
      ↓
 Sudah isi jurnal?
      │
      ├── Ya
      └── Belum → Reminder

Akibatnya:

  • Guru sakit tidak menerima reminder.

  • Guru izin tidak menerima reminder.

  • Guru cuti tidak menerima reminder.

  • Guru tugas luar tidak menerima reminder.

  • Guru alpa tetap menerima reminder.


5. Rekap Admin

Rekap yang sebelumnya hanya berisi:

Guru Belum Mengisi Jurnal

sekarang menjadi:

Rekap Jurnal Mengajar

Belum Mengisi Jurnal
...

Guru Tidak Hadir
...

Sehingga admin dapat membedakan:

  • siapa yang memang lupa mengisi jurnal,

  • siapa yang memang tidak masuk.


6. Template WhatsApp

Ditambahkan placeholder baru:

{tidakhadir}

Template default juga diperbarui agar menampilkan dua bagian:

  • Belum Mengisi Jurnal

  • Guru Tidak Hadir


7. Debug CLI

Ditambahkan debug baru:

FILLED:
TAKHADIR:

Contoh:

TAKHADIR: Ahmad | XI-A | Sakit
TAKHADIR: Ahmad | XI-B | Sakit

Sehingga proses pengecekan lebih mudah ditelusuri saat menjalankan:

php local/jurnalmengajar/cli/notif_jurnal.php

Hasil akhir

Sekarang sistem reminder jurnal mampu membedakan tiga kondisi guru:

Kondisi GuruReminder WARekap Admin
Sudah mengisi jurnal❌ Tidak❌ Tidak
Belum mengisi jurnal✅ Ya✅ Ya
Sakit / Izin / Cuti / Tugas Luar❌ Tidak✅ Ya

Dampak bagi plugin

Menurut saya, ini adalah peningkatan yang cukup signifikan karena sebelumnya sistem hanya mengenal dua kondisi:

  • Sudah mengisi jurnal.

  • Belum mengisi jurnal.

Sekarang sistem mengenal tiga kondisi operasional:

  1. Sudah mengisi jurnal → tidak perlu tindakan.

  2. Belum mengisi jurnal → dikirim reminder.

  3. Guru tidak hadir (sakit, izin, cuti, tugas luar) → tidak dikirim reminder, tetapi tetap tercatat dalam rekap admin.

Dengan demikian, reminder menjadi lebih akurat dan rekap yang diterima admin lebih informatif serta sesuai dengan kondisi pembelajaran di sekolah.

debug diagnostik rekap reminder

 <?php
define('CLI_SCRIPT', true);
require_once(__DIR__.'/../../../config.php');

require_once(__DIR__.'/../jam_pelajaran_lib.php');
require_once(__DIR__.'/../jadwal_acuan_lib.php');
require_once(__DIR__.'/../lib.php'); 
require_once(__DIR__.'/../lib_notifikasi.php');// fungsi kirim WA

global $DB;

$cohortmap = [];
$cohorts = $DB->get_records('cohort', null, '', 'id,name');
foreach ($cohorts as $c) {
    $cohortmap[$c->id] = $c->name;
}

$today = date('Y-m-d');
$hariIndo = jurnalmengajar_get_hari_ini();
$current = time();
$todayLabel = tanggal_indo(time());

$jamrekap = '19:50';
$jamsekarang = date('H:i');
//$isrekap = ($jamsekarang >= $jamrekap);
$isrekap = true; //test mode rekap

// ===== Cek hari sekolah =====
$hariSekolah = get_config('local_jurnalmengajar', 'harisekolah');

if (empty($hariSekolah)) {
    $hariSekolah = 'Senin,Selasa,Rabu,Kamis,Jumat';
}

$hariSekolah = array_map('trim', explode(',', $hariSekolah));

if (!in_array($hariIndo, $hariSekolah)) {
    mtrace("Hari $hariIndo bukan hari sekolah.");
    exit(0);
}

// ===== Cek tanggal libur =====
if (jurnalmengajar_cek_libur($today)) {
    mtrace("Hari ini tanggal libur.");
    exit(0);
}

// ===== Cek tanggal asesmen =====
$tanggalasesmen = trim(get_config('local_jurnalmengajar', 'tanggalasesmen'));

if (!empty($tanggalasesmen)) {

    if (preg_match('/(\d{4}-\d{2}-\d{2})\s*s\/d\s*(\d{4}-\d{2}-\d{2})/i',
        $tanggalasesmen,
        $match)) {

        $mulai  = strtotime($match[1]);
        $selesai = strtotime($match[2]);
        $hariini = strtotime($today);

        if ($hariini >= $mulai && $hariini <= $selesai) {
            mtrace("Hari ini berada dalam rentang asesmen.");
            exit(0);
        }
    }
}

mtrace("=== Notifikasi Jurnal Rekap ===");
mtrace("Hari: $hariIndo");

// ===== Ambil jam pelajaran =====
$jam_pelajaran = jurnalmengajar_generate_jam();

// ===== Tentukan jam yang sudah selesai =====
$jam_terlewat = [];
foreach ($jam_pelajaran as $jamke => $jam) {
    $selesai = $jam['selesai'];
    if ($current > strtotime("$today $selesai")) {
        $jam_terlewat[] = $jamke;
    }
}

if (empty($jam_terlewat)) {
    mtrace("Belum ada jam pelajaran yang terlewat.");
    exit(0);
}

mtrace("Jam terlewat: " . implode(',', $jam_terlewat));

// ===== Ambil jurnal hari ini =====
$starttoday = strtotime("$today 00:00:00");
$endtoday   = strtotime("$today 23:59:59");

$jurnaltoday = $DB->get_records_sql("
    SELECT id, userid, kelas, jamke
    FROM {local_jurnalmengajar}
    WHERE timecreated BETWEEN :starttoday AND :endtoday
", [
    'starttoday' => $starttoday,
    'endtoday'   => $endtoday
]);

$filled = [];
foreach ($jurnaltoday as $row) {
    foreach (explode(',', $row->jamke) as $j) {
        $j = (int)trim($j);

        // Samakan kelas dengan jadwal
        $kelas = $row->kelas;
        if (isset($cohortmap[$kelas])) {
            $kelas = $cohortmap[$kelas];
        }

        $key = $row->userid . '-' . $kelas . '-' . $j;
        $filled[$key] = true;

        // Debug
        mtrace("FILLED: " . $key);
    }
}
//

// ===== Ambil jadwal dari database =====
$jadwal_db = $DB->get_records_sql("
    SELECT j.id, j.userid, j.kelas, j.jamke, u.lastname
    FROM {local_jurnalmengajar_jadwal} j
    JOIN {user} u ON u.id = j.userid
    WHERE j.hari = :hari
", [
    'hari' => $hariIndo
]);

$jadwal = [];

foreach ($jadwal_db as $j) {
    $jadwal[] = [
        'userid'   => $j->userid,
        'lastname' => $j->lastname,
        'kelas'    => $j->kelas,
        'jamke'    => $j->jamke
    ];
}

if (empty($jadwal)) {
    mtrace("Tidak ada jadwal di database untuk hari $hariIndo");
    exit(0);
}
mtrace("=== JADWAL ===");
foreach ($jadwal as $j) {
    $k = $j['userid'].'-'.$j['kelas'].'-'.$j['jamke'];
    mtrace("JADWAL: " . $k);
}

// ===== Group jurnal yang belum diisi =====
$pending = [];
$tidakhadir = [];
$cutoff_cache = [];
foreach ($jadwal as $j) {

// 🔥 FILTER CUT OFF MULTI KELAS
$kelas_level = null;

// deteksi kelas (VI, IX, XII)
if (preg_match('/\b(VI|IX|XII)\b/i', $j['kelas'], $match)) {
    $kelas_level = strtoupper($match[1]);
}

if ($kelas_level) {

    if (!isset($cutoff_cache[$kelas_level])) {
        $cutoff_cache[$kelas_level] = jurnalmengajar_get_cutoff_by_kelas($kelas_level, $current);
    }

    $cutoff = $cutoff_cache[$kelas_level];

    if ($cutoff && $current >= $cutoff) {
        continue;
    }
}

// ===== Lewati jika jam belum selesai =====
if (!in_array((int)$j['jamke'], $jam_terlewat)) {
    continue;
}

// ===== Cek Guru Tidak Hadir =====
$status = jurnalmengajar_get_status_takhadir(
    $j['userid'],
    $today
);

if ($status !== false) {

    if (!isset($tidakhadir[$j['userid']])) {

        $tidakhadir[$j['userid']] = [
            'lastname' => $j['lastname'],
            'status'   => $status
        ];
    }

    // Debug: tampilkan semua kelas yang sudah terlewat
    mtrace(
        "TAKHADIR: {$j['lastname']} | {$j['kelas']} | " .
        ucfirst($status)
    );

    continue;
}
    $key = $j['userid'] . '-' . $j['kelas'] . '-' . (int)$j['jamke'];

if (isset($filled[$key])) {
    continue;
}

    if (!isset($pending[$j['userid']])) {
        $pending[$j['userid']] = [
            'lastname' => $j['lastname'],
            'kelasjam' => []
        ];
    }

    if (!isset($pending[$j['userid']]['kelasjam'][$j['kelas']])) {
        $pending[$j['userid']]['kelasjam'][$j['kelas']] = [];
    }

    $pending[$j['userid']]['kelasjam'][$j['kelas']][] = (int)$j['jamke'];
}

if (empty($pending) && empty($tidakhadir)) {
    mtrace("Semua jurnal sudah diisi.");
    exit(0);
}
// ===== Kirim WA per guru =====
$mengirim = 0;

if (!$isrekap) {

    mtrace("Mode: Reminder Guru");

    foreach ($pending as $userid => $info) {

//    $user = $DB->get_record('user', ['id'=>$userid], 'id, firstname, lastname');

    // Ambil nomor WA
    $nowa = $DB->get_field_sql("
        SELECT d.data
        FROM {user_info_data} d
        JOIN {user_info_field} f ON f.id = d.fieldid
        WHERE d.userid = :userid AND f.shortname = 'nowa'
    ", ['userid' => $userid]);

    if (empty($nowa)) {
        mtrace("Tidak ada nomor WA untuk {$info['lastname']}");
        continue;
    }

    $nomor = preg_replace('/[^0-9]/', '', $nowa);

    // Urutkan berdasarkan jam pertama
$urut = [];

foreach ($info['kelasjam'] as $kelas => $jamlist) {
    $jamlist = array_unique($jamlist);
    sort($jamlist);
    $urut[$kelas] = $jamlist;
}

// Sort berdasarkan jam pertama
uasort($urut, function($a, $b) {
    return $a[0] <=> $b[0];
});

$listkelas = "";
$ringkasParts = [];

foreach ($urut as $kelas => $jamlist) {
    $listkelas .= "$kelas jam ke " . implode(',', $jamlist) . "\n";
    $ringkasParts[] = $kelas . ':' . implode(',', $jamlist);
}

    $ringkas = implode('; ', $ringkasParts);

    $datawa = [
        '{guru}'     => $info['lastname'],
        '{tanggal}'  => $todayLabel,
        '{kelasjam}' => trim($listkelas)
    ];

        $res = jm_kirim_template(
        'reminder_jurnal',
        $nomor,
        $datawa
    );

    $pending[$userid]['ringkas'] = $ringkas;
    
    mtrace("Kirim ke $nomor ({$info['lastname']}) -> $res");
    if ($res) {
    $mengirim++;
}

    // ===== Log TXT =====
$logtxt = __DIR__ . '/notif_log_' . date('Y-m-d') . '.txt';

$logstatus = $res ? 'BERHASIL' : 'GAGAL';

$line = date('Y-m-d H:i:s')
      . " | Guru: {$info['lastname']}"
      . " | Nomor: $nomor"
      . " | Kelas/Jam: $ringkas"
      . " | Status: $logstatus"
      . "\n";

file_put_contents($logtxt, $line, FILE_APPEND);
}

} else {

    // Mode rekap: tidak kirim WA ke guru,
    // hanya membuat ringkasan.
    mtrace("Mode: Rekap Admin");

    foreach ($pending as $userid => $info) {

        $urut = [];

        foreach ($info['kelasjam'] as $kelas => $jamlist) {
            $jamlist = array_unique($jamlist);
            sort($jamlist);
            $urut[$kelas] = $jamlist;
        }

        uasort($urut, function($a, $b) {
            return $a[0] <=> $b[0];
        });

        $ringkasParts = [];

        foreach ($urut as $kelas => $jamlist) {
            $ringkasParts[] = $kelas . ':' . implode(',', $jamlist);
        }

        $pending[$userid]['ringkas'] = implode('; ', $ringkasParts);
    }
}

if ($isrekap) {

$daftar = '';
$daftartakhadir = '';

if (empty($tidakhadir)) {
    $daftartakhadir = '-';
}
    
    foreach ($pending as $info) {
        $daftar .= "• {$info['lastname']} - {$info['ringkas']}\n";
    }

foreach ($tidakhadir as $info) {

    $daftartakhadir .=
        "• {$info['lastname']} - " .
        ucfirst($info['status']) .
        "\n";
}

    $datawa = [
        '{tanggal}' => $todayLabel,
        '{daftar}'  => trim($daftar),
        '{jumlah}'  => count($pending),
        '{tidakhadir}'  => trim($daftartakhadir)
    ];

// DEBUG
$config = get_config(
    'local_jurnalmengajar',
    'tujuan_rekap_reminder'
);

mtrace("CONFIG TUJUAN : " . $config);

$nomor = jm_get_nomor_tujuan(
    'rekap_reminder',
    $datawa
);

mtrace("NOMOR TUJUAN:");
print_r($nomor);

$template = jm_get_template('rekap_reminder');

mtrace("TEMPLATE:");
mtrace(empty($template) ? "(KOSONG)" : "ADA");

$res = jm_kirim_template_auto(
    'rekap_reminder',
    $datawa
);

if ($res) {
    mtrace("Rekap reminder dikirim.");
} else {
    mtrace("Rekap reminder gagal.");
}
}

mtrace("Selesai. Total notifikasi dikirim: $mengirim");

Thursday, 18 June 2026

atur jadwal mengajar

  • Hanya 3 JP: 4 guru
  • Hanya 2 JP: 12 guru
  • 2 JP dan 3 JP: 24 guru
  • Memiliki 1 JP: Misran S.Pd dan Syamdani S.Pd

Jumlah keseluruhan = 42 guru.

Guru yang memiliki aktivitas 2 JP dan juga 3 JP (minimal satu aktivitas 2 JP dan minimal satu aktivitas 3 JP) adalah:

  1. Ahmad Hafie S.Pd
  2. Anna Herlina S.Pd
  3. Dra. Hj. Tri Krisnawati M.Pd
  4. Drs. Muhammad Rafi'i M.Pd
  5. Fajri Wahyudi S.Pd
  6. Farindra Ramadhan P. S.Pd
  7. Fitria Muallifah S.Pd
  8. Hartati Jum'ah S.Pd
  9. Hj. Mahliyani S.Pd
  10. Hj. Samnah Hayati M.Pd
  11. Hj. Silfiana Hasni S.Pd
  12. Indah Wiratesta Manik S.Th
  13. Khusnul Khotimah S.Pd
  14. Mariatul Qibtiah S.Pd
  15. Muhammad Naufal Akbar M.Pd
  16. Muhammad Saubari S.Pd.I
  17. Mukhtar Fuaddi S.Pd
  18. Nadya Rahmi Sari S.Pd
  19. Nini Rahmini S.Pd.I
  20. Noor Ridhwan S.Pd M.A
  21. Risyandi S.Pd
  22. Sri Hidayati S.Pd
  23. Virawati S.Pd
  24. Yekti Hanani S.Pd MA

Total: 24 guru.

Guru yang hanya memiliki aktivitas 2 JP

  1. Ainayya Almadiyati S.Pd
  2. Ari Deviyanti S.Pd.
  3. Dra. Hj. Marfuah M.Pd
  4. Dwiyani Lestari M.Pd
  5. Ema Susi Darmawati S.Pd
  6. Fatia Elfira Luthfyari S.Pd
  7. Hj. Rianawati S.Pd
  8. Hj. St. Faridah S.Pd
  9. M. Riduan S.Pd
  10. Muhammad Noor S.Pd.
  11. Muhammad Supiani S.IP
  12. Sri Heldawati S.Pd

Total: 12 guru.

 

Guru yang seluruh aktivitasnya hanya berdurasi 3 JP (tidak memiliki aktivitas 1 JP, 2 JP, atau durasi lain) adalah:

  1. Fathah Arsyad S.Pd
  2. Muhammad Wildan Ariandi S.Pd
  3. Rakhmad Taufik S.Pd
  4. Sri Muliyatini S.Pd

======================================= 

Max 7 jam/hari

Drs. Muhammad Rafi'i M.Pd      35
Muhammad Saubari S.Pd.I        35
Nini Rahmini S.Pd.I            35
Ainayya Almadiyati S.Pd        34
Fathah Arsyad S.Pd             33
Muhammad Wildan Ariandi S.Pd   33
Ahmad Hafie S.Pd              32
Fajri Wahyudi S.Pd            32

Max 6 jam/hari

Indah Wiratesta Manik S.Th     30
Rakhmad Taufik S.Pd            30
Sri Muliyatini S.Pd            30
Hj. Silfiana Hani S.Pd         30
Virawati S.Pd                  29
Dra. Hj. Marfuah M.Pd          28
Dwiyani Lestari M.Pd           28
Hartati Jum'ah S.Pd            28
M. Riduan S.Pd                 28
Risyandi S.Pd                  28
Muhammad Naufal Akbar M.Pd     27
Hj. Maliyani S.Pd              26
Hj. Samnah Hayati M.Pd         26
Mariatul Qibtiah S.Pd          26
Sri Heldawati S.Pd             26
Sri Hidayati S.Pd              26

Max 5 jam/hari

Anna Herlina S.Pd              25
Noor Ridhwan S.Pd M.A          25
Dra. Hj. Tri Krisnawati M.Pd   24
Ema Susi Darmawati S.Pd        24
Faridra Ramadhan P.S.Pd        24
Hj. St. Faridah S.Pd           24
Yekti Hanani S.Pd MA           24

Max 4 jam/hari

Mukhtar Fuaddi S.Pd            18
Fatia Elfira Luthfyari S.Pd    16
Hj. Rianawati S.Pd             16
Muhammad Supiani S.IP          16
Nadya Rahmi Sari S.Pd          16
Khusnul Khotimah S.Pd          15

Max 2 jam/hari

Muhammad Noor S.Pd              3
Misan S.Pd                      2
Syamdani S.Pd                   1
Ari Deviyanti S.Pd              1

Namun saya menyarankan jangan langsung memasukkan semua constraint ini.

Mulailah dari yang berat dulu:

35 JP -> max 7
34 JP -> max 7
33 JP -> max 7
32 JP -> max 7

Generate.

Kalau masih sukses, lanjut kelompok 30 JP (max 6).

Kalau langsung diberi constraint ketat untuk 42 guru sekaligus, FET bisa menjadi sulit menemukan solusi. Biasanya saya menambahkan bertahap sampai ketemu batas yang masih feasible.

 

HariGuru
Seninguru Seni, PJOK, Ekonomi
Selasaguru Sosiologi, Sejarah, PPKn, Bahasa Indonesia
Rabuguru Inggris, Geografi, Matematika, Agama
Kamisguru Fisika, Kimia, Biologi, BK

 

Saat Kenaikan Kelas

 Saat kenaikan kelas

  1.   Jalankan simpan_riwayatkelas.php.
  2. Keluarkan alumni XII dari cohort.
  3. Upload CSV siswa aktif untuk memperbarui firstname dan cohort sesuai kelas baru.
  4. Perbarui membership cohort sesuai pembagian kelas baru.
  5. Ganti setting tahun ajaran ke 2026/2027.
  6. Jalankan simpan_riwayatkelas.php.

 

Ya, karena Anda sudah berhasil mengekspor 461 siswa aktif ke CSV, maka Anda sudah punya cadangan data keanggotaan kelas saat ini.

Urutan yang saya sarankan sekarang:

1. Simpan file CSV hasil ekspor

Jangan diubah dulu. Simpan sebagai arsip:

siswa_aktif_20260618.csv

2. Kosongkan cohort aktif

Saat ini cohort yang masih berisi siswa adalah:

75  X-A
76  X-B
77  X-C
78  X-D
79  X-E
80  X-F
81  X-G

82  XI-A
83  XI-B
84  XI-C
85  XI-D
86  XI-E
87  XI-F
88  XI-G

Cek dulu jumlah yang akan dihapus:

SELECT COUNT(*)
FROM mdl_cohort_members
WHERE cohortid IN (
75,76,77,78,79,80,81,
82,83,84,85,86,87,88
);

Harusnya hasil:

461

Kalau benar, lanjut:

DELETE FROM mdl_cohort_members
WHERE cohortid IN (
75,76,77,78,79,80,81,
82,83,84,85,86,87,88
);

3. Verifikasi

SELECT COUNT(*)
FROM mdl_cohort_members;

Target:

0

Artinya seluruh siswa sudah tidak punya cohort.


4. Edit CSV

Misalnya:

X-A  → XI-C
X-B  → XI-A
XI-A → XII-B
XI-B → XII-D

Isi kolom:

firstname
cohort1

dengan kelas baru.


5. Upload kembali

Moodle:

Site administration
→ Users
→ Upload users

Pilih:

Update existing users only

dan aktifkan:

Allow cohort assignments

Maka:

  • firstname berubah menjadi kelas baru.

  • User masuk ke cohort baru.

  • Tidak ada sisa cohort lama karena sudah dikosongkan.


Satu hal yang perlu dicek dulu

Sebelum menghapus 461 anggota cohort, pastikan cohort X-A s.d XII-E tetap ada (yang dikosongkan hanya anggotanya).

Cek:

SELECT id,name
FROM mdl_cohort
ORDER BY name;

Kalau cohortnya masih ada, maka aman untuk mengosongkan mdl_cohort_members dan mengisi ulang melalui CSV.

 

Saturday, 6 June 2026

Ganti kode plugin situs tunggal untuk shared kode


lakukan penggantian:

find . -type f -name "*.php" -exec sed -i \
"s|require_once(__DIR__ . '/../../config.php');|require_once(\$_SERVER['DOCUMENT_ROOT'] . '/config.php');|g" {} +

find . -type f -name "*.php" -exec sed -i \
"s|require_once(__DIR__ . '/../../config.php');|require_once(\$_SERVER['DOCUMENT_ROOT'] . '/config.php');|g" {} +


verifikasi:
grep -R "DOCUMENT_ROOT" .

pastikan tidak ada"
grep -R "__DIR__ . '/../../config.php'" .
cek 
require(__DIR__ . '/../../config.php');
require('../../config.php');
require_once('../../config.php');


ganti yang masih pakai:
find . -maxdepth 1 -type f -name "*.php" \
-exec sed -i \
"s|require(__DIR__ . '/../../config.php');|require_once(\$_SERVER['DOCUMENT_ROOT'] . '/config.php');|g" {} +

verifikasi lagi:
grep -R "config.php" . --include="*.php"


find . -maxdepth 1 -type f -name "*.php" -exec sed -i \
"s|require_once('../../config.php');|require_once(\$_SERVER['DOCUMENT_ROOT'] . '/config.php');|g" {} +

cek syntax error:
php -l index.php

dan
find . -name "*.php" -exec php -l {} \; | grep -v "No syntax errors"


**kirim ke stb:**
rsync -avz --delete \
/home/dhwan/moodle_all/jurnaldo/SIMG-mai/SIMG-main/ \
root@stb:/var/www/shared/local/jurnalmengajar/

**sesudah transfer:**
cd /var/www/shared/local/jurnalmengajar
find . -name "*.php" -exec php -l {} \; | grep -v "No syntax errors"

Ganti Label dan tambah jenis Pembinaan oleh Guru Mata Pelajaran

 nano /var/www/html/moodle/local/jurnalmengajar/classes/form/jurnal_form.php

 

Mudah Guru Duan.

1. Ganti label Catatan → Masalah

Cari:

$mform->addElement(
    'textarea',
    'catatan_pembinaan',
    'Catatan',
    'rows="3" cols="60"'
);

Ganti menjadi:

$mform->addElement(
    'textarea',
    'catatan_pembinaan',
    'Masalah',
    'rows="3" cols="60"'
);

2. Ganti label Tindak Lanjut → Tindak Lanjut / Solusi

Cari:

$mform->addElement(
    'textarea',
    'tindaklanjut_pembinaan',
    'Tindak Lanjut',
    'rows="3" cols="60"'
);

Ganti menjadi:

$mform->addElement(
    'textarea',
    'tindaklanjut_pembinaan',
    'Tindak Lanjut / Solusi',
    'rows="3" cols="60"'
);

3. Menambah pilihan Jenis Pembinaan

Sekarang kemungkinan seperti ini:

$jenispembinaan = [
    '' => '-- Pilih Jenis --',
    'disiplin' => 'Disiplin',
    'sikap' => 'Sikap',
    'akademik' => 'Akademik',
    'kerapian' => 'Kerapian',
    'lainnya' => 'Lainnya'
];

Tinggal tambah item baru:

$jenispembinaan = [
    '' => '-- Pilih Jenis --',

    'disiplin' => 'Disiplin',
    'sikap' => 'Sikap',
    'akademik' => 'Akademik',
    'kerapian' => 'Kerapian',

    'kehadiran' => 'Kehadiran',
    'etika' => 'Etika',
    'ibadah' => 'Ibadah',
    'pergaulan' => 'Pergaulan',
    'motivasi' => 'Motivasi Belajar',
    'tugas' => 'Tidak Mengerjakan Tugas',
    'gadged' => 'Penggunaan HP/Gadget',

    'lainnya' => 'Lainnya'
];

Rekomendasi untuk SMA

Kalau saya melihat kebutuhan Log Perkembangan Murid di sekolah, dropdown yang lebih berguna:

$jenispembinaan = [
    '' => '-- Pilih Jenis --',

    'kehadiran' => 'Kehadiran',
    'disiplin' => 'Disiplin',
    'kerapian' => 'Kerapian',
    'sikap' => 'Sikap',

    'akademik' => 'Akademik',
    'tugas' => 'Tugas',
    'motivasi' => 'Motivasi Belajar',

    'pergaulan' => 'Pergaulan',
    'ibadah' => 'Ibadah',

    'lainnya' => 'Lainnya'
];

Karena nanti saat direkap di Log Perkembangan akan lebih mudah melihat pola masalah siswa:

Kehadiran        : 5 kali
Disiplin         : 3 kali
Akademik         : 2 kali
Pergaulan        : 1 kali

dan ini sangat membantu wali kelas serta BK.

 

Pengaturan Warna BG dan Teks Bootsrap Beranda Guru

 teks gelap background terang
<a class="btn fw-bold w-100" style="background:#7fc7f5;color:#000;"

EKSTRA BG KUNING
<a class="btn fw-bold w-100" style="background:#fde668;color:#000;"

teks putih background gelap
BG BIRU TUA TEKS PUTIH WALI KELAS
<a class="btn text-white fw-bold w-100" style="background:#0a1347;"

BG HIJAU TEKS PUTIH
<a class="btn text-white fw-bold w-100" style="background:#2e7d32;"

SURAT IZIN MURID DI PENGAWAS [MERAH BG]
            <a class="btn btn-danger btn-block w-100"
               href="izin_murid.php">
                📝 Isi Surat Izin Murid


Friday, 22 May 2026

Ekspor dan Import User Murid dari SiM ke CBT moodle

 

1. Ambil data kelas XI

SELECT 
    u.username,
    u.firstname,
    u.lastname,
    u.email,
    nis.data AS profile_field_nis,
    pw.data AS profile_field_pwexam,
    c.name AS cohort1

INTO OUTFILE '/tmp/user_xi.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'

FROM mdl_user u
JOIN mdl_cohort_members cm ON cm.userid = u.id
JOIN mdl_cohort c ON c.id = cm.cohortid

LEFT JOIN mdl_user_info_field fnis 
    ON fnis.shortname = 'nis'

LEFT JOIN mdl_user_info_data nis 
    ON nis.fieldid = fnis.id 
    AND nis.userid = u.id

LEFT JOIN mdl_user_info_field fpw 
    ON fpw.shortname = 'pwexam'

LEFT JOIN mdl_user_info_data pw 
    ON pw.fieldid = fpw.id 
    AND pw.userid = u.id

WHERE c.name IN (
    'XI-A','XI-B','XI-C',
    'XI-D','XI-E','XI-F','XI-G'
)

AND u.deleted = 0
AND u.suspended = 0

ORDER BY c.name, u.lastname;

2. Ambil data kelas X

SELECT 
    u.username,
    u.firstname,
    u.lastname,
    u.email,
    nis.data AS profile_field_nis,
    pw.data AS profile_field_pwexam,
    c.name AS cohort1

INTO OUTFILE '/tmp/user_x.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'

FROM mdl_user u
JOIN mdl_cohort_members cm ON cm.userid = u.id
JOIN mdl_cohort c ON c.id = cm.cohortid

LEFT JOIN mdl_user_info_field fnis 
    ON fnis.shortname = 'nis'

LEFT JOIN mdl_user_info_data nis 
    ON nis.fieldid = fnis.id 
    AND nis.userid = u.id

LEFT JOIN mdl_user_info_field fpw 
    ON fpw.shortname = 'pwexam'

LEFT JOIN mdl_user_info_data pw 
    ON pw.fieldid = fpw.id 
    AND pw.userid = u.id

WHERE c.name IN (
    'X-A','X-B','X-C',
    'X-D','X-E','X-F','X-G'
)

AND u.deleted = 0
AND u.suspended = 0

ORDER BY c.name, u.lastname;

3. Ambil file dari laptop dengan SCP

Kelas XI:

scp dhwan@192.168.200.20:/tmp/user_xi.csv ~/Downloads/

Kelas X:

scp dhwan@192.168.200.20:/tmp/user_x.csv ~/Downloads/

Kalau gagal karena secure_file_priv, cek:

SHOW VARIABLES LIKE 'secure_file_priv';

Lalu ganti path /tmp/ sesuai folder yang ditampilkan MariaDB, misalnya:

/var/lib/mysql-files/

tambah penginput untuk database surat izin guru

 ALTER TABLE mdl_local_jurnalmengajar_suratizinguru 
ADD COLUMN userinput BIGINT(10) NULL AFTER nip;


Tuesday, 19 May 2026

Moodle Tabel yang PERLU diubah di laptop

 

Kesimpulan sinkronisasi struktur tabel dari SiM → laptop Guru Duan:

Tabel yang PERLU diubah di laptop

1. mdl_local_jurnalmengajar

Tambahan kolom:

ALTER TABLE mdl_local_jurnalmengajar
ADD COLUMN absenid LONGTEXT NULL AFTER absen,
ADD COLUMN timemodified BIGINT(10) NOT NULL DEFAULT 0 AFTER timecreated,
ADD COLUMN modifiedby BIGINT(10) NOT NULL DEFAULT 0 AFTER timemodified;

Fungsi:

  • absenid → migrasi userid absensi

  • timemodified → tracking edit

  • modifiedby → siapa editor jurnal


2. mdl_local_jurnalpembinaan

Tambahan kolom:

ALTER TABLE mdl_local_jurnalpembinaan
ADD COLUMN pesertaid LONGTEXT NULL
AFTER peserta;

Fungsi:

  • menyimpan userid siswa BK/pembinaan


3. mdl_local_jurnallayananbk

Tambahan kolom:

ALTER TABLE mdl_local_jurnallayananbk
ADD COLUMN pesertaid LONGTEXT NULL
AFTER peserta;

Fungsi:

  • menyimpan userid layanan BK


4. mdl_local_jurnalguruwali

Tambahan kolom:

ALTER TABLE mdl_local_jurnalguruwali
ADD COLUMN userid BIGINT(10) NULL AFTER muridid,
ADD COLUMN kelas VARCHAR(20) NULL AFTER userid;

Fungsi:

  • kompatibilitas log_perkembangan.php

  • filter guru wali

  • tampil nama kelas


5. mdl_local_jurnalmengajar_suratizin

Tambahan kolom:

ALTER TABLE mdl_local_jurnalmengajar_suratizin
ADD COLUMN kelas VARCHAR(20) NULL AFTER userid;

Fungsi:

  • snapshot nama kelas surat izin

  • dipakai timeline perkembangan


Tabel yang SUDAH aman / tidak perlu diubah

TabelStatus
mdl_user
mdl_cohort
mdl_cohort_members
tipe bigint vs int✅ aman
tipe longtext vs text✅ aman

Setelah semua selesai

Lakukan:

php admin/cli/purge_caches.php

supaya Moodle laptop membaca schema & code terbaru dengan bersih.


Hasil akhir

Fitur berikut akan sinkron penuh dengan SiM:

FiturStatus
Jurnal Mengajar
Absensi userid
Surat Izin
Layanan BK
Pembinaan BK
Guru Wali
Log Perkembangan
Filter card dashboard
Timeline hybrid lama+baru

Sunday, 26 April 2026

github simg dan sim2plus

 

Bisa—dan justru itu desain yang tepat 👍


🎯 Konsep yang Guru pakai sudah benar

🟢 SIMG

➡️ untuk 1 Moodle (single instance)

  • cocok untuk server sederhana

  • semua langsung di satu tempat


🔵 sim2plus

➡️ untuk multi Moodle (shared codebase)

  • 2 Moodle ✔

  • 3, 4, dst ✔

  • scalable ✔


🧠 Cara kerja sim2plus (yang Guru sudah lakukan)

Struktur kira-kira:

/var/www/
├── shared/
│   └── local/jurnalmengajar   ← 1 source code (dipakai semua)
│
├── site1/moodle
├── site2/moodle
├── site3/moodle

👉 semua Moodle:

  • pakai plugin yang sama

  • cukup 1 kali update


🔥 Keuntungan besar

✔ update sekali → semua ikut update
✔ hemat storage
✔ konsisten (tidak beda versi plugin)
✔ mudah maintenance


⚠️ Hal yang WAJIB dijaga

1. Jangan hardcode path ❌

Sudah benar tadi:

require_once($_SERVER['DOCUMENT_ROOT'] . '/config.php');

2. Gunakan $CFG->wwwroot di JS ✔

$.get(M.cfg.wwwroot + '/local/jurnalmengajar/get_students.php')

3. Session beda ✔ (sudah Guru lakukan)

$CFG->sessioncookie = 'MoodleSessionDEV';

4. database masing-masing Moodle ✔

👉 jangan share DB


🚀 NEXT LEVEL (kalau mau lebih pro)

🔥 1. Pakai symlink (kalau belum full)

ln -s /var/www/shared/local/jurnalmengajar \
/var/www/site1/moodle/local/jurnalmengajar

🔥 2. Integrasi dengan Git (sim2plus)

cd /var/www/shared/local/jurnalmengajar
git pull

👉 semua Moodle langsung update


🔥 3. Auto deploy (opsional)

Bisa nanti:

  • git hook

  • atau script cron


🎯 Kesimpulan

✔ SIMG = single Moodle ✔
✔ sim2plus = multi Moodle ✔
✔ bisa berkembang ke 3+ Moodle ✔
✔ desain Guru sudah scalable


💡 Saran jujur

🔥 sim2plus ini sudah masuk level arsitektur production


Kalau Guru mau lanjut:

👉 saya bisa bantu:

  • struktur multi-Moodle yang paling optimal (Nginx + Cloudflare)

  • atau bikin auto update plugin dari GitHub ke semua Moodle

Tinggal bilang 👍

Thursday, 8 January 2026

Pasang Nextcloud di Debian SiM Server

 

Siap Guru Duan 👍
Kita pasang Nextcloud dengan prinsip yang sama seperti Immich: aman, terpisah, tidak mengganggu Moodle & WordPress, dan data besar di /dev/sdb.

Saya buat langkah rapi & bertahap. Fokus dulu install lokal sampai bisa dibuka, belum Cloudflared dulu.


🟦 RENCANA NEXTCLOUD (AMAN & TERPISAH)

Arsitektur

  • Docker sendiri (tidak campur Immich)

  • Port lokal: 8081

  • Data user: /srv/nextcloud/data/dev/sdb

  • Database: MariaDB khusus Nextcloud (container)/dev/sda

  • Tidak sentuh:

    • Nginx host

    • MariaDB Moodle

    • WordPress


🔹 LANGKAH 1 — Siapkan Folder Nextcloud

sudo mkdir -p /opt/docker/nextcloud
sudo mkdir -p /srv/nextcloud/data
sudo chown -R 1000:1000 /srv/nextcloud

Cek:

df -h /srv/nextcloud

Harus terlihat /dev/sdb1.


🔹 LANGKAH 2 — Buat .env Nextcloud

cd /opt/docker/nextcloud
nano .env

Isi (boleh copy):

# ===== Nextcloud =====
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=adminpasswordku
NEXTCLOUD_TRUSTED_DOMAINS=cloud.sman2kandangan.sch.id localhost

# ===== Database =====
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
MYSQL_PASSWORD=nextcloudpass
MYSQL_ROOT_PASSWORD=rootpass

# ===== PHP / Upload =====
PHP_UPLOAD_LIMIT=10G

📌 Password bebas, tapi ingat/simpan.


🔹 LANGKAH 3 — Buat docker-compose.yml

nano docker-compose.yml

Isi versi stabil & ringan:

services:
  nextcloud:
    image: nextcloud:28
    container_name: nextcloud_app
    restart: unless-stopped
    ports:
      - "8081:80"
    environment:
      MYSQL_HOST: db
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
      NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_TRUSTED_DOMAINS}
      PHP_UPLOAD_LIMIT: ${PHP_UPLOAD_LIMIT}
    volumes:
      - /srv/nextcloud/data:/var/www/html/data
      - /opt/docker/nextcloud/html:/var/www/html
    depends_on:
      - db

  db:
    image: mariadb:10.11
    container_name: nextcloud_db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - /opt/docker/nextcloud/db:/var/lib/mysql

📌 MariaDB ini KHUSUS Nextcloud, tidak ada hubungannya dengan moodle-db.


🔹 LANGKAH 4 — Jalankan Nextcloud

docker compose up -d

Tunggu ±1 menit.


🔹 LANGKAH 5 — Cek Status

docker ps

Target:

nextcloud_app   Up
nextcloud_db    Up

🔹 LANGKAH 6 — Tes Lokal

Buka di browser (server/LAN):

http://IP-server:8081

Atau dari server:

curl http://localhost:8081

🎯 Target:

  • Muncul halaman Nextcloud

  • Login pakai admin yang dibuat


🔐 JAMINAN KEAMANAN

✔ Moodle AMAN
✔ WordPress AMAN
✔ MariaDB Moodle TIDAK disentuh
✔ Data Nextcloud masuk /dev/sdb
✔ Nextcloud & Immich tidak saling ganggu


⏭️ LANGKAH SELANJUTNYA

Setelah Nextcloud bisa dibuka lokal, kita lanjut:

LANGKAH 7 — Integrasi Cloudflared

  • cloud.sman2kandangan.sch.idhttp://localhost:8081

  • Tanpa buka port

  • Tanpa ubah Nginx


Balas salah satu:

  • “nextcloud lokal sudah muncul”

  • atau kirim docker ps kalau mau saya cek

Kita lanjutkan sampai cloud sekolah siap dipakai ☁️💪

Thursday, 11 December 2025

server SiM Kamis 11 Desember 2025

 # lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL
NAME   FSTYPE     SIZE MOUNTPOINT                        LABEL
loop0  squashfs  66.8M /snap/core24/1225                 
loop1  squashfs 104.2M /snap/core/17247                  
loop2  squashfs  63.8M /snap/core20/2669                 
loop3  squashfs  66.8M /snap/core24/1196                 
loop4  squashfs   8.1M /snap/certbot-dns-cloudflare/4856 
loop5  squashfs 104.2M /snap/core/17212                  
loop6  squashfs   8.2M /snap/certbot-dns-cloudflare/4697 
loop7  squashfs  63.8M /snap/core20/2682                 
sda             931.5G                                   
├─sda1 ntfs       500M                                   System Reserved
├─sda2 ntfs     438.5G                                   
├─sda3 ntfs       521M                                   
├─sda4              1K                                   
├─sda5 ntfs     154.5G                                   New Volume
├─sda6 swap      15.3G [SWAP]                            
├─sda7 ext4      14.9G /                                 
├─sda8 ext4      27.9G /home                             
└─sda9 ext4     279.4G /var                              
sr0              1024M                    


# df -h
du -sh /var/www/
du -sh /var/lib/mysql/
du -sh /var/moodledata/
Filesystem      Size  Used Avail Use% Mounted on
udev            7.8G     0  7.8G   0% /dev
tmpfs           1.6G  1.2M  1.6G   1% /run
/dev/sda7        15G  5.2G  8.7G  38% /
tmpfs           7.8G     0  7.8G   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
/dev/sda8        28G   52K   26G   1% /home
/dev/sda9       274G   46G  215G  18% /var
/dev/loop5      105M  105M     0 100% /snap/core/17212
/dev/loop1      105M  105M     0 100% /snap/core/17247
/dev/loop3       67M   67M     0 100% /snap/core24/1196
/dev/loop6      8.3M  8.3M     0 100% /snap/certbot-dns-cloudflare/4697
/dev/loop2       64M   64M     0 100% /snap/core20/2669
/dev/loop0       67M   67M     0 100% /snap/core24/1225
/dev/loop7       64M   64M     0 100% /snap/core20/2682
/dev/loop4      8.2M  8.2M     0 100% /snap/certbot-dns-cloudflare/4856
tmpfs           1.6G  4.0K  1.6G   1% /run/user/0
3.2G    /var/www/
1.9G    /var/lib/mysql/
297M    /var/moodledata/

# sudo lshw -short | grep disk
sudo lsblk -o NAME,SIZE,MODEL
/0/100/17/0      /dev/sda    disk           1TB WDC WD10EZEX-21W
/0/100/17/1      /dev/cdrom  disk           DVDRAM GHD0N
NAME     SIZE MODEL
loop0   66.8M 
loop1  104.2M 
loop2   63.8M 
loop3   66.8M 
loop4    8.1M 
loop5  104.2M 
loop6    8.2M 
loop7   63.8M 
sda    931.5G WDC_WD10EZEX-21WN4A0
├─sda1   500M 
├─sda2 438.5G 
├─sda3   521M 
├─sda4     1K 
├─sda5 154.5G 
├─sda6  15.3G 
├─sda7  14.9G 
├─sda8  27.9G 
└─sda9 279.4G 
sr0     1024M HL-DT-ST_DVDRAM_GHD0N

# sudo lspci | grep -i sata
00:17.0 SATA controller: Intel Corporation Q170/Q150/B150/H170/H110/Z170/CM236 Chipset SATA Controller [AHCI Mode] (rev 31)


# sudo lshw -class disk -class storage
  *-sata                    
       description: SATA controller
       product: Q170/Q150/B150/H170/H110/Z170/CM236 Chipset SATA Controller [AHCI Mode]
       vendor: Intel Corporation
       physical id: 17
       bus info: pci@0000:00:17.0
       logical name: scsi0
       logical name: scsi2
       version: 31
       width: 32 bits
       clock: 66MHz
       capabilities: sata msi pm ahci_1.0 bus_master cap_list emulated
       configuration: driver=ahci latency=0
       resources: irq:126 memory:df228000-df229fff memory:df22c000-df22c0ff ioport:f090(size=8) ioport:f080(size=4) ioport:f060(size=32) memory:df22b000-df22b7ff
     *-disk
          description: ATA Disk
          product: WDC WD10EZEX-21W
          vendor: Western Digital
          physical id: 0
          bus info: scsi@0:0.0.0
          logical name: /dev/sda
          version: 1A01
          serial: WCC6Y3UNVZEY
          size: 931GiB (1TB)
          capabilities: partitioned partitioned:dos
          configuration: ansiversion=5 logicalsectorsize=512 sectorsize=4096 signature=ee238f62
     *-cdrom
          description: DVD-RAM writer
          product: DVDRAM GHD0N
          vendor: HL-DT-ST
          physical id: 1
          bus info: scsi@2:0.0.0
          logical name: /dev/cdrom
          logical name: /dev/cdrw
          logical name: /dev/dvd
          logical name: /dev/dvdrw
          logical name: /dev/sr0
          version: 1.00
          capabilities: removable audio cd-r cd-rw dvd dvd-r dvd-ram
          configuration: ansiversion=5 status=nodisc


# sudo grep -i ahci /sys/class/scsi_host/host*/proc_name
/sys/class/scsi_host/host0/proc_name:ahci
/sys/class/scsi_host/host1/proc_name:ahci
/sys/class/scsi_host/host2/proc_name:ahci
/sys/class/scsi_host/host3/proc_name:ahci

# ls /sys/class/ata_port
ata1  ata2  ata3  ata4

# sudo ls -l /sys/class/ata_port
total 0
lrwxrwxrwx 1 root root 0 Dec  9 13:30 ata1 -> ../../devices/pci0000:00/0000:00:17.0/ata1/ata_port/ata1
lrwxrwxrwx 1 root root 0 Dec  9 13:30 ata2 -> ../../devices/pci0000:00/0000:00:17.0/ata2/ata_port/ata2
lrwxrwxrwx 1 root root 0 Dec  9 13:30 ata3 -> ../../devices/pci0000:00/0000:00:17.0/ata3/ata_port/ata3
lrwxrwxrwx 1 root root 0 Dec  9 13:30 ata4 -> ../../devices/pci0000:00/0000:00:17.0/ata4/ata_port/ata4