File size: 11,643 Bytes
692776a
58a3465
692776a
 
 
76b7e39
c8d8c86
692776a
 
 
 
 
 
824f265
692776a
 
 
b17ebb1
 
 
 
 
692776a
 
 
 
 
 
 
 
 
 
824f265
692776a
9d045f5
 
 
 
 
 
 
 
b17ebb1
692776a
b17ebb1
 
 
 
 
 
 
 
 
 
 
 
8cc5c2b
692776a
 
3663605
692776a
1ef6e9f
 
 
 
692776a
 
 
 
e337288
692776a
 
 
 
 
 
 
 
63d3232
692776a
3c97477
5287471
 
 
3c97477
5287471
63d3232
 
 
692776a
 
 
 
b17ebb1
162f3dd
692776a
 
 
 
162f3dd
692776a
76b7e39
d16f094
 
 
76b7e39
d16f094
692776a
 
d16f094
692776a
162f3dd
 
 
76b7e39
162f3dd
76b7e39
a18ba81
162f3dd
 
 
 
 
 
692776a
162f3dd
8c858e3
a01b1ac
8c858e3
162f3dd
 
 
60ad9a0
162f3dd
 
 
 
 
 
 
 
63d3232
 
3c97477
 
63d3232
 
3c97477
 
63d3232
162f3dd
 
39edcdc
 
 
 
 
 
162f3dd
692776a
 
 
405d754
692776a
3914d50
692776a
 
39edcdc
 
815c9c6
 
7d23a77
692776a
 
3914d50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b5414a
692776a
 
 
 
 
b6fc404
 
405d754
b6fc404
692776a
405d754
692776a
 
405d754
692776a
 
 
 
 
 
 
 
405d754
692776a
7691320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
692776a
17d5a47
692776a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615626d
 
 
 
 
 
692776a
5a2d4ef
615626d
692776a
615626d
 
 
 
 
 
692776a
5a2d4ef
615626d
692776a
615626d
 
 
 
 
 
692776a
b8951c8
615626d
692776a
615626d
 
 
 
 
 
 
692776a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84626be
692776a
 
 
 
 
 
79a2bbf
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
import streamlit as st
import uuid

from utils import (
    load_idioms, generate_audio, init_db, add_favorite, get_favorite,
    detect_idioms, lemmatize_word, translate_literal, normalize_idiom, build_examples_map, remove_favorite,
    generate_adaptive_quiz, update_analytics, get_learning_stats, detect_idioms_ai
)

st.set_page_config(page_title="Idioms Learning App", layout="wide")
st.title("English–Spanish Idioms Learning App")

# Load resources
idiom_map = load_idioms("idiom2.json")
idioms = sorted(idiom_map.keys())
conn = init_db()
examples_map = build_examples_map()
##13/5
topics = sorted(list(set(
    v.get("topic", "General")
    for v in idiom_map.values()
)))

# Sidebar
mode = st.sidebar.selectbox(
    "Choose a mode",
    ["Explore Idioms", "Idioms in sentences", "Quiz time!", "Favorites", "Learning Analytics"]
)

# EXPLORE

if mode == "Explore Idioms":

    search = st.text_input("Search idiom:")

    topics = sorted(list(set(
        v.get("topic", "General")
        for v in idiom_map.values()
    )))
    
    selected_topic = st.selectbox("Choose Topic", ["All"] + topics)
#filtered = [i for i in idioms if search.lower() in i.lower()]   
    filtered = []

    for idiom in idioms:
    
        matches_search = search.lower() in idiom.lower()
        topic = idiom_map[idiom].get("topic", "General")
        matches_topic = (
            selected_topic == "All"
            or topic == selected_topic
        )
        if matches_search and matches_topic:
            filtered.append(idiom)
            
    selected = st.selectbox("Choose idiom:", filtered)

    if selected:
        st.subheader("Natural Spanish Meaning")
        st.write(idiom_map[selected]["meaning"])

        st.subheader("English Meaning")
        st.write(idiom_map[selected].get("english_meaning", "Not available"))


        st.subheader("Literal Translation")
        st.write(translate_literal(selected))

        st.subheader("Examples:")
        examples = examples_map.get(normalize_idiom(selected), []) #changed
        if examples:
            for ex in examples[:3]:
                st.write("**English:**", ex["en"])
                st.write("**Spanish:**", ex["es"])
                st.write("---")
        else:
            st.write("No examples found.")

        
        st.subheader("Audio")
        st.write("English Idiom")
        audio_en = generate_audio(selected, lang="en")
        st.audio(audio_en)

        st.write("Spanish Equivalent")
        audio_es = generate_audio(idiom_map[selected]["meaning"], lang="es")
        st.audio(audio_es)
        #audio_file = generate_audio(selected)
        #st.audio(audio_file)

        if st.button("⭐ Add to Favorites"):
            add_favorite(conn, selected)
            st.success("Added to favorites!")
        

