feat(blockly): Blockly visual programming interface - Sprint 1 - #260
Draft
ikramelaimaa wants to merge 2 commits into
Draft
feat(blockly): Blockly visual programming interface - Sprint 1#260ikramelaimaa wants to merge 2 commits into
ikramelaimaa wants to merge 2 commits into
Conversation
- Add Blockly editor with Python code generation - Add AI exercise generation via Ollama (SSE streaming) - Add Python sandbox execution (subprocess, 5s timeout) - Add AI feedback with score /100 - Add level progression system (beginner -> intermediate -> advanced) - Add Blockly button in student dashboard with form popup - Fix Vite proxy: open-tutor-backend -> localhost:8080 Co-authored-by: Oumaima GAOURGOUT Co-authored-by: Hicham BAAKI
ikramelaimaa
marked this pull request as draft
June 11, 2026 09:13
Dakir-Ai
reviewed
Jun 27, 2026
Dakir-Ai
left a comment
Contributor
There was a problem hiding this comment.
Hi !
I noticed the draft PR currently has merge conflicts.
Please resolve them before marking it as ready for review
There was a problem hiding this comment.
Pull request overview
This PR introduces a first sprint of a Blockly-based visual programming workflow, adding a new student UI route for building blocks → generating Python, along with backend endpoints for exercise generation, code execution, and AI feedback streaming.
Changes:
- Added a new Blockly student page (
/student/blockly/new) that generates exercises, runs generated Python, and streams AI feedback/score. - Added backend
learning/blocklymodule (router/service/sandbox/models/schemas) plus an Ollama-based LLM generator. - Updated the student dashboard to launch Blockly via a popup, and adjusted Vite proxy settings / UI dependencies.
Reviewed changes
Copilot reviewed 13 out of 16 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
ui/vite.config.ts |
Changes dev proxy targets for /api and /ws. |
ui/src/routes/student/blockly/new/+page.svelte |
New Blockly UI: exercise generation (SSE), block workspace, code execution and submission (SSE). |
ui/src/lib/components/student/pages/Dashboard.svelte |
Adds “Blockly” button + popup to capture context and navigate to Blockly page; refactors some existing logic. |
ui/src/lib/components/student/pages/Dashboard.svelte.backup |
Adds a large backup copy of the dashboard component (should not be committed). |
ui/package.json |
Adds Blockly dependency and coverage tool entry adjustment. |
ui/package-lock.json |
Lockfile updates reflecting new dependencies. |
package-lock.json |
Adds a root lockfile with empty packages (likely accidental). |
main.py |
Registers the Blockly router on the FastAPI app (currently placed after the blocking main() call). |
learning/blockly/service.py |
Introduces service layer wrapping sandbox execution and scoring. |
learning/blockly/schemas.py |
Adds Pydantic schemas for execution requests/results and assignments. |
learning/blockly/sandbox.py |
Implements Python execution via subprocess.run with a timeout. |
learning/blockly/router.py |
Adds /api/blockly/* endpoints for execute/test/submit/generate/workspace and SSE streaming. |
learning/blockly/models.py |
Adds SQLAlchemy models for submissions and saved workspaces. |
learning/blockly/__init__.py |
Initializes the new module. |
devops/docker/docker-compose.piston.yml |
Adds a Piston container definition (currently privileged). |
ai/llm/blockly_generator.py |
Adds Ollama-backed generator for exercises and feedback. |
Files not reviewed (1)
- ui/package-lock.json: Generated file
Comment on lines
43
to
+45
| if __name__ == "__main__": | ||
| main() | ||
| app.include_router(blockly_router) No newline at end of file |
Comment on lines
+8
to
+9
| async def execute_code(self, python_code: str) -> dict: | ||
| return execute_python(python_code, timeout=5) |
Comment on lines
+21
to
+24
| async def event_stream(): | ||
| result = await service.execute_code(req.python_code) | ||
| score = 85.0 | ||
| yield f"data: {json.dumps({'type': 'score', 'value': score})}\n\n" |
Comment on lines
+31
to
+33
| @router.post("/generate/stream") | ||
| async def generate_exercise(body: dict = {}): | ||
| level = body.get("level", "beginner") |
Comment on lines
+9
to
+13
| result = subprocess.run( | ||
| [sys.executable, tmp_path], | ||
| capture_output=True, text=True, | ||
| timeout=timeout | ||
| ) |
Comment on lines
+87
to
91
| if (!window.openTutorEvents) window.openTutorEvents = new EventTarget(); | ||
| window.openTutorEvents.addEventListener('chatCreated', ((event: CustomEvent) => { | ||
| const newChatId = event.detail?.chatId; | ||
| const timestamp = event.detail?.timestamp; | ||
| console.log('Received chatCreated event with chatId:', newChatId, 'timestamp:', timestamp); | ||
|
|
||
| if (newChatId && pendingSupportId) { | ||
| console.log('Immediately updating support with new chat ID from event'); | ||
| updateSupportWithChatId(pendingSupportId, newChatId); | ||
| } | ||
| if (newChatId && pendingSupportId) updateSupportWithChatId(pendingSupportId, newChatId); | ||
| }) as EventListener); |
Comment on lines
122
to
126
| onDestroy(() => { | ||
| console.log('Dashboard component destroyed'); | ||
| if (browser) { | ||
| // Remove global event listener | ||
| window.openTutorEvents.removeEventListener('chatCreated', ((event: CustomEvent) => { | ||
| // This is just for cleanup, the actual handler is defined in onMount | ||
| }) as EventListener); | ||
|
|
||
| if (chatIdSubscription) { | ||
| chatIdSubscription(); | ||
| console.log('Chat ID subscription removed'); | ||
| } | ||
|
|
||
| if (urlCheckInterval) { | ||
| clearInterval(urlCheckInterval); | ||
| console.log('URL check interval cleared'); | ||
| } | ||
| if (chatIdSubscription) chatIdSubscription(); | ||
| if (urlCheckInterval) clearInterval(urlCheckInterval); | ||
| } |
Comment on lines
+1
to
+5
| <!-- Dashboard.svelte --> | ||
| <script lang="ts"> | ||
| import { getContext, onMount, onDestroy } from 'svelte'; | ||
| import type { Writable } from 'svelte/store'; | ||
| import type { i18n as i18nType } from 'i18next'; |
Comment on lines
+4
to
+7
| image: ghcr.io/engineer-man/piston | ||
| container_name: piston | ||
| privileged: true | ||
| ports: |
Comment on lines
+1
to
+2
| from fastapi import APIRouter | ||
| from fastapi.responses import StreamingResponse |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🧩 Blockly Visual Programming Interface — Sprint 1
Features
New Files
learning/blockly/— models, schemas, service, router, sandboxai/llm/blockly_generator.py— LLM generatorui/src/routes/student/blockly/new/+page.svelte— Blockly interfaceModified Files
ui/src/lib/components/student/pages/Dashboard.svelteui/vite.config.ts— Fix proxymain.py— Register routerScreenshots
1. Dashboard avec bouton Blockly

2. Interface Blockly avec toolbox

3. Exécution code → résultat 24

4. Score 85/100 + Feedback IA
