lanna_lalala;- commited on
Commit Β·
a0ad5fe
1
Parent(s): 2950f9c
init frontend
Browse files- app.py +46 -21
- utils/api.py +61 -22
app.py
CHANGED
|
@@ -15,6 +15,10 @@ from phase.Student_view.games import profitpuzzle
|
|
| 15 |
from utils import db,api
|
| 16 |
import os
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
try:
|
| 20 |
ok = api.health().get("ok")
|
|
@@ -228,28 +232,48 @@ def main():
|
|
| 228 |
submit = st.form_submit_button("Login")
|
| 229 |
|
| 230 |
if submit:
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
st.error("β Unable to connect to the database.")
|
| 234 |
-
else:
|
| 235 |
try:
|
| 236 |
-
user =
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
# --- STUDENT DASHBOARD ---
|
| 255 |
elif page == "Student Dashboard":
|
|
@@ -308,3 +332,4 @@ def main():
|
|
| 308 |
|
| 309 |
if __name__ == "__main__":
|
| 310 |
main()
|
|
|
|
|
|
| 15 |
from utils import db,api
|
| 16 |
import os
|
| 17 |
|
| 18 |
+
from utils.api import BACKEND
|
| 19 |
+
st.sidebar.caption(f"Backend URL: {BACKEND}")
|
| 20 |
+
|
| 21 |
+
DISABLE_DB = os.getenv("DISABLE_DB", "1") == "1"
|
| 22 |
|
| 23 |
try:
|
| 24 |
ok = api.health().get("ok")
|
|
|
|
| 232 |
submit = st.form_submit_button("Login")
|
| 233 |
|
| 234 |
if submit:
|
| 235 |
+
if DISABLE_DB:
|
| 236 |
+
# Route login to your Backend Space
|
|
|
|
|
|
|
| 237 |
try:
|
| 238 |
+
user = api.login(email, password) # calls POST /auth/login
|
| 239 |
+
# Normalize to the structure your app already uses
|
| 240 |
+
st.session_state.user = {
|
| 241 |
+
"user_id": user["user_id"],
|
| 242 |
+
"name": user["name"],
|
| 243 |
+
"role": user["role"], # "Student" or "Teacher" from backend
|
| 244 |
+
"email": user["email"],
|
| 245 |
+
}
|
| 246 |
+
st.success(f"π Logged in as {user['name']} ({user['role']})")
|
| 247 |
+
st.session_state.current_page = (
|
| 248 |
+
"Student Dashboard" if user["role"] == "Student" else "Teacher Dashboard"
|
| 249 |
+
)
|
| 250 |
+
st.rerun()
|
| 251 |
+
except Exception as e:
|
| 252 |
+
st.error(f"Login failed. {e}")
|
| 253 |
+
else:
|
| 254 |
+
# Local fallback: keep your old direct-DB logic
|
| 255 |
+
conn = db.get_db_connection()
|
| 256 |
+
if not conn:
|
| 257 |
+
st.error("β Unable to connect to the database.")
|
| 258 |
+
else:
|
| 259 |
+
try:
|
| 260 |
+
user = db.check_password(email, password)
|
| 261 |
+
if user:
|
| 262 |
+
st.session_state.user = {
|
| 263 |
+
"user_id": user["user_id"],
|
| 264 |
+
"name": user["name"],
|
| 265 |
+
"role": user["role"], # "Student" or "Teacher"
|
| 266 |
+
"email": user["email"],
|
| 267 |
+
}
|
| 268 |
+
st.success(f"π Logged in as {user['name']} ({user['role']})")
|
| 269 |
+
st.session_state.current_page = (
|
| 270 |
+
"Student Dashboard" if user["role"] == "Student" else "Teacher Dashboard"
|
| 271 |
+
)
|
| 272 |
+
st.rerun()
|
| 273 |
+
else:
|
| 274 |
+
st.error("β Incorrect email or password, or account not found.")
|
| 275 |
+
finally:
|
| 276 |
+
conn.close()
|
| 277 |
|
| 278 |
# --- STUDENT DASHBOARD ---
|
| 279 |
elif page == "Student Dashboard":
|
|
|
|
| 332 |
|
| 333 |
if __name__ == "__main__":
|
| 334 |
main()
|
| 335 |
+
|
utils/api.py
CHANGED
|
@@ -1,32 +1,71 @@
|
|
| 1 |
# utils/api.py
|
| 2 |
-
import os
|
|
|
|
| 3 |
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
if not BACKEND:
|
| 6 |
-
raise RuntimeError("BACKEND_URL is not set
|
| 7 |
-
BACKEND = BACKEND.rstrip("/")
|
| 8 |
|
| 9 |
-
def
|
| 10 |
-
r = requests.get(f"{BACKEND}
|
| 11 |
r.raise_for_status()
|
| 12 |
-
return r
|
| 13 |
|
| 14 |
-
def
|
| 15 |
-
r = requests.post(f"{BACKEND}
|
| 16 |
r.raise_for_status()
|
| 17 |
-
return r
|
| 18 |
|
| 19 |
-
def
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
-
def
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
|
| 29 |
-
def
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# utils/api.py
|
| 2 |
+
import os
|
| 3 |
+
import requests
|
| 4 |
|
| 5 |
+
def _normalize_backend_url(url: str) -> str:
|
| 6 |
+
url = (url or "").strip()
|
| 7 |
+
# fix accidental comma typos
|
| 8 |
+
url = url.replace(",", ".")
|
| 9 |
+
# convert huggingface.co/spaces/<owner>/<space> -> https://<owner>-<space>.hf.space
|
| 10 |
+
if "huggingface.co/spaces/" in url:
|
| 11 |
+
tail = url.split("huggingface.co/spaces/")[-1].strip("/")
|
| 12 |
+
parts = tail.split("/")
|
| 13 |
+
if len(parts) >= 2:
|
| 14 |
+
owner, space = parts[0], parts[1]
|
| 15 |
+
url = f"https://{owner}-{space}.hf.space"
|
| 16 |
+
return url.rstrip("/")
|
| 17 |
+
|
| 18 |
+
BACKEND = _normalize_backend_url(os.getenv("BACKEND_URL"))
|
| 19 |
if not BACKEND:
|
| 20 |
+
raise RuntimeError("BACKEND_URL is not set")
|
|
|
|
| 21 |
|
| 22 |
+
def _get(path: str, **kwargs):
|
| 23 |
+
r = requests.get(f"{BACKEND}{path}", timeout=kwargs.pop("timeout", 10), **kwargs)
|
| 24 |
r.raise_for_status()
|
| 25 |
+
return r
|
| 26 |
|
| 27 |
+
def _post(path: str, json: dict, **kwargs):
|
| 28 |
+
r = requests.post(f"{BACKEND}{path}", json=json, timeout=kwargs.pop("timeout", 15), **kwargs)
|
| 29 |
r.raise_for_status()
|
| 30 |
+
return r
|
| 31 |
|
| 32 |
+
def health() -> dict:
|
| 33 |
+
# Try /health, then /
|
| 34 |
+
for path in ("/health", "/"):
|
| 35 |
+
try:
|
| 36 |
+
r = _get(path)
|
| 37 |
+
try:
|
| 38 |
+
data = r.json()
|
| 39 |
+
except Exception:
|
| 40 |
+
return {"ok": True}
|
| 41 |
+
if path == "/health":
|
| 42 |
+
return {"ok": bool(data.get("ok", True))}
|
| 43 |
+
return {"ok": data.get("status") == "ok"}
|
| 44 |
+
except Exception:
|
| 45 |
+
continue
|
| 46 |
+
raise RuntimeError(f"Backend not reachable at {BACKEND}")
|
| 47 |
|
| 48 |
+
def start_agent(student_id: int, lesson_id: int, level_slug: str) -> dict:
|
| 49 |
+
return _post("/agent/start", json={
|
| 50 |
+
"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug
|
| 51 |
+
}).json()
|
| 52 |
|
| 53 |
+
def get_quiz(student_id: int, lesson_id: int, level_slug: str) -> list:
|
| 54 |
+
return _post("/agent/quiz", json={
|
| 55 |
+
"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug
|
| 56 |
+
}).json()["items"]
|
| 57 |
+
|
| 58 |
+
def grade_quiz(student_id: int, lesson_id: int, level_slug: str,
|
| 59 |
+
answers: list[str], assignment_id: int | None = None):
|
| 60 |
+
data = _post("/agent/grade", json={
|
| 61 |
+
"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug,
|
| 62 |
+
"answers": answers, "assignment_id": assignment_id
|
| 63 |
+
}).json()
|
| 64 |
+
return data["score"], data["total"]
|
| 65 |
+
|
| 66 |
+
def next_step(student_id: int, lesson_id: int, level_slug: str,
|
| 67 |
+
answers: list[str], assignment_id: int | None = None) -> dict:
|
| 68 |
+
return _post("/agent/coach_or_celebrate", json={
|
| 69 |
+
"student_id": student_id, "lesson_id": lesson_id, "level_slug": level_slug,
|
| 70 |
+
"answers": answers, "assignment_id": assignment_id
|
| 71 |
+
}).json()
|