feat: add markdown link support for lesson prerequisites

- Update lesson_service.py to parse markdown link format [title](path)
- Convert all lesson files to use markdown link format for prerequisites
- Backend now extracts slug directly from link path
- Supports both plain text and markdown link formats
This commit is contained in:
a2nr 2026-07-18 07:31:10 +07:00
parent cf3131bed3
commit d6bb2073ac
9 changed files with 51 additions and 31 deletions

View File

@ -8,9 +8,9 @@ Pelajaran Arduino: Membaca input tombol dan mengontrol LED.
- Menghubungkan push button dan LED ke Arduino - Menghubungkan push button dan LED ke Arduino
**Prerequisites:** **Prerequisites:**
- Hello, World! - [Hello, World!](lesson/hello_world.md)
- LED Blink - [LED Blink](lesson/led_blink_arduino.md)
- Hello Serial - [Hello Serial](lesson/hello_serial_arduino.md)
---END_LESSON_INFO--- ---END_LESSON_INFO---
# Button Input — Membaca Tombol # Button Input — Membaca Tombol

View File

@ -8,9 +8,8 @@ Pelajaran Arduino: Komunikasi Serial Monitor dan debugging.
- Menggunakan `millis()` untuk tracking waktu - Menggunakan `millis()` untuk tracking waktu
**Prerequisites:** **Prerequisites:**
- Hello, World! - [Hello, World!](lesson/hello_world.md)
---END_LESSON_INFO--- ---END_LESSON_INFO---
# Hello Serial — Komunikasi Serial Arduino # Hello Serial — Komunikasi Serial Arduino
Serial Monitor adalah **alat debugging utama** saat bekerja dengan Arduino. Serial Monitor adalah **alat debugging utama** saat bekerja dengan Arduino.

View File

@ -7,9 +7,8 @@ Pelajaran Arduino: Mengedipkan LED menggunakan simulator Velxio.
- Menggunakan `Serial.print()` untuk debugging - Menggunakan `Serial.print()` untuk debugging
**Prerequisites:** **Prerequisites:**
- Hello, World! - [Hello, World!](lesson/hello_world.md)
---END_LESSON_INFO--- ---END_LESSON_INFO---
# LED Blink dengan Arduino # LED Blink dengan Arduino
Proyek pertama setiap programmer Arduino: **mengedipkan LED!** Proyek pertama setiap programmer Arduino: **mengedipkan LED!**

View File

@ -8,10 +8,10 @@ Pelajaran Arduino: Membuat simulasi lampu lalu lintas dengan 3 LED.
- Menggunakan `const int` untuk nama pin yang mudah dibaca - Menggunakan `const int` untuk nama pin yang mudah dibaca
**Prerequisites:** **Prerequisites:**
- Hello, World! - [Hello, World!](lesson/hello_world.md)
- LED Blink - [LED Blink](lesson/led_blink_arduino.md)
- Hello Serial - [Hello Serial](lesson/hello_serial_arduino.md)
- Button Input - [Button Input](lesson/button_input_arduino.md)
---END_LESSON_INFO--- ---END_LESSON_INFO---
# Traffic Light — Lampu Lalu Lintas # Traffic Light — Lampu Lalu Lintas

View File

@ -7,8 +7,8 @@ Pelajaran hybrid: Pemrograman C + Simulator Rangkaian Elektronika.
- Menggunakan simulator rangkaian - Menggunakan simulator rangkaian
**Prerequisites:** **Prerequisites:**
- Hello, World! - [Hello, World!](lesson/hello_world.md)
- Variabel - [Variabel](lesson/variabel.md)
---END_LESSON_INFO--- ---END_LESSON_INFO---
# Rangkaian Voltage Divider # Rangkaian Voltage Divider

View File

@ -5,7 +5,7 @@
- Membuat siklus perulangan (LOOP) untuk pemantauan berkelanjutan. - Membuat siklus perulangan (LOOP) untuk pemantauan berkelanjutan.
**Prerequisites:** **Prerequisites:**
- Variabel - [Variabel](lesson/variabel.md)
**Challenge:** **Challenge:**
Buatlah flowchart untuk **Sistem Penyiraman Tanaman Otomatis**. Buatlah flowchart untuk **Sistem Penyiraman Tanaman Otomatis**.

