Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -297,80 +297,107 @@ class TranscriptParser:
|
|
297 |
def parse_transcript(self, text: str) -> Dict:
|
298 |
"""Parse Miami-Dade formatted transcripts with updated regex patterns."""
|
299 |
try:
|
300 |
-
#
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
|
|
|
|
|
|
|
|
|
|
305 |
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
"weighted_gpa": float(student_match.group(6)),
|
314 |
-
"total_credits": float(student_match.group(7)),
|
315 |
-
"community_service_hours": int(student_match.group(8))
|
316 |
-
}
|
317 |
|
318 |
-
|
319 |
-
self.
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
code = req_match.group(1).strip()
|
330 |
-
self.requirements[code] = {
|
331 |
-
"description": req_match.group(2).strip(),
|
332 |
-
"required": float(req_match.group(3)),
|
333 |
-
"waived": float(req_match.group(4)),
|
334 |
-
"completed": float(req_match.group(5)),
|
335 |
-
"status": f"{req_match.group(6)}%"
|
336 |
-
}
|
337 |
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
|
|
|
|
|
|
|
|
357 |
|
358 |
-
|
359 |
-
|
360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
361 |
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
"current_courses": self.current_courses,
|
366 |
-
"course_history": self.course_history,
|
367 |
-
"graduation_status": self.graduation_status,
|
368 |
-
"format": "miami_dade"
|
369 |
-
}
|
370 |
|
371 |
-
|
372 |
-
|
373 |
-
|
|
|
|
|
|
|
|
|
|
|
374 |
|
375 |
def _extract_current_courses(self):
|
376 |
"""Identify in-progress courses."""
|
@@ -383,7 +410,8 @@ class TranscriptParser:
|
|
383 |
"credits": c["credits"],
|
384 |
"grade_level": c["grade_level"]
|
385 |
}
|
386 |
-
for c in self.course_history
|
|
|
387 |
]
|
388 |
|
389 |
def _calculate_completion(self):
|
@@ -421,8 +449,6 @@ def format_transcript_output(data: Dict) -> str:
|
|
421 |
output.append(f"**Total Credits Earned:** {student['total_credits']}")
|
422 |
if 'community_service_hours' in student:
|
423 |
output.append(f"**Community Service Hours:** {student['community_service_hours']}")
|
424 |
-
if 'parent' in student:
|
425 |
-
output.append(f"**Parent/Guardian:** {student['parent']}")
|
426 |
|
427 |
output.append("")
|
428 |
|
@@ -461,8 +487,8 @@ def format_transcript_output(data: Dict) -> str:
|
|
461 |
# Course History by Year
|
462 |
courses_by_year = defaultdict(list)
|
463 |
for course in data.get("course_history", []):
|
464 |
-
|
465 |
-
|
466 |
|
467 |
if courses_by_year:
|
468 |
output.append("## Course History\n" + '='*50)
|
@@ -471,7 +497,7 @@ def format_transcript_output(data: Dict) -> str:
|
|
471 |
for course in courses_by_year[year]:
|
472 |
output.append(
|
473 |
f"- **{course.get('course_code', '')} {course.get('description', 'Unnamed course')}**\n"
|
474 |
-
f" Subject: {course.get('
|
475 |
f"Grade: {course.get('grade', 'N/A')} | "
|
476 |
f"Credits: {course.get('credits', 'N/A')}"
|
477 |
)
|
@@ -500,26 +526,63 @@ def parse_transcript_with_ai(text: str, progress=gr.Progress()) -> Dict:
|
|
500 |
return parse_transcript_with_ai_fallback(text, progress)
|
501 |
|
502 |
def parse_transcript_with_ai_fallback(text: str, progress=gr.Progress()) -> Dict:
|
503 |
-
"""Fallback AI parsing method"""
|
504 |
# Pre-process the text
|
505 |
text = remove_sensitive_info(text[:15000]) # Limit input size
|
506 |
|
507 |
prompt = f"""
|
508 |
-
Analyze this academic transcript and extract structured information:
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
523 |
|
524 |
Transcript Text:
|
525 |
{text}
|
@@ -534,7 +597,7 @@ def parse_transcript_with_ai_fallback(text: str, progress=gr.Progress()) -> Dict
|
|
534 |
raise gr.Error(f"Model failed to load. {model_loader.error or 'Please try loading a model first.'}")
|
535 |
|
536 |
# Tokenize and generate response
|
537 |
-
inputs = tokenizer(prompt, return_tensors="pt").to(model_loader.device)
|
538 |
if progress:
|
539 |
progress(0.4)
|
540 |
|
@@ -542,7 +605,9 @@ def parse_transcript_with_ai_fallback(text: str, progress=gr.Progress()) -> Dict
|
|
542 |
**inputs,
|
543 |
max_new_tokens=2000,
|
544 |
temperature=0.1,
|
545 |
-
do_sample=True
|
|
|
|
|
546 |
)
|
547 |
if progress:
|
548 |
progress(0.8)
|
@@ -555,9 +620,17 @@ def parse_transcript_with_ai_fallback(text: str, progress=gr.Progress()) -> Dict
|
|
555 |
json_str = response.split('```json')[1].split('```')[0].strip()
|
556 |
parsed_data = json.loads(json_str)
|
557 |
except (IndexError, json.JSONDecodeError):
|
558 |
-
# Fallback:
|
559 |
-
|
560 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
561 |
|
562 |
if progress:
|
563 |
progress(1.0)
|
@@ -1003,28 +1076,40 @@ class ProfileManager:
|
|
1003 |
|
1004 |
def _format_transcript(self, transcript: Dict) -> str:
|
1005 |
"""Format transcript data for display."""
|
1006 |
-
if not transcript or "
|
1007 |
return "_No transcript information available_"
|
1008 |
|
1009 |
display = "#### Course History\n"
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
|
|
|
|
|
|
1017 |
if 'grade' in course and course['grade']:
|
1018 |
display += f" (Grade: {course['grade']})"
|
1019 |
if 'credits' in course:
|
1020 |
display += f" | Credits: {course['credits']}"
|
1021 |
-
display += f" |
|
1022 |
-
|
1023 |
-
if '
|
1024 |
-
|
1025 |
-
display += "\n**
|
1026 |
-
display += f"- Unweighted: {
|
1027 |
-
display += f"- Weighted: {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1028 |
|
1029 |
return display
|
1030 |
|
@@ -1051,10 +1136,10 @@ class TeachingAssistant:
|
|
1051 |
# Extract profile information
|
1052 |
name = profile.get("name", "there")
|
1053 |
learning_style = profile.get("learning_style", "")
|
1054 |
-
grade_level = profile.get("transcript", {}).get("
|
1055 |
-
gpa = profile.get("transcript", {}).get("
|
1056 |
interests = profile.get("interests", "")
|
1057 |
-
courses = profile.get("transcript", {}).get("
|
1058 |
favorites = profile.get("favorites", {})
|
1059 |
|
1060 |
# Process message with context
|
@@ -1174,19 +1259,18 @@ class TeachingAssistant:
|
|
1174 |
|
1175 |
def _generate_grade_advice(self, profile: Dict) -> str:
|
1176 |
"""Generate response about grades and GPA."""
|
1177 |
-
gpa = profile.get("transcript", {}).get("
|
1178 |
-
courses = profile.get("transcript", {}).get("
|
1179 |
|
1180 |
response = (f"Your GPA information:\n"
|
1181 |
-
f"- Unweighted: {gpa.get('
|
1182 |
-
f"- Weighted: {gpa.get('
|
1183 |
|
1184 |
# Identify any failing grades
|
1185 |
weak_subjects = []
|
1186 |
-
for
|
1187 |
-
|
1188 |
-
|
1189 |
-
weak_subjects.append(f"{course.get('code', '')} {course.get('name', 'Unknown course')}")
|
1190 |
|
1191 |
if weak_subjects:
|
1192 |
response += ("**Areas for Improvement**:\n"
|
@@ -1215,14 +1299,19 @@ class TeachingAssistant:
|
|
1215 |
|
1216 |
def _generate_course_advice(self, profile: Dict) -> str:
|
1217 |
"""Generate response about courses."""
|
1218 |
-
courses = profile.get("transcript", {}).get("
|
1219 |
-
grade_level = profile.get("transcript", {}).get("
|
1220 |
-
|
1221 |
-
response = "Here's a summary of your courses:\n"
|
1222 |
-
|
1223 |
-
|
1224 |
-
|
1225 |
-
|
|
|
|
|
|
|
|
|
|
|
1226 |
if 'grade' in course:
|
1227 |
response += f" (Grade: {course['grade']})"
|
1228 |
response += "\n"
|
|
|
297 |
def parse_transcript(self, text: str) -> Dict:
|
298 |
"""Parse Miami-Dade formatted transcripts with updated regex patterns."""
|
299 |
try:
|
300 |
+
# First try structured parsing for Miami-Dade format
|
301 |
+
if "Graduation Progress Summary" in text and "Miami-Dade" in text:
|
302 |
+
return self._parse_miami_dade_format(text)
|
303 |
+
else:
|
304 |
+
# Fall back to AI parsing if not Miami-Dade format
|
305 |
+
return parse_transcript_with_ai_fallback(text)
|
306 |
+
|
307 |
+
except Exception as e:
|
308 |
+
logging.error(f"Error parsing transcript: {str(e)}")
|
309 |
+
raise ValueError(f"Couldn't parse transcript: {str(e)}")
|
310 |
|
311 |
+
def _parse_miami_dade_format(self, text: str) -> Dict:
|
312 |
+
"""Specialized parser for Miami-Dade County Public Schools transcripts."""
|
313 |
+
# Extract student info
|
314 |
+
student_match = re.search(
|
315 |
+
r"(\d{7})\s*-\s*([A-Z\s,]+).*?Current Grade:\s*(\d+).*?YOG\s*(\d{4}).*?Un-weighted GPA\s*([\d.]+).*?Weighted GPA\s*([\d.]+).*?Total Credits Earned\s*([\d.]+).*?Comm Serv Hours\s*(\d+)",
|
316 |
+
text, re.DOTALL
|
317 |
+
)
|
|
|
|
|
|
|
|
|
318 |
|
319 |
+
if student_match:
|
320 |
+
self.student_data = {
|
321 |
+
"id": student_match.group(1).strip(),
|
322 |
+
"name": student_match.group(2).replace(",", ", ").strip(),
|
323 |
+
"current_grade": student_match.group(3),
|
324 |
+
"graduation_year": student_match.group(4),
|
325 |
+
"unweighted_gpa": float(student_match.group(5)),
|
326 |
+
"weighted_gpa": float(student_match.group(6)),
|
327 |
+
"total_credits": float(student_match.group(7)),
|
328 |
+
"community_service_hours": int(student_match.group(8))
|
329 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
330 |
|
331 |
+
# Extract requirements
|
332 |
+
self.requirements = {}
|
333 |
+
req_section = re.search(
|
334 |
+
r"Code\s+Description\s+Required\s+Waived\s+Completed\s+Status(.*?)Total\s+\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+\d+%",
|
335 |
+
text, re.DOTALL
|
336 |
+
)
|
337 |
+
if req_section:
|
338 |
+
req_lines = req_section.group(1).strip().split('\n')
|
339 |
+
for line in req_lines:
|
340 |
+
line = line.strip()
|
341 |
+
if not line:
|
342 |
+
continue
|
343 |
+
|
344 |
+
req_match = re.match(r"([A-Z]-[^\s]+)\s+(.+?)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+)%", line)
|
345 |
+
if req_match:
|
346 |
+
code = req_match.group(1).strip()
|
347 |
+
self.requirements[code] = {
|
348 |
+
"description": req_match.group(2).strip(),
|
349 |
+
"required": float(req_match.group(3)),
|
350 |
+
"waived": float(req_match.group(4)),
|
351 |
+
"completed": float(req_match.group(5)),
|
352 |
+
"status": f"{req_match.group(6)}%"
|
353 |
+
}
|
354 |
|
355 |
+
# Extract course history
|
356 |
+
self.course_history = []
|
357 |
+
course_section = re.search(
|
358 |
+
r"Requirement\s+School Year\s+GradeLv1\s+CrsNum\s+Description\s+Term\s+DstNumber\s+FG\s+Incl\s+Credits(.*?)Legend for Incl",
|
359 |
+
text, re.DOTALL
|
360 |
+
)
|
361 |
+
|
362 |
+
if course_section:
|
363 |
+
course_lines = course_section.group(1).strip().split('\n')
|
364 |
+
for line in course_lines:
|
365 |
+
line = line.strip()
|
366 |
+
if not line or line.startswith('='):
|
367 |
+
continue
|
368 |
+
|
369 |
+
# Handle both regular and in-progress courses
|
370 |
+
course_match = re.match(
|
371 |
+
r"([A-Z]-[^\s]+)?\s*(\d{4}-\d{4}|\d{4})?\s*(\d{2})?\s*([A-Z0-9]+)?\s*(.+?)\s+([AT12]+)?\s*([A-Z0-9]+)?\s*([A-Z])?\s*([A-Z])?\s*(inProgress|\d+\.\d+)?",
|
372 |
+
line
|
373 |
+
)
|
374 |
+
|
375 |
+
if course_match:
|
376 |
+
self.course_history.append({
|
377 |
+
"requirement_category": course_match.group(1) if course_match.group(1) else None,
|
378 |
+
"school_year": course_match.group(2) if course_match.group(2) else None,
|
379 |
+
"grade_level": course_match.group(3) if course_match.group(3) else None,
|
380 |
+
"course_code": course_match.group(4) if course_match.group(4) else None,
|
381 |
+
"description": course_match.group(5).strip() if course_match.group(5) else None,
|
382 |
+
"term": course_match.group(6) if course_match.group(6) else None,
|
383 |
+
"district_number": course_match.group(7) if course_match.group(7) else None,
|
384 |
+
"grade": course_match.group(8) if course_match.group(8) else None,
|
385 |
+
"inclusion_status": course_match.group(9) if course_match.group(9) else None,
|
386 |
+
"credits": course_match.group(10) if course_match.group(10) else None
|
387 |
+
})
|
388 |
|
389 |
+
# Extract in-progress courses
|
390 |
+
self._extract_current_courses()
|
391 |
+
self._calculate_completion()
|
|
|
|
|
|
|
|
|
|
|
392 |
|
393 |
+
return {
|
394 |
+
"student_info": self.student_data,
|
395 |
+
"requirements": self.requirements,
|
396 |
+
"current_courses": self.current_courses,
|
397 |
+
"course_history": self.course_history,
|
398 |
+
"graduation_status": self.graduation_status,
|
399 |
+
"format": "miami_dade"
|
400 |
+
}
|
401 |
|
402 |
def _extract_current_courses(self):
|
403 |
"""Identify in-progress courses."""
|
|
|
410 |
"credits": c["credits"],
|
411 |
"grade_level": c["grade_level"]
|
412 |
}
|
413 |
+
for c in self.course_history
|
414 |
+
if c.get("credits") and isinstance(c["credits"], str) and c["credits"].lower() == "inprogress"
|
415 |
]
|
416 |
|
417 |
def _calculate_completion(self):
|
|
|
449 |
output.append(f"**Total Credits Earned:** {student['total_credits']}")
|
450 |
if 'community_service_hours' in student:
|
451 |
output.append(f"**Community Service Hours:** {student['community_service_hours']}")
|
|
|
|
|
452 |
|
453 |
output.append("")
|
454 |
|
|
|
487 |
# Course History by Year
|
488 |
courses_by_year = defaultdict(list)
|
489 |
for course in data.get("course_history", []):
|
490 |
+
if course.get("school_year"):
|
491 |
+
courses_by_year[course["school_year"]].append(course)
|
492 |
|
493 |
if courses_by_year:
|
494 |
output.append("## Course History\n" + '='*50)
|
|
|
497 |
for course in courses_by_year[year]:
|
498 |
output.append(
|
499 |
f"- **{course.get('course_code', '')} {course.get('description', 'Unnamed course')}**\n"
|
500 |
+
f" Subject: {course.get('requirement_category', 'N/A')} | "
|
501 |
f"Grade: {course.get('grade', 'N/A')} | "
|
502 |
f"Credits: {course.get('credits', 'N/A')}"
|
503 |
)
|
|
|
526 |
return parse_transcript_with_ai_fallback(text, progress)
|
527 |
|
528 |
def parse_transcript_with_ai_fallback(text: str, progress=gr.Progress()) -> Dict:
|
529 |
+
"""Fallback AI parsing method with improved prompt engineering"""
|
530 |
# Pre-process the text
|
531 |
text = remove_sensitive_info(text[:15000]) # Limit input size
|
532 |
|
533 |
prompt = f"""
|
534 |
+
Analyze this academic transcript and extract structured information in JSON format. Follow this exact structure:
|
535 |
+
|
536 |
+
{{
|
537 |
+
"student_info": {{
|
538 |
+
"name": "Full Name",
|
539 |
+
"id": "Student ID",
|
540 |
+
"current_grade": "Grade Level",
|
541 |
+
"graduation_year": "Year of Graduation",
|
542 |
+
"unweighted_gpa": 0.0,
|
543 |
+
"weighted_gpa": 0.0,
|
544 |
+
"total_credits": 0.0,
|
545 |
+
"community_service_hours": 0
|
546 |
+
}},
|
547 |
+
"requirements": {{
|
548 |
+
"A-English": {{
|
549 |
+
"description": "English requirement description",
|
550 |
+
"required": 4.0,
|
551 |
+
"completed": 4.0,
|
552 |
+
"status": "100%"
|
553 |
+
}}
|
554 |
+
}},
|
555 |
+
"current_courses": [
|
556 |
+
{{
|
557 |
+
"course": "Course Name",
|
558 |
+
"code": "Course Code",
|
559 |
+
"category": "Requirement Category",
|
560 |
+
"term": "Term",
|
561 |
+
"credits": "inProgress or credit value",
|
562 |
+
"grade_level": "Grade Level"
|
563 |
+
}}
|
564 |
+
],
|
565 |
+
"course_history": [
|
566 |
+
{{
|
567 |
+
"requirement_category": "Category Code",
|
568 |
+
"school_year": "Year Taken",
|
569 |
+
"grade_level": "Grade Level",
|
570 |
+
"course_code": "Course Code",
|
571 |
+
"description": "Course Description",
|
572 |
+
"term": "Term",
|
573 |
+
"grade": "Grade Received",
|
574 |
+
"credits": "Credits Earned"
|
575 |
+
}}
|
576 |
+
],
|
577 |
+
"graduation_status": {{
|
578 |
+
"total_required_credits": 24.0,
|
579 |
+
"total_completed_credits": 24.0,
|
580 |
+
"percent_complete": 100.0,
|
581 |
+
"remaining_credits": 0.0,
|
582 |
+
"on_track": true
|
583 |
+
}},
|
584 |
+
"format": "miami_dade or standard"
|
585 |
+
}}
|
586 |
|
587 |
Transcript Text:
|
588 |
{text}
|
|
|
597 |
raise gr.Error(f"Model failed to load. {model_loader.error or 'Please try loading a model first.'}")
|
598 |
|
599 |
# Tokenize and generate response
|
600 |
+
inputs = tokenizer(prompt, return_tensors="pt", max_length=4096, truncation=True).to(model_loader.device)
|
601 |
if progress:
|
602 |
progress(0.4)
|
603 |
|
|
|
605 |
**inputs,
|
606 |
max_new_tokens=2000,
|
607 |
temperature=0.1,
|
608 |
+
do_sample=True,
|
609 |
+
top_p=0.9,
|
610 |
+
repetition_penalty=1.1
|
611 |
)
|
612 |
if progress:
|
613 |
progress(0.8)
|
|
|
620 |
json_str = response.split('```json')[1].split('```')[0].strip()
|
621 |
parsed_data = json.loads(json_str)
|
622 |
except (IndexError, json.JSONDecodeError):
|
623 |
+
# Fallback: Try to find JSON in the response
|
624 |
+
json_match = re.search(r'\{.*\}', response, re.DOTALL)
|
625 |
+
if json_match:
|
626 |
+
parsed_data = json.loads(json_match.group())
|
627 |
+
else:
|
628 |
+
raise ValueError("Could not extract JSON from AI response")
|
629 |
+
|
630 |
+
# Validate the parsed data structure
|
631 |
+
required_keys = ["student_info", "requirements", "course_history"]
|
632 |
+
if not all(key in parsed_data for key in required_keys):
|
633 |
+
raise ValueError("AI returned incomplete data structure")
|
634 |
|
635 |
if progress:
|
636 |
progress(1.0)
|
|
|
1076 |
|
1077 |
def _format_transcript(self, transcript: Dict) -> str:
|
1078 |
"""Format transcript data for display."""
|
1079 |
+
if not transcript or "course_history" not in transcript:
|
1080 |
return "_No transcript information available_"
|
1081 |
|
1082 |
display = "#### Course History\n"
|
1083 |
+
courses_by_year = defaultdict(list)
|
1084 |
+
for course in transcript.get("course_history", []):
|
1085 |
+
if course.get("school_year"):
|
1086 |
+
courses_by_year[course["school_year"]].append(course)
|
1087 |
+
|
1088 |
+
if courses_by_year:
|
1089 |
+
for year in sorted(courses_by_year.keys()):
|
1090 |
+
display += f"\n**{year}**\n"
|
1091 |
+
for course in courses_by_year[year]:
|
1092 |
+
display += f"- {course.get('course_code', '')} {course.get('description', 'Unnamed course')}"
|
1093 |
if 'grade' in course and course['grade']:
|
1094 |
display += f" (Grade: {course['grade']})"
|
1095 |
if 'credits' in course:
|
1096 |
display += f" | Credits: {course['credits']}"
|
1097 |
+
display += f" | Category: {course.get('requirement_category', 'N/A')}\n"
|
1098 |
+
|
1099 |
+
if 'student_info' in transcript:
|
1100 |
+
student = transcript['student_info']
|
1101 |
+
display += "\n**Academic Summary**\n"
|
1102 |
+
display += f"- Unweighted GPA: {student.get('unweighted_gpa', 'N/A')}\n"
|
1103 |
+
display += f"- Weighted GPA: {student.get('weighted_gpa', 'N/A')}\n"
|
1104 |
+
display += f"- Total Credits: {student.get('total_credits', 'N/A')}\n"
|
1105 |
+
|
1106 |
+
if 'graduation_status' in transcript:
|
1107 |
+
status = transcript['graduation_status']
|
1108 |
+
display += "\n**Graduation Progress**\n"
|
1109 |
+
display += f"- Completion: {status.get('percent_complete', 0)}%\n"
|
1110 |
+
display += f"- Credits Required: {status.get('total_required_credits', 0)}\n"
|
1111 |
+
display += f"- Credits Completed: {status.get('total_completed_credits', 0)}\n"
|
1112 |
+
display += f"- On Track: {'Yes' if status.get('on_track', False) else 'No'}\n"
|
1113 |
|
1114 |
return display
|
1115 |
|
|
|
1136 |
# Extract profile information
|
1137 |
name = profile.get("name", "there")
|
1138 |
learning_style = profile.get("learning_style", "")
|
1139 |
+
grade_level = profile.get("transcript", {}).get("student_info", {}).get("current_grade", "unknown")
|
1140 |
+
gpa = profile.get("transcript", {}).get("student_info", {})
|
1141 |
interests = profile.get("interests", "")
|
1142 |
+
courses = profile.get("transcript", {}).get("course_history", [])
|
1143 |
favorites = profile.get("favorites", {})
|
1144 |
|
1145 |
# Process message with context
|
|
|
1259 |
|
1260 |
def _generate_grade_advice(self, profile: Dict) -> str:
|
1261 |
"""Generate response about grades and GPA."""
|
1262 |
+
gpa = profile.get("transcript", {}).get("student_info", {})
|
1263 |
+
courses = profile.get("transcript", {}).get("course_history", [])
|
1264 |
|
1265 |
response = (f"Your GPA information:\n"
|
1266 |
+
f"- Unweighted: {gpa.get('unweighted_gpa', 'N/A')}\n"
|
1267 |
+
f"- Weighted: {gpa.get('weighted_gpa', 'N/A')}\n\n")
|
1268 |
|
1269 |
# Identify any failing grades
|
1270 |
weak_subjects = []
|
1271 |
+
for course in courses:
|
1272 |
+
if course.get('grade', '').upper() in ['D', 'F']:
|
1273 |
+
weak_subjects.append(f"{course.get('course_code', '')} {course.get('description', 'Unknown course')}")
|
|
|
1274 |
|
1275 |
if weak_subjects:
|
1276 |
response += ("**Areas for Improvement**:\n"
|
|
|
1299 |
|
1300 |
def _generate_course_advice(self, profile: Dict) -> str:
|
1301 |
"""Generate response about courses."""
|
1302 |
+
courses = profile.get("transcript", {}).get("course_history", [])
|
1303 |
+
grade_level = profile.get("transcript", {}).get("student_info", {}).get("current_grade", "unknown")
|
1304 |
+
|
1305 |
+
response = "Here's a summary of your courses by year:\n"
|
1306 |
+
courses_by_year = defaultdict(list)
|
1307 |
+
for course in courses:
|
1308 |
+
if course.get("school_year"):
|
1309 |
+
courses_by_year[course["school_year"]].append(course)
|
1310 |
+
|
1311 |
+
for year in sorted(courses_by_year.keys()):
|
1312 |
+
response += f"\n**{year}**:\n"
|
1313 |
+
for course in courses_by_year[year]:
|
1314 |
+
response += f"- {course.get('course_code', '')} {course.get('description', 'Unnamed course')}"
|
1315 |
if 'grade' in course:
|
1316 |
response += f" (Grade: {course['grade']})"
|
1317 |
response += "\n"
|