feat: update to support folder inside content folder to easy to manage
This commit is contained in:
parent
8485425d0f
commit
bc49877123
|
|
@ -43,8 +43,8 @@ init)
|
||||||
echo "✅ [Skip] Folder content/ sudah ada"
|
echo "✅ [Skip] Folder content/ sudah ada"
|
||||||
else
|
else
|
||||||
mkdir -p "$PARENT_DIR/content"
|
mkdir -p "$PARENT_DIR/content"
|
||||||
cp -n "$EXAMPLES_DIR/content/"*.md "$PARENT_DIR/content/"
|
cp -rn "$EXAMPLES_DIR/content/"* "$PARENT_DIR/content/"
|
||||||
echo "📁 [Buat] Folder content/ ($(ls "$PARENT_DIR/content/"*.md 2>/dev/null | wc -l) materi contoh ditambahkan)"
|
echo "📁 [Buat] Folder content/ ($(find "$PARENT_DIR/content/" -name "*.md" 2>/dev/null | wc -l) materi contoh ditambahkan)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# assets/
|
# assets/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Test Slide Feature
|
||||||
|
|
||||||
|
Selamat datang di materi uji coba fitur slide.
|
||||||
|
|
||||||
|
---slide-start---
|
||||||
|
# Slide 1: Pengenalan
|
||||||
|
Ini adalah konten slide pertama.
|
||||||
|
---
|
||||||
|
# Slide 2: Keuntungan
|
||||||
|
- Mudah digunakan
|
||||||
|
- Berbasis Markdown
|
||||||
|
- Interaktif
|
||||||
|
---
|
||||||
|
# Slide 3: Selesai
|
||||||
|
Terima kasih telah mencoba!
|
||||||
|
---slide-end---
|
||||||
|
|
||||||
|
Setelah slide di atas, ini adalah konten materi biasa.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <stdio.h>
|
||||||
|
int main() {
|
||||||
|
printf("Uji coba slide\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -18,15 +18,16 @@ Semua materi bisa dikerjakan langsung di browser!
|
||||||
3. [Uji Coba Flowchart](lesson/flowchart_test.md)
|
3. [Uji Coba Flowchart](lesson/flowchart_test.md)
|
||||||
4. [Uji Coba Kuis](lesson/quiz_test.md)
|
4. [Uji Coba Kuis](lesson/quiz_test.md)
|
||||||
5. [Uji Coba LaTeX](lesson/latex_test.md)
|
5. [Uji Coba LaTeX](lesson/latex_test.md)
|
||||||
|
6. [Uji Coba Slide](lesson/test_slides.md)
|
||||||
|
|
||||||
### Elektronika (Hybrid)
|
### Elektronika & Sirkuit (Hybrid)
|
||||||
6. [Rangkaian Dasar](lesson/rangkaian_dasar.md)
|
7. [Rangkaian Dasar](lesson/rangkaian_dasar.md)
|
||||||
|
|
||||||
### Arduino (Velxio)
|
### Arduino & Robotika (Velxio)
|
||||||
7. [LED Blink](lesson/led_blink_arduino.md)
|
8. [LED Blink](lesson/led_blink_arduino.md)
|
||||||
8. [Hello Serial](lesson/hello_serial_arduino.md)
|
9. [Hello Serial](lesson/hello_serial_arduino.md)
|
||||||
9. [Button Input](lesson/button_input_arduino.md)
|
10. [Button Input](lesson/button_input_arduino.md)
|
||||||
10. [Traffic Light](lesson/traffic_light_arduino.md)
|
11. [Traffic Light](lesson/traffic_light_arduino.md)
|
||||||
|
|
||||||
----Available_Lessons----
|
----Available_Lessons----
|
||||||
|
|
||||||
|
|
@ -34,8 +35,10 @@ Semua materi bisa dikerjakan langsung di browser!
|
||||||
2. [Variabel](lesson/variabel.md)
|
2. [Variabel](lesson/variabel.md)
|
||||||
3. [Uji Coba Flowchart](lesson/flowchart_test.md)
|
3. [Uji Coba Flowchart](lesson/flowchart_test.md)
|
||||||
4. [Uji Coba Kuis](lesson/quiz_test.md)
|
4. [Uji Coba Kuis](lesson/quiz_test.md)
|
||||||
5. [Rangkaian Dasar](lesson/rangkaian_dasar.md)
|
5. [Uji Coba LaTeX](lesson/latex_test.md)
|
||||||
6. [LED Blink](lesson/led_blink_arduino.md)
|
6. [Uji Coba Slide](lesson/test_slides.md)
|
||||||
7. [Hello Serial](lesson/hello_serial_arduino.md)
|
7. [Rangkaian Dasar](lesson/rangkaian_dasar.md)
|
||||||
8. [Button Input](lesson/button_input_arduino.md)
|
8. [LED Blink](lesson/led_blink_arduino.md)
|
||||||
9. [Traffic Light](lesson/traffic_light_arduino.md)
|
9. [Hello Serial](lesson/hello_serial_arduino.md)
|
||||||
|
10. [Button Input](lesson/button_input_arduino.md)
|
||||||
|
11. [Traffic Light](lesson/traffic_light_arduino.md)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from services.lesson_service import (
|
||||||
get_ordered_lessons_with_learning_objectives,
|
get_ordered_lessons_with_learning_objectives,
|
||||||
render_markdown_content,
|
render_markdown_content,
|
||||||
render_home_content,
|
render_home_content,
|
||||||
|
find_lesson_file,
|
||||||
)
|
)
|
||||||
from services.token_service import get_student_progress
|
from services.token_service import get_student_progress
|
||||||
|
|
||||||
|
|
@ -52,10 +53,9 @@ def api_lessons():
|
||||||
@lessons_bp.route('/lesson/<filename>.json')
|
@lessons_bp.route('/lesson/<filename>.json')
|
||||||
def api_lesson(filename):
|
def api_lesson(filename):
|
||||||
"""Return single lesson data as JSON."""
|
"""Return single lesson data as JSON."""
|
||||||
safe_filename = secure_filename(filename)
|
full_filename = filename if filename.endswith('.md') else f'{filename}.md'
|
||||||
full_filename = safe_filename if safe_filename.endswith('.md') else f'{safe_filename}.md'
|
file_path = find_lesson_file(full_filename)
|
||||||
file_path = os.path.join(CONTENT_DIR, full_filename)
|
if not file_path:
|
||||||
if not os.path.exists(file_path):
|
|
||||||
return jsonify({'error': 'Lesson not found'}), 404
|
return jsonify({'error': 'Lesson not found'}), 404
|
||||||
|
|
||||||
parsed_data = render_markdown_content(file_path)
|
parsed_data = render_markdown_content(file_path)
|
||||||
|
|
@ -215,9 +215,8 @@ def api_lesson(filename):
|
||||||
@lessons_bp.route('/get-key-text/<filename>')
|
@lessons_bp.route('/get-key-text/<filename>')
|
||||||
def get_key_text(filename):
|
def get_key_text(filename):
|
||||||
"""Get the key text for a specific lesson."""
|
"""Get the key text for a specific lesson."""
|
||||||
safe_filename = secure_filename(filename)
|
file_path = find_lesson_file(filename)
|
||||||
file_path = os.path.join(CONTENT_DIR, safe_filename)
|
if not file_path:
|
||||||
if not os.path.exists(file_path):
|
|
||||||
return jsonify({'success': False, 'error': 'Lesson not found'}), 404
|
return jsonify({'success': False, 'error': 'Lesson not found'}), 404
|
||||||
|
|
||||||
parsed_data = render_markdown_content(file_path)
|
parsed_data = render_markdown_content(file_path)
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,19 @@ def _parse_lesson_links(home_content):
|
||||||
# Lesson listing
|
# Lesson listing
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@lru_cache(maxsize=128)
|
||||||
|
def find_lesson_file(filename):
|
||||||
|
"""Recursively search for filename in CONTENT_DIR and return its full path."""
|
||||||
|
# Security: Prevent directory traversal
|
||||||
|
if '/' in filename or '\\' in filename:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for root, _, files in os.walk(CONTENT_DIR):
|
||||||
|
if filename in files:
|
||||||
|
return os.path.join(root, filename)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=32)
|
@lru_cache(maxsize=32)
|
||||||
def get_lessons():
|
def get_lessons():
|
||||||
"""Get lessons from the Available_Lessons section in home.md."""
|
"""Get lessons from the Available_Lessons section in home.md."""
|
||||||
|
|
@ -52,8 +65,8 @@ def get_lessons():
|
||||||
return lessons
|
return lessons
|
||||||
|
|
||||||
for link_text, filename in _parse_lesson_links(home_content):
|
for link_text, filename in _parse_lesson_links(home_content):
|
||||||
file_path = os.path.join(CONTENT_DIR, filename)
|
file_path = find_lesson_file(filename)
|
||||||
if not os.path.exists(file_path):
|
if not file_path:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
with open(file_path, 'r', encoding='utf-8') as f:
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
|
@ -92,8 +105,8 @@ def get_lesson_names():
|
||||||
|
|
||||||
names = []
|
names = []
|
||||||
for _link_text, filename in _parse_lesson_links(home_content):
|
for _link_text, filename in _parse_lesson_links(home_content):
|
||||||
file_path = os.path.join(CONTENT_DIR, filename)
|
file_path = find_lesson_file(filename)
|
||||||
if os.path.exists(file_path):
|
if file_path:
|
||||||
names.append(filename.replace('.md', ''))
|
names.append(filename.replace('.md', ''))
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
@ -107,8 +120,8 @@ def get_lessons_with_learning_objectives():
|
||||||
return lessons
|
return lessons
|
||||||
|
|
||||||
for link_text, filename in _parse_lesson_links(home_content):
|
for link_text, filename in _parse_lesson_links(home_content):
|
||||||
file_path = os.path.join(CONTENT_DIR, filename)
|
file_path = find_lesson_file(filename)
|
||||||
if not os.path.exists(file_path):
|
if not file_path:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
with open(file_path, 'r', encoding='utf-8') as f:
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue