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()