from collections import defaultdict, OrderedDict from django.views.generic import TemplateView from django.urls import reverse_lazy from django.db.models import Count, Q, Prefetch, Sum, F, IntegerField, Value, BooleanField, Case, When from django.db.models.functions import Coalesce from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from trades.models import TradeOffer, TradeAcceptance, TradeOfferHaveCard, TradeOfferWantCard from cards.models import Card from django.utils.decorators import method_decorator from django.template.response import TemplateResponse from django.http import HttpResponseRedirect import logging from django.views import View from django.http import HttpResponse import contextlib logger = logging.getLogger(__name__) class HomePageView(TemplateView): template_name = "home/home.html" #@silk_profile(name='Home Page') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) try: # Get all cards ordered by name, exclude cards with rarity level > 5 context["cards"] = Card.objects.filter(rarity_level__lte=5).order_by("name", "rarity_level") # Reuse base trade offer queryset for market stats base_offer_qs = TradeOffer.objects.filter(is_closed=False) # Recent Offers try: recent_offers_qs = base_offer_qs.order_by("-created_at")[:6] context["recent_offers"] = recent_offers_qs context["cache_key_recent_offers"] = f"recent_offers_{recent_offers_qs.values_list('pk', 'updated_at')}" except Exception as e: logger.error(f"Error fetching recent offers: {str(e)}") context["recent_offers"] = [] context["cache_key_recent_offers"] = "recent_offers_error" # Most Offered Cards try: most_offered_cards_qs = ( Card.objects.filter(tradeofferhavecard__isnull=False).filter(rarity_level__lte=5) .annotate(offer_count=Sum("tradeofferhavecard__quantity")) .order_by("-offer_count")[:6] ) context["most_offered_cards"] = most_offered_cards_qs context["cache_key_most_offered_cards"] = f"most_offered_cards_{most_offered_cards_qs.values_list('pk', 'updated_at')}" except Exception as e: logger.error(f"Error fetching most offered cards: {str(e)}") context["most_offered_cards"] = [] context["cache_key_most_offered_cards"] = "most_offered_cards_error" # Most Wanted Cards try: most_wanted_cards_qs = ( Card.objects.filter(tradeofferwantcard__isnull=False).filter(rarity_level__lte=5) .annotate(offer_count=Sum("tradeofferwantcard__quantity")) .order_by("-offer_count")[:6] ) context["most_wanted_cards"] = most_wanted_cards_qs context["cache_key_most_wanted_cards"] = f"most_wanted_cards_{most_wanted_cards_qs.values_list('pk', 'updated_at')}" except Exception as e: logger.error(f"Error fetching most wanted cards: {str(e)}") context["most_wanted_cards"] = [] # Least Offered Cards try: least_offered_cards_qs = ( Card.objects.filter(rarity_level__lte=5).annotate( offer_count=Coalesce(Sum("tradeofferhavecard__quantity"), 0) ) .order_by("offer_count")[:6] ) context["least_offered_cards"] = least_offered_cards_qs context["cache_key_least_offered_cards"] = f"least_offered_cards_{least_offered_cards_qs.values_list('pk', 'updated_at')}" except Exception as e: logger.error(f"Error fetching least offered cards: {str(e)}") context["least_offered_cards"] = [] context["cache_key_least_offered_cards"] = "least_offered_cards_error" # Build featured offers with custom ordering featured = OrderedDict() # Featured "All" offers remains fixed at the top try: featured["All"] = base_offer_qs.order_by("created_at")[:6] except Exception as e: logger.error(f"Error fetching 'All' featured offers: {str(e)}") featured["All"] = [] try: # Pull out distinct (rarity_level, rarity_icon) tuples distinct_rarities = base_offer_qs.values_list("rarity_level", "rarity_icon").distinct() # Prepare a list that holds tuples of (rarity_level, rarity_icon, offers) rarity_offers = [] for rarity_level, rarity_icon in distinct_rarities: offers = base_offer_qs.filter(rarity_level=rarity_level).order_by("created_at")[:6] rarity_offers.append((rarity_level, rarity_icon, offers)) # Sort by rarity_level (from greatest to least) rarity_offers.sort(key=lambda x: x[0], reverse=True) # Add the sorted offers to the OrderedDict for rarity_level, rarity_icon, offers in rarity_offers: featured[rarity_icon] = offers except Exception as e: logger.error(f"Error processing rarity-based featured offers: {str(e)}") context["featured_offers"] = featured # Generate a cache key based on the pks and updated_at timestamps of all featured offers all_offer_identifiers = [] for section_name,section_offers in featured.items(): # featured_section is a QuerySet. Fetch (pk, updated_at) tuples. identifiers = section_offers.values_list('pk', 'updated_at') # Format each tuple as "pk_timestamp" and add to the list section_strings = [f"{section_name}_{pk}_{ts.timestamp()}" for pk, ts in identifiers] all_offer_identifiers.extend(section_strings) # Join all identifiers into a single string, sorted for consistency regardless of order combined_identifiers = "|".join(sorted(all_offer_identifiers)) context["cache_key_featured_offers"] = f"featured_offers_{combined_identifiers}" except Exception as e: logger.error(f"Unhandled error in HomePageView.get_context_data: {str(e)}") # Provide fallback empty data context["cards"] = None context["recent_offers"] = [] context["most_offered_cards"] = [] context["most_wanted_cards"] = [] context["least_offered_cards"] = [] context["featured_offers"] = OrderedDict([("All", [])]) return context def get(self, request, *args, **kwargs): """Override get method to add caching""" return super().get(request, *args, **kwargs) class HealthCheckView(View): def get(self, request, *args, **kwargs): try: from django.db import connection connection.cursor().execute("SELECT 1") except Exception as e: return HttpResponse("Database connection failed", status=500) try: from trades.models import TradeOffer with contextlib.redirect_stdout(None): print(TradeOffer.objects.count()) except Exception as e: return HttpResponse("DB models not reachable, but db is reachable", status=500) try: from django.core.cache import cache cache.set("test", "test") with contextlib.redirect_stdout(None): print(cache.get("test")) except Exception as e: return HttpResponse("Cache not reachable", status=500) logger.info("OK/HEALTHY") return HttpResponse("OK/HEALTHY")