# teacher_panel.py from typing import List, Tuple import gradio as gr # ⬅️ needed for gr.SelectData type import qa_store from qa_store import normalize_question from loader import ( generate_new_manual_id, save_manual_qa_file, rebuild_combined_qa, manual_qa_table_data, ) def _table() -> List[List[str]]: return manual_qa_table_data() def teacher_save_add_overwrite( question: str, answer: str, current_id: str ) -> Tuple[str, str, str, List[List[str]], str]: """ Add a new manual Q&A or overwrite existing one with the same normalized question. """ question = (question or "").strip() answer = (answer or "").strip() current_id = current_id or "" if not question or not answer: msg = "❌ ກະລຸນາໃສ່ທັງຄໍາຖາມແລະຄໍາຕອບ." return question, answer, current_id, _table(), msg norm_q = normalize_question(question) entry = qa_store.MANUAL_QA_INDEX.get(norm_q) if entry: # overwrite existing manual Q&A entry["q"] = question entry["a"] = answer entry["norm_q"] = norm_q current_id = entry["id"] msg = "✅ ອັບເດດ Q&A ທີ່ມີຢູ່ແລ້ວ." else: # create new entry new_id = generate_new_manual_id() entry = { "id": new_id, "q": question, "a": answer, "norm_q": norm_q, } qa_store.MANUAL_QA_LIST.append(entry) qa_store.MANUAL_QA_INDEX[norm_q] = entry current_id = new_id msg = "✅ ບັນທຶກ Q&A ໃໝ່ສໍາເລັດ." save_manual_qa_file() rebuild_combined_qa() return question, answer, current_id, _table(), msg def teacher_update_current( question: str, answer: str, current_id: str ) -> Tuple[str, str, str, List[List[str]], str]: """ Update the currently selected manual Q&A by id. """ question = (question or "").strip() answer = (answer or "").strip() current_id = current_id or "" if not current_id: msg = "❌ ກະລຸນາເລືອກ Q&A ຈາກຕາຕະລາງກ່ອນ." return question, answer, current_id, _table(), msg if not question or not answer: msg = "❌ ກະລຸນາໃສ່ທັງຄໍາຖາມແລະຄໍາຕອບ." return question, answer, current_id, _table(), msg entry = next((e for e in qa_store.MANUAL_QA_LIST if e["id"] == current_id), None) if not entry: msg = "⚠️ ບໍ່ພົບ Q&A ທີ່ເລືອກ." current_id = "" return question, answer, current_id, _table(), msg # remove old index qa_store.MANUAL_QA_INDEX.pop(entry["norm_q"], None) new_norm = normalize_question(question) entry["q"] = question entry["a"] = answer entry["norm_q"] = new_norm qa_store.MANUAL_QA_INDEX[new_norm] = entry save_manual_qa_file() rebuild_combined_qa() msg = "✅ ແກ້ໄຂ Q&A ສໍາເລັດ." return question, answer, current_id, _table(), msg def teacher_delete_current( current_id: str, ) -> Tuple[str, str, str, List[List[str]], str]: """ Delete the currently selected manual Q&A. """ current_id = current_id or "" if not current_id: msg = "❌ ກະລຸນາເລືອກ Q&A ກ່ອນຈະລຶບ." return "", "", current_id, _table(), msg entry = next((e for e in qa_store.MANUAL_QA_LIST if e["id"] == current_id), None) if not entry: msg = "⚠️ ບໍ່ພົບ Q&A ທີ່ຈະລຶບ." return "", "", "", _table(), msg qa_store.MANUAL_QA_LIST = [e for e in qa_store.MANUAL_QA_LIST if e["id"] != current_id] qa_store.MANUAL_QA_INDEX.pop(entry["norm_q"], None) save_manual_qa_file() rebuild_combined_qa() msg = "🗑️ ລຶບ Q&A ສໍາເລັດ." return "", "", "", _table(), msg def teacher_on_table_select( evt: gr.SelectData, ) -> Tuple[str, str, str, str]: """ When teacher clicks a row in the Q&A table, load Q/A into textboxes. We only use the SelectData event: - evt.index -> (row, col) - evt.row_value -> the whole row [id, question, answer] """ try: if evt is None or evt.index is None or evt.row_value is None: raise ValueError("Empty select event") # row_value is already the full row (list-like) row = list(evt.row_value) current_id = str(row[0]) if len(row) > 0 else "" question = str(row[1]) if len(row) > 1 else "" answer = str(row[2]) if len(row) > 2 else "" if not current_id: raise ValueError("Empty ID from selected row") msg = f"✏️ ເລືອກ Q&A ID {current_id} ສໍາລັບແກ້ໄຂ." except Exception as e: # noqa: BLE001 print(f"[WARN] teacher_on_table_select error: {e}") current_id = "" question = "" answer = "" msg = "⚠️ ບໍ່ສາມາດເລືອກແຖວໄດ້." return question, answer, current_id, msg