Skip to content

Commit 1728e36

Browse files
committed
Add Python Compiler and Enhanced Quiz Integration
- Implemented secure Python code execution with sandboxed environment - Added interactive code editor with syntax highlighting and real-time execution - Created practice templates system with 10+ coding exercises - Integrated compiler into quiz pages with direct navigation links - Added user progress tracking, history, and leaderboard features - Implemented comprehensive security measures (rate limiting, malicious pattern detection) - Enhanced UI with professional layout and responsive design - Added admin interface for template and execution monitoring
1 parent b1d2f66 commit 1728e36

2,423 files changed

Lines changed: 141057 additions & 1290 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/settings.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@
125125
"django.contrib.auth.middleware.AuthenticationMiddleware",
126126
"django.contrib.messages.middleware.MessageMiddleware",
127127
# "home.idle.IdleTimeoutMiddleware",
128-
"home.idle.LogoutMiddleware",
128+
"home.idle.LogoutMiddleware",
129129
"django.middleware.clickjacking.XFrameOptionsMiddleware",
130130
"home.ratelimit_middleware.GlobalLockoutMiddleware",
131-
'core.middleware.AutoLogoutMiddleware'
131+
'core.middleware.AutoLogoutMiddleware',
132132
]
133133

134134
LOGGING = {
@@ -729,7 +729,7 @@
729729

730730
# # STATIC_URL = 'custom_static/'
731731
# STATIC_URL = 'static/'
732-
# STATIC_ROOT = os.path.join(BASE_DIR, 'static')
732+
# STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
733733
# STATICFILES_DIRS = [
734734
# os.path.join(BASE_DIR, 'custom_static')
735735
# ]

core/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,4 @@
9191
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
9292
path('', include('home.urls')),
9393

94-
]
94+
]

home/admin.py

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
2+
13
from django.contrib import admin
24

35
from .models import AdminNotification
@@ -38,7 +40,13 @@
3840

3941
AppAttackReport,
4042
PenTestingRequest,
41-
SecureCodeReviewRequest
43+
SecureCodeReviewRequest,
44+
45+
# Python Compiler Models
46+
CodeExecution,
47+
CodeTemplate,
48+
CodeSubmission,
49+
CompilerSettings,
4250

4351
)
4452

@@ -191,14 +199,7 @@ def job__title(self,obj):
191199

192200
@admin.register(LeaderBoardTable)
193201
class LeaderboardTableAdmin(admin.ModelAdmin):
194-
list_display = ('user', 'category', 'total_points', 'rank', 'last_updated')
195-
list_filter = ('category', 'rank')
196-
search_fields = ('user__email', 'user__first_name', 'user__last_name')
197-
ordering = ('category', 'rank')
198-
readonly_fields = ('rank', 'last_updated')
199-
200-
def get_queryset(self, request):
201-
return super().get_queryset(request).select_related('user')
202+
list_display = ('user', 'category', 'total_points')
202203

203204

204205

@@ -228,6 +229,70 @@ class SecureCodeReviewRequestAdmin(admin.ModelAdmin):
228229
readonly_fields = ['submitted_at']
229230

230231

