Yoans commited on
Commit
dcc4244
ยท
verified ยท
1 Parent(s): 3536568

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -21
app.py CHANGED
@@ -1,7 +1,11 @@
1
  # app.py
2
  # ThinkPal โ€“ Hugging Face Space (Gradio)
3
 
4
- import os, json, uuid, re, unicodedata
 
 
 
 
5
  from difflib import get_close_matches, SequenceMatcher
6
 
7
  import gradio as gr
@@ -29,23 +33,27 @@ else:
29
  # -----------------------------
30
  # Storage helpers (JSON)
31
  # -----------------------------
32
- def load_students() -> dict:
33
  if not os.path.exists(DATA_FILE):
34
  return {}
35
  with open(DATA_FILE, "r", encoding="utf-8") as f:
36
  return json.load(f)
37
 
38
- def save_students(data: dict) -> None:
 
39
  with open(DATA_FILE, "w", encoding="utf-8") as f:
40
  json.dump(data, f, indent=2, ensure_ascii=False)
41
 
42
- def list_student_ids() -> list:
 
43
  return sorted(load_students().keys())
44
 
45
- def get_student(student_id: str) -> dict | None:
 
46
  return load_students().get(student_id)
47
 
48
- def add_student(data: dict) -> str:
 
49
  students = load_students()
50
  # unique random id (short)
51
  new_id = f"student_{uuid.uuid4().hex[:8]}"
@@ -53,7 +61,8 @@ def add_student(data: dict) -> str:
53
  save_students(students)
54
  return new_id
55
 
56
- def update_student(student_id: str, updates: dict) -> str:
 
57
  students = load_students()
58
  if student_id not in students:
59
  return f"โŒ {student_id} not found."
@@ -106,16 +115,18 @@ FAQ_SYNONYMS = [
106
  "community", "mentors", "progress tracking", "insights meaning"
107
  ]
108
 
109
- def _normalize(text: str) -> str:
110
- t = text.lower().strip()
 
111
  # remove diacritics / normalize Arabic
112
  t = "".join(c for c in unicodedata.normalize("NFD", t) if unicodedata.category(c) != "Mn")
113
  t = re.sub(r"[^\w\s\u0600-\u06FF]", " ", t)
114
  t = re.sub(r"\s+", " ", t)
115
  return t
116
 
117
- def find_faq_answer(user_input: str, cutoff: float = 0.6) -> str | None:
118
- if not user_input:
 
119
  return None
120
  ui = _normalize(user_input)
121
 
@@ -133,10 +144,9 @@ def find_faq_answer(user_input: str, cutoff: float = 0.6) -> str | None:
133
  best_q = max(candidates, key=lambda q: SequenceMatcher(None, ui, _normalize(q)).ratio())
134
  if SequenceMatcher(None, ui, _normalize(best_q)).ratio() >= cutoff:
135
  # map a synonym to a reasonable FAQ (heuristic)
136
- # for stopping/resume
137
  if any(k in ui for k in ["ู†ุต ุงู„ุทุฑูŠู‚", "ู„ูˆ ูˆู‚ูุช", "stop halfway", "resume"]):
138
- return FAQS["ู„ูˆ ูˆู‚ูุช ููŠ ู†ุต ุงู„ุทุฑูŠู‚ุŸ"]
139
- # else: return closest questionโ€™s answer if it exists
140
  if best_q in FAQS:
141
  return FAQS[best_q]
142
  return None
@@ -165,7 +175,8 @@ Sections required:
165
  4) Milestones (by Phase)
166
  """
167
 
168
- def _compose_profile(student: dict) -> str:
 
169
  mapping = [
170
  ("learning_style", "Learning Style"),
171
  ("academic_progress", "Academic Progress"),
@@ -183,14 +194,15 @@ def _compose_profile(student: dict) -> str:
183
  ]
184
  parts = []
185
  for key, label in mapping:
186
- val = student.get(key)
187
  if isinstance(val, list):
188
  val = ", ".join(val)
189
  if val:
190
  parts.append(f"{label}: {val}")
191
  return " | ".join(parts) if parts else "No student data provided."
192
 
193
- def get_gemini_response(query: str, student: dict | None = None) -> str:
 
194
  profile = _compose_profile(student or {})
195
  prompt = f"""Student Profile: {profile}
