some more info and some difficulty added!

This commit is contained in:
2025-08-12 18:56:52 +02:00
parent 0bffdf612c
commit c7c1b8ecd6
8 changed files with 116 additions and 43 deletions

13
app.py
View File

@@ -41,13 +41,14 @@ def index():
db_path = Path(__file__).parent / 'problems.sqlite3' db_path = Path(__file__).parent / 'problems.sqlite3'
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
c = conn.cursor() 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() problems = c.fetchall()
conn.close() conn.close()
# Get leaderboard entries # Get leaderboard entries
leaderboard = get_leaderboard() leaderboard = get_leaderboard()
# Map folder to title for display # 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) return render_template('index.html', problems=problems, leaderboard=leaderboard, problem_titles=problem_titles)
@app.route('/problem/new', methods=['GET', 'POST']) @app.route('/problem/new', methods=['GET', 'POST'])
@@ -67,17 +68,21 @@ def view_problem(folder):
db_path = Path(__file__).parent / 'problems.sqlite3' db_path = Path(__file__).parent / 'problems.sqlite3'
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
c = conn.cursor() 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() row = c.fetchone()
conn.close() conn.close()
if not row: if not row:
return 'Problem not found', 404 return 'Problem not found', 404
problem = { problem = {
'folder': row[0], 'folder': row[0],
'description': row[1], 'description': row[1],
'test_code': row[2], 'difficulty': row[2],
'test_code': row[3], # This is used internally, not displayed
'title': row[0].replace('_', ' ').title() 'title': row[0].replace('_', ' ').title()
} }
result = None result = None
if request.method == 'POST': if request.method == 'POST':
user_code = request.form['user_code'] user_code = request.form['user_code']

View File

@@ -35,6 +35,7 @@ class ProblemScannerThread(threading.Thread):
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
folder TEXT, folder TEXT,
description TEXT, description TEXT,
difficulty TEXT,
test_code TEXT test_code TEXT
)''') )''')
conn.commit() conn.commit()
@@ -65,13 +66,18 @@ class ProblemScannerThread(threading.Thread):
description = f.read() description = f.read()
with open(test_path, 'r', encoding='utf-8') as f: with open(test_path, 'r', encoding='utf-8') as f:
test_code = f.read() 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({ problems.append({
'folder': folder.name, 'folder': folder.name,
'description': description, '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: except Exception as e:
print(f"Error reading problem files for {folder.name}: {e}") print(f"Error reading problem files for {folder.name}: {e}")
else: else:
@@ -99,8 +105,10 @@ class ProblemScannerThread(threading.Thread):
# Insert new problems # Insert new problems
for p in problems: for p in problems:
c.execute('INSERT INTO problems (folder, description, test_code) VALUES (?, ?, ?)', c.execute('''INSERT INTO problems
(p['folder'], p['description'], p['test_code'])) (folder, description, difficulty, test_code)
VALUES (?, ?, ?, ?)''',
(p['folder'], p['description'], p['difficulty'], p['test_code']))
conn.commit() conn.commit()
print(f"Updated database with {len(problems)} problems") print(f"Updated database with {len(problems)} problems")

View File

@@ -2,5 +2,6 @@
"title": "Fibonacci Sequence", "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": "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", "description_md": "problems/fibonacisequence/description.md",
"difficulty": "medium",
"test_code": "problems/fibonacisequence/test.py" "test_code": "problems/fibonacisequence/test.py"
} }

View File

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

View File

@@ -2,5 +2,6 @@
"title": "Sort List", "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": "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", "description_md": "problems/sortlist/description.md",
"difficulty": "easy",
"test_code": "problems/sortlist/test.py" "test_code": "problems/sortlist/test.py"
} }

View File

@@ -6,23 +6,55 @@ but more lightweight
if you want to contribute write tests like this: 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 ```python
"""
@TESTSAMPLE.PY / NAME THIS "test.py" in your actual project
"""
import unittest import unittest
#<!-- The Function the User needs to write --> " )) First Point from the List "
# This is importantly commented out. The UnitTest tests this if uncommented. # def sortlist(lst = [4,3,2,1]) -> list:
# This is only here for reference # return sorted(lst)
#
# def revstring(x):
# return x[::-1]
#<!-- 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): class TestSolution(unittest.TestCase):
def test_simple(self): def test_sort(self):
# !! This needs to be dynamic ; if the user enters some shit then it is supposed to work too self.x = []
x=""; self.assertEqual(sortlist(self.x), sorted(self.x)) # pyright: ignore[reportUndefinedVariable] <- This is only here so that pyright doesnt complain ; NOT NECCESARY!
self.assertEqual(revstring(x), x[::-1])
if __name__ == "__main__": if __name__ == "__main__":
unittest.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

View File

@@ -73,6 +73,9 @@ header p {
.problems-list .problem-item { .problems-list .problem-item {
padding: 8px; padding: 8px;
border-bottom: 1px solid #e5e7eb; border-bottom: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
align-items: center;
} }
.problem-item:last-child { .problem-item:last-child {
border-bottom: none; border-bottom: none;
@@ -82,6 +85,28 @@ header p {
color: #0077ff; color: #0077ff;
font-weight: 600; 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 */
.leaderboard-head { .leaderboard-head {
display: flex; display: flex;
@@ -155,6 +180,6 @@ header p {
@media (max-width: 800px) { @media (max-width: 800px) {
.content { grid-template-columns: 1fr; } .content { grid-template-columns: 1fr; }
.leaderboard-controls { .leaderboard-controls {
flex-direction: column; flex-direction: column;
} }
} }

View File

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