Compare commits

10 Commits

Author SHA1 Message Date
04dc638cf0 if the user submitted solution is shit wrong then fucking dont add to the shit database.
also removed unnccessary fucking function
2025-08-13 22:56:06 +02:00
38c3256f19 zajebis2 2025-08-12 20:42:52 +02:00
1374cb9cb1 zajebis 2025-08-12 20:42:26 +02:00
c1ef310f6a shit didnt woirk. frucky ou 2025-08-12 20:37:59 +02:00
525297f19b delete templates which was empty 2025-08-12 20:17:14 +02:00
89ea87951e made this a hell of a lot better 2025-08-12 20:16:46 +02:00
c7c1b8ecd6 some more info and some difficulty added! 2025-08-12 18:56:52 +02:00
0bffdf612c refactored the prb page and added fib seqq 2025-08-12 16:37:35 +02:00
a03f9ddb14 Update readme.md 2025-08-12 11:52:15 +00:00
3f1f709f30 Merge pull request 'shitdontwork' (#1) from shitdontwork into main
Reviewed-on: #1
2025-08-12 11:47:10 +00:00
31 changed files with 656 additions and 162 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
__pycache__
instance
.venv
venv
*.sqlite3

View File

@@ -1,37 +0,0 @@
import json
import os
import threading
import time
from models import db, Problem
from flask import current_app
def load_problems_from_json(json_path):
if not os.path.exists(json_path):
print(f"Problem JSON file not found: {json_path}")
return
with open(json_path, 'r') as f:
problems = json.load(f)
for p in problems:
# Check if problem already exists by title
existing = Problem.query.filter_by(title=p['title']).first()
# Load test code from solution file if provided
test_code = ''
if 'solution' in p and os.path.exists(p['solution']):
with open(p['solution'], 'r') as sf:
test_code = sf.read()
if existing:
existing.description = p['description']
existing.test_code = test_code
else:
new_problem = Problem(title=p['title'], description=p['description'], test_code=test_code)
db.session.add(new_problem)
db.session.commit()
def schedule_problem_reload(app, json_path, interval_hours=10):
def reload_loop():
while True:
with app.app_context():
load_problems_from_json(json_path)
time.sleep(interval_hours * 3600)
t = threading.Thread(target=reload_loop, daemon=True)
t.start()

View File

@@ -4,22 +4,59 @@
but more lightweight
run the bash script to start the server.
if you want to contribute write tests like this:
### FileStructure:
In /problems/ create a folder named after the problem.
In this folder create ```manifest.json, test.py, description.md```
**Manifest.JSON needs to exsist and _needs_ to look like this:**
```json
{
"title": "Title of the Problem",
"description": "Write a very short description here",
"description_md": "problems/problempath/description.md",
"difficulty": "easy || medium || hard",
"test_code": "problems/problempath/test.py"
}
```
I do know it might be a bit tedious but this is required and its the easiest way.
#### After you've decided on how you would name / write your Test write it like this:
- It is important to note that you _CAN_ write the Code the User is expected to write firstly. **BUT** after writing the UnitTest and it passing, comment out the written code.
It is supposed to look something like this (/sortlist/):
```python
"""
@TESTSAMPLE.PY / NAME THIS "test.py" in your actual project
"""
import unittest
#<!-- The Function the User needs to write -->
def revstring(x):
return x[::-1]
" )) First Point from the List "
# def sortlist(lst = [4,3,2,1]) -> list:
# return sorted(lst)
#<!-- This Test, test if the function works -->
")) This is a 'easy' Test, if you want you can write more defined ones."
class TestSolution(unittest.TestCase):
def test_simple(self):
# !! This needs to be dynamic ; if the user enters some shit then it is supposed to work too
x="";
self.assertEqual(revstring(x), x[::-1])
def test_sort(self):
self.x = []
self.assertEqual(sortlist(self.x), sorted(self.x)) # pyright: ignore[reportUndefinedVariable] <- This is only here so that pyright doesnt complain ; NOT NECCESARY!
if __name__ == "__main__":
unittest.main()
```
#### Writing the description:
**Please** by _God_ write simple and easy to understand terms. If you write like Einstein noone is going to understand you.
- Syntax:
- Normal Markdown.
- Start with "##" instead of "#" ; "##" looks better
- Use CrossLinks ( something like [W3](https://www.w3schools.com/), or the [PyDocs](https://docs.python.org/3/))
- Good Formatting is always appreciated

5
requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
Flask>=3.0
Flask-SQLAlchemy>=3.1
Markdown>=3.6
MarkupSafe>=2.1
watchdog>=4.0

17
run.bash Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
set -e # exit if any command fails
# Ensure QPP/database directory exists
mkdir -p src/database
python -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
export FLASK_APP=src.app
export FLASK_ENV=production
flask run --host=0.0.0.0 --port=5000

View File

@@ -1,24 +1,27 @@
from markupsafe import Markup
from flask import Flask, render_template, request, redirect, url_for, send_from_directory
import markdown as md
from models import db, Problem, Solution
from utils import run_code_against_tests
from leaderboard import create_leaderboard_table, log_leaderboard, get_leaderboard
import ast
from src.models import db, Problem, Solution
from src.utils import run_code_against_tests
from src.leaderboard import create_leaderboard_table, log_leaderboard, get_leaderboard
import os
## from problem_loader import load_problems_from_json, schedule_problem_reload
from problem_scanner import start_problem_scanner
from src.problem_scanner import start_problem_scanner
import sqlite3
from pathlib import Path
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite3'
BASE_DIR = Path(__file__).parent
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{BASE_DIR / 'database' / 'db.sqlite3'}"
print(f">>>>>>>>>>>>>>>>>>>>< Using database URI: {app.config['SQLALCHEMY_DATABASE_URI']}")
db.init_app(app)
@app.before_request
def setup():
db.create_all()
@@ -38,16 +41,17 @@ def favicon():
@app.route('/')
def index():
db_path = Path(__file__).parent / 'problems.sqlite3'
db_path = Path(__file__).parent / 'database/problems.sqlite3'
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute('SELECT folder, description, test_code FROM problems')
#<!-- The query was fucked up so it fetched the fucking testcode -->
c.execute('SELECT folder, description, test_code, difficulty FROM problems')
problems = c.fetchall()
conn.close()
# Get leaderboard entries
leaderboard = get_leaderboard()
# Map folder to title for display
problem_titles = {folder: folder.replace('_', ' ').title() for folder, _, _ in problems}
problem_titles = {folder: folder.replace('_', ' ').title() for folder, _, _, _ in problems}
return render_template('index.html', problems=problems, leaderboard=leaderboard, problem_titles=problem_titles)
@app.route('/problem/new', methods=['GET', 'POST'])
@@ -64,20 +68,23 @@ def new_problem():
@app.route('/problem/<folder>', methods=['GET', 'POST'])
def view_problem(folder):
db_path = Path(__file__).parent / 'problems.sqlite3'
db_path = Path(__file__).parent / 'database/problems.sqlite3'
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute('SELECT folder, description, test_code FROM problems WHERE folder = ?', (folder,))
c.execute('SELECT folder, description,test_code , difficulty FROM problems WHERE folder = ?', (folder,))
row = c.fetchone()
conn.close()
if not row:
return 'Problem not found', 404
problem = {
'folder': row[0],
'description': row[1],
'test_code': row[2],
'title': row[0].replace('_', ' ').title()
'difficulty': row[3], # now correct
'test_code': row[2], # now correct
}
result = None
if request.method == 'POST':
user_code = request.form['user_code']
@@ -88,9 +95,9 @@ def view_problem(folder):
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
memory_used = peak // 1024 # in KB
# Try to get the last line number executed (even for successful runs)
line_number = None
import ast
try:
tree = ast.parse(user_code)
# Find the highest line number in the AST (for multi-function/user code)
@@ -102,6 +109,7 @@ def view_problem(folder):
line_number = get_max_lineno(tree)
except Exception:
pass
# If there was an error, try to get the error line number from the traceback
if run_result['error']:
tb = run_result['error']
@@ -112,7 +120,11 @@ def view_problem(folder):
line_number = tb_lines[-1].lineno
except Exception:
pass
log_leaderboard(username, problem['folder'], run_result['runtime'], memory_used, line_number)
# ONLY log to leaderboard if the solution passed all tests
if run_result['passed']:
log_leaderboard(username, problem['folder'], run_result['runtime'], memory_used, line_number)
result = run_result
return render_template('problem.html', problem=problem, result=result)

View File

@@ -2,12 +2,14 @@ from flask_sqlalchemy import SQLAlchemy
from flask import g
import os
import sqlite3
from pathlib import Path
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'db.sqlite3'))
return db
if 'db' not in g:
db_path = Path(__file__).parent / 'database' / 'db.sqlite3'
db_path.parent.mkdir(exist_ok=True) # Ensure /database folder exists
g.db = sqlite3.connect(db_path)
return g.db
def create_leaderboard_table():
db = get_db()

15
src/problem_loader.py Normal file
View File

@@ -0,0 +1,15 @@
import json
import os
import threading
import time
from models import db, Problem
from flask import current_app
def schedule_problem_reload(app, json_path, interval_hours=10):
def reload_loop():
while True:
with app.app_context():
load_problems_from_json(json_path)
time.sleep(interval_hours * 3600)
t = threading.Thread(target=reload_loop, daemon=True)
t.start()

View File

@@ -19,7 +19,7 @@ except ImportError:
WATCHDOG_AVAILABLE = False
PROBLEMS_DIR = Path(__file__).parent / 'problems'
DB_PATH = Path(__file__).parent / 'problems.sqlite3'
DB_PATH = Path(__file__).parent / 'database/problems.sqlite3'
class ProblemScannerThread(threading.Thread):
def __init__(self, scan_interval=2):
@@ -35,6 +35,7 @@ class ProblemScannerThread(threading.Thread):
id INTEGER PRIMARY KEY AUTOINCREMENT,
folder TEXT,
description TEXT,
difficulty TEXT,
test_code TEXT
)''')
conn.commit()
@@ -65,13 +66,18 @@ class ProblemScannerThread(threading.Thread):
description = f.read()
with open(test_path, 'r', encoding='utf-8') as f:
test_code = f.read()
with open(manifest_path, 'r', encoding='utf-8') as f:
manifest = json.load(f)
difficulty = manifest.get('difficulty', 'unknown')
problems.append({
'folder': folder.name,
'description': description,
'test_code': test_code
'test_code': test_code,
'difficulty': difficulty
})
print(f"Found problem: {folder.name}")
print(f"Found problem: {folder.name} ; Difficulty: {difficulty}")
except Exception as e:
print(f"Error reading problem files for {folder.name}: {e}")
else:
@@ -99,8 +105,10 @@ class ProblemScannerThread(threading.Thread):
# Insert new problems
for p in problems:
c.execute('INSERT INTO problems (folder, description, test_code) VALUES (?, ?, ?)',
(p['folder'], p['description'], p['test_code']))
c.execute('''INSERT INTO problems
(folder, description, difficulty, test_code)
VALUES (?, ?, ?, ?)''',
(p['folder'], p['description'], p['difficulty'], p['test_code']))
conn.commit()
print(f"Updated database with {len(problems)} problems")

View File

@@ -0,0 +1,44 @@
## Reverse a List
Write a function called `reverse_list` that takes a list as input and returns the list in reverse order.
You are **not allowed** to just use Pythons built-in `.reverse()` method or slicing (`[::-1]`) — try to reverse it manually for practice.
### Function Signature:
```python
def reverse_list(lst):
# your code here
```
### Requirements
* The function should return a new list with the elements in reversed order.
* The input list can contain:
* Numbers
* Strings
* Booleans
* A mix of different types
* Your function will be tested with:
* A small list (e.g., `[1, 2, 3]``[3, 2, 1]`)
* A longer list (e.g., `[1, 2, 3, 4]``[4, 3, 2, 1]`)
* An empty list (should return an empty list)
* A single-element list (should return the same list)
* A mixed-type list (e.g., `[1, 'a', True]``[True, 'a', 1]`)
### Example
```python
reverse_list([1, 2, 3])
# Output: [3, 2, 1]
reverse_list([])
# Output: []
reverse_list([5])
# Output: [5]
reverse_list([1, 'a', True])
# Output: [True, 'a', 1]
```

View File

@@ -0,0 +1,7 @@
{
"title": "Reversed List",
"description": "Given a list, return a new list with the elements in reverse order.",
"description_md": "problems/reversedlist/description.md",
"difficulty": "easy",
"test_code": "problems/reversedlist/test.py"
}

View File

@@ -0,0 +1,27 @@
import unittest
#def reverse_list(lst : list) -> list:
#return lst[::-1]
class TestSolution(unittest.TestCase):
def test_simple(self):
test_cases = [
([1, 2, 3], [3, 2, 1]), # Simple case
([1, 2, 3, 4], [4, 3, 2, 1]), # Longer list
([], []), # Empty list
([5], [5]), # Single element list
([1, 'a', True], [True, 'a', 1]) # Mixed types
]
print("\n FUNCTION OUTPUT TEST RESULTS")
for input_val , expected in test_cases:
try:
actual = reverse_list(input_val) # pyright: ignore[reportUndefinedVariable]
status = "✓ PASS" if actual == expected else "✗ FAIL"
print(f"{status} | Input: {input_val} -> Got: {actual} | Expected: {expected}")
self.assertEqual(actual, expected)
except Exception as e:
print(f"✗ ERROR | Input: {input_val} -> Exception: {e}")
raise
if __name__ == "__main__":
unittest.main(verbosity=2)

View File

@@ -0,0 +1,40 @@
## Fibonacci Number
Write a function called `fibonacci` that takes a non-negative integer `n` as input and returns the **n-th Fibonacci number**.
The Fibonacci sequence is defined as:
* `F(0) = 0`
* `F(1) = 1`
* `F(n) = F(n-1) + F(n-2)` for `n > 1`
### Function Signature:
```python
def fibonacci(n):
# return your solution
```
#### Requirements
* The function should return the `n`-th number in the Fibonacci sequence.
* If `n` is less than `0`, print `"Incorrect input"`.
* Your function will be tested with:
* Base cases (`n = 0` and `n = 1`)
* Small values of `n`
* Larger values of `n` (e.g., 9)
* Multiple test cases in sequence
#### Example:
```python
fibonacci(0) # returns 0
fibonacci(1) # returns 1
fibonacci(2) # returns 1
fibonacci(3) # returns 2
fibonacci(5) # returns 5
fibonacci(9) # returns 34
```
You can copy this into your problems solution description.

View File

@@ -0,0 +1,7 @@
{
"title": "Fibonacci Sequence",
"description": "Calculate the n-th Fibonacci number using a function. The Fibonacci sequence is defined as follows: F(0) = 0, F(1) = 1, and F(n) = F(n-1) + F(n-2) for n > 1.",
"description_md": "problems/fibonacisequence/description.md",
"difficulty": "medium",
"test_code": "problems/fibonacisequence/test.py"
}

View File

@@ -0,0 +1,52 @@
import unittest
class TestSolution(unittest.TestCase):
def test_simple(self):
test_cases = [
(0, 0), # Base case: n = 0
(1, 1), # Base case: n = 1
(2, 1), # Fibonacci(2) = 1
(3, 2), # Fibonacci(3) = 2
(5, 5), # Fibonacci(5) = 5
(9, 34), # Fibonacci(9) = 34
]
print("\n=== Function Output Test Results ===")
for input_val, expected in test_cases:
try:
actual = fibonacci(input_val) # pyright: ignore[reportUndefinedVariable]
status = "✓ PASS" if actual == expected else "✗ FAIL"
print(f"{status} | Input: {input_val} -> Got: {actual} | Expected: {expected}")
self.assertEqual(actual, expected)
except Exception as e:
print(f"✗ ERROR | Input: {input_val} -> Exception: {e}")
raise
if __name__ == "__main__":
unittest.main(verbosity=2)
"""
def fibonacci(n):
a = 0
b = 1
# Check if n is less than 0
if n < 0:
print("Incorrect input")
# Check if n is equal to 0
elif n == 0:
return 0
# Check if n is equal to 1
elif n == 1:
return b
else:
for i in range(1, n):
c = a + b
a = b
b = c
return b
print(fibonacci(9))
"""

View File

@@ -2,5 +2,6 @@
"title":"Reversed String",
"description":"Reverse a String using a Function ; Try to write as little code as possible",
"description_md":"problems/reversedstring/description.md",
"difficulty":"easy",
"test_code":"problems/reversedstring/test.py"
}

View File

@@ -2,5 +2,6 @@
"title": "Sort List",
"description": "Sort a List with a Function (sortlist); the function is supposed to take the list as an argument and is supposed to return the sorted list and print it.",
"description_md": "problems/sortlist/description.md",
"difficulty": "easy",
"test_code": "problems/sortlist/test.py"
}

View File

@@ -73,6 +73,9 @@ header p {
.problems-list .problem-item {
padding: 8px;
border-bottom: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
align-items: center;
}
.problem-item:last-child {
border-bottom: none;
@@ -82,6 +85,28 @@ header p {
color: #0077ff;
font-weight: 600;
}
/* Difficulty badge */
.difficulty {
display: inline-flex;
align-items: center;
padding: 0.25em 0.6em;
border-radius: 10px;
font-size: 0.85em;
font-weight: bold;
text-transform: uppercase;
color: white;
white-space: nowrap;
}
.difficulty[data-difficulty="easy"] {
background-color: #4CAF50; /* Green */
}
.difficulty[data-difficulty="medium"] {
background-color: #FFC107; /* Amber */
color: #333;
}
.difficulty[data-difficulty="hard"] {
background-color: #F44336; /* Red */
}
/* Leaderboard */
.leaderboard-head {
display: flex;
@@ -155,6 +180,6 @@ header p {
@media (max-width: 800px) {
.content { grid-template-columns: 1fr; }
.leaderboard-controls {
flex-direction: column;
flex-direction: column;
}
}

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -14,29 +14,29 @@
<header>
<h1>Quick Problem Platform</h1>
</header>
<div class="content" id="contentContainer">
<!-- Problems -->
<section class="card problems-list">
<div class="search-controls">
<input
type="text"
class="search-input"
id="problemSearch"
placeholder="Search problems..."
/>
<section class="card problems-list">
<div class="search-controls">
<input
type="text"
class="search-input"
id="problemSearch"
placeholder="Search problems..."
/>
</div>
<h2 style="margin-bottom:6px;font-size:1.1rem">Problems</h2>
<div id="problemsContainer">
{% for folder, description, test_code, difficulty in problems %}
<div class="problem-item" data-name="{{ folder.replace('_',' ').title() }}" data-desc="{{ description }}">
<a href="/problem/{{ folder }}">{{ folder.replace('_',' ').title() }}</a>
<span class="difficulty" data-difficulty="{{ difficulty|lower }}">{{ difficulty }}</span>
</div>
<h2 style="margin-bottom:6px;font-size:1.1rem">Problems</h2>
<div id="problemsContainer">
{% for folder, description, test_code in problems %}
<div class="problem-item" data-name="{{ folder.replace('_',' ').title() }}" data-desc="{{ description }}">
<a href="/problem/{{ folder }}">{{ folder.replace('_',' ').title() }}</a>
</div>
{% else %}
<div class="problem-item">No problems yet.</div>
{% endfor %}
</div>
</section>
{% else %}
<div class="problem-item">No problems yet.</div>
{% endfor %}
</div>
</section>
<!-- Leaderboard -->
<section class="card" id="leaderboardSection">

296
src/templates/problem.html Normal file
View File

@@ -0,0 +1,296 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ problem.title }} - Coding Problem</title>
<link rel="stylesheet" href="/static/style.css">
<link href="https://fonts.cdnfonts.com/css/jetbrains-mono" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
margin: 0;
padding: 0;
background-color: #f9f9f9;
color: #333;
min-height: 100vh; /* allow content to grow */
overflow-y: auto; /* allow vertical scroll */
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
.main-container {
display: flex;
flex-wrap: wrap; /* wrap on small screens */
min-height: 100vh;
width: 100vw;
}
.problem-panel {
flex: 1 1 400px; /* grow/shrink with base 400px */
min-width: 300px;
background: white;
overflow-y: auto;
padding: 20px;
border-right: 1px solid #eaeaea;
max-height: 100vh;
}
.editor-container {
flex: 1 1 400px;
min-width: 300px;
display: flex;
flex-direction: column;
background: white;
max-height: 100vh;
overflow: hidden; /* internal scroll handling */
}
.editor-header {
padding: 15px 20px;
border-bottom: 1px solid #eaeaea;
flex-shrink: 0;
}
.editor-wrapper {
flex: 1 1 auto;
display: flex;
flex-direction: column;
min-height: 0;
padding: 0 20px;
overflow-y: auto;
}
.problem-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.back-btn {
background: none;
border: none;
cursor: pointer;
font-size: 16px;
color: #666;
margin-right: 15px;
padding: 5px;
}
.back-btn:hover {
color: #000;
}
h1 {
font-size: 22px;
font-weight: 600;
margin: 0;
color: #1a1a1a;
}
.problem-desc {
line-height: 1.6;
font-size: 15px;
overflow-wrap: break-word;
}
.problem-desc pre {
background: #f6f8fa;
padding: 12px;
border-radius: 4px;
overflow-x: auto;
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
}
.problem-desc code {
background: #f6f8fa;
padding: 2px 4px;
border-radius: 3px;
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
}
.editor-actions {
padding: 15px 0;
display: flex;
justify-content: flex-end;
flex-shrink: 0;
}
.editor-actions button {
background-color: #007bff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
font-size: 14px;
}
.editor-actions button:hover {
background-color: #0069d9;
}
#editor {
flex: 1 1 auto;
min-height: 300px;
border: 1px solid #ddd;
border-radius: 4px;
overflow: auto;
max-height: 60vh;
}
.result-panel {
margin-top: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 4px;
margin-bottom: 20px;
min-height: 120px;
overflow-y: auto;
max-height: 30vh;
}
.result-panel h3 {
margin-top: 0;
font-size: 16px;
margin-bottom: 10px;
}
.result-panel pre {
background: #f6f8fa;
padding: 12px;
border-radius: 4px;
overflow-x: auto;
white-space: pre-wrap;
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
margin: 5px 0;
}
.placeholder {
color: #999;
font-style: italic;
text-align: center;
padding: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-size: 14px;
color: #666;
}
input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 15px;
font-family: 'Inter', sans-serif;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.main-container {
flex-direction: column;
height: auto;
overflow-y: visible;
}
.problem-panel, .editor-container {
flex: none;
width: 100%;
min-width: auto;
max-height: none;
border-right: none;
border-bottom: 1px solid #eaeaea;
}
#editor {
min-height: 400px;
max-height: none;
}
.result-panel {
max-height: none;
}
}
</style>
</head>
<body>
<div class="main-container">
<div class="problem-panel">
<div class="problem-header">
<button class="back-btn" onclick="window.location.href='/'">← Back</button>
<h1>{{ problem.title }}</h1>
</div>
<div class="problem-desc">{{ problem.description | safe | markdown }}</div>
</div>
<div class="editor-container">
<div class="editor-header">
<h2 style="margin:0;font-size:18px;">Submit Your Solution (Python)</h2>
</div>
<div class="editor-wrapper">
<form method="post">
<label for="username">Username (optional):</label>
<input type="text" name="username" id="username" placeholder="Anonymous">
<div id="editor"></div>
<textarea name="user_code" id="user_code" style="display:none;"></textarea>
<div class="editor-actions">
<button type="submit">Run & Submit</button>
</div>
</form>
<div class="result-panel">
<h3>Result</h3>
{% if result %}
<p><b>Runtime:</b> {{ '%.4f'|format(result.runtime) }} seconds</p>
<p><b>Output:</b></p>
<pre>{{ result.output }}</pre>
{% if result.error %}
<p><b>Error:</b></p>
<pre>{{ result.error }}</pre>
{% endif %}
{% else %}
<div class="placeholder">
Your code execution results will appear here
</div>
{% endif %}
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs/loader.js"></script>
<script>
require.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs' } });
require(['vs/editor/editor.main'], function() {
var editor = monaco.editor.create(document.getElementById('editor'), {
value: '',
language: 'python',
theme: 'vs-light',
fontFamily: 'JetBrains Mono, monospace',
fontLigatures: true,
automaticLayout: true,
fontSize: 16,
minimap: { enabled: false }
});
document.querySelector('form').addEventListener('submit', function(e) {
var code = editor.getValue();
if (!code.trim()) {
alert('Please enter your code before submitting.');
e.preventDefault();
return false;
}
document.getElementById('user_code').value = code;
});
});
</script>
</body>
</html>

View File

@@ -1,66 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ problem.title }}</title>
<link rel="stylesheet" href="/static/style.css">
<link href="https://fonts.cdnfonts.com/css/jetbrains-mono" rel="stylesheet">
</head>
<body>
<div class="problem-header">
<button class="back-btn" onclick="window.location.href='/'">← Back</button>
<h1 style="margin-bottom:0;">{{ problem.title }}</h1>
</div>
<div class="problem-desc">{{ problem.description | safe | markdown }}</div>
<div class="editor-section" style="max-width:1160;margin:0">
<h2 style="margin-top:0;">Submit Your Solution (Python)</h2>
<form method="post">
<label for="username">Username (optional):</label>
<input type="text" name="username" id="username" placeholder="Anonymous" style="margin-bottom:10px;">
<div id="editor"></div>
<textarea name="user_code" id="user_code" style="display:none;"></textarea>
<div class="editor-actions">
<button type="submit">Run & Submit</button>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs/loader.js"></script>
<script>
require.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs' } });
require(['vs/editor/editor.main'], function() {
var editor = monaco.editor.create(document.getElementById('editor'), {
value: '',
language: 'python',
theme: 'vs-light',
fontFamily: 'JetBrains Mono, monospace',
fontLigatures: true,
automaticLayout: true,
fontSize: 16,
minimap: { enabled: false }
});
document.querySelector('form').addEventListener('submit', function(e) {
var code = editor.getValue();
if (!code.trim()) {
alert('Please enter your code before submitting.');
e.preventDefault();
return false;
}
document.getElementById('user_code').value = code;
});
});
</script>
{% if result %}
<div class="editor-section" style="max-width:1160;margin:0;margin-top: 5px;">
<h3>Result:</h3>
<b>Runtime:</b> {{ '%.4f'|format(result.runtime) }} seconds<br>
<b>Output:</b>
<pre>{{ result.output }}</pre>
{% if result.error %}
<b>Error:</b>
<pre>{{ result.error }}</pre>
{% endif %}
</div>
{% endif %}
</body>
</html>