update multi language programing (currently c and python)

master
a2nr 2026-01-14 10:35:20 +07:00
parent 869870a598
commit 4939c4edd5
12 changed files with 601 additions and 109 deletions

View File

@ -5,12 +5,14 @@ A web-based learning management system for C programming with interactive exerci
## Features ## Features
- View lessons written in Markdown format - View lessons written in Markdown format
- Interactive C code editor with compilation and execution - Interactive code editor with compilation and execution for multiple programming languages
- Real-time feedback on code compilation and execution - Real-time feedback on code compilation and execution
- No database required - content stored as Markdown files - No database required - content stored as Markdown files
- Student token-based progress tracking system - Student token-based progress tracking system
- No authentication required - ready to use out of the box - No authentication required - ready to use out of the box
- Containerized with Podman for easy deployment - Containerized with Podman for easy deployment
- Support for multiple programming languages (C, Python, and extensible for others)
- Configurable default programming language via environment variables
## Prerequisites ## Prerequisites
@ -747,18 +749,58 @@ Monitor the LMS server's resource usage (CPU, memory, disk I/O) during load test
- Database performance (if one is added in the future) - Database performance (if one is added in the future)
- Container resource limits - Container resource limits
The included `locustfile.py` simulates the following user behaviors: ## Multi-Language Programming Support
- Browsing the home page
- Viewing lessons
- Compiling C code
- Validating student tokens
- Logging in with tokens
- Tracking student progress
### Monitoring Performance The system now supports multiple programming languages with a modular compiler architecture. Currently supports C and Python with easy extensibility for additional languages.
Monitor the LMS server's resource usage (CPU, memory, disk I/O) during load testing to identify potential bottlenecks. Pay attention to: ### Supported Languages
- Response times for API requests
- Compilation performance under load - **C**: Compiled using GCC with standard compilation and execution workflow
- Database performance (if one is added in the future) - **Python**: Interpreted using Python 3 with syntax checking and execution
- Container resource limits
### Configuration
The default programming language can be configured via environment variables in your `.env` file:
```
# To use C as default language (default setting)
DEFAULT_PROGRAMMING_LANGUAGE=c
# To use Python as default language
DEFAULT_PROGRAMMING_LANGUAGE=python
```
### Extending to Additional Languages
The system is designed to be easily extended with additional programming languages:
1. Create a new compiler class that inherits from `BaseCompiler` in the `compiler/` directory
2. Implement the `compile()` and `run()` methods for your language
3. Register the new compiler in the `CompilerFactory` in `compiler/__init__.py`
4. Update the UI if needed to support the new language
Example structure for a new language compiler:
```python
# compiler/new_language_compiler.py
from .base_compiler import BaseCompiler
import subprocess
class NewLanguageCompiler(BaseCompiler):
def __init__(self):
super().__init__("NewLanguage", ".nl") # Replace with appropriate extension
def compile(self, file_path, timeout=10):
# Implement compilation logic for your language
pass
def run(self, file_path, timeout=5):
# Implement execution logic for your language
pass
```
### Language-Specific Features
- The code editor header now dynamically displays the active programming language
- File extensions are automatically adjusted based on the selected language
- The system maintains backward compatibility with existing C lessons
- All language compilation follows the same security and timeout constraints

78
app.py
View File

