|
| 1 | +# app/ml/team_collaboration.py |
| 2 | +""" |
| 3 | +Team Collaboration AI System |
| 4 | +Real-time collaboration, smart suggestions, activity tracking, and team insights. |
| 5 | +""" |
| 6 | + |
| 7 | +from dataclasses import dataclass, field |
| 8 | +from datetime import datetime, timedelta |
| 9 | +from typing import Dict, List, Optional, Set |
| 10 | +from enum import Enum |
| 11 | +import uuid |
| 12 | + |
| 13 | + |
| 14 | +class CollaborationActivityType(Enum): |
| 15 | + """Types of collaboration activities.""" |
| 16 | + MESSAGE = "message" |
| 17 | + MENTION = "mention" |
| 18 | + EDIT = "edit" |
| 19 | + COMMENT = "comment" |
| 20 | + APPROVAL = "approval" |
| 21 | + ASSIGNMENT = "assignment" |
| 22 | + COMPLETION = "completion" |
| 23 | + |
| 24 | + |
| 25 | +class SuggestionType(Enum): |
| 26 | + """Smart suggestion types.""" |
| 27 | + ASSIGNMENT = "assignment" # Suggest who should do task |
| 28 | + TIMING = "timing" # Suggest best time for meeting |
| 29 | + EXPERTISE = "expertise" # Suggest expert for question |
| 30 | + COLLABORATION = "collaboration" # Suggest who should collaborate |
| 31 | + AUTOMATION = "automation" # Suggest task automation |
| 32 | + |
| 33 | + |
| 34 | +@dataclass |
| 35 | +class TeamMember: |
| 36 | + """Team member profile.""" |
| 37 | + member_id: str = field(default_factory=lambda: str(uuid.uuid4())) |
| 38 | + name: str = "" |
| 39 | + email: str = "" |
| 40 | + role: str = "" |
| 41 | + skills: List[str] = field(default_factory=list) |
| 42 | + availability: Dict = field(default_factory=dict) # day -> hours |
| 43 | + activity_level: float = 0.0 # 0-1 scale |
| 44 | + last_active: datetime = field(default_factory=datetime.utcnow) |
| 45 | + |
| 46 | + def to_dict(self) -> Dict: |
| 47 | + return { |
| 48 | + 'member_id': self.member_id, |
| 49 | + 'name': self.name, |
| 50 | + 'role': self.role, |
| 51 | + 'skills': self.skills, |
| 52 | + 'activity_level': self.activity_level, |
| 53 | + 'last_active': self.last_active.isoformat() |
| 54 | + } |
| 55 | + |
| 56 | + |
| 57 | +@dataclass |
| 58 | +class CollaborationActivity: |
| 59 | + """Collaboration activity record.""" |
| 60 | + activity_id: str = field(default_factory=lambda: str(uuid.uuid4())) |
| 61 | + member_id: str = "" |
| 62 | + activity_type: CollaborationActivityType = CollaborationActivityType.MESSAGE |
| 63 | + resource_id: str = "" |
| 64 | + resource_type: str = "" # task, document, project |
| 65 | + timestamp: datetime = field(default_factory=datetime.utcnow) |
| 66 | + details: Dict = field(default_factory=dict) |
| 67 | + |
| 68 | + def to_dict(self) -> Dict: |
| 69 | + return { |
| 70 | + 'activity_id': self.activity_id, |
| 71 | + 'member_id': self.member_id, |
| 72 | + 'activity_type': self.activity_type.value, |
| 73 | + 'resource_type': self.resource_type, |
| 74 | + 'timestamp': self.timestamp.isoformat() |
| 75 | + } |
| 76 | + |
| 77 | + |
| 78 | +@dataclass |
| 79 | +class SmartSuggestion: |
| 80 | + """AI-generated smart suggestion.""" |
| 81 | + suggestion_id: str = field(default_factory=lambda: str(uuid.uuid4())) |
| 82 | + suggestion_type: SuggestionType = SuggestionType.ASSIGNMENT |
| 83 | + context: str = "" |
| 84 | + suggested_members: List[str] = field(default_factory=list) |
| 85 | + confidence: float = 0.0 |
| 86 | + rationale: str = "" |
| 87 | + created_at: datetime = field(default_factory=datetime.utcnow) |
| 88 | + accepted: bool = False |
| 89 | + |
| 90 | + def to_dict(self) -> Dict: |
| 91 | + return { |
| 92 | + 'suggestion_id': self.suggestion_id, |
| 93 | + 'type': self.suggestion_type.value, |
| 94 | + 'suggested_members': self.suggested_members, |
| 95 | + 'confidence': round(self.confidence, 2), |
| 96 | + 'rationale': self.rationale, |
| 97 | + 'accepted': self.accepted |
| 98 | + } |
| 99 | + |
| 100 | + |
| 101 | +class TeamCollaborationAI: |
| 102 | + """ |
| 103 | + AI-powered team collaboration system with real-time insights. |
| 104 | + """ |
| 105 | + |
| 106 | + def __init__(self): |
| 107 | + """Initialize team collaboration system.""" |
| 108 | + self.team_members: Dict[str, TeamMember] = {} |
| 109 | + self.activities: List[CollaborationActivity] = [] |
| 110 | + self.suggestions: Dict[str, SmartSuggestion] = {} |
| 111 | + self.team_metrics = { |
| 112 | + 'total_activities': 0, |
| 113 | + 'collaboration_score': 0.0, |
| 114 | + 'response_time_avg': 0, |
| 115 | + 'productivity_index': 0.0 |
| 116 | + } |
| 117 | + |
| 118 | + def add_team_member(self, name: str, email: str, role: str, |
| 119 | + skills: List[str] = None) -> TeamMember: |
| 120 | + """Add team member.""" |
| 121 | + member = TeamMember( |
| 122 | + name=name, |
| 123 | + email=email, |
| 124 | + role=role, |
| 125 | + skills=skills or [] |
| 126 | + ) |
| 127 | + |
| 128 | + self.team_members[member.member_id] = member |
| 129 | + return member |
| 130 | + |
| 131 | + def log_activity(self, member_id: str, activity_type: CollaborationActivityType, |
| 132 | + resource_id: str, resource_type: str, |
| 133 | + details: Dict = None) -> CollaborationActivity: |
| 134 | + """Log collaboration activity.""" |
| 135 | + activity = CollaborationActivity( |
| 136 | + member_id=member_id, |
| 137 | + activity_type=activity_type, |
| 138 | + resource_id=resource_id, |
| 139 | + resource_type=resource_type, |
| 140 | + details=details or {} |
| 141 | + ) |
| 142 | + |
| 143 | + self.activities.append(activity) |
| 144 | + self.team_metrics['total_activities'] += 1 |
| 145 | + |
| 146 | + # Update member's activity level |
| 147 | + if member_id in self.team_members: |
| 148 | + member = self.team_members[member_id] |
| 149 | + member.last_active = datetime.utcnow() |
| 150 | + member.activity_level = min(1.0, member.activity_level + 0.05) |
| 151 | + |
| 152 | + return activity |
| 153 | + |
| 154 | + def suggest_team_member_for_task(self, task_description: str, |
| 155 | + required_skills: List[str]) -> SmartSuggestion: |
| 156 | + """AI suggestion for who should be assigned to task.""" |
| 157 | + # Find members with matching skills |
| 158 | + candidates = [] |
| 159 | + for member in self.team_members.values(): |
| 160 | + skill_match = sum(1 for skill in required_skills if skill in member.skills) |
| 161 | + if skill_match > 0: |
| 162 | + candidates.append((member, skill_match)) |
| 163 | + |
| 164 | + # Sort by skill match and activity level |
| 165 | + candidates.sort(key=lambda x: (x[1], x[0].activity_level), reverse=True) |
| 166 | + |
| 167 | + suggested_members = [c[0].member_id for c in candidates[:3]] |
| 168 | + confidence = min(1.0, len(candidates) / max(len(required_skills), 1)) |
| 169 | + |
| 170 | + suggestion = SmartSuggestion( |
| 171 | + suggestion_type=SuggestionType.ASSIGNMENT, |
| 172 | + context=task_description, |
| 173 | + suggested_members=suggested_members, |
| 174 | + confidence=confidence, |
| 175 | + rationale=f"Based on skill match and current activity level" |
| 176 | + ) |
| 177 | + |
| 178 | + self.suggestions[suggestion.suggestion_id] = suggestion |
| 179 | + return suggestion |
| 180 | + |
| 181 | + def suggest_meeting_time(self, member_ids: List[str], |
| 182 | + duration_minutes: int = 60) -> SmartSuggestion: |
| 183 | + """AI suggestion for best meeting time.""" |
| 184 | + # Find common available slots |
| 185 | + available_slots = {} |
| 186 | + |
| 187 | + for hour in range(9, 18): # 9am to 6pm |
| 188 | + day_key = 'all_days' |
| 189 | + availability = sum( |
| 190 | + 1 for mid in member_ids |
| 191 | + if mid in self.team_members and hour in self.team_members[mid].availability.get(day_key, []) |
| 192 | + ) |
| 193 | + |
| 194 | + if availability == len(member_ids): |
| 195 | + available_slots[hour] = availability |
| 196 | + |
| 197 | + best_slot = max(available_slots.items(), key=lambda x: x[1])[0] if available_slots else 10 |
| 198 | + |
| 199 | + suggestion = SmartSuggestion( |
| 200 | + suggestion_type=SuggestionType.TIMING, |
| 201 | + context=f"Meeting for {len(member_ids)} members ({duration_minutes} min)", |
| 202 | + suggested_members=member_ids, |
| 203 | + confidence=0.85, |
| 204 | + rationale=f"Suggested time: {best_slot}:00 - all participants available" |
| 205 | + ) |
| 206 | + |
| 207 | + self.suggestions[suggestion.suggestion_id] = suggestion |
| 208 | + return suggestion |
| 209 | + |
| 210 | + def suggest_expert(self, topic: str, member_ids: List[str] = None) -> SmartSuggestion: |
| 211 | + """AI suggestion for expert on topic.""" |
| 212 | + if not member_ids: |
| 213 | + member_ids = list(self.team_members.keys()) |
| 214 | + |
| 215 | + topic_keywords = topic.lower().split() |
| 216 | + |
| 217 | + # Find expert by skill match |
| 218 | + candidates = [] |
| 219 | + for mid in member_ids: |
| 220 | + if mid not in self.team_members: |
| 221 | + continue |
| 222 | + |
| 223 | + member = self.team_members[mid] |
| 224 | + skill_match = sum(1 for keyword in topic_keywords if keyword in member.skills) |
| 225 | + |
| 226 | + if skill_match > 0: |
| 227 | + candidates.append((mid, skill_match)) |
| 228 | + |
| 229 | + candidates.sort(key=lambda x: x[1], reverse=True) |
| 230 | + suggested_members = [c[0] for c in candidates[:2]] |
| 231 | + confidence = min(1.0, (candidates[0][1] / len(topic_keywords)) if candidates else 0) |
| 232 | + |
| 233 | + suggestion = SmartSuggestion( |
| 234 | + suggestion_type=SuggestionType.EXPERTISE, |
| 235 | + context=f"Expert needed for: {topic}", |
| 236 | + suggested_members=suggested_members, |
| 237 | + confidence=confidence, |
| 238 | + rationale=f"Member(s) with relevant skills in {topic}" |
| 239 | + ) |
| 240 | + |
| 241 | + self.suggestions[suggestion.suggestion_id] = suggestion |
| 242 | + return suggestion |
| 243 | + |
| 244 | + def get_collaboration_insights(self, member_id: str = None) -> Dict: |
| 245 | + """Get collaboration insights.""" |
| 246 | + if member_id and member_id in self.team_members: |
| 247 | + # Individual insights |
| 248 | + member = self.team_members[member_id] |
| 249 | + member_activities = [a for a in self.activities if a.member_id == member_id] |
| 250 | + |
| 251 | + activity_by_type = {} |
| 252 | + for activity in member_activities: |
| 253 | + atype = activity.activity_type.value |
| 254 | + activity_by_type[atype] = activity_by_type.get(atype, 0) + 1 |
| 255 | + |
| 256 | + return { |
| 257 | + 'member_id': member_id, |
| 258 | + 'name': member.name, |
| 259 | + 'activity_count': len(member_activities), |
| 260 | + 'activity_by_type': activity_by_type, |
| 261 | + 'activity_level': member.activity_level, |
| 262 | + 'last_active': member.last_active.isoformat() |
| 263 | + } |
| 264 | + else: |
| 265 | + # Team insights |
| 266 | + activities_by_type = {} |
| 267 | + for activity in self.activities: |
| 268 | + atype = activity.activity_type.value |
| 269 | + activities_by_type[atype] = activities_by_type.get(atype, 0) + 1 |
| 270 | + |
| 271 | + # Calculate collaboration score |
| 272 | + team_members_active = sum(1 for m in self.team_members.values() if m.activity_level > 0.3) |
| 273 | + collaboration_score = (team_members_active / max(len(self.team_members), 1)) * 100 |
| 274 | + |
| 275 | + return { |
| 276 | + 'total_members': len(self.team_members), |
| 277 | + 'total_activities': self.team_metrics['total_activities'], |
| 278 | + 'activities_by_type': activities_by_type, |
| 279 | + 'active_members': team_members_active, |
| 280 | + 'collaboration_score': round(collaboration_score, 1), |
| 281 | + 'recent_activities': [a.to_dict() for a in self.activities[-10:]] |
| 282 | + } |
| 283 | + |
| 284 | + def get_team_stats(self) -> Dict: |
| 285 | + """Get team statistics.""" |
| 286 | + return { |
| 287 | + 'total_members': len(self.team_members), |
| 288 | + 'total_activities': self.team_metrics['total_activities'], |
| 289 | + 'total_suggestions': len(self.suggestions), |
| 290 | + 'accepted_suggestions': sum(1 for s in self.suggestions.values() if s.accepted), |
| 291 | + 'team_roles': list(set(m.role for m in self.team_members.values())), |
| 292 | + 'average_activity_level': round( |
| 293 | + sum(m.activity_level for m in self.team_members.values()) / max(len(self.team_members), 1), 2 |
| 294 | + ) |
| 295 | + } |
| 296 | + |
| 297 | + def accept_suggestion(self, suggestion_id: str) -> bool: |
| 298 | + """Mark suggestion as accepted.""" |
| 299 | + if suggestion_id in self.suggestions: |
| 300 | + self.suggestions[suggestion_id].accepted = True |
| 301 | + return True |
| 302 | + return False |
| 303 | + |
| 304 | + def get_member_activity_timeline(self, member_id: str, |
| 305 | + days: int = 7) -> List[Dict]: |
| 306 | + """Get member activity timeline.""" |
| 307 | + cutoff_date = datetime.utcnow() - timedelta(days=days) |
| 308 | + activities = [a for a in self.activities |
| 309 | + if a.member_id == member_id and a.timestamp > cutoff_date] |
| 310 | + |
| 311 | + return [a.to_dict() for a in activities] |
| 312 | + |
| 313 | + |
| 314 | +# Global team collaboration AI instance |
| 315 | +team_collab = TeamCollaborationAI() |
0 commit comments