kappai commited on
Commit
60285d9
Β·
verified Β·
1 Parent(s): 0ad30f3

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +60 -17
  2. style.css +13 -1
app.py CHANGED
@@ -261,34 +261,64 @@ FEWSHOTS = {
261
  # REPOSITORY HELPERS (questions only, per category)
262
 
263
 
264
- def _default_repo() -> Dict[str, List[str]]:
265
- return {c["key"]: [] for c in CATEGORIES}
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
 
268
- def load_repo() -> Dict[str, List[str]]:
269
  os.makedirs(os.path.dirname(REPO_PATH), exist_ok=True)
270
  if not os.path.exists(REPO_PATH):
271
  data = _default_repo()
272
  with open(REPO_PATH, "w", encoding="utf-8") as f:
273
  json.dump(data, f, ensure_ascii=False, indent=2)
274
  return data
 
275
  try:
276
  with open(REPO_PATH, "r", encoding="utf-8") as f:
277
  data = json.load(f)
278
  except Exception:
279
  data = _default_repo()
280
- # Ensure all categories exist
 
 
 
 
 
 
 
 
 
 
 
281
  base = _default_repo()
282
- base.update({k: v for k, v in data.items() if isinstance(v, list)})
 
 
 
 
 
283
  return base
284
 
285
 
286
- def save_repo(data: Dict[str, List[str]]) -> None:
287
  os.makedirs(os.path.dirname(REPO_PATH), exist_ok=True)
288
  with open(REPO_PATH, "w", encoding="utf-8") as f:
289
  json.dump(data, f, ensure_ascii=False, indent=2)
290
 
291
 
 
292
  # ────────────────────────────────────────────────────────────────────────────────
293
  # PROMPT + MODEL HELPERS
294
 
@@ -581,18 +611,15 @@ def ai_generate(lang: str, category_key: str, theme: str) -> Dict[str, Any]:
581
  def get_questions_and_micro(
582
  lang: str,
583
  category_key: str,
584
- theme: str,
585
  seen: List[str],
586
  ) -> Dict[str, Any]:
587
- """
588
- 1. Load /data/questions.json
589
- 2. If repo has >=4 unseen questions -> sample 4 from repo, micro from local pool.
590
- 3. Else -> call AI, store any new questions into repo, use AI's questions + micro.
591
- 4. Update seen list so this session won't see the same question twice.
592
- """
593
  seen_set = set(seen or [])
594
  repo = load_repo()
595
- repo_qs = repo.get(category_key, [])
 
 
596
 
597
  unseen_repo = [q for q in repo_qs if q and q not in seen_set]
598
 
@@ -618,13 +645,15 @@ def get_questions_and_micro(
618
  safety_notes = ai_out.get("safety_notes", "")
619
  used_ai = True
620
 
621
- # store new questions in repo
622
  new_qs = [q for q in questions if q and q not in repo_qs]
623
  if new_qs:
624
- repo_qs_extended = repo_qs + new_qs
625
- repo[category_key] = repo_qs_extended
 
626
  save_repo(repo)
627
 
 
628
  # update seen for this session
629
  for q in questions:
630
  if q:
@@ -782,6 +811,10 @@ def get_ui_texts(lang: str) -> Dict[str, Any]:
782
  "category_default": "alimentation 🍎",
783
  "theme_choices": theme_choices,
784
  "theme_default": THEME_LABELS["fr"]["family"],
 
 
 
 
785
  }
786
  else:
787
  header = """
@@ -814,6 +847,10 @@ def get_ui_texts(lang: str) -> Dict[str, Any]:
814
  "category_default": "Nutrition 🍎",
815
  "theme_choices": theme_choices,
816
  "theme_default": THEME_LABELS["en"]["family"],
 
 
 
 
817
  }
818
 
819
 
@@ -902,11 +939,16 @@ with gr.Blocks(title="Neurovie – Question Studio") as demo:
902
  q2 = gr.HTML()
903
  q3 = gr.HTML()
904
  q4 = gr.HTML()
 
905
  with gr.Column():
906
  micro_label_html = gr.HTML(f"<div class='nv-label nv-fade'>{ui_texts['micro_label']}</div>")
907
  with gr.Column(elem_classes="nv-card-grid"):
908
  m1 = gr.HTML()
909
  m2 = gr.HTML()
 
 
 
 
910
 
911
  # Update labels & category choices when language changes
912
  lang.change(
@@ -922,6 +964,7 @@ with gr.Blocks(title="Neurovie – Question Studio") as demo:
922
  theme,
923
  category,
924
  btn,
 
925
  ],
926
  show_progress=False
927
  )
 
261
  # REPOSITORY HELPERS (questions only, per category)
262
 
263
 
264
+ # ────────────────────────────────────────────────────────────────────────────────
265
+ # REPOSITORY HELPERS (questions only, per language + category)
266
+
267
+
268
+ def _default_repo() -> Dict[str, Dict[str, List[str]]]:
269
+ """
270
+ Structure:
271
+ {
272
+ "fr": {"alimentation": [...], "mouvement": [...], ...},
273
+ "en": {"alimentation": [...], "mouvement": [...], ...}
274
+ }
275
+ """
276
+ base_per_lang = {c["key"]: [] for c in CATEGORIES}
277
+ return {"fr": dict(base_per_lang), "en": dict(base_per_lang)}
278
 
279
 
280
+ def load_repo() -> Dict[str, Dict[str, List[str]]]:
281
  os.makedirs(os.path.dirname(REPO_PATH), exist_ok=True)
282
  if not os.path.exists(REPO_PATH):
283
  data = _default_repo()
284
  with open(REPO_PATH, "w", encoding="utf-8") as f:
285
  json.dump(data, f, ensure_ascii=False, indent=2)
286
  return data
287
+
288
  try:
289
  with open(REPO_PATH, "r", encoding="utf-8") as f:
290
  data = json.load(f)
291
  except Exception:
292
  data = _default_repo()
293
+ with open(REPO_PATH, "w", encoding="utf-8") as f:
294
+ json.dump(data, f, ensure_ascii=False, indent=2)
295
+ return data
296
+
297
+ # If this is the old format (categories at top level), reset to new format.
298
+ if not isinstance(data, dict) or "fr" not in data or "en" not in data:
299
+ data = _default_repo()
300
+ with open(REPO_PATH, "w", encoding="utf-8") as f:
301
+ json.dump(data, f, ensure_ascii=False, indent=2)
302
+ return data
303
+
304
+ # Ensure both languages + all categories exist
305
  base = _default_repo()
306
+ for lang in ("fr", "en"):
307
+ src = data.get(lang, {})
308
+ if isinstance(src, dict):
309
+ for k, v in src.items():
310
+ if k in base[lang] and isinstance(v, list):
311
+ base[lang][k] = v
312
  return base
313
 
314
 
315
+ def save_repo(data: Dict[str, Dict[str, List[str]]]) -> None:
316
  os.makedirs(os.path.dirname(REPO_PATH), exist_ok=True)
317
  with open(REPO_PATH, "w", encoding="utf-8") as f:
318
  json.dump(data, f, ensure_ascii=False, indent=2)
319
 
320
 
321
+
322
  # ────────────────────────────────────────────────────────────────────────────────
323
  # PROMPT + MODEL HELPERS
324
 
 
611
  def get_questions_and_micro(
612
  lang: str,
613
  category_key: str,
614
+ variant: str,
615
  seen: List[str],
616
  ) -> Dict[str, Any]:
617
+ ...
 
 
 
 
 
618
  seen_set = set(seen or [])
619
  repo = load_repo()
620
+ lang_repo = repo.get(lang, {})
621
+ repo_qs = lang_repo.get(category_key, [])
622
+
623
 
624
  unseen_repo = [q for q in repo_qs if q and q not in seen_set]
625
 
 
645
  safety_notes = ai_out.get("safety_notes", "")
646
  used_ai = True
647
 
648
+ # store new questions in the repo for this language only
649
  new_qs = [q for q in questions if q and q not in repo_qs]
650
  if new_qs:
651
+ updated = repo_qs + new_qs
652
+ repo.setdefault(lang, {})
653
+ repo[lang][category_key] = updated
654
  save_repo(repo)
655
 
656
+
657
  # update seen for this session
658
  for q in questions:
659
  if q:
 
811
  "category_default": "alimentation 🍎",
812
  "theme_choices": theme_choices,
813
  "theme_default": THEME_LABELS["fr"]["family"],
814
+ "note_text": (
815
+ "Note : les cartes s’estompent doucement avec le temps, "
816
+ "pour symboliser la mΓ©moire qui s’efface."
817
+ ),
818
  }
819
  else:
820
  header = """
 
847
  "category_default": "Nutrition 🍎",
848
  "theme_choices": theme_choices,
849
  "theme_default": THEME_LABELS["en"]["family"],
850
+ "note_text": (
851
+ "Note: the cards gently fade over time, "
852
+ "to echo how memories can fade."
853
+ ),
854
  }