# DETECT 

elif mode == "Idioms in sentences":
    text = st.text_area("Write your text:")

    if st.button("Detect"):
        found, spans = detect_idioms(text, idioms) #add spans
        # use AI if no matching idioms
        if not found:
            found = detect_idioms_ai(text, idiom_map)
            spans = {idiom: idiom for idiom in found}

        if not found:
            st.info("No idioms detected.")
        
        else:
            # Highlight detected idioms in the text
            highlighted_text = text
            for idiom in found:
                matched_text = spans.get(idiom, idiom) #instead idiom
                highlighted_text = highlighted_text.replace(
                    matched_text,
                    f"<span style='background-color: #FFAB91; font-weight:bold'>{idiom}</span>"
                )
            st.markdown("### Your Text with Detected Idioms")
            st.markdown(highlighted_text, unsafe_allow_html=True)
            st.markdown("---")

            # Show idiom details
            for idiom in found:
                with st.expander(f"🔹 {idiom}"):
                    st.write("**Meaning:**", idiom_map[idiom]["meaning"])
                    st.write("**English Meaning:**", idiom_map[idiom].get("english_meaning", "Not available"))
                    st.caption(f"Topic: {idiom_map[idiom].get('topic', 'General')}")
                    st.write("**Literal Translation:**", translate_literal(idiom))

                    # Examples
                    examples = examples_map.get(normalize_idiom(idiom), []) #changed
                    if examples:
                        st.subheader("Examples:")
                        for ex in examples[:2]:
                            st.write("•", ex["en"])
                    else:
                        st.write("No examples available.")

                    # Audio
                    #audio_file = generate_audio(idiom)
                    #st.audio(audio_file)
                    st.write("English Idiom")
                    audio_en = generate_audio(idiom, lang="en")
                    st.audio(audio_en)
                    
                    st.write("Spanish Equivalent")
                    audio_es = generate_audio(idiom_map[idiom]["meaning"], lang="es")
                    st.audio(audio_es)
                    # Practice button
                    if st.button(f"Practice '{idiom}'"):
                        st.session_state.quiz = generate_adaptive_quiz(
                                                    conn,
                                                    {idiom: idiom_map[idiom]},
                                                    examples_map,
                                                    st.session_state.used_questions,
                                                    st.session_state.used_structures)
                        st.rerun()

# QUIZ
elif mode == "Quiz time!":
    st.header("🎮 Quiz")

    # Initialize state FIRST, before anything else
    if "xp" not in st.session_state:
        st.session_state.xp = 0
    if "used_structures" not in st.session_state:
        st.session_state.used_structures = set()
    if "used_questions" not in st.session_state:
        st.session_state.used_questions = set()

    st.metric("XP", st.session_state.xp)

    if "quiz" not in st.session_state or st.session_state.quiz is None:
        with st.spinner("Generating question..."):
            st.session_state.quiz = generate_adaptive_quiz(
                conn,
                idiom_map,
                examples_map,
                st.session_state.used_questions,
                st.session_state.used_structures
            )

    if st.session_state.quiz is None:
        st.error("Could not generate a quiz question. The AI model may have failed. Try clicking New Question.")
        if st.button("New Question"):
            st.session_state.pop("quiz", None)
            st.rerun()
        st.stop()

    quiz = st.session_state.quiz

    st.write("Fill in the blank")
    st.write(quiz["question"])

    user_answer = st.radio(
        "Choose your answer:",
        quiz["options"]
    )

    # ---------- SUBMIT ----------
    if st.button("Submit"):

        correct = (user_answer == quiz["answer"])
        update_analytics(conn, quiz["answer"], correct)

        if correct:
            st.success("Correct! +5 XP")
            st.session_state.xp += 5
        else:
            st.error(f"Wrong! Correct answer: {quiz['answer']}")

    # ---------- NEW QUESTION ----------
    if st.button("New Question"):
        with st.spinner("Generating question..."):
            new_quiz = generate_adaptive_quiz(
                conn,
                idiom_map,
                examples_map,
                st.session_state.used_questions,
                st.session_state.used_structures
            )
        #the spinner
        if new_quiz is None:
            st.error("Could not generate a new question. Try again.")
        else:
            # store used items properly
            st.session_state.used_questions.add(new_quiz["answer"])
    
            st.session_state.quiz = new_quiz
    
            st.rerun()


