update cara menampilkan available leasson tergantung dari home.md, menampilkan tombol review code apabila leasson telah complete

master
a2nr 2026-01-18 22:40:31 +07:00
parent 767678bb51
commit d59eae3bd0
4 changed files with 229 additions and 99 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
test/__pycache__/locustfile.cpython-311.pyc
__pycache__

112
app.py
View File

@ -60,25 +60,44 @@ Talisman(app,
)
def get_lessons():
"""Get all lesson files from the content directory"""
lesson_files = glob.glob(os.path.join(CONTENT_DIR, "*.md"))
"""Get lessons from the Available_Lessons section in home.md"""
lessons = []
for file_path in lesson_files:
filename = os.path.basename(file_path)
# Skip home.md as it's not a lesson
if filename == "home.md":
continue
# Read home content to get the lesson list
home_file_path = os.path.join(CONTENT_DIR, "home.md")
if not os.path.exists(home_file_path):
return lessons # Return empty list if home.md doesn't exist
with open(home_file_path, 'r', encoding='utf-8') as f:
home_content = f.read()
# Split content to get only the lesson list part
parts = home_content.split('---Available_Lessons---')
if len(parts) > 1:
lesson_list_content = parts[1]
# Find lesson links in the lesson list content
import re
# Look for markdown links that point to lessons
lesson_links = re.findall(r'\[([^\]]+)\]\((?:lesson/)?([^\)]+)\)', lesson_list_content)
for link_text, filename in lesson_links:
file_path = os.path.join(CONTENT_DIR, filename)
# Only add the lesson if the file actually exists
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Extract title from first line if it starts with #
lines = content.split('\n')
title = "Untitled"
title = link_text # Use the link text as title
description = "Learn C programming concepts with practical examples."
for i, line in enumerate(lines):
if line.startswith('# '):
if line.startswith('# ') and title == link_text:
# If title wasn't set from link text, use the heading
if title == "Untitled" or title == link_text:
title = line[2:].strip()
# Look for a line after the title that might be a description
elif title != "Untitled" and line.strip() != "" and not line.startswith('#') and i < 10:
@ -98,15 +117,32 @@ def get_lessons():
return lessons
def get_lesson_names():
"""Get all lesson names from the content directory (excluding home.md)"""
lesson_files = glob.glob(os.path.join(CONTENT_DIR, "*.md"))
"""Get lesson names from the Available_Lessons section in home.md"""
lesson_names = []
for file_path in lesson_files:
filename = os.path.basename(file_path)
# Skip home.md as it's not a lesson
if filename == "home.md":
continue
# Read home content to get the lesson list
home_file_path = os.path.join(CONTENT_DIR, "home.md")
if not os.path.exists(home_file_path):
return lesson_names # Return empty list if home.md doesn't exist
with open(home_file_path, 'r', encoding='utf-8') as f:
home_content = f.read()
# Split content to get only the lesson list part
parts = home_content.split('---Available_Lessons---')
if len(parts) > 1:
lesson_list_content = parts[1]
# Find lesson links in the lesson list content
import re
# Look for markdown links that point to lessons
lesson_links = re.findall(r'\[([^\]]+)\]\((?:lesson/)?([^\)]+)\)', lesson_list_content)
for link_text, filename in lesson_links:
file_path = os.path.join(CONTENT_DIR, filename)
# Only add the lesson name if the file actually exists
if os.path.exists(file_path):
lesson_names.append(filename.replace('.md', ''))
return lesson_names
@ -238,21 +274,38 @@ def get_ordered_lessons():
def get_lessons_with_learning_objectives():
"""Get all lesson files from the content directory with learning objectives as descriptions"""
lesson_files = glob.glob(os.path.join(CONTENT_DIR, "*.md"))
"""Get lessons from the Available_Lessons section in home.md with learning objectives as descriptions"""
lessons = []
for file_path in lesson_files:
filename = os.path.basename(file_path)
# Skip home.md as it's not a lesson
if filename == "home.md":
continue
# Read home content to get the lesson list
home_file_path = os.path.join(CONTENT_DIR, "home.md")
if not os.path.exists(home_file_path):
return lessons # Return empty list if home.md doesn't exist
with open(home_file_path, 'r', encoding='utf-8') as f:
home_content = f.read()
# Split content to get only the lesson list part
parts = home_content.split('---Available_Lessons---')
if len(parts) > 1:
lesson_list_content = parts[1]
# Find lesson links in the lesson list content
import re
# Look for markdown links that point to lessons
lesson_links = re.findall(r'\[([^\]]+)\]\((?:lesson/)?([^\)]+)\)', lesson_list_content)
for link_text, filename in lesson_links:
file_path = os.path.join(CONTENT_DIR, filename)
# Only add the lesson if the file actually exists
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Extract title from first line if it starts with #
lines = content.split('\n')
title = "Untitled"
title = link_text # Use the link text as title
description = "Learn C programming concepts with practical examples."
# Look for lesson info section to extract learning objectives
@ -268,7 +321,6 @@ def get_lessons_with_learning_objectives():
objectives_section = lesson_info_section[objectives_start:]
# Find the objectives list
import re
# Look for bullet points after Learning Objectives
objective_matches = re.findall(r'- ([^\n]+)', objectives_section)
@ -293,7 +345,9 @@ def get_lessons_with_learning_objectives():
else:
# If no lesson info section, use the original method
for i, line in enumerate(lines):
if line.startswith('# '):
if line.startswith('# ') and title == link_text:
# If title wasn't set from link text, use the heading
if title == "Untitled" or title == link_text:
title = line[2:].strip()
break
@ -467,9 +521,9 @@ def render_markdown_content(file_path):
lesson_content = parts[0] if len(parts) > 0 else lesson_content
exercise_content = parts[1] if len(parts) > 1 else ""
lesson_html = markdown.markdown(lesson_content, extensions=['fenced_code', 'codehilite', 'tables'])
exercise_html = markdown.markdown(exercise_content, extensions=['fenced_code', 'codehilite', 'tables']) if exercise_content else ""
lesson_info_html = markdown.markdown(lesson_info, extensions=['fenced_code', 'codehilite', 'tables']) if lesson_info else ""
lesson_html = markdown.markdown(lesson_content, extensions=['fenced_code', 'codehilite', 'tables', 'nl2br', 'toc'])
exercise_html = markdown.markdown(exercise_content, extensions=['fenced_code', 'codehilite', 'tables', 'nl2br', 'toc']) if exercise_content else ""
lesson_info_html = markdown.markdown(lesson_info, extensions=['fenced_code', 'codehilite', 'tables', 'nl2br', 'toc']) if lesson_info else ""
return lesson_html, exercise_html, expected_output, lesson_info_html, initial_code, solution_code, key_text

