markobinario commited on
Commit
3d0f15e
·
verified ·
1 Parent(s): a046f1c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +235 -324
app.py CHANGED
@@ -1,336 +1,247 @@
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
- import os
8
- import requests
9
 
10
- class CourseRecommender:
11
- def __init__(self):
12
- self.model = None
13
- self.label_encoders = {}
14
- self.scaler = StandardScaler()
15
- self.courses = self.get_courses()
16
- self.training_data = self.get_training_data()
17
- self.train_model()
18
-
19
- def get_courses(self):
20
- """Get static course data"""
21
- return {
22
- 'BSCS': 'Bachelor of Science in Computer Science',
23
- 'BSIT': 'Bachelor of Science in Information Technology',
24
- 'BSBA': 'Bachelor of Science in Business Administration',
25
- 'BSED': 'Bachelor of Science in Education',
26
- 'BSN': 'Bachelor of Science in Nursing',
27
- 'BSArch': 'Bachelor of Science in Architecture',
28
- 'BSIE': 'Bachelor of Science in Industrial Engineering',
29
- 'BSHM': 'Bachelor of Science in Hospitality Management',
30
- 'BSA': 'Bachelor of Science in Accountancy',
31
- 'BSPsych': 'Bachelor of Science in Psychology',
32
- 'BSAgri': 'Bachelor of Science in Agriculture'
33
- }
34
-
35
- def save_student_data(self, stanine, gwa, strand, course, rating, hobbies=None):
36
- """Save student feedback to in-memory storage (for demonstration purposes)"""
37
- try:
38
- # In a real implementation, you could save this to a file or external storage
39
- print(f"Student feedback saved: Stanine={stanine}, GWA={gwa}, Strand={strand}, Course={course}, Rating={rating}, Hobbies={hobbies}")
40
- return True
41
- except Exception as e:
42
- print(f"Error saving student feedback: {e}")
43
- return False
44
-
45
- def get_training_data(self):
46
- """Get static training data for demonstration purposes"""
47
- # Sample training data to demonstrate the recommender system
48
- training_data = [
49
- # STEM students
50
- (8, 95, 'STEM', 'BSCS', 5, 'programming, gaming, technology'),
51
- (7, 90, 'STEM', 'BSIT', 4, 'computers, software, coding'),
52
- (9, 98, 'STEM', 'BSCS', 5, 'programming, algorithms, math'),
53
- (6, 85, 'STEM', 'BSIT', 3, 'technology, computers'),
54
- (8, 92, 'STEM', 'BSArch', 4, 'design, drawing, creativity'),
55
- (7, 88, 'STEM', 'BSIE', 4, 'engineering, problem solving'),
56
-
57
- # ABM students
58
- (8, 90, 'ABM', 'BSBA', 5, 'business, management, leadership'),
59
- (7, 85, 'ABM', 'BSA', 4, 'accounting, numbers, finance'),
60
- (6, 82, 'ABM', 'BSBA', 3, 'business, marketing'),
61
- (9, 95, 'ABM', 'BSA', 5, 'accounting, finance, analysis'),
62
-
63
- # HUMSS students
64
- (8, 88, 'HUMSS', 'BSED', 5, 'teaching, helping, education'),
65
- (7, 85, 'HUMSS', 'BSPsych', 4, 'psychology, helping, people'),
66
- (6, 80, 'HUMSS', 'BSED', 3, 'teaching, children'),
67
- (9, 92, 'HUMSS', 'BSPsych', 5, 'psychology, counseling, people'),
68
-
69
- # General interests
70
- (7, 87, 'STEM', 'BSN', 4, 'helping, healthcare, caring'),
71
- (8, 89, 'ABM', 'BSHM', 4, 'hospitality, service, management'),
72
- (6, 83, 'HUMSS', 'BSAgri', 3, 'agriculture, environment, nature'),
73
- ]
74
-
75
- return pd.DataFrame(training_data, columns=['stanine', 'gwa', 'strand', 'course', 'rating', 'hobbies'])
76
 
77
- def train_model(self):
78
- """Train the recommendation model using the training data"""
79
- try:
80
- training_data = self.get_training_data()
81
-
82
- if training_data.empty:
83
- print("No training data available - using default recommendations")
84
- return
85
-
86
- # Prepare features (hobbies required)
87
- feature_columns = ['stanine', 'gwa', 'strand', 'hobbies']
88
-
89
- # Create feature matrix
90
- X = training_data[feature_columns].copy()
91
- y = training_data['course']
92
-
93
- # Handle categorical variables
94
- categorical_columns = ['strand', 'hobbies']
95
-
96
- # Refit encoders every training to incorporate new categories
97
- for col in categorical_columns:
98
- if col in X.columns:
99
- X[col] = X[col].fillna('unknown')
100
- self.label_encoders[col] = LabelEncoder()
101
- X[col] = self.label_encoders[col].fit_transform(X[col])
102
-
103
- # Scale numerical features
104
- numerical_columns = ['stanine', 'gwa']
105
- if not X[numerical_columns].empty:
106
- X[numerical_columns] = self.scaler.fit_transform(X[numerical_columns])
107
-
108
- # Train KNN model
109
- self.model = KNeighborsClassifier(n_neighbors=3, weights='distance')
110
- self.model.fit(X, y)
111
-
112
- print("Model trained successfully (hobbies required and encoded)")
113
-
114
- except Exception as e:
115
- print(f"Error training model: {e}")
116
- self.model = None
117
 
118
- def get_default_recommendations(self, stanine, gwa, strand):
119
- """Provide default recommendations based on basic rules when no training data is available"""
120
- courses = self.courses
121
- recommendations = []
122
-
123
- # Basic rules for recommendations
124
- if strand == 'STEM':
125
- if stanine >= 8 and gwa >= 90:
126
- priority_courses = ['BSCS', 'BSIT']
127
- else:
128
- priority_courses = ['BSIT', 'BSCS']
129
- elif strand == 'ABM':
130
- priority_courses = ['BSBA']
131
- elif strand == 'HUMSS':
132
- priority_courses = ['BSED']
133
  else:
134
- priority_courses = list(courses.keys())
135
-
136
- # Add courses with default probabilities
137
- for i, course in enumerate(priority_courses[:2]): # Only take top 2
138
- if course in courses:
139
- recommendations.append({
140
- 'code': course,
141
- 'name': courses[course],
142
- 'probability': 1.0 - (i * 0.2) # Decreasing probability for each course
143
- })
144
-
145
- return recommendations
146
 
147
- def recommend_courses(self, stanine, gwa, strand, hobbies=None, top_n=5):
148
- """Recommend courses based on student profile (hobbies required)"""
149
- try:
150
- if self.model is None:
151
- return self.get_default_recommendations(stanine, gwa, strand)
152
-
153
- # Prepare input features
154
- input_data = pd.DataFrame([{
155
- 'stanine': stanine,
156
- 'gwa': gwa,
157
- 'strand': strand,
158
- 'hobbies': (hobbies or '').strip()
159
- }])
160
- # Validate hobbies
161
- if not input_data['hobbies'].iloc[0]:
162
- raise ValueError('hobbies is required for recommendations')
163
-
164
- # Encode categorical variables
165
- for col in ['strand', 'hobbies']:
166
- if col in input_data.columns and col in self.label_encoders:
167
- value = input_data[col].iloc[0]
168
- if value not in self.label_encoders[col].classes_:
169
- # Extend encoder classes to include unseen value at inference
170
- self.label_encoders[col].classes_ = np.append(self.label_encoders[col].classes_, value)
171
- input_data[col] = self.label_encoders[col].transform(input_data[col])
172
-
173
- # Scale numerical features
174
- numerical_columns = ['stanine', 'gwa']
175
- if not input_data[numerical_columns].empty:
176
- input_data[numerical_columns] = self.scaler.transform(input_data[numerical_columns])
177
-
178
- # Get predictions
179
- predictions = self.model.predict_proba(input_data)
180
- courses = self.model.classes_
181
-
182
- # Get top recommendations
183
- top_indices = np.argsort(predictions[0])[-top_n:][::-1]
184
- recommendations = []
185
-
186
- course_map = self.courses
187
- for idx in top_indices:
188
- code = courses[idx]
189
- confidence = predictions[0][idx]
190
- recommendations.append({
191
- 'code': code,
192
- 'name': course_map.get(code, code),
193
- 'rating': round(confidence * 100, 1)
194
- })
195
-
196
- return recommendations
197
-
198
- except Exception as e:
199
- print(f"Error recommending courses: {e}")
200
- return self.get_default_recommendations(stanine, gwa, strand)
201
 
