diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..dce64e9 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# ── Identitas LMS ────────────────────────────── +# Variabel ini tampil di frontend (navbar, footer, title) +APP_BAR_TITLE=Belajar Pemrograman C +COPYRIGHT_TEXT=Nama Sekolah @ 2025 +PAGE_TITLE_SUFFIX=Belajar Pemrograman C + +# ── Lokasi file (relatif dari parent folder) ─── +CONTENT_DIR=content +TOKENS_FILE=tokens.csv + +# ── Flask (dev mode: python app.py) ──────────── +FLASK_DEBUG=false + +# ── Tailscale (opsional, untuk akses remote) ─── +ELEMES_HOST=nama-host-tailscale +TS_AUTHKEY=tskey-auth-xxxx + +# ── UI Tuning (opsional) ─────────────────────── +# CURSOR_OFFSET_Y=50 diff --git a/README.md b/README.md new file mode 100644 index 0000000..bddc686 --- /dev/null +++ b/README.md @@ -0,0 +1,283 @@ +# Elemes - Panduan Guru + +**Elemes** adalah Learning Management System (LMS) untuk mengajar pemrograman atau elektronika. +Guru cukup menyiapkan file konfigurasi dan konten materi dalam format Markdown, +lalu menjalankan container untuk deploy. + +## Struktur Folder + +Setelah setup, struktur folder utama (parent folder) akan terlihat seperti ini: + +``` +project/ +├── .env # Konfigurasi environment +├── content/ # Folder materi pelajaran (file .md) +│ ├── home.md # Halaman utama & daftar pelajaran +│ ├── hello_world.md # Contoh materi +│ └── ... +├── assets/ # Gambar untuk materi (opsional) +│ └── gambar.png +├── tokens_siswa.csv # Data token siswa (auto-generated) +├── state/ # State Tailscale (auto-generated) +└── elemes/ # Folder engine LMS (JANGAN DIUBAH) + ├── elemes.sh # Script untuk menjalankan LMS + └── ... +``` + +## Quick Start + +```bash +cd elemes +./elemes.sh init # Generate .env, content/, dan tokens dari contoh +``` + +Output: + +``` +=== Elemes Quick Start === + +[buat] .env (edit sesuai kebutuhan) +[buat] content/ (4 materi) +[buat] tokens_siswa.csv (edit untuk tambah siswa) + +Selesai! Langkah selanjutnya: + 1. Edit ../.env sesuai kebutuhan + 2. Edit ../content/home.md untuk daftar materi + 3. Edit ../tokens_siswa.csv untuk data siswa + 4. Jalankan: ./elemes.sh runbuild +``` + +Perintah `init` aman dijalankan ulang — file yang sudah ada tidak akan ditimpa. + +### 1. Edit `.env` + +Sesuaikan `.env` di parent folder: + +```env +# Identitas LMS (tampil di frontend) +APP_BAR_TITLE=Pemrograman C - SMK Nusantara +COPYRIGHT_TEXT=SMK Nusantara @ 2025 +PAGE_TITLE_SUFFIX=SMK Nusantara + +# Lokasi file +CONTENT_DIR=content +TOKENS_FILE=tokens.csv + +# Tailscale (opsional, untuk akses remote) +ELEMES_HOST=lms-smk-nusantara +TS_AUTHKEY=tskey-auth-xxxx +``` + +### 2. Edit Konten + +### 3. Buat `home.md` (Halaman Utama) + +File `content/home.md` adalah halaman utama LMS. Di sinilah guru mendefinisikan +judul sambutan dan **daftar pelajaran yang tersedia**. + +Contoh `content/home.md`: + +```markdown +## Selamat Datang di Kelas Pemrograman C + +Situs LMS ini untuk belajar dasar-dasar pemrograman C. + +## Topik yang Akan Dipelajari + +1. [Hello, World!](lesson/hello_world.md) +2. [Variables](lesson/variables.md) +3. [Conditions](lesson/conditions.md) + +----Available_Lessons---- + +1. [Hello, World!](lesson/hello_world.md) +2. [Variables](lesson/variables.md) +3. [Conditions](lesson/conditions.md) +``` + +> **Penting:** Bagian setelah `----Available_Lessons----` adalah daftar pelajaran +> yang dikenali sistem. Pastikan setiap materi yang ada di folder `content/` +> terdaftar di sini. + +### 4. Buat Materi Pelajaran + +Setiap file `.md` di folder `content/` adalah satu materi pelajaran. +Format dasar materi: + +```markdown +---LESSON_INFO--- +**Learning Objectives:** +- Tujuan pembelajaran 1 +- Tujuan pembelajaran 2 + +**Prerequisites:** +- Materi prasyarat (atau "Tidak ada") +---END_LESSON_INFO--- + +# Judul Materi + +Penjelasan materi di sini. Gunakan format Markdown biasa. + +Contoh kode bisa ditulis dalam code block: + +` ` `c +#include + +int main() { + printf("Hello, World!\n"); + return 0; +} +` ` ` + +---EXERCISE--- +### Latihan +Buat program yang mencetak "Hello, World!" +--- + +---INITIAL_CODE--- +#include + +int main() { + // Tulis kode kamu di sini + + return 0; +} +---END_INITIAL_CODE--- + +---EXPECTED_OUTPUT--- +Hello, World! +---END_EXPECTED_OUTPUT--- + +---KEY_TEXT--- +printf +---END_KEY_TEXT--- +``` + +#### Penjelasan Blok-Blok Khusus + +| Blok | Fungsi | +|------|--------| +| `---LESSON_INFO---` | Info pelajaran: tujuan & prasyarat | +| `---EXERCISE---` | Deskripsi latihan soal | +| `---INITIAL_CODE---` | Kode awal yang muncul di editor siswa | +| `---EXPECTED_OUTPUT---` | Output yang diharapkan untuk validasi jawaban | +| `---KEY_TEXT---` | Kata kunci yang harus ada di kode siswa | + +#### Blok Khusus Circuit (Opsional) + +Untuk materi yang melibatkan simulator rangkaian elektronika: + +| Blok | Fungsi | +|------|--------| +| `` ```circuit `` | Rangkaian yang ditampilkan di materi | +| `---INITIAL_CIRCUIT---` | Rangkaian awal untuk latihan | +| `---EXPECTED_CIRCUIT_OUTPUT---` | Validasi rangkaian (format JSON) | +| `---KEY_TEXT_CIRCUIT---` | Kata kunci rangkaian | + +### 5. Generate Token Siswa + +Setelah materi siap, generate file `tokens_siswa.csv`: + +```bash +cd elemes +./elemes.sh generatetoken +``` + +File `tokens_siswa.csv` akan dibuat di parent folder. Edit file ini untuk +menambahkan siswa: + +```csv +token;nama_siswa;hello_world;variables;conditions +TOKEN_GURU_001;Pak Guru;not_started;not_started;not_started +TOKEN_SISWA_001;Budi Santoso;not_started;not_started;not_started +TOKEN_SISWA_002;Siti Aminah;not_started;not_started;not_started +``` + +> **Catatan:** +> - Kolom dipisahkan dengan **titik koma** (`;`), bukan koma. +> - Baris **pertama data** (setelah header) dianggap sebagai **token guru**. +> - Token bisa berupa string apa saja yang unik per siswa. +> - File ini bisa diedit menggunakan spreadsheet (LibreOffice Calc, Excel). +> Saat membuka di Excel/Calc, pilih delimiter **titik koma**. + +### 6. (Opsional) Tambahkan Gambar + +Letakkan gambar di folder `assets/` di parent folder. +Referensi di materi menggunakan: + +```markdown +![deskripsi](assets/nama_gambar.png) +``` + +### 7. Jalankan LMS + +```bash +cd elemes + +# Build dan jalankan (pertama kali atau setelah update) +./elemes.sh runbuild + +# Jalankan tanpa build ulang +./elemes.sh run + +# Stop +./elemes.sh stop +``` + +LMS akan berjalan dan bisa diakses melalui Tailscale (jika dikonfigurasi) +atau langsung di `http://localhost:3000`. + +## Perintah `elemes.sh` + +| Perintah | Fungsi | +|----------|--------| +| `./elemes.sh init` | Setup awal: generate `.env`, `content/`, `tokens_siswa.csv` dari contoh | +| `./elemes.sh run` | Jalankan container | +| `./elemes.sh runbuild` | Build ulang & jalankan container | +| `./elemes.sh stop` | Hentikan container | +| `./elemes.sh generatetoken` | Generate/update `tokens_siswa.csv` | + +## Contoh Siap Pakai + +Folder `examples/` berisi contoh lengkap yang digunakan oleh `./elemes.sh init`: + +``` +examples/ +├── content/ +│ ├── home.md # Halaman utama (3 materi) +│ ├── hello_world.md # Materi dasar: Hello World +│ ├── variabel.md # Materi dasar: Variabel +│ └── rangkaian_dasar.md # Materi hybrid: C + Circuit +└── tokens_siswa.csv # Data siswa contoh (1 guru + 3 siswa) +``` + +File `rangkaian_dasar.md` adalah contoh **materi hybrid** yang menggabungkan +latihan pemrograman C dan simulator rangkaian elektronika dalam satu lesson. + +## FAQ + +**Q: Bagaimana menambah materi baru?** +Buat file `.md` baru di `content/`, lalu tambahkan link-nya di `content/home.md` +(di bagian daftar topik DAN bagian `----Available_Lessons----`). +Jalankan `./elemes.sh generatetoken` untuk update kolom di CSV. + +**Q: Bagaimana menambah siswa baru?** +Edit `tokens_siswa.csv`, tambahkan baris baru dengan token unik dan nama siswa. +Kolom pelajaran diisi `not_started`. + +**Q: Siswa lupa token-nya?** +Buka file `tokens_siswa.csv` dan cari nama siswa untuk melihat token-nya. + +**Q: Bagaimana melihat progress siswa?** +Login menggunakan token guru (baris pertama di CSV). Dashboard progress +akan otomatis muncul. + +**Q: Apakah harus pakai Tailscale?** +Tidak. Tailscale opsional untuk akses remote. Tanpa Tailscale, LMS +bisa diakses di jaringan lokal via `http://localhost:3000`. + +## Persyaratan Sistem + +- [Podman](https://podman.io/) dan `podman-compose` +- Python 3 (untuk `generatetoken`) +- Koneksi internet (saat build pertama kali untuk download image) diff --git a/compiler/__init__.py b/compiler/__init__.py index 19bdb91..b8a4f7d 100644 --- a/compiler/__init__.py +++ b/compiler/__init__.py @@ -2,7 +2,6 @@ Compiler Factory for managing different language compilers """ -import os from .c_compiler import CCompiler from .python_compiler import PythonCompiler @@ -11,16 +10,13 @@ class CompilerFactory: """ Factory class to create and manage different language compilers """ - + def __init__(self): self.compilers = { 'c': CCompiler(), 'python': PythonCompiler() } - - # Get default language from environment variable - default_language = os.environ.get('DEFAULT_PROGRAMMING_LANGUAGE', 'c').lower() - self.default_compiler = self.compilers.get(default_language, self.compilers['c']) + self.default_compiler = self.compilers['c'] def get_compiler(self, language=None): """ diff --git a/config.py b/config.py index 0627c0d..c7e801b 100644 --- a/config.py +++ b/config.py @@ -6,12 +6,4 @@ import os CONTENT_DIR = os.environ.get('CONTENT_DIR', 'content') -STATIC_DIR = os.environ.get('STATIC_DIR', 'static') -TEMPLATES_DIR = os.environ.get('TEMPLATES_DIR', 'templates') TOKENS_FILE = os.environ.get('TOKENS_FILE', 'tokens.csv') - -APP_BAR_TITLE = os.environ.get('APP_BAR_TITLE', 'C Programming Learning System') -COPYRIGHT_TEXT = os.environ.get('COPYRIGHT_TEXT', 'C Programming Learning System © 2025') -PAGE_TITLE_SUFFIX = os.environ.get('PAGE_TITLE_SUFFIX', 'C Programming Learning System') - -DEFAULT_PROGRAMMING_LANGUAGE = os.environ.get('DEFAULT_PROGRAMMING_LANGUAGE', 'c').lower() diff --git a/elemes.sh b/elemes.sh index 4796532..c3151df 100755 --- a/elemes.sh +++ b/elemes.sh @@ -1,8 +1,49 @@ #!/bin/bash SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PARENT_DIR="$(dirname "$SCRIPT_DIR")" +EXAMPLES_DIR="$SCRIPT_DIR/examples" case "$1" in +init) + echo "=== Elemes Quick Start ===" + echo "" + + # .env + if [ -f "$PARENT_DIR/.env" ]; then + echo "[skip] .env sudah ada" + else + cp "$EXAMPLES_DIR/../.env.example" "$PARENT_DIR/.env" + echo "[buat] .env (edit sesuai kebutuhan)" + fi + + # content/ + if [ -d "$PARENT_DIR/content" ] && [ "$(ls -A "$PARENT_DIR/content" 2>/dev/null)" ]; then + echo "[skip] content/ sudah ada" + else + mkdir -p "$PARENT_DIR/content" + cp -n "$EXAMPLES_DIR/content/"*.md "$PARENT_DIR/content/" + echo "[buat] content/ ($(ls "$PARENT_DIR/content/"*.md 2>/dev/null | wc -l) materi)" + fi + + # assets/ + mkdir -p "$PARENT_DIR/assets" + + # tokens + if [ -f "$PARENT_DIR/tokens_siswa.csv" ]; then + echo "[skip] tokens_siswa.csv sudah ada" + else + cp "$EXAMPLES_DIR/tokens_siswa.csv" "$PARENT_DIR/tokens_siswa.csv" + echo "[buat] tokens_siswa.csv (edit untuk tambah siswa)" + fi + + echo "" + echo "Selesai! Langkah selanjutnya:" + echo " 1. Edit ../.env sesuai kebutuhan" + echo " 2. Edit ../content/home.md untuk daftar materi" + echo " 3. Edit ../tokens_siswa.csv untuk data siswa" + echo " 4. Jalankan: ./elemes.sh runbuild" + ;; stop | run | runbuild) echo "Stop Container..." podman-compose --env-file ../.env down @@ -20,6 +61,6 @@ generatetoken) python3 "$SCRIPT_DIR/generate_tokens.py" ;;& *) - echo "elemes.sh ( run | runbuild | stop | generatetoken )" + echo "elemes.sh ( init | run | runbuild | stop | generatetoken )" ;; esac diff --git a/examples/assets/put_your_image.here b/examples/assets/put_your_image.here new file mode 100644 index 0000000..e69de29 diff --git a/examples/content/hello_world.md b/examples/content/hello_world.md new file mode 100644 index 0000000..9ece9e4 --- /dev/null +++ b/examples/content/hello_world.md @@ -0,0 +1,57 @@ +---LESSON_INFO--- +**Learning Objectives:** +- Memahami struktur dasar program C +- Belajar menggunakan printf untuk menampilkan output + +**Prerequisites:** +- Tidak ada persyaratan khusus +---END_LESSON_INFO--- + +# Hello, World! + +Program C paling sederhana terdiri dari fungsi `main()` dan perintah `printf()`. + +## Struktur Dasar + +```c +#include + +int main() { + printf("Hello, World!\n"); + return 0; +} +``` + +Penjelasan: +- `#include ` — memasukkan pustaka input/output standar +- `int main()` — fungsi utama, titik awal program +- `printf()` — mencetak teks ke layar +- `\n` — membuat baris baru +- `return 0` — menandakan program selesai tanpa error + +---EXERCISE--- +### Latihan +Buat program yang mencetak teks berikut: + +``` +Halo Dunia +``` +--- + +---INITIAL_CODE--- +#include + +int main() { + // Tulis kode kamu di sini + + return 0; +} +---END_INITIAL_CODE--- + +---EXPECTED_OUTPUT--- +Halo Dunia +---END_EXPECTED_OUTPUT--- + +---KEY_TEXT--- +printf +---END_KEY_TEXT--- diff --git a/examples/content/home.md b/examples/content/home.md new file mode 100644 index 0000000..d1547c0 --- /dev/null +++ b/examples/content/home.md @@ -0,0 +1,26 @@ +## Selamat Datang di Kelas Pemrograman C + +Situs LMS ini untuk belajar dasar-dasar pemrograman C. +Semua materi bisa dikerjakan langsung di browser! + +### Fitur LMS + +1. __Materi Interaktif__ : Belajar teori dan langsung praktik menulis kode +2. __Code Editor__ : Tulis dan jalankan program C langsung di browser +3. __Validasi Otomatis__ : Sistem mengecek jawaban kamu secara otomatis +4. __Tracking Progress__ : Guru bisa memantau perkembangan siswa (opsional) + +## Topik yang Akan Dipelajari + +### Dasar-Dasar +1. [Hello, World!](lesson/hello_world.md) +2. [Variabel](lesson/variabel.md) + +### Elektronika (Hybrid) +3. [Rangkaian Dasar](lesson/rangkaian_dasar.md) + +----Available_Lessons---- + +1. [Hello, World!](lesson/hello_world.md) +2. [Variabel](lesson/variabel.md) +3. [Rangkaian Dasar](lesson/rangkaian_dasar.md) diff --git a/examples/content/rangkaian_dasar.md b/examples/content/rangkaian_dasar.md new file mode 100644 index 0000000..a62bcd6 --- /dev/null +++ b/examples/content/rangkaian_dasar.md @@ -0,0 +1,92 @@ +---LESSON_INFO--- +Pelajaran hybrid: Pemrograman C + Simulator Rangkaian Elektronika. + +**Learning Objectives:** +- Memahami konsep voltage divider +- Menulis program C untuk menghitung tegangan +- Menggunakan simulator rangkaian + +**Prerequisites:** +- Hello, World! +- Variabel +---END_LESSON_INFO--- + +# Rangkaian Voltage Divider + +Rangkaian **voltage divider** membagi tegangan input menjadi tegangan yang lebih kecil +menggunakan dua resistor. + +## Rumus + +``` +Vout = Vin * (R2 / (R1 + R2)) +``` + +Jika R1 = R2 = 1kΩ dan Vin = 5V: +``` +Vout = 5 * (1000 / (1000 + 1000)) = 2.5V +``` + +## Contoh Rangkaian + +Berikut rangkaian voltage divider sederhana: + +```circuit + + + + + + + +``` + +Perhatikan tegangan di **Vout** adalah ~2.5V. + +---EXERCISE--- +### Tantangan 1: Pemrograman C +Buat program yang mencetak hasil perhitungan voltage divider. + +### Tantangan 2: Elektronika +Lengkapi rangkaian agar tegangan di **Vout** bernilai **2.5V**. +--- + +---INITIAL_CODE--- +#include + +int main() { + // Hitung voltage divider: Vout = Vin * R2 / (R1 + R2) + // Vin=5, R1=1000, R2=1000 + + return 0; +} +---END_INITIAL_CODE--- + +---INITIAL_CIRCUIT--- + + + + + + +---END_INITIAL_CIRCUIT--- + +---EXPECTED_OUTPUT--- +Vout = 2.50V +---END_EXPECTED_OUTPUT--- + +---EXPECTED_CIRCUIT_OUTPUT--- +{ + "nodes": { + "Vout": { "voltage": 2.5, "tolerance": 0.2 } + } +} +---END_EXPECTED_CIRCUIT_OUTPUT--- + +---KEY_TEXT--- +printf +---END_KEY_TEXT--- + +---KEY_TEXT_CIRCUIT--- +Vout +---END_KEY_TEXT_CIRCUIT--- diff --git a/examples/content/variabel.md b/examples/content/variabel.md new file mode 100644 index 0000000..e8cfb6f --- /dev/null +++ b/examples/content/variabel.md @@ -0,0 +1,76 @@ +---LESSON_INFO--- +**Learning Objectives:** +- Memahami konsep variabel di bahasa C +- Belajar mendeklarasikan dan menginisialisasi variabel +- Mengenal tipe data dasar: int, float, char + +**Prerequisites:** +- Hello, World! +---END_LESSON_INFO--- + +# Variabel dalam C + +**Variabel** adalah tempat untuk menyimpan data di memori komputer. +Setiap variabel memiliki **nama** dan **tipe data**. + +## Tipe Data Dasar + +| Tipe | Deskripsi | Contoh | +|------|-----------|--------| +| `int` | Bilangan bulat | `42`, `-7` | +| `float` | Bilangan desimal | `3.14`, `-0.5` | +| `char` | Satu karakter | `'A'`, `'z'` | + +## Deklarasi dan Inisialisasi + +```c +#include + +int main() { + int umur = 17; + float tinggi = 165.5; + char huruf = 'A'; + + printf("Umur: %d tahun\n", umur); + printf("Tinggi: %.1f cm\n", tinggi); + printf("Huruf: %c\n", huruf); + + return 0; +} +``` + +Format specifier untuk `printf()`: +- `%d` — integer +- `%f` — float (gunakan `%.1f` untuk 1 desimal) +- `%c` — character + +---EXERCISE--- +### Latihan +Buat program yang mendeklarasikan variabel `nama_panjang` bertipe `int` dengan nilai `10`, +lalu cetak hasilnya. + +Output yang diharapkan: +``` +Panjang nama: 10 +``` +--- + +---INITIAL_CODE--- +#include + +int main() { + // Deklarasikan variabel nama_panjang bertipe int + // Cetak hasilnya menggunakan printf + + return 0; +} +---END_INITIAL_CODE--- + +---EXPECTED_OUTPUT--- +Panjang nama: 10 +---END_EXPECTED_OUTPUT--- + +---KEY_TEXT--- +int +printf +---END_KEY_TEXT--- diff --git a/examples/tokens_siswa.csv b/examples/tokens_siswa.csv new file mode 100644 index 0000000..3b61baa --- /dev/null +++ b/examples/tokens_siswa.csv @@ -0,0 +1,5 @@ +token;nama_siswa;hello_world;variabel;rangkaian_dasar +GURU001;Pak Budi;not_started;not_started;not_started +SISWA001;Andi Pratama;not_started;not_started;not_started +SISWA002;Siti Nurhaliza;not_started;not_started;not_started +SISWA003;Rizky Firmansyah;not_started;not_started;not_started diff --git a/podman-compose.yml b/podman-compose.yml index ed0bf6f..b515305 100644 --- a/podman-compose.yml +++ b/podman-compose.yml @@ -3,7 +3,6 @@ version: '3.8' services: elemes: build: . - container_name: elemes volumes: - ../content:/app/content - ../tokens_siswa.csv:/app/tokens.csv @@ -19,7 +18,6 @@ services: elemes-frontend: build: ./frontend - container_name: elemes-frontend # ports: # - 3000:3000 environment: @@ -34,7 +32,6 @@ services: elemes-ts: image: docker.io/tailscale/tailscale:latest - container_name: elemes-ts hostname: ${ELEMES_HOST} environment: - TS_AUTHKEY=${TS_AUTHKEY} diff --git a/routes/lessons.py b/routes/lessons.py index 6e73069..8cd51cb 100644 --- a/routes/lessons.py +++ b/routes/lessons.py @@ -7,7 +7,7 @@ import os from flask import Blueprint, request, jsonify, send_from_directory from compiler import compiler_factory -from config import CONTENT_DIR, DEFAULT_PROGRAMMING_LANGUAGE +from config import CONTENT_DIR from services.lesson_service import ( get_ordered_lessons_with_learning_objectives, render_markdown_content, @@ -90,7 +90,11 @@ def api_lesson(filename): prev_lesson = all_lessons[current_idx - 1] if current_idx > 0 else None next_lesson = all_lessons[current_idx + 1] if 0 <= current_idx < len(all_lessons) - 1 else None - programming_language = DEFAULT_PROGRAMMING_LANGUAGE + # Derive language from active_tabs instead of global env var + if 'python' in active_tabs: + programming_language = 'python' + else: + programming_language = 'c' language_display_name = compiler_factory.get_language_display_name(programming_language) return jsonify({