# ANALYTICS

elif mode == "Learning Analytics":

    st.header("Learning Dashboard")

    stats = get_learning_stats(conn)

    if not stats:
        st.info("No quiz data yet. Try the quiz first!")
    else:
        import numpy as np
        import pandas as pd

        total_idioms = len(idioms)

        mastered = len([i for i,a,c,acc in stats if acc >= 0.8 and a >= 2])
        practiced = len([i for i,a,c,acc in stats if a > 0])
        weak = len([i for i,a,c,acc in stats if acc < 0.6 and a > 0])

        avg_acc = np.mean([acc for _,a,_,acc in stats if a > 0]) if stats else 0

        # XP system
        xp = st.session_state.get("xp",0)
        next_level = 100
        xp_progress = xp / next_level

        st.subheader("⭐ XP Progress")

        st.metric("Total XP", xp)
        st.progress(xp_progress)

        st.markdown("---")

        # Global Stats
        st.subheader("🏆 Your Progress")

        col1,col2,col3,col4 = st.columns(4)

        with col1:
            st.metric(
                label="🎯 Mastered",
                value=f"{mastered}/{total_idioms}",
            )
            with st.expander("❓ What is Mastered?"):
                st.caption("Idioms you have answered correctly at least 80% of the time.")
            st.progress(mastered/total_idioms)
        
        
        with col2:
            st.metric(
                label="📚 Practiced",
                value=f"{practiced}/{total_idioms}",
            )
            with st.expander("❓ What is Practiced?"):
                st.caption("Idioms you have attempted at least once.")
            st.progress(practiced/total_idioms)
        
        
        with col3:
            st.metric(
                label="⚠️ Weak",
                value=weak,
            )
            with st.expander("❓ What is Weak?"):
                st.caption("Idioms with accuracy below 60%.")
            st.progress(weak/total_idioms)
        
        
        with col4:
            st.metric(
                label="🎯 Accuracy",
                value=f"{avg_acc*100:.1f}%",
            )
            with st.expander("❓ What is Accuracy?"):
                st.caption("Your average correct rate across all attempted idioms.")
            st.progress(avg_acc)        

        st.markdown("---")

        # Performance Chart
        st.subheader("📊 Performance")

        df = pd.DataFrame(stats, columns=["Idiom","Attempts","Correct","Accuracy"])

        df_sorted = df.sort_values("Accuracy")

        st.bar_chart(
            df_sorted.set_index("Idiom")["Accuracy"]
        )

        st.markdown("---")

        # Weak Idioms
        st.subheader("🔥 Idioms You Should Practice")

        weak_list = df[df["Accuracy"] < 0.6]["Idiom"].tolist()

        if weak_list:
            for w in weak_list:
                st.warning(f"Practice: **{w}**")
        else:
            st.success("Great job! No weak idioms 🎉")

        st.markdown("---")

        # Full Table
        st.subheader("Detailed Stats")

        st.dataframe(df)
# FAVORITES

elif mode == "Favorites":

    st.header("Your Favorites")
    favs = get_favorite(conn)

    if favs:
        for idiom in favs:
            col1, col2 = st.columns([4, 1])
            with col1:
                st.write("⭐", idiom)
                st.write("Meaning:",idiom_map.get(idiom, {}).get("meaning", "Unknown"))
            with col2:
                if st.button("Remove", key=f"remove_{idiom}"):
                    remove_favorite(conn, idiom)
                    st.rerun()
            st.write("---")
    else:
        st.write("No favorites yet.")