202
- def _get_recommendation_reason(self, course, stanine, gwa, strand, hobbies, interests, personality_type, learning_style, career_goals):
203
- """Generate personalized reason for recommendation"""
204
- reasons = []
205
-
206
- # Academic performance reasons
207
- if stanine >= 8:
208
- reasons.append("Excellent academic performance")
209
- elif stanine >= 6:
210
- reasons.append("Good academic foundation")
211
-
212
- if gwa >= 85:
213
- reasons.append("High academic achievement")
214
- elif gwa >= 80:
215
- reasons.append("Strong academic record")
216
-
217
- # Strand alignment
218
- if strand == "STEM" and course in ["BSCS", "BSIT", "BSArch", "BSIE", "BSN"]:
219
- reasons.append("Perfect match with your STEM background")
220
- elif strand == "ABM" and course in ["BSBA", "BSA"]:
221
- reasons.append("Excellent alignment with your ABM strand")
222
- elif strand == "HUMSS" and course in ["BSED", "BSPsych"]:
223
- reasons.append("Great fit with your HUMSS background")
224
-
225
- # Hobbies and interests alignment
226
- if hobbies and any(hobby in hobbies.lower() for hobby in ["gaming", "programming", "technology", "computers"]):
227
- if course in ["BSCS", "BSIT"]:
228
- reasons.append("Matches your technology interests")
229
-
230
- if hobbies and any(hobby in hobbies.lower() for hobby in ["business", "leadership", "management"]):
231
- if course in ["BSBA", "BSA"]:
232
- reasons.append("Aligns with your business interests")
233
-
234
- if hobbies and any(hobby in hobbies.lower() for hobby in ["helping", "teaching", "caring"]):
235
- if course in ["BSED", "BSN", "BSPsych"]:
236
- reasons.append("Perfect for your helping nature")
237
-
238
- # Personality type alignment
239
- if personality_type == "introvert" and course in ["BSCS", "BSA", "BSArch"]:
240
- reasons.append("Suits your introverted personality")
241
- elif personality_type == "extrovert" and course in ["BSBA", "BSED", "BSHM"]:
242
- reasons.append("Great for your outgoing personality")
243
-
244
- # Learning style alignment
245
- if learning_style == "hands-on" and course in ["BSIT", "BSHM", "BSAgri"]:
246
- reasons.append("Matches your hands-on learning preference")
247
- elif learning_style == "visual" and course in ["BSArch", "BSCS"]:
248
- reasons.append("Perfect for your visual learning style")
249
-
250
- # Career goals alignment
251
- if career_goals and any(goal in career_goals.lower() for goal in ["developer", "programmer", "software"]):
252
- if course in ["BSCS", "BSIT"]:
253
- reasons.append("Direct path to your career goals")
254
-
255
- if career_goals and any(goal in career_goals.lower() for goal in ["business", "entrepreneur", "manager"]):
256
- if course in ["BSBA", "BSA"]:
257
- reasons.append("Direct path to your business goals")
258
 
