markobinario commited on
Commit
db51021
·
verified ·
1 Parent(s): 1067ffe

Upload 4 files

Browse files
Files changed (4) hide show
  1. ai_chatbot.py +100 -0
  2. app_hf.py +240 -0
  3. database_recommender.py +293 -0
  4. requirements.txt +7 -0
ai_chatbot.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sentence_transformers import SentenceTransformer
2
+ import numpy as np
3
+ from typing import List, Dict, Tuple
4
+ import re
5
+
6
+ class AIChatbot:
7
+ def __init__(self):
8
+ # Load the pre-trained model (can use a smaller model for more speed)
9
+ self.model = SentenceTransformer('all-MiniLM-L6-v2')
10
+ # Warm up the model to avoid first-request slowness
11
+ _ = self.model.encode(["Hello, world!"])
12
+ self.faq_embeddings = None
13
+ self.faqs = None
14
+ self.load_faqs()
15
+
16
+ def load_faqs(self):
17
+ """Disable FAQs entirely; operate as a general-conversation bot."""
18
+ self.faqs = []
19
+ self.faq_embeddings = None
20
+
21
+ def save_unanswered_question(self, question):
22
+ """Log unanswered questions to console (can be extended to save to file)"""
23
+ print(f"Unanswered question logged: {question}")
24
+ # In a real implementation, you could save this to a file or send to an admin
25
+
26
+ def _tokenize(self, text: str):
27
+ if not text:
28
+ return []
29
+ return [t for t in re.findall(r"[a-z0-9]+", text.lower()) if len(t) > 2]
30
+
31
+ def _overlap_ratio(self, q_tokens, faq_tokens):
32
+ if not q_tokens or not faq_tokens:
33
+ return 0.0
34
+ q_set = set(q_tokens)
35
+ f_set = set(faq_tokens)
36
+ inter = len(q_set & f_set)
37
+ denom = max(len(q_set), 1)
38
+ return inter / denom
39
+
40
+ def _wh_class(self, text: str) -> str:
41
+ if not text:
42
+ return ''
43
+ s = text.strip().lower()
44
+ # simple heuristic classification by leading wh-word
45
+ for key in ['who', 'where', 'when', 'what', 'how', 'why', 'which']:
46
+ if s.startswith(key + ' ') or s.startswith(key + "?"):
47
+ return key
48
+ # also check presence if not leading
49
+ for key in ['who', 'where', 'when', 'what', 'how', 'why', 'which']:
50
+ if f' {key} ' in f' {s} ':
51
+ return key
52
+ return ''
53
+
54
+ def find_best_match(self, question: str, threshold: float = 0.7) -> Tuple[str, float]:
55
+ print(f"find_best_match called with: {question}") # Debug print
56
+ # Always act as a general-conversation bot
57
+ return self._generate_general_response(question)
58
+
59
+ def _generate_general_response(self, question: str) -> Tuple[str, float]:
60
+ """Generate general conversation responses for non-FAQ questions"""
61
+ question_lower = question.lower().strip()
62
+
63
+ # Greeting responses
64
+ if any(greeting in question_lower for greeting in ['hello', 'hi', 'hey', 'good morning', 'good afternoon', 'good evening']):
65
+ return "Hello! I'm the PSAU AI assistant. I'm here to help you with questions about university admissions, courses, and general information about Pangasinan State University. How can I assist you today?", 0.8
66
+
67
+ # Thank you responses
68
+ if any(thanks in question_lower for thanks in ['thank you', 'thanks', 'thank', 'appreciate']):
69
+ return "You're very welcome! I'm happy to help. Is there anything else you'd like to know about PSAU or university admissions?", 0.9
70
+
71
+ # Goodbye responses
72
+ if any(goodbye in question_lower for goodbye in ['bye', 'goodbye', 'see you', 'farewell']):
73
+ return "Goodbye! It was nice chatting with you. Feel free to come back anytime if you have more questions about PSAU. Good luck with your academic journey!", 0.9
74
+
75
+ # How are you responses
76
+ if any(how in question_lower for how in ['how are you', 'how do you do', 'how is it going']):
77
+ return "I'm doing great, thank you for asking! I'm here and ready to help you with any questions about PSAU admissions, courses, or university life. What would you like to know?", 0.8
78
+
79
+ # What can you do responses
80
+ if any(what in question_lower for what in ['what can you do', 'what do you do', 'what are your capabilities']):
81
+ return "I can help you with:\n• University admission requirements and procedures\n• Course information and recommendations\n• General questions about PSAU\n• Academic guidance and support\n• Information about campus life\n\nWhat specific information are you looking for?", 0.9
82
+
83
+ # About PSAU responses
84
+ if any(about in question_lower for about in ['about psa', 'about psu', 'about pangasinan state', 'tell me about']):
85
+ return "Pangasinan State University (PSAU) is a premier state university in the Philippines offering quality education across various fields. We provide undergraduate and graduate programs in areas like Computer Science, Business, Education, Nursing, and more. We're committed to academic excellence and student success. What would you like to know more about?", 0.8
86
+
87
+ # Help responses
88
+ if any(help in question_lower for help in ['help', 'assist', 'support']):
89
+ return "I'm here to help! I can assist you with:\n• Admission requirements and deadlines\n• Course information and recommendations\n• Academic programs and majors\n• Campus facilities and services\n• General university information\n\nJust ask me any question and I'll do my best to help you!", 0.9
90
+
91
+ # Default general response
92
+ return "I understand you're asking about something, but I'm specifically designed to help with PSAU-related questions like admissions, courses, and university information. Could you rephrase your question to be more specific about what you'd like to know about Pangasinan State University? I'm here to help with academic guidance and university-related inquiries!", 0.6
93
+
94
+ def get_suggested_questions(self, question: str, num_suggestions: int = 3) -> List[str]:
95
+ """No suggestions when FAQs are disabled."""
96
+ return []
97
+
98
+ def add_faq(self, question: str, answer: str) -> bool:
99
+ """No-op when FAQs are disabled."""
100
+ return False
app_hf.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import pandas as pd
4
+ import numpy as np
5
+ from ai_chatbot import AIChatbot
6
+ from database_recommender import CourseRecommender
7
+ import warnings
8
+ import logging
9
+
10
+ # Suppress warnings
11
+ warnings.filterwarnings('ignore')
12
+ logging.getLogger('tensorflow').setLevel(logging.ERROR)
13
+
14
+ # Initialize components
15
+ try:
16
+ chatbot = AIChatbot()
17
+ print("✅ Chatbot initialized successfully")
18
+ except Exception as e:
19
+ print(f"⚠️ Warning: Could not initialize chatbot: {e}")
20
+ chatbot = None
21
+
22
+ try:
23
+ recommender = CourseRecommender()
24
+ print("✅ Recommender initialized successfully")
25
+ except Exception as e:
26
+ print(f"⚠️ Warning: Could not initialize recommender: {e}")
27
+ recommender = None
28
+
29
+ def chat_with_bot(message, history):
30
+ """Handle chatbot interactions"""
31
+ if chatbot is None:
32
+ return "Sorry, the chatbot is not available at the moment. Please try again later."
33
+
34
+ if not message.strip():
35
+ return "Please enter a message to start the conversation."
36
+
37
+ # Get answer from chatbot
38
+ answer, confidence = chatbot.find_best_match(message)
39
+
40
+ # For general conversation, just return the answer
41
+ # For FAQ questions, include suggested questions
42
+ if confidence > 0.7: # High confidence FAQ match
43
+ suggested_questions = chatbot.get_suggested_questions(message)
44
+ if suggested_questions:
45
+ response = f"{answer}\n\n**Related Questions:**\n"
46
+ for i, q in enumerate(suggested_questions, 1):
47
+ response += f"{i}. {q}\n"
48
+ return response
49
+
50
+ # For general conversation or low confidence, just return the answer
51
+ return answer
52
+
53
+ def get_course_recommendations(stanine, gwa, strand, hobbies):
54
+ """Get course recommendations"""
55
+ if recommender is None:
56
+ return "Sorry, the recommendation system is not available at the moment. Please try again later."
57
+
58
+ try:
59
+ # Validate and convert inputs
60
+ try:
61
+ stanine = int(stanine.strip()) if stanine else 0
62
+ except (ValueError, AttributeError):
63
+ return "❌ Stanine score must be a valid number between 1 and 9"
64
+
65
+ try:
66
+ gwa = float(gwa.strip()) if gwa else 0
67
+ except (ValueError, AttributeError):
68
+ return "❌ GWA must be a valid number between 75 and 100"
69
+
70
+ # Validate ranges
71
+ if not (1 <= stanine <= 9):
72
+ return "❌ Stanine score must be between 1 and 9"
73
+
74
+ if not (75 <= gwa <= 100):
75
+ return "❌ GWA must be between 75 and 100"
76
+
77
+ if not strand:
78
+ return "❌ Please select a strand"
79
+
80
+ if not hobbies or not hobbies.strip():
81
+ return "❌ Please enter your hobbies/interests"
82
+
83
+ # Get recommendations
84
+ recommendations = recommender.recommend_courses(
85
+ stanine=stanine,
86
+ gwa=gwa,
87
+ strand=strand,
88
+ hobbies=hobbies
89
+ )
90
+
91
+ if not recommendations:
92
+ return "No recommendations available at the moment."
93
+
94
+ # Format recommendations
95
+ response = f"## 🎯 Course Recommendations for You\n\n"
96
+ response += f"**Profile:** Stanine {stanine}, GWA {gwa}, {strand} Strand\n"
97
+ response += f"**Interests:** {hobbies}\n\n"
98
+
99
+ for i, rec in enumerate(recommendations, 1):
100
+ response += f"### {i}. {rec['code']} - {rec['name']}\n"
101
+ response += f"**Match Score:** {rec.get('rating', rec.get('probability', 0)):.1f}%\n\n"
102
+
103
+ return response
104
+
105
+ except Exception as e:
106
+ return f"❌ Error getting recommendations: {str(e)}"
107
+
108
+ def get_faqs():
109
+ """Get available FAQs"""
110
+ if chatbot and chatbot.faqs:
111
+ faq_text = "## 📚 Frequently Asked Questions\n\n"
112
+ for i, faq in enumerate(chatbot.faqs, 1):
113
+ faq_text += f"**{i}. {faq['question']}**\n"
114
+ faq_text += f"{faq['answer']}\n\n"
115
+ return faq_text
116
+ return "No FAQs available at the moment."
117
+
118
+ def get_available_courses():
119
+ """Get available courses"""
120
+ if recommender and recommender.courses:
121
+ course_text = "## 🎓 Available Courses\n\n"
122
+ for code, name in recommender.courses.items():
123
+ course_text += f"**{code}** - {name}\n"
124
+ return course_text
125
+ return "No courses available at the moment."
126
+
127
+ # Create Gradio interface
128
+ with gr.Blocks(title="PSAU AI Chatbot & Course Recommender", theme=gr.themes.Soft()) as demo:
129
+ gr.Markdown(
130
+ """
131
+ # 🤖 PSAU AI Chatbot & Course Recommender
132
+
133
+ Welcome to the Pangasinan State University AI-powered admission assistant!
134
+ Get instant answers to your questions and receive personalized course recommendations.
135
+ """
136
+ )
137
+
138
+ with gr.Tabs():
139
+ # Chatbot Tab
140
+ with gr.Tab("🤖 AI Chatbot"):
141
+ gr.Markdown("""
142
+ **Chat with the PSAU AI Assistant!**
143
+
144
+ I can help you with:
145
+ • University admission questions
146
+ • Course information and guidance
147
+ • General conversation
148
+ • Academic support
149
+
150
+ Just type your message below and I'll respond naturally!
151
+ """)
152
+
153
+ chatbot_interface = gr.ChatInterface(
154
+ fn=chat_with_bot,
155
+ title="PSAU AI Assistant",
156
+ description="Chat with me about university admissions, courses, or just say hello!",
157
+ examples=[
158
+ "Hello!",
159
+ "What are the admission requirements?",
160
+ "How are you?",
161
+ "What courses are available?",
162
+ "Tell me about PSAU",
163
+ "What can you help me with?",
164
+ "Thank you",
165
+ "Goodbye"
166
+ ],
167
+ cache_examples=True
168
+ )
169
+
170
+ # Course Recommender Tab
171
+ with gr.Tab("🎯 Course Recommender"):
172
+ gr.Markdown("""
173
+ Get personalized course recommendations based on your academic profile and interests!
174
+
175
+ **Input Guidelines:**
176
+ - **Stanine Score**: Enter a number between 1-9 (from your entrance exam)
177
+ - **GWA**: Enter your General Weighted Average (75-100)
178
+ - **Strand**: Select your senior high school strand
179
+ - **Hobbies**: Describe your interests and hobbies in detail
180
+ """)
181
+
182
+ with gr.Row():
183
+ with gr.Column():
184
+ stanine_input = gr.Textbox(
185
+ label="Stanine Score (1-9)",
186
+ placeholder="Enter your stanine score (1-9)",
187
+ info="Your stanine score from entrance examination",
188
+ value="7"
189
+ )
190
+ gwa_input = gr.Textbox(
191
+ label="GWA (75-100)",
192
+ placeholder="Enter your GWA (75-100)",
193
+ info="Your General Weighted Average",
194
+ value="85.0"
195
+ )
196
+ strand_input = gr.Dropdown(
197
+ choices=["STEM", "ABM", "HUMSS"],
198
+ value="STEM",
199
+ label="High School Strand",
200
+ info="Your senior high school strand"
201
+ )
202
+ hobbies_input = gr.Textbox(
203
+ label="Hobbies & Interests",
204
+ placeholder="e.g., programming, gaming, business, teaching, healthcare...",
205
+ info="Describe your interests and hobbies"
206
+ )
207
+
208
+ recommend_btn = gr.Button("Get Recommendations", variant="primary")
209
+
210
+ with gr.Column():
211
+ recommendations_output = gr.Markdown()
212
+
213
+ recommend_btn.click(
214
+ fn=get_course_recommendations,
215
+ inputs=[stanine_input, gwa_input, strand_input, hobbies_input],
216
+ outputs=recommendations_output
217
+ )
218
+
219
+ # Information Tab
220
+ with gr.Tab("📚 Information"):
221
+ with gr.Row():
222
+ with gr.Column():
223
+ gr.Markdown("### FAQ Section")
224
+ faq_btn = gr.Button("Show FAQs")
225
+ faq_output = gr.Markdown()
226
+ faq_btn.click(fn=get_faqs, outputs=faq_output)
227
+
228
+ with gr.Column():
229
+ gr.Markdown("### Available Courses")
230
+ courses_btn = gr.Button("Show Courses")
231
+ courses_output = gr.Markdown()
232
+ courses_btn.click(fn=get_available_courses, outputs=courses_output)
233
+
234
+ if __name__ == "__main__":
235
+ demo.launch(
236
+ server_name="0.0.0.0",
237
+ server_port=7860,
238
+ share=False,
239
+ show_error=True
240
+ )
database_recommender.py ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from sklearn.neighbors import KNeighborsClassifier
4
+ from sklearn.preprocessing import LabelEncoder, StandardScaler
5
+ import joblib
6
+ import json
7
+
8
+ class CourseRecommender:
9
+ def __init__(self):
10
+ self.model = None
11
+ self.label_encoders = {}
12
+ self.scaler = StandardScaler()
13
+ self.courses = self.get_courses()
14
+ self.training_data = self.get_training_data()
15
+ self.train_model()
16
+
17
+ def get_courses(self):
18
+ """Get static course data"""
19
+ return {
20
+ 'BSCS': 'Bachelor of Science in Computer Science',
21
+ 'BSIT': 'Bachelor of Science in Information Technology',
22
+ 'BSBA': 'Bachelor of Science in Business Administration',
23
+ 'BSED': 'Bachelor of Science in Education',
24
+ 'BSN': 'Bachelor of Science in Nursing',
25
+ 'BSArch': 'Bachelor of Science in Architecture',
26
+ 'BSIE': 'Bachelor of Science in Industrial Engineering',
27
+ 'BSHM': 'Bachelor of Science in Hospitality Management',
28
+ 'BSA': 'Bachelor of Science in Accountancy',
29
+ 'BSPsych': 'Bachelor of Science in Psychology',
30
+ 'BSAgri': 'Bachelor of Science in Agriculture'
31
+ }
32
+
33
+ def save_student_data(self, stanine, gwa, strand, course, rating, hobbies=None):
34
+ """Save student feedback to in-memory storage (for demonstration purposes)"""
35
+ try:
36
+ # In a real implementation, you could save this to a file or external storage
37
+ print(f"Student feedback saved: Stanine={stanine}, GWA={gwa}, Strand={strand}, Course={course}, Rating={rating}, Hobbies={hobbies}")
38
+ return True
39
+ except Exception as e:
40
+ print(f"Error saving student feedback: {e}")
41
+ return False
42
+
43
+ def get_training_data(self):
44
+ """Get static training data for demonstration purposes"""
45
+ # Sample training data to demonstrate the recommender system
46
+ training_data = [
47
+ # STEM students
48
+ (8, 95, 'STEM', 'BSCS', 5, 'programming, gaming, technology'),
49
+ (7, 90, 'STEM', 'BSIT', 4, 'computers, software, coding'),
50
+ (9, 98, 'STEM', 'BSCS', 5, 'programming, algorithms, math'),
51
+ (6, 85, 'STEM', 'BSIT', 3, 'technology, computers'),
52
+ (8, 92, 'STEM', 'BSArch', 4, 'design, drawing, creativity'),
53
+ (7, 88, 'STEM', 'BSIE', 4, 'engineering, problem solving'),
54
+
55
+ # ABM students
56
+ (8, 90, 'ABM', 'BSBA', 5, 'business, management, leadership'),
57
+ (7, 85, 'ABM', 'BSA', 4, 'accounting, numbers, finance'),
58
+ (6, 82, 'ABM', 'BSBA', 3, 'business, marketing'),
59
+ (9, 95, 'ABM', 'BSA', 5, 'accounting, finance, analysis'),
60
+
61
+ # HUMSS students
62
+ (8, 88, 'HUMSS', 'BSED', 5, 'teaching, helping, education'),
63
+ (7, 85, 'HUMSS', 'BSPsych', 4, 'psychology, helping, people'),
64
+ (6, 80, 'HUMSS', 'BSED', 3, 'teaching, children'),
65
+ (9, 92, 'HUMSS', 'BSPsych', 5, 'psychology, counseling, people'),
66
+
67
+ # General interests
68
+ (7, 87, 'STEM', 'BSN', 4, 'helping, healthcare, caring'),
69
+ (8, 89, 'ABM', 'BSHM', 4, 'hospitality, service, management'),
70
+ (6, 83, 'HUMSS', 'BSAgri', 3, 'agriculture, environment, nature'),
71
+ ]
72
+
73
+ return pd.DataFrame(training_data, columns=['stanine', 'gwa', 'strand', 'course', 'rating', 'hobbies'])
74
+
75
+ def train_model(self):
76
+ """Train the recommendation model using the training data"""
77
+ try:
78
+ training_data = self.get_training_data()
79
+
80
+ if training_data.empty:
81
+ print("No training data available - using default recommendations")
82
+ return
83
+
84
+ # Prepare features (hobbies required)
85
+ feature_columns = ['stanine', 'gwa', 'strand', 'hobbies']
86
+
87
+ # Create feature matrix
88
+ X = training_data[feature_columns].copy()
89
+ y = training_data['course']
90
+
91
+ # Handle categorical variables
92
+ categorical_columns = ['strand', 'hobbies']
93
+
94
+ # Refit encoders every training to incorporate new categories
95
+ for col in categorical_columns:
96
+ if col in X.columns:
97
+ X[col] = X[col].fillna('unknown')
98
+ self.label_encoders[col] = LabelEncoder()
99
+ X[col] = self.label_encoders[col].fit_transform(X[col])
100
+
101
+ # Scale numerical features
102
+ numerical_columns = ['stanine', 'gwa']
103
+ if not X[numerical_columns].empty:
104
+ X[numerical_columns] = self.scaler.fit_transform(X[numerical_columns])
105
+
106
+ # Train KNN model
107
+ self.model = KNeighborsClassifier(n_neighbors=3, weights='distance')
108
+ self.model.fit(X, y)
109
+
110
+ print("✅ Model trained successfully (hobbies required and encoded)")
111
+
112
+ except Exception as e:
113
+ print(f"Error training model: {e}")
114
+ self.model = None
115
+
116
+ def get_default_recommendations(self, stanine, gwa, strand):
117
+ """Provide default recommendations based on basic rules when no training data is available"""
118
+ courses = self.courses
119
+ recommendations = []
120
+
121
+ # Basic rules for recommendations
122
+ if strand == 'STEM':
123
+ if stanine >= 8 and gwa >= 90:
124
+ priority_courses = ['BSCS', 'BSIT']
125
+ else:
126
+ priority_courses = ['BSIT', 'BSCS']
127
+ elif strand == 'ABM':
128
+ priority_courses = ['BSBA']
129
+ elif strand == 'HUMSS':
130
+ priority_courses = ['BSED']
131
+ else:
132
+ priority_courses = list(courses.keys())
133
+
134
+ # Add courses with default probabilities
135
+ for i, course in enumerate(priority_courses[:2]): # Only take top 2
136
+ if course in courses:
137
+ recommendations.append({
138
+ 'code': course,
139
+ 'name': courses[course],
140
+ 'probability': 1.0 - (i * 0.2) # Decreasing probability for each course
141
+ })
142
+
143
+ return recommendations
144
+
145
+ def recommend_courses(self, stanine, gwa, strand, hobbies=None, top_n=5):
146
+ """Recommend courses based on student profile (hobbies required)"""
147
+ try:
148
+ if self.model is None:
149
+ return self.get_default_recommendations(stanine, gwa, strand)
150
+
151
+ # Prepare input features
152
+ input_data = pd.DataFrame([{
153
+ 'stanine': stanine,
154
+ 'gwa': gwa,
155
+ 'strand': strand,
156
+ 'hobbies': (hobbies or '').strip()
157
+ }])
158
+ # Validate hobbies
159
+ if not input_data['hobbies'].iloc[0]:
160
+ raise ValueError('hobbies is required for recommendations')
161
+
162
+ # Encode categorical variables
163
+ for col in ['strand', 'hobbies']:
164
+ if col in input_data.columns and col in self.label_encoders:
165
+ value = input_data[col].iloc[0]
166
+ if value not in self.label_encoders[col].classes_:
167
+ # Extend encoder classes to include unseen value at inference
168
+ self.label_encoders[col].classes_ = np.append(self.label_encoders[col].classes_, value)
169
+ input_data[col] = self.label_encoders[col].transform(input_data[col])
170
+
171
+ # Scale numerical features
172
+ numerical_columns = ['stanine', 'gwa']
173
+ if not input_data[numerical_columns].empty:
174
+ input_data[numerical_columns] = self.scaler.transform(input_data[numerical_columns])
175
+
176
+ # Get predictions
177
+ predictions = self.model.predict_proba(input_data)
178
+ courses = self.model.classes_
179
+
180
+ # Get top recommendations
181
+ top_indices = np.argsort(predictions[0])[-top_n:][::-1]
182
+ recommendations = []
183
+
184
+ course_map = self.courses
185
+ for idx in top_indices:
186
+ code = courses[idx]
187
+ confidence = predictions[0][idx]
188
+ recommendations.append({
189
+ 'code': code,
190
+ 'name': course_map.get(code, code),
191
+ 'rating': round(confidence * 100, 1)
192
+ })
193
+
194
+ return recommendations
195
+
196
+ except Exception as e:
197
+ print(f"Error recommending courses: {e}")
198
+ return self.get_default_recommendations(stanine, gwa, strand)
199
+
200
+ def _get_recommendation_reason(self, course, stanine, gwa, strand, hobbies, interests, personality_type, learning_style, career_goals):
201
+ """Generate personalized reason for recommendation"""
202
+ reasons = []
203
+
204
+ # Academic performance reasons
205
+ if stanine >= 8:
206
+ reasons.append("Excellent academic performance")
207
+ elif stanine >= 6:
208
+ reasons.append("Good academic foundation")
209
+
210
+ if gwa >= 85:
211
+ reasons.append("High academic achievement")
212
+ elif gwa >= 80:
213
+ reasons.append("Strong academic record")
214
+
215
+ # Strand alignment
216
+ if strand == "STEM" and course in ["BSCS", "BSIT", "BSArch", "BSIE", "BSN"]:
217
+ reasons.append("Perfect match with your STEM background")
218
+ elif strand == "ABM" and course in ["BSBA", "BSA"]:
219
+ reasons.append("Excellent alignment with your ABM strand")
220
+ elif strand == "HUMSS" and course in ["BSED", "BSPsych"]:
221
+ reasons.append("Great fit with your HUMSS background")
222
+
223
+ # Hobbies and interests alignment
224
+ if hobbies and any(hobby in hobbies.lower() for hobby in ["gaming", "programming", "technology", "computers"]):
225
+ if course in ["BSCS", "BSIT"]:
226
+ reasons.append("Matches your technology interests")
227
+
228
+ if hobbies and any(hobby in hobbies.lower() for hobby in ["business", "leadership", "management"]):
229
+ if course in ["BSBA", "BSA"]:
230
+ reasons.append("Aligns with your business interests")
231
+
232
+ if hobbies and any(hobby in hobbies.lower() for hobby in ["helping", "teaching", "caring"]):
233
+ if course in ["BSED", "BSN", "BSPsych"]:
234
+ reasons.append("Perfect for your helping nature")
235
+
236
+ # Personality type alignment
237
+ if personality_type == "introvert" and course in ["BSCS", "BSA", "BSArch"]:
238
+ reasons.append("Suits your introverted personality")
239
+ elif personality_type == "extrovert" and course in ["BSBA", "BSED", "BSHM"]:
240
+ reasons.append("Great for your outgoing personality")
241
+
242
+ # Learning style alignment
243
+ if learning_style == "hands-on" and course in ["BSIT", "BSHM", "BSAgri"]:
244
+ reasons.append("Matches your hands-on learning preference")
245
+ elif learning_style == "visual" and course in ["BSArch", "BSCS"]:
246
+ reasons.append("Perfect for your visual learning style")
247
+
248
+ # Career goals alignment
249
+ if career_goals and any(goal in career_goals.lower() for goal in ["developer", "programmer", "software"]):
250
+ if course in ["BSCS", "BSIT"]:
251
+ reasons.append("Direct path to your career goals")
252
+
253
+ if career_goals and any(goal in career_goals.lower() for goal in ["business", "entrepreneur", "manager"]):
254
+ if course in ["BSBA", "BSA"]:
255
+ reasons.append("Direct path to your business goals")
256
+
257
+ # Default reason if no specific matches
258
+ if not reasons:
259
+ reasons.append("Good academic and personal fit")
260
+
261
+ return " • ".join(reasons[:3]) # Limit to top 3 reasons
262
+
263
+ def save_model(self, model_path='course_recommender_model.joblib'):
264
+ """Save the trained model"""
265
+ if self.model is None:
266
+ raise Exception("No model to save!")
267
+
268
+ model_data = {
269
+ 'model': self.model,
270
+ 'scaler': self.scaler,
271
+ 'label_encoders': self.label_encoders
272
+ }
273
+ joblib.dump(model_data, model_path)
274
+
275
+ def load_model(self, model_path='course_recommender_model.joblib'):
276
+ """Load a trained model"""
277
+ model_data = joblib.load(model_path)
278
+ self.model = model_data['model']
279
+ self.scaler = model_data['scaler']
280
+ self.label_encoders = model_data['label_encoders']
281
+
282
+ # Example usage
283
+ if __name__ == "__main__":
284
+ recommender = CourseRecommender()
285
+
286
+ # Example recommendation
287
+ recommendations = recommender.recommend_courses(
288
+ stanine=8,
289
+ gwa=95,
290
+ strand='STEM',
291
+ hobbies='programming, gaming, technology'
292
+ )
293
+ print("Recommended courses:", json.dumps(recommendations, indent=2))
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio
2
+ numpy
3
+ pandas
4
+ scikit-learn
5
+ joblib
6
+ sentence-transformers
7
+ torch