232+
# Python Compiler Admin Classes
233+
@admin.register(CodeExecution)
234+
class CodeExecutionAdmin(admin.ModelAdmin):
235+
list_display = ['user', 'language', 'is_successful', 'execution_time', 'created_at', 'ip_address']
236+
list_filter = ['language', 'is_successful', 'created_at', 'ip_address']
237+
search_fields = ['user__email', 'code', 'output', 'error_message']
238+
readonly_fields = ['created_at', 'execution_time', 'memory_used']
239+
ordering = ['-created_at']
240+
241+
def get_readonly_fields(self, request, obj=None):
242+
if obj: # If editing existing object
243+
return ['user', 'language', 'code', 'input_data', 'output', 'error_message',
244+
'execution_time', 'memory_used', 'is_successful', 'created_at', 'ip_address']
245+
return self.readonly_fields
246+
247+
248+
@admin.register(CodeTemplate)
249+
class CodeTemplateAdmin(admin.ModelAdmin):
250+
list_display = ['title', 'category', 'difficulty', 'is_active', 'created_at']
251+
list_filter = ['category', 'difficulty', 'is_active', 'created_at']
252+
search_fields = ['title', 'description', 'template_code']
253+
readonly_fields = ['created_at', 'updated_at']
254+
255+
fieldsets = (
256+
('Basic Information', {
257+
'fields': ('title', 'description', 'category', 'difficulty', 'is_active')
258+
}),
259+
('Code Content', {
260+
'fields': ('template_code', 'expected_output', 'hints')
261+
}),
262+
('Timestamps', {
263+
'fields': ('created_at', 'updated_at'),
264+
'classes': ('collapse',)
265+
}),
266+
)
267+
268+
269+
@admin.register(CodeSubmission)
270+
class CodeSubmissionAdmin(admin.ModelAdmin):
271+
list_display = ['user', 'template', 'is_correct', 'execution_time', 'submitted_at']
272+
list_filter = ['is_correct', 'submitted_at', 'template__category', 'template__difficulty']
273+
search_fields = ['user__email', 'template__title', 'user_code']
274+
readonly_fields = ['submitted_at', 'execution_time']
275+
ordering = ['-submitted_at']
276+
277+
def get_readonly_fields(self, request, obj=None):
278+
if obj: # If editing existing object
279+
return ['user', 'template', 'user_code', 'is_correct', 'execution_time', 'submitted_at']
280+
return self.readonly_fields
281+
282+
283+
@admin.register(CompilerSettings)
284+
class CompilerSettingsAdmin(admin.ModelAdmin):
285+
list_display = ['max_execution_time', 'max_memory_limit', 'max_code_length', 'is_active', 'updated_at']
286+
list_filter = ['is_active', 'created_at', 'updated_at']
287+
readonly_fields = ['created_at', 'updated_at']
288+
289+
def has_add_permission(self, request):
290+
# Only allow one settings instance
291+
return not CompilerSettings.objects.exists()
292+
293+
def has_delete_permission(self, request, obj=None):
294+
# Prevent deletion of settings
295+
return False
231296

232297

233298

home/apps.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ def ready(self):
1515
insert_default_projects()
1616
insert_default_courses()
1717
except Exception as e:
18-
print(f"[ERROR] insert_defaults failed: {e}")
18+
print(f"[ERROR] insert_defaults failed: {e}")
19+
# Continue running even if inserts fail

home/insert_defaults.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def insert_default_projects():
1313
]
1414

1515
for title in default_titles:
16-
Project.objects.get_or_create(title=title)
16+
Project.objects.get_or_create(title=title, defaults={'title': title})
1717

