divid_teacher / tools_manager.py
Asem75's picture
Create tools_manager.py
3502d9e verified
Raw
History Blame Contribute Delete
7 kB
# tools_manager.py
import os
import re
import json
import math
from datetime import datetime
class ToolsManager:
def __init__(self):
self.base_dir = "tools_records"
os.makedirs(self.base_dir, exist_ok=True)
def safe_name(self, value):
value = str(value or "").strip()
value = re.sub(r"[^a-zA-Z0-9_\-ء-ي]+", "_", value)
return value[:80] or "student"
def calculate(self, expression):
try:
expression = str(expression or "").strip()
if not expression:
return {
"success": False,
"message": "اكتب العملية الحسابية أولًا."
}
expression = expression.replace("^", "**")
allowed_names = {
"sqrt": math.sqrt,
"sin": math.sin,
"cos": math.cos,
"tan": math.tan,
"pi": math.pi,
"e": math.e,
"abs": abs,
"round": round,
"pow": pow
}
result = eval(expression, {"__builtins__": {}}, allowed_names)
return {
"success": True,
"message": f"النتيجة: {result}",
"result": result
}
except Exception as e:
return {
"success": False,
"message": f"تعذر الحساب: {e}"
}
def save_student_tool_note(self, identity, context, note, uploaded_file=None):
try:
identity = self.safe_name(identity)
context = str(context or "عام").strip()
note = str(note or "").strip()
student_dir = os.path.join(self.base_dir, identity)
os.makedirs(student_dir, exist_ok=True)
stamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
uploaded_path = None
if uploaded_file is not None:
try:
uploaded_path = uploaded_file.name
except Exception:
uploaded_path = str(uploaded_file)
payload = {
"identity": identity,
"context": context,
"note": note,
"uploaded_file": uploaded_path,
"created_at": datetime.utcnow().isoformat(),
"status": "saved_for_teacher_review"
}
path = os.path.join(student_dir, f"tool_note_{stamp}.json")
with open(path, "w", encoding="utf-8") as f:
json.dump(payload, f, ensure_ascii=False, indent=2)
return {
"success": True,
"message": f"تم حفظ الملاحظة/السؤال لمراجعة الأستاذ: {path}",
"path": path
}
except Exception as e:
return {
"success": False,
"message": f"فشل حفظ الملاحظة: {e}"
}
def get_whiteboard_html(self):
return """
<div style="direction:rtl; font-family:Arial; border:1px solid #cbd5e1; border-radius:14px; padding:10px; background:#f8fafc;">
<h3>السبورة التجريبية</h3>
<div style="display:flex; flex-wrap:wrap; gap:8px; margin-bottom:8px;">
<button onclick="dtSetTool('pen')" type="button">قلم</button>
<button onclick="dtSetTool('eraser')" type="button">محاية</button>
<button onclick="dtClearBoard()" type="button">مسح</button>
<button onclick="dtDownloadBoard()" type="button">حفظ صورة السبورة</button>
<label>
الحجم
<input id="dt_size" type="range" min="1" max="14" value="4">
</label>
<label>
اللون
<input id="dt_color" type="color" value="#000000">
</label>
</div>
<div style="height:420px; overflow:auto; border:2px solid #334155; border-radius:12px; background:#e2e8f0;">
<canvas id="dt_board" width="1200" height="1700" style="display:block; background:white; touch-action:none;"></canvas>
</div>
<p style="font-size:13px; color:#475569;">
هذه نسخة خفيفة آمنة. احفظ صورة السبورة، ثم يمكنك رفعها من أدواتي ليشاهدها الأستاذ.
</p>
</div>
<script>
(function () {
const canvas = document.getElementById("dt_board");
if (!canvas) return;
const ctx = canvas.getContext("2d");
let drawing = false;
let tool = "pen";
let lastX = 0;
let lastY = 0;
function grid() {
ctx.save();
ctx.strokeStyle = "#e5e7eb";
ctx.lineWidth = 1;
for (let x = 0; x < canvas.width; x += 32) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
for (let y = 0; y < canvas.height; y += 32) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
ctx.restore();
}
window.dtSetTool = function (nextTool) {
tool = nextTool;
};
window.dtClearBoard = function () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
grid();
};
window.dtDownloadBoard = function () {
const link = document.createElement("a");
link.download = "student_whiteboard.png";
link.href = canvas.toDataURL("image/png");
link.click();
};
function getPoint(event) {
const rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
}
function start(event) {
event.preventDefault();
drawing = true;
const p = getPoint(event);
lastX = p.x;
lastY = p.y;
}
function move(event) {
if (!drawing) return;
event.preventDefault();
const p = getPoint(event);
const sizeInput = document.getElementById("dt_size");
const colorInput = document.getElementById("dt_color");
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(p.x, p.y);
if (tool === "eraser") {
ctx.strokeStyle = "#ffffff";
ctx.lineWidth = Math.max(18, parseInt(sizeInput.value, 10) * 3);
} else {
ctx.strokeStyle = colorInput.value;
ctx.lineWidth = parseInt(sizeInput.value, 10);
}
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.stroke();
lastX = p.x;
lastY = p.y;
}
function stop() {
drawing = false;
}
canvas.addEventListener("pointerdown", start);
canvas.addEventListener("pointermove", move);
window.addEventListener("pointerup", stop);
window.addEventListener("pointercancel", stop);
grid();
})();
</script>
""".strip()