Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -15,19 +15,19 @@ import time
|
|
| 15 |
try:
|
| 16 |
import google.generativeai as genai
|
| 17 |
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
| 18 |
-
gemini_model = genai.GenerativeModel('gemini-2.5
|
| 19 |
-
print(" Gemini AI initialized successfully (PRIMARY)")
|
| 20 |
except Exception as e:
|
| 21 |
-
print(f" Error initializing Gemini: {e}")
|
| 22 |
gemini_model = None
|
| 23 |
|
| 24 |
# Cohere (Secondary)
|
| 25 |
try:
|
| 26 |
import cohere
|
| 27 |
cohere_client = cohere.Client(os.getenv("COHERE_API_KEY"))
|
| 28 |
-
print(" Cohere initialized successfully (SECONDARY)")
|
| 29 |
except Exception as e:
|
| 30 |
-
print(f" Error initializing Cohere: {e}")
|
| 31 |
cohere_client = None
|
| 32 |
|
| 33 |
# Z.ai (Tertiary)
|
|
@@ -37,9 +37,9 @@ try:
|
|
| 37 |
provider="novita",
|
| 38 |
api_key=os.environ.get("HF_TOKEN"),
|
| 39 |
)
|
| 40 |
-
print(" Z.ai GLM-4.6 initialized successfully (TERTIARY)")
|
| 41 |
except Exception as e:
|
| 42 |
-
print(f" Error initializing Z.ai: {e}")
|
| 43 |
zai_client = None
|
| 44 |
|
| 45 |
# MiniMax (Final Fallback)
|
|
@@ -48,9 +48,9 @@ try:
|
|
| 48 |
provider="novita",
|
| 49 |
api_key=os.environ.get("HF_TOKEN"),
|
| 50 |
)
|
| 51 |
-
print(" MiniMax AI initialized successfully (FINAL FALLBACK)")
|
| 52 |
except Exception as e:
|
| 53 |
-
print(f" Error initializing MiniMax: {e}")
|
| 54 |
minimax_client = None
|
| 55 |
|
| 56 |
# ---------- 2. Unified AI Function with Smart Fallback ----------
|
|
@@ -74,7 +74,7 @@ def ask_ai(prompt, temperature=0.7, max_retries=2):
|
|
| 74 |
return response.text, "gemini"
|
| 75 |
except Exception as e:
|
| 76 |
last_error = e
|
| 77 |
-
print(f" Gemini attempt {attempt+1} failed: {str(e)}")
|
| 78 |
if attempt < max_retries - 1:
|
| 79 |
time.sleep(1)
|
| 80 |
|
|
@@ -90,7 +90,7 @@ def ask_ai(prompt, temperature=0.7, max_retries=2):
|
|
| 90 |
return response.text, "cohere"
|
| 91 |
except Exception as e:
|
| 92 |
last_error = e
|
| 93 |
-
print(f" Cohere attempt {attempt+1} failed: {str(e)}")
|
| 94 |
if attempt < max_retries - 1:
|
| 95 |
time.sleep(1)
|
| 96 |
|
|
@@ -106,7 +106,7 @@ def ask_ai(prompt, temperature=0.7, max_retries=2):
|
|
| 106 |
return completion.choices[0].message.content, "zai"
|
| 107 |
except Exception as e:
|
| 108 |
last_error = e
|
| 109 |
-
print(f" Z.ai attempt {attempt+1} failed: {str(e)}")
|
| 110 |
if attempt < max_retries - 1:
|
| 111 |
time.sleep(1)
|
| 112 |
|
|
@@ -121,10 +121,10 @@ def ask_ai(prompt, temperature=0.7, max_retries=2):
|
|
| 121 |
return completion.choices[0].message.content, "minimax"
|
| 122 |
except Exception as e:
|
| 123 |
last_error = e
|
| 124 |
-
print(f" MiniMax fallback failed: {str(e)}")
|
| 125 |
|
| 126 |
# All failed
|
| 127 |
-
error_msg = f" Error: All AI services failed. Last error: {str(last_error)}"
|
| 128 |
return error_msg, "error"
|
| 129 |
|
| 130 |
# ---------- 3. Global storage ----------
|
|
@@ -166,7 +166,7 @@ def extract_text_from_pdf(pdf_file):
|
|
| 166 |
except Exception as e:
|
| 167 |
return f"Error extracting PDF: {e}"
|
| 168 |
|
| 169 |
-
# ---------- 6. AI Tutor with Multi-Model Support ----------
|
| 170 |
def ai_tutor_chat(message, history, subject, topic, level):
|
| 171 |
if not message.strip():
|
| 172 |
return history
|
|
@@ -183,8 +183,8 @@ Use a friendly, supportive tone to help students learn effectively."""
|
|
| 183 |
if user_msg:
|
| 184 |
conversation += f"Student: {user_msg}\n"
|
| 185 |
if bot_msg:
|
| 186 |
-
# Remove
|
| 187 |
-
clean_msg = bot_msg.replace("
|
| 188 |
conversation += f"Tutor: {clean_msg}\n"
|
| 189 |
|
| 190 |
conversation += f"Student: {message}\nTutor:"
|
|
@@ -304,15 +304,15 @@ Return JSON (no markdown):
|
|
| 304 |
try:
|
| 305 |
clean_txt = response.replace("```json", "").replace("```", "").strip()
|
| 306 |
fb = json.loads(clean_txt)
|
| 307 |
-
result = f""" Score: {fb['score']}%
|
| 308 |
|
| 309 |
-
Detailed Feedback:
|
| 310 |
{fb['feedback']}
|
| 311 |
|
| 312 |
-
Your Strengths:
|
| 313 |
{fb.get('strengths', 'Good effort!')}
|
| 314 |
|
| 315 |
-
How to Improve:
|
| 316 |
{fb['improvements']}"""
|
| 317 |
|
| 318 |
if source in ["cohere", "zai", "minimax"]:
|
|
@@ -325,12 +325,12 @@ Return JSON (no markdown):
|
|
| 325 |
# ---------- 10. Admin β Past Papers ----------
|
| 326 |
def verify_admin_password(password):
|
| 327 |
if password == ADMIN_PASSWORD:
|
| 328 |
-
return gr.update(visible=True), gr.update(visible=False), " Access granted!"
|
| 329 |
-
return gr.update(visible=False), gr.update(visible=True), " Incorrect password!"
|
| 330 |
|
| 331 |
def upload_paper(title, subject, level, content, pdf_file):
|
| 332 |
if not all([title, subject, level, content]):
|
| 333 |
-
return " Please fill all required fields!", get_papers_list()
|
| 334 |
|
| 335 |
paper_id = len(papers_storage) + 1
|
| 336 |
|
|
@@ -350,13 +350,13 @@ def upload_paper(title, subject, level, content, pdf_file):
|
|
| 350 |
"has_pdf": bool(pdf_text and not pdf_text.startswith("Error")),
|
| 351 |
"uploaded_at": datetime.now().strftime("%Y-%m-%d %H:%M")
|
| 352 |
})
|
| 353 |
-
return " Paper uploaded!", get_papers_list()
|
| 354 |
|
| 355 |
def get_papers_list():
|
| 356 |
if not papers_storage:
|
| 357 |
return "No papers yet."
|
| 358 |
return "\n".join(
|
| 359 |
-
f"**{p['title']}** ({p['subject'].upper()} - {p['level']}) {' PDF' if p.get('has_pdf') else ''}\n {p['uploaded_at']}\n{p['content'][:120]}...\n{'β'*60}"
|
| 360 |
for p in papers_storage
|
| 361 |
)
|
| 362 |
|
|
@@ -364,25 +364,26 @@ def view_papers_student(subject, level):
|
|
| 364 |
filtered = [p for p in papers_storage
|
| 365 |
if p["subject"] == subject.lower() and p["level"] == level]
|
| 366 |
if not filtered:
|
| 367 |
-
return f" No {subject} {level} papers available."
|
| 368 |
return "\n".join(
|
| 369 |
-
f"**{p['title']}** {' PDF' if p.get('has_pdf') else ''}\n {p['uploaded_at']}\n\n{p['content']}\n\n{'β'*60}"
|
| 370 |
for p in filtered
|
| 371 |
)
|
| 372 |
|
| 373 |
# ---------- 11. Gradio UI ----------
|
| 374 |
with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
|
| 375 |
gr.Markdown("""
|
| 376 |
-
#
|
| 377 |
-
|
| 378 |
_Powered by Gemini AI with intelligent fallback system_
|
| 379 |
""")
|
| 380 |
|
| 381 |
with gr.Tabs():
|
| 382 |
-
# βββββ STUDENT βββββ
|
| 383 |
-
with gr.Tab(" Student Portal"):
|
| 384 |
with gr.Tabs():
|
| 385 |
-
|
|
|
|
| 386 |
gr.Markdown("### Chat with Your AI Tutor\n*Powered by Gemini 2.5 with automatic fallback*")
|
| 387 |
with gr.Row():
|
| 388 |
subj = gr.Radio(["French", "EFL"], label="Subject", value="French")
|
|
@@ -396,26 +397,29 @@ with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
|
|
| 396 |
chat = gr.Chatbot(height=450, show_label=False)
|
| 397 |
txt = gr.Textbox(placeholder="Ask anything... e.g., 'Explain the passΓ© composΓ©'", label="Message")
|
| 398 |
with gr.Row():
|
| 399 |
-
send = gr.Button("Send ", variant="primary")
|
| 400 |
-
clr = gr.Button("Clear ")
|
| 401 |
send.click(ai_tutor_chat, [txt, chat, subj, topc, lvl], chat)
|
| 402 |
txt.submit(ai_tutor_chat, [txt, chat, subj, topc, lvl], chat)
|
| 403 |
clr.click(clear_chat, outputs=chat)
|
| 404 |
|
| 405 |
-
|
|
|
|
| 406 |
gr.Markdown("### English β· French Translation")
|
| 407 |
dir_ = gr.Radio(["English β French", "French β English"], label="Direction", value="English β French")
|
| 408 |
inp = gr.Textbox(lines=6, label="Input Text", placeholder="Enter text...")
|
| 409 |
out = gr.Textbox(lines=6, label="Translation")
|
| 410 |
-
gr.Button("Translate ", variant="primary").click(translate_text, [inp, dir_], out)
|
| 411 |
|
| 412 |
-
|
|
|
|
| 413 |
gr.Markdown("### French Dictionary")
|
| 414 |
w = gr.Textbox(placeholder="Enter French word...", label="Word")
|
| 415 |
o = gr.Textbox(lines=16, label="Definition")
|
| 416 |
-
gr.Button("Look Up ", variant="primary").click(dictionary_lookup, w, o)
|
| 417 |
|
| 418 |
-
|
|
|
|
| 419 |
gr.Markdown("### Generate & Practice Exam Questions")
|
| 420 |
with gr.Row():
|
| 421 |
ps = gr.Radio(["French", "EFL"], label="Subject", value="French")
|
|
@@ -423,34 +427,35 @@ with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
|
|
| 423 |
pt = gr.Dropdown(french_topics, label="Topic")
|
| 424 |
ps.change(upd_topics, ps, pt)
|
| 425 |
|
| 426 |
-
q = gr.Textbox(label=" Question", lines=5, interactive=False)
|
| 427 |
exp = gr.Textbox(label="Expected", lines=2, visible=False)
|
| 428 |
-
mark = gr.Textbox(label=" Mark Scheme", lines=3, interactive=False)
|
| 429 |
-
ans = gr.Textbox(lines=8, label=" Your Answer", placeholder="Type your answer...")
|
| 430 |
-
fb = gr.Textbox(lines=12, label=" Feedback", interactive=False)
|
| 431 |
|
| 432 |
with gr.Row():
|
| 433 |
-
gr.Button(" Generate", variant="primary").click(generate_question, [ps, pt, pl], [q, exp, mark])
|
| 434 |
-
gr.Button(" Check", variant="secondary").click(check_answer, [q, exp, ans, ps, pl], fb)
|
| 435 |
|
| 436 |
-
|
|
|
|
| 437 |
gr.Markdown("### Browse Past Papers")
|
| 438 |
with gr.Row():
|
| 439 |
psb = gr.Radio(["French", "EFL"], label="Subject", value="French")
|
| 440 |
plb = gr.Radio(["IGCSE", "GCSE"], label="Level", value="IGCSE")
|
| 441 |
pd = gr.Textbox(lines=22, label="Papers", interactive=False)
|
| 442 |
-
gr.Button(" Show", variant="primary").click(view_papers_student, [psb, plb], pd)
|
| 443 |
|
| 444 |
-
# βββββ ADMIN βββββ
|
| 445 |
-
with gr.Tab(" Admin Panel"):
|
| 446 |
with gr.Column() as login_section:
|
| 447 |
-
gr.Markdown("###
|
| 448 |
pwd = gr.Textbox(label="Password", type="password", placeholder="Enter password")
|
| 449 |
-
login_btn = gr.Button(" Login", variant="primary")
|
| 450 |
login_status = gr.Textbox(label="Status", interactive=False)
|
| 451 |
|
| 452 |
with gr.Column(visible=False) as admin_section:
|
| 453 |
-
gr.Markdown("###
|
| 454 |
with gr.Row():
|
| 455 |
with gr.Column():
|
| 456 |
t = gr.Textbox(label="Title", placeholder="e.g., Paper 1 - June 2023")
|
|
@@ -458,8 +463,8 @@ with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
|
|
| 458 |
s = gr.Radio(["French", "EFL"], label="Subject", value="French")
|
| 459 |
lv = gr.Radio(["IGCSE", "GCSE"], label="Level", value="IGCSE")
|
| 460 |
c = gr.Textbox(lines=6, label="Description")
|
| 461 |
-
pdf = gr.File(label=" PDF (optional)", file_types=[".pdf"])
|
| 462 |
-
up = gr.Button(" Upload", variant="primary")
|
| 463 |
st = gr.Textbox(label="Status")
|
| 464 |
with gr.Column():
|
| 465 |
lst = gr.Textbox(lines=24, label="All Papers", value=get_papers_list(), interactive=False)
|
|
@@ -467,4 +472,9 @@ with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
|
|
| 467 |
|
| 468 |
login_btn.click(verify_admin_password, [pwd], [admin_section, login_section, login_status])
|
| 469 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
app.launch()
|
|
|
|
| 15 |
try:
|
| 16 |
import google.generativeai as genai
|
| 17 |
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
| 18 |
+
gemini_model = genai.GenerativeModel('gemini-2.5-pro')
|
| 19 |
+
print("β
Gemini AI initialized successfully (PRIMARY)")
|
| 20 |
except Exception as e:
|
| 21 |
+
print(f"β Error initializing Gemini: {e}")
|
| 22 |
gemini_model = None
|
| 23 |
|
| 24 |
# Cohere (Secondary)
|
| 25 |
try:
|
| 26 |
import cohere
|
| 27 |
cohere_client = cohere.Client(os.getenv("COHERE_API_KEY"))
|
| 28 |
+
print("β
Cohere initialized successfully (SECONDARY)")
|
| 29 |
except Exception as e:
|
| 30 |
+
print(f"β Error initializing Cohere: {e}")
|
| 31 |
cohere_client = None
|
| 32 |
|
| 33 |
# Z.ai (Tertiary)
|
|
|
|
| 37 |
provider="novita",
|
| 38 |
api_key=os.environ.get("HF_TOKEN"),
|
| 39 |
)
|
| 40 |
+
print("β
Z.ai GLM-4.6 initialized successfully (TERTIARY)")
|
| 41 |
except Exception as e:
|
| 42 |
+
print(f"β Error initializing Z.ai: {e}")
|
| 43 |
zai_client = None
|
| 44 |
|
| 45 |
# MiniMax (Final Fallback)
|
|
|
|
| 48 |
provider="novita",
|
| 49 |
api_key=os.environ.get("HF_TOKEN"),
|
| 50 |
)
|
| 51 |
+
print("β
MiniMax AI initialized successfully (FINAL FALLBACK)")
|
| 52 |
except Exception as e:
|
| 53 |
+
print(f"β Error initializing MiniMax: {e}")
|
| 54 |
minimax_client = None
|
| 55 |
|
| 56 |
# ---------- 2. Unified AI Function with Smart Fallback ----------
|
|
|
|
| 74 |
return response.text, "gemini"
|
| 75 |
except Exception as e:
|
| 76 |
last_error = e
|
| 77 |
+
print(f"β Gemini attempt {attempt+1} failed: {str(e)}")
|
| 78 |
if attempt < max_retries - 1:
|
| 79 |
time.sleep(1)
|
| 80 |
|
|
|
|
| 90 |
return response.text, "cohere"
|
| 91 |
except Exception as e:
|
| 92 |
last_error = e
|
| 93 |
+
print(f"β Cohere attempt {attempt+1} failed: {str(e)}")
|
| 94 |
if attempt < max_retries - 1:
|
| 95 |
time.sleep(1)
|
| 96 |
|
|
|
|
| 106 |
return completion.choices[0].message.content, "zai"
|
| 107 |
except Exception as e:
|
| 108 |
last_error = e
|
| 109 |
+
print(f"β Z.ai attempt {attempt+1} failed: {str(e)}")
|
| 110 |
if attempt < max_retries - 1:
|
| 111 |
time.sleep(1)
|
| 112 |
|
|
|
|
| 121 |
return completion.choices[0].message.content, "minimax"
|
| 122 |
except Exception as e:
|
| 123 |
last_error = e
|
| 124 |
+
print(f"β MiniMax fallback failed: {str(e)}")
|
| 125 |
|
| 126 |
# All failed
|
| 127 |
+
error_msg = f"β Error: All AI services failed. Last error: {str(last_error)}"
|
| 128 |
return error_msg, "error"
|
| 129 |
|
| 130 |
# ---------- 3. Global storage ----------
|
|
|
|
| 166 |
except Exception as e:
|
| 167 |
return f"Error extracting PDF: {e}"
|
| 168 |
|
| 169 |
+
# ---------- 6. AI Tutor with Multi-Model Support (FIXED) ----------
|
| 170 |
def ai_tutor_chat(message, history, subject, topic, level):
|
| 171 |
if not message.strip():
|
| 172 |
return history
|
|
|
|
| 183 |
if user_msg:
|
| 184 |
conversation += f"Student: {user_msg}\n"
|
| 185 |
if bot_msg:
|
| 186 |
+
# FIXED: Remove emoji indicators that match what we're actually adding
|
| 187 |
+
clean_msg = bot_msg.replace("π΅ ", "").replace("π’ ", "").replace("π£ ", "")
|
| 188 |
conversation += f"Tutor: {clean_msg}\n"
|
| 189 |
|
| 190 |
conversation += f"Student: {message}\nTutor:"
|
|
|
|
| 304 |
try:
|
| 305 |
clean_txt = response.replace("```json", "").replace("```", "").strip()
|
| 306 |
fb = json.loads(clean_txt)
|
| 307 |
+
result = f"""π Score: {fb['score']}%
|
| 308 |
|
| 309 |
+
π Detailed Feedback:
|
| 310 |
{fb['feedback']}
|
| 311 |
|
| 312 |
+
β
Your Strengths:
|
| 313 |
{fb.get('strengths', 'Good effort!')}
|
| 314 |
|
| 315 |
+
οΏ½οΏ½ How to Improve:
|
| 316 |
{fb['improvements']}"""
|
| 317 |
|
| 318 |
if source in ["cohere", "zai", "minimax"]:
|
|
|
|
| 325 |
# ---------- 10. Admin β Past Papers ----------
|
| 326 |
def verify_admin_password(password):
|
| 327 |
if password == ADMIN_PASSWORD:
|
| 328 |
+
return gr.update(visible=True), gr.update(visible=False), "β
Access granted!"
|
| 329 |
+
return gr.update(visible=False), gr.update(visible=True), "β Incorrect password!"
|
| 330 |
|
| 331 |
def upload_paper(title, subject, level, content, pdf_file):
|
| 332 |
if not all([title, subject, level, content]):
|
| 333 |
+
return "β Please fill all required fields!", get_papers_list()
|
| 334 |
|
| 335 |
paper_id = len(papers_storage) + 1
|
| 336 |
|
|
|
|
| 350 |
"has_pdf": bool(pdf_text and not pdf_text.startswith("Error")),
|
| 351 |
"uploaded_at": datetime.now().strftime("%Y-%m-%d %H:%M")
|
| 352 |
})
|
| 353 |
+
return "β
Paper uploaded!", get_papers_list()
|
| 354 |
|
| 355 |
def get_papers_list():
|
| 356 |
if not papers_storage:
|
| 357 |
return "No papers yet."
|
| 358 |
return "\n".join(
|
| 359 |
+
f"**{p['title']}** ({p['subject'].upper()} - {p['level']}) {'π PDF' if p.get('has_pdf') else ''}\nβ° {p['uploaded_at']}\n{p['content'][:120]}...\n{'β'*60}"
|
| 360 |
for p in papers_storage
|
| 361 |
)
|
| 362 |
|
|
|
|
| 364 |
filtered = [p for p in papers_storage
|
| 365 |
if p["subject"] == subject.lower() and p["level"] == level]
|
| 366 |
if not filtered:
|
| 367 |
+
return f"π No {subject} {level} papers available."
|
| 368 |
return "\n".join(
|
| 369 |
+
f"**{p['title']}** {'π PDF' if p.get('has_pdf') else ''}\nβ° {p['uploaded_at']}\n\n{p['content']}\n\n{'β'*60}"
|
| 370 |
for p in filtered
|
| 371 |
)
|
| 372 |
|
| 373 |
# ---------- 11. Gradio UI ----------
|
| 374 |
with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE/GCSE Platform") as app:
|
| 375 |
gr.Markdown("""
|
| 376 |
+
# π IGCSE/GCSE Language Learning Platform
|
| 377 |
+
π€ AI Tutor | π Translator | π Dictionary | π Past Papers
|
| 378 |
_Powered by Gemini AI with intelligent fallback system_
|
| 379 |
""")
|
| 380 |
|
| 381 |
with gr.Tabs():
|
| 382 |
+
# βββββ STUDENT PORTAL βββββ
|
| 383 |
+
with gr.Tab("π¨βπ Student Portal"):
|
| 384 |
with gr.Tabs():
|
| 385 |
+
# AI TUTOR
|
| 386 |
+
with gr.Tab("π€ AI Tutor"):
|
| 387 |
gr.Markdown("### Chat with Your AI Tutor\n*Powered by Gemini 2.5 with automatic fallback*")
|
| 388 |
with gr.Row():
|
| 389 |
subj = gr.Radio(["French", "EFL"], label="Subject", value="French")
|
|
|
|
| 397 |
chat = gr.Chatbot(height=450, show_label=False)
|
| 398 |
txt = gr.Textbox(placeholder="Ask anything... e.g., 'Explain the passΓ© composΓ©'", label="Message")
|
| 399 |
with gr.Row():
|
| 400 |
+
send = gr.Button("Send π€", variant="primary")
|
| 401 |
+
clr = gr.Button("Clear π")
|
| 402 |
send.click(ai_tutor_chat, [txt, chat, subj, topc, lvl], chat)
|
| 403 |
txt.submit(ai_tutor_chat, [txt, chat, subj, topc, lvl], chat)
|
| 404 |
clr.click(clear_chat, outputs=chat)
|
| 405 |
|
| 406 |
+
# TRANSLATOR
|
| 407 |
+
with gr.Tab("π Translator"):
|
| 408 |
gr.Markdown("### English β· French Translation")
|
| 409 |
dir_ = gr.Radio(["English β French", "French β English"], label="Direction", value="English β French")
|
| 410 |
inp = gr.Textbox(lines=6, label="Input Text", placeholder="Enter text...")
|
| 411 |
out = gr.Textbox(lines=6, label="Translation")
|
| 412 |
+
gr.Button("Translate π", variant="primary").click(translate_text, [inp, dir_], out)
|
| 413 |
|
| 414 |
+
# DICTIONARY
|
| 415 |
+
with gr.Tab("π Dictionary"):
|
| 416 |
gr.Markdown("### French Dictionary")
|
| 417 |
w = gr.Textbox(placeholder="Enter French word...", label="Word")
|
| 418 |
o = gr.Textbox(lines=16, label="Definition")
|
| 419 |
+
gr.Button("Look Up π", variant="primary").click(dictionary_lookup, w, o)
|
| 420 |
|
| 421 |
+
# PRACTICE QUESTIONS
|
| 422 |
+
with gr.Tab("β Practice"):
|
| 423 |
gr.Markdown("### Generate & Practice Exam Questions")
|
| 424 |
with gr.Row():
|
| 425 |
ps = gr.Radio(["French", "EFL"], label="Subject", value="French")
|
|
|
|
| 427 |
pt = gr.Dropdown(french_topics, label="Topic")
|
| 428 |
ps.change(upd_topics, ps, pt)
|
| 429 |
|
| 430 |
+
q = gr.Textbox(label="π Question", lines=5, interactive=False)
|
| 431 |
exp = gr.Textbox(label="Expected", lines=2, visible=False)
|
| 432 |
+
mark = gr.Textbox(label="π Mark Scheme", lines=3, interactive=False)
|
| 433 |
+
ans = gr.Textbox(lines=8, label="β Your Answer", placeholder="Type your answer...")
|
| 434 |
+
fb = gr.Textbox(lines=12, label="π Feedback", interactive=False)
|
| 435 |
|
| 436 |
with gr.Row():
|
| 437 |
+
gr.Button("π² Generate", variant="primary").click(generate_question, [ps, pt, pl], [q, exp, mark])
|
| 438 |
+
gr.Button("β
Check", variant="secondary").click(check_answer, [q, exp, ans, ps, pl], fb)
|
| 439 |
|
| 440 |
+
# PAST PAPERS
|
| 441 |
+
with gr.Tab("π Past Papers"):
|
| 442 |
gr.Markdown("### Browse Past Papers")
|
| 443 |
with gr.Row():
|
| 444 |
psb = gr.Radio(["French", "EFL"], label="Subject", value="French")
|
| 445 |
plb = gr.Radio(["IGCSE", "GCSE"], label="Level", value="IGCSE")
|
| 446 |
pd = gr.Textbox(lines=22, label="Papers", interactive=False)
|
| 447 |
+
gr.Button("π Show", variant="primary").click(view_papers_student, [psb, plb], pd)
|
| 448 |
|
| 449 |
+
# βββββ ADMIN PANEL βββββ
|
| 450 |
+
with gr.Tab("π Admin Panel"):
|
| 451 |
with gr.Column() as login_section:
|
| 452 |
+
gr.Markdown("### π Admin Login")
|
| 453 |
pwd = gr.Textbox(label="Password", type="password", placeholder="Enter password")
|
| 454 |
+
login_btn = gr.Button("π Login", variant="primary")
|
| 455 |
login_status = gr.Textbox(label="Status", interactive=False)
|
| 456 |
|
| 457 |
with gr.Column(visible=False) as admin_section:
|
| 458 |
+
gr.Markdown("### π€ Upload Past Papers")
|
| 459 |
with gr.Row():
|
| 460 |
with gr.Column():
|
| 461 |
t = gr.Textbox(label="Title", placeholder="e.g., Paper 1 - June 2023")
|
|
|
|
| 463 |
s = gr.Radio(["French", "EFL"], label="Subject", value="French")
|
| 464 |
lv = gr.Radio(["IGCSE", "GCSE"], label="Level", value="IGCSE")
|
| 465 |
c = gr.Textbox(lines=6, label="Description")
|
| 466 |
+
pdf = gr.File(label="π PDF (optional)", file_types=[".pdf"])
|
| 467 |
+
up = gr.Button("β¬ Upload", variant="primary")
|
| 468 |
st = gr.Textbox(label="Status")
|
| 469 |
with gr.Column():
|
| 470 |
lst = gr.Textbox(lines=24, label="All Papers", value=get_papers_list(), interactive=False)
|
|
|
|
| 472 |
|
| 473 |
login_btn.click(verify_admin_password, [pwd], [admin_section, login_section, login_status])
|
| 474 |
|
| 475 |
+
gr.Markdown("""
|
| 476 |
+
---
|
| 477 |
+
**System Status:** π’ Gemini AI (Primary) | π΅ Cohere (Secondary) | π’ Z.ai (Tertiary) | π£ MiniMax (Fallback)
|
| 478 |
+
""")
|
| 479 |
+
|
| 480 |
app.launch()
|