File size: 6,523 Bytes
d1aea16
 
a2ba3d2
 
d1aea16
 
d1e5d7d
d1aea16
ad4defa
d1aea16
 
a2ba3d2
d1aea16
 
 
a2ba3d2
d1aea16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d1e5d7d
d1aea16
 
 
 
 
 
 
 
 
 
 
 
a09117f
d1aea16
 
 
 
a09117f
d1aea16
 
a09117f
d1aea16
 
a09117f
d1aea16
 
 
a09117f
d1aea16
 
 
 
 
 
 
 
 
a09117f
d1aea16
a09117f
d1aea16
 
 
 
 
 
 
 
 
 
 
 
 
 
d1e5d7d
d1aea16
 
 
d1e5d7d
d1aea16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import requests
import json
from sentence_transformers import SentenceTransformer
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from recommender import CourseRecommender

class Chatbot:
    def __init__(self):
        self.qa_pairs = []
        self.question_embeddings = []
        self.model = SentenceTransformer('all-MiniLM-L6-v2')
        self.database_url = "https://database-46m3.onrender.com"
        self.recommender = CourseRecommender()
        self.load_qa_data()
    
    def load_qa_data(self):
        """Load Q&A pairs from the faqs table in the database"""
        try:
            # Connect to the faqs table endpoint
            faqs_url = f"{self.database_url}/faqs"
            response = requests.get(faqs_url)
            if response.status_code == 200:
                data = response.json()
                # Assuming the database returns a list of FAQ objects
                if isinstance(data, list):
                    self.qa_pairs = data
                else:
                    # If it's a single object, wrap it in a list
                    self.qa_pairs = [data]
                
                # Generate embeddings for all questions
                questions = [item.get('question', '') for item in self.qa_pairs]
                self.question_embeddings = self.model.encode(questions)
                print(f"Loaded {len(self.qa_pairs)} FAQ pairs from database")
            else:
                print(f"Failed to load data from faqs table. Status code: {response.status_code}")
                self._load_fallback_data()
        except Exception as e:
            print(f"Error loading FAQ data: {str(e)}")
            self._load_fallback_data()
    
    def _load_fallback_data(self):
        """Load fallback data if database is unavailable"""
        self.qa_pairs = [
            {"question": "What is artificial intelligence?", "answer": "Artificial Intelligence (AI) is a branch of computer science that aims to create machines capable of intelligent behavior."},
            {"question": "How does machine learning work?", "answer": "Machine learning is a subset of AI that enables computers to learn and improve from experience without being explicitly programmed."},
            {"question": "What is deep learning?", "answer": "Deep learning is a subset of machine learning that uses neural networks with multiple layers to model and understand complex patterns in data."},
            {"question": "What is natural language processing?", "answer": "Natural Language Processing (NLP) is a field of AI that focuses on the interaction between computers and humans through natural language."},
            {"question": "What is a neural network?", "answer": "A neural network is a computing system inspired by biological neural networks that constitute animal brains. It consists of interconnected nodes (neurons) that process information."}
        ]
        questions = [item['question'] for item in self.qa_pairs]
        self.question_embeddings = self.model.encode(questions)
        print("Loaded fallback Q&A data")
    
    def find_best_match(self, user_input, threshold=0.7):
        """Find the best matching question using semantic similarity"""
        if not self.qa_pairs:
            return None, 0
        
        # Encode the user input
        user_embedding = self.model.encode([user_input])
        
        # Calculate cosine similarity with all questions
        similarities = cosine_similarity(user_embedding, self.question_embeddings)[0]
        
        # Find the best match
        best_match_idx = np.argmax(similarities)
        best_similarity = similarities[best_match_idx]
        
        if best_similarity >= threshold:
            return self.qa_pairs[best_match_idx], best_similarity
        else:
            return None, best_similarity
    
    def get_response(self, user_input):
        """Get response for user input"""
        if not user_input.strip():
            return "Please enter a message."
        
        best_match, similarity = self.find_best_match(user_input)
        
        if best_match:
            return {
                'answer': best_match.get('answer', 'No answer found'),
                'confidence': float(similarity),
                'matched_question': best_match.get('question', ''),
                'status': 'success'
            }
        else:
            return {
                'answer': "I'm sorry, I couldn't find a relevant answer to your question. Could you please rephrase it or ask something else?",
                'confidence': float(similarity),
                'matched_question': '',
                'status': 'no_match'
            }
    
    def get_qa_count(self):
        """Get the number of loaded Q&A pairs"""
        return len(self.qa_pairs)
    
    def get_course_recommendations(self, stanine, gwa, strand, hobbies):
        """Get course recommendations using the recommender system"""
        try:
            # Validate inputs
            stanine = int(stanine) if isinstance(stanine, str) else stanine
            gwa = float(gwa) if isinstance(gwa, str) else gwa
            
            if not (1 <= stanine <= 9):
                return "❌ Stanine score must be between 1 and 9"
            if not (75 <= gwa <= 100):
                return "❌ GWA must be between 75 and 100"
            if not strand:
                return "❌ Please select a strand"
            if not hobbies or not str(hobbies).strip():
                return "❌ Please enter your hobbies/interests"
            
            # Get recommendations
            recommendations = self.recommender.recommend_courses(
                stanine=stanine,
                gwa=gwa,
                strand=strand,
                hobbies=str(hobbies)
            )
            
            if not recommendations:
                return "No recommendations available at the moment."
            
            # Format response (without confidence scores)
            response = f"## 🎯 Course Recommendations for You\n\n"
            response += f"**Profile:** Stanine {stanine}, GWA {gwa}, {strand} Strand\n"
            response += f"**Interests:** {hobbies}\n\n"
            
            for i, rec in enumerate(recommendations, 1):
                response += f"### {i}. {rec['code']} - {rec['name']}\n\n"
            
            return response
            
        except Exception as e:
            return f"❌ Error getting recommendations: {str(e)}"