@ -22,6 +22,9 @@ app = Flask(__name__)
# Configure logging # Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Import compiler module after app initialization to avoid circular imports
from compiler import compiler_factory
# Load configuration from environment variables with defaults # Load configuration from environment variables with defaults
CONTENT_DIR = os.environ.get('CONTENT_DIR', 'content') CONTENT_DIR = os.environ.get('CONTENT_DIR', 'content')
STATIC_DIR = os.environ.get('STATIC_DIR', 'static') STATIC_DIR = os.environ.get('STATIC_DIR', 'static')
@ -571,6 +574,10 @@ int main() {
# Get ordered lessons for the sidebar # Get ordered lessons for the sidebar
ordered_lessons = get_ordered_lessons_with_learning_objectives(progress) ordered_lessons = get_ordered_lessons_with_learning_objectives(progress)
# Get the programming language from environment variable
programming_language = os.environ.get('DEFAULT_PROGRAMMING_LANGUAGE', 'c').lower()
language_display_name = compiler_factory.get_language_display_name(programming_language)
return render_template('lesson.html', return render_template('lesson.html',
lesson_content=lesson_html, lesson_content=lesson_html,
exercise_content=exercise_html, exercise_content=exercise_html,
@ -587,20 +594,25 @@ int main() {
ordered_lessons=ordered_lessons, ordered_lessons=ordered_lessons,
app_bar_title=APP_BAR_TITLE, app_bar_title=APP_BAR_TITLE,
copyright_text=COPYRIGHT_TEXT, copyright_text=COPYRIGHT_TEXT,
page_title_suffix=PAGE_TITLE_SUFFIX) page_title_suffix=PAGE_TITLE_SUFFIX,
language=programming_language,
language_display_name=language_display_name)
@app.route('/compile', methods=['POST']) @app.route('/compile', methods=['POST'])
def compile_code(): def compile_code():
"""Compile and run C code submitted by the user""" """Compile and run code submitted by the user in the selected programming language"""
try: try:
code = None code = None
language = None
# Try to get code from JSON data # Try to get code and language from JSON data
if request.content_type and 'application/json' in request.content_type: if request.content_type and 'application/json' in request.content_type:
try: try:
json_data = request.get_json(force=True) json_data = request.get_json(force=True)
if json_data and 'code' in json_data: if json_data:
code = json_data['code'] code = json_data.get('code', '')
language = json_data.get('language', '') # Get language from request
except Exception as e: except Exception as e:
# Log the error for debugging # Log the error for debugging
print(f"JSON parsing error: {e}") print(f"JSON parsing error: {e}")
@ -609,6 +621,7 @@ def compile_code():
# If not found in JSON, try form data # If not found in JSON, try form data
if not code: if not code:
code = request.form.get('code', '') code = request.form.get('code', '')
language = request.form.get('language', '') # Get language from form
if not code: if not code:
return jsonify({ return jsonify({
@ -617,58 +630,11 @@ def compile_code():
'error': 'No code provided' 'error': 'No code provided'
}) })
# Create a temporary file for the C code # Get the appropriate compiler based on the language
with tempfile.NamedTemporaryFile(mode='w', suffix='.c', delete=False) as temp_c: compiler = compiler_factory.get_compiler(language)
temp_c.write(code)
temp_c_path = temp_c.name
# Create a temporary file for the executable # Compile and run the code using the selected compiler
temp_exe_path = temp_c_path.replace('.c', '') result = compiler.compile_and_run(code)
# Compile the C code
compile_result = subprocess.run(
['gcc', temp_c_path, '-o', temp_exe_path],
capture_output=True,
text=True,
timeout=10
)
if compile_result.returncode != 0:
# Compilation failed
result = {
'success': False,
'output': compile_result.stdout, # Include any stdout if available
'error': compile_result.stderr # Show GCC error messages
}
else:
# Compilation succeeded, run the program
try:
run_result = subprocess.run(
[temp_exe_path],
capture_output=True,
text=True,
timeout=5
)
result = {
'success': True,
'output': run_result.stdout,
'error': run_result.stderr if run_result.stderr else None
}
except subprocess.TimeoutExpired:
result = {
'success': False,
'output': '',
'error': 'Program execution timed out'
}
# Clean up temporary files
try:
os.remove(temp_c_path)
if os.path.exists(temp_exe_path):
os.remove(temp_exe_path)
except:
pass # Ignore cleanup errors
return jsonify(result) return jsonify(result)

62
compiler/__init__.py Normal file
View File

@ -0,0 +1,62 @@
"""
Compiler Factory for managing different language compilers
"""
import os
from .c_compiler import CCompiler
from .python_compiler import PythonCompiler
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'])
def get_compiler(self, language=None):
"""
Get a compiler instance for the specified language
:param language: Language identifier ('c', 'python', etc.)
:return: Compiler instance
"""
if language is None:
return self.default_compiler
language = language.lower()
if language in self.compilers:
return self.compilers[language]
else:
# Return default compiler if requested language is not available
return self.default_compiler
def get_available_languages(self):
"""
Get list of available programming languages
:return: List of available language identifiers
"""
return list(self.compilers.keys())
def get_language_display_name(self, language):
"""
Get display name for a language
:param language: Language identifier
:return: Display name for the language
"""
display_names = {
'c': 'C',
'python': 'Python'
}
return display_names.get(language, language.capitalize())
# Global compiler factory instance
compiler_factory = CompilerFactory()

69
compiler/base_compiler.py Normal file
View File

@ -0,0 +1,69 @@
"""
Base compiler class for the modular compilation system
"""
import os
import subprocess
import tempfile
from abc import ABC, abstractmethod
class BaseCompiler(ABC):
"""
Abstract base class for all compilers
"""
def __init__(self, language, file_extension):
self.language = language
self.file_extension = file_extension
@abstractmethod
def compile(self, code, timeout=10):
"""
Compile the code and return the result
:param code: Source code to compile
:param timeout: Compilation timeout in seconds
:return: Dictionary with success, output, and error fields
"""
pass
@abstractmethod
def run(self, file_path, timeout=5):
"""
Run the compiled code and return the result
:param file_path: Path to the compiled file
:param timeout: Execution timeout in seconds
:return: Dictionary with success, output, and error fields
"""
pass
def compile_and_run(self, code, compile_timeout=10, run_timeout=5):
"""
Compile and run the code in one step
:param code: Source code to compile and run
:param compile_timeout: Compilation timeout in seconds
:param run_timeout: Execution timeout in seconds
:return: Dictionary with success, output, and error fields
"""
# Create a temporary file for the source code
with tempfile.NamedTemporaryFile(mode='w', suffix=self.file_extension, delete=False) as temp_source:
temp_source.write(code)
temp_source_path = temp_source.name
try:
# Compile the code
compile_result = self.compile(temp_source_path, compile_timeout)
if not compile_result['success']:
return compile_result
# Run the code
run_result = self.run(temp_source_path, run_timeout)
return run_result
finally:
# Clean up temporary files
try:
os.remove(temp_source_path)
except:
pass # Ignore cleanup errors

114
compiler/c_compiler.py Normal file
View File

@ -0,0 +1,114 @@
"""
C Compiler implementation for the modular compilation system
"""
import subprocess
import os
from .base_compiler import BaseCompiler
class CCompiler(BaseCompiler):
"""
Compiler class for C programming language
"""
def __init__(self):
super().__init__("C", ".c")
def compile(self, file_path, timeout=10):
"""
Compile C code
:param file_path: Path to the C source file
:param timeout: Compilation timeout in seconds
:return: Dictionary with success, output, and error fields
"""
# Create a temporary file for the executable
temp_exe_path = file_path.replace('.c', '')
try:
# Compile the C code
compile_result = subprocess.run(
['gcc', file_path, '-o', temp_exe_path],
capture_output=True,
text=True,
timeout=timeout
)
if compile_result.returncode != 0:
# Compilation failed
return {
'success': False,
'output': compile_result.stdout,
'error': compile_result.stderr
}
else:
# Compilation succeeded
return {
'success': True,
'output': compile_result.stdout,
'error': compile_result.stderr if compile_result.stderr else None,
'executable_path': temp_exe_path
}
except subprocess.TimeoutExpired:
return {
'success': False,
'output': '',
'error': 'Compilation timed out'
}
except Exception as e:
return {
'success': False,
'output': '',
'error': f'Compilation error: {str(e)}'
}
def run(self, file_path, timeout=5):
"""
Run compiled C program
:param file_path: Path to the C source file (executable will be derived)
:param timeout: Execution timeout in seconds
:return: Dictionary with success, output, and error fields
"""
temp_exe_path = file_path.replace('.c', '')
if not os.path.exists(temp_exe_path):
return {
'success': False,
'output': '',
'error': 'Executable not found'
}
try:
# Run the compiled program
run_result = subprocess.run(
[temp_exe_path],
capture_output=True,
text=True,
timeout=timeout
)
result = {
'success': True,
'output': run_result.stdout,
'error': run_result.stderr if run_result.stderr else None
}
# Clean up the executable
try:
os.remove(temp_exe_path)
except:
pass # Ignore cleanup errors
return result
except subprocess.TimeoutExpired:
return {
'success': False,
'output': '',
'error': 'Program execution timed out'
}
except Exception as e:
return {
'success': False,
'output': '',
'error': f'Runtime error: {str(e)}'
}

View File

@ -0,0 +1,85 @@
"""
Python Compiler/Interpreter implementation for the modular compilation system
"""
import subprocess
import tempfile
import os
from .base_compiler import BaseCompiler
class PythonCompiler(BaseCompiler):
"""
Compiler/Interpreter class for Python programming language
"""
def __init__(self):
super().__init__("Python", ".py")
def compile(self, file_path, timeout=10):
"""
Check Python syntax (simulate compilation)
:param file_path: Path to the Python source file
:param timeout: Compilation timeout in seconds
:return: Dictionary with success, output, and error fields
"""
try:
# Use Python's compile function to check syntax
with open(file_path, 'r', encoding='utf-8') as f:
code = f.read()
# Attempt to compile the code to check for syntax errors
compile(code, file_path, 'exec')
# If we reach this point, syntax is valid
return {
'success': True,
'output': '',
'error': None
}
except SyntaxError as e:
return {
'success': False,
'output': '',
'error': f"SyntaxError: {e.msg} at line {e.lineno}"
}
except Exception as e:
return {
'success': False,
'output': '',
'error': f'Syntax check error: {str(e)}'
}
def run(self, file_path, timeout=5):
"""
Run Python code
:param file_path: Path to the Python source file
:param timeout: Execution timeout in seconds
:return: Dictionary with success, output, and error fields
"""
try:
# Run the Python script
run_result = subprocess.run(
['python3', file_path],
capture_output=True,
text=True,
timeout=timeout
)
return {
'success': True,
'output': run_result.stdout,
'error': run_result.stderr if run_result.stderr else None
}
except subprocess.TimeoutExpired:
return {
'success': False,
'output': '',
'error': 'Program execution timed out'
}
except Exception as e:
return {
'success': False,
'output': '',
'error': f'Runtime error: {str(e)}'
}

View File

@ -19,6 +19,10 @@ APP_BAR_TITLE=Belajar Pemrograman C
COPYRIGHT_TEXT=Sistem Pembelajaran Pemrograman C © 2025 COPYRIGHT_TEXT=Sistem Pembelajaran Pemrograman C © 2025
PAGE_TITLE_SUFFIX=Belajar Pemrograman C PAGE_TITLE_SUFFIX=Belajar Pemrograman C
# Programming Language Configuration
DEFAULT_PROGRAMMING_LANGUAGE=c
# To use Python as default language instead, uncomment the line below:
# DEFAULT_PROGRAMMING_LANGUAGE=python
# Server Configuration # Server Configuration
HOST=0.0.0.0 HOST=0.0.0.0

View File

@ -1,33 +0,0 @@
# Welcome to C Programming Learning System
This is a comprehensive learning platform designed to help you master the C programming language through interactive lessons and exercises.
## Learning Objectives
| Module | Objective | Skills Acquired |
|--------|-----------|-----------------|
| Introduction to C | Understand basic syntax and structure | Writing first C program |
| Variables & Data Types | Learn about different data types | Proper variable declaration |
| Control Structures | Master conditional statements and loops | Logic implementation |
| Functions | Create and use functions | Code organization |
| Arrays & Pointers | Work with complex data structures | Memory management |
## How to Use This System
1. **Browse Lessons**: Select from the available lessons on the left
2. **Read Content**: Study the lesson materials and examples
3. **Practice Coding**: Use the integrated code editor to write and test C code
4. **Complete Exercises**: Apply your knowledge to solve programming challenges
5. **Get Feedback**: See immediate results of your code execution
## Getting Started
Start with the "Introduction to C" lesson to begin your journey in C programming. Each lesson builds upon the previous one, so it's recommended to follow them in order.
Happy coding!
---Available_Lessons---
1. [Introduction to C Programming](lesson/introduction_to_c.md)
2. [Variables and Data Types in C](lesson/variables_and_data_types.md)

View File

@ -0,0 +1,52 @@
# Python Introduction Lesson
This lesson introduces the basics of Python programming.
## Variables and Data Types
Python is a high-level programming language known for its simplicity and readability.
```python
# This is a comment in Python
name = "John" # String variable
age = 25 # Integer variable
height = 5.9 # Float variable
is_student = True # Boolean variable
print(f"Name: {name}, Age: {age}, Height: {height}, Student: {is_student}")
```
## Basic Operations
You can perform basic arithmetic operations in Python:
```python
a = 10
b = 5
addition = a + b
subtraction = a - b
multiplication = a * b
division = a / b
print(f"Addition: {addition}")
print(f"Subtraction: {subtraction}")
print(f"Multiplication: {multiplication}")
print(f"Division: {division}")
```
---EXERCISE---
Write a Python program that prints "Hello, Python!" to the console.
---INITIAL_CODE---
print("Write your Python code here")
---END_INITIAL_CODE---
---EXPECTED_OUTPUT---
Hello, Python!
---END_EXPECTED_OUTPUT---
---SOLUTION_CODE---
print("Hello, Python!")
---END_SOLUTION_CODE---

View File

@ -0,0 +1,121 @@
---LESSON_INFO---
**Learning Objectives:**
- Memahami berbagai tipe data dalam bahasa C
- Belajar mendeklarasikan dan menginisialisasi variabel
- Mengenal batas-batas masing-masing tipe data
- Memahami perbedaan antara tipe data signed dan unsigned
**Prerequisites:**
- Dasar-dasar pemrograman
- Pemahaman tentang program Halo Dunia
---END_LESSON_INFO---
# Tipe Data dan Variabel dalam C
C memiliki beberapa jenis variabel, tetapi ada beberapa tipe dasar:
* Bilangan Bulat - bilangan bulat yang bisa positif atau negatif. Didefinisikan menggunakan `char`, `int`, `short`, `long` atau `long long`.
* Bilangan Bulat Tak Bertanda - bilangan bulat yang hanya bisa positif. Didefinisikan menggunakan `unsigned char`, `unsigned int`, `unsigned short`, `unsigned long` atau `unsigned long long`.
* Bilangan Pecahan - bilangan real (bilangan dengan pecahan). Didefinisikan menggunakan `float` dan `double`.
* Struktur - akan dijelaskan nanti, di bagian Struktur.
## Tipe Data dalam C
Jenis-jenis variabel yang berbeda menentukan batas-batasnya. Sebuah `char` bisa dari -128 hingga 127, sedangkan sebuah `long` bisa dari -2,147,483,648 hingga 2,147,483,647 (`long` dan tipe data numerik lainnya mungkin memiliki rentang lain di komputer yang berbeda, misalnya - dari 9,223,372,036,854,775,808 hingga 9,223,372,036,854,775,807 di komputer 64-bit).
Perhatikan bahwa C _tidak_ memiliki tipe boolean. Biasanya, itu didefinisikan menggunakan notasi berikut:
```c
#define BOOL char
#define FALSE 0
#define TRUE 1
```
C menggunakan array karakter untuk mendefinisikan string, dan akan dijelaskan di bagian String.
## Mendefinisikan variabel
Untuk angka, kita biasanya akan menggunakan tipe `int`. Di kebanyakan komputer saat ini, itu adalah bilangan 32-bit, yang berarti angkanya bisa dari -2,147,483,648 hingga 2,147,483,647.
Untuk mendefinisikan variabel `foo` dan `bar`, kita perlu menggunakan sintaks berikut:
```c
int foo;
int bar = 1;
```
Variabel `foo` bisa digunakan, tetapi karena kita tidak menginisialisasinya, kita tidak tahu apa yang ada di dalamnya. Variabel `bar` berisi angka 1.
Sekarang, kita bisa melakukan beberapa operasi matematika. Dengan mengasumsikan `a`, `b`, `c`, `d`, dan `e` adalah variabel, kita bisa menggunakan operator penjumlahan, pengurangan dan perkalian dalam notasi berikut, dan memberikan nilai baru ke `a`:
```c
int a = 0, b = 1, c = 2, d = 3, e = 4;
a = b - c + d * e;
printf("%d", a); /* akan mencetak 1-2+3*4 = 11 */
```
---
## Tabel Tipe Data dalam C
| Tipe | Ukuran (bit) | Rentang Nilai | Contoh |
|------|--------------|---------------|--------|
| char | 8 | -128 hingga 127 | `char grade = 'A';` |
| int | 32 | -2,147,483,648 hingga 2,147,483,647 | `int age = 25;` |
| short | 16 | -32,768 hingga 32,767 | `short year = 2023;` |
| long | 64 | -9,223,372,036,854,775,808 hingga 9,223,372,036,854,775,807 | `long population = 1000000L;` |
| float | 32 | ~7 digit desimal | `float price = 19.99f;` |
| double | 64 | ~15 digit desimal | `double pi = 3.14159;` |
| unsigned char | 8 | 0 hingga 255 | `unsigned char count = 100;` |
---EXERCISE---
# Latihan: Menjumlahkan Variabel
Di latihan berikutnya, Anda perlu membuat program yang mencetak jumlah dari angka `a`, `b`, dan `c`.
**Requirements:**
- Hitung jumlah dari variabel a, b, dan c
- Simpan hasilnya dalam variabel sum
- Pastikan tipe data yang digunakan sesuai
**Expected Output:**
```
The sum of a, b, and c is 12.750000.
```
Try writing your solution in the code editor below!
---EXPECTED_OUTPUT---
The sum of a, b, and c is 12.750000.
---END_EXPECTED_OUTPUT---
---INITIAL_CODE---
#include <stdio.h>
int main() {
int a = 3;
float b = 4.5;
double c = 5.25;
float sum;
/* Kode Anda di sini */
printf("The sum of a, b, and c is %f.", sum);
return 0;
}
---END_INITIAL_CODE---
---SOLUTION_CODE---
#include <stdio.h>
int main() {
int a = 3;
float b = 4.5;
double c = 5.25;
float sum;
sum = a + b + c;
printf("The sum of a, b, and c is %f.", sum);
return 0;
}
---END_SOLUTION_CODE---

View File

@ -4,6 +4,8 @@ services:
elemes: elemes:
build: . build: .
container_name: elemes container_name: elemes
ports:
- "5000:5000" # Expose port 5000 to the host
volumes: volumes:
- ../content:/app/content - ../content:/app/content
- ./static:/app/static - ./static:/app/static

View File

@ -72,7 +72,7 @@
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-between align-items-center" style="background-color: #4a76a8; color: white;"> <div class="card-header d-flex justify-content-between align-items-center" style="background-color: #4a76a8; color: white;">
<span><i class="fas fa-file-code me-2"></i>C code.c</span> <span><i class="fas fa-file-code me-2"></i>{{ language_display_name }} code.{{ 'py' if language == 'python' else 'c' }}</span>
<div> <div>
<button id="theme-toggle" class="btn btn-sm btn-outline-light me-2"> <button id="theme-toggle" class="btn btn-sm btn-outline-light me-2">
<i class="fas fa-moon"></i> Dark <i class="fas fa-moon"></i> Dark
@ -366,7 +366,7 @@
const code = codeEditor.value; const code = codeEditor.value;
if (!code.trim()) { if (!code.trim()) {
alert('Please enter some C code to run.'); alert('Please enter some code to run.');
return; return;
} }
@ -374,13 +374,21 @@
runButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Running...'; runButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Running...';
runButton.disabled = true; runButton.disabled = true;
// Create form data to send to the server // Get language from environment/config - default to C for backward compatibility
const formData = new FormData(); const language = '{{ language | default("c") }}'; // This would be passed from the backend
formData.append('code', code);
// Prepare data to send to the server
const requestData = {
code: code,
language: language
};
fetch('/compile', { fetch('/compile', {
method: 'POST', method: 'POST',
body: formData headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {