Tesneem commited on
Commit
0c0ae50
·
verified ·
1 Parent(s): f2c38a3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -25
app.py CHANGED
@@ -270,41 +270,87 @@ def _responses_for_student_stage(uri, db, responses_coll, student: str, stage: s
270
  return [d for d in docs if (d.get("answer") or "").strip()]
271
  except Exception:
272
  return []
273
-
274
  import re
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  def _fix_cutoff_quotes(quotes: list[str], responses: list[dict]) -> list[str]:
277
  """
278
- Replace truncated quotes with full answers from responses_IFE_2025
279
- when the original quote is found as a substring.
 
 
 
280
  """
281
  if not quotes:
282
  return []
283
-
 
 
 
 
 
 
 
 
 
 
 
284
  fulls = []
285
  for q in quotes:
286
- q_clean = (q or "").strip()
287
- if not q_clean:
288
  continue
289
-
290
- # Check if it looks truncated
291
- is_truncated = "..." in q_clean or len(q_clean) < 80 # tweak len threshold if needed
292
-
293
- replaced = False
294
- if is_truncated:
295
- # Normalize for matching
296
- q_norm = re.sub(r"\s+", " ", q_clean.lower()).strip(" .")
297
- for r in responses:
298
- ans = (r.get("answer") or "").strip()
299
- ans_norm = re.sub(r"\s+", " ", ans.lower()).strip(" .")
300
- if q_norm in ans_norm:
301
- fulls.append(ans) # use the full original answer
302
- replaced = True
303
- break
304
-
305
- if not replaced:
306
- fulls.append(q_clean) # fallback to given quote
307
-
 
 
 
 
 
 
 
 
 
308
  return fulls
309
 
310
  def _top3_answers_by_skill_sum(responses: list[dict]) -> list[str]:
 
270
  return [d for d in docs if (d.get("answer") or "").strip()]
271
  except Exception:
272
  return []
 
273
  import re
274
 
275
+ def _answer_total_score(resp: dict) -> float:
276
+ skills = resp.get("skills") or {}
277
+ total = 0.0
278
+ for v in skills.values():
279
+ try:
280
+ total += float(v)
281
+ except Exception:
282
+ pass
283
+ return total
284
+
285
+ def _norm_text(s: str) -> str:
286
+ # lower, collapse whitespace, strip surrounding punctuation/dots
287
+ return re.sub(r"\s+", " ", (s or "").lower()).strip(" .,\"'`”’“‘-–—()[]{}")
288
+
289
+ def _fragments_in_order(answer_norm: str, frags_norm: list[str]) -> bool:
290
+ """Return True if all fragments appear in order anywhere in the answer."""
291
+ start = 0
292
+ for frag in frags_norm:
293
+ idx = answer_norm.find(frag, start)
294
+ if idx == -1:
295
+ return False
296
+ start = idx + len(frag)
297
+ return True
298
+
299
  def _fix_cutoff_quotes(quotes: list[str], responses: list[dict]) -> list[str]:
300
  """
301
+ Replace truncated quotes with full answers when possible.
302
+ Works for:
303
+ - '...' ellipsis in the middle (checks fragments in order, anywhere)
304
+ - plain middle fragments (substring match)
305
+ If multiple matches, pick the response with highest total skill score.
306
  """
307
  if not quotes:
308
  return []
309
+ # Precompute normalized answers + scores
310
+ norm_answers = []
311
+ for r in responses:
312
+ ans = (r.get("answer") or "").strip()
313
+ if not ans:
314
+ continue
315
+ norm_answers.append((
316
+ _norm_text(ans),
317
+ ans,
318
+ _answer_total_score(r)
319
+ ))
320
+
321
  fulls = []
322
  for q in quotes:
323
+ q_raw = (q or "").strip()
324
+ if not q_raw:
325
  continue
326
+
327
+ q_norm = _norm_text(q_raw)
328
+ candidates = []
329
+
330
+ if "..." in q_raw:
331
+ # split on ellipses, keep non-empty normalized fragments
332
+ parts = [p.strip() for p in re.split(r"\.\.\.|…", q_raw)]
333
+ parts_norm = [_norm_text(p) for p in parts if _norm_text(p)]
334
+ if parts_norm:
335
+ for ans_norm, ans_full, score in norm_answers:
336
+ if _fragments_in_order(ans_norm, parts_norm):
337
+ candidates.append((score, ans_full))
338
+ else:
339
+ # plain substring anywhere in the answer
340
+ for ans_norm, ans_full, score in norm_answers:
341
+ if q_norm and q_norm in ans_norm:
342
+ candidates.append((score, ans_full))
343
+
344
+ if candidates:
345
+ # pick highest scoring answer
346
+ candidates.sort(key=lambda x: x[0], reverse=True)
347
+ fulls.append(candidates[0][1])
348
+ else:
349
+ # no match found → keep original
350
+ fulls.append(q_raw)
351
+
352
+ return fulls
353
+
354
  return fulls
355
 
356
  def _top3_answers_by_skill_sum(responses: list[dict]) -> list[str]: