""" Recommendation Service für ML-basierte Empfehlungen """ import numpy as np from django.db.models import Q, Count, Avg from django.utils import timezone from datetime import timedelta from .models import UserBehavior, UserProfile, ProductSimilarity, Recommendation, RecommendationModel from products.models import Product import json class RecommendationService: """Service für ML-basierte Empfehlungen""" def __init__(self): self.collaborative_weight = 0.4 self.content_based_weight = 0.3 self.popular_weight = 0.2 self.trending_weight = 0.1 def get_recommendations_for_user(self, user, limit=10): """Personalisiertes Empfehlungen für User""" recommendations = [] # Get user profile profile, created = UserProfile.objects.get_or_create(user=user) # 1. Collaborative Filtering collaborative_recs = self._get_collaborative_recommendations(user, limit=limit//2) recommendations.extend(collaborative_recs) # 2. Content-based Filtering content_recs = self._get_content_based_recommendations(user, profile, limit=limit//2) recommendations.extend(content_recs) # 3. Popular Products (fallback) if len(recommendations) < limit: popular_recs = self._get_popular_recommendations(limit=limit-len(recommendations)) recommendations.extend(popular_recs) # 4. Trending Products (fallback) if len(recommendations) < limit: trending_recs = self._get_trending_recommendations(limit=limit-len(recommendations)) recommendations.extend(trending_recs) # Remove duplicates and sort by confidence unique_recs = {} for rec in recommendations: product_id = rec['product']['id'] if product_id not in unique_recs or rec['confidence_score'] > unique_recs[product_id]['confidence_score']: unique_recs[product_id] = rec # Sort by confidence score final_recommendations = sorted( unique_recs.values(), key=lambda x: x['confidence_score'], reverse=True )[:limit] # Save recommendations to database self._save_recommendations(user, final_recommendations) return final_recommendations def _get_collaborative_recommendations(self, user, limit=5): """Collaborative Filtering basierend auf ähnlichen Usern""" recommendations = [] # Get users with similar behavior similar_users = self._find_similar_users(user) # Get products liked by similar users for similar_user in similar_users[:5]: liked_products = UserBehavior.objects.filter( user=similar_user, behavior_type__in=['purchase', 'cart_add', 'wishlist_add'] ).values_list('product', flat=True).distinct() for product_id in liked_products: product = Product.objects.filter(id=product_id, is_active=True).first() if product and not self._user_has_interacted(user, product): recommendations.append({ 'product': { 'id': product.id, 'name': product.name, 'price': float(product.price), 'image_url': product.image.url if product.image else None, 'category': product.category, }, 'confidence_score': self.collaborative_weight, 'recommendation_type': 'collaborative', 'reason': f'Ähnlich zu {similar_user.username}' }) if len(recommendations) >= limit: break return recommendations def _get_content_based_recommendations(self, user, profile, limit=5): """Content-based Filtering basierend auf User Preferences""" recommendations = [] # Get user preferences preferred_categories = profile.preferred_categories preferred_fursuit_types = profile.preferred_fursuit_types preferred_price_range = profile.preferred_price_range # Build query based on preferences query = Q(is_active=True) if preferred_categories: query &= Q(category__in=preferred_categories) if preferred_fursuit_types: query &= Q(fursuit_type__in=preferred_fursuit_types) if preferred_price_range: min_price = preferred_price_range.get('min', 0) max_price = preferred_price_range.get('max', float('inf')) query &= Q(price__gte=min_price, price__lte=max_price) # Get products matching preferences matching_products = Product.objects.filter(query).exclude( behaviors__user=user ).distinct()[:limit*2] for product in matching_products: confidence = self._calculate_content_based_confidence(product, profile) recommendations.append({ 'product': { 'id': product.id, 'name': product.name, 'price': float(product.price), 'image_url': product.image.url if product.image else None, 'category': product.category, }, 'confidence_score': confidence, 'recommendation_type': 'content_based', 'reason': 'Basierend auf deinen Präferenzen' }) # Sort by confidence and return top results recommendations.sort(key=lambda x: x['confidence_score'], reverse=True) return recommendations[:limit] def _get_popular_recommendations(self, limit=5): """Beliebte Produkte basierend auf Verhalten""" recommendations = [] # Get popular products popular_products = Product.objects.annotate( purchase_count=Count('behaviors', filter=Q(behaviors__behavior_type='purchase')), view_count=Count('behaviors', filter=Q(behaviors__behavior_type='view')), ).filter( is_active=True, purchase_count__gt=0 ).order_by('-purchase_count', '-view_count')[:limit] for product in popular_products: popularity_score = (product.purchase_count * 2 + product.view_count) / 100 confidence = min(0.8, popularity_score) recommendations.append({ 'product': { 'id': product.id, 'name': product.name, 'price': float(product.price), 'image_url': product.image.url if product.image else None, 'category': product.category, }, 'confidence_score': confidence, 'recommendation_type': 'popular', 'reason': 'Beliebt bei anderen Kunden' }) return recommendations def _get_trending_recommendations(self, limit=5): """Trending Produkte (recent activity)""" recommendations = [] # Get recent activity (last 7 days) recent_date = timezone.now() - timedelta(days=7) trending_products = Product.objects.annotate( recent_purchases=Count('behaviors', filter=Q( behaviors__behavior_type='purchase', behaviors__created_at__gte=recent_date )), recent_views=Count('behaviors', filter=Q( behaviors__behavior_type='view', behaviors__created_at__gte=recent_date )), ).filter( is_active=True, recent_purchases__gt=0 ).order_by('-recent_purchases', '-recent_views')[:limit] for product in trending_products: trending_score = (product.recent_purchases * 3 + product.recent_views) / 50 confidence = min(0.7, trending_score) recommendations.append({ 'product': { 'id': product.id, 'name': product.name, 'price': float(product.price), 'image_url': product.image.url if product.image else None, 'category': product.category, }, 'confidence_score': confidence, 'recommendation_type': 'trending', 'reason': 'Trending diese Woche' }) return recommendations def _find_similar_users(self, user, limit=10): """Finde ähnliche User basierend auf Verhalten""" # Get user's behavior user_behaviors = UserBehavior.objects.filter(user=user).values_list('product', 'behavior_type') if not user_behaviors: return [] # Find users with similar behavior similar_users = [] for behavior in user_behaviors: product_id, behavior_type = behavior # Find users who had similar behavior with this product similar_behaviors = UserBehavior.objects.filter( product_id=product_id, behavior_type=behavior_type ).exclude(user=user).values_list('user', flat=True) similar_users.extend(similar_behaviors) # Count occurrences and get top users from collections import Counter user_counts = Counter(similar_users) return [user_id for user_id, count in user_counts.most_common(limit)] def _user_has_interacted(self, user, product): """Prüfe ob User bereits mit Produkt interagiert hat""" return UserBehavior.objects.filter( user=user, product=product, behavior_type__in=['purchase', 'cart_add', 'wishlist_add', 'view'] ).exists() def _calculate_content_based_confidence(self, product, profile): """Berechne Confidence Score für Content-based Filtering""" confidence = 0.5 # Base confidence # Category match if product.category in profile.preferred_categories: confidence += 0.2 # Fursuit type match if product.fursuit_type in profile.preferred_fursuit_types: confidence += 0.2 # Price range match if profile.preferred_price_range: min_price = profile.preferred_price_range.get('min', 0) max_price = profile.preferred_price_range.get('max', float('inf')) if min_price <= product.price <= max_price: confidence += 0.1 return min(0.9, confidence) def _save_recommendations(self, user, recommendations): """Speichere Empfehlungen in Datenbank""" # Delete old recommendations Recommendation.objects.filter(user=user).delete() # Create new recommendations for rec in recommendations: Recommendation.objects.create( user=user, product_id=rec['product']['id'], recommendation_type=rec['recommendation_type'], confidence_score=rec['confidence_score'], reason=rec['reason'], expires_at=timezone.now() + timedelta(days=7) ) def update_product_similarities(self): """Update Produkt-Ähnlichkeitsmatrix""" products = Product.objects.filter(is_active=True) for product in products: # Find similar products based on various criteria similar_products = self._find_similar_products(product) # Update similarity matrix for similar_product, similarity_score, similarity_type in similar_products: ProductSimilarity.objects.update_or_create( product=product, similar_product=similar_product, defaults={ 'similarity_score': similarity_score, 'similarity_type': similarity_type } ) def _find_similar_products(self, product): """Finde ähnliche Produkte""" similar_products = [] # Category-based similarity category_similar = Product.objects.filter( category=product.category, is_active=True ).exclude(id=product.id)[:5] for similar_product in category_similar: similarity_score = 0.7 if similar_product.fursuit_type == product.fursuit_type: similarity_score += 0.2 if abs(similar_product.price - product.price) / product.price < 0.3: similarity_score += 0.1 similar_products.append((similar_product, similarity_score, 'category')) # Price-based similarity price_range = product.price * 0.3 price_similar = Product.objects.filter( price__range=(product.price - price_range, product.price + price_range), is_active=True ).exclude(id=product.id)[:5] for similar_product in price_similar: similarity_score = 0.6 if similar_product.category == product.category: similarity_score += 0.2 if similar_product.fursuit_type == product.fursuit_type: similarity_score += 0.2 similar_products.append((similar_product, similarity_score, 'price_range')) return similar_products def train_recommendation_model(self, model_type='hybrid'): """Trainiere ML Model für Empfehlungen""" # Create model record model = RecommendationModel.objects.create( model_type=model_type, model_name=f"{model_type}_model", model_version="1.0", is_active=False ) # Get training data behaviors = UserBehavior.objects.all() training_data_size = behaviors.count() # Simple training (in production, use proper ML library) accuracy_score = 0.75 # Placeholder precision_score = 0.70 recall_score = 0.65 f1_score = 0.67 # Update model model.training_data_size = training_data_size model.accuracy_score = accuracy_score model.precision_score = precision_score model.recall_score = recall_score model.f1_score = f1_score model.trained_at = timezone.now() model.is_active = True model.save() return model def update_analytics(self): """Update Recommendation Analytics""" today = timezone.now().date() analytics, created = RecommendationAnalytics.objects.get_or_create(date=today) # Calculate today's metrics today_recommendations = Recommendation.objects.filter( created_at__date=today ) analytics.total_recommendations = today_recommendations.count() analytics.total_clicks = today_recommendations.filter(is_clicked=True).count() analytics.total_purchases = today_recommendations.filter(is_purchased=True).count() if analytics.total_recommendations > 0: analytics.click_through_rate = (analytics.total_clicks / analytics.total_recommendations) * 100 if analytics.total_clicks > 0: analytics.conversion_rate = (analytics.total_purchases / analytics.total_clicks) * 100 # Calculate revenue purchased_recommendations = today_recommendations.filter(is_purchased=True) total_revenue = sum( rec.product.price for rec in purchased_recommendations ) analytics.revenue_from_recommendations = total_revenue # Average recommendation score if analytics.total_recommendations > 0: avg_score = today_recommendations.aggregate( avg_score=Avg('confidence_score') )['avg_score'] or 0 analytics.avg_recommendation_score = avg_score # Per recommendation type analytics.collaborative_recommendations = today_recommendations.filter( recommendation_type='collaborative' ).count() analytics.content_based_recommendations = today_recommendations.filter( recommendation_type='content_based' ).count() analytics.popular_recommendations = today_recommendations.filter( recommendation_type='popular' ).count() analytics.personalized_recommendations = today_recommendations.filter( recommendation_type='personalized' ).count() analytics.save() return analytics