mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 12:31:22 -05:00
update
This commit is contained in:
125
apps/core/analytics.py
Normal file
125
apps/core/analytics.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils import timezone
|
||||
from django.db.models import Count
|
||||
from datetime import timedelta
|
||||
import pghistory
|
||||
|
||||
|
||||
@pghistory.track()
|
||||
class PageView(models.Model):
|
||||
content_type = models.ForeignKey(
|
||||
ContentType, on_delete=models.CASCADE, related_name="page_views"
|
||||
)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey("content_type", "object_id")
|
||||
|
||||
timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||
ip_address = models.GenericIPAddressField()
|
||||
user_agent = models.CharField(max_length=512, blank=True)
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=["timestamp"]),
|
||||
models.Index(fields=["content_type", "object_id"]),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_trending_items(cls, model_class, hours=168, limit=10):
|
||||
"""Get trending items of a specific model class based on views in last X hours.
|
||||
|
||||
Args:
|
||||
model_class: The model class to get trending items for (e.g., Park, Ride)
|
||||
hours (int): Number of hours to look back for views (default: 168 = 7 days)
|
||||
limit (int): Maximum number of items to return (default: 10)
|
||||
|
||||
Returns:
|
||||
QuerySet: The trending items ordered by view count
|
||||
"""
|
||||
content_type = ContentType.objects.get_for_model(model_class)
|
||||
cutoff = timezone.now() - timedelta(hours=hours)
|
||||
|
||||
# Query through the ContentType relationship
|
||||
item_ids = (
|
||||
cls.objects.filter(content_type=content_type, timestamp__gte=cutoff)
|
||||
.values("object_id")
|
||||
.annotate(view_count=Count("id"))
|
||||
.filter(view_count__gt=0)
|
||||
.order_by("-view_count")
|
||||
.values_list("object_id", flat=True)[:limit]
|
||||
)
|
||||
|
||||
# Get the actual items in the correct order
|
||||
if item_ids:
|
||||
# Convert the list to a string of comma-separated values
|
||||
id_list = list(item_ids)
|
||||
# Use Case/When to preserve the ordering
|
||||
from django.db.models import Case, When
|
||||
|
||||
preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(id_list)])
|
||||
return model_class.objects.filter(pk__in=id_list).order_by(preserved)
|
||||
|
||||
return model_class.objects.none()
|
||||
|
||||
@classmethod
|
||||
def get_views_growth(
|
||||
cls, content_type, object_id, current_period_hours, previous_period_hours
|
||||
):
|
||||
"""Get view growth statistics between two time periods.
|
||||
|
||||
Args:
|
||||
content_type: ContentType instance for the model
|
||||
object_id: ID of the specific object
|
||||
current_period_hours: Hours for current period (e.g., 24)
|
||||
previous_period_hours: Hours for previous period (e.g., 48)
|
||||
|
||||
Returns:
|
||||
tuple: (current_views, previous_views, growth_percentage)
|
||||
"""
|
||||
from datetime import timedelta
|
||||
|
||||
now = timezone.now()
|
||||
|
||||
# Current period: last X hours
|
||||
current_start = now - timedelta(hours=current_period_hours)
|
||||
current_views = cls.objects.filter(
|
||||
content_type=content_type, object_id=object_id, timestamp__gte=current_start
|
||||
).count()
|
||||
|
||||
# Previous period: X hours before current period
|
||||
previous_start = now - timedelta(hours=previous_period_hours)
|
||||
previous_end = current_start
|
||||
previous_views = cls.objects.filter(
|
||||
content_type=content_type,
|
||||
object_id=object_id,
|
||||
timestamp__gte=previous_start,
|
||||
timestamp__lt=previous_end,
|
||||
).count()
|
||||
|
||||
# Calculate growth percentage
|
||||
if previous_views == 0:
|
||||
growth_percentage = current_views * 100 if current_views > 0 else 0
|
||||
else:
|
||||
growth_percentage = (
|
||||
(current_views - previous_views) / previous_views
|
||||
) * 100
|
||||
|
||||
return current_views, previous_views, growth_percentage
|
||||
|
||||
@classmethod
|
||||
def get_total_views_count(cls, content_type, object_id, hours=168):
|
||||
"""Get total view count for an object within specified hours.
|
||||
|
||||
Args:
|
||||
content_type: ContentType instance for the model
|
||||
object_id: ID of the specific object
|
||||
hours: Number of hours to look back (default: 168 = 7 days)
|
||||
|
||||
Returns:
|
||||
int: Total view count
|
||||
"""
|
||||
cutoff = timezone.now() - timedelta(hours=hours)
|
||||
return cls.objects.filter(
|
||||
content_type=content_type, object_id=object_id, timestamp__gte=cutoff
|
||||
).count()
|
||||
Reference in New Issue
Block a user