From 65b24b496a5c65cb9b9ff2956630c51d8f1b0211 Mon Sep 17 00:00:00 2001 From: a2nr Date: Fri, 2 Jan 2026 06:18:48 +0700 Subject: [PATCH] init --- Dockerfile | 22 + README.md | 252 +++++++++++ access_container.sh | 25 ++ app.py | 331 ++++++++++++++ assets/InitilizationofaVariable-660x330.png | Bin 0 -> 31022 bytes content/home.md | 33 ++ content/introduction_to_c.md | 123 ++++++ content/variables_and_data_types.md | 98 +++++ podman-compose.yml | 14 + requirements.txt | 3 + start.sh | 24 + static/style.css | 330 ++++++++++++++ stop.sh | 10 + templates/index.html | 66 +++ templates/lesson.html | 464 ++++++++++++++++++++ 15 files changed, 1795 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100755 access_container.sh create mode 100644 app.py create mode 100755 assets/InitilizationofaVariable-660x330.png create mode 100644 content/home.md create mode 100644 content/introduction_to_c.md create mode 100644 content/variables_and_data_types.md create mode 100644 podman-compose.yml create mode 100644 requirements.txt create mode 100755 start.sh create mode 100644 static/style.css create mode 100755 stop.sh create mode 100644 templates/index.html create mode 100644 templates/lesson.html diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0450b47 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM python:3.11-slim + +# Install gcc compiler for C code compilation +RUN apt-get update && \ + apt-get install -y gcc build-essential && \ + rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /app + +# Copy requirements and install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . + +# Expose port 5000 +EXPOSE 5000 + +# Run the application with Gunicorn +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "1", "app:app"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fbb1b2b --- /dev/null +++ b/README.md @@ -0,0 +1,252 @@ +# C Programming Learning Management System + +A web-based learning management system for C programming with interactive exercises and code compilation capabilities. + +## Features + +- View lessons written in Markdown format +- Interactive C code editor with compilation and execution +- Real-time feedback on code compilation and execution +- No database required - content stored as Markdown files +- No authentication required - ready to use out of the box +- Containerized with Podman for easy deployment + +## Prerequisites + +- Podman installed on your system +- Basic knowledge of Docker/Podman commands + +## Getting Started + +### Option 1: Using Podman Compose (Recommended) + +1. Navigate to the project directory: + ```bash + cd lms-c + ``` + +2. Build and start the container: + ```bash + podman-compose -f podman-compose.yml up --build + ``` + +3. Access the application at `http://localhost:5000` + +### Option 2: Using the Access Script + +1. Make the access script executable: + ```bash + chmod +x access_container.sh + ``` + +2. Run the access script: + ```bash + ./access_container.sh + ``` + + This will start the container and open a shell inside it. From there, you can run: + ```bash + python app.py + ``` + +### Option 3: Direct Podman Commands + +1. Build the image: + ```bash + podman build -t lms-c . + ``` + +2. Run the container: + ```bash + podman run -p 5000:5000 -v $(pwd)/content:/app/content -v $(pwd)/static:/app/static -v $(pwd)/templates:/app/templates lms-c + ``` + +## Adding New Lessons + +To add new lessons: + +1. Create a new Markdown file in the `content` directory with a `.md` extension +2. Structure your lesson with: + - Lesson content at the top (using standard Markdown syntax) + - A separator `---EXERCISE---` (optional) + - Exercise content at the bottom (optional) + +### Lesson Structure + +**With Exercise:** +```markdown +# Lesson Title + +Lesson content goes here... + +## Section + +More content... + +--- + +EXERCISE--- + +# Exercise Title + +Exercise instructions go here... + +Try writing your solution in the code editor below! +``` + +**Without Exercise:** +```markdown +# Lesson Title + +Lesson content goes here... + +## Section + +More content... +``` + +### Markdown Features Supported + +- Headers: `#`, `##`, `###` +- Code blocks: Use triple backticks (```c for C code highlighting) +- Lists: `-` for unordered, `1.` for ordered +- Bold/italic: `**bold**`, `*italic*` +- Links: `[text](url)` +- Tables: Using standard Markdown table syntax + +### Exercise Guidelines + +- Exercises are optional - lessons can exist without them +- When an exercise is provided, it will appear above the code editor +- If no exercise is provided, a message will indicate that users can still practice with the code editor +- The code editor is always available for practice regardless of whether an exercise is defined + +## How to Use + +1. Access the application at `http://localhost:5000` +2. Browse available lessons +3. Click on a lesson to view content and exercises +4. Use the code editor to write C code +5. Click "Run Code" to compile and execute your code +6. View the output in the output panel + +## Security Considerations + +- The application runs C code in a containerized environment +- Timeouts are implemented to prevent infinite loops +- File system access is limited to the application directory + +## Project Structure + +- `app.py`: Main Flask application +- `content/`: Directory for lesson Markdown files +- `templates/`: HTML templates +- `static/`: CSS, JavaScript, and other static assets +- `Dockerfile`: Container configuration +- `podman-compose.yml`: Podman Compose configuration +- `requirements.txt`: Python dependencies + +## Development + +To access the running container for development: + +```bash +podman exec -it /bin/bash +``` + +## Troubleshooting + +- If you get permission errors, make sure your user has access to Podman +- If the application doesn't start, check that port 5000 is available +- If code compilation fails, verify that the gcc compiler is available in the container + +## Stopping the Application + +To stop the application: + +### If running with Podman Compose: +```bash +# Stop the containers +podman-compose -f podman-compose.yml down + +# Or if you want to stop and remove containers, networks, and volumes: +podman-compose -f podman-compose.yml down -v +``` + +### If running with direct Podman command: +```bash +# Find the container name or ID +podman ps + +# Stop the container (replace with actual name) +podman stop lms-c-container + +# Remove the container after stopping (optional but recommended) +podman rm lms-c-container + +# Or do both in one command: +podman stop lms-c-container && podman rm lms-c-container +``` + +### Using the access script: +If you're currently in the container shell opened via `access_container.sh`, you can: +1. Exit the container shell (type `exit`) +2. Then stop the container from outside: + ```bash + podman stop lms-c-container + ``` + +### Troubleshooting: Port Already in Use + +If you get an error about the port already being in use, it means the container wasn't properly stopped. +First, check what's using the port: +```bash +podman ps +``` + +If you see the `lms-c-container` still running, stop it: +```bash +podman stop lms-c-container && podman rm lms-c-container +``` + +Or stop all running containers: +```bash +podman stop $(podman ps -q) +``` + +Then you can run the application again on the desired port. + +## API Endpoints + +- `GET /` - Main page with all lessons +- `GET /lesson/` - View a specific lesson +- `POST /compile` - Compile and run C code (expects JSON with "code" field) +- `GET /static/` - Serve static files + +## Example API Usage + +To compile and run C code via the API: + +```bash +curl -X POST http://localhost:5000/compile \ + -H "Content-Type: application/json" \ + -d '{"code": "#include \\n\\nint main() {\\n printf(\\"Hello, World!\\\\n\\");\\n return 0;\\n}"}' +``` + +Response: +```json +{ + "success": true, + "output": "Hello, World!\n", + "error": null +} +``` + +In case of compilation errors: +```json +{ + "success": false, + "output": "error message...", + "error": "Compilation failed" +} +``` \ No newline at end of file diff --git a/access_container.sh b/access_container.sh new file mode 100755 index 0000000..c60a265 --- /dev/null +++ b/access_container.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Script to access the LMS-C container for development +# This script will start the container (if not running) and open a shell + +echo "Starting LMS-C container and opening shell..." + +# Check if container is already running +if [ "$(podman ps -q -f name=lms-c-container)" ]; then + echo "Container is already running. Opening shell..." + podman exec -it lms-c-container /bin/bash +else + # Check if container exists but is stopped + if [ "$(podman ps -aq -f name=lms-c-container)" ]; then + echo "Starting existing container..." + podman start lms-c-container + sleep 2 + podman exec -it lms-c-container /bin/bash + else + echo "Building and starting container..." + podman build -t lms-c . && podman run -d -p 5000:5000 --name lms-c-container lms-c + sleep 5 # Wait for the application to start + podman exec -it lms-c-container /bin/bash + fi +fi \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..8e6b1f4 --- /dev/null +++ b/app.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 +""" +C Programming Learning Management System +A web-based application for learning C programming with exercise compilation +""" + +import os +import subprocess +import tempfile +import markdown +from flask import Flask, render_template, request, jsonify, send_from_directory +import glob + +app = Flask(__name__) + +# Configuration +CONTENT_DIR = 'content' +STATIC_DIR = 'static' +TEMPLATES_DIR = 'templates' + +def get_lessons(): + """Get all lesson files from the content directory""" + lesson_files = glob.glob(os.path.join(CONTENT_DIR, "*.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 + + 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" + description = "Learn C programming concepts with practical examples." + + for i, line in enumerate(lines): + if line.startswith('# '): + 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: + # Take the first substantial line as description + clean_line = line.strip().replace('#', '').strip() + if len(clean_line) > 10: # Only if it's a meaningful description + description = clean_line + break + + lessons.append({ + 'filename': filename, + 'title': title, + 'description': description, + 'path': file_path + }) + + return lessons + +def get_ordered_lessons(): + """Get lessons in the order specified in home.md if available""" + # Read home content to check for lesson order + home_file_path = os.path.join(CONTENT_DIR, "home.md") + if os.path.exists(home_file_path): + 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) + + if lesson_links: + # Create ordered list based on home.md + all_lessons = get_lessons() + ordered_lessons = [] + + for link_text, filename in lesson_links: + for lesson in all_lessons: + if lesson['filename'] == filename: + # Update title to use the link text from home.md if needed + lesson_copy = lesson.copy() + lesson_copy['title'] = link_text + ordered_lessons.append(lesson_copy) + break + + # Add any remaining lessons not mentioned in home.md + for lesson in all_lessons: + if not any(l['filename'] == lesson['filename'] for l in ordered_lessons): + ordered_lessons.append(lesson) + + return ordered_lessons + + # If no specific order is defined in home.md, return lessons in default order + return get_lessons() + +def render_markdown_content(file_path): + """Render markdown content to HTML""" + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Check if there's additional lesson info at the beginning + lesson_info = "" + lesson_content = content + initial_code = "" + solution_code = "" + expected_output = "" + + # Look for expected output separator - format: [content] ---EXPECTED_OUTPUT--- [output] ---END_EXPECTED_OUTPUT--- [content] + if '---EXPECTED_OUTPUT---' in lesson_content and '---END_EXPECTED_OUTPUT---' in lesson_content: + start_idx = lesson_content.find('---EXPECTED_OUTPUT---') + end_idx = lesson_content.find('---END_EXPECTED_OUTPUT---') + + if start_idx != -1 and end_idx != -1 and end_idx > start_idx: + # Extract the expected output between the separators + expected_start = start_idx + len('---EXPECTED_OUTPUT---') + expected_output = lesson_content[expected_start:end_idx].strip() + # Remove the expected output section from the lesson content + lesson_content = lesson_content[:start_idx] + lesson_content[end_idx + len('---END_EXPECTED_OUTPUT---'):] + + # Look for lesson info separator - format: ---LESSON_INFO--- [info] ---END_LESSON_INFO--- [content] + if '---LESSON_INFO---' in lesson_content and '---END_LESSON_INFO---' in lesson_content: + start_idx = lesson_content.find('---LESSON_INFO---') + len('---LESSON_INFO---') + end_idx = lesson_content.find('---END_LESSON_INFO---') + + if start_idx != -1 and end_idx != -1 and end_idx > start_idx: + lesson_info = lesson_content[start_idx:end_idx].strip() + lesson_content = lesson_content[end_idx + len('---END_LESSON_INFO---'):].strip() + else: + # If the format is not correct, treat as before + pass # lesson_content remains the same + elif '---LESSON_INFO---' in lesson_content: + # Fallback for old format: content before first separator is info, after is lesson + parts = lesson_content.split('---LESSON_INFO---', 1) + if len(parts) == 2: + lesson_info = parts[0].strip() + lesson_content = parts[1].strip() + + # Look for solution code separator - format: [content] ---SOLUTION_CODE--- [code] ---END_SOLUTION_CODE--- + if '---SOLUTION_CODE---' in lesson_content and '---END_SOLUTION_CODE---' in lesson_content: + start_idx = lesson_content.find('---SOLUTION_CODE---') + end_idx = lesson_content.find('---END_SOLUTION_CODE---') + + if start_idx != -1 and end_idx != -1 and end_idx > start_idx: + # Extract the code between the separators + code_start = start_idx + len('---SOLUTION_CODE---') + solution_code = lesson_content[code_start:end_idx].strip() + # Remove the solution code section from the lesson content + lesson_content = lesson_content[:start_idx] + lesson_content[end_idx + len('---END_SOLUTION_CODE---'):] + + # Look for initial code separator - format: [content] ---INITIAL_CODE--- [code] ---END_INITIAL_CODE--- + if '---INITIAL_CODE---' in lesson_content and '---END_INITIAL_CODE---' in lesson_content: + start_idx = lesson_content.find('---INITIAL_CODE---') + end_idx = lesson_content.find('---END_INITIAL_CODE---') + + if start_idx != -1 and end_idx != -1 and end_idx > start_idx: + # Extract the code between the separators + code_start = start_idx + len('---INITIAL_CODE---') + initial_code = lesson_content[code_start:end_idx].strip() + # Remove the initial code section from the lesson content + lesson_content = lesson_content[:start_idx] + lesson_content[end_idx + len('---END_INITIAL_CODE---'):] + + # Split content into lesson and exercise parts + parts = lesson_content.split('---EXERCISE---') + 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 "" + + return lesson_html, exercise_html, expected_output, lesson_info_html, initial_code, solution_code + +@app.route('/') +def index(): + """Main page showing all lessons""" + lessons = get_ordered_lessons() + + # Read home content from a home.md file if it exists + home_content = "" + home_file_path = os.path.join(CONTENT_DIR, "home.md") + if os.path.exists(home_file_path): + with open(home_file_path, 'r', encoding='utf-8') as f: + full_content = f.read() + + # Split content to get only the main content part (before the lesson list) + parts = full_content.split('---Available_Lessons---') + main_content = parts[0] if len(parts) > 0 else full_content + home_content = markdown.markdown(main_content, extensions=['fenced_code', 'codehilite', 'tables']) + + return render_template('index.html', lessons=lessons, home_content=home_content) + +@app.route('/lesson/') +def lesson(filename): + """Display a specific lesson""" + file_path = os.path.join(CONTENT_DIR, filename) + if not os.path.exists(file_path): + return "Lesson not found", 404 + + lesson_html, exercise_html, expected_output, lesson_info, initial_code, solution_code = render_markdown_content(file_path) + + # If no initial code is provided, use a default template + if not initial_code: + initial_code = """#include + +int main() { + // Write your code here + printf("Hello, World!\\n"); + return 0; +}""" + + return render_template('lesson.html', + lesson_content=lesson_html, + exercise_content=exercise_html, + expected_output=expected_output, + lesson_info=lesson_info, + initial_code=initial_code, + solution_code=solution_code, + lesson_title=filename.replace('.md', '').replace('_', ' ').title()) + +@app.route('/compile', methods=['POST']) +def compile_code(): + """Compile and run C code submitted by the user""" + try: + code = None + + # Try to get code from JSON data + if request.content_type and 'application/json' in request.content_type: + try: + json_data = request.get_json(force=True) + if json_data and 'code' in json_data: + code = json_data['code'] + except Exception as e: + # Log the error for debugging + print(f"JSON parsing error: {e}") + pass # If JSON parsing fails, continue to try form data + + # If not found in JSON, try form data + if not code: + code = request.form.get('code', '') + + if not code: + return jsonify({ + 'success': False, + 'output': '', + 'error': 'No code provided' + }) + + # Create a temporary file for the C code + with tempfile.NamedTemporaryFile(mode='w', suffix='.c', delete=False) as temp_c: + temp_c.write(code) + temp_c_path = temp_c.name + + # Create a temporary file for the executable + temp_exe_path = temp_c_path.replace('.c', '') + + # 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) + + except Exception as e: + return jsonify({ + 'success': False, + 'output': '', + 'error': f'An error occurred: {str(e)}' + }) + +@app.route('/static/') +def send_static(path): + """Serve static files""" + return send_from_directory('static', path) + +@app.route('/assets/') +def send_assets(path): + """Serve asset files (images, etc.)""" + return send_from_directory('assets', path) + +@app.context_processor +def inject_functions(): + """Make get_lessons function available in templates""" + return dict(get_lessons=get_lessons) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000, debug=False) \ No newline at end of file diff --git a/assets/InitilizationofaVariable-660x330.png b/assets/InitilizationofaVariable-660x330.png new file mode 100755 index 0000000000000000000000000000000000000000..664876657ee9d2d32f91e198261581d660cbc59c GIT binary patch literal 31022 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 Hefa+aNQA&c literal 0 HcmV?d00001 diff --git a/content/home.md b/content/home.md new file mode 100644 index 0000000..fa17a7d --- /dev/null +++ b/content/home.md @@ -0,0 +1,33 @@ +# 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) + diff --git a/content/introduction_to_c.md b/content/introduction_to_c.md new file mode 100644 index 0000000..7741082 --- /dev/null +++ b/content/introduction_to_c.md @@ -0,0 +1,123 @@ +---LESSON_INFO--- +**Learning Objectives:** +- Understand basic syntax and structure of C programs +- Learn how to write your first C program +- Familiarize with the compilation process + +**Prerequisites:** +- Basic understanding of programming concepts +- Familiarity with command line interface (optional) + +---END_LESSON_INFO--- +# Introduction to C Programming + +C is a general-purpose, procedural computer programming language supporting structured programming, lexical variable scope, and recursion, with a static type system. By design, C provides constructs that map efficiently to typical machine instructions, which has made it one of the most widely used programming languages. + + +# Your First C Program Exercise + +Write a C program that prints your name and your favorite programming language to the console. + +**Requirements:** +- Use `printf` to output your name +- Include your favorite programming language in the output +- Make sure to include the standard input/output library +- The output should be in the format: "Hello, I am [Your Name] and my favorite programming language is [Language]" + +**Example Output:** +``` +Hello, I am John and my favorite programming language is C +``` + +Try writing your solution in the code editor below! + +## History of C + +C was originally developed at Bell Labs by Dennis Ritchie between 1972 and 1973. It was created to re-implement the Unix operating system, which was previously coded in assembly language. + +## Basic Structure of a C Program + +A basic C program consists of: +- Preprocessor directives +- Function definitions +- Variable declarations +- Comments +- Executable statements + +Here's a simple example: + +```c +#include + +int main() { + printf("Hello, World!\n"); + return 0; +} +``` + +## Key Concepts + +1. **Variables**: Storage locations with a name and data type +2. **Data Types**: Define what kind of value a variable can hold +3. **Functions**: Blocks of code that perform specific tasks +4. **Control Structures**: Direct the flow of program execution + +--- +## Common Data Types in C + +| Type | Description | Example | +|------|-------------|---------| +| int | Integer values | `int age = 25;` | +| float | Floating-point numbers | `float price = 19.99;` | +| char | Single character | `char grade = 'A';` | +| double | Double precision float | `double pi = 3.14159;` | + + +---EXERCISE--- + +# Your First C Program Exercise + +Write a C program that prints your name and your favorite programming language to the console. + +**Requirements:** +- Use `printf` to output your name +- Include your favorite programming language in the output +- Make sure to include the standard input/output library +- The output should be in the format: "Hello, I am [Your Name] and my favorite programming language is [Language]" + +**Expected Output:** +``` +Hello, I am John and my favorite programming language is C +``` + +Try writing your solution in the code editor below! + + +---EXPECTED_OUTPUT--- +Hello, I am John and my favorite programming language is C + +---END_EXPECTED_OUTPUT--- + +---INITIAL_CODE--- +#include + +int main() { + // Write your code here + printf("Hello, World!\\n"); + return 0; +} +---END_INITIAL_CODE--- + + +---SOLUTION_CODE--- +#include + +int main() { + // Write your code here + printf("Hello, I am John and my favorite programming language is C\n"); + return 0; +} +---END_SOLUTION_CODE--- + + +- diff --git a/content/variables_and_data_types.md b/content/variables_and_data_types.md new file mode 100644 index 0000000..385c1c5 --- /dev/null +++ b/content/variables_and_data_types.md @@ -0,0 +1,98 @@ +# Variables and Data Types in C + +In C programming, variables are used to store data that can be manipulated during program execution. Each variable has a specific data type that determines the size and layout of the variable's memory, the range of values that can be stored, and the set of operations that can be applied to the variable. + +## Declaring Variables + +![variable](/assets/InitilizationofaVariable-660x330.png) + +To use a variable in C, you must first declare it. The syntax for declaring a variable is: + +```c +data_type variable_name; +``` + +For example: +```c +int age; +float height; +char initial; +``` + +You can also initialize variables at the time of declaration: + +```c +int age = 25; +float height = 5.8; +char initial = 'J'; +``` + +## Common Data Types + +### Integer Types +- `int`: Used for integers (whole numbers) +- `short`: Short integer +- `long`: Long integer +- `long long`: Extended size integer + +### Floating-Point Types +- `float`: Single precision floating-point +- `double`: Double precision floating-point +- `long double`: Extended precision floating-point + +### Character Types +- `char`: Single character or small integer + +### Void Type +- `void`: Represents the absence of type + +## Variable Naming Rules + +1. Variable names must begin with a letter or underscore +2. Names can contain letters, digits, and underscores +3. Names are case-sensitive +4. Names cannot be keywords (like `int`, `char`, etc.) +5. Names should be descriptive + +## Constants + +Constants are fixed values that your program cannot alter during execution. They can be: +- Integer constants: `100`, `-50`, `0` +- Floating-point constants: `3.14`, `-2.5`, `0.001` +- Character constants: `'a'`, `'X'`, `'3'` +- String literals: `"Hello, World!"` + +--- + +---EXERCISE--- + +# Variables and Data Types Exercise + +Write a C program that declares variables of different data types and prints their values. + +**Requirements:** +1. Declare an integer variable called `quantity` and assign it the value 42 +2. Declare a float variable called `price` and assign it the value 19.99 +3. Declare a character variable called `grade` and assign it the value 'A' +4. Declare a double variable called `pi` and assign it the value 3.14159 +5. Print all variables with appropriate labels + +**Expected Output:** +``` +Quantity: 42 +Price: 19.990000 +Grade: A +Pi: 3.141590 +``` + +Remember to include the stdio.h header file and use the correct format specifiers in printf statements (%d for int, %f for float/double, %c for char). + +Try writing your solution in the code editor below! + + +---EXPECTED_OUTPUT--- + +Quantity: 42 +Price: 19.990000 +Grade: A +Pi: 3.141590 diff --git a/podman-compose.yml b/podman-compose.yml new file mode 100644 index 0000000..52a35fa --- /dev/null +++ b/podman-compose.yml @@ -0,0 +1,14 @@ +version: '3.8' + +services: + lms-c: + build: . + ports: + - "5000:5000" + volumes: + - ./content:/app/content + - ./static:/app/static + - ./templates:/app/templates + environment: + - FLASK_ENV=development + command: python app.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3a2b2ec --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Flask==2.3.3 +markdown==3.5 +gunicorn==21.2.0 \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..0bb0776 --- /dev/null +++ b/start.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Script to start the C Programming Learning Management System + +echo "Starting C Programming Learning Management System..." + +# Check if container is already running +if [ "$(podman ps -q -f name=lms-c-container)" ]; then + echo "Container is already running. Access the application at http://localhost:5000" + exit 0 +fi + +# Check if container exists but is stopped +if [ "$(podman ps -aq -f name=lms-c-container)" ]; then + echo "Starting existing container..." + podman start lms-c-container +else + # Build and run the container + echo "Building and starting container..." + podman build -t lms-c . && podman run -d -p 5000:5000 --name lms-c-container lms-c +fi + +echo "Application is now running. Access at http://localhost:5000" +echo "To stop the application, run: ./stop.sh" \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..9f67320 --- /dev/null +++ b/static/style.css @@ -0,0 +1,330 @@ +/* Custom styles for the C Programming Learning System */ + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background-color: #f8f9fa; +} + +.lesson-card { + transition: transform 0.2s, box-shadow 0.2s; + height: 100%; +} + +.lesson-card:hover { + transform: translateY(-5px); + box-shadow: 0 4px 8px rgba(0,0,0,0.1); +} + +.lesson-content { + background: white; + padding: 20px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.lesson-content h1, .lesson-content h2, .lesson-content h3 { + color: #2c3e50; + margin-top: 20px; +} + +.lesson-content h1 { + border-bottom: 2px solid #3498db; + padding-bottom: 10px; +} + +.lesson-content p { + line-height: 1.6; + margin-bottom: 15px; +} + +.lesson-content code { + background-color: #f1f1f1; + padding: 2px 6px; + border-radius: 4px; + font-family: 'Courier New', monospace; + color: #d6336c; /* More colorful */ +} + +.lesson-content pre { + background-color: #f8f9fa; + color: #333; + padding: 15px; + border-radius: 8px; + border: 1px solid #e9ecef; + overflow-x: auto; + margin: 15px 0; +} + +/* More colorful code blocks in lesson content */ +.lesson-content pre code { + background: none; + padding: 0; + color: #333; + font-family: 'Courier New', monospace; +} + +/* Colorful syntax highlighting for lesson content code blocks */ +.lesson-content pre { + background-color: #2b2b2b; + color: #f8f8f2; + border: 1px solid #49483e; + padding: 15px; + border-radius: 8px; + overflow-x: auto; + margin: 15px 0; +} + +.lesson-content pre code { + background: none; + color: #f8f8f2; + display: block; + overflow-x: auto; +} + +/* Use highlight.js theme for lesson content code blocks */ +.lesson-content pre code { + background: #2b2b2b; + padding: 15px; + border-radius: 8px; + overflow-x: auto; +} + +/* Override default hljs theme to use dark theme for lesson content */ +.lesson-content .hljs { + background: #2b2b2b; + color: #f8f8f2; + padding: 15px; + border-radius: 8px; + overflow-x: auto; +} + +.lesson-content .hljs-keyword { + color: #f92672; /* Red for keywords */ +} + +.lesson-content .hljs-function { + color: #a6e22e; /* Green for functions */ +} + +.lesson-content .hljs-number { + color: #ae81ff; /* Purple for numbers */ +} + +.lesson-content .hljs-string { + color: #e6db74; /* Yellow for strings */ +} + +.lesson-content .hljs-comment { + color: #75715e; /* Brown for comments */ +} + +.lesson-content .hljs-meta { + color: #f8f8f2; /* Default color for meta */ +} + +.lesson-content .hljs-title { + color: #a6e22e; /* Green for titles/functions */ +} + +.lesson-content .hljs-type { + color: #66d9ef; /* Light blue for types */ +} + +.lesson-content .hljs-params { + color: #fd971f; /* Orange for parameters */ +} + +.lesson-content .hljs-built_in { + color: #a6e22e; /* Green for built-in functions */ +} + +.lesson-content table { + width: 100%; + border-collapse: collapse; + margin: 15px 0; +} + +.lesson-content table, +.lesson-content th, +.lesson-content td { + border: 1px solid #ddd; +} + +.lesson-content th { + background-color: #f2f2f2; + padding: 10px; + text-align: left; +} + +.lesson-content td { + padding: 10px; +} + +.lesson-content img { + max-width: 100%; + height: auto; + margin: 10px 0; +} + +.lesson-content figure { + margin: 15px 0; + text-align: center; +} + +.lesson-content figcaption { + font-style: italic; + color: #666; + margin-top: 5px; +} + +.lesson-content pre code { + background: none; + padding: 0; +} + +.exercise-section { + background: white; + padding: 25px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.exercise-content { + background-color: #f8f9fa; + padding: 15px; + border-left: 4px solid #3498db; + border-radius: 0 4px 4px 0; +} + +.editor-section { + margin-top: 20px; +} + +#code-editor { + font-family: 'Courier New', monospace; + font-size: 14px; + resize: vertical; +} + +#output { + max-height: 300px; + overflow-y: auto; +} + +.output-success { + color: #28a745; +} + +.output-error { + color: #dc3545; +} + +.footer { + margin-top: 50px; +} + +.navbar-brand { + font-weight: bold; +} + +.card { + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.list-group-item.active { + background-color: #3498db; + border-color: #3498db; +} + +/* Syntax highlighting for code blocks */ +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #f0f0f0; +} + +/* Dark theme for code editor */ +.code-editor-dark { + background-color: #272822 !important; + color: #f8f8f2 !important; +} + +.code-editor-dark #line-numbers { + background-color: #3e3d32 !important; + color: #75715e !important; +} + +/* Line numbers container styling */ +.line-numbers { + background-color: #eef0f3; + color: #6c757d; + border-right: 1px solid #dee2e6; + font-family: monospace; + font-size: 14px; + line-height: 1.4; + padding: 10px 5px; + text-align: right; + overflow: hidden; + user-select: none; + flex-shrink: 0; + min-width: 40px; +} + +/* Dark theme for line numbers */ +.code-editor-dark + .line-numbers, +.code-editor-dark .line-numbers { + background-color: #3e3d32; + color: #75715e; + border-right: 1px solid #49483e; +} + +/* Alternative selector for dark theme */ +.code-editor-dark ~ div .line-numbers, +#editor-wrapper.code-editor-dark .line-numbers { + background-color: #3e3d32; + color: #75715e; + border-right: 1px solid #49483e; +} + +/* Editor wrapper styling */ +.editor-wrapper { + display: flex; + overflow: hidden; +} + +.editor-wrapper textarea { + border: none; + outline: none; + resize: none; + font-family: monospace; + font-size: 14px; + line-height: 1.4; + padding: 10px; + margin: 0; + overflow: auto; + flex: 1; +} + +/* Dark theme for output panel */ +.output-dark { + background-color: #272822 !important; + color: #f8f8f2 !important; + border: 1px solid #49483e !important; +} + +.output-dark pre { + color: #f8f8f2 !important; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .container { + padding-left: 15px; + padding-right: 15px; + } + + .lesson-content, .exercise-section { + padding: 15px; + } +} \ No newline at end of file diff --git a/stop.sh b/stop.sh new file mode 100755 index 0000000..9ff867b --- /dev/null +++ b/stop.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Script to stop the C Programming Learning Management System + +echo "Stopping C Programming Learning Management System..." + +# Stop and remove the container +podman stop lms-c-container && podman rm lms-c-container + +echo "Application has been stopped." \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..30eeb3d --- /dev/null +++ b/templates/index.html @@ -0,0 +1,66 @@ + + + + + + C Programming Learning System + + + + + + + +
+
+
+

