Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
83f85ab
chore: add example chunks
Saannddy Mar 17, 2026
c8072bb
Merge pull request #45 from Saannddy/add-example-chunks
Saannddy Mar 17, 2026
e185fd9
feat: implement update problem title by ID
PunTwT Mar 19, 2026
cafb050
Merge pull request #46 from Saannddy/feat/update-problem-title
Saannddy Mar 21, 2026
cfa1c08
feat: add converter scripts for chunks and riddles, and populate JSON…
Saannddy Mar 21, 2026
2147b10
feat: enhance random chunk retrieval with tag filtering and update re…
Saannddy Mar 21, 2026
60c6b51
Merge pull request #47 from Saannddy/update-utils-folder-that-contain…
Saannddy Mar 21, 2026
a5a2e80
feat: Add Java problems, questions, and chunks for the hallway, and i…
Saannddy Mar 21, 2026
81fb52a
feat: refactor chunk processing to improve template handling and snip…
Saannddy Mar 23, 2026
5eb0e6d
feat: Add Java problems, questions, and chunks for the hallway, and i…
Saannddy Mar 21, 2026
7d93589
Merge branch 'update-hallway-seeder' of https://github.com/Saannddy/C…
Saannddy Mar 29, 2026
d36e25e
Merge pull request #48 from Saannddy/update-hallway-seeder
Saannddy Apr 3, 2026
5688e4d
fix: Sort snippet keys in chunk_service.py
Saannddy Apr 3, 2026
b703c7d
chore: remove mandate comments on sorting snippets dictionary keys
Saannddy Apr 3, 2026
cf72739
refactor: update Java snippet descriptions to use descriptive, human-…
Saannddy Apr 3, 2026
7315d89
refactor: update Java chunk schema to use object-based templates and …
Saannddy Apr 3, 2026
a450c84
feat: add category support to chunk retrieval and implement Elevator …
Saannddy Apr 3, 2026
070823e
refactor: update seeder imports to avoid circular dependency warnings…
Saannddy Apr 3, 2026
85a6140
chore: remove package initialization comment from scripts module
Saannddy Apr 3, 2026
8519a77
feat: add level 4 Java coding challenges and update chunk retrieval c…
Saannddy Apr 5, 2026
3eb3fd8
Merge pull request #57 from Saannddy/add-data-elevator-hallway-and-ca…
Saannddy Apr 6, 2026
cbe4363
Merge pull request #56 from Saannddy/debug-the-always-pass-condition-…
Saannddy Apr 6, 2026
07157f4
Merge pull request #55 from Saannddy/update-chunks-data-add-clue-for-…
Saannddy Apr 6, 2026
a35fbf7
Merge pull request #54 from Saannddy/critical-bug-fix-on-non-sorted-s…
Saannddy Apr 6, 2026
8368ef0
Merge pull request #49 from Saannddy/update-regex-pattern-to-detect-a…
Saannddy Apr 6, 2026
f0bd77d
Merge pull request #52 from Saannddy/dev
Saannddy Apr 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions bruno/local/Chunk/Execute Java Chunk.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
meta {
name: Execute Java Chunk
type: http
seq: 8
}

post {
url: http://localhost:3000/chunk/execute/22e04d50-3aea-43ac-9a89-32daea0cc9f8?lang=java
body: json
auth: none
}

params:query {
lang: java
}

body:json {
{
"snippets": {
"logic": "return x * y"
}
}
}
6 changes: 3 additions & 3 deletions bruno/local/Chunk/Get Random Chunks.bru
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ meta {
}

get {
url: {{base_url}}/chunk/random
url: {{base_url}}/chunk/random?tags=JAV_ELVHALL&category=ELV-P5
body: none
auth: none
}

params:query {
limit: 2
lang: javascript
tags: JAV_ELVHALL
category: ELV-P5
}
21 changes: 21 additions & 0 deletions bruno/local/Problem/PATCH update problem title by ID.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
meta {
name: PATCH update problem title by ID
type: http
seq: 7
}

patch {
url: {{base_url}}/problem/2a3380b4-fbc7-517d-9583-01d41d1cbb80/title
body: json
auth: inherit
}

