import gradio as gr import pandas as pd import numpy as np from course_recommender import CourseRecommender from database_connection import DatabaseConnection import os # Global variables to store current recommendations current_recommendations = [] current_user_input = {} # Initialize components with error handling recommender = None db_connection = None def initialize_components(): """Initialize the recommender system and database connection with error handling""" global recommender, db_connection try: if recommender is None: recommender = CourseRecommender() print("✅ CourseRecommender initialized") except Exception as e: print(f"⚠️ Warning: Could not initialize CourseRecommender: {e}") # Create a minimal fallback recommender = None try: if db_connection is None: db_connection = DatabaseConnection() print("✅ DatabaseConnection initialized") except Exception as e: print(f"⚠️ Warning: Could not initialize DatabaseConnection: {e}") # Create a minimal fallback db_connection = None # Initialize components initialize_components() def get_course_recommendations(stanine, gwa, strand, hobbies): """Get course recommendations based on user input""" global current_recommendations, current_user_input, recommender # Check if recommender is initialized if recommender is None: return "❌ System not properly initialized. Please try again.", "", "", "", "", "" # Validate inputs if not stanine or not gwa or not strand or not hobbies: return "Please fill in all fields", "", "", "", "", "" try: stanine = int(stanine) gwa = float(gwa) if stanine < 1 or stanine > 9: return "Stanine must be between 1-9", "", "", "", "", "" if gwa < 75 or gwa > 100: return "GWA must be between 75-100", "", "", "", "", "" # Normalize strand to uppercase for case-insensitive matching strand = strand.upper() if strand not in ["STEM", "ABM", "HUMSS", "GAS", "TVL"]: return "Strand must be one of: STEM, ABM, HUMSS, GAS, TVL", "", "", "", "", "" # Store current user input current_user_input = { 'stanine': stanine, 'gwa': gwa, 'strand': strand, 'hobbies': hobbies } # Get recommendations recommendations = recommender.predict_course(stanine, gwa, strand, hobbies) current_recommendations = recommendations # Format top 3 recommendations if len(recommendations) >= 3: course1 = f"{recommendations[0][0]} (Confidence: {recommendations[0][1]*100:.1f}%)" course2 = f"{recommendations[1][0]} (Confidence: {recommendations[1][1]*100:.1f}%)" course3 = f"{recommendations[2][0]} (Confidence: {recommendations[2][1]*100:.1f}%)" elif len(recommendations) == 2: course1 = f"{recommendations[0][0]} (Confidence: {recommendations[0][1]*100:.1f}%)" course2 = f"{recommendations[1][0]} (Confidence: {recommendations[1][1]*100:.1f}%)" course3 = "No third recommendation available" elif len(recommendations) == 1: course1 = f"{recommendations[0][0]} (Confidence: {recommendations[0][1]*100:.1f}%)" course2 = "No second recommendation available" course3 = "No third recommendation available" else: course1 = "No recommendations available" course2 = "" course3 = "" return course1, course2, course3, None, None, None except ValueError as e: return f"Invalid input: {str(e)}", "", "", None, None, None except Exception as e: return f"Error getting recommendations: {str(e)}", "", "", None, None, None def submit_all_ratings(course1_rating, course2_rating, course3_rating): """Submit ratings for all three recommendations""" global current_recommendations, current_user_input, recommender if recommender is None: return "❌ System not properly initialized. Please try again." if not current_recommendations or not current_user_input: return "No recommendations to rate. Please get recommendations first." try: results = [] ratings_submitted = 0 # Rate first recommendation if course1_rating and len(current_recommendations) >= 1: rating_value = "like" if course1_rating == "👍 Like" else "dislike" course = current_recommendations[0][0] success = recommender.add_feedback_with_learning( course=course, stanine=current_user_input['stanine'], gwa=current_user_input['gwa'], strand=current_user_input['strand'], rating=rating_value, hobbies=current_user_input['hobbies'] ) if success: results.append(f"✅ Rating for '{course}' recorded") ratings_submitted += 1 else: results.append(f"❌ Failed to record rating for '{course}'") # Rate second recommendation if course2_rating and len(current_recommendations) >= 2: rating_value = "like" if course2_rating == "👍 Like" else "dislike" course = current_recommendations[1][0] success = recommender.add_feedback_with_learning( course=course, stanine=current_user_input['stanine'], gwa=current_user_input['gwa'], strand=current_user_input['strand'], rating=rating_value, hobbies=current_user_input['hobbies'] ) if success: results.append(f"✅ Rating for '{course}' recorded") ratings_submitted += 1 else: results.append(f"❌ Failed to record rating for '{course}'") # Rate third recommendation if course3_rating and len(current_recommendations) >= 3: rating_value = "like" if course3_rating == "👍 Like" else "dislike" course = current_recommendations[2][0] success = recommender.add_feedback_with_learning( course=course, stanine=current_user_input['stanine'], gwa=current_user_input['gwa'], strand=current_user_input['strand'], rating=rating_value, hobbies=current_user_input['hobbies'] ) if success: results.append(f"✅ Rating for '{course}' recorded") ratings_submitted += 1 else: results.append(f"❌ Failed to record rating for '{course}'") if ratings_submitted > 0: return f"Thank you! {ratings_submitted} rating(s) submitted successfully.\n\n" + "\n".join(results) else: return "Please select at least one rating before submitting." except Exception as e: return f"Error recording feedback: {str(e)}" def train_model(): """Train the model with current data""" global recommender if recommender is None: return "❌ System not properly initialized. Please try again." try: accuracy = recommender.train_model(use_database=True) return f"✅ Model trained successfully! Accuracy: {accuracy:.3f}" except Exception as e: return f"❌ Error training model: {str(e)}" def get_available_courses_info(): """Get information about available courses from database""" global db_connection if db_connection is None: return "❌ Database connection not available. Please try again." try: courses = db_connection.get_available_courses() if courses: return f"📚 Available courses in database: {len(courses)}\n\n" + "\n".join([f"• {course}" for course in courses[:10]]) + (f"\n... and {len(courses)-10} more" if len(courses) > 10 else "") else: return "📚 No courses found in database. Please check the /courses endpoint." except Exception as e: return f"❌ Error fetching courses: {str(e)}" # Create Gradio interface def create_interface(): with gr.Blocks(title="Course AI Recommender", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 🎓 Course AI Machine Learning Recommender Get personalized course recommendations based on your academic profile and interests! """) with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📝 Your Profile") stanine_input = gr.Textbox( label="Stanine Score (1-9)", placeholder="Enter your stanine score (1-9)", info="Your stanine score from standardized tests" ) gwa_input = gr.Textbox( label="GWA (75-100)", placeholder="Enter your GWA (75-100)", info="Your Grade Weighted Average" ) strand_input = gr.Dropdown( choices=["STEM", "ABM", "HUMSS", "GAS", "TVL"], value="STEM", label="Academic Strand", info="Your current academic strand" ) hobbies_input = gr.Textbox( label="Hobbies & Interests", placeholder="e.g., Programming, Reading, Sports, Music", info="List your hobbies and interests (comma-separated)" ) get_recommendations_btn = gr.Button("🎯 Get Recommendations", variant="primary") train_model_btn = gr.Button("🤖 Train Model", variant="secondary") show_courses_btn = gr.Button("📚 Show Available Courses", variant="secondary") with gr.Column(scale=1): gr.Markdown("### 🎓 Top 3 Course Recommendations") # Display top 3 recommendations course1_output = gr.Textbox( label="1st Recommendation", interactive=False ) course1_rating = gr.Radio( choices=["👍 Like", "👎 Dislike"], label="Rate 1st Recommendation", interactive=True ) course2_output = gr.Textbox( label="2nd Recommendation", interactive=False ) course2_rating = gr.Radio( choices=["👍 Like", "👎 Dislike"], label="Rate 2nd Recommendation", interactive=True ) course3_output = gr.Textbox( label="3rd Recommendation", interactive=False ) course3_rating = gr.Radio( choices=["👍 Like", "👎 Dislike"], label="Rate 3rd Recommendation", interactive=True ) submit_ratings_btn = gr.Button("Submit All Ratings", variant="primary") rating_feedback = gr.Textbox( label="Rating Feedback", interactive=False ) courses_info = gr.Textbox( label="Available Courses", lines=8, interactive=False ) # Event handlers get_recommendations_btn.click( fn=get_course_recommendations, inputs=[stanine_input, gwa_input, strand_input, hobbies_input], outputs=[course1_output, course2_output, course3_output, course1_rating, course2_rating, course3_rating] ) submit_ratings_btn.click( fn=submit_all_ratings, inputs=[course1_rating, course2_rating, course3_rating], outputs=[rating_feedback] ) train_model_btn.click( fn=train_model, outputs=[gr.Textbox(label="Training Status", interactive=False)] ) show_courses_btn.click( fn=get_available_courses_info, outputs=[courses_info] ) # Add some example inputs gr.Markdown(""" ### 💡 Example Inputs **For STEM students:** - Stanine: 7-9, GWA: 85-95, Strand: STEM, Hobbies: Programming, Mathematics, Science **For ABM students:** - Stanine: 6-8, GWA: 80-90, Strand: ABM, Hobbies: Business, Leadership, Economics **For HUMSS students:** - Stanine: 5-8, GWA: 78-88, Strand: HUMSS, Hobbies: Literature, History, Writing """) return demo # Initialize the interface if __name__ == "__main__": # Try to load existing model if recommender is available if recommender is not None: try: recommender.load_model() print("✅ Loaded existing model") except: print("⚠️ No existing model found. Training with basic data...") try: recommender.train_model(use_database=False) print("✅ Model trained with basic data") except Exception as e: print(f"❌ Error training model: {e}") else: print("⚠️ Recommender not initialized. App will run with limited functionality.") # Create and launch interface demo = create_interface() demo.launch( server_name="0.0.0.0", server_port=7860, share=True )