855
 
856
 
 
939
  q2 = gr.HTML()
940
  q3 = gr.HTML()
941
  q4 = gr.HTML()
942
+
943
  with gr.Column():
944
  micro_label_html = gr.HTML(f"<div class='nv-label nv-fade'>{ui_texts['micro_label']}</div>")
945
  with gr.Column(elem_classes="nv-card-grid"):
946
  m1 = gr.HTML()
947
  m2 = gr.HTML()
948
+ # Small explanatory note under the cards
949
+ note_html = gr.HTML(
950
+ f"<div class='nv-note'>{ui_texts['note_text']}</div>"
951
+ )
952
 
953
  # Update labels & category choices when language changes
954
  lang.change(
 
964
  theme,
965
  category,
966
  btn,
967
+ note_html,
968
  ],
969
  show_progress=False
970
  )
style.css CHANGED
@@ -115,6 +115,18 @@
115
  margin-bottom: 18px;
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  /* ─────────────────────────────────────────────────────────────
119
  SECTIONS + LABELS
120
  ───────────────────────────────────────────────────────────── */
@@ -283,7 +295,7 @@
283
  transform: translateY(0) scale(1);
284
  animation:
285
  nvCardDeal 420ms ease-out forwards,
286
- nvCardSlowFade 90s linear forwards;
287
  cursor: default;
288
  transition:
289
  box-shadow 180ms ease,
 
115
  margin-bottom: 18px;
116
  }
117
 
118
+ .nv-note {
119
+ margin-top: 10px;
120
+ font-size: 0.78rem;
121
+ font-weight: 400;
122
+ letter-spacing: 0.03em;
123
+ text-transform: none;
124
+ color: rgba(15, 23, 42, 0.55);
125
+ text-align: center;
126
+ font-style: italic;
127
+ }
128
+
129
+
130
  /* ─────────────────────────────────────────────────────────────
131
  SECTIONS + LABELS
132
  ───────────────────────────────────────────────────────────── */
 
295
  transform: translateY(0) scale(1);
296
  animation:
297
  nvCardDeal 420ms ease-out forwards,
298
+ nvCardSlowFade 50s linear forwards;
299
  cursor: default;
300
  transition:
301
  box-shadow 180ms ease,