38531-vm/core/models.py
2026-02-17 18:44:08 +00:00

365 lines
14 KiB
Python

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')