259
- # Default reason if no specific matches
260
- if not reasons:
261
- reasons.append("Good academic and personal fit")
262
 
263
- return " • ".join(reasons[:3]) # Limit to top 3 reasons
264
-
265
- def save_model(self, model_path='course_recommender_model.joblib'):
266
- """Save the trained model"""
267
- if self.model is None:
268
- raise Exception("No model to save!")
269
-
270
- model_data = {
271
- 'model': self.model,
272
- 'scaler': self.scaler,
273
- 'label_encoders': self.label_encoders
274
- }
275
- joblib.dump(model_data, model_path)
276
 
277
- def load_model(self, model_path='course_recommender_model.joblib'):
278
- """Load a trained model"""
279
- model_data = joblib.load(model_path)
280
- self.model = model_data['model']
281
- self.scaler = model_data['scaler']
282
- self.label_encoders = model_data['label_encoders']
283
-
284
-
285
- # ===== UI helper for Hugging Face integration =====
286
- def get_course_recommendations_ui(recommender: "CourseRecommender", stanine, gwa, strand, hobbies) -> str:
287
- if recommender is None:
288
- return "Sorry, the recommendation system is not available at the moment. Please try again later."
289
- try:
290
- try:
291
- stanine = int(stanine.strip()) if isinstance(stanine, str) else int(stanine)
292
- except (ValueError, TypeError, AttributeError):
293
- return "❌ Stanine score must be a valid number between 1 and 9"
294
- try:
295
- gwa = float(gwa.strip()) if isinstance(gwa, str) else float(gwa)
296
- except (ValueError, TypeError, AttributeError):
297
- return "❌ GWA must be a valid number between 75 and 100"
298
- if not (1 <= stanine <= 9):
299
- return " Stanine score must be between 1 and 9"
300
- if not (75 <= gwa <= 100):
301
- return "❌ GWA must be between 75 and 100"
302
- if not strand:
303
- return "❌ Please select a strand"
304
- if not hobbies or not str(hobbies).strip():
305
- return "❌ Please enter your hobbies/interests"
306
-
307
- recommendations = recommender.recommend_courses(
308
- stanine=stanine,
309
- gwa=gwa,
310
- strand=strand,
311
- hobbies=str(hobbies)
312
- )
313
- if not recommendations:
314
- return "No recommendations available at the moment."
315
- response = f"## 🎯 Course Recommendations for You\n\n"
316
- response += f"**Profile:** Stanine {stanine}, GWA {gwa}, {strand} Strand\n"
317
- response += f"**Interests:** {hobbies}\n\n"
318
- for i, rec in enumerate(recommendations, 1):
319
- response += f"### {i}. {rec['code']} - {rec['name']}\n"
320
- response += f"**Match Score:** {rec.get('rating', rec.get('probability', 0)):.1f}%\n\n"
321
- return response
322
- except Exception as e:
323
- return f" Error getting recommendations: {str(e)}"
324
-
325
- # Example usage
326
- if __name__ == "__main__":
327
- recommender = CourseRecommender()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
 
329
- # Example recommendation
330
- recommendations = recommender.recommend_courses(
331
- stanine=8,
332
- gwa=95,
333
- strand='STEM',
334
- hobbies='programming, gaming, technology'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  )
336
- print("Recommended courses:", json.dumps(recommendations, indent=2))
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from chatbot import Chatbot
 
 
 
3
  import json
 
 
4
 
5
+ # Initialize chatbot
6
+ chatbot = Chatbot()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ def chat_interface(message, history):
9
+ """Handle chat interface with Gradio"""
10
+ if not message.strip():
11
+ return "Please enter a message."
12
+
13
+ # Get response from chatbot
14
+ response = chatbot.get_response(message)
15
+
16
+ # Format the response for display (removed confidence)
17
+ if response['status'] == 'success':
18
+ formatted_response = response['answer']
19
+ else:
20
+ formatted_response = response['answer']
21
+
22
+ return formatted_response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ def get_system_info():
25
+ """Get system information"""
26
+ faq_count = chatbot.get_qa_count()
27
+ database_url = chatbot.database_url
28
+
29
+ # Test database connection
30
+ try:
31
+ import requests
32
+ response = requests.get(f"{database_url}/faqs", params={'question': 'test'}, timeout=5)
33
+ if response.status_code == 200:
34
+ db_status = "Connected"
 
 
 
 
35
  else:
36
+ db_status = f"Error ({response.status_code})"
37
+ except:
38
+ db_status = "Unavailable"
39
+
40
+ return f"System Status: Active\nFAQ Pairs Loaded: {faq_count}\nDatabase: {db_status}\nCourse Recommender: Ready"
 
 
 
 
 
 
 
41
 
42
+ def get_course_recommendations(stanine, gwa, strand, hobbies):
43
+ """Get course recommendations"""
44
+ return chatbot.get_course_recommendations(stanine, gwa, strand, hobbies)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ # Create Gradio interface
47
+ with gr.Blocks(
48
+ title="AI Chatbot",
49
+ theme=gr.themes.Soft(),
50
+ css="""
51
+ .gradio-container {
52
+ max-width: 800px !important;
53
+ margin: auto !important;
54
+ }
55
+ .chat-message {
56
+ padding: 10px;
57
+ margin: 5px 0;
58
+ border-radius: 10px;
59
+ }
60
+ """
61
+ ) as demo:
62
+
63
+ gr.Markdown(
64
+ """
65
+ # 🤖 AI Student Assistant
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ Get answers to your questions and personalized course recommendations!
 
 
68
 
69
+ **Features:**
70
+ - FAQ Chat: Get instant answers from our knowledge base
71
+ - Course Recommendations: Get personalized course suggestions based on your profile
72
+ """
73
+ )
 
 
 
 
 
 
 
 
74
 