196
 
@@ -209,9 +221,11 @@ Formatting:
209
  except Exception as e:
210
  return f"(Gemini error fallback) {str(e)[:160]}"
211
  # fallback (no API key)
212
- return f"(Simulated) {prompt[:500]}..."
 
 
213
 
214
- def generate_ai_insights(student: dict) -> str:
215
  profile = _compose_profile(student or {})
216
  prompt = f"""Analyze the following student profile and provide insights:
217
  {profile}
@@ -233,7 +247,7 @@ Requirements:
233
  # -----------------------------
234
  # Chat logic
235
  # -----------------------------
236
- def chat(student_id: str, message: str) -> tuple[str, str, str]:
237
  """Returns (roadmap_text, insights_text, chatbot_response_text)"""
238
  roadmap, insights, reply = "", "", ""
239
  student = get_student(student_id)
@@ -269,6 +283,7 @@ ALL_FIELDS = [
269
  "preferred_study_environment", "community_groups"
270
  ]
271
 
 
272
  def create_student(
273
  learning_style, academic_progress, personality, interests, goals, level,
274
  preferred_methods, iq_level, eq_level, decision_making_style,
@@ -297,7 +312,8 @@ def create_student(
297
  gr.Dropdown.update(choices=list_student_ids(), value=new_id)
298
  )
299
 
300
- def load_student_to_form(student_id: str):
 
301
  s = get_student(student_id)
302
  if not s:
303
  # return empties
@@ -318,6 +334,7 @@ def load_student_to_form(student_id: str):
318
  ", ".join(s.get("community_groups", [])),
319
  ]
320
 
 
321
  def apply_update(
322
  student_id,
323
  learning_style, academic_progress, personality, interests, goals, level,
@@ -458,6 +475,10 @@ with gr.Blocks(theme=THEME, css="""
458
  fn=load_student_to_form,
459
  inputs=[target_id],
460
  outputs=[u_learning_style, u_academic_progress, u_personality,
 
 
 
 
461
  u_interests, u_goals, u_level, u_preferred_methods,
462
  u_iq_level, u_eq_level, u_decision_style, u_motivation_level,
463
  u_study_env, u_community_groups]
 
1
  # app.py
2
  # ThinkPal โ€“ Hugging Face Space (Gradio)
3
 
4
+ import os
5
+ import json
6
+ import uuid
7
+ import re
8
+ import unicodedata
9
  from difflib import get_close_matches, SequenceMatcher
10
 
11
  import gradio as gr
 
33
  # -----------------------------
34
  # Storage helpers (JSON)
35
  # -----------------------------
36
+ def load_students():
37
  if not os.path.exists(DATA_FILE):
38
  return {}
39
  with open(DATA_FILE, "r", encoding="utf-8") as f:
40
  return json.load(f)
41
 
42
+
43
+ def save_students(data):
44
  with open(DATA_FILE, "w", encoding="utf-8") as f:
45
  json.dump(data, f, indent=2, ensure_ascii=False)
46
 
47
+
48
+ def list_student_ids():
49
  return sorted(load_students().keys())
50
 
51
+
52
+ def get_student(student_id):
53
  return load_students().get(student_id)
54
 
55
+
56
+ def add_student(data):
57
  students = load_students()
58
  # unique random id (short)
59
  new_id = f"student_{uuid.uuid4().hex[:8]}"
 
61
  save_students(students)
62
  return new_id
63
 
64
+
65
+ def update_student(student_id, updates):
66
  students = load_students()
67
  if student_id not in students:
68
  return f"โŒ {student_id} not found."
 
115
  "community", "mentors", "progress tracking", "insights meaning"
116
  ]
117
 
118
+
119
+ def _normalize(text):
120
+ t = (text or "").lower().strip()
121
  # remove diacritics / normalize Arabic
122
  t = "".join(c for c in unicodedata.normalize("NFD", t) if unicodedata.category(c) != "Mn")
