# Import Konten Hotel University & Jobdesk

`bin/import-content.php` + `src/Service/HrContentImporter.php` mem-populate
`hr_faculties` / `hr_courses` / `hr_tests` / `hr_positions` dari dokumen
konten resmi di `hotelcrew/docs/` (format HTML/JSON/MD sudah baku — **importer
hanya MEMBACA, tidak pernah mengarang isi, kompetensi, atau tanggal**).

Konten HTML/JSON asli **tidak disalin** ke database — `hr_courses.materials`
hanya menyimpan referensi path relatif terhadap `content_root` supaya
`course.php`/`university.php` membaca file itu langsung dari disk.

## Pemakaian

```
php bin/import-content.php \
  --faculty=03-housekeeping --faculty=01-general-studies \
  --jobdesk=HK \
  --property=1 \
  [--all-jobdesk] [--dry-run] [--root=PATH] [--config=PATH]
```

| Flag | Arti |
|---|---|
| `--faculty=NN-nama` | Bisa diulang. Nama folder di `{content_root}/university/`. |
| `--jobdesk=PREFIX` | Bisa diulang. Impor semua `{content_root}/jobdesk/{PREFIX}*.md`. |
| `--jobdesk-file=PATH` | Impor satu file jobdesk spesifik. |
| `--all-jobdesk` | Impor SEMUA file jobdesk (abaikan `--jobdesk`). |
| `--property=ID` | Wajib secara logis; default `1` bila kosong. |
| `--root=PATH` | Override `content_root` (default: config `content_root` atau `../../hotelcrew/docs` relatif folder `backend/`, yaitu `engineering/hotelcrew/docs`). |
| `--config=PATH` | Muat config array PHP alternatif (mis. DB uji terpisah) TANPA menyentuh `config/config.php` produksi. Tanpa flag ini, script memakai `src/Core/bootstrap.php` standar (config produksi), sama seperti `bin/migrate.php`. |
| `--dry-run` | Baca & hitung semua seperti biasa, TAPI tidak menulis apa pun ke DB (aman untuk cek dulu). |

Contoh menambah faculty lain di kemudian hari (tanpa kode baru):

```
php bin/import-content.php --faculty=02-front-office --property=1
```

## Apa yang diimpor

### Faculty (`hr_faculties`)
Dibaca dari `{content_root}/university/{faculty-dir}/README.md`, baris
`# Faculty: Nama (KODE)`. Upsert asli via `UNIQUE(property_id, code)` (kolom
ini memang sudah ada di skema).

### Course (`hr_courses`) — satu per file `*.html` di `level-N/`
- `curriculum.html` (indeks silabus) dilewati — bukan materi.
- Judul, `lesson-code`, daftar kompetensi, durasi, dan pass-grade diambil dari
  tag `<title>` + `<meta name="lesson-code|competencies|duration-minutes|pass-grade">`
  di kepala file (format `TEST-FORMAT.md`/`SYLLABUS.md`). Bila sebuah HTML
  tidak punya meta (kartu referensi tambahan seperti
  `GEN-18-kartu-frasa-perancis-korea.html`), kompetensi diturunkan dari
  prefix nama file (`GEN-18-...` → `GEN-18`).
- `hr_courses` **tidak punya kolom kode** di skema (`0008_hr_init.sql`), jadi
  kunci alami idempoten disimpan di elemen pertama `materials` JSON:
  `{"type":"meta","lesson_code":"HK-L1-W01", ...}`. Import ulang mencari baris
  lama via `materials LIKE '%"lesson_code":"HK-L1-W01"%'` (prepared statement)
  sebelum memutuskan INSERT vs UPDATE.
- `competency_id` (kolom FK tunggal) diisi kompetensi **pertama** dari daftar
  meta yang SUDAH terdaftar di `hr_competencies`; seluruh daftar kompetensi
  tetap disimpan apa adanya di `materials[0].competencies` untuk referensi.
- `mandatory_departments`: `NULL` untuk faculty `GEN` (wajib semua
  departemen); `[nama_faculty_lowercase]` untuk faculty lain (mengikuti pola
  seed `0010_seed_hr_hk.sql`).
