diff --git a/Dockerfile b/Dockerfile index 0f9dcde..49a19b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,7 +34,7 @@ EXPOSE 8000 RUN python manage.py collectstatic --noinput -#RUN python manage.py createcachetable django_cache +#RUN python manage.py loaddata seed/* && python manage.py createcachetable django_cache # Use gunicorn on port 8000 CMD ["gunicorn", "--bind", ":8000", "django_project.wsgi", "--timeout", "300"] diff --git a/cards/urls.py b/cards/urls.py index ccb00fd..599427f 100644 --- a/cards/urls.py +++ b/cards/urls.py @@ -3,11 +3,13 @@ from .views import ( CardDetailView, TradeOfferHaveCardListView, TradeOfferWantCardListView, + CardListView, ) app_name = "cards" urlpatterns = [ + path('', CardListView.as_view(), name='card_list'), path('/', CardDetailView.as_view(), name='card_detail'), path('/trade-offers-have/', TradeOfferHaveCardListView.as_view(), name='card_trade_offer_have_list'), path('/trade-offers-want/', TradeOfferWantCardListView.as_view(), name='card_trade_offer_want_list'), diff --git a/cards/views.py b/cards/views.py index b543c92..9f92e32 100644 --- a/cards/views.py +++ b/cards/views.py @@ -27,7 +27,7 @@ class TradeOfferHaveCardListView(ListView): model = TradeOffer template_name = "cards/_trade_offer_list.html" context_object_name = "trade_offers" - paginate_by = 2 + paginate_by = 6 def get_queryset(self): card_id = self.kwargs.get("pk") @@ -47,7 +47,7 @@ class TradeOfferWantCardListView(ListView): model = TradeOffer template_name = "cards/_trade_offer_list.html" context_object_name = "trade_offers" - paginate_by = 2 + paginate_by = 6 def get_queryset(self): card_id = self.kwargs.get("pk") @@ -62,3 +62,108 @@ class TradeOfferWantCardListView(ListView): context['side'] = 'want' return context +class CardListView(ListView): + model = Card + paginate_by = 100 # For non-grouped mode; grouping mode will override default pagination. + context_object_name = "cards" + + def get_template_names(self): + if self.request.headers.get("x-requested-with") == "XMLHttpRequest": + return ["cards/_card_list.html"] + return ["cards/card_list.html"] + + def get_ordering(self): + order = self.request.GET.get("order", "absolute") + if order == "alphabetical": + return "name" + elif order == "rarity": + return "-rarity_level" + else: # absolute ordering + return "id" + + def get_queryset(self): + qs = super().get_queryset() + ordering = self.get_ordering() + qs = qs.order_by(ordering) + return qs.prefetch_related("decks").distinct() + + def get_paginate_by(self, queryset): + group_by = self.request.GET.get("group_by") + if group_by in ("deck", "cardset", "rarity"): + # When grouping is enabled, we want to paginate manually so disable default pagination. + return None + return self.paginate_by + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + order = self.request.GET.get("order", "absolute") + group_by = self.request.GET.get("group_by") + context["order"] = order + context["group_by"] = group_by + + if group_by in ("deck", "cardset", "rarity"): + # Fetch the complete queryset (no slicing) + full_qs = self.get_queryset() + all_cards = list(full_qs) + flat_cards = [] + + if group_by == "deck": + # Each card may belong to multiple decks – reproduce the existing logic. + for card in all_cards: + for deck in card.decks.all(): + flat_cards.append({"group": deck.name, "card": card}) + flat_cards.sort(key=lambda x: x["group"].lower()) + elif group_by == "cardset": + for card in all_cards: + flat_cards.append({"group": card.cardset, "card": card}) + flat_cards.sort(key=lambda x: x["group"].lower()) + elif group_by == "rarity": + for card in all_cards: + flat_cards.append({"group": card.rarity_level, "card": card}) + flat_cards.sort(key=lambda x: x["group"], reverse=True) + + total_cards = len(flat_cards) + try: + page_number = int(self.request.GET.get("page", 1)) + if page_number < 1: + page_number = 1 + except ValueError: + page_number = 1 + + per_page = 96 + start = (page_number - 1) * per_page + end = page_number * per_page + page_flat_cards = flat_cards[start:end] + + # Reassemble the flat list into grouped structure for just this page. + page_groups = [] + for item in page_flat_cards: + group_value = item["group"] + card_obj = item["card"] + if page_groups and page_groups[-1]["group"] == group_value: + page_groups[-1]["cards"].append(card_obj) + else: + page_groups.append({"group": group_value, "cards": [card_obj]}) + context["groups"] = page_groups + + # Set up custom pagination context. + from math import ceil + num_pages = ceil(total_cards / per_page) + page_obj = { + "number": page_number, + "has_previous": page_number > 1, + "has_next": page_number < num_pages, + "previous_page_number": page_number - 1 if page_number > 1 else None, + "next_page_number": page_number + 1 if page_number < num_pages else None, + "paginator": { + "num_pages": num_pages, + }, + } + context["page_obj"] = page_obj + context["is_paginated"] = total_cards > per_page + context["total_cards"] = total_cards + # Optionally, keep the full queryset in object_list. + context["object_list"] = full_qs + return context + + return context diff --git a/theme/templates/base.html b/theme/templates/base.html index 59ddeea..a630126 100644 --- a/theme/templates/base.html +++ b/theme/templates/base.html @@ -1,7 +1,7 @@ {% load static tailwind_tags gravatar %} {% url 'home' as home_url %} {% url 'trade_offer_list' as trade_offer_list_url %} -{% url 'trade_offer_my_list' as trade_offer_my_list_url %} +{% url 'cards:card_list' as cards_list_url %} {% url 'settings' as settings_url %} @@ -67,6 +67,7 @@ tabindex="0" class="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
  • Home
  • +
  • Cards
  • Trades
      @@ -87,6 +88,7 @@ + {% endif %} + \ No newline at end of file diff --git a/theme/templates/cards/card_list.html b/theme/templates/cards/card_list.html new file mode 100644 index 0000000..032cd92 --- /dev/null +++ b/theme/templates/cards/card_list.html @@ -0,0 +1,52 @@ +{% extends "base.html" %} +{% load static card_badge %} +{% block content %} +
      +

      Cards

      +
      + + + + +
      + +
      + +
      +
      +{% endblock %} \ No newline at end of file diff --git a/theme/templatetags/card_multiselect.html b/theme/templatetags/card_multiselect.html index dfaabfd..6e30aea 100644 --- a/theme/templatetags/card_multiselect.html +++ b/theme/templatetags/card_multiselect.html @@ -92,7 +92,7 @@ document.addEventListener('DOMContentLoaded', function() { searchEnabled: true, shouldSort: false, allowHTML: true, - closeDropdownOnSelect: false, + closeDropdownOnSelect: true, removeItemButton: true, searchFields: ['label'], resetScrollPosition: false, diff --git a/theme/templatetags/trade_offer_png.html b/theme/templatetags/trade_offer_png.html index 89eb9d4..d6c9bba 100644 --- a/theme/templatetags/trade_offer_png.html +++ b/theme/templatetags/trade_offer_png.html @@ -1,12 +1,14 @@ {% load gravatar card_badge tailwind_tags %} - + - + - -
      + +
      diff --git a/trades/templatetags/trade_offer_tags.py b/trades/templatetags/trade_offer_tags.py index 96a770d..fe20ee5 100644 --- a/trades/templatetags/trade_offer_tags.py +++ b/trades/templatetags/trade_offer_tags.py @@ -103,7 +103,11 @@ def render_trade_offer_png(context, offer, show_friend_code=False): image_width = base_width image_height = int(round(image_width / aspect_ratio)) - base_url = context.get('request').build_absolute_uri('/') + request = context.get("request") + if request.get_host().startswith("localhost"): + base_url = "http://{0}".format(request.get_host()) + else: + base_url = "https://{0}".format(request.get_host()) return { 'offer_pk': offer.pk, diff --git a/trades/views.py b/trades/views.py index 56db7a3..3265264 100644 --- a/trades/views.py +++ b/trades/views.py @@ -618,26 +618,60 @@ class TradeOfferPNGView(View): html = render_to_string("templatetags/trade_offer_png.html", tag_context) + # If there's a query parameter 'debug' set to true, render the HTML to the response. + if request.GET.get('debug'): + return HttpResponse(html, content_type="text/html") + + css = render_to_string("static/css/dist/styles.css") + # Launch Playwright to render the HTML and capture a screenshot. with sync_playwright() as p: + print("Launching browser") browser = p.chromium.launch( headless=True, args=[ - "--disable-gpu", "--no-sandbox", - '--disable-setuid-sandbox', - '--disable-dev-shm-usage', - '--disable-accelerated-2d-canvas', - '--no-first-run', - '--disable-gpu' + "--disable-setuid-sandbox", + "--disable-dev-shm-usage", + "--disable-accelerated-2d-canvas", + "--disable-gpu", + #"--single-process", + "--no-zygote", + "--disable-audio-output", + #"--disable-software-rasterizer", + "--disable-webgl", + #"--disable-web-security", + #"--disable-features=LazyFrameLoading", + #"--disable-features=IsolateOrigins", + #"--disable-background-networking", + "--no-first-run", ] ) + print("Launched browser, creating context") context_browser = browser.new_context(viewport={"width": image_width, "height": image_height}) + print("Created context, creating page") page = context_browser.new_page() - page.set_content(html, wait_until="networkidle") + print("Created page, setting content") + + # Listen for all console logs, errors, and warnings + page.on("console", lambda msg: print(f"Console {msg.type}: {msg.text}")) + page.on("pageerror", lambda err: print(f"Page error: {err}")) + + # Listen specifically for failed resource loads + page.on("requestfailed", lambda request: print(f"Failed to load: {request.url} - {request.failure.error_text}")) + + # # Instead of using a link tag, let's inject the CSS directly + # css = render_to_string("static/css/dist/styles.css") + # page.add_style_tag(content=css) + + page.set_content(html, wait_until="domcontentloaded") + print("Set content, waiting for element") element = page.wait_for_selector(".trade-offer-card-screenshot") + print("Found element, capturing screenshot") screenshot_bytes = element.screenshot(type="png", omit_background=True) + print("Captured screenshot, closing browser") browser.close() + print("Closed browser, returning screenshot") return HttpResponse(screenshot_bytes, content_type="image/png")