body:json {
{
"title": "Multiply Two Numbers (2)"
}
}

settings {
encodeUrl: true
}
7 changes: 6 additions & 1 deletion src/api/routes/problem_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ def add_test_cases(problem_id):
@problem_bp.post('/<problem_id>/testcases/import')
def import_test_cases(problem_id):
""" Import test cases from a ZIP file (Base: /problem/<id>/testcases/import)"""
return problem_handler.import_test_cases(problem_id)
return problem_handler.import_test_cases(problem_id)

@problem_bp.patch('/<problem_id>/title')
def update_problem_title(problem_id):
"""Update problem title (Base: /problem/<id>/title)"""
return problem_handler.update_problem_title(problem_id)
6 changes: 5 additions & 1 deletion src/handlers/chunk_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ def get_chunk(self, chunk_id):
def get_random_chunks(self):
limit = request.args.get('limit', default=1, type=int)
lang = request.args.get('lang')
category = request.args.get('category')
tags = request.args.get('tags')
if tags:
tags = [tag.strip() for tag in tags.split(',')]

chunks = self.service.get_random_chunks(limit=limit, lang=lang)
chunks = self.service.get_random_chunks(limit=limit, lang=lang, tags=tags, category=category)
return jsonify(status="success", data=chunks), 200
18 changes: 18 additions & 0 deletions src/handlers/problem_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ def get_random_problem(self):
if not problem:
return jsonify(status="error", message="No problems found"), 404
return jsonify(status="success", data=problem), 200

def update_problem_title(self, problem_id):
""" Update problem title """
if not request.is_json:
return jsonify(status='error', message='Request must be JSON'), 400

data = request.get_json()
new_title = data.get('title')

if not new_title:
return jsonify(status='error', message='Title is required'), 400

new_title = new_title.strip()
result = self.problem_service.update_problem_title(problem_id, new_title)

if result['status'] == 'error':
return jsonify(status='error', message=result.get('message')), 400
return jsonify(status='success', data=result.get('data')), 200

def add_test_cases(self, problem_id):
""" Add multiple test case """
Expand Down
29 changes: 29 additions & 0 deletions src/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,35 @@ paths:
'404':
description: Problem not found

/problem/{problem_id}/title:
patch:
summary: Update problem title
parameters:
- name: problem_id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [title]
properties:
title:
type: string
example: "Two Sum Updated"
responses:
'200':
description: Problem title updated successfully
'400':
description: Invalid request
'404':
description: Problem not found

/problem/{problem_id}/testcases:
post:
summary: Add test cases to a problem
Expand Down
42 changes: 35 additions & 7 deletions src/repositories/chunk_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,48 @@ def get_details(self, chunk_id, lang=None):

return self._serialize_chunk(chunk, lang)

def find_random(self, limit=1, lang=None):
"""Fetch random N chunks with their implementation details. Filters by language if provided."""
def find_random(self, limit=1, lang=None, tags=None, category=None):
"""Fetch random N chunks with their implementation details. Filters by language, tags, and category if provided."""
with self._get_session() as session:
statement = select(Chunk).order_by(func.random())
# For simplicity, if lang is provided, we fetch more and filter in Python
# or just fetch all and filter.
results = session.exec(statement).all()
statement = select(Chunk).options(
joinedload(Chunk.templates).joinedload(ChunkTemplate.snippets),
joinedload(Chunk.tags),
joinedload(Chunk.categories)
).order_by(func.random())

# If language is specified, filter chunks that HAVE at least one template in that language
if lang:
statement = statement.join(Chunk.templates).where(ChunkTemplate.language == lang)

# If tags or category are specified, we might need more to filter in Python
if tags or category:
fetch_limit = limit * 20 # Fetch more to account for filtering
statement = statement.limit(fetch_limit)
else:
statement = statement.limit(limit * 2) # Some buffer

results = session.exec(statement).unique().all()
if not results:
return []

chunks = []
for chunk in results:
if lang and lang not in chunk.config.get("templates", {}):
# Basic language check (redundant but safe)
if lang and lang not in [t.language for t in chunk.templates]:
continue

# Tag check
if tags:
chunk_tag_names = [t.name for t in chunk.tags]
if not all(tag in chunk_tag_names for tag in tags):
continue

