/
servgri
/
interview
Обзор
Документация
Войти
/
servgri
/
interview
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
static/script.js
230 строк
8 KB
Seal
full project
27 ноя 2025, 14:24
27 ноя 2025, 14:24
d1507ba
Код
Авторство
О чём код?
const container = document.getElementById('editor-container'); let TASK_ID = container.dataset.taskId; const USER_TOKEN = container.dataset.userToken; const SERVER_URL = container.dataset.serverUrl; console.log("TASK_ID:", TASK_ID); console.log("USER_TOKEN:", USER_TOKEN); console.log("SERVER_URL:", SERVER_URL); let editor; let pyodideReady = false; let runCount = 0; // --- UI helpers --- function addOutput(text) { const out = document.getElementById('output'); out.textContent += text + '\n'; out.scrollTop = out.scrollHeight; } function clearOutput() { document.getElementById('output').textContent = ''; } function showAlert(msg, cls = 'alert-info', timeout = 3000) { const c = document.getElementById('alert-container'); const el = document.createElement('div'); el.className = `alert ${cls} mt-2`; el.textContent = msg; c.appendChild(el); setTimeout(() => { el.classList.add('fade'); el.addEventListener('transitionend', () => el.remove(), {once: true}); }, timeout); } // --- Инициализация CodeMirror и Pyodide --- async function initEditor() { const container = document.getElementById('editor-container'); editor = CodeMirror(document.getElementById('editor'), { // вставляем прямо в #editor value: "# Вставьте код на Python здесь\n", mode: "python", theme: "material-darker", lineNumbers: true, indentUnit: 4, smartIndent: true, autofocus: true }); // Принудительно устанавливаем размеры editor.setSize("100%", "100%"); new ResizeObserver(() => editor.setSize("100%", "100%")).observe(container); // Загружаем Pyodide try { window.pyodide = await loadPyodide({ stdout: (text) => addOutput(text), stderr: (text) => addOutput(text) }); pyodideReady = true; } catch (e) { console.error("Pyodide error:", e); showAlert("Ошибка загрузки Pyodide", "alert-danger", 5000); } // UX/античит document.addEventListener('copy', () => showAlert('Попытка копирования кода зафиксирована', 'alert-warning', 2000)); document.addEventListener('visibilitychange', () => { if (document.hidden) showAlert('Вкладка потеряна (фиксируется)', 'alert-warning', 2000); }); } // Запуск кода локально через Pyodide исправленная версия async function runCode() { if (!pyodideReady) { showAlert("Python ещё загружается... подождите", "alert-info", 2000); return; } clearOutput(); runCount++; const code = editor.getValue(); const stdin = document.getElementById('stdin').value.split("\n"); try { // сбрасываем старый input() из предыдущих запусков await pyodide.runPythonAsync(` import builtins if hasattr(builtins, "input"): del builtins.input `); // передаём stdin в JS window.stdin_lines = stdin; const pyShim = ` from js import stdin_lines import builtins def input(prompt=""): if prompt: print(prompt, end="") if not stdin_lines or len(stdin_lines) == 0: raise EOFError("No more input lines") return stdin_lines.pop(0) builtins.input = input `; await pyodide.runPythonAsync(pyShim + "\n" + code); } catch (err) { addOutput(err.toString()); } } // --- Отправка решения на сервер Django --- async function submitSolution() { if (!pyodideReady) { showAlert("Python ещё загружается... подождите", "alert-info", 2000); return; } const code = editor.getValue(); const antiCheat = { sessionId: localStorage.getItem('sessionId'), pageHidden: document.hidden, runCount: runCount + 1 }; if (!TASK_ID) { showAlert("task_id отсутствует — невозможно отправить решение", "alert-danger"); return; } try { const resp = await fetch(`/api/task/submit/${TASK_ID}/`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": USER_TOKEN ? `Bearer ${USER_TOKEN}` : "" }, body: JSON.stringify({ user_solution: code, anti_cheat: antiCheat }) }); // --------- ЛОГИКА 1: сервер ДОШЁЛ (НЕ ВАЖНО правильный/неправильный код) ---------- if (resp.ok) { showAlert("Решение отправлено и сохранено", "alert-success", 4000); } else { showAlert("Ошибка сервера при отправке решения", "alert-danger", 6000); return; } // --------- Разбираем ответ и показываем тесты ---------- const result = await resp.json(); const ul = document.getElementById("test-results"); ul.innerHTML = ""; if (Array.isArray(result.feedback)) { result.feedback.forEach((r, i) => { const li = document.createElement("li"); li.className = "list-group-item " + (r.passed ? "list-group-item-success" : "list-group-item-danger"); const got = r.output ?? r.error ?? ""; li.textContent = `Тест ${i + 1}: ${r.passed ? "✅" : "❌"} — Ожидалось: "${r.expected}" | Получено: "${got}"`; ul.appendChild(li); }); } // --------- Автозагрузка следующей задачи в вебе ---------- setTimeout(fetchNextTask, 1000); } catch (err) { console.error(err); showAlert("Ошибка при отправке решения: " + (err.message || err), "alert-danger", 6000); } } // --- Получение следующей задачи --- async function fetchNextTask() { try { const resp = await fetch(`/api/task/next/`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": USER_TOKEN ? `Bearer ${USER_TOKEN}` : "" } }); if (!resp.ok) { showAlert("Не удалось получить следующую задачу", "alert-danger"); return; } const data = await resp.json(); const task = data.task; if (!task) { showAlert("Новых задач нет", "alert-info", 3000); return; } // Обновить ID задачи container.dataset.taskId = task.id; TASK_ID = task.id; editor.setValue("# Новая задача:\n" + task.task_text); document.getElementById('stdin').value = ""; clearOutput(); showAlert(`Следующая задача: ${task.title}`, "alert-info", 2500); } catch (err) { console.error(err); showAlert("Ошибка при загрузке следующей задачи: " + (err.message || err), "alert-danger", 6000); } } // --- DOM события --- document.addEventListener('DOMContentLoaded', () => { initEditor(); document.getElementById('run-btn').addEventListener('click', runCode); document.getElementById('submit-btn').addEventListener('click', submitSolution); });