from django.db import models from django.contrib.auth.models import User class Intent(models.Model): name = models.CharField(max_length=100) icon = models.CharField(max_length=50, blank=True, help_text="Bootstrap icon class name") def __str__(self): return str(self.name) class ValueTag(models.Model): name = models.CharField(max_length=100) def __str__(self): return str(self.name) class Game(models.Model): GENRE_CHOICES = [ ('fps', 'FPS'), ('moba', 'MOBA'), ('battle_royale', 'Battle Royale'), ('sports', 'Sports'), ('mmo', 'MMO'), ('fighting', 'Fighting'), ('rpg', 'RPG'), ('strategy', 'Strategy'), ('other', 'Other'), ] name = models.CharField(max_length=255, unique=True) slug = models.SlugField(max_length=255, unique=True) genre = models.CharField(max_length=50, choices=GENRE_CHOICES) team_size = models.PositiveIntegerField(null=True, blank=True) has_roles = models.BooleanField(default=False) roles_json = models.JSONField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return str(self.name) class Profile(models.Model): PLATFORM_CHOICES = [ ('pc', 'PC'), ('playstation', 'PlayStation'), ('xbox', 'Xbox'), ('nintendo', 'Nintendo Switch'), ('mobile', 'Mobile'), ] TRANSITION_CHOICES = [ ('none', 'Stable'), ('post-divorce', 'Post-Divorce'), ('relocating', 'Relocating'), ('career-change', 'Career Change'), ('new-in-town', 'New in Town'), ] user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') professional_headline = models.CharField(max_length=255, blank=True) transition_status = models.CharField(max_length=50, choices=TRANSITION_CHOICES, default='none') bio = models.TextField(blank=True) location_city = models.CharField(max_length=100, blank=True) intents = models.ManyToManyField(Intent, blank=True) value_tags = models.ManyToManyField(ValueTag, blank=True) avatar = models.ImageField(upload_to='avatars/', blank=True, null=True) # Gaming Fields primary_game = models.ForeignKey(Game, on_delete=models.SET_NULL, null=True, blank=False, related_name='primary_players') secondary_games = models.ManyToManyField(Game, blank=True, related_name='secondary_players') platform = models.CharField(max_length=20, choices=PLATFORM_CHOICES, blank=False) gamer_tag = models.CharField(max_length=100, blank=False) rank = models.CharField(max_length=100, blank=True) preferred_role = models.CharField(max_length=100, blank=True) # Momentum & Engagement accountability_streak = models.IntegerField(default=0) # Auth & Security is_email_verified = models.BooleanField(default=False) two_factor_enabled = models.BooleanField(default=False) onboarding_completed = models.BooleanField(default=False) # Multitenancy (Future Proofing) organization_id = models.IntegerField(null=True, blank=True) @property def get_avatar_url(self): if self.avatar and hasattr(self.avatar, 'url'): return self.avatar.url return f"https://i.pravatar.cc/150?u={self.user.username}" @property def connection_count(self): return Match.objects.filter(models.Q(user_a=self.user) | models.Q(user_b=self.user)).count() @property def following_count(self): return Follow.objects.filter(follower=self.user).count() @property def followers_count(self): return Follow.objects.filter(followed=self.user).count() @property def events_attended_count(self): return self.user.rsvps.filter(status='going').count() @property def profile_completion_percentage(self): steps = 0 total_steps = 6 if self.gamer_tag: steps += 1 if self.bio: steps += 1 if self.location_city: steps += 1 if self.intents.exists(): steps += 1 if self.avatar: steps += 1 if self.primary_game: steps += 1 return int((steps / total_steps) * 100) def __str__(self): return f"{self.user.username}'s Profile" class Connection(models.Model): user1 = models.ForeignKey(User, on_delete=models.CASCADE, related_name='connections1') user2 = models.ForeignKey(User, on_delete=models.CASCADE, related_name='connections2') created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('user1', 'user2') def __str__(self): return f"{self.user1.username} <-> {self.user2.username}" class Follow(models.Model): follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name='following_relations') followed = models.ForeignKey(User, on_delete=models.CASCADE, related_name='follower_relations') created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('follower', 'followed') def __str__(self): return f"{self.follower.username} follows {self.followed.username}" class Group(models.Model): name = models.CharField(max_length=255) description = models.TextField() is_private = models.BooleanField(default=False) moderators = models.ManyToManyField(User, related_name='moderated_groups') members = models.ManyToManyField(User, related_name='joined_groups') organization_id = models.IntegerField(null=True, blank=True) def __str__(self): return str(self.name) class EventTag(models.Model): name = models.CharField(max_length=50, unique=True) def __str__(self): return str(self.name) class Event(models.Model): VISIBILITY_CHOICES = [ ('public', 'Public (Members Only)'), ('group', 'Group Only'), ('invite', 'Invite Only'), ] creator = models.ForeignKey(User, on_delete=models.CASCADE, related_name='created_events') title = models.CharField(max_length=255) description = models.TextField() start_datetime = models.DateTimeField() end_datetime = models.DateTimeField(null=True, blank=True) timezone = models.CharField(max_length=50, default='UTC') location_name = models.CharField(max_length=255) location_address = models.CharField(max_length=255, blank=True) city = models.CharField(max_length=100, blank=True) state = models.CharField(max_length=100, blank=True) is_online = models.BooleanField(default=False) online_url = models.URLField(blank=True) visibility = models.CharField(max_length=20, choices=VISIBILITY_CHOICES, default='public') group = models.ForeignKey(Group, on_delete=models.SET_NULL, null=True, blank=True, related_name='group_events') capacity = models.PositiveIntegerField(null=True, blank=True) cover_image = models.ImageField(upload_to='events/', blank=True, null=True) tags = models.ManyToManyField(EventTag, blank=True) created_at = models.DateTimeField(auto_now_add=True, null=True) updated_at = models.DateTimeField(auto_now=True, null=True) def __str__(self): return str(self.title) @property def rsvp_count(self): return self.rsvps.filter(status='going').count() class RSVP(models.Model): STATUS_CHOICES = [ ('going', 'Going'), ('maybe', 'Maybe'), ('not_going', 'Not Going'), ] event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='rsvps') user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='rsvps') status = models.CharField(max_length=20, choices=STATUS_CHOICES) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: unique_together = ('event', 'user') def __str__(self): return f"{self.user.username} - {self.event.title} ({self.status})" class EventInvite(models.Model): STATUS_CHOICES = [ ('pending', 'Pending'), ('accepted', 'Accepted'), ('declined', 'Declined'), ] event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='invites') from_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='invites_sent') to_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='invites_received') status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"Invite: {self.event.title} to {self.to_user.username}" class Report(models.Model): reporter = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reports_made') reported_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reports_received', null=True, blank=True) content = models.TextField() timestamp = models.DateTimeField(auto_now_add=True) resolved = models.BooleanField(default=False) organization_id = models.IntegerField(null=True, blank=True) def __str__(self): return f"Report by {self.reporter.username} at {self.timestamp}" class Thread(models.Model): participants = models.ManyToManyField(User, related_name='threads') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return f"Thread with {self.participants.count()} participants" class Message(models.Model): thread = models.ForeignKey(Thread, on_delete=models.CASCADE, related_name='messages', null=True, blank=True) sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sent_messages') recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name='received_messages', null=True, blank=True) body = models.TextField() timestamp = models.DateTimeField(auto_now_add=True) is_read = models.BooleanField(default=False) class Meta: ordering = ['timestamp'] def __str__(self): return f"From {self.sender.username} at {self.timestamp}" class Like(models.Model): from_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='likes_given') to_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='likes_received') created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('from_user', 'to_user') class ConnectionRequest(models.Model): STATUS_CHOICES = [ ('pending', 'Pending'), ('accepted', 'Accepted'), ('declined', 'Declined'), ('canceled', 'Canceled'), ('blocked', 'Blocked'), ] from_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='requests_sent') to_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='requests_received') status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') created_at = models.DateTimeField(auto_now_add=True) responded_at = models.DateTimeField(null=True, blank=True) class Meta: unique_together = ('from_user', 'to_user') class Match(models.Model): STATUS_CHOICES = [ ('active', 'Active'), ('archived', 'Archived'), ] user_a = models.ForeignKey(User, on_delete=models.CASCADE, related_name='matches_a') user_b = models.ForeignKey(User, on_delete=models.CASCADE, related_name='matches_b') created_at = models.DateTimeField(auto_now_add=True) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active') class Meta: unique_together = ('user_a', 'user_b') class Block(models.Model): blocker = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blocks_given') blocked = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blocks_received') created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('blocker', 'blocked') class Post(models.Model): POST_TYPE_CHOICES = [ ('reflection', 'Reflection'), ('looking_for', 'Looking For'), ('offering', 'Offering'), ('event_invite', 'Event Invite'), ('progress_update', 'Progress Update'), ('skill_share', 'Skill Share'), ] author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts') content = models.TextField() image = models.ImageField(upload_to='posts/', blank=True, null=True) post_type = models.CharField(max_length=20, choices=POST_TYPE_CHOICES, default='reflection') timestamp = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-timestamp'] def __str__(self): return f"Post by {self.author.username} at {self.timestamp}" def user_has_reacted(self, user, reaction_type='heart'): if user.is_authenticated: return self.reactions.filter(user=user, reaction_type=reaction_type).exists() return False class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments') author = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField() timestamp = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['timestamp'] def __str__(self): return f"Comment by {self.author.username} on {self.post}" class Reaction(models.Model): REACTION_CHOICES = [ ('heart', 'Heart'), ('like', 'Like'), ('smile', 'Smile'), ] post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='reactions') user = models.ForeignKey(User, on_delete=models.CASCADE) reaction_type = models.CharField(max_length=20, choices=REACTION_CHOICES, default='heart') class Meta: unique_together = ('post', 'user', 'reaction_type') def __str__(self): return f"{self.user.username} {self.reaction_type}ed {self.post}" class HiddenPost(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='hidden_posts') post = models.ForeignKey(Post, on_delete=models.CASCADE) class Meta: unique_together = ('user', 'post')