123
  t = re.sub(r"[^\w\s\u0600-\u06FF]", " ", t)
124
  t = re.sub(r"\s+", " ", t)
125
  return t
126
 
127
+
128
+ def find_faq_answer(user_input, cutoff=0.6):
129
+ if not user_input:
130
  return None
131
  ui = _normalize(user_input)
132
 
 
144
  best_q = max(candidates, key=lambda q: SequenceMatcher(None, ui, _normalize(q)).ratio())
145
  if SequenceMatcher(None, ui, _normalize(best_q)).ratio() >= cutoff:
146
  # map a synonym to a reasonable FAQ (heuristic)
 
147
  if any(k in ui for k in ["ู†ุต ุงู„ุทุฑูŠู‚", "ู„ูˆ ูˆู‚ูุช", "stop halfway", "resume"]):
148
+ # key exists in FAQS
149
+ return FAQS.get("ู„ูˆ ูˆู‚ูุช ููŠ ู†ุต ุงู„ุทุฑูŠู‚ุŸ") or FAQS.get("ู„ูˆ ูˆู‚ูุช ููŠ ู†ุต ุงู„ุทุฑูŠู‚") or FAQS.get("What if I stop halfway?")
150
  if best_q in FAQS:
151
  return FAQS[best_q]
152
  return None
 
175
  4) Milestones (by Phase)
176
  """
177
 
178
+
179
+ def _compose_profile(student):
180
  mapping = [
181
  ("learning_style", "Learning Style"),
182
  ("academic_progress", "Academic Progress"),
 
194
  ]
195
  parts = []
196
  for key, label in mapping:
197
+ val = student.get(key) if student else None
198
  if isinstance(val, list):
199
  val = ", ".join(val)
200
  if val:
201
  parts.append(f"{label}: {val}")
202
  return " | ".join(parts) if parts else "No student data provided."
203
 
204
+
205
+ def get_gemini_response(query, student=None):
206
  profile = _compose_profile(student or {})
207
  prompt = f"""Student Profile: {profile}
208
 
 
221
  except Exception as e:
222
  return f"(Gemini error fallback) {str(e)[:160]}"
223
  # fallback (no API key)
224
+ # keep simulated short to avoid huge outputs in UI
225
+ return f"(Simulated) {prompt[:400]}..."
226
+
227
 
228
+ def generate_ai_insights(student):
229
  profile = _compose_profile(student or {})
230
  prompt = f"""Analyze the following student profile and provide insights:
231
  {profile}
 
247
  # -----------------------------
248
  # Chat logic
249
  # -----------------------------
250
+ def chat(student_id, message):
251
  """Returns (roadmap_text, insights_text, chatbot_response_text)"""
252
  roadmap, insights, reply = "", "", ""
253
  student = get_student(student_id)
 
283
  "preferred_study_environment", "community_groups"
284
  ]
285
 
286
+
287
  def create_student(
288
  learning_style, academic_progress, personality, interests, goals, level,
289
  preferred_methods, iq_level, eq_level, decision_making_style,
 
312
  gr.Dropdown.update(choices=list_student_ids(), value=new_id)
313
  )
314
 
315
+
316
+ def load_student_to_form(student_id):
317
  s = get_student(student_id)
318
  if not s:
319
  # return empties
 
334
  ", ".join(s.get("community_groups", [])),
335
  ]
336
 
337
+
338
  def apply_update(
339
  student_id,
340
  learning_style, academic_progress, personality, interests, goals, level,
 
475
  fn=load_student_to_form,
476
  inputs=[target_id],
477
  outputs=[u_learning_style, u_academic_progress, u_personality,
478
+ u_interests, u_goals, u_level, u_preferred_methods,
479
+ u_iq_level, u_eq_level, u_decision_style, u_motivation_level,
480
+ u_study_env, u_u_community_groups] if False else
481
+ [u_learning_style, u_academic_progress, u_personality,
482
  u_interests, u_goals, u_level, u_preferred_methods,
483
  u_iq_level, u_eq_level, u_decision_style, u_motivation_level,
484
  u_study_env, u_community_groups]