# Category check
if category:
chunk_category_names = [c.name for c in chunk.categories]
if category not in chunk_category_names:
continue

chunks.append(self._serialize_chunk(chunk, lang))
if len(chunks) >= limit:
break
Expand Down
13 changes: 13 additions & 0 deletions src/repositories/problem_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,16 @@ def find_random(self, category_name=None, tag_name=None, limit=1):
problems.append(p_dict)

return problems

def update_title_by_id(self, problem_id, new_title):
""" Update problem title """
with self._get_session() as session:
problem = session.get(Problem, problem_id)
if not problem:
return {"status": "error", "message": "Problem not found"}

problem.title = new_title
session.commit()
session.refresh(problem)

return {"status": "success", "data": problem.model_dump(exclude={"config", "description"})}
1 change: 0 additions & 1 deletion src/scripts/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
# This file makes the folder a Python package.
162 changes: 162 additions & 0 deletions src/scripts/data/java/elevatorhall/chunks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
[
{
"title": "ELV-P4-CHUNK-001",
"difficulty": "Medium",
"category": "ELV-P4",
"templates": {
"java": {
"name": "Java Implementation",
"template_code": "// [NULL] stripped the exception handling from the ID scanner.\n// It now crashes on any non-integer input and floods the audit log with errors.\n// Restore the three blocks. Every scan must be logged, pass or fail.\n\npublic class CheckpointScanner {\n\n public static String parseStudentID(String input) {\n try {\n // TASK 1: Parse input. Return \"VALID:\" + id if 10000\u201399999, else \"INVALID:OUT_OF_RANGE\".\n {{{snippet_1}}}\n } catch (NumberFormatException e) {\n // TASK 2: Return \"INVALID:NOT_A_NUMBER\".\n {{{snippet_2}}}\n } finally {\n // TASK 3: Always print \"scan attempt logged\".\n {{{snippet_3}}}\n }\n }\n}",
"snippets": {
"snippet_1": "None to be shown here",
"snippet_2": "None to be shown here",
"snippet_3": "None to be shown here"
}
}
},
"expectation": {
"input": "12345",
"output": "VALID:12345"
}
},
{
"title": "ELV-P4-CHUNK-002",
"difficulty": "Medium",
"category": "ELV-P4",
"templates": {
"java": {
"name": "Java Implementation",
"template_code": "// The room code scanner is throwing unhandled exceptions on every bad input.\n// [NULL] knew exactly which lines to delete to break the audit trail.\n// Restore all three exception blocks.\n\npublic class CheckpointScanner {\n\n public static String parseRoomCode(String input) {\n try {\n // TASK 1: Parse input. Return \"ROOM:\" + code if 100\u2013999, else \"INVALID:NOT_A_ROOM\".\n {{{snippet_1}}}\n } catch (NumberFormatException e) {\n // TASK 2: Return \"INVALID:FORMAT_ERROR\".\n {{{snippet_2}}}\n } finally {\n // TASK 3: Always print \"door scan logged\".\n {{{snippet_3}}}\n }\n }\n}",
"snippets": {
"snippet_1": "None to be shown here",
"snippet_2": "None to be shown here",
"snippet_3": "None to be shown here"
}
}
},
"expectation": {
"input": "123",
"output": "ROOM:123"
}
},
{
"title": "ELV-P4-CHUNK-003",
"difficulty": "Medium",
"category": "ELV-P4",
"templates": {
"java": {
"name": "Java Implementation",
"template_code": "// The port scanner crashes on anything that is not a clean integer.\n// Without proper exception handling the network audit trail is blind.\n// Restore it.\n\npublic class CheckpointScanner {\n\n public static String parsePort(String input) {\n try {\n // TASK 1: Parse input. Return \"PORT:\" + number if 1\u201365535, else \"INVALID:PORT_OUT_OF_RANGE\".\n {{{snippet_1}}}\n } catch (NumberFormatException e) {\n // TASK 2: Return \"INVALID:NOT_A_PORT\".\n {{{snippet_2}}}\n } finally {\n // TASK 3: Always print \"connection attempt logged\".\n {{{snippet_3}}}\n }\n }\n}",
"snippets": {
"snippet_1": "None to be shown here",
"snippet_2": "None to be shown here",
"snippet_3": "None to be shown here"
}
}
},
"expectation": {
"input": "80",
"output": "PORT:80"
}
},
{
"title": "ELV-P4-CHUNK-004",
"difficulty": "Medium",
"category": "ELV-P4",
"templates": {
"java": {
"name": "Java Implementation",
"template_code": "// The elevator rejects every floor request because the parser throws on bad input.\n// [NULL] removed the safety net. Restore the exception blocks.\n// The finally block is not optional \u2014 the lift log must always write.\n\npublic class CheckpointScanner {\n\n public static String parseFloor(String input) {\n try {\n // TASK 1: Parse input. Return \"FLOOR:\" + number if 1\u201310, else \"INVALID:NO_SUCH_FLOOR\".\n {{{snippet_1}}}\n } catch (NumberFormatException e) {\n // TASK 2: Return \"INVALID:NOT_A_NUMBER\".\n {{{snippet_2}}}\n } finally {\n // TASK 3: Always print \"elevator request logged\".\n {{{snippet_3}}}\n }\n }\n}",
"snippets": {
"snippet_1": "None to be shown here",
"snippet_2": "None to be shown here",
"snippet_3": "None to be shown here"
}
}
},
"expectation": {
"input": "5",
"output": "FLOOR:5"
}
},
{
"title": "ELV-P5-CHUNK-001",
"difficulty": "Medium",
"category": "ELV-P5",
"templates": {
"java": {
"name": "Java Implementation",
"template_code": "// 4,847 log entries. The threat filter is gone \u2014 [NULL] deleted it first.\n// Without it the emergency system cannot assess severity.\n// Find the threats. The building needs this report before it can act.\n\nimport java.util.ArrayList;\n\npublic class ThreatAnalyser {\n\n public static String analyzeLogs(ArrayList<String> logs) {\n\n // TASK 1: Declare a counter and an ArrayList<String> for threat descriptions.\n {{{snippet_1}}}\n\n for (String entry : logs) {\n // TASK 2: If entry starts with \"THREAT:\", increment and collect the description (after 8 chars).\n {{{snippet_2}}}\n }\n\n // TASK 3: Return \"THREATS:0|none\" if clean, else \"THREATS:\" + count + \"|\" + descriptions joined by \",\".\n {{{snippet_3}}}\n }\n\n public static void main(String[] args) {\n ArrayList<String> logs1 = new ArrayList<>();\n logs1.add(\"INFO: door opened\");\n logs1.add(\"THREAT: brute force\");\n logs1.add(\"INFO: scan ok\");\n logs1.add(\"THREAT: port scan\");\n logs1.add(\"THREAT: login fail\");\n System.out.println(analyzeLogs(logs1));\n\n ArrayList<String> logs2 = new ArrayList<>();\n logs2.add(\"INFO: all clear\");\n System.out.println(analyzeLogs(logs2));\n }\n}",
"snippets": {
"snippet_1": "None to be shown here",
"snippet_2": "None to be shown here",
"snippet_3": "None to be shown here"
}
}
},
"expectation": {
"input": "",
"output": ""
}
},
{
"title": "ELV-P5-CHUNK-002",
"difficulty": "Medium",
"category": "ELV-P5",
"templates": {
"java": {
"name": "Java Implementation",
"template_code": "// The server error filter was wiped along with the intrusion logs.\n// Critical errors are invisible to the monitoring system.\n// Restore the filter \u2014 find every ERROR entry and report the count.\n\nimport java.util.ArrayList;\n\npublic class ThreatAnalyser {\n\n public static String filterErrors(ArrayList<String> logs) {\n\n // TASK 1: Declare a counter and an ArrayList<String> for error messages.\n {{{snippet_1}}}\n\n for (String entry : logs) {\n // TASK 2: If entry starts with \"ERROR:\", increment and collect the message (after 7 chars).\n {{{snippet_2}}}\n }\n\n // TASK 3: Return \"ERRORS:0|none\" if clean, else \"ERRORS:\" + count + \"|\" + messages joined by \",\".\n {{{snippet_3}}}\n }\n\n public static void main(String[] args) {\n ArrayList<String> logs1 = new ArrayList<>();\n logs1.add(\"INFO: server started\");\n logs1.add(\"ERROR: disk full\");\n logs1.add(\"ERROR: null pointer\");\n logs1.add(\"INFO: backup ok\");\n System.out.println(filterErrors(logs1));\n\n ArrayList<String> logs2 = new ArrayList<>();\n logs2.add(\"INFO: running\");\n System.out.println(filterErrors(logs2));\n }\n}",
"snippets": {
"snippet_1": "None to be shown here",
"snippet_2": "None to be shown here",
"snippet_3": "None to be shown here"
}
}
},
"expectation": {
"input": "",
"output": ""
}
},
{
"title": "ELV-P5-CHUNK-003",
"difficulty": "Medium",
"category": "ELV-P5",
"templates": {
"java": {
"name": "Java Implementation",
"template_code": "// The badge access log is full of denied entries \u2014 someone was probing every door.\n// The scanner that should have flagged them is offline.\n// Find every DENIED entry before [NULL] makes another move.\n\nimport java.util.ArrayList;\n\npublic class ThreatAnalyser {\n\n public static String findDenied(ArrayList<String> entries) {\n\n // TASK 1: Declare a counter and an ArrayList<String> for denied entries.\n {{{snippet_1}}}\n\n for (String entry : entries) {\n // TASK 2: If entry contains \"DENIED\" anywhere, increment and collect the full entry.\n {{{snippet_2}}}\n }\n\n // TASK 3: Return \"DENIED:0|none\" if none, else \"DENIED:\" + count + \"|\" + entries joined by \",\".\n {{{snippet_3}}}\n }\n\n public static void main(String[] args) {\n ArrayList<String> e1 = new ArrayList<>();\n e1.add(\"Student 001: GRANTED\");\n e1.add(\"Student 002: DENIED low clearance\");\n e1.add(\"Student 003: GRANTED\");\n e1.add(\"Visitor 01: DENIED no badge\");\n System.out.println(findDenied(e1));\n\n ArrayList<String> e2 = new ArrayList<>();\n e2.add(\"Staff 001: GRANTED\");\n System.out.println(findDenied(e2));\n }\n}",
"snippets": {
"snippet_1": "None to be shown here",
"snippet_2": "None to be shown here",
"snippet_3": "None to be shown here"
}
}
},
"expectation": {
"input": "",
"output": ""
}
},
{
"title": "ELV-P5-CHUNK-004",
"difficulty": "Medium",
"category": "ELV-P5",
"templates": {
"java": {
"name": "Java Implementation",
"template_code": "// Elevator requests are piling up but the floor filter is down.\n// Security needs to know exactly how many calls are going to each floor.\n// Restore the filter \u2014 match by floor number, report the count.\n\nimport java.util.ArrayList;\n\npublic class ThreatAnalyser {\n\n public static String findFloorRequests(ArrayList<String> logs, int floor) {\n\n // TASK 1: Declare a counter and an ArrayList<String> for matching requests.\n {{{snippet_1}}}\n\n for (String entry : logs) {\n // TASK 2: If entry ends with \":\" + floor, increment and collect the full entry.\n {{{snippet_2}}}\n }\n\n // TASK 3: Return \"FLOOR\" + floor + \":0|none\" if none,\n // else \"FLOOR\" + floor + \":\" + count + \"|\" + entries joined by \",\".\n {{{snippet_3}}}\n }\n\n public static void main(String[] args) {\n ArrayList<String> logs = new ArrayList<>();\n logs.add(\"User A requested floor:2\");\n logs.add(\"User B requested floor:3\");\n logs.add(\"User C requested floor:2\");\n logs.add(\"User D requested floor:1\");\n System.out.println(findFloorRequests(logs, 2));\n System.out.println(findFloorRequests(logs, 5));\n }\n}",
"snippets": {
"snippet_1": "None to be shown here",
"snippet_2": "None to be shown here",
"snippet_3": "None to be shown here"
}
}
},
"expectation": {
"input": "",
"output": ""
}
}
]
Loading
Loading