442 lines
15 KiB
Python
442 lines
15 KiB
Python
"""
|
|
Recommendation Engine Views für ML-basierte Empfehlungen
|
|
"""
|
|
|
|
from django.shortcuts import render
|
|
from django.http import JsonResponse
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.utils import timezone
|
|
from django.db.models import Q, Count, Avg, Sum
|
|
from rest_framework.decorators import api_view, permission_classes
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from rest_framework.response import Response
|
|
from .models import UserBehavior, UserProfile, ProductSimilarity, Recommendation, RecommendationModel, ABTest, RecommendationAnalytics
|
|
from .services import RecommendationService
|
|
import json
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def get_recommendations(request):
|
|
"""Personalisiertes Empfehlungen für User"""
|
|
|
|
try:
|
|
# Get user profile
|
|
profile, created = UserProfile.objects.get_or_create(user=request.user)
|
|
|
|
# Get recommendations
|
|
recommendation_service = RecommendationService()
|
|
recommendations = recommendation_service.get_recommendations_for_user(request.user)
|
|
|
|
# Track recommendation view
|
|
UserBehavior.objects.create(
|
|
user=request.user,
|
|
behavior_type='view',
|
|
metadata={'recommendation_count': len(recommendations)}
|
|
)
|
|
|
|
return Response({
|
|
'recommendations': recommendations,
|
|
'user_profile': {
|
|
'engagement_score': profile.engagement_score,
|
|
'loyalty_level': profile.loyalty_level,
|
|
'preferred_categories': profile.preferred_categories,
|
|
}
|
|
})
|
|
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def get_similar_products(request, product_id):
|
|
"""Ähnliche Produkte für ein spezifisches Produkt"""
|
|
|
|
try:
|
|
from products.models import Product
|
|
product = Product.objects.get(id=product_id)
|
|
|
|
# Get similar products
|
|
similar_products = ProductSimilarity.objects.filter(
|
|
product=product
|
|
).select_related('similar_product').order_by('-similarity_score')[:10]
|
|
|
|
recommendations = []
|
|
for similarity in similar_products:
|
|
recommendations.append({
|
|
'product': {
|
|
'id': similarity.similar_product.id,
|
|
'name': similarity.similar_product.name,
|
|
'price': float(similarity.similar_product.price),
|
|
'image_url': similarity.similar_product.image.url if similarity.similar_product.image else None,
|
|
},
|
|
'similarity_score': similarity.similarity_score,
|
|
'similarity_type': similarity.similarity_type,
|
|
'reason': f"Ähnlich zu {product.name}"
|
|
})
|
|
|
|
return Response({'similar_products': recommendations})
|
|
|
|
except Product.DoesNotExist:
|
|
return Response({'error': 'Product not found'}, status=404)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsAuthenticated])
|
|
def track_behavior(request):
|
|
"""User Behavior tracken"""
|
|
|
|
try:
|
|
data = request.data
|
|
behavior_type = data.get('behavior_type')
|
|
product_id = data.get('product_id')
|
|
auction_id = data.get('auction_id')
|
|
metadata = data.get('metadata', {})
|
|
|
|
if not behavior_type:
|
|
return Response({'error': 'Behavior type required'}, status=400)
|
|
|
|
# Create behavior record
|
|
behavior = UserBehavior.objects.create(
|
|
user=request.user,
|
|
behavior_type=behavior_type,
|
|
product_id=product_id,
|
|
auction_id=auction_id,
|
|
session_id=data.get('session_id', ''),
|
|
ip_address=request.META.get('REMOTE_ADDR'),
|
|
user_agent=request.META.get('HTTP_USER_AGENT', ''),
|
|
metadata=metadata
|
|
)
|
|
|
|
# Update user profile
|
|
profile, created = UserProfile.objects.get_or_create(user=request.user)
|
|
profile.update_engagement_score()
|
|
|
|
return Response({'behavior_id': str(behavior.id)})
|
|
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def get_user_profile(request):
|
|
"""User Profile für Empfehlungen abrufen"""
|
|
|
|
try:
|
|
profile, created = UserProfile.objects.get_or_create(user=request.user)
|
|
|
|
return Response({
|
|
'engagement_score': profile.engagement_score,
|
|
'loyalty_level': profile.loyalty_level,
|
|
'preferred_categories': profile.preferred_categories,
|
|
'preferred_fursuit_types': profile.preferred_fursuit_types,
|
|
'preferred_price_range': profile.preferred_price_range,
|
|
'total_purchases': profile.total_purchases,
|
|
'total_views': profile.total_views,
|
|
'last_active': profile.last_active,
|
|
})
|
|
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsAuthenticated])
|
|
def update_user_preferences(request):
|
|
"""User Preferences aktualisieren"""
|
|
|
|
try:
|
|
data = request.data
|
|
profile, created = UserProfile.objects.get_or_create(user=request.user)
|
|
|
|
# Update preferences
|
|
if 'preferred_categories' in data:
|
|
profile.preferred_categories = data['preferred_categories']
|
|
if 'preferred_fursuit_types' in data:
|
|
profile.preferred_fursuit_types = data['preferred_fursuit_types']
|
|
if 'preferred_price_range' in data:
|
|
profile.preferred_price_range = data['preferred_price_range']
|
|
if 'preferred_colors' in data:
|
|
profile.preferred_colors = data['preferred_colors']
|
|
|
|
profile.save()
|
|
|
|
return Response({'message': 'Preferences updated'})
|
|
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def get_popular_products(request):
|
|
"""Beliebte Produkte basierend auf Verhalten"""
|
|
|
|
try:
|
|
# Get popular products based on behavior
|
|
popular_products = Product.objects.annotate(
|
|
view_count=Count('behaviors', filter=Q(behaviors__behavior_type='view')),
|
|
purchase_count=Count('behaviors', filter=Q(behaviors__behavior_type='purchase')),
|
|
cart_count=Count('behaviors', filter=Q(behaviors__behavior_type='cart_add')),
|
|
).filter(
|
|
is_active=True
|
|
).order_by('-purchase_count', '-view_count')[:10]
|
|
|
|
recommendations = []
|
|
for product in popular_products:
|
|
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,
|
|
},
|
|
'popularity_score': product.purchase_count + (product.view_count * 0.1),
|
|
'reason': 'Beliebt bei anderen Kunden'
|
|
})
|
|
|
|
return Response({'popular_products': recommendations})
|
|
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def get_trending_products(request):
|
|
"""Trending Produkte (recent activity)"""
|
|
|
|
try:
|
|
# Get trending products (recent activity)
|
|
from datetime import timedelta
|
|
|
|
recent_date = timezone.now() - timedelta(days=7)
|
|
|
|
trending_products = Product.objects.annotate(
|
|
recent_views=Count('behaviors', filter=Q(
|
|
behaviors__behavior_type='view',
|
|
behaviors__created_at__gte=recent_date
|
|
)),
|
|
recent_purchases=Count('behaviors', filter=Q(
|
|
behaviors__behavior_type='purchase',
|
|
behaviors__created_at__gte=recent_date
|
|
)),
|
|
).filter(
|
|
is_active=True,
|
|
recent_views__gt=0
|
|
).order_by('-recent_purchases', '-recent_views')[:10]
|
|
|
|
recommendations = []
|
|
for product in trending_products:
|
|
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,
|
|
},
|
|
'trending_score': product.recent_purchases + (product.recent_views * 0.1),
|
|
'reason': 'Trending diese Woche'
|
|
})
|
|
|
|
return Response({'trending_products': recommendations})
|
|
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def get_frequently_bought_together(request, product_id):
|
|
"""Häufig zusammen gekaufte Produkte"""
|
|
|
|
try:
|
|
from products.models import Product
|
|
product = Product.objects.get(id=product_id)
|
|
|
|
# Get users who bought this product
|
|
buyers = UserBehavior.objects.filter(
|
|
product=product,
|
|
behavior_type='purchase'
|
|
).values_list('user', flat=True).distinct()
|
|
|
|
# Get other products bought by same users
|
|
frequently_bought = Product.objects.filter(
|
|
behaviors__user__in=buyers,
|
|
behaviors__behavior_type='purchase'
|
|
).exclude(id=product_id).annotate(
|
|
bought_together_count=Count('behaviors', filter=Q(
|
|
behaviors__behavior_type='purchase',
|
|
behaviors__user__in=buyers
|
|
))
|
|
).filter(
|
|
bought_together_count__gt=0,
|
|
is_active=True
|
|
).order_by('-bought_together_count')[:5]
|
|
|
|
recommendations = []
|
|
for product in frequently_bought:
|
|
recommendations.append({
|
|
'product': {
|
|
'id': product.id,
|
|
'name': product.name,
|
|
'price': float(product.price),
|
|
'image_url': product.image.url if product.image else None,
|
|
},
|
|
'bought_together_count': product.bought_together_count,
|
|
'reason': 'Häufig zusammen gekauft'
|
|
})
|
|
|
|
return Response({'frequently_bought_together': recommendations})
|
|
|
|
except Product.DoesNotExist:
|
|
return Response({'error': 'Product not found'}, status=404)
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsAuthenticated])
|
|
def track_recommendation_click(request):
|
|
"""Recommendation Click tracken"""
|
|
|
|
try:
|
|
data = request.data
|
|
product_id = data.get('product_id')
|
|
recommendation_type = data.get('recommendation_type')
|
|
|
|
if not product_id or not recommendation_type:
|
|
return Response({'error': 'Product ID and recommendation type required'}, status=400)
|
|
|
|
# Update recommendation
|
|
recommendation = Recommendation.objects.filter(
|
|
user=request.user,
|
|
product_id=product_id,
|
|
recommendation_type=recommendation_type
|
|
).first()
|
|
|
|
if recommendation:
|
|
recommendation.is_clicked = True
|
|
recommendation.save()
|
|
|
|
# Track behavior
|
|
UserBehavior.objects.create(
|
|
user=request.user,
|
|
behavior_type='view',
|
|
product_id=product_id,
|
|
metadata={'recommendation_type': recommendation_type}
|
|
)
|
|
|
|
return Response({'success': True})
|
|
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsAuthenticated])
|
|
def track_recommendation_purchase(request):
|
|
"""Recommendation Purchase tracken"""
|
|
|
|
try:
|
|
data = request.data
|
|
product_id = data.get('product_id')
|
|
recommendation_type = data.get('recommendation_type')
|
|
|
|
if not product_id or not recommendation_type:
|
|
return Response({'error': 'Product ID and recommendation type required'}, status=400)
|
|
|
|
# Update recommendation
|
|
recommendation = Recommendation.objects.filter(
|
|
user=request.user,
|
|
product_id=product_id,
|
|
recommendation_type=recommendation_type
|
|
).first()
|
|
|
|
if recommendation:
|
|
recommendation.is_purchased = True
|
|
recommendation.save()
|
|
|
|
# Track behavior
|
|
UserBehavior.objects.create(
|
|
user=request.user,
|
|
behavior_type='purchase',
|
|
product_id=product_id,
|
|
metadata={'recommendation_type': recommendation_type}
|
|
)
|
|
|
|
return Response({'success': True})
|
|
|
|
except Exception as e:
|
|
return Response({'error': str(e)}, status=500)
|
|
|
|
|
|
# Admin Views
|
|
@login_required
|
|
def recommendation_dashboard(request):
|
|
"""Admin Dashboard für Empfehlungen"""
|
|
|
|
if not request.user.is_staff:
|
|
return JsonResponse({'error': 'Access denied'}, status=403)
|
|
|
|
# Get analytics
|
|
today_analytics = RecommendationAnalytics.get_or_create_today()
|
|
|
|
# Get recent behaviors
|
|
recent_behaviors = UserBehavior.objects.select_related('user', 'product').order_by('-created_at')[:20]
|
|
|
|
# Get active models
|
|
active_models = RecommendationModel.objects.filter(is_active=True)
|
|
|
|
# Get A/B tests
|
|
active_tests = ABTest.objects.filter(status='running')
|
|
|
|
context = {
|
|
'today_analytics': today_analytics,
|
|
'recent_behaviors': recent_behaviors,
|
|
'active_models': active_models,
|
|
'active_tests': active_tests,
|
|
}
|
|
|
|
return render(request, 'recommendations/dashboard.html', context)
|
|
|
|
|
|
@login_required
|
|
def recommendation_analytics(request):
|
|
"""Recommendation Analytics"""
|
|
|
|
if not request.user.is_staff:
|
|
return JsonResponse({'error': 'Access denied'}, status=403)
|
|
|
|
# Get analytics for last 30 days
|
|
from datetime import timedelta
|
|
|
|
analytics = RecommendationAnalytics.objects.filter(
|
|
date__gte=timezone.now().date() - timedelta(days=30)
|
|
).order_by('date')
|
|
|
|
# Calculate totals
|
|
total_recommendations = sum(a.total_recommendations for a in analytics)
|
|
total_clicks = sum(a.total_clicks for a in analytics)
|
|
total_purchases = sum(a.total_purchases for a in analytics)
|
|
total_revenue = sum(a.revenue_from_recommendations for a in analytics)
|
|
|
|
context = {
|
|
'analytics': analytics,
|
|
'totals': {
|
|
'recommendations': total_recommendations,
|
|
'clicks': total_clicks,
|
|
'purchases': total_purchases,
|
|
'revenue': total_revenue,
|
|
'ctr': (total_clicks / total_recommendations * 100) if total_recommendations > 0 else 0,
|
|
'conversion_rate': (total_purchases / total_clicks * 100) if total_clicks > 0 else 0,
|
|
}
|
|
}
|
|
|
|
return render(request, 'recommendations/analytics.html', context) |