from django.db import models from django.contrib.auth import get_user_model from django.utils import timezone from at_django_boilerplate.utils.mixins import UUIDMixin class SessionConsentModel(UUIDMixin): session_id = models.CharField(max_length=255, unique=True) accept_all_consent_received = models.BooleanField(default=False) essential_only_consent_received = models.BooleanField(default=False) received_on = models.DateTimeField(auto_now_add=True) @property def consent_received(self): return self.accept_all_consent_received or self.essential_only_consent_received class VisitorModel(UUIDMixin): visiter_number = models.IntegerField() session_id = models.CharField(max_length=255) def __str__(self): return f'#{self.visiter_number}' class PageVisit(UUIDMixin): user = models.ForeignKey(get_user_model(), on_delete=models.SET_NULL, null=True, blank=True) session_id = models.CharField(max_length=255) visitor = models.ForeignKey(VisitorModel,related_name='page_visits',null=True,blank=True ,on_delete=models.SET_NULL) url = models.CharField(max_length=255) ip_address = models.CharField(max_length=45) # Supports IPv4 and IPv6 query_params = models.TextField(blank=True, null=True) timestamp = models.DateTimeField(default=timezone.now) path = models.CharField(max_length=512) user_agent = models.TextField(blank=True, null=True) session_data = models.JSONField(default=dict, blank=True, null=True) # Keep this field location = models.CharField(max_length=64,null=True,blank=True) country = models.CharField(max_length=64,null=True,blank=True) latitude=models.CharField(max_length=64,null=True,blank=True) longitude=models.CharField(max_length=64,null=True,blank=True) is_new_user = models.BooleanField(default=True) referrer = models.CharField(max_length=512, blank=True, null=True) visit_count = models.PositiveIntegerField(default=1,null=True,blank=True) entered_at = models.DateTimeField(auto_now_add=True,null=True,blank=True) exited_at = models.DateTimeField(null=True, blank=True) time_spent_seconds = models.PositiveIntegerField(default=0,null=True,blank=True) class Meta: ordering = ['-timestamp'] indexes = [ models.Index(fields=['session_id', 'timestamp']), ] def __str__(self): return f"{self.user.email if self.user else 'Anonymous'} visited {self.url} at {self.timestamp}" def save(self, *args, **kwargs): # Detect if this is a new user if self.user and PageVisit.objects.filter(user=self.user).exists(): self.is_new_user = False super().save(*args, **kwargs) def calculate_time_spent(self): if self.exited_at: self.time_spent_seconds = int( (self.exited_at - self.entered_at).total_seconds() ) class DailyUserStats(UUIDMixin): date = models.DateField(unique=True,null=True,blank=True) new_users = models.PositiveIntegerField(null=True,blank=True) returning_users = models.PositiveIntegerField(null=True,blank=True) created_at = models.DateTimeField(auto_now_add=True,null=True,blank=True) class VisitorSession(UUIDMixin): session_id = models.CharField(max_length=255, unique=True,null=True,blank=True) visitor = models.ForeignKey(VisitorModel, on_delete=models.SET_NULL, null=True, blank=True) is_bounce = models.BooleanField(default=False,null=True,blank=True) started_at = models.DateTimeField(auto_now_add=True,null=True,blank=True) ended_at = models.DateTimeField(null=True, blank=True) duration_seconds = models.PositiveIntegerField(default=0,null=True,blank=True) def calculate_duration(self): if self.ended_at: self.duration_seconds = int( (self.ended_at - self.started_at).total_seconds() ) class ClickEvent(UUIDMixin): session = models.ForeignKey(VisitorSession, on_delete=models.SET_NULL, related_name="clicks",null=True,blank=True) visitor = models.ForeignKey(VisitorModel, on_delete=models.SET_NULL, null=True, blank=True) page_path = models.CharField(max_length=512,null=True,blank=True) element_text = models.CharField(max_length=255,null=True,blank=True) element_type = models.CharField(max_length=50,null=True,blank=True) timestamp = models.DateTimeField(auto_now_add=True,null=True,blank=True)