View File

@ -4,7 +4,7 @@
- Belajar menggunakan printf untuk menampilkan output - Belajar menggunakan printf untuk menampilkan output
**Prerequisites:** **Prerequisites:**
- Tidak ada persyaratan khusus - Tidak ada
---END_LESSON_INFO--- ---END_LESSON_INFO---
# Hello, World! # Hello, World!

View File

@ -5,7 +5,7 @@
- Mengenal tipe data dasar: int, float, char - Mengenal tipe data dasar: int, float, char
**Prerequisites:** **Prerequisites:**
- Hello, World! - [Hello, World!](lesson/hello_world.md)
---END_LESSON_INFO--- ---END_LESSON_INFO---
# Variabel dalam C # Variabel dalam C

View File

@ -174,18 +174,31 @@ def get_lessons_with_learning_objectives():
prereq_start = lesson_info_section.find('**Prerequisites:**') prereq_start = lesson_info_section.find('**Prerequisites:**')
if prereq_start != -1: if prereq_start != -1:
prereq_section = lesson_info_section[prereq_start + len('**Prerequisites:**'):] prereq_section = lesson_info_section[prereq_start + len('**Prerequisites:**'):]
# Look for bullet points # Look for bullet points - support both plain text and markdown link format
prerequisite_titles = re.findall(r'- ([^\n]+)', prereq_section) # Plain text: - Hello, World!
if not prerequisite_titles: # Markdown link: - [Hello, World!](lesson/hello_world.md)
# Fallback to lines until next bold or end bullet_lines = re.findall(r'- ([^\n]+)', prereq_section)
next_bold = prereq_section.find('**')
if next_bold != -1:
prereq_section = prereq_section[:next_bold]
prerequisite_titles = [l.strip() for l in prereq_section.split('\n') if l.strip()]
prerequisite_slugs = []
for bullet in bullet_lines:
bullet = bullet.strip()
# Filter out "None" or "Tidak ada" # Filter out "None" or "Tidak ada"
prerequisite_titles = [t.strip() for t in prerequisite_titles if bullet.lower() in ('tidak ada', 'none', '-', ''):
if t.strip().lower() not in ('tidak ada', 'none', '-', '')] continue
# Check if it's a markdown link format [title](path)
md_link_match = re.match(r'\[([^\]]+)\]\(([^)]+)\)', bullet)
if md_link_match:
# Extract slug from the link path
link_path = md_link_match.group(2)
# Handle paths like lesson/hello_world.md or just hello_world.md
slug = link_path.replace('.md', '').split('/')[-1]
prerequisite_slugs.append(slug)
else:
# Plain text - keep as title for later resolution
prerequisite_slugs.append(bullet)
prerequisite_titles = prerequisite_slugs
content_after_info = content[lesson_info_end + len('---END_LESSON_INFO---'):].strip() content_after_info = content[lesson_info_end + len('---END_LESSON_INFO---'):].strip()
for line in content_after_info.split('\n'): for line in content_after_info.split('\n'):
@ -232,9 +245,18 @@ def get_ordered_lessons_with_learning_objectives(progress=None):
else: else:
lesson['completed'] = False lesson['completed'] = False
# Resolve prerequisite titles to slugs # Resolve prerequisites - now contains slugs directly from markdown links
titles = lesson.get('prerequisite_titles', []) # or still contains plain text titles that need resolution
lesson['prerequisites'] = [title_to_slug[t] for t in titles if t in title_to_slug] items = lesson.get('prerequisite_titles', [])
resolved_prereqs = []
for item in items:
# If it's already a valid slug (exists in all_lessons), use it directly
if item in title_to_slug.values():
resolved_prereqs.append(item)
# Otherwise try to resolve via title mapping
elif item in title_to_slug:
resolved_prereqs.append(title_to_slug[item])
lesson['prerequisites'] = resolved_prereqs
return lesson return lesson
if lesson_links: if lesson_links: