2121 load_questions ,
2222)
2323from core .list_builder import build_school_list
24+ from core .lr_predictor import predict_prob , get_model_stats
2425from core .prerequisite_matcher import match_prerequisites
2526from core .profile_evaluator import evaluate as evaluate_profile
2627from core .roi_calculator import calculate_roi
@@ -41,12 +42,13 @@ def cmd_evaluate(args: argparse.Namespace) -> None:
4142 """Evaluate a user profile against MFE programs."""
4243 profile = load_profile (args .profile )
4344 programs = load_all_programs ()
44- result = evaluate_profile (profile )
45+ projected = getattr (args , "projected" , False )
46+ result = evaluate_profile (profile , projected = projected )
4547
4648 # PDF output path requested
4749 output_path = getattr (args , "output" , None )
4850 if output_path and output_path .endswith (".pdf" ):
49- rankings = rank_schools (profile , programs , result )
51+ rankings = rank_schools (profile , programs , result , projected = projected )
5052 gap_recs = analyze_gaps (result .gaps ) if result .gaps else []
5153
5254 from core .report_generator import generate_report
@@ -61,12 +63,14 @@ def cmd_evaluate(args: argparse.Namespace) -> None:
6163 console .print (f"[green]PDF report saved to:[/green] { path } " )
6264 return
6365
66+ mode_label = " [bold yellow](Projected — including planned courses)[/bold yellow]" if projected else ""
6467 # Header
6568 console .print ()
6669 console .print (
6770 Panel (
6871 f"[bold]{ profile .name } [/bold] | { profile .university } | "
69- f"GPA { profile .gpa } | { 'International' if profile .is_international else 'Domestic' } " ,
72+ f"GPA { profile .gpa } | { 'International' if profile .is_international else 'Domestic' } "
73+ + mode_label ,
7074 title = "QuantPath Profile Evaluation" ,
7175 border_style = "cyan" ,
7276 )
@@ -116,20 +120,71 @@ def cmd_evaluate(args: argparse.Namespace) -> None:
116120 )
117121 console .print ()
118122
119- # School recommendations
120- rankings = rank_schools (profile , programs , result )
123+ # School recommendations (use projected mode if flag set)
124+ rankings = rank_schools (profile , programs , result , projected = projected )
121125
122126 if rankings .get ("reach" ):
123127 reach_names = ", " .join (r ["name" ] for r in rankings ["reach" ])
124- console .print (f" 🎯 [bold] Reach:[/bold] { reach_names } " )
128+ console .print (f" Reach: { reach_names } " )
125129 if rankings .get ("target" ):
126130 target_names = ", " .join (r ["name" ] for r in rankings ["target" ])
127- console .print (f" 🎯 [bold] Target:[/bold] { target_names } " )
131+ console .print (f" Target: { target_names } " )
128132 if rankings .get ("safety" ):
129133 safety_names = ", " .join (r ["name" ] for r in rankings ["safety" ])
130- console .print (f" 🎯 [bold] Safety:[/bold] { safety_names } " )
134+ console .print (f" Safety: { safety_names } " )
131135 console .print ()
132136
137+ # If projected mode, show comparison: current vs projected
138+ if projected and profile .planned_coursework :
139+ current_result = evaluate_profile (profile , projected = False )
140+ curr_overall = current_result .overall_score
141+ proj_overall = result .overall_score
142+ diff = proj_overall - curr_overall
143+ diff_color = "green" if diff > 0 else "red"
144+
145+ compare_table = Table (
146+ title = "[bold]Current vs Projected Profile[/bold]" ,
147+ border_style = "yellow" ,
148+ show_lines = True ,
149+ )
150+ compare_table .add_column ("Dimension" , style = "bold" , width = 16 )
151+ compare_table .add_column ("Current" , justify = "right" , width = 10 )
152+ compare_table .add_column ("Projected" , justify = "right" , width = 10 )
153+ compare_table .add_column ("Change" , justify = "right" , width = 10 )
154+
155+ dim_labels_compare = {
156+ "math" : "Math" ,
157+ "statistics" : "Statistics" ,
158+ "cs" : "CS" ,
159+ "finance_econ" : "Finance/Econ" ,
160+ "gpa" : "GPA" ,
161+ }
162+ for dim_id , label in dim_labels_compare .items ():
163+ curr_s = current_result .dimension_scores .get (dim_id , 0 )
164+ proj_s = result .dimension_scores .get (dim_id , 0 )
165+ delta = proj_s - curr_s
166+ d_color = "green" if delta > 0.1 else "dim" if abs (delta ) <= 0.1 else "red"
167+ compare_table .add_row (
168+ label ,
169+ f"{ curr_s :.1f} " ,
170+ f"[bold]{ proj_s :.1f} [/bold]" ,
171+ f"[{ d_color } ]{ delta :+.1f} [/{ d_color } ]" ,
172+ )
173+ compare_table .add_row (
174+ "[bold]OVERALL[/bold]" ,
175+ f"{ curr_overall :.1f} " ,
176+ f"[bold]{ proj_overall :.1f} [/bold]" ,
177+ f"[{ diff_color } ]{ diff :+.1f} [/{ diff_color } ]" ,
178+ )
179+ console .print (compare_table )
180+ n_planned = len (profile .planned_coursework )
181+ console .print (
182+ f" [dim]({ n_planned } planned courses included: "
183+ + ", " .join (c .code for c in profile .planned_coursework [:5 ])
184+ + ("..." if n_planned > 5 else "" ) + ")[/dim]"
185+ )
186+ console .print ()
187+
133188 # Gaps
134189 if result .gaps :
135190 console .print (" [bold red]⚠️ Gaps Found:[/bold red]" )
@@ -563,9 +618,14 @@ def cmd_list(args: argparse.Namespace) -> None:
563618 """Build and display an optimised school application list."""
564619 profile = load_profile (args .profile )
565620 programs = load_all_programs ()
566- evaluation = evaluate_profile (profile )
621+ projected = getattr (args , "projected" , False )
622+ evaluation = evaluate_profile (profile , projected = projected )
567623 school_list = build_school_list (profile , programs , evaluation )
568624
625+ # Determine GRE Quant score for LR prediction
626+ gre_quant = profile .test_scores .gre_quant
627+ gpa = profile .gpa
628+
569629 console .print ()
570630 console .print (
571631 Panel (
@@ -591,14 +651,22 @@ def cmd_list(args: argparse.Namespace) -> None:
591651 table .add_column ("University" , min_width = 18 )
592652 table .add_column ("Fit" , justify = "right" , width = 6 )
593653 table .add_column ("Prereq" , justify = "right" , width = 7 )
594- table .add_column ("Reason" , min_width = 30 )
654+ table .add_column ("P(Admit)" , justify = "right" , width = 9 )
655+ table .add_column ("Reason" , min_width = 28 )
595656
596657 for e in entries :
658+ prob = predict_prob (e .program_id , gpa , gre_quant )
659+ if prob is not None :
660+ pcolor = "green" if prob >= 0.6 else "yellow" if prob >= 0.35 else "red"
661+ prob_str = f"[{ pcolor } ]{ prob :.0%} [/{ pcolor } ]"
662+ else :
663+ prob_str = "[dim]N/A[/dim]"
597664 table .add_row (
598665 e .name ,
599666 e .university ,
600667 f"{ e .fit_score :.1f} " ,
601668 f"{ e .prereq_match_score :.0%} " ,
669+ prob_str ,
602670 e .reason ,
603671 )
604672 console .print (table )
@@ -1018,6 +1086,11 @@ def main() -> None:
10181086 "-o" ,
10191087 help = "Output file path (use .pdf extension for PDF report)" ,
10201088 )
1089+ p_eval .add_argument (
1090+ "--projected" ,
1091+ action = "store_true" ,
1092+ help = "Include planned_courses from profile YAML (shows profile at application time)" ,
1093+ )
10211094
10221095 # match
10231096 p_match = subparsers .add_parser ("match" , help = "Match prerequisites" )
@@ -1114,6 +1187,11 @@ def main() -> None:
11141187 # list
11151188 p_list = subparsers .add_parser ("list" , help = "Build optimised school application list" )
11161189 p_list .add_argument ("--profile" , "-p" , required = True , help = "Path to profile YAML" )
1190+ p_list .add_argument (
1191+ "--projected" ,
1192+ action = "store_true" ,
1193+ help = "Include planned courses (shows school list at application time)" ,
1194+ )
11171195
11181196 args = parser .parse_args ()
11191197
0 commit comments