C Programming Learning System

+ + +
+ {{ home_content | safe }} +
+ +

Available Lessons

+ + {% if lessons %} +
+ {% for lesson in lessons %} +
+
+
+
{{ lesson.title }}
+

{{ lesson.description }}

+
+ +
+
+ {% endfor %} +
+ {% else %} +
+

No lessons available

+

Add markdown files to the content directory to create lessons.

+
+ {% endif %} +
+
+
+ +
+
+ C Programming Learning System © 2025 +
+
+ + + + \ No newline at end of file diff --git a/templates/lesson.html b/templates/lesson.html new file mode 100644 index 0000000..0c4d5f3 --- /dev/null +++ b/templates/lesson.html @@ -0,0 +1,464 @@ + + + + + + {{ lesson_title }} - C Programming Learning System + + + + + + + + + +
+
+
+
+ {{ lesson_content | safe }} +
+ +
+

Exercise

+ {% if exercise_content %} +
+ {{ exercise_content | safe }} +
+ {% else %} +
+

No specific exercise for this lesson, but you can practice writing C code below.

+
+ {% endif %} + +
+

Code Editor

+ +
+
+ C code.c +
+ + + + +
+
+
+
+
+
+ 1 +
+
+ +
+
+
+
+
+ + + + +
+
+
Output
+ +
+

+                        
+
+
+
+ +
+
+
+
Lesson Information
+
+
+

Current Lesson: {{ lesson_title }}

+ {% if lesson_info %} +
+ {{ lesson_info | safe }} +
+ {% else %} +

Complete the exercise to practice what you've learned.

+ {% endif %} +
+
+ +
+
+
All Lessons
+
+
+ {% for lesson in get_lessons() %} + + {{ lesson.title }} + + {% endfor %} +
+
+
+
+
+ +
+
+ C Programming Learning System © 2025 +
+
+ + + + + + + + \ No newline at end of file