AryanJh commited on
Commit
3e76b59
Β·
verified Β·
1 Parent(s): d2471c6

created event_matcher.py

Browse files

Focused file to manage the app

Files changed (1) hide show
  1. event_matcher.py +285 -0
event_matcher.py ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict, Tuple, Optional
2
+ from datetime import datetime
3
+ from transformers import T5Tokenizer, T5ForConditionalGeneration
4
+ import torch
5
+ import pytz
6
+ from fuzzywuzzy import fuzz
7
+
8
+ class Event:
9
+ """Class to structure event data"""
10
+ def __init__(
11
+ self,
12
+ title: str,
13
+ description: str,
14
+ start_time: datetime,
15
+ end_time: Optional[datetime],
16
+ location: str,
17
+ categories: List[str],
18
+ hosts: List[str],
19
+ link: str,
20
+ guid: str
21
+ ):
22
+ self.title = title
23
+ self.description = description
24
+ self.start_time = start_time
25
+ self.end_time = end_time
26
+ self.location = location
27
+ self.categories = categories
28
+ self.hosts = hosts
29
+ self.link = link
30
+ self.guid = guid
31
+
32
+ class EnhancedEventMatcher:
33
+ def __init__(self):
34
+ """Initialize the enhanced event matcher with T5"""
35
+ print("Initializing event matcher...")
36
+
37
+ # Initialize T5 for response enhancement
38
+ self.tokenizer = T5Tokenizer.from_pretrained("t5-small")
39
+ self.t5_model = T5ForConditionalGeneration.from_pretrained("t5-small")
40
+
41
+ # Initialize pattern learning
42
+ self.known_categories = set()
43
+ self.known_hosts = set()
44
+ self.known_locations = set()
45
+ self.faculty_patterns = {}
46
+ self.category_patterns = {}
47
+
48
+ # Define static patterns
49
+ self.patterns = {
50
+ 'faculty': {
51
+ 'math': ['mathematics', 'math', 'stats', 'computer science'],
52
+ 'humanities': ['humanities', 'language', 'literature'],
53
+ 'business': ['goodman', 'business', 'accounting'],
54
+ 'science': ['science', 'biology', 'chemistry', 'physics']
55
+ },
56
+ 'event_type': {
57
+ 'academic': ['lecture', 'seminar', 'workshop', 'conference'],
58
+ 'social': ['meetup', 'social', 'gathering', 'networking'],
59
+ 'career': ['career', 'job', 'employment', 'professional']
60
+ },
61
+ 'location': {
62
+ 'online': ['online', 'virtual', 'teams', 'zoom'],
63
+ 'campus': ['room', 'hall', 'building', 'plaza'],
64
+ 'library': ['library', 'learning commons', 'makerspace']
65
+ }
66
+ }
67
+
68
+ def convert_dict_to_event(self, event_dict: Dict) -> Event:
69
+ """Convert a dictionary to an Event object"""
70
+ try:
71
+ return Event(
72
+ title=event_dict['title'],
73
+ description=event_dict.get('description', ''),
74
+ start_time=event_dict['start_time'],
75
+ end_time=event_dict.get('end_time'),
76
+ location=event_dict['location'],
77
+ categories=event_dict.get('categories', '').split(';'),
78
+ hosts=event_dict.get('hosts', '').split(';'),
79
+ link=event_dict['link'],
80
+ guid=event_dict['guid']
81
+ )
82
+ except Exception as e:
83
+ print(f"Error converting dict to event: {e}")
84
+ raise
85
+
86
+ def learn_from_events(self, events: List[Event]) -> None:
87
+ """Learn patterns from existing events"""
88
+ try:
89
+ for event in events:
90
+ # Update known sets
91
+ self.known_categories.update(event.categories)
92
+ self.known_hosts.update(event.hosts)
93
+ self.known_locations.add(event.location)
94
+
95
+ # Learn faculty associations
96
+ for host in event.hosts:
97
+ for category in event.categories:
98
+ if 'faculty' in host.lower():
99
+ key = (host, category)
100
+ self.faculty_patterns[key] = self.faculty_patterns.get(key, 0) + 1
101
+
102
+ # Learn category associations
103
+ for cat1 in event.categories:
104
+ for cat2 in event.categories:
105
+ if cat1 != cat2:
106
+ key = (cat1, cat2)
107
+ self.category_patterns[key] = self.category_patterns.get(key, 0) + 1
108
+ except Exception as e:
109
+ print(f"Error learning from events: {e}")
110
+ raise
111
+
112
+ def get_faculty_score(self, event: Event, query: str) -> float:
113
+ """Score faculty relevance using learned patterns"""
114
+ try:
115
+ score = 0.0
116
+ query_lower = query.lower()
117
+
118
+ # Direct faculty mention check
119
+ for host in event.hosts:
120
+ if 'faculty' in host.lower():
121
+ ratio = fuzz.partial_ratio(query_lower, host.lower())
122
+ if ratio > 80:
123
+ score += 2.0 * (ratio / 100)
124
+
125
+ # Category association check
126
+ for category in event.categories:
127
+ for (host, cat), count in self.faculty_patterns.items():
128
+ if category == cat and fuzz.partial_ratio(query_lower, host.lower()) > 80:
129
+ score += 1.0 * (count / max(self.faculty_patterns.values(), default=1))
130
+
131
+ return score
132
+ except Exception as e:
133
+ print(f"Error calculating faculty score: {e}")
134
+ return 0.0
135
+
136
+ def get_category_score(self, event: Event, query_type: str) -> float:
137
+ """Score category relevance using learned patterns"""
138
+ try:
139
+ if not query_type:
140
+ return 0.0
141
+
142
+ score = 0.0
143
+ for category in event.categories:
144
+ # Direct category match
145
+ ratio = fuzz.partial_ratio(query_type.lower(), category.lower())
146
+ if ratio > 80:
147
+ score += 1.5 * (ratio / 100)
148
+
149
+ # Associated categories
150
+ for (cat1, cat2), count in self.category_patterns.items():
151
+ if category == cat1 and fuzz.partial_ratio(query_type.lower(), cat2.lower()) > 80:
152
+ score += 0.5 * (count / max(self.category_patterns.values(), default=1))
153
+
154
+ return score
155
+ except Exception as e:
156
+ print(f"Error calculating category score: {e}")
157
+ return 0.0
158
+
159
+ def get_location_score(self, event: Event, query: str) -> float:
160
+ """Score location relevance"""
161
+ try:
162
+ score = 0.0
163
+ location_lower = event.location.lower()
164
+ query_lower = query.lower()
165
+
166
+ # Check online/virtual events
167
+ if any(term in query_lower for term in self.patterns['location']['online']):
168
+ if any(term in location_lower for term in self.patterns['location']['online']):
169
+ score += 1.5
170
+
171
+ # Check campus/in-person events
172
+ if any(term in query_lower for term in ['in-person', 'campus', 'building']):
173
+ if any(term in location_lower for term in self.patterns['location']['campus']):
174
+ score += 1.5
175
+
176
+ # Check library events
177
+ if any(term in query_lower for term in self.patterns['location']['library']):
178
+ if any(term in location_lower for term in self.patterns['location']['library']):
179
+ score += 1.5
180
+
181
+ return score
182
+ except Exception as e:
183
+ print(f"Error calculating location score: {e}")
184
+ return 0.0
185
+
186
+ def generate_llm_response(self, query: str, events_text: str) -> str:
187
+ """Generate response using T5"""
188
+ try:
189
+ # Create prompt for T5
190
+ prompt = f"summarize: Query: {query}\nAvailable Events:\n{events_text}"
191
+
192
+ # Generate response
193
+ inputs = self.tokenizer.encode(prompt, return_tensors="pt", max_length=512, truncation=True)
194
+ outputs = self.t5_model.generate(
195
+ inputs,
196
+ max_length=300,
197
+ num_beams=4,
198
+ temperature=0.7,
199
+ early_stopping=True
200
+ )
201
+
202
+ return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
203
+ except Exception as e:
204
+ print(f"Error generating LLM response: {e}")
205
+ return "Here are some relevant events I found:"
206
+
207
+ def format_response(self, events: List[Tuple[Event, float]], llm_response: str) -> str:
208
+ """Format the final response with event details"""
209
+ try:
210
+ response = f"{llm_response}\n\n"
211
+
212
+ for event, score in events:
213
+ # Determine location icon
214
+ location_icon = "πŸ“±" if any(term in event.location.lower()
215
+ for term in self.patterns['location']['online']) else "πŸ“"
216
+
217
+ # Format event details
218
+ response += f"""
219
+ **{event.title}** {'🌟' * int(min(score, 5))}
220
+ πŸ“… {event.start_time.strftime('%A, %B %d, %Y')} at {event.start_time.strftime('%I:%M %p')}
221
+ {location_icon} {event.location}
222
+ πŸ‘₯ Hosted by: {', '.join(event.hosts)}
223
+ 🏷️ Categories: {', '.join(event.categories)}
224
+ πŸ”— {event.link}
225
+ """
226
+
227
+ return response
228
+ except Exception as e:
229
+ print(f"Error formatting response: {e}")
230
+ return "Error formatting the response. Please try again."
231
+
232
+ def match_and_respond(self, events: List[Dict], query: str) -> str:
233
+ """Main method to match events and generate response"""
234
+ try:
235
+ # Convert dictionary events to Event objects
236
+ event_objects = [self.convert_dict_to_event(event) for event in events]
237
+
238
+ # Learn patterns from events
239
+ self.learn_from_events(event_objects)
240
+
241
+ # Process query
242
+ query_lower = query.lower()
243
+ matched_events = []
244
+
245
+ # Score and match events
246
+ for event in event_objects:
247
+ faculty_score = self.get_faculty_score(event, query)
248
+ category_score = self.get_category_score(event, query_lower)
249
+ location_score = self.get_location_score(event, query)
250
+
251
+ total_score = (
252
+ faculty_score * 1.5 +
253
+ category_score * 1.2 +
254
+ location_score * 1.0
255
+ )
256
+
257
+ if total_score > 0:
258
+ matched_events.append((event, total_score))
259
+
260
+ # Sort and get top matches
261
+ matched_events.sort(key=lambda x: x[1], reverse=True)
262
+ top_matches = matched_events[:3]
263
+
264
+ if not top_matches:
265
+ return "I couldn't find any events matching your query. Try asking in a different way!"
266
+
267
+ # Format events for LLM
268
+ events_text = ""
269
+ for event, score in top_matches:
270
+ events_text += f"""
271
+ Event: {event.title}
272
+ Date: {event.start_time.strftime('%A, %B %d, %Y')}
273
+ Time: {event.start_time.strftime('%I:%M %p')}
274
+ Location: {event.location}
275
+ Categories: {', '.join(event.categories)}
276
+ Score: {score:.2f}
277
+ """
278
+
279
+ # Generate LLM response and format final response
280
+ llm_response = self.generate_llm_response(query, events_text)
281
+ return self.format_response(top_matches, llm_response)
282
+
283
+ except Exception as e:
284
+ print(f"Error in match_and_respond: {e}")
285
+ return "I encountered an error processing your query. Please try again!"