from collections import defaultdict from django.views.generic import TemplateView from django.urls import reverse_lazy from django.db.models import Count, Q, Prefetch, Sum from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from trades.models import TradeOffer from cards.models import Card, CardSet, Rarity from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_page from django.template.response import TemplateResponse @method_decorator(cache_page(60), name='get') # Cache view for 60 seconds class HomePageView(TemplateView): template_name = "home/home.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Use POST data if available, else fallback to GET request_data = self.request.POST if self.request.method == "POST" else self.request.GET # --- Search form logic --- offered_cards = request_data.getlist("offered_cards") wanted_cards = request_data.getlist("wanted_cards") context["offered_cards"] = offered_cards context["wanted_cards"] = wanted_cards # Define prefetch objects ordered by number of associated trade offers ascending, # and by id secondarily. have_cards_prefetch = Prefetch( 'have_cards', queryset=Card.objects.annotate( trade_offer_count=Count("trade_offers_have") ).order_by("trade_offer_count", "id") ) want_cards_prefetch = Prefetch( 'want_cards', queryset=Card.objects.annotate( trade_offer_count=Count("trade_offers_want") ).order_by("trade_offer_count", "id") ) search_results = None if offered_cards or wanted_cards: # Instead of filtering by a 'state' field (which no longer exists), # we fetch all offers. You may later add logic to filter only "open" offers. qs = TradeOffer.objects.all().prefetch_related( have_cards_prefetch, "have_cards__decks", "have_cards__rarity", "have_cards__cardset", want_cards_prefetch, "want_cards__decks", "want_cards__rarity", "want_cards__cardset" ).select_related( "initiated_by__user" ) if offered_cards: try: offered_card_ids = [int(card) for card in offered_cards] except ValueError: qs = qs.none() else: qs = qs.filter(want_cards__id__in=offered_card_ids) if wanted_cards: try: wanted_card_ids = [int(card) for card in wanted_cards] except ValueError: qs = qs.none() else: qs = qs.filter(have_cards__id__in=wanted_card_ids) page_number = request_data.get("page", 1) paginator = Paginator(qs, 6) try: search_results = paginator.page(page_number) except PageNotAnInteger: search_results = paginator.page(1) except EmptyPage: search_results = paginator.page(paginator.num_pages) context["search_results"] = search_results # --- Recently posted offers (latest 5, newest first) --- context["recent_offers"] = TradeOffer.objects.order_by("-created_at").prefetch_related( have_cards_prefetch, "have_cards__decks", "have_cards__rarity", "have_cards__cardset", want_cards_prefetch, "want_cards__decks", "want_cards__rarity", "want_cards__cardset" ).select_related( "initiated_by__user" )[:5] # --- Most offered cards --- context["most_offered_cards"] = Card.objects.filter( tradeofferhavecard__isnull=False ).annotate( offer_count=Sum("tradeofferhavecard__quantity") ).order_by("-offer_count").select_related("rarity", "cardset").prefetch_related("decks")[:5] # --- Most wanted cards --- context["most_wanted_cards"] = Card.objects.filter( tradeofferwantcard__isnull=False ).annotate( offer_count=Sum("tradeofferwantcard__quantity") ).order_by("-offer_count").select_related("rarity", "cardset").prefetch_related("decks")[:5] # --- Least offered cards --- context["least_offered_cards"] = Card.objects.annotate( offer_count=Sum("tradeofferhavecard__quantity") ).order_by("offer_count", "?")[:5] # --- Featured offers grouped by rarity (using card.rarity.icon for tab names) --- featured = {} all_offers = list( TradeOffer.objects.order_by("created_at").prefetch_related( have_cards_prefetch, "have_cards__decks", "have_cards__rarity", "have_cards__cardset", want_cards_prefetch, "want_cards__decks", "want_cards__rarity", "want_cards__cardset" ).select_related( "initiated_by__user" ) ) featured["All"] = all_offers[:5] # Group offers by normalized rarity id from their have_cards grouped = defaultdict(list) for offer in all_offers: normalized_ids = set() for card in offer.have_cards.all(): if card.rarity: normalized_ids.add(card.rarity.normalized_id) for norm in normalized_ids: grouped[norm].append(offer) # Map each normalized rarity id to a representative icon norm_ids_available = list(grouped.keys()) rareness_qs = Rarity.objects.filter(pk__in=[6] + [nid for nid in norm_ids_available if nid != 6]) rarity_map = {rarity.pk: rarity.icons for rarity in rareness_qs} # Order groups by descending normalized rarity id for norm in sorted(grouped.keys(), reverse=True): offers = grouped[norm] icon_label = rarity_map.get(norm) if icon_label: featured[icon_label] = offers[:5] context["featured_offers"] = featured return context def post(self, request, *args, **kwargs): # If the request is AJAX, return only the search results fragment context = self.get_context_data(**kwargs) if request.headers.get('x-requested-with') == 'XMLHttpRequest': return TemplateResponse(request, "home/_search_results.html", context) return self.render_to_response(context)