""" Auction Views für Biet-System """ from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.contrib import messages from django.http import JsonResponse from django.utils import timezone from django.db.models import Q, Count, Max from django.core.paginator import Paginator from django.views.decorators.http import require_POST from django.views.decorators.csrf import csrf_exempt from .models import Auction, Bid, AuctionWatch, AuctionAnalytics from .forms import AuctionForm, BidForm import json def auction_list(request): """Liste aller Auktionen""" # Filter status_filter = request.GET.get('status', '') fursuit_type_filter = request.GET.get('fursuit_type', '') search_query = request.GET.get('search', '') auctions = Auction.objects.select_related('created_by', 'winner').prefetch_related('bids') if status_filter: auctions = auctions.filter(status=status_filter) if fursuit_type_filter: auctions = auctions.filter(fursuit_type=fursuit_type_filter) if search_query: auctions = auctions.filter( Q(title__icontains=search_query) | Q(description__icontains=search_query) | Q(character_description__icontains=search_query) ) # Nur aktive Auktionen anzeigen (außer für Admins) if not request.user.is_staff: auctions = auctions.filter(status='active') # Sortierung sort_by = request.GET.get('sort', '-created_at') auctions = auctions.order_by(sort_by) # Pagination paginator = Paginator(auctions, 12) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) # Statistiken total_auctions = auctions.count() active_auctions = auctions.filter(status='active').count() total_bids = sum(auction.total_bids for auction in page_obj) context = { 'page_obj': page_obj, 'status_filter': status_filter, 'fursuit_type_filter': fursuit_type_filter, 'search_query': search_query, 'sort_by': sort_by, 'total_auctions': total_auctions, 'active_auctions': active_auctions, 'total_bids': total_bids, 'status_choices': Auction.AUCTION_STATUS_CHOICES, 'fursuit_type_choices': Auction._meta.get_field('fursuit_type').choices, } return render(request, 'auction/auction_list.html', context) def auction_detail(request, auction_id): """Auktion Detail-Ansicht""" auction = get_object_or_404(Auction, id=auction_id) # View Count erhöhen auction.view_count += 1 auction.save() # Analytics aktualisieren analytics, created = AuctionAnalytics.objects.get_or_create(auction=auction) analytics.total_views += 1 analytics.save() # Bid History bid_history = auction.bids.select_related('bidder').order_by('-created_at')[:10] # User's current bid user_bid = None if request.user.is_authenticated: user_bid = auction.bids.filter(bidder=request.user).order_by('-amount').first() # Watchlist Status is_watching = False if request.user.is_authenticated: is_watching = auction.watchers.filter(user=request.user).exists() # Similar Auctions similar_auctions = Auction.objects.filter( fursuit_type=auction.fursuit_type, status='active' ).exclude(id=auction.id)[:3] context = { 'auction': auction, 'bid_history': bid_history, 'user_bid': user_bid, 'is_watching': is_watching, 'similar_auctions': similar_auctions, 'bid_form': BidForm() if auction.is_active else None, } return render(request, 'auction/auction_detail.html', context) @login_required def auction_create(request): """Neue Auktion erstellen""" if request.method == 'POST': form = AuctionForm(request.POST, request.FILES) if form.is_valid(): auction = form.save(commit=False) auction.created_by = request.user # Start- und Endzeit setzen if not auction.start_time: auction.start_time = timezone.now() if not auction.end_time: auction.end_time = auction.start_time + timezone.timedelta(days=auction.duration_days) auction.save() messages.success(request, 'Auktion erfolgreich erstellt!') return redirect('auction:auction_detail', auction_id=auction.id) else: form = AuctionForm() context = { 'form': form, 'title': 'Neue Auktion erstellen', } return render(request, 'auction/auction_form.html', context) @login_required def auction_edit(request, auction_id): """Auktion bearbeiten""" auction = get_object_or_404(Auction, id=auction_id, created_by=request.user) if request.method == 'POST': form = AuctionForm(request.POST, request.FILES, instance=auction) if form.is_valid(): form.save() messages.success(request, 'Auktion erfolgreich aktualisiert!') return redirect('auction:auction_detail', auction_id=auction.id) else: form = AuctionForm(instance=auction) context = { 'form': form, 'auction': auction, 'title': 'Auktion bearbeiten', } return render(request, 'auction/auction_form.html', context) @login_required @require_POST def place_bid(request, auction_id): """Gebot platzieren""" auction = get_object_or_404(Auction, id=auction_id) if not auction.is_active: return JsonResponse({'error': 'Auktion ist nicht aktiv'}, status=400) form = BidForm(request.POST) if form.is_valid(): amount = form.cleaned_data['amount'] try: bid = auction.place_bid(request.user, amount) return JsonResponse({ 'success': True, 'bid_id': str(bid.id), 'amount': float(bid.amount), 'current_bid': float(auction.current_bid), 'total_bids': auction.total_bids, 'message': 'Gebot erfolgreich platziert!' }) except ValueError as e: return JsonResponse({'error': str(e)}, status=400) else: return JsonResponse({'error': 'Ungültige Eingabe'}, status=400) @login_required @require_POST def toggle_watch(request, auction_id): """Auktion zur Watchlist hinzufügen/entfernen""" auction = get_object_or_404(Auction, id=auction_id) watch, created = AuctionWatch.objects.get_or_create( user=request.user, auction=auction ) if not created: watch.delete() is_watching = False message = 'Von Watchlist entfernt' else: is_watching = True message = 'Zur Watchlist hinzugefügt' return JsonResponse({ 'success': True, 'is_watching': is_watching, 'message': message }) @login_required def my_auctions(request): """Eigene Auktionen""" auctions = Auction.objects.filter(created_by=request.user).order_by('-created_at') # Filter status_filter = request.GET.get('status', '') if status_filter: auctions = auctions.filter(status=status_filter) context = { 'auctions': auctions, 'status_choices': Auction.AUCTION_STATUS_CHOICES, 'status_filter': status_filter, } return render(request, 'auction/my_auctions.html', context) @login_required def my_bids(request): """Eigene Gebote""" bids = Bid.objects.filter(bidder=request.user).select_related('auction').order_by('-created_at') context = { 'bids': bids, } return render(request, 'auction/my_bids.html', context) @login_required def watchlist(request): """Watchlist""" watched_auctions = Auction.objects.filter( watchers__user=request.user ).order_by('-created_at') context = { 'watched_auctions': watched_auctions, } return render(request, 'auction/watchlist.html', context) # API Views für AJAX @csrf_exempt def auction_api(request, auction_id): """API für Auktion-Details""" auction = get_object_or_404(Auction, id=auction_id) data = { 'id': str(auction.id), 'title': auction.title, 'description': auction.description, 'current_bid': float(auction.current_bid) if auction.current_bid else None, 'starting_bid': float(auction.starting_bid), 'reserve_price': float(auction.reserve_price) if auction.reserve_price else None, 'status': auction.status, 'is_active': auction.is_active, 'time_remaining': auction.time_remaining_formatted, 'total_bids': auction.total_bids, 'total_bidders': auction.total_bidders, 'winner': auction.winner.username if auction.winner else None, 'created_at': auction.created_at.isoformat(), 'end_time': auction.end_time.isoformat(), } return JsonResponse(data) @csrf_exempt def bid_history_api(request, auction_id): """API für Bid-History""" auction = get_object_or_404(Auction, id=auction_id) bids = auction.bids.select_related('bidder').order_by('-created_at')[:20] bid_list = [] for bid in bids: bid_list.append({ 'id': str(bid.id), 'bidder': bid.bidder.username, 'amount': float(bid.amount), 'created_at': bid.created_at.isoformat(), }) return JsonResponse({'bids': bid_list}) @csrf_exempt def place_bid_api(request, auction_id): """API für Gebot platzieren""" if request.method != 'POST': return JsonResponse({'error': 'Nur POST erlaubt'}, status=405) if not request.user.is_authenticated: return JsonResponse({'error': 'Nicht eingeloggt'}, status=401) try: data = json.loads(request.body) amount = float(data.get('amount', 0)) auction = get_object_or_404(Auction, id=auction_id) if not auction.is_active: return JsonResponse({'error': 'Auktion ist nicht aktiv'}, status=400) bid = auction.place_bid(request.user, amount) return JsonResponse({ 'success': True, 'bid_id': str(bid.id), 'amount': float(bid.amount), 'current_bid': float(auction.current_bid), 'total_bids': auction.total_bids, }) except (json.JSONDecodeError, ValueError) as e: return JsonResponse({'error': str(e)}, status=400) def auction_analytics(request, auction_id): """Auktion Analytics""" auction = get_object_or_404(Auction, id=auction_id) analytics = get_object_or_404(AuctionAnalytics, auction=auction) # Bid Distribution bid_distribution = auction.bids.extra( select={'hour': "EXTRACT(hour FROM created_at)"} ).values('hour').annotate(count=Count('id')).order_by('hour') # Top Bidders top_bidders = auction.bids.values('bidder__username').annotate( total_bids=Count('id'), max_bid=Max('amount') ).order_by('-max_bid')[:5] context = { 'auction': auction, 'analytics': analytics, 'bid_distribution': bid_distribution, 'top_bidders': top_bidders, } return render(request, 'auction/analytics.html', context)