75
+ with gr.Tabs():
76
+ with gr.TabItem("💬 FAQ Chat"):
77
+ with gr.Row():
78
+ with gr.Column(scale=3):
79
+ chatbot_interface = gr.Chatbot(
80
+ label="FAQ Chat",
81
+ height=400,
82
+ show_label=True,
83
+ container=True,
84
+ bubble_full_width=False
85
+ )
86
+
87
+ with gr.Row():
88
+ msg = gr.Textbox(
89
+ placeholder="Type your question here...",
90
+ show_label=False,
91
+ scale=4,
92
+ container=False
93
+ )
94
+ submit_btn = gr.Button("Send", variant="primary", scale=1)
95
+
96
+ with gr.Column(scale=1):
97
+ gr.Markdown("### System Info")
98
+ system_info = gr.Textbox(
99
+ value=get_system_info(),
100
+ label="Status",
101
+ interactive=False,
102
+ lines=4
103
+ )
104
+
105
+ refresh_btn = gr.Button("Refresh Status", variant="secondary")
106
+
107
+ gr.Markdown("### FAQ Instructions")
108
+ gr.Markdown(
109
+ """
110
+ **How to use:**
111
+ 1. Type your question in the text box
112
+ 2. Click Send or press Enter
113
+ 3. Get AI-powered answers from FAQ database
114
+
115
+ **Tips:**
116
+ - Ask specific questions for better results
117
+ - Try rephrasing if you don't get a good match
118
+ """
119
+ )
120
+
121
+ with gr.TabItem("🎯 Course Recommendations"):
122
+ with gr.Row():
123
+ with gr.Column(scale=2):
124
+ gr.Markdown("### 📝 Student Profile")
125
+
126
+ stanine_input = gr.Slider(
127
+ minimum=1,
128
+ maximum=9,
129
+ step=1,
130
+ value=5,
131
+ label="Stanine Score (1-9)",
132
+ info="Your stanine score from standardized tests"
133
+ )
134
+
135
+ gwa_input = gr.Slider(
136
+ minimum=75,
137
+ maximum=100,
138
+ step=0.1,
139
+ value=85,
140
+ label="GWA (75-100)",
141
+ info="Your General Weighted Average"
142
+ )
143
+
144
+ strand_input = gr.Dropdown(
145
+ choices=["STEM", "ABM", "HUMSS"],
146
+ label="Senior High School Strand",
147
+ info="Select your SHS strand"
148
+ )
149
+
150
+ hobbies_input = gr.Textbox(
151
+ label="Hobbies & Interests",
152
+ placeholder="e.g., programming, gaming, business, teaching...",
153
+ lines=3,
154
+ info="Describe your interests and hobbies"
155
+ )
156
+
157
+ recommend_btn = gr.Button("Get Recommendations", variant="primary", size="lg")
158
+
159
+ with gr.Column(scale=3):
160
+ gr.Markdown("### 🎯 Your Course Recommendations")
161
+ recommendations_output = gr.Markdown(
162
+ value="Enter your profile details and click 'Get Recommendations' to see personalized course suggestions.",
163
+ label="Recommendations"
164
+ )
165
+
166
+ gr.Markdown("### 📚 Available Courses")
167
+ gr.Markdown(
168
+ """
169
+ **STEM Courses:**
170
+ - BSCS: Bachelor of Science in Computer Science
171
+ - BSIT: Bachelor of Science in Information Technology
172
+ - BSArch: Bachelor of Science in Architecture
173
+ - BSIE: Bachelor of Science in Industrial Engineering
174
+ - BSN: Bachelor of Science in Nursing
175
+
176
+ **ABM Courses:**
177
+ - BSBA: Bachelor of Science in Business Administration
178
+ - BSA: Bachelor of Science in Accountancy
179
+
180
+ **HUMSS Courses:**
181
+ - BSED: Bachelor of Science in Education
182
+ - BSPsych: Bachelor of Science in Psychology
183
+
184
+ **Other Courses:**
185
+ - BSHM: Bachelor of Science in Hospitality Management
186
+ - BSAgri: Bachelor of Science in Agriculture
187
+ """
188
+ )
189
+
190
+ # Event handlers
191
+ def user(user_message, history):
192
+ return "", history + [[user_message, None]]
193
 
194
+ def bot(history):
195
+ user_message = history[-1][0]
196
+ bot_message = chat_interface(user_message, history)
197
+ history[-1][1] = bot_message
198
+ return history
199
+
200
+ def refresh_system_info():
201
+ return get_system_info()
202
+
203
+ # Connect FAQ Chat events
204
+ submit_btn.click(
205
+ fn=user,
206
+ inputs=[msg, chatbot_interface],
207
+ outputs=[msg, chatbot_interface],
208
+ queue=False
209
+ ).then(
210
+ fn=bot,
211
+ inputs=chatbot_interface,
212
+ outputs=chatbot_interface,
213
+ queue=True
214
+ )
215
+
216
+ msg.submit(
217
+ fn=user,
218
+ inputs=[msg, chatbot_interface],
219
+ outputs=[msg, chatbot_interface],
220
+ queue=False
221
+ ).then(
222
+ fn=bot,
223
+ inputs=chatbot_interface,
224
+ outputs=chatbot_interface,
225
+ queue=True
226
+ )
227
+
228
+ # Connect Course Recommendation events
229
+ recommend_btn.click(
230
+ fn=get_course_recommendations,
231
+ inputs=[stanine_input, gwa_input, strand_input, hobbies_input],
232
+ outputs=recommendations_output
233
+ )
234
+
235
+ refresh_btn.click(
236
+ fn=refresh_system_info,
237
+ outputs=system_info
238
  )
239
+
240
+ if __name__ == "__main__":
241
+ demo.launch(
242
+ server_name="0.0.0.0",
243
+ server_port=7860,
244
+ share=False,
245
+ show_error=True,
246
+ quiet=False
247
+ )