File size: 6,193 Bytes
861a478 bab6d73 861a478 bab6d73 861a478 bab6d73 861a478 bab6d73 861a478 bab6d73 861a478 bab6d73 861a478 bab6d73 861a478 bab6d73 861a478 bab6d73 2b8a999 bab6d73 2b8a999 bab6d73 2b8a999 bab6d73 2b8a999 bab6d73 2b8a999 bab6d73 2b8a999 bab6d73 2b8a999 bab6d73 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# data/manual_answers.py – manual teacher answers
import os
import json
from typing import Dict, List, Optional
from .text_utils import normalize_question
MANUAL_QA_PATH = "data/manual_qa.jsonl"
# Map: normalized question -> answer (used by chatbot)
MANUAL_ANSWERS: Dict[str, str] = {}
# List used for UI (table)
# Each item: {"norm_q": ..., "q": ..., "a": ...}
MANUAL_LIST: List[Dict[str, str]] = []
# Fallback manual Q&A (built-in defaults)
_FALLBACK_MANUAL_QA: List[Dict[str, str]] = [
{
"q": "ປະຫວັດສາດມີຄວາມສໍາຄັນຈັ່ງໃດ໋?",
"a": (
"ປະຫວັດສາດມີຄວາມສໍາຄັນເພາະຊ່ວຍໃຫ້ເຮົາຮູ້ປະຫວັດຄວາມເປັນມາຂອງຊາດ "
"ຂອງບັນພະບຸລຸດ ແລະຂອງສັງຄົມມະນຸດ. ມັນເຮັດໃຫ້ເຮົ້າມີຄວາມພາກພູມໃຈ "
"ຮູ້ບຸນຄຸນປູ່ຍ່າຕາຍາຍ ແລະຮູ້ຫນ້າທີ່ຂອງຕົນເອງໃນການຮັກສາແລະພັດທະນາປະເທດຊາດ."
),
},
{
"q": "ປະຫວັດສາດແມ່ນຫຍັງ?",
"a": (
"ປະຫວັດສາດແມ່ນວິທະຍາສາດທີ່ສຶກສາເຫດການຕ່າງໆໃນອະດີດຂອງສັງຄົມມະນຸດ "
"ຕັ້ງແຕ່ການເກີດຂຶ້ນ ການເຕີບໂຕ ແລະການປ່ຽນແປງຕາມເວລາ."
),
},
{
"q": "ຫຼັກຖານປະຫວັດສາດມີຫຍັງແດ່?",
"a": (
"ຫຼັກຖານປະຫວັດສາດມີ 3 ປະເພດຫຼັກ: ຫຼັກຖານຜ່ານການບອກເລົ່າ, "
"ຫຼັກຖານປະເພດວັດຖຸ ແລະເອກະສານປະເພດການບັນທຶກ."
),
},
]
# ---------- Internal helpers ----------
def _add_pair(q: str, a: str) -> str:
"""
Add/overwrite a (q,a) pair into MANUAL_ANSWERS + MANUAL_LIST.
Returns normalized key.
"""
q_clean = q.strip()
a_clean = a.strip()
if not q_clean or not a_clean:
return ""
norm_q = normalize_question(q_clean)
# Update dict
MANUAL_ANSWERS[norm_q] = a_clean
# Update list (remove old entry with same key, then append new)
global MANUAL_LIST
MANUAL_LIST = [row for row in MANUAL_LIST if row["norm_q"] != norm_q]
MANUAL_LIST.append({"norm_q": norm_q, "q": q_clean, "a": a_clean})
return norm_q
def _load_fallback():
for pair in _FALLBACK_MANUAL_QA:
q = pair.get("q", "")
a = pair.get("a", "")
if q and a:
_add_pair(q, a)
def _load_from_file():
if not os.path.exists(MANUAL_QA_PATH):
return
with open(MANUAL_QA_PATH, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
obj = json.loads(line)
except json.JSONDecodeError:
continue
q = obj.get("q", "")
a = obj.get("a", "")
if q and a:
_add_pair(q, a)
def _load_all():
MANUAL_ANSWERS.clear()
MANUAL_LIST.clear()
_load_fallback()
_load_from_file()
def _save_to_file():
"""
Rewrite manual_qa.jsonl from MANUAL_LIST.
(Includes fallback entries once we start saving – that’s OK.)
"""
os.makedirs(os.path.dirname(MANUAL_QA_PATH), exist_ok=True)
with open(MANUAL_QA_PATH, "w", encoding="utf-8") as f:
for row in MANUAL_LIST:
json.dump({"q": row["q"], "a": row["a"]}, f, ensure_ascii=False)
f.write("\n")
# ---------- Public API (used by chatbot + UI) ----------
def add_manual_qa(question: str, answer: str) -> str:
"""
Add new manual Q&A (or overwrite existing same question).
Updates in-memory index and rewrites file.
Returns normalized key.
"""
if not question.strip() or not answer.strip():
raise ValueError("Question and answer must not be empty.")
norm_q = _add_pair(question, answer)
_save_to_file()
return norm_q
def list_manual_qa() -> List[Dict[str, str]]:
"""
Return a copy of all manual Q&A rows for displaying in UI.
Each row: { 'norm_q', 'q', 'a' }.
"""
return list(MANUAL_LIST)
def get_manual_qa(norm_q: str) -> Optional[Dict[str, str]]:
"""
Look up one Q&A by normalized key (for loading into edit boxes).
"""
for row in MANUAL_LIST:
if row["norm_q"] == norm_q:
return row
return None
def update_manual_qa(old_norm_q: str, new_q: str, new_a: str) -> str:
"""
Update an existing entry identified by old_norm_q.
Allows changing question text (so key may change).
Returns the new normalized key.
"""
if not old_norm_q:
raise ValueError("No entry selected for update.")
if not new_q.strip() or not new_a.strip():
raise ValueError("Question and answer must not be empty.")
# Remove old entry (if exists)
global MANUAL_LIST
MANUAL_LIST = [row for row in MANUAL_LIST if row["norm_q"] != old_norm_q]
if old_norm_q in MANUAL_ANSWERS:
MANUAL_ANSWERS.pop(old_norm_q, None)
# Add as new pair
norm_q = _add_pair(new_q, new_a)
_save_to_file()
return norm_q
def delete_manual_qa(norm_q: str) -> None:
"""
Delete an entry by normalized key.
"""
if not norm_q:
raise ValueError("No entry selected for deletion.")
global MANUAL_LIST
before = len(MANUAL_LIST)
MANUAL_LIST = [row for row in MANUAL_LIST if row["norm_q"] != norm_q]
if len(MANUAL_LIST) == before:
raise ValueError("Entry not found.")
if norm_q in MANUAL_ANSWERS:
MANUAL_ANSWERS.pop(norm_q, None)
_save_to_file()
# Load everything at import time
_load_all()
|