1818
def insert_default_courses():
1919
course_data = [
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
from django.core.management.base import BaseCommand
2+
from home.models import CodeTemplate
3+
4+
class Command(BaseCommand):
5+
help = 'Populate the database with sample Python code templates'
6+
7+
def handle(self, *args, **options):
8+
templates = [
9+
{
10+
'title': 'Hello World',
11+
'description': 'Write a simple program that prints "Hello, World!" to the console.',
12+
'category': 'basics',
13+
'difficulty': 'beginner',
14+
'template_code': '# Write a program that prints "Hello, World!"\nprint("Hello, World!")',
15+
'expected_output': 'Hello, World!',
16+
'hints': 'Use the print() function to display text.'
17+
},
18+
{
19+
'title': 'Basic Calculator',
20+
'description': 'Create a simple calculator that can add, subtract, multiply, and divide two numbers.',
21+
'category': 'basics',
22+
'difficulty': 'beginner',
23+
'template_code': '# Create a basic calculator\n# Define two numbers\na = 10\nb = 5\n\n# Perform calculations\nprint(f"Addition: {a} + {b} = {a + b}")\nprint(f"Subtraction: {a} - {b} = {a - b}")\nprint(f"Multiplication: {a} * {b} = {a * b}")\nprint(f"Division: {a} / {b} = {a / b}")',
24+
'expected_output': 'Addition: 10 + 5 = 15\nSubtraction: 10 - 5 = 5\nMultiplication: 10 * 5 = 50\nDivision: 10 / 5 = 2.0',
25+
'hints': 'Use arithmetic operators (+, -, *, /) to perform calculations.'
26+
},
27+
{
28+
'title': 'List Operations',
29+
'description': 'Create a list and perform various operations like adding, removing, and accessing elements.',
30+
'category': 'data_structures',
31+
'difficulty': 'beginner',
32+
'template_code': '# Create a list and perform operations\nfruits = ["apple", "banana", "cherry"]\n\n# Add an element\nfruits.append("orange")\n\n# Remove an element\nfruits.remove("banana")\n\n# Access elements\nprint(f"First fruit: {fruits[0]}")\nprint(f"All fruits: {fruits}")\nprint(f"Number of fruits: {len(fruits)}")',
33+
'expected_output': 'First fruit: apple\nAll fruits: [\'apple\', \'cherry\', \'orange\']\nNumber of fruits: 3',
34+
'hints': 'Use append() to add elements, remove() to delete elements, and len() to get the length.'
35+
},
36+
{
37+
'title': 'Function Definition',
38+
'description': 'Write a function that takes two parameters and returns their sum.',
39+
'category': 'basics',
40+
'difficulty': 'intermediate',
41+
'template_code': '# Define a function that adds two numbers\ndef add_numbers(a, b):\n return a + b\n\n# Test the function\nresult = add_numbers(5, 3)\nprint(f"The sum is: {result}")',
42+
'expected_output': 'The sum is: 8',
43+
'hints': 'Use the def keyword to define a function and return to send back a value.'
44+
},
45+
{
46+
'title': 'Dictionary Operations',
47+
'description': 'Create a dictionary to store student information and perform various operations.',
48+
'category': 'data_structures',
49+
'difficulty': 'intermediate',
50+
'template_code': '# Create a student dictionary\nstudent = {\n "name": "John Doe",\n "age": 20,\n "grade": "A"\n}\n\n# Add new information\nstudent["subject"] = "Computer Science"\n\n# Update existing information\nstudent["age"] = 21\n\n# Display information\nfor key, value in student.items():\n print(f"{key}: {value}")',
51+
'expected_output': 'name: John Doe\nage: 21\ngrade: A\nsubject: Computer Science',
52+
'hints': 'Use curly braces {} to create dictionaries and for loops to iterate through them.'
53+
},
54+
{
55+
'title': 'Class Definition',
56+
'description': 'Create a simple class with methods to represent a bank account.',
57+
'category': 'oop',
58+
'difficulty': 'intermediate',
59+
'template_code': '# Create a BankAccount class\nclass BankAccount:\n def __init__(self, account_holder, initial_balance=0):\n self.account_holder = account_holder\n self.balance = initial_balance\n \n def deposit(self, amount):\n self.balance += amount\n return self.balance\n \n def withdraw(self, amount):\n if amount <= self.balance:\n self.balance -= amount\n return self.balance\n else:\n return "Insufficient funds"\n\n# Test the class\naccount = BankAccount("Alice", 1000)\nprint(f"Initial balance: {account.balance}")\naccount.deposit(500)\nprint(f"After deposit: {account.balance}")\nresult = account.withdraw(200)\nprint(f"After withdrawal: {result}")',
60+
'expected_output': 'Initial balance: 1000\nAfter deposit: 1500\nAfter withdrawal: 1300',
61+
'hints': 'Use class keyword to define a class, __init__ for constructor, and self to refer to the instance.'
62+
},
63+
{
64+
'title': 'File Reading',
65+
'description': 'Write a program that reads content from a file and displays it.',
66+
'category': 'file_handling',
67+
'difficulty': 'intermediate',
68+
'template_code': '# Read content from a file\n# Note: This is a simulation since we can\'t create actual files in the compiler\n# In real scenarios, you would use: with open("filename.txt", "r") as file:\n\n# Simulate file content\nfile_content = "Hello from file!\\nThis is line 2.\\nThis is line 3."\n\n# Process the content\nlines = file_content.split("\\n")\nprint(f"Number of lines: {len(lines)}")\nfor i, line in enumerate(lines, 1):\n print(f"Line {i}: {line}")',
69+
'expected_output': 'Number of lines: 3\nLine 1: Hello from file!\nLine 2: This is line 2.\nLine 3: This is line 3.',
70+
'hints': 'Use split() to separate lines and enumerate() to get both index and value.'
71+
},
72+
{
73+
'title': 'Exception Handling',
74+
'description': 'Write a program that handles division by zero and other exceptions gracefully.',
75+
'category': 'basics',
76+
'difficulty': 'intermediate',
77+
'template_code': '# Handle exceptions gracefully\ndef safe_divide(a, b):\n try:\n result = a / b\n return result\n except ZeroDivisionError:\n return "Cannot divide by zero!"\n except TypeError:\n return "Invalid input types!"\n except Exception as e:\n return f"An error occurred: {e}"\n\n# Test the function\nprint(safe_divide(10, 2))\nprint(safe_divide(10, 0))\nprint(safe_divide("10", 2))',
78+
'expected_output': '5.0\nCannot divide by zero!\nInvalid input types!',
79+
'hints': 'Use try-except blocks to catch and handle different types of exceptions.'
80+
},
81+
{
82+
'title': 'List Comprehension',
83+
'description': 'Use list comprehension to create a list of squares of numbers from 1 to 10.',
84+
'category': 'data_structures',
85+
'difficulty': 'advanced',
86+
'template_code': '# Create a list of squares using list comprehension\nsquares = [x**2 for x in range(1, 11)]\nprint(f"Squares from 1 to 10: {squares}")\n\n# Filter even squares\neven_squares = [x**2 for x in range(1, 11) if x % 2 == 0]\nprint(f"Even squares: {even_squares}")\n\n# Create a dictionary using dictionary comprehension\nsquare_dict = {x: x**2 for x in range(1, 6)}\nprint(f"Square dictionary: {square_dict}")',
87+
'expected_output': 'Squares from 1 to 10: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]\nEven squares: [4, 16, 36, 64, 100]\nSquare dictionary: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}',
88+
'hints': 'List comprehension syntax: [expression for item in iterable if condition]'
89+
},
90+
{
91+
'title': 'Recursive Function',
92+
'description': 'Write a recursive function to calculate the factorial of a number.',
93+
'category': 'algorithms',
94+
'difficulty': 'advanced',
95+
'template_code': '# Recursive factorial function\ndef factorial(n):\n if n == 0 or n == 1:\n return 1\n else:\n return n * factorial(n - 1)\n\n# Test the function\nfor i in range(1, 6):\n result = factorial(i)\n print(f"Factorial of {i} is {result}")',
96+
'expected_output': 'Factorial of 1 is 1\nFactorial of 2 is 2\nFactorial of 3 is 6\nFactorial of 4 is 24\nFactorial of 5 is 120',
97+
'hints': 'A recursive function calls itself with a smaller input until it reaches a base case.'
98+
}
99+
]
100+
101+
created_count = 0
102+
for template_data in templates:
103+
template, created = CodeTemplate.objects.get_or_create(
104+
title=template_data['title'],
105+
defaults=template_data
106+
)
107+
if created:
108+
created_count += 1
109+
self.stdout.write(
110+
self.style.SUCCESS(f'Created template: {template.title}')
111+
)
112+
else:
113+
self.stdout.write(
114+
self.style.WARNING(f'Template already exists: {template.title}')
115+
)
116+
117+
self.stdout.write(
118+
self.style.SUCCESS(f'Successfully created {created_count} new templates!')
119+
)
120+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from django.core.management.base import BaseCommand
2+
from home.models import CompilerSettings
3+
4+
class Command(BaseCommand):
5+
help = 'Create default compiler settings'
6+
7+
def handle(self, *args, **options):
8+
settings, created = CompilerSettings.objects.get_or_create(
9+
defaults={
10+
'max_execution_time': 5,
11+
'max_memory_limit': 128,
12+
'max_code_length': 1000,
13+
'allowed_modules': [],
14+
'is_active': True
15+
}
16+
)
17+
18+
if created:
19+
self.stdout.write(
20+
self.style.SUCCESS('Successfully created default compiler settings!')
21+
)
22+
else:
23+
self.stdout.write(
24+
self.style.WARNING('Compiler settings already exist!')
25+
)

home/management/commands/update_leaderboard.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ def handle(self, *args, **options):
1717
)
1818

1919

20+

0 commit comments

Comments
 (0)