furry/recommendations/models.py

308 lines
12 KiB
Python

"""
Recommendation Engine Models für ML-basierte Empfehlungen
"""
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from products.models import Product
from auction.models import Auction
import uuid
class UserBehavior(models.Model):
"""User Behavior Tracking für Empfehlungen"""
BEHAVIOR_TYPE_CHOICES = [
('view', 'Produkt angesehen'),
('cart_add', 'Zum Warenkorb hinzugefügt'),
('cart_remove', 'Aus Warenkorb entfernt'),
('purchase', 'Gekauft'),
('wishlist_add', 'Zur Wunschliste hinzugefügt'),
('wishlist_remove', 'Von Wunschliste entfernt'),
('search', 'Gesucht'),
('auction_bid', 'Bei Auktion geboten'),
('auction_watch', 'Auktion beobachtet'),
('review', 'Bewertung abgegeben'),
('share', 'Geteilt'),
('like', 'Geliked'),
('dislike', 'Nicht gemocht'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='behaviors')
behavior_type = models.CharField(max_length=20, choices=BEHAVIOR_TYPE_CHOICES)
product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True, blank=True, related_name='behaviors')
auction = models.ForeignKey(Auction, on_delete=models.CASCADE, null=True, blank=True, related_name='behaviors')
session_id = models.CharField(max_length=100, blank=True)
ip_address = models.GenericIPAddressField(null=True, blank=True)
user_agent = models.TextField(blank=True)
metadata = models.JSONField(default=dict) # Additional data
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
verbose_name = 'User Behavior'
verbose_name_plural = 'User Behaviors'
indexes = [
models.Index(fields=['user', 'behavior_type', 'created_at']),
models.Index(fields=['product', 'behavior_type', 'created_at']),
]
def __str__(self):
return f"{self.user.username} - {self.get_behavior_type_display()}"
class UserProfile(models.Model):
"""User Profile für Personalisierung"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='recommendation_profile')
# Preferences
preferred_categories = models.JSONField(default=list)
preferred_fursuit_types = models.JSONField(default=list)
preferred_price_range = models.JSONField(default=dict)
preferred_colors = models.JSONField(default=list)
# Behavior patterns
avg_session_duration = models.FloatField(default=0) # in minutes
total_purchases = models.IntegerField(default=0)
total_views = models.IntegerField(default=0)
total_searches = models.IntegerField(default=0)
# Engagement metrics
last_active = models.DateTimeField(auto_now=True)
engagement_score = models.FloatField(default=0) # 0-100
loyalty_level = models.CharField(max_length=20, choices=[
('new', 'Neu'),
('regular', 'Regulär'),
('loyal', 'Loyal'),
('vip', 'VIP'),
], default='new')
# ML features
feature_vector = models.JSONField(default=dict) # ML feature vector
last_updated = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'User Profile'
verbose_name_plural = 'User Profiles'
def __str__(self):
return f"Profile für {self.user.username}"
def update_engagement_score(self):
"""Engagement Score basierend auf Verhalten aktualisieren"""
behaviors = self.user.behaviors.all()
# Calculate engagement score
view_weight = 1
cart_weight = 3
purchase_weight = 10
search_weight = 2
total_score = 0
total_actions = 0
for behavior in behaviors:
if behavior.behavior_type == 'view':
total_score += view_weight
elif behavior.behavior_type == 'cart_add':
total_score += cart_weight
elif behavior.behavior_type == 'purchase':
total_score += purchase_weight
elif behavior.behavior_type == 'search':
total_score += search_weight
total_actions += 1
if total_actions > 0:
self.engagement_score = min(100, (total_score / total_actions) * 10)
# Update loyalty level
if self.engagement_score >= 80:
self.loyalty_level = 'vip'
elif self.engagement_score >= 60:
self.loyalty_level = 'loyal'
elif self.engagement_score >= 30:
self.loyalty_level = 'regular'
else:
self.loyalty_level = 'new'
self.save()
class ProductSimilarity(models.Model):
"""Produkt-Ähnlichkeitsmatrix für Collaborative Filtering"""
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='similarities')
similar_product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='similar_to')
similarity_score = models.FloatField() # 0-1
similarity_type = models.CharField(max_length=20, choices=[
('category', 'Kategorie'),
('fursuit_type', 'Fursuit Typ'),
('price_range', 'Preisbereich'),
('color', 'Farbe'),
('collaborative', 'Collaborative'),
('content_based', 'Content-based'),
])
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ['product', 'similar_product']
ordering = ['-similarity_score']
verbose_name = 'Product Similarity'
verbose_name_plural = 'Product Similarities'
def __str__(self):
return f"{self.product.name} ~ {self.similar_product.name} ({self.similarity_score:.2f})"
class Recommendation(models.Model):
"""Generierte Empfehlungen für User"""
RECOMMENDATION_TYPE_CHOICES = [
('collaborative', 'Collaborative Filtering'),
('content_based', 'Content-based Filtering'),
('popular', 'Beliebte Produkte'),
('trending', 'Trending'),
('personalized', 'Personalisiert'),
('similar', 'Ähnliche Produkte'),
('frequently_bought', 'Häufig zusammen gekauft'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='recommendations')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='recommendations')
recommendation_type = models.CharField(max_length=20, choices=RECOMMENDATION_TYPE_CHOICES)
confidence_score = models.FloatField() # 0-1
reason = models.CharField(max_length=200, blank=True)
is_clicked = models.BooleanField(default=False)
is_purchased = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ['-confidence_score', '-created_at']
verbose_name = 'Recommendation'
verbose_name_plural = 'Recommendations'
indexes = [
models.Index(fields=['user', 'recommendation_type', 'created_at']),
models.Index(fields=['product', 'recommendation_type', 'created_at']),
]
def __str__(self):
return f"{self.user.username} - {self.product.name} ({self.get_recommendation_type_display()})"
class RecommendationModel(models.Model):
"""ML Model für Empfehlungen"""
MODEL_TYPE_CHOICES = [
('collaborative', 'Collaborative Filtering'),
('content_based', 'Content-based Filtering'),
('hybrid', 'Hybrid Model'),
('neural_network', 'Neural Network'),
('matrix_factorization', 'Matrix Factorization'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
model_type = models.CharField(max_length=20, choices=MODEL_TYPE_CHOICES)
model_name = models.CharField(max_length=100)
model_version = models.CharField(max_length=20)
model_file = models.FileField(upload_to='recommendation_models/', null=True, blank=True)
model_parameters = models.JSONField(default=dict)
training_data_size = models.IntegerField(default=0)
accuracy_score = models.FloatField(default=0)
precision_score = models.FloatField(default=0)
recall_score = models.FloatField(default=0)
f1_score = models.FloatField(default=0)
is_active = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
trained_at = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ['-created_at']
verbose_name = 'Recommendation Model'
verbose_name_plural = 'Recommendation Models'
def __str__(self):
return f"{self.model_name} v{self.model_version} ({self.get_model_type_display()})"
class ABTest(models.Model):
"""A/B Testing für Empfehlungen"""
TEST_STATUS_CHOICES = [
('draft', 'Entwurf'),
('running', 'Läuft'),
('paused', 'Pausiert'),
('completed', 'Abgeschlossen'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
test_name = models.CharField(max_length=100)
description = models.TextField()
test_type = models.CharField(max_length=50)
variant_a = models.JSONField() # Control group
variant_b = models.JSONField() # Test group
traffic_split = models.FloatField(default=0.5) # % für Variante B
status = models.CharField(max_length=20, choices=TEST_STATUS_CHOICES, default='draft')
start_date = models.DateTimeField(null=True, blank=True)
end_date = models.DateTimeField(null=True, blank=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='ab_tests')
created_at = models.DateTimeField(auto_now_add=True)
# Results
variant_a_conversion = models.FloatField(default=0)
variant_b_conversion = models.FloatField(default=0)
statistical_significance = models.FloatField(default=0)
winner = models.CharField(max_length=10, choices=[
('a', 'Variant A'),
('b', 'Variant B'),
('none', 'Kein Gewinner'),
], blank=True)
class Meta:
ordering = ['-created_at']
verbose_name = 'A/B Test'
verbose_name_plural = 'A/B Tests'
def __str__(self):
return f"{self.test_name} ({self.get_status_display()})"
class RecommendationAnalytics(models.Model):
"""Analytics für Empfehlungen"""
date = models.DateField()
total_recommendations = models.IntegerField(default=0)
total_clicks = models.IntegerField(default=0)
total_purchases = models.IntegerField(default=0)
click_through_rate = models.FloatField(default=0)
conversion_rate = models.FloatField(default=0)
revenue_from_recommendations = models.DecimalField(max_digits=10, decimal_places=2, default=0)
avg_recommendation_score = models.FloatField(default=0)
# Per recommendation type
collaborative_recommendations = models.IntegerField(default=0)
content_based_recommendations = models.IntegerField(default=0)
popular_recommendations = models.IntegerField(default=0)
personalized_recommendations = models.IntegerField(default=0)
class Meta:
unique_together = ['date']
ordering = ['-date']
verbose_name = 'Recommendation Analytics'
verbose_name_plural = 'Recommendation Analytics'
def __str__(self):
return f"Recommendation Analytics - {self.date}"
@classmethod
def get_or_create_today(cls):
"""Heutige Analytics erstellen oder abrufen"""
today = timezone.now().date()
analytics, created = cls.objects.get_or_create(date=today)
return analytics