Spaces:
Sleeping
Sleeping
Update src/core/QuizEngine.py
Browse files- src/core/QuizEngine.py +51 -39
src/core/QuizEngine.py
CHANGED
|
@@ -25,58 +25,70 @@ class QuizEngine:
|
|
| 25 |
}
|
| 26 |
|
| 27 |
# --- MODE 2: DOCUMENTS (NEW) ---
|
|
|
|
|
|
|
| 28 |
def get_document_context(self, username):
|
| 29 |
-
"""
|
| 30 |
-
Picks a random file, reads it, and selects a random segment.
|
| 31 |
-
Returns the segment to be used as the 'Answer Key'.
|
| 32 |
-
"""
|
| 33 |
user_dir = os.path.join(self.source_dir, username)
|
| 34 |
-
if not os.path.exists(user_dir):
|
| 35 |
-
return None
|
| 36 |
|
| 37 |
-
# 1. Get
|
| 38 |
files = [f for f in os.listdir(user_dir) if f.lower().endswith(('.txt', '.md'))]
|
| 39 |
-
if not files:
|
| 40 |
-
return None
|
| 41 |
|
| 42 |
-
#
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
|
| 51 |
-
# Filter out short/empty paragraphs to avoid bad questions
|
| 52 |
-
paragraphs = [p.strip() for p in text.split('\n\n') if len(p.strip()) > 200]
|
| 53 |
-
|
| 54 |
-
if not paragraphs:
|
| 55 |
-
return None
|
| 56 |
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
| 69 |
|
| 70 |
-
# --- PROMPT CONSTRUCTORS ---
|
| 71 |
def construct_question_generation_prompt(self, context_text):
|
| 72 |
-
"""
|
|
|
|
|
|
|
| 73 |
return (
|
| 74 |
f"Act as a US Navy Engineering Duty Officer Board Examiner.\n"
|
| 75 |
-
f"
|
| 76 |
-
f"'{context_text}'\n\n"
|
| 77 |
-
f"
|
| 78 |
-
f"
|
| 79 |
-
f"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
)
|
| 81 |
|
| 82 |
def construct_grading_prompt(self, question, answer, context_text):
|
|
|
|
| 25 |
}
|
| 26 |
|
| 27 |
# --- MODE 2: DOCUMENTS (NEW) ---
|
| 28 |
+
# ... inside QuizEngine class ...
|
| 29 |
+
|
| 30 |
def get_document_context(self, username):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
user_dir = os.path.join(self.source_dir, username)
|
| 32 |
+
if not os.path.exists(user_dir): return None
|
|
|
|
| 33 |
|
| 34 |
+
# 1. Get files
|
| 35 |
files = [f for f in os.listdir(user_dir) if f.lower().endswith(('.txt', '.md'))]
|
| 36 |
+
if not files: return None
|
|
|
|
| 37 |
|
| 38 |
+
# Retry Loop: Try up to 5 times to find a "worthy" chunk
|
| 39 |
+
for attempt in range(5):
|
| 40 |
+
selected_file = random.choice(files)
|
| 41 |
+
try:
|
| 42 |
+
with open(os.path.join(user_dir, selected_file), 'r', encoding='utf-8', errors='ignore') as f:
|
| 43 |
+
text = f.read()
|
| 44 |
|
| 45 |
+
# DEFENSE 1: Aggressive Heuristic Filtering
|
| 46 |
+
# Split by double newline (paragraphs)
|
| 47 |
+
paragraphs = text.split('\n\n')
|
| 48 |
+
candidates = []
|
| 49 |
+
for p in paragraphs:
|
| 50 |
+
p = p.strip()
|
| 51 |
+
# A. Too short?
|
| 52 |
+
if len(p) < 250: continue
|
| 53 |
+
# B. Looks like a list item or table row? (starts with number/bullet)
|
| 54 |
+
if p[0].isdigit() or p.startswith(('-', '*', '•')): continue
|
| 55 |
+
# C. Looks like administrative noise?
|
| 56 |
+
if "intentionally left blank" in p.lower(): continue
|
| 57 |
+
|
| 58 |
+
candidates.append(p)
|
| 59 |
|
| 60 |
+
if not candidates: continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
+
# Pick a random survivor
|
| 63 |
+
selected_context = random.choice(candidates)
|
| 64 |
+
|
| 65 |
+
return {
|
| 66 |
+
"type": "document",
|
| 67 |
+
"source_file": selected_file,
|
| 68 |
+
"context_text": selected_context
|
| 69 |
+
}
|
| 70 |
|
| 71 |
+
except Exception as e:
|
| 72 |
+
self.logger.error(f"Error fetching context: {e}")
|
| 73 |
+
continue
|
| 74 |
+
|
| 75 |
+
return None # Failed to find good text after 5 tries
|
| 76 |
|
|
|
|
| 77 |
def construct_question_generation_prompt(self, context_text):
|
| 78 |
+
"""
|
| 79 |
+
DEFENSE 2: Explicit Instructions to Ignore Trivia
|
| 80 |
+
"""
|
| 81 |
return (
|
| 82 |
f"Act as a US Navy Engineering Duty Officer Board Examiner.\n"
|
| 83 |
+
f"Review the following source text for suitability:\n"
|
| 84 |
+
f"'''{context_text}'''\n\n"
|
| 85 |
+
f"DECISION LOGIC:\n"
|
| 86 |
+
f"1. Does this text contain a specific Engineering Concept, Responsibility, or Procedural Rule?\n"
|
| 87 |
+
f"2. Is it free of pure administrative trivia (dates, page numbers, formatting rules)?\n\n"
|
| 88 |
+
f"IF NO: Output the word 'SKIP'.\n"
|
| 89 |
+
f"IF YES: Generate a difficult, scenario-based question that tests the candidate's understanding of the concept. "
|
| 90 |
+
f"Do not ask 'What does the text say?'. Ask 'How would you apply...?' or 'What are the requirements for...?'\n\n"
|
| 91 |
+
f"OUTPUT: Just the question text."
|
| 92 |
)
|
| 93 |
|
| 94 |
def construct_grading_prompt(self, question, answer, context_text):
|