furry/auction/views.py

372 lines
11 KiB
Python

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