GeetaAIVisionary commited on
Commit
bfc4596
·
verified ·
1 Parent(s): 9bdd039

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +180 -6
app.py CHANGED
@@ -1,10 +1,15 @@
 
 
 
1
  from pathlib import Path
2
- import pandas as pd
3
  import csv
 
 
4
 
5
- # make path robust in Spaces
6
- WORDS_PATH = Path(__file__).parent / "words.csv"
7
 
 
8
  EXPECTED_COLS = ["word", "difficulty", "definition", "origin", "sentence"]
9
 
10
  def _load_words(path: Path = WORDS_PATH) -> pd.DataFrame:
@@ -28,7 +33,6 @@ def _load_words(path: Path = WORDS_PATH) -> pd.DataFrame:
28
  dtype=str,
29
  keep_default_na=False
30
  )
31
- # If it parsed to 5+ columns, normalize below
32
  df = df_try
33
  except Exception:
34
  # fall back to manual glue if pandas fails
@@ -54,13 +58,11 @@ def _load_words(path: Path = WORDS_PATH) -> pd.DataFrame:
54
  df = pd.DataFrame(rows, columns=EXPECTED_COLS)
55
 
56
  # --- Normalize columns ---
57
- cols = [c.strip().lower() for c in df.columns]
58
  mapper = {c: c.strip().lower() for c in df.columns}
59
  df = df.rename(columns=mapper)
60
 
61
  # If there are more than 5 columns, rebuild to our schema
62
  if df.shape[1] >= 5:
63
- # take first four known fields if present, then glue rest into sentence
64
  def pick(colname, default=""):
65
  return df[colname] if colname in df.columns else default
66
  base_df = pd.DataFrame({
@@ -106,3 +108,175 @@ def _load_words(path: Path = WORDS_PATH) -> pd.DataFrame:
106
  # sort hardest → easiest
107
  df = df.sort_values("difficulty_score", ascending=False).reset_index(drop=True)
108
  return df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ # CLI-like Spelling Bee Tutor as a simple Gradio UI (no LLMs, all offline)
3
+
4
  from pathlib import Path
5
+ import os
6
  import csv
7
+ import pandas as pd
8
+ import gradio as gr
9
 
10
+ # ---------- Data loading (robust to messy CSVs) ----------
 
11
 
12
+ WORDS_PATH = Path(__file__).parent / "words.csv"
13
  EXPECTED_COLS = ["word", "difficulty", "definition", "origin", "sentence"]
14
 
15
  def _load_words(path: Path = WORDS_PATH) -> pd.DataFrame:
 
33
  dtype=str,
34
  keep_default_na=False
35
  )
 
36
  df = df_try
37
  except Exception:
38
  # fall back to manual glue if pandas fails
 
58
  df = pd.DataFrame(rows, columns=EXPECTED_COLS)
59
 
60
  # --- Normalize columns ---
 
61
  mapper = {c: c.strip().lower() for c in df.columns}
62
  df = df.rename(columns=mapper)
63
 
64
  # If there are more than 5 columns, rebuild to our schema
65
  if df.shape[1] >= 5:
 
66
  def pick(colname, default=""):
67
  return df[colname] if colname in df.columns else default
68
  base_df = pd.DataFrame({
 
108
  # sort hardest → easiest
109
  df = df.sort_values("difficulty_score", ascending=False).reset_index(drop=True)
110
  return df
111
+
112
+ DF = _load_words()
113
+
114
+ # ---------- Quiz logic ----------
115
+
116
+ def _summary(state):
117
+ if not state:
118
+ return "No round active."
119
+ n = state["n"]
120
+ score = state["score"]
121
+ return f"Round complete. Score this round: {score}/{n}."
122
+
123
+ def start_quiz(n_words, state):
124
+ df = DF
125
+ if df.empty:
126
+ return (state,
127
+ "words.csv not found or empty.",
128
+ gr.update(value="", interactive=False),
129
+ "Please add a words.csv with at least one 'word' column.",
130
+ "Score: 0/0",
131
+ gr.update(value="", interactive=False))
132
+
133
+ try:
134
+ n = int(n_words or 5)
135
+ except Exception:
136
+ n = 5
137
+ n = max(1, min(n, len(df))) # clip to available size
138
+
139
+ block = df.head(n).reset_index(drop=True)
140
+ s = {
141
+ "i": 0,
142
+ "n": n,
143
+ "score": 0,
144
+ "words": block["word"].astype(str).tolist(),
145
+ "defs": block["definition"].astype(str).tolist(),
146
+ "orig": block["origin"].astype(str).tolist(),
147
+ "sent": block["sentence"].astype(str).tolist(),
148
+ }
149
+
150
+ current = s["words"][0]
151
+ hist = f"Okay! We'll do {n} words this round, hardest → easiest.\nSpell this word: {current}"
152
+ status = "Type your spelling attempt, or click definition/origin/sentence."
153
+ return s, hist, gr.update(value=current, interactive=False), status, f"Score: 0/{n}", gr.update(value="", interactive=True)
154
+
155
+ def _check_state(state):
156
+ return bool(state and "words" in state and state["i"] < state["n"])
157
+
158
+ def check_attempt(state, attempt, history, current_word, score_md):
159
+ if not _check_state(state):
160
+ return state, history, current_word, "No round active. Click Start.", score_md, gr.update(value="")
161
+ attempt = (attempt or "").strip()
162
+ if not attempt:
163
+ return state, history, current_word, "Type your attempt first.", score_md, gr.update(value="")
164
+
165
+ target = state["words"][state["i"]]
166
+ if attempt.lower() == target.lower():
167
+ state["score"] += 1
168
+ msg = "✅ Correct!"
169
+ else:
170
+ msg = "❌ Not quite. Try again or ask for definition/origin/sentence."
171
+
172
+ history = f"{history}\nYou: {attempt}\nTutor: {msg}"
173
+ score_md = f"Score: {state['score']}/{state['n']}"
174
+ return state, history, current_word, msg, score_md, gr.update(value="")
175
+
176
+ def _safe_text(val, fallback="Not available."):
177
+ v = (val or "").strip()
178
+ return v if v else fallback
179
+
180
+ def show_def(state, history, current_word, score_md):
181
+ if not _check_state(state):
182
+ return state, history, current_word, "No round active. Click Start.", score_md
183
+ i = state["i"]
184
+ word = state["words"][i]
185
+ text = _safe_text(state["defs"][i])
186
+ history = f"{history}\nTutor (definition of {word}): {text}"
187
+ return state, history, current_word, f"Definition of {word}: {text}", score_md
188
+
189
+ def show_origin(state, history, current_word, score_md):
190
+ if not _check_state(state):
191
+ return state, history, current_word, "No round active. Click Start.", score_md
192
+ i = state["i"]
193
+ word = state["words"][i]
194
+ text = _safe_text(state["orig"][i])
195
+ history = f"{history}\nTutor (origin of {word}): {text}"
196
+ return state, history, current_word, f"Origin of {word}: {text}", score_md
197
+
198
+ def show_sentence(state, history, current_word, score_md):
199
+ if not _check_state(state):
200
+ return state, history, current_word, "No round active. Click Start.", score_md
201
+ i = state["i"]
202
+ word = state["words"][i]
203
+ text = _safe_text(state["sent"][i])
204
+ history = f"{history}\nTutor (sentence with {word}): {text}"
205
+ return state, history, current_word, f"Sentence: {text}", score_md
206
+
207
+ def next_word(state, history, current_word, score_md):
208
+ if not _check_state(state):
209
+ return state, history, current_word, "No round active. Click Start.", score_md
210
+ state["i"] += 1
211
+ if state["i"] >= state["n"]:
212
+ summary = _summary(state)
213
+ history = f"{history}\n{summary}"
214
+ return {}, history, gr.update(value="", interactive=False), summary, f"Score: {state['score']}/{state['n']}"
215
+ w = state["words"][state["i"]]
216
+ history = f"{history}\nNext word: {w}"
217
+ status = "Type your spelling attempt, or click definition/origin/sentence."
218
+ return state, history, gr.update(value=w, interactive=False), status, f"Score: {state['score']}/{state['n']}"
219
+
220
+ def stop_round(state, history, current_word, score_md):
221
+ if not state:
222
+ return {}, history, current_word, "Stopped.", score_md
223
+ summary = _summary(state)
224
+ history = f"{history}\n{summary}"
225
+ return {}, history, gr.update(value="", interactive=False), "Stopped.", f"Score: {state['score']}/{state['n']}"
226
+
227
+ # ---------- UI ----------
228
+
229
+ with gr.Blocks() as demo:
230
+ gr.Markdown("# NeMo Guardrails Demo (starter UI)\n**CLI-style spelling quiz** — works offline from `words.csv`.")
231
+
232
+ with gr.Row():
233
+ n_words = gr.Number(value=5, precision=0, label="Words this round")
234
+ start = gr.Button("Start quiz")
235
+
236
+ current_word = gr.Textbox(label="Spell this word", interactive=False)
237
+ attempt = gr.Textbox(label="Your attempt")
238
+ with gr.Row():
239
+ check = gr.Button("Check")
240
+ bdef = gr.Button("definition")
241
+ borg = gr.Button("origin")
242
+ bsent = gr.Button("sentence")
243
+ bnext = gr.Button("next")
244
+ bstop = gr.Button("stop")
245
+
246
+ status = gr.Markdown("")
247
+ score_md = gr.Markdown("Score: 0/0")
248
+ history = gr.Textbox(label="History", lines=14)
249
+
250
+ state = gr.State({})
251
+
252
+ # wire events
253
+ start.click(start_quiz, [n_words, state],
254
+ [state, history, current_word, status, score_md, attempt])
255
+
256
+ check.click(check_attempt, [state, attempt, history, current_word, score_md],
257
+ [state, history, current_word, status, score_md, attempt])
258
+
259
+ bdef.click(show_def, [state, history, current_word, score_md],
260
+ [state, history, current_word, status, score_md], queue=False)
261
+ borg.click(show_origin, [state, history, current_word, score_md],
262
+ [state, history, current_word, status, score_md], queue=False)
263
+ bsent.click(show_sentence, [state, history, current_word, score_md],
264
+ [state, history, current_word, status, score_md], queue=False)
265
+ bnext.click(next_word, [state, history, current_word, score_md],
266
+ [state, history, current_word, status, score_md], queue=False)
267
+ bstop.click(stop_round, [state, history, current_word, score_md],
268
+ [state, history, current_word, status, score_md], queue=False)
269
+
270
+ # ---------- Launch (blocking on Spaces) ----------
271
+
272
+ if __name__ == "__main__":
273
+ try:
274
+ demo.queue(concurrency_count=8, max_size=32)
275
+ except Exception:
276
+ pass
277
+
278
+ demo.launch(
279
+ server_name="0.0.0.0",
280
+ server_port=int(os.getenv("PORT", "7860")),
281
+ show_error=True,
282
+ )