""" 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