- `week_of` / `deadline`: **sengaja dikosongkan (NULL)**. Importer tidak
  mengarang jadwal rilis nyata (Aturan Inti #6 PM-PLAN) — admin University
  mengisinya manual saat penjadwalan sungguhan lewat UI.

### Test course (`hr_tests`, `type='course'`) — dari `*-test.json` sibling
`question_bank` = array `questions` dari file **apa adanya** (field
`text/options/answer/explanation/type/competency` tidak direshape). `pass_grade`
dari file, default `80` bila tidak ada. `time_limit_minutes`/`max_attempts`
dari file bila ada (default `max_attempts=3`). Kunci alami idempoten:
`(property_id, course_id, type='course')`.

### Test placement (`hr_tests`, `type='placement'`) — dari `{KODE}-PLACEMENT.json`
File placement berbentuk **satu test dengan banyak `sections` per level**
(`level`, `time_limit_minutes`, `questions[]`). Skema `hr_tests` tidak
mendukung sub-section bersarang dalam satu baris, jadi **keputusan pemetaan
importer**: satu baris `hr_tests` per section/level, judul
`"{KODE} — Placement Level {N}"` (kunci alami idempoten =
`(property_id, title, type='placement')`). `competency_id` dibiarkan `NULL`
karena satu placement mencakup banyak kompetensi sekaligus (kolom FK tunggal
tidak cukup) — setiap kode kompetensi yang disebut di field `competency` tiap
soal tetap dicek terhadap `hr_competencies` dan dilaporkan sebagai "skipped"
bila belum terdaftar.

### Jobdesk (`hr_positions.jobdesc_code` / `jobdesc_version` / `required_competencies`)
Dari `hotelcrew/docs/jobdesk/{KODE}-{slug}.md`. Kode & versi diambil dari
baris 2 (`Kode: HK-RA-01 · Versi: 3 · ...`), department & level dari baris 3
(`Departemen: ... · Level: ...`), judul posisi dari heading
`# JOBDESK — Nama`. `required_competencies` diambil dari baris
`Kompetensi wajib (skill matrix): ...` (kode `{PREFIX}-{nn}` + frasa khusus
`GEN core` → `GEN-CORE`). **Isi 12-komponen jobdesk TETAP di file `.md`** —
hanya path (`jobdesc_code`) + versi yang disimpan di DB. Kunci alami
idempoten: `(property_id, jobdesc_code)` — tidak ada UNIQUE key DB di kolom
ini (skema `0008_hr_init.sql`), jadi importer SELECT dulu sebelum
INSERT/UPDATE.

## Aturan anti-karang (wajib, PM-PLAN §1 butir 6 / CLAUDE.md)

Importer **TIDAK PERNAH** membuat baris `hr_competencies` baru. Bila sebuah
kode kompetensi disebut di HTML/JSON/jobdesk tapi belum ada di
`hr_competencies` (mis. banyak kode `GEN-01..GEN-17` yang README sebut ada
tapi belum di-seed ke tabel — lihat komentar `0010_seed_hr_hk.sql`), baris itu
**dilewati dan dilaporkan** di ringkasan CLI sebagai:
```
skipped: [course HK-L1-W05] HK-07: kode kompetensi belum terdaftar di hr_competencies
```
Solusinya BUKAN mengarang kompetensi di importer, melainkan menambah baris
`hr_competencies` yang benar (dari `KOMPETENSI-UNIVERSITY.xlsx` sheet Master
Kompetensi) di migrasi seed terpisah.

## Hasil uji nyata (DB `import_test_s1`, dihapus setelah uji)

Migrasi `0001`→`0012` dijalankan bersih ke DB kosong, lalu
`--faculty=03-housekeeping --faculty=01-general-studies --jobdesk=HK
--property=1` dijalankan dua kali (idempotensi terverifikasi — angka baris
sama persis di kedua run):

| Metrik | Hasil |
|---|---|
| Course diimpor | 39 (12 HK + 27 GEN) |
| Test diimpor | 36 (13 HK termasuk 3 placement-level + 23 GEN termasuk 3 placement-level) |
| Soal diimpor | 305 |
| Jobdesk diimpor (upsert ke posisi seed existing, bukan duplikat) | 5 |
| Skipped (kode kompetensi belum terdaftar) | 161 — mayoritas `GEN-01..GEN-17`/`GEN-19` (README janjikan 21 kompetensi GEN, baru 4 yang di-seed: `GEN-CORE/18/20/21`) + `HK-02/05/06/07/11` (README janjikan 19 kompetensi HK, baru 15 yang di-seed) |

Setelah uji: `DROP DATABASE import_test_s1;` dan config uji sementara
dihapus — `config/config.php` produksi tidak pernah disentuh.