View File

@ -44,6 +44,40 @@ body {
margin-bottom: 20px;
}
/* Ensure proper list styling - with important to override Bootstrap */
.lesson-content ul {
list-style-type: disc !important;
padding-left: 20px !important;
}
.lesson-content ol {
list-style-type: decimal !important;
padding-left: 20px !important;
}
/* More specific selector to override Bootstrap styles */
.lesson-content ul li {
list-style-type: disc !important;
display: list-item !important;
}
.lesson-content ol li {
list-style-type: decimal !important;
display: list-item !important;
}
/* Alternative approach using before pseudo-element for custom bullets */
.lesson-content ul li {
position: relative;
padding-left: 20px;
}
.lesson-content ul li:before {
content: "• ";
position: absolute;
left: 0;
}
.lesson-content blockquote {
margin-top: 20px;
margin-bottom: 20px;

View File

@ -83,7 +83,7 @@
<i class="fas fa-moon"></i> Dark
</button>
<button id="solution-code" class="btn btn-sm btn-outline-light me-2 d-none">
<i class="fas fa-lightbulb"></i> Show Solution
<i class="fas fa-lightbulb"></i> <span id="solution-button-text">Show Solution</span>
</button>
<button id="run-code" class="btn btn-sm btn-success me-2">
<i class="fas fa-play"></i> Run
@ -730,6 +730,11 @@
}
}
});
// Show solution button with "Review Solution" text after lesson completion
if (window.updateSolutionButtonVisibility) {
window.updateSolutionButtonVisibility(true);
}
} else {
console.error('Failed to track progress:', data.message);
}
@ -840,6 +845,11 @@
}
}
});
// Show solution button with "Review Solution" text after lesson completion
if (window.updateSolutionButtonVisibility) {
window.updateSolutionButtonVisibility(true);
}
} else {
console.error('Failed to track progress:', data.message);
}
@ -901,14 +911,46 @@
outputDiv.classList.add('d-none');
});
// Add solution button functionality
if (solutionButton && solutionCode && solutionCode !== "None" && solutionCode !== "") {
// Check if solution code exists
if (solutionCode && solutionCode !== "None" && solutionCode !== "") {
// Check if the lesson is completed and show/hide button accordingly
const isLessonCompleted = {{ lesson_completed | tojson }};
if (isLessonCompleted) {
// Show the solution button and set text to "Review Solution" if the lesson is completed
solutionButton.classList.remove('d-none');
document.getElementById('solution-button-text').textContent = 'Review Solution';
} else {
// Hide the solution button if the lesson is not completed
solutionButton.classList.add('d-none');
}
solutionButton.addEventListener('click', function() {
codeEditor.value = solutionCode;
updateLineNumbers();
});
}
// Function to update solution button visibility and text based on completion status
function updateSolutionButtonVisibility(isCompleted) {
if (isCompleted) {
solutionButton.classList.remove('d-none');
document.getElementById('solution-button-text').textContent = 'Review Solution';
} else {
solutionButton.classList.add('d-none');
}
}
// Function to handle lesson completion and update UI
function handleLessonCompletion() {
// Show solution button with "Review Solution" text after lesson completion
updateSolutionButtonVisibility(true);
}
// Expose the functions globally so they can be called from other parts of the code
window.updateSolutionButtonVisibility = updateSolutionButtonVisibility;
window.handleLessonCompletion = handleLessonCompletion;
// Function to apply dark theme
function applyDarkTheme() {
codeEditor.classList.add('code-editor-dark');