From af2f48a491c3f14f939e26e3d20f43882cc4c2f8 Mon Sep 17 00:00:00 2001 From: badbl0cks <4161747+badbl0cks@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:42:36 -0700 Subject: [PATCH] refactor(db): update cursor rules and enhance deployment rollback script - Standardized string formatting in cursor rules for consistency. - Added a new rollback deployment script to facilitate blue-green deployment strategy. - Removed outdated seed data files and introduced new rarity mappings for better data management. - Improved model relationships and query optimizations in various views and admin configurations. - Enhanced caching strategies across templates to improve performance and reduce load times, including jitter in cache settings for better performance. - Refactored card-related views and templates to utilize new model fields and relationships. --- .cursorrules | 35 +- scripts/rollback-deployment.sh | 98 + seed/0002_Decks.json | 101 - seed/0002_RarityMappings.json | 1 + seed/0003_Cards.json | 12717 ---------------- src/pkmntrade_club/accounts/admin.py | 12 +- .../accounts/migrations/0001_initial.py | 196 +- src/pkmntrade_club/accounts/views.py | 137 +- src/pkmntrade_club/cards/admin.py | 759 +- .../cards/migrations/0001_initial.py | 751 +- src/pkmntrade_club/cards/models.py | 75 +- src/pkmntrade_club/cards/signals.py | 80 +- .../cards/templatetags/card_badge.py | 11 +- .../cards/templatetags/card_multiselect.py | 14 +- src/pkmntrade_club/cards/views.py | 127 +- .../common/context_processors.py | 16 +- src/pkmntrade_club/django_project/settings.py | 22 +- src/pkmntrade_club/home/views.py | 106 +- src/pkmntrade_club/theme/templates/base.html | 2 + .../theme/templates/cards/card_detail.html | 2 +- .../theme/templates/cards/card_list.html | 97 +- .../theme/templates/home/_card_list.html | 1 + .../theme/templates/home/home.html | 10 +- .../_card_multiselect_options.html | 18 +- .../theme/templatetags/card_badge.html | 2 +- .../theme/templatetags/card_multiselect.html | 2 +- .../theme/templatetags/trade_acceptance.html | 4 +- .../theme/templatetags/trade_offer.html | 2 +- .../theme/templatetags/trade_offer_png.html | 32 +- src/pkmntrade_club/trades/admin.py | 110 +- src/pkmntrade_club/trades/apps.py | 2 +- src/pkmntrade_club/trades/forms.py | 8 +- .../trades/migrations/0001_initial.py | 206 +- src/pkmntrade_club/trades/mixins.py | 7 +- src/pkmntrade_club/trades/models.py | 113 +- src/pkmntrade_club/trades/signals.py | 70 +- src/pkmntrade_club/trades/views.py | 63 +- 37 files changed, 2444 insertions(+), 13565 deletions(-) create mode 100755 scripts/rollback-deployment.sh delete mode 100644 seed/0002_Decks.json create mode 100644 seed/0002_RarityMappings.json delete mode 100644 seed/0003_Cards.json diff --git a/.cursorrules b/.cursorrules index 5e49a06..8b27274 100644 --- a/.cursorrules +++ b/.cursorrules @@ -10,9 +10,9 @@ Key Principles Django/Python -- Use Django’s class-based views (CBVs) for more complex views; prefer function-based views (FBVs) for simpler logic. -- Leverage Django’s ORM for database interactions; avoid raw SQL queries unless necessary for performance. -- Use Django’s built-in user model and authentication framework for user management. +- Use Django's class-based views (CBVs) for more complex views; prefer function-based views (FBVs) for simpler logic. +- Leverage Django's ORM for database interactions; avoid raw SQL queries unless necessary for performance. +- Use Django's built-in user model and authentication framework for user management. - Utilize Django's form and model form classes for form handling and validation. - Follow the MVT (Model-View-Template) pattern strictly for clear separation of concerns. - Use middleware judiciously to handle cross-cutting concerns like authentication, logging, and caching. @@ -25,20 +25,29 @@ Error Handling and Validation - Customize error pages (e.g., 404, 500) to improve user experience and provide helpful information. - Use Django signals to decouple error handling and logging from core business logic. +Development, Testing, and Operations + +- Use Gatus for service health monitoring and status pages. +- Employ Locust for load testing to ensure application scalability and performance under stress. +- Utilize Playwright for end-to-end testing to simulate user interactions and validate application behavior from the user's perspective. + Dependencies - Django -- Django REST Framework (for API development) - Celery (for background tasks) - Redis (for caching and task queues) - PostgreSQL or MySQL (preferred databases for production) +- Granian / Gunicorn (for serving the application) +- Whitenoise (for serving static files) - Tailwind CSS for the frontend -- Django Crispy Forms for the frontend - Django Allauth for authentication -- Django DaisyUI for the frontend -- Django El Pagination for the frontend +- Django Crispy Forms for the frontend +- Crispy Tailwind for Tailwind-compatible Crispy Forms +- Django DaisyUI for the admin frontend - Django Widget Tweaks for the frontend -- Django Crispy Tailwind for the frontend +- django-debug-toolbar for debugging +- django-health-check for application health monitoring +- django-parler for multilingual support Django-Specific Guidelines @@ -46,17 +55,17 @@ Django-Specific Guidelines - Keep business logic in models and forms; keep views light and focused on request handling. - Use Django's URL dispatcher (urls.py) to define clear and RESTful URL patterns. - Apply Django's security best practices (e.g., CSRF protection, SQL injection protection, XSS prevention). -- Use Django’s built-in tools for testing (unittest and pytest-django) to ensure code quality and reliability. -- Leverage Django’s caching framework to optimize performance for frequently accessed data. -- Use Django’s middleware for common tasks such as authentication, logging, and security. +- Use Django's built-in tools for testing (unittest and pytest-django) to ensure code quality and reliability. +- Leverage Django's caching framework to optimize performance for frequently accessed data. +- Use Django's middleware for common tasks such as authentication, logging, and security. Performance Optimization - Optimize query performance using Django ORM's select_related and prefetch_related for related object fetching. -- Use Django’s cache framework with backend support (e.g., Redis or Memcached) to reduce database load. +- Use Django's cache framework with backend support (e.g., Redis or Memcached) to reduce database load. - Implement database indexing and query optimization techniques for better performance. - Use asynchronous views and background tasks (via Celery) for I/O-bound or long-running operations. -- Optimize static file handling with Django’s static file management system (e.g., WhiteNoise or CDN integration). +- Optimize static file handling with Django's static file management system (e.g., WhiteNoise or CDN integration). Key Conventions diff --git a/scripts/rollback-deployment.sh b/scripts/rollback-deployment.sh new file mode 100755 index 0000000..481ee3c --- /dev/null +++ b/scripts/rollback-deployment.sh @@ -0,0 +1,98 @@ +#!/bin/bash +set -euo pipefail + +# Rollback deployment by swapping colors +# Usage: ./rollback-deployment.sh + +# Source common functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/common-lib.sh" + +validate_deployment_env + +# Get current state +STATE=$(get_deployment_state) + +echo "🔍 Current deployment state: $STATE" + +if [ "$STATE" = "none" ]; then + echo "❌ No active deployment found to rollback" + exit 1 +fi + +if [ "$STATE" != "both" ]; then + echo "❌ Rollback requires both colors to be running" + echo " Current state: only $STATE is running" + echo "" + echo " To perform a manual rollback:" + echo " 1. Find the previous release in ${RELEASES_PATH}/" + echo " 2. Update the symlink: ln -sfn ${CURRENT_LINK_PATH}" + echo " 3. Redeploy using: ./deploy-blue-green.sh" + exit 1 +fi + +# Both colors running - determine which is newer +CURRENT_COLOR=$(get_current_color) +ROLLBACK_COLOR=$(switch_color "$CURRENT_COLOR") + +# Get image tags for both deployments +CURRENT_IMAGE=$(get_deployment_image_tag "$CURRENT_COLOR") +ROLLBACK_IMAGE=$(get_deployment_image_tag "$ROLLBACK_COLOR") + +echo "🔄 Rolling back from $CURRENT_COLOR (newer) to $ROLLBACK_COLOR (older)" +echo " Current image: ${CURRENT_IMAGE}" +echo " Rollback image: ${ROLLBACK_IMAGE}" + +# Verify the rollback image exists +if ! run_on_target "docker images -q 'badbl0cks/pkmntrade-club:${ROLLBACK_IMAGE}' | grep -q ."; then + echo "❌ Rollback image not found: badbl0cks/pkmntrade-club:${ROLLBACK_IMAGE}" + echo " The image may have been pruned. Cannot perform rollback." + exit 1 +fi + +# Verify rollback color is healthy +ROLLBACK_PROJECT=$(get_project_name "$ROLLBACK_COLOR") +HEALTHY_COUNT=$(count_containers "label=com.docker.compose.project=${ROLLBACK_PROJECT} --filter status=running") + +if [ "$HEALTHY_COUNT" -eq 0 ]; then + echo "❌ No healthy $ROLLBACK_COLOR containers found, cannot perform rollback" + exit 1 +fi + +echo "✅ Found $HEALTHY_COUNT healthy $ROLLBACK_COLOR containers" + +# Store the release path of the current color before we change the symlink +CURRENT_RELEASE_PATH=$(readlink -f "${CURRENT_LINK_PATH}") + +echo "🔄 Performing rollback..." + +echo "🔎 Finding release for rollback color ($ROLLBACK_COLOR)..." +# Find the second newest release directory. This is assumed to be the rollback target. +ROLLBACK_RELEASE_PATH=$(ls -dt "${RELEASES_PATH}"/*/ | sed -n '2p' | tr -d '\n') + +if [ -z "$ROLLBACK_RELEASE_PATH" ]; then + echo "❌ Could not find a previous release to rollback to in ${RELEASES_PATH}" + exit 1 +fi +echo " Found rollback release: ${ROLLBACK_RELEASE_PATH}" + +echo "🔗 Switching 'current' symlink to point to rollback release..." +ln -sfn "${ROLLBACK_RELEASE_PATH}" "${CURRENT_LINK_PATH}" + +# Refresh gatekeepers to switch traffic to the rollback color +refresh_gatekeepers + +wait_with_countdown 10 "⏳ Waiting for traffic to stabilize on $ROLLBACK_COLOR..." + +# Stop and clean up current color containers, using the correct release path +export CLEANUP_RELEASE_PATH="${CURRENT_RELEASE_PATH}" +cleanup_color_containers "$CURRENT_COLOR" +unset CLEANUP_RELEASE_PATH + +# Refresh gatekeepers again to remove routes to the old color +refresh_gatekeepers + +echo "✅ Rollback completed!" +echo " Active deployment: $ROLLBACK_COLOR" +echo "" +echo "📌 Note: The next deployment will now deploy as $CURRENT_COLOR" \ No newline at end of file diff --git a/seed/0002_Decks.json b/seed/0002_Decks.json deleted file mode 100644 index 163c7cf..0000000 --- a/seed/0002_Decks.json +++ /dev/null @@ -1,101 +0,0 @@ -[ - { - "model": "cards.deck", - "pk": 1, - "fields": { - "name": "Promo-A", - "cardset": "Promo-A", - "hex_color": "#1070EB", - "created_at": "2025-02-16T07:55:34.988Z", - "updated_at": "2025-02-16T07:55:34.988Z" - } - }, - { - "model": "cards.deck", - "pk": 2, - "fields": { - "name": "Genetic Apex: Mewtwo", - "cardset": "A1", - "hex_color": "#8040E0", - "created_at": "2025-02-16T07:54:57.445Z", - "updated_at": "2025-02-16T07:54:57.445Z" - } - }, - { - "model": "cards.deck", - "pk": 3, - "fields": { - "name": "Genetic Apex: Charizard", - "cardset": "A1", - "hex_color": "#E00202", - "created_at": "2025-02-16T07:54:52.381Z", - "updated_at": "2025-02-16T07:54:52.381Z" - } - }, - { - "model": "cards.deck", - "pk": 4, - "fields": { - "name": "Genetic Apex: Pikachu", - "cardset": "A1", - "hex_color": "#FCF326", - "created_at": "2025-02-16T07:55:05.097Z", - "updated_at": "2025-02-16T07:55:05.097Z" - } - }, - { - "model": "cards.deck", - "pk": 5, - "fields": { - "name": "Mythical Island", - "cardset": "A1a", - "hex_color": "#20AA80", - "created_at": "2025-02-16T07:55:11.916Z", - "updated_at": "2025-02-16T07:55:11.916Z" - } - }, - { - "model": "cards.deck", - "pk": 6, - "fields": { - "name": "Space-Time Smackdown: Dialga", - "cardset": "A2", - "hex_color": "#302FD9", - "created_at": "2025-02-16T07:55:17.582Z", - "updated_at": "2025-02-16T07:55:17.582Z" - } - }, - { - "model": "cards.deck", - "pk": 7, - "fields": { - "name": "Space-Time Smackdown: Palkia", - "cardset": "A2", - "hex_color": "#CF36E0", - "created_at": "2025-02-16T07:55:27.503Z", - "updated_at": "2025-02-16T07:55:27.503Z" - } - }, - { - "model": "cards.deck", - "pk": 8, - "fields": { - "name": "Triumphant Light", - "cardset": "A2a", - "hex_color": "#DF8D2C", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.deck", - "pk": 9, - "fields": { - "name": "Shining Revelry", - "cardset": "A2b", - "hex_color": "#D7FDFC", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - } -] diff --git a/seed/0002_RarityMappings.json b/seed/0002_RarityMappings.json new file mode 100644 index 0000000..472a287 --- /dev/null +++ b/seed/0002_RarityMappings.json @@ -0,0 +1 @@ +[{"model": "cards.raritymapping", "pk": 1, "fields": {"original_name": "Common", "mapped_name": "Common", "icon": "🔷", "level": 1, "created_at": "2025-06-15T03:51:40.147Z", "updated_at": "2025-06-15T03:51:40.147Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 2, "fields": {"original_name": "Uncommon", "mapped_name": "Uncommon", "icon": "🔷🔷", "level": 2, "created_at": "2025-06-15T03:53:12.209Z", "updated_at": "2025-06-15T03:53:12.209Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 3, "fields": {"original_name": "Rare", "mapped_name": "Rare", "icon": "🔷🔷🔷", "level": 3, "created_at": "2025-06-15T03:53:31.267Z", "updated_at": "2025-06-15T03:53:31.267Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 4, "fields": {"original_name": "Rare EX", "mapped_name": "Double Rare", "icon": "🔷🔷🔷🔷", "level": 4, "created_at": "2025-06-15T03:53:54.712Z", "updated_at": "2025-06-15T03:53:54.712Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 5, "fields": {"original_name": "Full Art", "mapped_name": "Art Rare", "icon": "⭐️", "level": 5, "created_at": "2025-06-15T03:54:26.671Z", "updated_at": "2025-06-15T03:54:26.671Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 6, "fields": {"original_name": "Full Art EX/Support", "mapped_name": "Super Rare", "icon": "⭐️⭐️", "level": 6, "created_at": "2025-06-15T03:54:58.835Z", "updated_at": "2025-06-15T03:54:58.835Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 7, "fields": {"original_name": "Immersive", "mapped_name": "Immersive Rare", "icon": "⭐️⭐️⭐️", "level": 7, "created_at": "2025-06-15T03:55:25.941Z", "updated_at": "2025-06-15T03:59:14.725Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 8, "fields": {"original_name": "Gold Crown", "mapped_name": "Ultra Rare", "icon": "👑", "level": 10, "created_at": "2025-06-15T03:56:05.786Z", "updated_at": "2025-06-15T03:56:32.728Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 9, "fields": {"original_name": "One shiny star", "mapped_name": "Shiny Rare", "icon": "✨", "level": 8, "created_at": "2025-06-15T03:57:03.342Z", "updated_at": "2025-06-15T03:59:04.136Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 10, "fields": {"original_name": "Two shiny stars", "mapped_name": "Shiny Super Rare", "icon": "✨✨", "level": 9, "created_at": "2025-06-15T03:57:33.360Z", "updated_at": "2025-06-15T03:57:51.004Z", "deleted_at": null}}, {"model": "cards.raritymapping", "pk": 11, "fields": {"original_name": "Two shiny star", "mapped_name": "Shiny Super Rare", "icon": "✨✨", "level": 9, "created_at": "2025-06-15T03:58:10.204Z", "updated_at": "2025-06-15T03:58:10.204Z", "deleted_at": null}}] \ No newline at end of file diff --git a/seed/0003_Cards.json b/seed/0003_Cards.json deleted file mode 100644 index df7f1ac..0000000 --- a/seed/0003_Cards.json +++ /dev/null @@ -1,12717 +0,0 @@ -[ - { - "model": "cards.card", - "pk": 1, - "fields": { - "cardnum": 1, - "cardset": "A1", - "name": "Bulbasaur", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 2, - "fields": { - "cardnum": 2, - "cardset": "A1", - "name": "Ivysaur", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 3, - "fields": { - "cardnum": 3, - "cardset": "A1", - "name": "Venusaur", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 4, - "fields": { - "cardnum": 4, - "cardset": "A1", - "name": "Venusaur EX", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 5, - "fields": { - "cardnum": 5, - "cardset": "A1", - "name": "Caterpie", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 6, - "fields": { - "cardnum": 6, - "cardset": "A1", - "name": "Metapod", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 7, - "fields": { - "cardnum": 7, - "cardset": "A1", - "name": "Butterfree", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 8, - "fields": { - "cardnum": 8, - "cardset": "A1", - "name": "Weedle", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 9, - "fields": { - "cardnum": 9, - "cardset": "A1", - "name": "Kakuna", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 10, - "fields": { - "cardnum": 10, - "cardset": "A1", - "name": "Beedrill", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 11, - "fields": { - "cardnum": 11, - "cardset": "A1", - "name": "Oddish", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 12, - "fields": { - "cardnum": 12, - "cardset": "A1", - "name": "Gloom", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 13, - "fields": { - "cardnum": 13, - "cardset": "A1", - "name": "Vileplume", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 14, - "fields": { - "cardnum": 14, - "cardset": "A1", - "name": "Paras", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 15, - "fields": { - "cardnum": 15, - "cardset": "A1", - "name": "Parasect", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 16, - "fields": { - "cardnum": 16, - "cardset": "A1", - "name": "Venonat", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 17, - "fields": { - "cardnum": 17, - "cardset": "A1", - "name": "Venomoth", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 18, - "fields": { - "cardnum": 18, - "cardset": "A1", - "name": "Bellsprout", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 19, - "fields": { - "cardnum": 19, - "cardset": "A1", - "name": "Weepinbell", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 20, - "fields": { - "cardnum": 20, - "cardset": "A1", - "name": "Victreebel", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 21, - "fields": { - "cardnum": 21, - "cardset": "A1", - "name": "Exeggcute", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 22, - "fields": { - "cardnum": 22, - "cardset": "A1", - "name": "Exeggutor", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 23, - "fields": { - "cardnum": 23, - "cardset": "A1", - "name": "Exeggutor EX", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 24, - "fields": { - "cardnum": 24, - "cardset": "A1", - "name": "Tangela", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 25, - "fields": { - "cardnum": 25, - "cardset": "A1", - "name": "Scyther", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 26, - "fields": { - "cardnum": 26, - "cardset": "A1", - "name": "Pinsir", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 27, - "fields": { - "cardnum": 27, - "cardset": "A1", - "name": "Cottonee", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 28, - "fields": { - "cardnum": 28, - "cardset": "A1", - "name": "Whimsicott", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 29, - "fields": { - "cardnum": 29, - "cardset": "A1", - "name": "Petilil", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 30, - "fields": { - "cardnum": 30, - "cardset": "A1", - "name": "Lilligant", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 31, - "fields": { - "cardnum": 31, - "cardset": "A1", - "name": "Skiddo", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 32, - "fields": { - "cardnum": 32, - "cardset": "A1", - "name": "Gogoat", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 33, - "fields": { - "cardnum": 33, - "cardset": "A1", - "name": "Charmander", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 34, - "fields": { - "cardnum": 34, - "cardset": "A1", - "name": "Charmeleon", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 35, - "fields": { - "cardnum": 35, - "cardset": "A1", - "name": "Charizard", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 36, - "fields": { - "cardnum": 36, - "cardset": "A1", - "name": "Charizard EX", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 37, - "fields": { - "cardnum": 37, - "cardset": "A1", - "name": "Vulpix", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 38, - "fields": { - "cardnum": 38, - "cardset": "A1", - "name": "Ninetales", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 39, - "fields": { - "cardnum": 39, - "cardset": "A1", - "name": "Growlithe", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 40, - "fields": { - "cardnum": 40, - "cardset": "A1", - "name": "Arcanine", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 41, - "fields": { - "cardnum": 41, - "cardset": "A1", - "name": "Arcanine EX", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 42, - "fields": { - "cardnum": 42, - "cardset": "A1", - "name": "Ponyta", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 43, - "fields": { - "cardnum": 43, - "cardset": "A1", - "name": "Rapidash", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 44, - "fields": { - "cardnum": 44, - "cardset": "A1", - "name": "Magmar", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 45, - "fields": { - "cardnum": 45, - "cardset": "A1", - "name": "Flareon", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 46, - "fields": { - "cardnum": 46, - "cardset": "A1", - "name": "Moltres", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 47, - "fields": { - "cardnum": 47, - "cardset": "A1", - "name": "Moltres EX", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 48, - "fields": { - "cardnum": 48, - "cardset": "A1", - "name": "Heatmor", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 49, - "fields": { - "cardnum": 49, - "cardset": "A1", - "name": "Salandit", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 50, - "fields": { - "cardnum": 50, - "cardset": "A1", - "name": "Salazzle", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 51, - "fields": { - "cardnum": 51, - "cardset": "A1", - "name": "Sizzlipede", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 52, - "fields": { - "cardnum": 52, - "cardset": "A1", - "name": "Centiskorch", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 53, - "fields": { - "cardnum": 53, - "cardset": "A1", - "name": "Squirtle", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 54, - "fields": { - "cardnum": 54, - "cardset": "A1", - "name": "Wartortle", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 55, - "fields": { - "cardnum": 55, - "cardset": "A1", - "name": "Blastoise", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 56, - "fields": { - "cardnum": 56, - "cardset": "A1", - "name": "Blastoise EX", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 57, - "fields": { - "cardnum": 57, - "cardset": "A1", - "name": "Psyduck", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 58, - "fields": { - "cardnum": 58, - "cardset": "A1", - "name": "Golduck", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 59, - "fields": { - "cardnum": 59, - "cardset": "A1", - "name": "Poliwag", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 60, - "fields": { - "cardnum": 60, - "cardset": "A1", - "name": "Poliwhirl", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 61, - "fields": { - "cardnum": 61, - "cardset": "A1", - "name": "Poliwrath", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 62, - "fields": { - "cardnum": 62, - "cardset": "A1", - "name": "Tentacool", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 63, - "fields": { - "cardnum": 63, - "cardset": "A1", - "name": "Tentacruel", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 64, - "fields": { - "cardnum": 64, - "cardset": "A1", - "name": "Seel", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 65, - "fields": { - "cardnum": 65, - "cardset": "A1", - "name": "Dewgong", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 66, - "fields": { - "cardnum": 66, - "cardset": "A1", - "name": "Shellder", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 67, - "fields": { - "cardnum": 67, - "cardset": "A1", - "name": "Cloyster", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 68, - "fields": { - "cardnum": 68, - "cardset": "A1", - "name": "Krabby", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 69, - "fields": { - "cardnum": 69, - "cardset": "A1", - "name": "Kingler", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 70, - "fields": { - "cardnum": 70, - "cardset": "A1", - "name": "Horsea", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 71, - "fields": { - "cardnum": 71, - "cardset": "A1", - "name": "Seadra", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 72, - "fields": { - "cardnum": 72, - "cardset": "A1", - "name": "Goldeen", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 73, - "fields": { - "cardnum": 73, - "cardset": "A1", - "name": "Seaking", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 74, - "fields": { - "cardnum": 74, - "cardset": "A1", - "name": "Staryu", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 75, - "fields": { - "cardnum": 75, - "cardset": "A1", - "name": "Starmie", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 76, - "fields": { - "cardnum": 76, - "cardset": "A1", - "name": "Starmie EX", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 77, - "fields": { - "cardnum": 77, - "cardset": "A1", - "name": "Magikarp", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 78, - "fields": { - "cardnum": 78, - "cardset": "A1", - "name": "Gyarados", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 79, - "fields": { - "cardnum": 79, - "cardset": "A1", - "name": "Lapras", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 80, - "fields": { - "cardnum": 80, - "cardset": "A1", - "name": "Vaporeon", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 81, - "fields": { - "cardnum": 81, - "cardset": "A1", - "name": "Omanyte", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 82, - "fields": { - "cardnum": 82, - "cardset": "A1", - "name": "Omastar", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 83, - "fields": { - "cardnum": 83, - "cardset": "A1", - "name": "Articuno", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 84, - "fields": { - "cardnum": 84, - "cardset": "A1", - "name": "Articuno EX", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 85, - "fields": { - "cardnum": 85, - "cardset": "A1", - "name": "Ducklett", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 86, - "fields": { - "cardnum": 86, - "cardset": "A1", - "name": "Swanna", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 87, - "fields": { - "cardnum": 87, - "cardset": "A1", - "name": "Froakie", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 88, - "fields": { - "cardnum": 88, - "cardset": "A1", - "name": "Frogadier", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 89, - "fields": { - "cardnum": 89, - "cardset": "A1", - "name": "Greninja", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 90, - "fields": { - "cardnum": 90, - "cardset": "A1", - "name": "Pyukumuku", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 91, - "fields": { - "cardnum": 91, - "cardset": "A1", - "name": "Bruxish", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 92, - "fields": { - "cardnum": 92, - "cardset": "A1", - "name": "Snom", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 93, - "fields": { - "cardnum": 93, - "cardset": "A1", - "name": "Frosmoth", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 94, - "fields": { - "cardnum": 94, - "cardset": "A1", - "name": "Pikachu", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 95, - "fields": { - "cardnum": 95, - "cardset": "A1", - "name": "Raichu", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 96, - "fields": { - "cardnum": 96, - "cardset": "A1", - "name": "Pikachu EX", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 97, - "fields": { - "cardnum": 97, - "cardset": "A1", - "name": "Magnemite", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 98, - "fields": { - "cardnum": 98, - "cardset": "A1", - "name": "Magneton", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 99, - "fields": { - "cardnum": 99, - "cardset": "A1", - "name": "Voltorb", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 100, - "fields": { - "cardnum": 100, - "cardset": "A1", - "name": "Electrode", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 101, - "fields": { - "cardnum": 101, - "cardset": "A1", - "name": "Electabuzz", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 102, - "fields": { - "cardnum": 102, - "cardset": "A1", - "name": "Jolteon", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 103, - "fields": { - "cardnum": 103, - "cardset": "A1", - "name": "Zapdos", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 104, - "fields": { - "cardnum": 104, - "cardset": "A1", - "name": "Zapdos EX", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 105, - "fields": { - "cardnum": 105, - "cardset": "A1", - "name": "Blitzle", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 106, - "fields": { - "cardnum": 106, - "cardset": "A1", - "name": "Zebstrika", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 107, - "fields": { - "cardnum": 107, - "cardset": "A1", - "name": "Tynamo", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 108, - "fields": { - "cardnum": 108, - "cardset": "A1", - "name": "Eelektrik", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 109, - "fields": { - "cardnum": 109, - "cardset": "A1", - "name": "Eelektross", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 110, - "fields": { - "cardnum": 110, - "cardset": "A1", - "name": "Helioptile", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 111, - "fields": { - "cardnum": 111, - "cardset": "A1", - "name": "Heliolisk", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 112, - "fields": { - "cardnum": 112, - "cardset": "A1", - "name": "Pincurchin", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 113, - "fields": { - "cardnum": 113, - "cardset": "A1", - "name": "Clefairy", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 114, - "fields": { - "cardnum": 114, - "cardset": "A1", - "name": "Clefable", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 115, - "fields": { - "cardnum": 115, - "cardset": "A1", - "name": "Abra", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 116, - "fields": { - "cardnum": 116, - "cardset": "A1", - "name": "Kadabra", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 117, - "fields": { - "cardnum": 117, - "cardset": "A1", - "name": "Alakazam", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 118, - "fields": { - "cardnum": 118, - "cardset": "A1", - "name": "Slowpoke", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 119, - "fields": { - "cardnum": 119, - "cardset": "A1", - "name": "Slowbro", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 120, - "fields": { - "cardnum": 120, - "cardset": "A1", - "name": "Gastly", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 121, - "fields": { - "cardnum": 121, - "cardset": "A1", - "name": "Haunter", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 122, - "fields": { - "cardnum": 122, - "cardset": "A1", - "name": "Gengar", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 123, - "fields": { - "cardnum": 123, - "cardset": "A1", - "name": "Gengar EX", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 124, - "fields": { - "cardnum": 124, - "cardset": "A1", - "name": "Drowzee", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 125, - "fields": { - "cardnum": 125, - "cardset": "A1", - "name": "Hypno", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 126, - "fields": { - "cardnum": 126, - "cardset": "A1", - "name": "Mr. Mime", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 127, - "fields": { - "cardnum": 127, - "cardset": "A1", - "name": "Jynx", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 128, - "fields": { - "cardnum": 128, - "cardset": "A1", - "name": "Mewtwo", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 129, - "fields": { - "cardnum": 129, - "cardset": "A1", - "name": "Mewtwo EX", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 130, - "fields": { - "cardnum": 130, - "cardset": "A1", - "name": "Ralts", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 131, - "fields": { - "cardnum": 131, - "cardset": "A1", - "name": "Kirlia", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 132, - "fields": { - "cardnum": 132, - "cardset": "A1", - "name": "Gardevoir", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 133, - "fields": { - "cardnum": 133, - "cardset": "A1", - "name": "Woobat", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 134, - "fields": { - "cardnum": 134, - "cardset": "A1", - "name": "Swoobat", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 135, - "fields": { - "cardnum": 135, - "cardset": "A1", - "name": "Golett", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 136, - "fields": { - "cardnum": 136, - "cardset": "A1", - "name": "Golurk", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 137, - "fields": { - "cardnum": 137, - "cardset": "A1", - "name": "Sandshrew", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 138, - "fields": { - "cardnum": 138, - "cardset": "A1", - "name": "Sandslash", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 139, - "fields": { - "cardnum": 139, - "cardset": "A1", - "name": "Diglett", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 140, - "fields": { - "cardnum": 140, - "cardset": "A1", - "name": "Dugtrio", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 141, - "fields": { - "cardnum": 141, - "cardset": "A1", - "name": "Mankey", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 142, - "fields": { - "cardnum": 142, - "cardset": "A1", - "name": "Primeape", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 143, - "fields": { - "cardnum": 143, - "cardset": "A1", - "name": "Machop", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 144, - "fields": { - "cardnum": 144, - "cardset": "A1", - "name": "Machoke", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 145, - "fields": { - "cardnum": 145, - "cardset": "A1", - "name": "Machamp", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 146, - "fields": { - "cardnum": 146, - "cardset": "A1", - "name": "Machamp EX", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 147, - "fields": { - "cardnum": 147, - "cardset": "A1", - "name": "Geodude", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 148, - "fields": { - "cardnum": 148, - "cardset": "A1", - "name": "Graveler", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 149, - "fields": { - "cardnum": 149, - "cardset": "A1", - "name": "Golem", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 150, - "fields": { - "cardnum": 150, - "cardset": "A1", - "name": "Onix", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 151, - "fields": { - "cardnum": 151, - "cardset": "A1", - "name": "Cubone", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 152, - "fields": { - "cardnum": 152, - "cardset": "A1", - "name": "Marowak", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 153, - "fields": { - "cardnum": 153, - "cardset": "A1", - "name": "Marowak EX", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 154, - "fields": { - "cardnum": 154, - "cardset": "A1", - "name": "Hitmonlee", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 155, - "fields": { - "cardnum": 155, - "cardset": "A1", - "name": "Hitmonchan", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 156, - "fields": { - "cardnum": 156, - "cardset": "A1", - "name": "Rhyhorn", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 157, - "fields": { - "cardnum": 157, - "cardset": "A1", - "name": "Rhydon", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 158, - "fields": { - "cardnum": 158, - "cardset": "A1", - "name": "Kabuto", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 159, - "fields": { - "cardnum": 159, - "cardset": "A1", - "name": "Kabutops", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 160, - "fields": { - "cardnum": 160, - "cardset": "A1", - "name": "Mienfoo", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 161, - "fields": { - "cardnum": 161, - "cardset": "A1", - "name": "Mienshao", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 162, - "fields": { - "cardnum": 162, - "cardset": "A1", - "name": "Clobbopus", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 163, - "fields": { - "cardnum": 163, - "cardset": "A1", - "name": "Grapploct", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 164, - "fields": { - "cardnum": 164, - "cardset": "A1", - "name": "Ekans", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 165, - "fields": { - "cardnum": 165, - "cardset": "A1", - "name": "Arbok", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 166, - "fields": { - "cardnum": 166, - "cardset": "A1", - "name": "Nidoran♀", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 167, - "fields": { - "cardnum": 167, - "cardset": "A1", - "name": "Nidorina", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 168, - "fields": { - "cardnum": 168, - "cardset": "A1", - "name": "Nidoqueen", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 169, - "fields": { - "cardnum": 169, - "cardset": "A1", - "name": "Nidoran♂", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 170, - "fields": { - "cardnum": 170, - "cardset": "A1", - "name": "Nidorino", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 171, - "fields": { - "cardnum": 171, - "cardset": "A1", - "name": "Nidoking", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 172, - "fields": { - "cardnum": 172, - "cardset": "A1", - "name": "Zubat", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 173, - "fields": { - "cardnum": 173, - "cardset": "A1", - "name": "Golbat", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 174, - "fields": { - "cardnum": 174, - "cardset": "A1", - "name": "Grimer", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 175, - "fields": { - "cardnum": 175, - "cardset": "A1", - "name": "Muk", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 176, - "fields": { - "cardnum": 176, - "cardset": "A1", - "name": "Koffing", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 177, - "fields": { - "cardnum": 177, - "cardset": "A1", - "name": "Weezing", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 178, - "fields": { - "cardnum": 178, - "cardset": "A1", - "name": "Mawile", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 179, - "fields": { - "cardnum": 179, - "cardset": "A1", - "name": "Pawniard", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 180, - "fields": { - "cardnum": 180, - "cardset": "A1", - "name": "Bisharp", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 181, - "fields": { - "cardnum": 181, - "cardset": "A1", - "name": "Meltan", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 182, - "fields": { - "cardnum": 182, - "cardset": "A1", - "name": "Melmetal", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 183, - "fields": { - "cardnum": 183, - "cardset": "A1", - "name": "Dratini", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 184, - "fields": { - "cardnum": 184, - "cardset": "A1", - "name": "Dragonair", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 185, - "fields": { - "cardnum": 185, - "cardset": "A1", - "name": "Dragonite", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 186, - "fields": { - "cardnum": 186, - "cardset": "A1", - "name": "Pidgey", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 187, - "fields": { - "cardnum": 187, - "cardset": "A1", - "name": "Pidgeotto", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 188, - "fields": { - "cardnum": 188, - "cardset": "A1", - "name": "Pidgeot", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 189, - "fields": { - "cardnum": 189, - "cardset": "A1", - "name": "Rattata", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 190, - "fields": { - "cardnum": 190, - "cardset": "A1", - "name": "Raticate", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 191, - "fields": { - "cardnum": 191, - "cardset": "A1", - "name": "Spearow", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 192, - "fields": { - "cardnum": 192, - "cardset": "A1", - "name": "Fearow", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 193, - "fields": { - "cardnum": 193, - "cardset": "A1", - "name": "Jigglypuff", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 194, - "fields": { - "cardnum": 194, - "cardset": "A1", - "name": "Wigglytuff", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 195, - "fields": { - "cardnum": 195, - "cardset": "A1", - "name": "Wigglytuff EX", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 196, - "fields": { - "cardnum": 196, - "cardset": "A1", - "name": "Meowth", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 197, - "fields": { - "cardnum": 197, - "cardset": "A1", - "name": "Persian", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 198, - "fields": { - "cardnum": 198, - "cardset": "A1", - "name": "Farfetch'd", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 199, - "fields": { - "cardnum": 199, - "cardset": "A1", - "name": "Doduo", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 200, - "fields": { - "cardnum": 200, - "cardset": "A1", - "name": "Dodrio", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 201, - "fields": { - "cardnum": 201, - "cardset": "A1", - "name": "Lickitung", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 202, - "fields": { - "cardnum": 202, - "cardset": "A1", - "name": "Chansey", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 203, - "fields": { - "cardnum": 203, - "cardset": "A1", - "name": "Kangaskhan", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 204, - "fields": { - "cardnum": 204, - "cardset": "A1", - "name": "Tauros", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 205, - "fields": { - "cardnum": 205, - "cardset": "A1", - "name": "Ditto", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 206, - "fields": { - "cardnum": 206, - "cardset": "A1", - "name": "Eevee", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 207, - "fields": { - "cardnum": 207, - "cardset": "A1", - "name": "Eevee", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 208, - "fields": { - "cardnum": 208, - "cardset": "A1", - "name": "Eevee", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 209, - "fields": { - "cardnum": 209, - "cardset": "A1", - "name": "Porygon", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 210, - "fields": { - "cardnum": 210, - "cardset": "A1", - "name": "Aerodactyl", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 211, - "fields": { - "cardnum": 211, - "cardset": "A1", - "name": "Snorlax", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 212, - "fields": { - "cardnum": 212, - "cardset": "A1", - "name": "Minccino", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 213, - "fields": { - "cardnum": 213, - "cardset": "A1", - "name": "Cinccino", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 214, - "fields": { - "cardnum": 214, - "cardset": "A1", - "name": "Wooloo", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 215, - "fields": { - "cardnum": 215, - "cardset": "A1", - "name": "Dubwool", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 216, - "fields": { - "cardnum": 216, - "cardset": "A1", - "name": "Helix Fossil", - "decks": [ - 4 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 217, - "fields": { - "cardnum": 217, - "cardset": "A1", - "name": "Dome Fossil", - "decks": [ - 3 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 218, - "fields": { - "cardnum": 218, - "cardset": "A1", - "name": "Old Amber", - "decks": [ - 2 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 219, - "fields": { - "cardnum": 219, - "cardset": "A1", - "name": "Erika", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 220, - "fields": { - "cardnum": 220, - "cardset": "A1", - "name": "Misty", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 221, - "fields": { - "cardnum": 221, - "cardset": "A1", - "name": "Blaine", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 222, - "fields": { - "cardnum": 222, - "cardset": "A1", - "name": "Koga", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 223, - "fields": { - "cardnum": 223, - "cardset": "A1", - "name": "Giovanni", - "decks": [ - 2 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 224, - "fields": { - "cardnum": 224, - "cardset": "A1", - "name": "Brock", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 225, - "fields": { - "cardnum": 225, - "cardset": "A1", - "name": "Sabrina", - "decks": [ - 3 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 226, - "fields": { - "cardnum": 226, - "cardset": "A1", - "name": "Lt. Surge", - "decks": [ - 4 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 227, - "fields": { - "cardnum": 227, - "cardset": "A1", - "name": "Bulbasaur", - "decks": [ - 2 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 228, - "fields": { - "cardnum": 228, - "cardset": "A1", - "name": "Gloom", - "decks": [ - 3 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 229, - "fields": { - "cardnum": 229, - "cardset": "A1", - "name": "Pinsir", - "decks": [ - 3 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 230, - "fields": { - "cardnum": 230, - "cardset": "A1", - "name": "Charmander", - "decks": [ - 3 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 231, - "fields": { - "cardnum": 231, - "cardset": "A1", - "name": "Rapidash", - "decks": [ - 3 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 232, - "fields": { - "cardnum": 232, - "cardset": "A1", - "name": "Squirtle", - "decks": [ - 4 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 233, - "fields": { - "cardnum": 233, - "cardset": "A1", - "name": "Gyarados", - "decks": [ - 4 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 234, - "fields": { - "cardnum": 234, - "cardset": "A1", - "name": "Lapras", - "decks": [ - 3 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 235, - "fields": { - "cardnum": 235, - "cardset": "A1", - "name": "Electrode", - "decks": [ - 4 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 236, - "fields": { - "cardnum": 236, - "cardset": "A1", - "name": "Alakazam", - "decks": [ - 3 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 237, - "fields": { - "cardnum": 237, - "cardset": "A1", - "name": "Slowpoke", - "decks": [ - 3 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 238, - "fields": { - "cardnum": 238, - "cardset": "A1", - "name": "Diglett", - "decks": [ - 4 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 239, - "fields": { - "cardnum": 239, - "cardset": "A1", - "name": "Cubone", - "decks": [ - 2 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 240, - "fields": { - "cardnum": 240, - "cardset": "A1", - "name": "Nidoqueen", - "decks": [ - 4 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 241, - "fields": { - "cardnum": 241, - "cardset": "A1", - "name": "Nidoking", - "decks": [ - 4 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 242, - "fields": { - "cardnum": 242, - "cardset": "A1", - "name": "Golbat", - "decks": [ - 2 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 243, - "fields": { - "cardnum": 243, - "cardset": "A1", - "name": "Weezing", - "decks": [ - 2 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 244, - "fields": { - "cardnum": 244, - "cardset": "A1", - "name": "Dragonite", - "decks": [ - 2 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 245, - "fields": { - "cardnum": 245, - "cardset": "A1", - "name": "Pidgeot", - "decks": [ - 2 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 246, - "fields": { - "cardnum": 246, - "cardset": "A1", - "name": "Meowth", - "decks": [ - 3 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 247, - "fields": { - "cardnum": 247, - "cardset": "A1", - "name": "Ditto", - "decks": [ - 2 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 248, - "fields": { - "cardnum": 248, - "cardset": "A1", - "name": "Eevee", - "decks": [ - 4 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 249, - "fields": { - "cardnum": 249, - "cardset": "A1", - "name": "Porygon", - "decks": [ - 2 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 250, - "fields": { - "cardnum": 250, - "cardset": "A1", - "name": "Snorlax", - "decks": [ - 4 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 251, - "fields": { - "cardnum": 251, - "cardset": "A1", - "name": "Venusaur EX", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 252, - "fields": { - "cardnum": 252, - "cardset": "A1", - "name": "Exeggutor EX", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 253, - "fields": { - "cardnum": 253, - "cardset": "A1", - "name": "Charizard EX", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 254, - "fields": { - "cardnum": 254, - "cardset": "A1", - "name": "Arcanine EX", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 255, - "fields": { - "cardnum": 255, - "cardset": "A1", - "name": "Moltres EX", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 256, - "fields": { - "cardnum": 256, - "cardset": "A1", - "name": "Blastoise EX", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 257, - "fields": { - "cardnum": 257, - "cardset": "A1", - "name": "Starmie EX", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 258, - "fields": { - "cardnum": 258, - "cardset": "A1", - "name": "Articuno EX", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 259, - "fields": { - "cardnum": 259, - "cardset": "A1", - "name": "Pikachu EX", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 260, - "fields": { - "cardnum": 260, - "cardset": "A1", - "name": "Zapdos EX", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 261, - "fields": { - "cardnum": 261, - "cardset": "A1", - "name": "Gengar EX", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 262, - "fields": { - "cardnum": 262, - "cardset": "A1", - "name": "Mewtwo EX", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 263, - "fields": { - "cardnum": 263, - "cardset": "A1", - "name": "Machamp EX", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 264, - "fields": { - "cardnum": 264, - "cardset": "A1", - "name": "Marowak EX", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 265, - "fields": { - "cardnum": 265, - "cardset": "A1", - "name": "Wigglytuff EX", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 266, - "fields": { - "cardnum": 266, - "cardset": "A1", - "name": "Erika", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 267, - "fields": { - "cardnum": 267, - "cardset": "A1", - "name": "Misty", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 268, - "fields": { - "cardnum": 268, - "cardset": "A1", - "name": "Blaine", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 269, - "fields": { - "cardnum": 269, - "cardset": "A1", - "name": "Koga", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 270, - "fields": { - "cardnum": 270, - "cardset": "A1", - "name": "Giovanni", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 271, - "fields": { - "cardnum": 271, - "cardset": "A1", - "name": "Brock", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 272, - "fields": { - "cardnum": 272, - "cardset": "A1", - "name": "Sabrina", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 273, - "fields": { - "cardnum": 273, - "cardset": "A1", - "name": "Lt. Surge", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 274, - "fields": { - "cardnum": 274, - "cardset": "A1", - "name": "Moltres EX", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 275, - "fields": { - "cardnum": 275, - "cardset": "A1", - "name": "Articuno EX", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 276, - "fields": { - "cardnum": 276, - "cardset": "A1", - "name": "Zapdos EX", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 277, - "fields": { - "cardnum": 277, - "cardset": "A1", - "name": "Gengar EX", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 278, - "fields": { - "cardnum": 278, - "cardset": "A1", - "name": "Machamp EX", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 279, - "fields": { - "cardnum": 279, - "cardset": "A1", - "name": "Wigglytuff EX", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 280, - "fields": { - "cardnum": 280, - "cardset": "A1", - "name": "Charizard EX", - "decks": [ - 3 - ], - "rarity_icon": "⭐️⭐️⭐️", - "rarity_level": 7, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 281, - "fields": { - "cardnum": 281, - "cardset": "A1", - "name": "Pikachu EX", - "decks": [ - 4 - ], - "rarity_icon": "⭐️⭐️⭐️", - "rarity_level": 7, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 282, - "fields": { - "cardnum": 282, - "cardset": "A1", - "name": "Mewtwo EX", - "decks": [ - 2 - ], - "rarity_icon": "⭐️⭐️⭐️", - "rarity_level": 7, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 283, - "fields": { - "cardnum": 283, - "cardset": "A1", - "name": "Mew", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "⭐️⭐️⭐️", - "rarity_level": 7, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 284, - "fields": { - "cardnum": 284, - "cardset": "A1", - "name": "Charizard EX", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "👑", - "rarity_level": 8, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 285, - "fields": { - "cardnum": 285, - "cardset": "A1", - "name": "Pikachu EX", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "👑", - "rarity_level": 8, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 286, - "fields": { - "cardnum": 286, - "cardset": "A1", - "name": "Mewtwo EX", - "decks": [ - 2, - 3, - 4 - ], - "rarity_icon": "👑", - "rarity_level": 8, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 287, - "fields": { - "cardnum": 1, - "cardset": "A1a", - "name": "Exeggcute", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 288, - "fields": { - "cardnum": 2, - "cardset": "A1a", - "name": "Exeggutor", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 289, - "fields": { - "cardnum": 3, - "cardset": "A1a", - "name": "Celebi EX", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 290, - "fields": { - "cardnum": 4, - "cardset": "A1a", - "name": "Snivy", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 291, - "fields": { - "cardnum": 5, - "cardset": "A1a", - "name": "Servine", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 292, - "fields": { - "cardnum": 6, - "cardset": "A1a", - "name": "Serperior", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 293, - "fields": { - "cardnum": 7, - "cardset": "A1a", - "name": "Morelull", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 294, - "fields": { - "cardnum": 8, - "cardset": "A1a", - "name": "Shiinotic", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 295, - "fields": { - "cardnum": 9, - "cardset": "A1a", - "name": "Dhelmise", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 296, - "fields": { - "cardnum": 10, - "cardset": "A1a", - "name": "Ponyta", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 297, - "fields": { - "cardnum": 11, - "cardset": "A1a", - "name": "Rapidash", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 298, - "fields": { - "cardnum": 12, - "cardset": "A1a", - "name": "Magmar", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 299, - "fields": { - "cardnum": 13, - "cardset": "A1a", - "name": "Larvesta", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 300, - "fields": { - "cardnum": 14, - "cardset": "A1a", - "name": "Volcarona", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 301, - "fields": { - "cardnum": 15, - "cardset": "A1a", - "name": "Salandit", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 302, - "fields": { - "cardnum": 16, - "cardset": "A1a", - "name": "Salazzle", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 303, - "fields": { - "cardnum": 17, - "cardset": "A1a", - "name": "Magikarp", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 304, - "fields": { - "cardnum": 18, - "cardset": "A1a", - "name": "Gyarados EX", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 305, - "fields": { - "cardnum": 19, - "cardset": "A1a", - "name": "Vaporeon", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 306, - "fields": { - "cardnum": 20, - "cardset": "A1a", - "name": "Finneon", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 307, - "fields": { - "cardnum": 21, - "cardset": "A1a", - "name": "Lumineon", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 308, - "fields": { - "cardnum": 22, - "cardset": "A1a", - "name": "Chewtle", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 309, - "fields": { - "cardnum": 23, - "cardset": "A1a", - "name": "Drednaw", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 310, - "fields": { - "cardnum": 24, - "cardset": "A1a", - "name": "Cramorant", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 311, - "fields": { - "cardnum": 25, - "cardset": "A1a", - "name": "Pikachu", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 312, - "fields": { - "cardnum": 26, - "cardset": "A1a", - "name": "Raichu", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 313, - "fields": { - "cardnum": 27, - "cardset": "A1a", - "name": "Electabuzz", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 314, - "fields": { - "cardnum": 28, - "cardset": "A1a", - "name": "Joltik", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 315, - "fields": { - "cardnum": 29, - "cardset": "A1a", - "name": "Galvantula", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 316, - "fields": { - "cardnum": 30, - "cardset": "A1a", - "name": "Dedenne", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 317, - "fields": { - "cardnum": 31, - "cardset": "A1a", - "name": "Mew", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 318, - "fields": { - "cardnum": 32, - "cardset": "A1a", - "name": "Mew EX", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 319, - "fields": { - "cardnum": 33, - "cardset": "A1a", - "name": "Sigilyph", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 320, - "fields": { - "cardnum": 34, - "cardset": "A1a", - "name": "Elgyem", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 321, - "fields": { - "cardnum": 35, - "cardset": "A1a", - "name": "Beheeyem", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 322, - "fields": { - "cardnum": 36, - "cardset": "A1a", - "name": "Flabébé", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 323, - "fields": { - "cardnum": 37, - "cardset": "A1a", - "name": "Floette", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 324, - "fields": { - "cardnum": 38, - "cardset": "A1a", - "name": "Florges", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 325, - "fields": { - "cardnum": 39, - "cardset": "A1a", - "name": "Swirlix", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 326, - "fields": { - "cardnum": 40, - "cardset": "A1a", - "name": "Slurpuff", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 327, - "fields": { - "cardnum": 41, - "cardset": "A1a", - "name": "Mankey", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 328, - "fields": { - "cardnum": 42, - "cardset": "A1a", - "name": "Primeape", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 329, - "fields": { - "cardnum": 43, - "cardset": "A1a", - "name": "Geodude", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 330, - "fields": { - "cardnum": 44, - "cardset": "A1a", - "name": "Graveler", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 331, - "fields": { - "cardnum": 45, - "cardset": "A1a", - "name": "Golem", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 332, - "fields": { - "cardnum": 46, - "cardset": "A1a", - "name": "Aerodactyl EX", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 333, - "fields": { - "cardnum": 47, - "cardset": "A1a", - "name": "Marshadow", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 334, - "fields": { - "cardnum": 48, - "cardset": "A1a", - "name": "Stonjourner", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 335, - "fields": { - "cardnum": 49, - "cardset": "A1a", - "name": "Koffing", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 336, - "fields": { - "cardnum": 50, - "cardset": "A1a", - "name": "Weezing", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 337, - "fields": { - "cardnum": 51, - "cardset": "A1a", - "name": "Purrloin", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 338, - "fields": { - "cardnum": 52, - "cardset": "A1a", - "name": "Liepard", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 339, - "fields": { - "cardnum": 53, - "cardset": "A1a", - "name": "Venipede", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 340, - "fields": { - "cardnum": 54, - "cardset": "A1a", - "name": "Whirlipede", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 341, - "fields": { - "cardnum": 55, - "cardset": "A1a", - "name": "Scolipede", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 342, - "fields": { - "cardnum": 56, - "cardset": "A1a", - "name": "Druddigon", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 343, - "fields": { - "cardnum": 57, - "cardset": "A1a", - "name": "Pidgey", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 344, - "fields": { - "cardnum": 58, - "cardset": "A1a", - "name": "Pidgeotto", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 345, - "fields": { - "cardnum": 59, - "cardset": "A1a", - "name": "Pidgeot EX", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 346, - "fields": { - "cardnum": 60, - "cardset": "A1a", - "name": "Tauros", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 347, - "fields": { - "cardnum": 61, - "cardset": "A1a", - "name": "Eevee", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 348, - "fields": { - "cardnum": 62, - "cardset": "A1a", - "name": "Chatot", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 349, - "fields": { - "cardnum": 63, - "cardset": "A1a", - "name": "Old Amber", - "decks": [ - 5 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 350, - "fields": { - "cardnum": 64, - "cardset": "A1a", - "name": "Pokémon Flute", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 351, - "fields": { - "cardnum": 65, - "cardset": "A1a", - "name": "Mythical Slab", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 352, - "fields": { - "cardnum": 66, - "cardset": "A1a", - "name": "Budding Expeditioner", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 353, - "fields": { - "cardnum": 67, - "cardset": "A1a", - "name": "Blue", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 354, - "fields": { - "cardnum": 68, - "cardset": "A1a", - "name": "Leaf", - "decks": [ - 5 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 355, - "fields": { - "cardnum": 69, - "cardset": "A1a", - "name": "Exeggutor", - "decks": [ - 5 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 356, - "fields": { - "cardnum": 70, - "cardset": "A1a", - "name": "Serperior", - "decks": [ - 5 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 357, - "fields": { - "cardnum": 71, - "cardset": "A1a", - "name": "Salandit", - "decks": [ - 5 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 358, - "fields": { - "cardnum": 72, - "cardset": "A1a", - "name": "Vaporeon", - "decks": [ - 5 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 359, - "fields": { - "cardnum": 73, - "cardset": "A1a", - "name": "Dedenne", - "decks": [ - 5 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 360, - "fields": { - "cardnum": 74, - "cardset": "A1a", - "name": "Marshadow", - "decks": [ - 5 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 361, - "fields": { - "cardnum": 75, - "cardset": "A1a", - "name": "Celebi EX", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 362, - "fields": { - "cardnum": 76, - "cardset": "A1a", - "name": "Gyarados EX", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 363, - "fields": { - "cardnum": 77, - "cardset": "A1a", - "name": "Mew EX", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 364, - "fields": { - "cardnum": 78, - "cardset": "A1a", - "name": "Aerodactyl EX", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 365, - "fields": { - "cardnum": 79, - "cardset": "A1a", - "name": "Pidgeot EX", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 366, - "fields": { - "cardnum": 80, - "cardset": "A1a", - "name": "Budding Expeditioner", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 367, - "fields": { - "cardnum": 81, - "cardset": "A1a", - "name": "Blue", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 368, - "fields": { - "cardnum": 82, - "cardset": "A1a", - "name": "Leaf", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 369, - "fields": { - "cardnum": 83, - "cardset": "A1a", - "name": "Mew EX", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 370, - "fields": { - "cardnum": 84, - "cardset": "A1a", - "name": "Aerodactyl EX", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 371, - "fields": { - "cardnum": 85, - "cardset": "A1a", - "name": "Celebi EX", - "decks": [ - 5 - ], - "rarity_icon": "⭐️⭐️⭐️", - "rarity_level": 7, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 372, - "fields": { - "cardnum": 86, - "cardset": "A1a", - "name": "Mew EX", - "decks": [ - 5 - ], - "rarity_icon": "👑", - "rarity_level": 8, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 373, - "fields": { - "cardnum": 1, - "cardset": "A2", - "name": "Oddish", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 374, - "fields": { - "cardnum": 2, - "cardset": "A2", - "name": "Gloom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 375, - "fields": { - "cardnum": 3, - "cardset": "A2", - "name": "Bellossom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 376, - "fields": { - "cardnum": 4, - "cardset": "A2", - "name": "Tangela", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 377, - "fields": { - "cardnum": 5, - "cardset": "A2", - "name": "Tangrowth", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 378, - "fields": { - "cardnum": 6, - "cardset": "A2", - "name": "Yanma", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 379, - "fields": { - "cardnum": 7, - "cardset": "A2", - "name": "Yanmega EX", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 380, - "fields": { - "cardnum": 8, - "cardset": "A2", - "name": "Roselia", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 381, - "fields": { - "cardnum": 9, - "cardset": "A2", - "name": "Roserade", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 382, - "fields": { - "cardnum": 10, - "cardset": "A2", - "name": "Turtwig", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 383, - "fields": { - "cardnum": 11, - "cardset": "A2", - "name": "Grotle", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 384, - "fields": { - "cardnum": 12, - "cardset": "A2", - "name": "Torterra", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 385, - "fields": { - "cardnum": 13, - "cardset": "A2", - "name": "Kricketot", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 386, - "fields": { - "cardnum": 14, - "cardset": "A2", - "name": "Kricketune", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 387, - "fields": { - "cardnum": 15, - "cardset": "A2", - "name": "Burmy", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 388, - "fields": { - "cardnum": 16, - "cardset": "A2", - "name": "Wormadam", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 389, - "fields": { - "cardnum": 17, - "cardset": "A2", - "name": "Combee", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 390, - "fields": { - "cardnum": 18, - "cardset": "A2", - "name": "Vespiquen", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 391, - "fields": { - "cardnum": 19, - "cardset": "A2", - "name": "Carnivine", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 392, - "fields": { - "cardnum": 20, - "cardset": "A2", - "name": "Leafeon", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 393, - "fields": { - "cardnum": 21, - "cardset": "A2", - "name": "Mow Rotom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 394, - "fields": { - "cardnum": 22, - "cardset": "A2", - "name": "Shaymin", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 395, - "fields": { - "cardnum": 23, - "cardset": "A2", - "name": "Magmar", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 396, - "fields": { - "cardnum": 24, - "cardset": "A2", - "name": "Magmortar", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 397, - "fields": { - "cardnum": 25, - "cardset": "A2", - "name": "Slugma", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 398, - "fields": { - "cardnum": 26, - "cardset": "A2", - "name": "Magcargo", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 399, - "fields": { - "cardnum": 27, - "cardset": "A2", - "name": "Chimchar", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 400, - "fields": { - "cardnum": 28, - "cardset": "A2", - "name": "Monferno", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 401, - "fields": { - "cardnum": 29, - "cardset": "A2", - "name": "Infernape EX", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 402, - "fields": { - "cardnum": 30, - "cardset": "A2", - "name": "Heat Rotom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 403, - "fields": { - "cardnum": 31, - "cardset": "A2", - "name": "Swinub", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 404, - "fields": { - "cardnum": 32, - "cardset": "A2", - "name": "Piloswine", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 405, - "fields": { - "cardnum": 33, - "cardset": "A2", - "name": "Mamoswine", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 406, - "fields": { - "cardnum": 34, - "cardset": "A2", - "name": "Regice", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 407, - "fields": { - "cardnum": 35, - "cardset": "A2", - "name": "Piplup", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 408, - "fields": { - "cardnum": 36, - "cardset": "A2", - "name": "Prinplup", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 409, - "fields": { - "cardnum": 37, - "cardset": "A2", - "name": "Empoleon", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 410, - "fields": { - "cardnum": 38, - "cardset": "A2", - "name": "Buizel", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 411, - "fields": { - "cardnum": 39, - "cardset": "A2", - "name": "Floatzel", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 412, - "fields": { - "cardnum": 40, - "cardset": "A2", - "name": "Shellos", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 413, - "fields": { - "cardnum": 41, - "cardset": "A2", - "name": "Gastrodon", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 414, - "fields": { - "cardnum": 42, - "cardset": "A2", - "name": "Finneon", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 415, - "fields": { - "cardnum": 43, - "cardset": "A2", - "name": "Lumineon", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 416, - "fields": { - "cardnum": 44, - "cardset": "A2", - "name": "Snover", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 417, - "fields": { - "cardnum": 45, - "cardset": "A2", - "name": "Abomasnow", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 418, - "fields": { - "cardnum": 46, - "cardset": "A2", - "name": "Glaceon", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 419, - "fields": { - "cardnum": 47, - "cardset": "A2", - "name": "Wash Rotom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 420, - "fields": { - "cardnum": 48, - "cardset": "A2", - "name": "Frost Rotom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 421, - "fields": { - "cardnum": 49, - "cardset": "A2", - "name": "Palkia EX", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 422, - "fields": { - "cardnum": 50, - "cardset": "A2", - "name": "Manaphy", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 423, - "fields": { - "cardnum": 51, - "cardset": "A2", - "name": "Magnemite", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 424, - "fields": { - "cardnum": 52, - "cardset": "A2", - "name": "Magneton", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 425, - "fields": { - "cardnum": 53, - "cardset": "A2", - "name": "Magnezone", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 426, - "fields": { - "cardnum": 54, - "cardset": "A2", - "name": "Voltorb", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 427, - "fields": { - "cardnum": 55, - "cardset": "A2", - "name": "Electrode", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 428, - "fields": { - "cardnum": 56, - "cardset": "A2", - "name": "Electabuzz", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 429, - "fields": { - "cardnum": 57, - "cardset": "A2", - "name": "Electivire", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 430, - "fields": { - "cardnum": 58, - "cardset": "A2", - "name": "Shinx", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 431, - "fields": { - "cardnum": 59, - "cardset": "A2", - "name": "Luxio", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 432, - "fields": { - "cardnum": 60, - "cardset": "A2", - "name": "Luxray", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 433, - "fields": { - "cardnum": 61, - "cardset": "A2", - "name": "Pachirisu EX", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 434, - "fields": { - "cardnum": 62, - "cardset": "A2", - "name": "Rotom", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 435, - "fields": { - "cardnum": 63, - "cardset": "A2", - "name": "Togepi", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 436, - "fields": { - "cardnum": 64, - "cardset": "A2", - "name": "Togetic", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 437, - "fields": { - "cardnum": 65, - "cardset": "A2", - "name": "Togekiss", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 438, - "fields": { - "cardnum": 66, - "cardset": "A2", - "name": "Misdreavus", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 439, - "fields": { - "cardnum": 67, - "cardset": "A2", - "name": "Mismagius EX", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 440, - "fields": { - "cardnum": 68, - "cardset": "A2", - "name": "Ralts", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 441, - "fields": { - "cardnum": 69, - "cardset": "A2", - "name": "Kirlia", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 442, - "fields": { - "cardnum": 70, - "cardset": "A2", - "name": "Duskull", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 443, - "fields": { - "cardnum": 71, - "cardset": "A2", - "name": "Dusclops", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 444, - "fields": { - "cardnum": 72, - "cardset": "A2", - "name": "Dusknoir", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 445, - "fields": { - "cardnum": 73, - "cardset": "A2", - "name": "Drifloon", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 446, - "fields": { - "cardnum": 74, - "cardset": "A2", - "name": "Drifblim", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 447, - "fields": { - "cardnum": 75, - "cardset": "A2", - "name": "Uxie", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 448, - "fields": { - "cardnum": 76, - "cardset": "A2", - "name": "Mesprit", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 449, - "fields": { - "cardnum": 77, - "cardset": "A2", - "name": "Azelf", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 450, - "fields": { - "cardnum": 78, - "cardset": "A2", - "name": "Giratina", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 451, - "fields": { - "cardnum": 79, - "cardset": "A2", - "name": "Cresselia", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 452, - "fields": { - "cardnum": 80, - "cardset": "A2", - "name": "Rhyhorn", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 453, - "fields": { - "cardnum": 81, - "cardset": "A2", - "name": "Rhydon", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 454, - "fields": { - "cardnum": 82, - "cardset": "A2", - "name": "Rhyperior", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 455, - "fields": { - "cardnum": 83, - "cardset": "A2", - "name": "Gligar", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 456, - "fields": { - "cardnum": 84, - "cardset": "A2", - "name": "Gliscor", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 457, - "fields": { - "cardnum": 85, - "cardset": "A2", - "name": "Hitmontop", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 458, - "fields": { - "cardnum": 86, - "cardset": "A2", - "name": "Nosepass", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 459, - "fields": { - "cardnum": 87, - "cardset": "A2", - "name": "Regirock", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 460, - "fields": { - "cardnum": 88, - "cardset": "A2", - "name": "Cranidos", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 461, - "fields": { - "cardnum": 89, - "cardset": "A2", - "name": "Rampardos", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 462, - "fields": { - "cardnum": 90, - "cardset": "A2", - "name": "Wormadam", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 463, - "fields": { - "cardnum": 91, - "cardset": "A2", - "name": "Riolu", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 464, - "fields": { - "cardnum": 92, - "cardset": "A2", - "name": "Lucario", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 465, - "fields": { - "cardnum": 93, - "cardset": "A2", - "name": "Hippopotas", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 466, - "fields": { - "cardnum": 94, - "cardset": "A2", - "name": "Hippowdon", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 467, - "fields": { - "cardnum": 95, - "cardset": "A2", - "name": "Gallade EX", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 468, - "fields": { - "cardnum": 96, - "cardset": "A2", - "name": "Murkrow", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 469, - "fields": { - "cardnum": 97, - "cardset": "A2", - "name": "Honchkrow", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 470, - "fields": { - "cardnum": 98, - "cardset": "A2", - "name": "Sneasel", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 471, - "fields": { - "cardnum": 99, - "cardset": "A2", - "name": "Weavile EX", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 472, - "fields": { - "cardnum": 100, - "cardset": "A2", - "name": "Poochyena", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 473, - "fields": { - "cardnum": 101, - "cardset": "A2", - "name": "Mightyena", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 474, - "fields": { - "cardnum": 102, - "cardset": "A2", - "name": "Stunky", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 475, - "fields": { - "cardnum": 103, - "cardset": "A2", - "name": "Skuntank", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 476, - "fields": { - "cardnum": 104, - "cardset": "A2", - "name": "Spiritomb", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 477, - "fields": { - "cardnum": 105, - "cardset": "A2", - "name": "Skorupi", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 478, - "fields": { - "cardnum": 106, - "cardset": "A2", - "name": "Drapion", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 479, - "fields": { - "cardnum": 107, - "cardset": "A2", - "name": "Croagunk", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 480, - "fields": { - "cardnum": 108, - "cardset": "A2", - "name": "Toxicroak", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 481, - "fields": { - "cardnum": 109, - "cardset": "A2", - "name": "Darkrai", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 482, - "fields": { - "cardnum": 110, - "cardset": "A2", - "name": "Darkrai EX", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 483, - "fields": { - "cardnum": 111, - "cardset": "A2", - "name": "Skarmory", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 484, - "fields": { - "cardnum": 112, - "cardset": "A2", - "name": "Registeel", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 485, - "fields": { - "cardnum": 113, - "cardset": "A2", - "name": "Shieldon", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 486, - "fields": { - "cardnum": 114, - "cardset": "A2", - "name": "Bastiodon", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 487, - "fields": { - "cardnum": 115, - "cardset": "A2", - "name": "Wormadam", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 488, - "fields": { - "cardnum": 116, - "cardset": "A2", - "name": "Bronzor", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 489, - "fields": { - "cardnum": 117, - "cardset": "A2", - "name": "Bronzong", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 490, - "fields": { - "cardnum": 118, - "cardset": "A2", - "name": "Probopass", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 491, - "fields": { - "cardnum": 119, - "cardset": "A2", - "name": "Dialga EX", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 492, - "fields": { - "cardnum": 120, - "cardset": "A2", - "name": "Heatran", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 493, - "fields": { - "cardnum": 121, - "cardset": "A2", - "name": "Gible", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 494, - "fields": { - "cardnum": 122, - "cardset": "A2", - "name": "Gabite", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 495, - "fields": { - "cardnum": 123, - "cardset": "A2", - "name": "Garchomp", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 496, - "fields": { - "cardnum": 124, - "cardset": "A2", - "name": "Lickitung", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 497, - "fields": { - "cardnum": 125, - "cardset": "A2", - "name": "Lickilicky EX", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷🔷", - "rarity_level": 4, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 498, - "fields": { - "cardnum": 126, - "cardset": "A2", - "name": "Eevee", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 499, - "fields": { - "cardnum": 127, - "cardset": "A2", - "name": "Porygon", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 500, - "fields": { - "cardnum": 128, - "cardset": "A2", - "name": "Porygon2", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 501, - "fields": { - "cardnum": 129, - "cardset": "A2", - "name": "Porygon-Z", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 502, - "fields": { - "cardnum": 130, - "cardset": "A2", - "name": "Aipom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 503, - "fields": { - "cardnum": 131, - "cardset": "A2", - "name": "Ambipom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 504, - "fields": { - "cardnum": 132, - "cardset": "A2", - "name": "Starly", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 505, - "fields": { - "cardnum": 133, - "cardset": "A2", - "name": "Staravia", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 506, - "fields": { - "cardnum": 134, - "cardset": "A2", - "name": "Staraptor", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 507, - "fields": { - "cardnum": 135, - "cardset": "A2", - "name": "Bidoof", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 508, - "fields": { - "cardnum": 136, - "cardset": "A2", - "name": "Bibarel", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 509, - "fields": { - "cardnum": 137, - "cardset": "A2", - "name": "Buneary", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 510, - "fields": { - "cardnum": 138, - "cardset": "A2", - "name": "Lopunny", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 511, - "fields": { - "cardnum": 139, - "cardset": "A2", - "name": "Glameow", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 512, - "fields": { - "cardnum": 140, - "cardset": "A2", - "name": "Purugly", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 513, - "fields": { - "cardnum": 141, - "cardset": "A2", - "name": "Chatot", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 514, - "fields": { - "cardnum": 142, - "cardset": "A2", - "name": "Fan Rotom", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 515, - "fields": { - "cardnum": 143, - "cardset": "A2", - "name": "Regigigas", - "decks": [ - 6, - 7 - ], - "rarity_icon": "🔷🔷🔷", - "rarity_level": 3, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 516, - "fields": { - "cardnum": 144, - "cardset": "A2", - "name": "Skull Fossil", - "decks": [ - 6 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 517, - "fields": { - "cardnum": 145, - "cardset": "A2", - "name": "Armor Fossil", - "decks": [ - 7 - ], - "rarity_icon": "🔷", - "rarity_level": 1, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 518, - "fields": { - "cardnum": 146, - "cardset": "A2", - "name": "Pokémon Communication", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 519, - "fields": { - "cardnum": 147, - "cardset": "A2", - "name": "Giant Cape", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 520, - "fields": { - "cardnum": 148, - "cardset": "A2", - "name": "Rocky Helmet", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 521, - "fields": { - "cardnum": 149, - "cardset": "A2", - "name": "Lum Berry", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 522, - "fields": { - "cardnum": 150, - "cardset": "A2", - "name": "Cyrus", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 523, - "fields": { - "cardnum": 151, - "cardset": "A2", - "name": "Team Galactic Grunt", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 524, - "fields": { - "cardnum": 152, - "cardset": "A2", - "name": "Cynthia", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 525, - "fields": { - "cardnum": 153, - "cardset": "A2", - "name": "Volkner", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 526, - "fields": { - "cardnum": 154, - "cardset": "A2", - "name": "Dawn", - "decks": [ - 6 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 527, - "fields": { - "cardnum": 155, - "cardset": "A2", - "name": "Mars", - "decks": [ - 7 - ], - "rarity_icon": "🔷🔷", - "rarity_level": 2, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 528, - "fields": { - "cardnum": 156, - "cardset": "A2", - "name": "Tangrowth", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 529, - "fields": { - "cardnum": 157, - "cardset": "A2", - "name": "Combee", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 530, - "fields": { - "cardnum": 158, - "cardset": "A2", - "name": "Carnivine", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 531, - "fields": { - "cardnum": 159, - "cardset": "A2", - "name": "Shaymin", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 532, - "fields": { - "cardnum": 160, - "cardset": "A2", - "name": "Mamoswine", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 533, - "fields": { - "cardnum": 161, - "cardset": "A2", - "name": "Gastrodon", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 534, - "fields": { - "cardnum": 162, - "cardset": "A2", - "name": "Manaphy", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 535, - "fields": { - "cardnum": 163, - "cardset": "A2", - "name": "Shinx", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 536, - "fields": { - "cardnum": 164, - "cardset": "A2", - "name": "Rotom", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 537, - "fields": { - "cardnum": 165, - "cardset": "A2", - "name": "Drifloon", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 538, - "fields": { - "cardnum": 166, - "cardset": "A2", - "name": "Mesprit", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 539, - "fields": { - "cardnum": 167, - "cardset": "A2", - "name": "Giratina", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 540, - "fields": { - "cardnum": 168, - "cardset": "A2", - "name": "Cresselia", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 541, - "fields": { - "cardnum": 169, - "cardset": "A2", - "name": "Rhyperior", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 542, - "fields": { - "cardnum": 170, - "cardset": "A2", - "name": "Lucario", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 543, - "fields": { - "cardnum": 171, - "cardset": "A2", - "name": "Hippopotas", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 544, - "fields": { - "cardnum": 172, - "cardset": "A2", - "name": "Spiritomb", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 545, - "fields": { - "cardnum": 173, - "cardset": "A2", - "name": "Croagunk", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 546, - "fields": { - "cardnum": 174, - "cardset": "A2", - "name": "Heatran", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 547, - "fields": { - "cardnum": 175, - "cardset": "A2", - "name": "Garchomp", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 548, - "fields": { - "cardnum": 176, - "cardset": "A2", - "name": "Staraptor", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 549, - "fields": { - "cardnum": 177, - "cardset": "A2", - "name": "Bidoof", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 550, - "fields": { - "cardnum": 178, - "cardset": "A2", - "name": "Glameow", - "decks": [ - 7 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 551, - "fields": { - "cardnum": 179, - "cardset": "A2", - "name": "Regigigas", - "decks": [ - 6 - ], - "rarity_icon": "⭐️", - "rarity_level": 5, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 552, - "fields": { - "cardnum": 180, - "cardset": "A2", - "name": "Yanmega EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 553, - "fields": { - "cardnum": 181, - "cardset": "A2", - "name": "Infernape EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 554, - "fields": { - "cardnum": 182, - "cardset": "A2", - "name": "Palkia EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 555, - "fields": { - "cardnum": 183, - "cardset": "A2", - "name": "Pachirisu EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 556, - "fields": { - "cardnum": 184, - "cardset": "A2", - "name": "Mismagius EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 557, - "fields": { - "cardnum": 185, - "cardset": "A2", - "name": "Gallade EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 558, - "fields": { - "cardnum": 186, - "cardset": "A2", - "name": "Weavile EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 559, - "fields": { - "cardnum": 187, - "cardset": "A2", - "name": "Darkrai EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 560, - "fields": { - "cardnum": 188, - "cardset": "A2", - "name": "Dialga EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 561, - "fields": { - "cardnum": 189, - "cardset": "A2", - "name": "Lickilicky EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 562, - "fields": { - "cardnum": 190, - "cardset": "A2", - "name": "Cyrus", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 563, - "fields": { - "cardnum": 191, - "cardset": "A2", - "name": "Team Galactic Grunt", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 564, - "fields": { - "cardnum": 192, - "cardset": "A2", - "name": "Cynthia", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 565, - "fields": { - "cardnum": 193, - "cardset": "A2", - "name": "Volkner", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 566, - "fields": { - "cardnum": 194, - "cardset": "A2", - "name": "Dawn", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 567, - "fields": { - "cardnum": 195, - "cardset": "A2", - "name": "Mars", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 568, - "fields": { - "cardnum": 196, - "cardset": "A2", - "name": "Yanmega EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 569, - "fields": { - "cardnum": 197, - "cardset": "A2", - "name": "Infernape EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 570, - "fields": { - "cardnum": 198, - "cardset": "A2", - "name": "Pachirisu EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 571, - "fields": { - "cardnum": 199, - "cardset": "A2", - "name": "Mismagius EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 572, - "fields": { - "cardnum": 200, - "cardset": "A2", - "name": "Gallade EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 573, - "fields": { - "cardnum": 201, - "cardset": "A2", - "name": "Weavile EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 574, - "fields": { - "cardnum": 202, - "cardset": "A2", - "name": "Darkrai EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 575, - "fields": { - "cardnum": 203, - "cardset": "A2", - "name": "Lickilicky EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️", - "rarity_level": 6, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 576, - "fields": { - "cardnum": 204, - "cardset": "A2", - "name": "Palkia EX", - "decks": [ - 7 - ], - "rarity_icon": "⭐️⭐️⭐️", - "rarity_level": 7, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 577, - "fields": { - "cardnum": 205, - "cardset": "A2", - "name": "Dialga EX", - "decks": [ - 6 - ], - "rarity_icon": "⭐️⭐️⭐️", - "rarity_level": 7, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 578, - "fields": { - "cardnum": 206, - "cardset": "A2", - "name": "Palkia EX", - "decks": [ - 6, - 7 - ], - "rarity_icon": "👑", - "rarity_level": 8, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 579, - "fields": { - "cardnum": 207, - "cardset": "A2", - "name": "Dialga EX", - "decks": [ - 6, - 7 - ], - "rarity_icon": "👑", - "rarity_level": 8, - "created_at": "2025-02-17T02:44:18.706Z", - "updated_at": "2025-02-17T02:44:18.706Z" - } - }, - { - "model": "cards.card", - "pk": 580, - "fields": { - "cardnum": 1, - "cardset": "A2a", - "name": "Heracross", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 581, - "fields": { - "cardnum": 2, - "cardset": "A2a", - "name": "Burmy", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 582, - "fields": { - "cardnum": 3, - "cardset": "A2a", - "name": "Mothim", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 583, - "fields": { - "cardnum": 4, - "cardset": "A2a", - "name": "Combee", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 584, - "fields": { - "cardnum": 5, - "cardset": "A2a", - "name": "Vespiquen", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 585, - "fields": { - "cardnum": 6, - "cardset": "A2a", - "name": "Cherubi", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 586, - "fields": { - "cardnum": 7, - "cardset": "A2a", - "name": "Cherrim", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 587, - "fields": { - "cardnum": 8, - "cardset": "A2a", - "name": "Cherrim", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 588, - "fields": { - "cardnum": 9, - "cardset": "A2a", - "name": "Carnivine", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 589, - "fields": { - "cardnum": 10, - "cardset": "A2a", - "name": "Leafeon EX", - "decks": [ - 8 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 590, - "fields": { - "cardnum": 11, - "cardset": "A2a", - "name": "Houndour", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 591, - "fields": { - "cardnum": 12, - "cardset": "A2a", - "name": "Houndoom", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 592, - "fields": { - "cardnum": 13, - "cardset": "A2a", - "name": "Heatran", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 593, - "fields": { - "cardnum": 14, - "cardset": "A2a", - "name": "Marill", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 594, - "fields": { - "cardnum": 15, - "cardset": "A2a", - "name": "Azumarill", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 595, - "fields": { - "cardnum": 16, - "cardset": "A2a", - "name": "Barboach", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 596, - "fields": { - "cardnum": 17, - "cardset": "A2a", - "name": "Whiscash", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 597, - "fields": { - "cardnum": 18, - "cardset": "A2a", - "name": "Snorunt", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 598, - "fields": { - "cardnum": 19, - "cardset": "A2a", - "name": "Froslass", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 599, - "fields": { - "cardnum": 20, - "cardset": "A2a", - "name": "Snover", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 600, - "fields": { - "cardnum": 21, - "cardset": "A2a", - "name": "Abomasnow", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 601, - "fields": { - "cardnum": 22, - "cardset": "A2a", - "name": "Glaceon EX", - "decks": [ - 8 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 602, - "fields": { - "cardnum": 23, - "cardset": "A2a", - "name": "Origin Forme Palkia", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 603, - "fields": { - "cardnum": 24, - "cardset": "A2a", - "name": "Phione", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 604, - "fields": { - "cardnum": 25, - "cardset": "A2a", - "name": "Pikachu", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 605, - "fields": { - "cardnum": 26, - "cardset": "A2a", - "name": "Raichu", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 606, - "fields": { - "cardnum": 27, - "cardset": "A2a", - "name": "Electrike", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 607, - "fields": { - "cardnum": 28, - "cardset": "A2a", - "name": "Manectric", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 608, - "fields": { - "cardnum": 29, - "cardset": "A2a", - "name": "Clefairy", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 609, - "fields": { - "cardnum": 30, - "cardset": "A2a", - "name": "Clefable", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 610, - "fields": { - "cardnum": 31, - "cardset": "A2a", - "name": "Gastly", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 611, - "fields": { - "cardnum": 32, - "cardset": "A2a", - "name": "Haunter", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 612, - "fields": { - "cardnum": 33, - "cardset": "A2a", - "name": "Gengar", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 613, - "fields": { - "cardnum": 34, - "cardset": "A2a", - "name": "Unown", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 614, - "fields": { - "cardnum": 35, - "cardset": "A2a", - "name": "Rotom", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 615, - "fields": { - "cardnum": 36, - "cardset": "A2a", - "name": "Sudowoodo", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 616, - "fields": { - "cardnum": 37, - "cardset": "A2a", - "name": "Phanpy", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 617, - "fields": { - "cardnum": 38, - "cardset": "A2a", - "name": "Donphan", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 618, - "fields": { - "cardnum": 39, - "cardset": "A2a", - "name": "Larvitar", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 619, - "fields": { - "cardnum": 40, - "cardset": "A2a", - "name": "Pupitar", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 620, - "fields": { - "cardnum": 41, - "cardset": "A2a", - "name": "Tyranitar", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 621, - "fields": { - "cardnum": 42, - "cardset": "A2a", - "name": "Nosepass", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 622, - "fields": { - "cardnum": 43, - "cardset": "A2a", - "name": "Meditite", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 623, - "fields": { - "cardnum": 44, - "cardset": "A2a", - "name": "Medicham", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 624, - "fields": { - "cardnum": 45, - "cardset": "A2a", - "name": "Gible", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 625, - "fields": { - "cardnum": 46, - "cardset": "A2a", - "name": "Gabite", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 626, - "fields": { - "cardnum": 47, - "cardset": "A2a", - "name": "Garchomp EX", - "decks": [ - 8 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 627, - "fields": { - "cardnum": 48, - "cardset": "A2a", - "name": "Zubat", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 628, - "fields": { - "cardnum": 49, - "cardset": "A2a", - "name": "Golbat", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 629, - "fields": { - "cardnum": 50, - "cardset": "A2a", - "name": "Crobat", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 630, - "fields": { - "cardnum": 51, - "cardset": "A2a", - "name": "Croagunk", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 631, - "fields": { - "cardnum": 52, - "cardset": "A2a", - "name": "Toxicroak", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 632, - "fields": { - "cardnum": 53, - "cardset": "A2a", - "name": "Magnemite", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 633, - "fields": { - "cardnum": 54, - "cardset": "A2a", - "name": "Magneton", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 634, - "fields": { - "cardnum": 55, - "cardset": "A2a", - "name": "Magnezone", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 635, - "fields": { - "cardnum": 56, - "cardset": "A2a", - "name": "Mawile", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 636, - "fields": { - "cardnum": 57, - "cardset": "A2a", - "name": "Probopass EX", - "decks": [ - 8 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 637, - "fields": { - "cardnum": 58, - "cardset": "A2a", - "name": "Bronzor", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 638, - "fields": { - "cardnum": 59, - "cardset": "A2a", - "name": "Bronzong", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 639, - "fields": { - "cardnum": 60, - "cardset": "A2a", - "name": "Origin Forme Dialga", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 640, - "fields": { - "cardnum": 61, - "cardset": "A2a", - "name": "Giratina", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 641, - "fields": { - "cardnum": 62, - "cardset": "A2a", - "name": "Eevee", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 642, - "fields": { - "cardnum": 63, - "cardset": "A2a", - "name": "Snorlax", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 643, - "fields": { - "cardnum": 64, - "cardset": "A2a", - "name": "Hoothoot", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 644, - "fields": { - "cardnum": 65, - "cardset": "A2a", - "name": "Noctowl", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 645, - "fields": { - "cardnum": 66, - "cardset": "A2a", - "name": "Starly", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 646, - "fields": { - "cardnum": 67, - "cardset": "A2a", - "name": "Staravia", - "decks": [ - 8 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 647, - "fields": { - "cardnum": 68, - "cardset": "A2a", - "name": "Staraptor", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 648, - "fields": { - "cardnum": 69, - "cardset": "A2a", - "name": "Shaymin", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 649, - "fields": { - "cardnum": 70, - "cardset": "A2a", - "name": "Arceus", - "decks": [ - 8 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 650, - "fields": { - "cardnum": 71, - "cardset": "A2a", - "name": "Arceus EX", - "decks": [ - 8 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 651, - "fields": { - "cardnum": 72, - "cardset": "A2a", - "name": "Irida", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 652, - "fields": { - "cardnum": 73, - "cardset": "A2a", - "name": "Celestic Town Elder", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 653, - "fields": { - "cardnum": 74, - "cardset": "A2a", - "name": "Barry", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 654, - "fields": { - "cardnum": 75, - "cardset": "A2a", - "name": "Adaman", - "decks": [ - 8 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 655, - "fields": { - "cardnum": 76, - "cardset": "A2a", - "name": "Houndoom", - "decks": [ - 8 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 656, - "fields": { - "cardnum": 77, - "cardset": "A2a", - "name": "Marill", - "decks": [ - 8 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 657, - "fields": { - "cardnum": 78, - "cardset": "A2a", - "name": "Unown", - "decks": [ - 8 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 658, - "fields": { - "cardnum": 79, - "cardset": "A2a", - "name": "Sudowoodo", - "decks": [ - 8 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 659, - "fields": { - "cardnum": 80, - "cardset": "A2a", - "name": "Magnemite", - "decks": [ - 8 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 660, - "fields": { - "cardnum": 81, - "cardset": "A2a", - "name": "Shaymin", - "decks": [ - 8 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 661, - "fields": { - "cardnum": 82, - "cardset": "A2a", - "name": "Leafeon EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 662, - "fields": { - "cardnum": 83, - "cardset": "A2a", - "name": "Glaceon EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 663, - "fields": { - "cardnum": 84, - "cardset": "A2a", - "name": "Garchomp EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 664, - "fields": { - "cardnum": 85, - "cardset": "A2a", - "name": "Probopass EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 665, - "fields": { - "cardnum": 86, - "cardset": "A2a", - "name": "Arceus EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 666, - "fields": { - "cardnum": 87, - "cardset": "A2a", - "name": "Irida", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 667, - "fields": { - "cardnum": 88, - "cardset": "A2a", - "name": "Celestic Town Elder", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 668, - "fields": { - "cardnum": 89, - "cardset": "A2a", - "name": "Barry", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 669, - "fields": { - "cardnum": 90, - "cardset": "A2a", - "name": "Adaman", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 670, - "fields": { - "cardnum": 91, - "cardset": "A2a", - "name": "Leafeon EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 671, - "fields": { - "cardnum": 92, - "cardset": "A2a", - "name": "Glaceon EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 672, - "fields": { - "cardnum": 93, - "cardset": "A2a", - "name": "Garchomp EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 673, - "fields": { - "cardnum": 94, - "cardset": "A2a", - "name": "Probopass EX", - "decks": [ - 8 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 674, - "fields": { - "cardnum": 95, - "cardset": "A2a", - "name": "Arceus EX", - "decks": [ - 8 - ], - "rarity_level": 7, - "rarity_icon": "⭐️⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 675, - "fields": { - "cardnum": 96, - "cardset": "A2a", - "name": "Arceus EX", - "decks": [ - 8 - ], - "rarity_level": 8, - "rarity_icon": "👑", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 676, - "fields": { - "cardnum": 1, - "cardset": "A2b", - "name": "Weedle", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 677, - "fields": { - "cardnum": 2, - "cardset": "A2b", - "name": "Kakuna", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 678, - "fields": { - "cardnum": 3, - "cardset": "A2b", - "name": "Beedrill EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 679, - "fields": { - "cardnum": 4, - "cardset": "A2b", - "name": "Pinsir", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 680, - "fields": { - "cardnum": 5, - "cardset": "A2b", - "name": "Sprigatito", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 681, - "fields": { - "cardnum": 6, - "cardset": "A2b", - "name": "Floragato", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 682, - "fields": { - "cardnum": 7, - "cardset": "A2b", - "name": "Meowscarada", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 683, - "fields": { - "cardnum": 8, - "cardset": "A2b", - "name": "Charmander", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 684, - "fields": { - "cardnum": 9, - "cardset": "A2b", - "name": "Charmeleon", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 685, - "fields": { - "cardnum": 10, - "cardset": "A2b", - "name": "Charizard EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 686, - "fields": { - "cardnum": 11, - "cardset": "A2b", - "name": "Magmar", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 687, - "fields": { - "cardnum": 12, - "cardset": "A2b", - "name": "Magmortar", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 688, - "fields": { - "cardnum": 13, - "cardset": "A2b", - "name": "Paldean Tauros", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 689, - "fields": { - "cardnum": 14, - "cardset": "A2b", - "name": "Tentacool", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 690, - "fields": { - "cardnum": 15, - "cardset": "A2b", - "name": "Tentacruel", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 691, - "fields": { - "cardnum": 16, - "cardset": "A2b", - "name": "Buizel", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 692, - "fields": { - "cardnum": 17, - "cardset": "A2b", - "name": "Floatzel", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 693, - "fields": { - "cardnum": 18, - "cardset": "A2b", - "name": "Wiglett", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 694, - "fields": { - "cardnum": 19, - "cardset": "A2b", - "name": "Wugtrio EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 695, - "fields": { - "cardnum": 20, - "cardset": "A2b", - "name": "Dondozo", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 696, - "fields": { - "cardnum": 21, - "cardset": "A2b", - "name": "Tatsugiri", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 697, - "fields": { - "cardnum": 22, - "cardset": "A2b", - "name": "Pikachu EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 698, - "fields": { - "cardnum": 23, - "cardset": "A2b", - "name": "Voltorb", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 699, - "fields": { - "cardnum": 24, - "cardset": "A2b", - "name": "Electrode", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 700, - "fields": { - "cardnum": 25, - "cardset": "A2b", - "name": "Pachirisu", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 701, - "fields": { - "cardnum": 26, - "cardset": "A2b", - "name": "Pawmi", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 702, - "fields": { - "cardnum": 27, - "cardset": "A2b", - "name": "Pawmo", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 703, - "fields": { - "cardnum": 28, - "cardset": "A2b", - "name": "Pawmot", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 704, - "fields": { - "cardnum": 29, - "cardset": "A2b", - "name": "Abra", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 705, - "fields": { - "cardnum": 30, - "cardset": "A2b", - "name": "Kadabra", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 706, - "fields": { - "cardnum": 31, - "cardset": "A2b", - "name": "Alakazam", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 707, - "fields": { - "cardnum": 32, - "cardset": "A2b", - "name": "Mr. Mime", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 708, - "fields": { - "cardnum": 33, - "cardset": "A2b", - "name": "Drifloon", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 709, - "fields": { - "cardnum": 34, - "cardset": "A2b", - "name": "Drifblim", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 710, - "fields": { - "cardnum": 35, - "cardset": "A2b", - "name": "Giratina EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 711, - "fields": { - "cardnum": 36, - "cardset": "A2b", - "name": "Gimmighoul", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 712, - "fields": { - "cardnum": 37, - "cardset": "A2b", - "name": "Machop", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 713, - "fields": { - "cardnum": 38, - "cardset": "A2b", - "name": "Machoke", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 714, - "fields": { - "cardnum": 39, - "cardset": "A2b", - "name": "Machamp", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 715, - "fields": { - "cardnum": 40, - "cardset": "A2b", - "name": "Hitmonlee", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 716, - "fields": { - "cardnum": 41, - "cardset": "A2b", - "name": "Hitmonchan", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 717, - "fields": { - "cardnum": 42, - "cardset": "A2b", - "name": "Riolu", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 718, - "fields": { - "cardnum": 43, - "cardset": "A2b", - "name": "Lucario EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 719, - "fields": { - "cardnum": 44, - "cardset": "A2b", - "name": "Flamigo", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 720, - "fields": { - "cardnum": 45, - "cardset": "A2b", - "name": "Ekans", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 721, - "fields": { - "cardnum": 46, - "cardset": "A2b", - "name": "Arbok", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 722, - "fields": { - "cardnum": 47, - "cardset": "A2b", - "name": "Paldean Wooper", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 723, - "fields": { - "cardnum": 48, - "cardset": "A2b", - "name": "Paldean Clodsire EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 724, - "fields": { - "cardnum": 49, - "cardset": "A2b", - "name": "Spiritomb", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 725, - "fields": { - "cardnum": 50, - "cardset": "A2b", - "name": "Shroodle", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 726, - "fields": { - "cardnum": 51, - "cardset": "A2b", - "name": "Grafaiai", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 727, - "fields": { - "cardnum": 52, - "cardset": "A2b", - "name": "Tinkatink", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 728, - "fields": { - "cardnum": 53, - "cardset": "A2b", - "name": "Tinkatuff", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 729, - "fields": { - "cardnum": 54, - "cardset": "A2b", - "name": "Tinkaton EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 730, - "fields": { - "cardnum": 55, - "cardset": "A2b", - "name": "Varoom", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 731, - "fields": { - "cardnum": 56, - "cardset": "A2b", - "name": "Revavroom", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 732, - "fields": { - "cardnum": 57, - "cardset": "A2b", - "name": "Gholdengo", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 733, - "fields": { - "cardnum": 58, - "cardset": "A2b", - "name": "Rattata", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 734, - "fields": { - "cardnum": 59, - "cardset": "A2b", - "name": "Raticate", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 735, - "fields": { - "cardnum": 60, - "cardset": "A2b", - "name": "Jigglypuff", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 736, - "fields": { - "cardnum": 61, - "cardset": "A2b", - "name": "Wigglytuff", - "decks": [ - 9 - ], - "rarity_level": 3, - "rarity_icon": "🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 737, - "fields": { - "cardnum": 62, - "cardset": "A2b", - "name": "Lickitung", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 738, - "fields": { - "cardnum": 63, - "cardset": "A2b", - "name": "Lickilicky", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 739, - "fields": { - "cardnum": 64, - "cardset": "A2b", - "name": "Bidoof", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 740, - "fields": { - "cardnum": 65, - "cardset": "A2b", - "name": "Bibarel EX", - "decks": [ - 9 - ], - "rarity_level": 4, - "rarity_icon": "🔷🔷🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 741, - "fields": { - "cardnum": 66, - "cardset": "A2b", - "name": "Buneary", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 742, - "fields": { - "cardnum": 67, - "cardset": "A2b", - "name": "Lopunny", - "decks": [ - 9 - ], - "rarity_level": 1, - "rarity_icon": "🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 743, - "fields": { - "cardnum": 68, - "cardset": "A2b", - "name": "Cyclizar", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 744, - "fields": { - "cardnum": 69, - "cardset": "A2b", - "name": "Iono", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 745, - "fields": { - "cardnum": 70, - "cardset": "A2b", - "name": "Pokémon Center Lady", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 746, - "fields": { - "cardnum": 71, - "cardset": "A2b", - "name": "Red", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 747, - "fields": { - "cardnum": 72, - "cardset": "A2b", - "name": "Team Rocket Grunt", - "decks": [ - 9 - ], - "rarity_level": 2, - "rarity_icon": "🔷🔷", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 748, - "fields": { - "cardnum": 73, - "cardset": "A2b", - "name": "Meowscarada", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 749, - "fields": { - "cardnum": 74, - "cardset": "A2b", - "name": "Buizel", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 750, - "fields": { - "cardnum": 75, - "cardset": "A2b", - "name": "Tatsugiri", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 751, - "fields": { - "cardnum": 76, - "cardset": "A2b", - "name": "Grafaiai", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 752, - "fields": { - "cardnum": 77, - "cardset": "A2b", - "name": "Gholdengo", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 753, - "fields": { - "cardnum": 78, - "cardset": "A2b", - "name": "Wigglytuff", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 754, - "fields": { - "cardnum": 79, - "cardset": "A2b", - "name": "Beedrill EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 755, - "fields": { - "cardnum": 80, - "cardset": "A2b", - "name": "Charizard EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 756, - "fields": { - "cardnum": 81, - "cardset": "A2b", - "name": "Wugtrio EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 757, - "fields": { - "cardnum": 82, - "cardset": "A2b", - "name": "Pikachu EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 758, - "fields": { - "cardnum": 83, - "cardset": "A2b", - "name": "Giratina EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 759, - "fields": { - "cardnum": 84, - "cardset": "A2b", - "name": "Lucario EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 760, - "fields": { - "cardnum": 85, - "cardset": "A2b", - "name": "Paldean Clodsire EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 761, - "fields": { - "cardnum": 86, - "cardset": "A2b", - "name": "Tinkaton EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 762, - "fields": { - "cardnum": 87, - "cardset": "A2b", - "name": "Bibarel EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 763, - "fields": { - "cardnum": 88, - "cardset": "A2b", - "name": "Iono", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 764, - "fields": { - "cardnum": 89, - "cardset": "A2b", - "name": "Pokémon Center Lady", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 765, - "fields": { - "cardnum": 90, - "cardset": "A2b", - "name": "Red", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 766, - "fields": { - "cardnum": 91, - "cardset": "A2b", - "name": "Team Rocket Grunt", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 767, - "fields": { - "cardnum": 92, - "cardset": "A2b", - "name": "Pikachu EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 768, - "fields": { - "cardnum": 93, - "cardset": "A2b", - "name": "Paldean Clodsire EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 769, - "fields": { - "cardnum": 94, - "cardset": "A2b", - "name": "Tinkaton EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 770, - "fields": { - "cardnum": 95, - "cardset": "A2b", - "name": "Bibarel EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 771, - "fields": { - "cardnum": 96, - "cardset": "A2b", - "name": "Giratina EX", - "decks": [ - 9 - ], - "rarity_level": 7, - "rarity_icon": "⭐️⭐️⭐️", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 772, - "fields": { - "cardnum": 97, - "cardset": "A2b", - "name": "Weedle", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 773, - "fields": { - "cardnum": 98, - "cardset": "A2b", - "name": "Kakuna", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 774, - "fields": { - "cardnum": 99, - "cardset": "A2b", - "name": "Charmander", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 775, - "fields": { - "cardnum": 100, - "cardset": "A2b", - "name": "Charmeleon", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 776, - "fields": { - "cardnum": 101, - "cardset": "A2b", - "name": "Wiglett", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 777, - "fields": { - "cardnum": 102, - "cardset": "A2b", - "name": "Dondozo", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 778, - "fields": { - "cardnum": 103, - "cardset": "A2b", - "name": "Pachirisu", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 779, - "fields": { - "cardnum": 104, - "cardset": "A2b", - "name": "Riolu", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 780, - "fields": { - "cardnum": 105, - "cardset": "A2b", - "name": "Varoom", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 781, - "fields": { - "cardnum": 106, - "cardset": "A2b", - "name": "Revavroom", - "decks": [ - 9 - ], - "rarity_level": 5, - "rarity_icon": "✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 782, - "fields": { - "cardnum": 107, - "cardset": "A2b", - "name": "Beedrill EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "✨✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 783, - "fields": { - "cardnum": 108, - "cardset": "A2b", - "name": "Charizard EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "✨✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 784, - "fields": { - "cardnum": 109, - "cardset": "A2b", - "name": "Wugtrio EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "✨✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 785, - "fields": { - "cardnum": 110, - "cardset": "A2b", - "name": "Lucario EX", - "decks": [ - 9 - ], - "rarity_level": 6, - "rarity_icon": "✨✨", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - }, - { - "model": "cards.card", - "pk": 786, - "fields": { - "cardnum": 111, - "cardset": "A2b", - "name": "Pokémon Ball", - "decks": [ - 9 - ], - "rarity_level": 8, - "rarity_icon": "👑", - "created_at": "2025-03-26T12:25:17.706Z", - "updated_at": "2025-03-26T12:25:17.706Z" - } - } -] \ No newline at end of file diff --git a/src/pkmntrade_club/accounts/admin.py b/src/pkmntrade_club/accounts/admin.py index bd475e3..7a004c7 100644 --- a/src/pkmntrade_club/accounts/admin.py +++ b/src/pkmntrade_club/accounts/admin.py @@ -1,8 +1,8 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin -from .forms import CustomUserCreationForm, CustomUserChangeForm -from .models import CustomUser +from .forms import CustomUserChangeForm, CustomUserCreationForm +from .models import CustomUser, FriendCode class CustomUserAdmin(UserAdmin): @@ -27,3 +27,11 @@ class CustomUserAdmin(UserAdmin): admin.site.register(CustomUser, CustomUserAdmin) + + +@admin.register(FriendCode) +class FriendCodeAdmin(admin.ModelAdmin): + list_display = ("friend_code", "in_game_name", "user") + search_fields = ("friend_code", "in_game_name", "user__username", "user__email") + list_select_related = ("user",) + autocomplete_fields = ("user",) diff --git a/src/pkmntrade_club/accounts/migrations/0001_initial.py b/src/pkmntrade_club/accounts/migrations/0001_initial.py index a706671..38c176e 100644 --- a/src/pkmntrade_club/accounts/migrations/0001_initial.py +++ b/src/pkmntrade_club/accounts/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1 on 2025-05-17 02:07 +# Generated by Django 5.1 on 2025-06-15 03:44 import django.contrib.auth.models import django.contrib.auth.validators @@ -14,53 +14,183 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0001_initial'), + ("auth", "0001_initial"), ] operations = [ migrations.CreateModel( - name='CustomUser', + name="CustomUser", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('show_friend_code_on_link_previews', models.BooleanField(default=False, help_text='This will primarily affect share link previews on X, Discord, etc.', verbose_name='Show Friend Code on Link Previews')), - ('enable_email_notifications', models.BooleanField(default=True, help_text='Receive trade notifications via email.', verbose_name='Enable Email Notifications')), - ('reputation_score', models.IntegerField(default=0)), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ( + "username", + models.CharField( + error_messages={ + "unique": "A user with that username already exists." + }, + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + max_length=150, + unique=True, + validators=[ + django.contrib.auth.validators.UnicodeUsernameValidator() + ], + verbose_name="username", + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=150, verbose_name="first name" + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=150, verbose_name="last name" + ), + ), + ( + "email", + models.EmailField( + blank=True, max_length=254, verbose_name="email address" + ), + ), + ( + "is_staff", + models.BooleanField( + default=False, + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", + ), + ), + ( + "is_active", + models.BooleanField( + default=True, + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", + ), + ), + ( + "date_joined", + models.DateTimeField( + default=django.utils.timezone.now, verbose_name="date joined" + ), + ), + ( + "show_friend_code_on_link_previews", + models.BooleanField( + default=False, + help_text="This will primarily affect share link previews on X, Discord, etc.", + verbose_name="Show Friend Code on Link Previews", + ), + ), + ( + "enable_email_notifications", + models.BooleanField( + default=True, + help_text="Receive trade notifications via email.", + verbose_name="Enable Email Notifications", + ), + ), + ("reputation_score", models.IntegerField(default=0)), + ( + "groups", + models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.group", + verbose_name="groups", + ), + ), + ( + "user_permissions", + models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.permission", + verbose_name="user permissions", + ), + ), ], options={ - 'verbose_name': 'user', - 'verbose_name_plural': 'users', - 'abstract': False, + "verbose_name": "user", + "verbose_name_plural": "users", + "abstract": False, }, managers=[ - ('objects', django.contrib.auth.models.UserManager()), + ("objects", django.contrib.auth.models.UserManager()), ], ), migrations.CreateModel( - name='FriendCode', + name="FriendCode", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('friend_code', models.CharField(max_length=19, validators=[pkmntrade_club.accounts.models.validate_friend_code])), - ('in_game_name', models.CharField(max_length=14)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='friend_codes', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "friend_code", + models.CharField( + max_length=19, + validators=[ + pkmntrade_club.accounts.models.validate_friend_code + ], + ), + ), + ("in_game_name", models.CharField(max_length=14)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="friend_codes", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.AddField( - model_name='customuser', - name='default_friend_code', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.friendcode'), + model_name="customuser", + name="default_friend_code", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="accounts.friendcode", + ), ), ] diff --git a/src/pkmntrade_club/accounts/views.py b/src/pkmntrade_club/accounts/views.py index 34e81b7..a170fb7 100644 --- a/src/pkmntrade_club/accounts/views.py +++ b/src/pkmntrade_club/accounts/views.py @@ -1,22 +1,23 @@ from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin -from django.shortcuts import redirect, get_object_or_404, render +from django.core.exceptions import PermissionDenied +from django.db.models import BooleanField, Case, Q, Value, When +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse +from django.utils.http import urlencode from django.views.generic import ( CreateView, DeleteView, - View, TemplateView, UpdateView, + View, ) -from pkmntrade_club.accounts.models import FriendCode + from pkmntrade_club.accounts.forms import FriendCodeForm, UserSettingsForm -from django.db.models import Case, When, Value, BooleanField -from pkmntrade_club.trades.models import TradeOffer, TradeAcceptance -from django.core.exceptions import PermissionDenied -from pkmntrade_club.trades.mixins import FriendCodeRequiredMixin +from pkmntrade_club.accounts.models import FriendCode from pkmntrade_club.common.mixins import ReusablePaginationMixin -from django.urls import reverse -from django.utils.http import urlencode +from pkmntrade_club.trades.mixins import FriendCodeRequiredMixin +from pkmntrade_club.trades.models import TradeAcceptance, TradeOffer class AddFriendCodeView(LoginRequiredMixin, CreateView): @@ -204,8 +205,6 @@ class DashboardView( return {"object_list": object_list, "page_obj": pagination_context} def get_involved_acceptances(self, selected_friend_code): - from django.db.models import Q - terminal_states = [ TradeAcceptance.AcceptanceState.THANKED_BY_INITIATOR, TradeAcceptance.AcceptanceState.THANKED_BY_ACCEPTOR, @@ -213,10 +212,25 @@ class DashboardView( TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, ] - involved = TradeAcceptance.objects.filter( - Q(trade_offer__initiated_by=selected_friend_code) - | Q(accepted_by=selected_friend_code) - ).order_by("-updated_at") + involved = ( + TradeAcceptance.objects.filter( + Q(trade_offer__initiated_by=selected_friend_code) + | Q(accepted_by=selected_friend_code) + ) + .select_related( + "trade_offer__initiated_by__user", + "accepted_by__user", + "requested_card__rarity", + "requested_card__cardset", + "offered_card__rarity", + "offered_card__cardset", + ) + .prefetch_related( + "requested_card__translations", + "offered_card__translations", + ) + .order_by("-updated_at") + ) return involved.exclude(state__in=terminal_states) def get_trade_acceptances_waiting_paginated(self, page_param): @@ -271,55 +285,94 @@ class DashboardView( return {"object_list": object_list, "page_obj": pagination_context} def get_closed_acceptances_paginated(self, page_param): - from django.db.models import Q - selected_friend_code = self.get_selected_friend_code() terminal_success_states = [ TradeAcceptance.AcceptanceState.THANKED_BY_INITIATOR, TradeAcceptance.AcceptanceState.THANKED_BY_ACCEPTOR, TradeAcceptance.AcceptanceState.THANKED_BY_BOTH, ] - acceptance_qs = TradeAcceptance.objects.filter( - Q(trade_offer__initiated_by=selected_friend_code) - | Q(accepted_by=selected_friend_code), - state__in=terminal_success_states, - ).order_by("-updated_at") + acceptance_qs = ( + TradeAcceptance.objects.filter( + Q(trade_offer__initiated_by=selected_friend_code) + | Q(accepted_by=selected_friend_code), + state__in=terminal_success_states, + ) + .select_related( + "trade_offer__initiated_by__user", + "accepted_by__user", + "requested_card__rarity", + "requested_card__cardset", + "offered_card__rarity", + "offered_card__cardset", + ) + .prefetch_related( + "requested_card__translations", + "offered_card__translations", + ) + .order_by("-updated_at") + ) object_list, pagination_context = self.paginate_data( acceptance_qs, int(page_param) ) return {"object_list": object_list, "page_obj": pagination_context} def get_rejected_by_me_paginated(self, page_param): - from django.db.models import Q - selected_friend_code = self.get_selected_friend_code() - rejection = TradeAcceptance.objects.filter( - Q( - trade_offer__initiated_by=selected_friend_code, - state=TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, + rejection = ( + TradeAcceptance.objects.filter( + Q( + trade_offer__initiated_by=selected_friend_code, + state=TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, + ) + | Q( + accepted_by=selected_friend_code, + state=TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, + ) ) - | Q( - accepted_by=selected_friend_code, - state=TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, + .select_related( + "trade_offer__initiated_by__user", + "accepted_by__user", + "requested_card__rarity", + "requested_card__cardset", + "offered_card__rarity", + "offered_card__cardset", ) - ).order_by("-updated_at") + .prefetch_related( + "requested_card__translations", + "offered_card__translations", + ) + .order_by("-updated_at") + ) object_list, pagination_context = self.paginate_data(rejection, int(page_param)) return {"object_list": object_list, "page_obj": pagination_context} def get_rejected_by_them_paginated(self, page_param): - from django.db.models import Q - selected_friend_code = self.get_selected_friend_code() - rejection = TradeAcceptance.objects.filter( - Q( - trade_offer__initiated_by=selected_friend_code, - state=TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, + rejection = ( + TradeAcceptance.objects.filter( + Q( + trade_offer__initiated_by=selected_friend_code, + state=TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, + ) + | Q( + accepted_by=selected_friend_code, + state=TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, + ) ) - | Q( - accepted_by=selected_friend_code, - state=TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, + .select_related( + "trade_offer__initiated_by__user", + "accepted_by__user", + "requested_card__rarity", + "requested_card__cardset", + "offered_card__rarity", + "offered_card__cardset", ) - ).order_by("-updated_at") + .prefetch_related( + "requested_card__translations", + "offered_card__translations", + ) + .order_by("-updated_at") + ) object_list, pagination_context = self.paginate_data(rejection, int(page_param)) return {"object_list": object_list, "page_obj": pagination_context} diff --git a/src/pkmntrade_club/cards/admin.py b/src/pkmntrade_club/cards/admin.py index b3ce633..759cd4e 100644 --- a/src/pkmntrade_club/cards/admin.py +++ b/src/pkmntrade_club/cards/admin.py @@ -1,34 +1,68 @@ -from django.contrib import admin, messages -from django.urls import path -from django.shortcuts import render -from django.http import HttpResponseRedirect -from parler.admin import TranslatableAdmin -from .models import ( - CardSet_New, Pack_New, Energy_New, Attack_New, Ability_New, - Rarity_New, CardType_New, Card_New, AttackCost_New, RarityMapping -) - +import hashlib +import io import json import os -import re # For parsing set name and ID +import re # For parsing set name and ID +import zipfile +from dataclasses import dataclass + +import requests from django.conf import settings -from django.utils.translation import gettext_lazy as _ +from django.contrib import admin, messages +from django.contrib.admin.filters import RelatedFieldListFilter from django.db import transaction -import hashlib +from django.http import HttpResponseRedirect +from django.shortcuts import render +from django.urls import path, reverse +from parler.admin import TranslatableAdmin + +from .models import ( + Ability, + Attack, + AttackCost, + Card, + CardSet, + CardType, + Energy, + Pack, + Rarity, + RarityMapping, +) + + +@dataclass +class ImportResult: + """A data class to hold the results of the card import process.""" + + newly_imported_count: int = 0 + updated_count: int = 0 + skipped_count: int = 0 + files_processed_count: int = 0 + has_error: bool = False + message: str = "" + + +class PrefetchedSortedRelatedFieldListFilter(RelatedFieldListFilter): + def field_choices(self, field, request, model_admin): + related_manager = field.related_model._default_manager + queryset = related_manager.all().prefetch_related("translations") + return [(obj.pk, str(obj)) for obj in queryset] + def parse_set_details(set_string): - match = re.match(r'^(.*?)\s*\(([A-Za-z0-9]+)\)$', set_string) + match = re.match(r"^(.*?)\s*\(([A-Za-z0-9]+)\)$", set_string) if match: name = match.group(1).strip() set_id = match.group(2) return name, set_id - match = re.match(r'^Promo-(.*?)$', set_string) + match = re.match(r"^Promo-(.*?)$", set_string) if match: name = set_string - set_id = 'P-' + match.group(1) + set_id = "P-" + match.group(1) return name, set_id return set_string, None + def calculate_card_checksum(card_data): """ Calculates a SHA256 checksum for a card's data. @@ -38,55 +72,67 @@ def calculate_card_checksum(card_data): # Select and normalize fields that define the card's state # Order of keys in `data_to_hash` and sorting of lists are important for consistency data_to_hash = { - 'id': card_data.get('id'), - 'name': card_data.get('name'), - 'type': card_data.get('type'), - 'subtype': card_data.get('subtype'), - 'rarity': card_data.get('rarity'), # Rarity name from JSON - 'health': card_data.get('health'), - 'evolvesFrom': card_data.get('evolvesFrom'), - 'retreatCost': card_data.get('retreatCost'), - 'element': card_data.get('element'), # Element name from JSON - 'weakness': card_data.get('weakness'), # Weakness name from JSON - 'pack': card_data.get('pack'), # Pack name from JSON + "id": card_data.get("id"), + "name": card_data.get("name"), + "type": card_data.get("type"), + "subtype": card_data.get("subtype"), + "rarity": card_data.get("rarity"), # Rarity name from JSON + "health": card_data.get("health"), + "evolvesFrom": card_data.get("evolvesFrom"), + "retreatCost": card_data.get("retreatCost"), + "element": card_data.get("element"), # Element name from JSON + "weakness": card_data.get("weakness"), # Weakness name from JSON + "pack": card_data.get("pack"), # Pack name from JSON # For abilities and attacks, ensure stable order and content - 'abilities': sorted([ - {'name': a.get('name'), 'effect': a.get('effect')} - for a in card_data.get('abilities', []) if a and a.get('name') # ensure ability itself and name exist - ], key=lambda x: x['name'] if x and x.get('name') else ''), - 'attacks': sorted([ - { - 'name': atk.get('name'), - 'effect': atk.get('effect', ''), - 'damage': atk.get('damage', ''), - 'cost': sorted(atk.get('cost', []) if atk.get('cost') else []) # Sort energy costs - } - for atk in card_data.get('attacks', []) if atk and atk.get('name') # ensure attack itself and name exist - ], key=lambda x: x['name'] if x and x.get('name') else ''), + "abilities": sorted( + [ + {"name": a.get("name"), "effect": a.get("effect")} + for a in card_data.get("abilities", []) + if a and a.get("name") # ensure ability itself and name exist + ], + key=lambda x: x["name"] if x and x.get("name") else "", + ), + "attacks": sorted( + [ + { + "name": atk.get("name"), + "effect": atk.get("effect", ""), + "damage": atk.get("damage", ""), + "cost": sorted( + atk.get("cost", []) if atk.get("cost") else [] + ), # Sort energy costs + } + for atk in card_data.get("attacks", []) + if atk and atk.get("name") # ensure attack itself and name exist + ], + key=lambda x: x["name"] if x and x.get("name") else "", + ), } # Serialize to a canonical JSON string (sort keys, no indent, compact) - canonical_json = json.dumps(data_to_hash, sort_keys=True, separators=(',', ':')) - - sha256_hash = hashlib.sha256(canonical_json.encode('utf-8')).hexdigest() + canonical_json = json.dumps(data_to_hash, sort_keys=True, separators=(",", ":")) + + sha256_hash = hashlib.sha256(canonical_json.encode("utf-8")).hexdigest() return sha256_hash + def _get_or_create_card_type(card_data): - card_type_obj, created = CardType_New.objects.language('en').get_or_create( - translations__name=card_data['type'], - translations__subtype=card_data.get('subtype', ''), - defaults={'name': card_data['type'], 'subtype': card_data.get('subtype', '')} + card_type_obj, created = CardType.objects.language("en").get_or_create( + translations__name=card_data["type"], + translations__subtype=card_data.get("subtype", ""), + defaults={"name": card_data["type"], "subtype": card_data.get("subtype", "")}, ) if not created: - current_subtype = card_data.get('subtype') + current_subtype = card_data.get("subtype") if current_subtype is not None and card_type_obj.subtype != current_subtype: - card_type_obj.set_current_language('en') + card_type_obj.set_current_language("en") card_type_obj.subtype = current_subtype card_type_obj.save() return card_type_obj + def _get_or_create_rarity(card_data, rarity_mappings_dict): - original_rarity_name_from_json = card_data.get('rarity') + original_rarity_name_from_json = card_data.get("rarity") # Attempt to find a mapping for the original rarity name mapping = rarity_mappings_dict.get(original_rarity_name_from_json) @@ -99,24 +145,28 @@ def _get_or_create_rarity(card_data, rarity_mappings_dict): elif original_rarity_name_from_json: # No mapping found, use the original name from JSON, default icon/level target_rarity_name = original_rarity_name_from_json - target_icon = 'x' # Default icon if no mapping - target_level = 0 # Default level if no mapping + target_icon = "x" # Default icon if no mapping + target_level = 0 # Default level if no mapping else: # Rarity is None or empty in JSON, treat as 'Promo' - target_rarity_name = 'Promo' + target_rarity_name = "Promo" # Check if 'Promo' itself has a mapping - promo_mapping = rarity_mappings_dict.get('Promo') + promo_mapping = rarity_mappings_dict.get("Promo") if promo_mapping: target_icon = promo_mapping.icon target_level = promo_mapping.level else: - target_icon = 'x' # Default icon for 'Promo' if no mapping for 'Promo' - target_level = 0 # Default level for 'Promo' if no mapping for 'Promo' + target_icon = "x" # Default icon for 'Promo' if no mapping for 'Promo' + target_level = 0 # Default level for 'Promo' if no mapping for 'Promo' - # Get or create the Rarity_New object using the (potentially mapped) values - rarity_obj, created = Rarity_New.objects.language('en').get_or_create( + # Get or create the Rarity object using the (potentially mapped) values + rarity_obj, created = Rarity.objects.language("en").get_or_create( translations__name=target_rarity_name, - defaults={'name': target_rarity_name, 'icon': target_icon, 'level': target_level} + defaults={ + "name": target_rarity_name, + "icon": target_icon, + "level": target_level, + }, ) # If the rarity already existed, check if its icon or level needs updating based on the mapping @@ -128,145 +178,150 @@ def _get_or_create_rarity(card_data, rarity_mappings_dict): if rarity_obj.level != target_level: rarity_obj.level = target_level updated_fields = True - + if updated_fields: rarity_obj.save() - + return rarity_obj + def _get_or_create_energy(energy_name): if not energy_name: return None - energy_obj, _ = Energy_New.objects.language('en').get_or_create( - translations__name=energy_name, - defaults={'name': energy_name} + energy_obj, _ = Energy.objects.language("en").get_or_create( + translations__name=energy_name, defaults={"name": energy_name} ) return energy_obj + def _update_card_packs(card_obj, card_data, card_set): card_obj.packs.clear() - pack_name_from_json = card_data.get('pack') + pack_name_from_json = card_data.get("pack") if pack_name_from_json: - card_set.set_current_language('en') + card_set.set_current_language("en") pack_full_name = f"{card_set.name}: {pack_name_from_json}" - pack_obj, _ = Pack_New.objects.language('en').get_or_create( + pack_obj, _ = Pack.objects.language("en").get_or_create( translations__name=pack_name_from_json, cardset=card_set, defaults={ - 'name': pack_name_from_json, - 'full_name': pack_full_name, - 'hex_color': '#FFFFFF' - } + "name": pack_name_from_json, + "full_name": pack_full_name, + "hex_color": "#FFFFFF", + }, ) card_obj.packs.add(pack_obj) else: - all_packs_in_set = Pack_New.objects.filter(cardset=card_set) + all_packs_in_set = Pack.objects.filter(cardset=card_set) if all_packs_in_set.exists(): card_obj.packs.add(*all_packs_in_set) + def _update_card_abilities(card_obj, card_data): card_obj.abilities.clear() - for ability_data in card_data.get('abilities', []): - ability_obj, created = Ability_New.objects.language('en').get_or_create( - translations__name=ability_data['name'], - defaults={'name': ability_data['name'], 'effect': ability_data['effect']} + for ability_data in card_data.get("abilities", []): + ability_obj, created = Ability.objects.language("en").get_or_create( + translations__name=ability_data["name"], + defaults={"name": ability_data["name"], "effect": ability_data["effect"]}, ) - if not created and ability_obj.effect != ability_data['effect']: - ability_obj.set_current_language('en') - ability_obj.effect = ability_data['effect'] + if not created and ability_obj.effect != ability_data["effect"]: + ability_obj.set_current_language("en") + ability_obj.effect = ability_data["effect"] ability_obj.save() card_obj.abilities.add(ability_obj) + def _update_card_attacks_and_costs(card_obj, card_data): card_obj.attacks.clear() - for attack_data in card_data.get('attacks', []): - attack_obj, created = Attack_New.objects.language('en').get_or_create( - translations__name=attack_data['name'], + for attack_data in card_data.get("attacks", []): + attack_obj, created = Attack.objects.language("en").get_or_create( + translations__name=attack_data["name"], defaults={ - 'name': attack_data['name'], - 'effect': attack_data.get('effect', ''), - 'damage': attack_data.get('damage', '') - } + "name": attack_data["name"], + "effect": attack_data.get("effect", ""), + "damage": attack_data.get("damage", ""), + }, ) - + needs_save = False if not created: - json_effect = attack_data.get('effect', '') + json_effect = attack_data.get("effect", "") if attack_obj.effect != json_effect: - attack_obj.set_current_language('en') + attack_obj.set_current_language("en") attack_obj.effect = json_effect needs_save = True - - json_damage = attack_data.get('damage', '') + + json_damage = attack_data.get("damage", "") if attack_obj.damage != json_damage: attack_obj.damage = json_damage needs_save = True - + if created or needs_save: - attack_obj.save() + attack_obj.save() card_obj.attacks.add(attack_obj) - attack_obj.energy_cost.clear() + attack_obj.energy_cost.clear() energy_counts = {} - for cost_energy_name in attack_data.get('cost', []): + for cost_energy_name in attack_data.get("cost", []): energy_counts[cost_energy_name] = energy_counts.get(cost_energy_name, 0) + 1 - + for energy_name, quantity in energy_counts.items(): energy_obj = _get_or_create_energy(energy_name) if energy_obj: - AttackCost_New.objects.update_or_create( + AttackCost.objects.update_or_create( attack=attack_obj, energy=energy_obj, - defaults={'quantity': quantity} + defaults={"quantity": quantity}, ) -def _process_single_card_data(card_data, card_set, stats_accumulator, error_tracking, rarity_mappings_dict): + +def _process_single_card_data( + card_data, card_set, stats_accumulator, error_tracking, rarity_mappings_dict +): """ Processes a single card's data from the JSON. Updates stats_accumulator with newly_imported_count, updated_count, or skipped_count. error_tracking is a dict {'file_name': ..., 'card_id': ...} for precise error reporting. """ - card_id = card_data['id'] + card_id = card_data["id"] incoming_checksum = calculate_card_checksum(card_data) - error_tracking['card_id'] = card_id + error_tracking["card_id"] = card_id try: - existing_card = Card_New.objects.language('en').get(id=card_id) + existing_card = Card.objects.language("en").get(id=card_id) if existing_card.checksum == incoming_checksum: - stats_accumulator['skipped_count'] += 1 - return - except Card_New.DoesNotExist: + stats_accumulator["skipped_count"] += 1 + return + except Card.DoesNotExist: existing_card = None card_type_obj = _get_or_create_card_type(card_data) rarity_obj = _get_or_create_rarity(card_data, rarity_mappings_dict) - pkmn_type_obj = _get_or_create_energy(card_data.get('element')) - weakness_type_obj = _get_or_create_energy(card_data.get('weakness')) - + pkmn_type_obj = _get_or_create_energy(card_data.get("element")) + weakness_type_obj = _get_or_create_energy(card_data.get("weakness")) + card_defaults = { - 'name': card_data['name'], - 'cardset': card_set, - 'card_type': card_type_obj, - 'rarity': rarity_obj, - 'health': card_data.get('health'), - 'evolves_from_name': card_data.get('evolvesFrom'), - 'retreat_cost': card_data.get('retreatCost'), - 'pkmn_type': pkmn_type_obj, - 'weakness_type': weakness_type_obj, - 'checksum': incoming_checksum + "name": card_data["name"], + "cardset": card_set, + "card_type": card_type_obj, + "rarity": rarity_obj, + "health": card_data.get("health"), + "evolves_from_name": card_data.get("evolvesFrom"), + "retreat_cost": card_data.get("retreatCost"), + "pkmn_type": pkmn_type_obj, + "weakness_type": weakness_type_obj, + "checksum": incoming_checksum, } - - card_obj, card_created = Card_New.objects.language('en').update_or_create( - id=card_id, - defaults=card_defaults + + card_obj, card_created = Card.objects.language("en").update_or_create( + id=card_id, defaults=card_defaults ) if card_created: - stats_accumulator['newly_imported_count'] += 1 - elif existing_card: - stats_accumulator['updated_count'] +=1 + stats_accumulator["newly_imported_count"] += 1 + elif existing_card: + stats_accumulator["updated_count"] += 1 # If not created and checksum differs, it's an update, which is handled by updated_count. # update_or_create takes care of setting the new checksum via defaults. @@ -282,200 +337,400 @@ def _process_single_card_data(card_data, card_set, stats_accumulator, error_trac # However, for skipping based on *incoming JSON data*, this approach is correct. # The `update_or_create` will ensure the `checksum` field (which is part of `card_defaults`) is saved. -def perform_card_import_logic(): + +def _fetch_card_data_from_github_zip(): + """ + Downloads and extracts card data from the GitHub repository zip archive. + + Yields: + tuple: A tuple containing the file name (str) and its parsed JSON data (list). + Raises: + requests.exceptions.RequestException: If the download fails. + """ + owner = "hugoburguete" + repo = "pokemon-tcg-pocket-card-database" + branch = "main" + zip_url = f"https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.zip" + + print(f"Downloading card data from {zip_url}...") + response = requests.get(zip_url) + response.raise_for_status() # Will raise an exception for 4xx/5xx status + print("Download complete.") + + zip_file = zipfile.ZipFile(io.BytesIO(response.content)) + + # The root folder in the zip is usually `repo-name-branch-name` + path_prefix = f"{repo}-{branch}/cards/en/" + + json_file_paths = sorted( + [ + name + for name in zip_file.namelist() + if name.startswith(path_prefix) and name.endswith(".json") + ] + ) + + print(f"Found {len(json_file_paths)} JSON files in the archive.") + + for file_path in json_file_paths: + file_name = os.path.basename(file_path) + with zip_file.open(file_path) as f: + data = json.load(f) + yield file_name, data + + +def _fetch_card_data_from_local_files(): + """ + Reads card data from local JSON files for debugging purposes. + + Yields: + tuple: A tuple containing the file name (str) and its parsed JSON data (list). + """ + base_path = os.path.join( + settings.BASE_DIR, + "REMOTE_GIT_REPOS", + "pokemon-tcg-pocket-card-database", + "cards", + "en", + ) + print(f"DEBUG MODE: Reading card data from local path: {base_path}") + + if not os.path.isdir(base_path): + print(f"Source directory not found: {base_path}. Import halted.") + return # An empty generator + + json_files = sorted([f for f in os.listdir(base_path) if f.endswith(".json")]) + + print(f"Found {len(json_files)} local JSON files to process.") + + for file_name in json_files: + file_path = os.path.join(base_path, file_name) + with open(file_path, "r", encoding="utf-8") as f: + data = json.load(f) + yield file_name, data + + +def perform_card_import_logic() -> ImportResult: """ Main importer logic. Iterates through JSON files and processes them. + In DEBUG mode, it reads from local files. Otherwise, fetches from a remote GitHub repo. Halts and rolls back on any error. """ print("Card import process started.") - base_path = os.path.join(settings.BASE_DIR, 'REMOTE_GIT_REPOS', 'pokemon-tcg-pocket-card-database', 'cards', 'en') - - stats = {'newly_imported_count': 0, 'updated_count': 0, 'skipped_count': 0, 'files_processed_count': 0} - error_tracking = {'file_name': "N/A", 'card_id': "N/A"} + result = ImportResult() + error_tracking = {"file_name": "N/A", "card_id": "N/A"} # Fetch all rarity mappings once rarity_mappings = RarityMapping.objects.all() - rarity_mappings_dict = {mapping.original_name: mapping for mapping in rarity_mappings} + rarity_mappings_dict = { + mapping.original_name: mapping for mapping in rarity_mappings + } print(f"Loaded {len(rarity_mappings_dict)} rarity mappings.") - if not os.path.isdir(base_path): - message = f"Source directory not found: {base_path}. Import halted." - print(message) - return 0, 0, True, message, 0, 0 - - json_files = [f for f in os.listdir(base_path) if f.endswith('.json')] - json_files.sort() - if not json_files: - message = "No JSON files found in the source directory to import." - print(message) - return 0, 0, False, message, 0, 0 - - print(f"Found {len(json_files)} JSON files to process.") - try: - with transaction.atomic(): - for idx, file_name in enumerate(json_files): - error_tracking['file_name'] = file_name - error_tracking['card_id'] = "N/A" - file_path = os.path.join(base_path, file_name) - - print(f"Processing file: {file_name} ({idx + 1}/{len(json_files)})") + if settings.DEBUG: + card_data_iterator = _fetch_card_data_from_local_files() + source_message = "local files" + else: + # Fetch card data from the GitHub zip archive + card_data_iterator = _fetch_card_data_from_github_zip() + source_message = "the GitHub archive" - with open(file_path, 'r', encoding='utf-8') as f: - data = json.load(f) + all_files_data = list(card_data_iterator) + total_files = len(all_files_data) + + if not all_files_data: + result.message = f"No JSON files found in {source_message} to import." + print(result.message) + return result + + print(f"Found {total_files} JSON files to process from {source_message}.") + + with transaction.atomic(): + stats_accumulator = { + "newly_imported_count": 0, + "updated_count": 0, + "skipped_count": 0, + } + + for idx, (file_name, data) in enumerate(all_files_data): + error_tracking["file_name"] = file_name + error_tracking["card_id"] = "N/A" + + print(f"Processing file: {file_name} ({idx + 1}/{total_files})") if not data: - raise ValueError(f"JSON file {file_name} is empty or contains no data.") + raise ValueError( + f"JSON file {file_name} is empty or contains no data." + ) - stats['files_processed_count'] += 1 + result.files_processed_count += 1 first_card_data = data[0] - set_info_str = first_card_data.get('set') + set_info_str = first_card_data.get("set") if not set_info_str: - raise ValueError(f"Could not determine set information from first card in {file_name}.") - - parsed_set_name, parsed_set_id = parse_set_details(set_info_str) - if not parsed_set_id: - raise ValueError(f"Could not parse set ID from '{set_info_str}' in {file_name}.") + raise ValueError( + f"Could not determine set information from first card in {file_name}." + ) - card_set_defaults = { - 'name': parsed_set_name, - 'file_name': file_name - } - card_set, _ = CardSet_New.objects.language('en').update_or_create( - id=parsed_set_id, - defaults=card_set_defaults + parsed_set_name, parsed_set_id = parse_set_details(set_info_str) + if not parsed_set_id: + raise ValueError( + f"Could not parse set ID from '{set_info_str}' in {file_name}." + ) + + card_set_defaults = {"name": parsed_set_name, "file_name": file_name} + card_set, _ = CardSet.objects.language("en").update_or_create( + id=parsed_set_id, defaults=card_set_defaults ) for card_data_item in data: - print("Processing card: ", card_data_item['id']) - _process_single_card_data(card_data_item, card_set, stats, error_tracking, rarity_mappings_dict) - + print("Processing card: ", card_data_item["id"]) + _process_single_card_data( + card_data_item, + card_set, + stats_accumulator, + error_tracking, + rarity_mappings_dict, + ) + print(f"Finished processing file: {file_name}") - success_message = ( - f"Import completed successfully. Processed {stats['files_processed_count']} files. " - f"Imported {stats['newly_imported_count']} new cards. " - f"Updated {stats['updated_count']} existing cards. " - f"Skipped {stats['skipped_count']} unchanged cards." + result.newly_imported_count = stats_accumulator["newly_imported_count"] + result.updated_count = stats_accumulator["updated_count"] + result.skipped_count = stats_accumulator["skipped_count"] + + result.message = ( + f"Import completed successfully. Processed {result.files_processed_count} files. " + f"Imported {result.newly_imported_count} new cards. " + f"Updated {result.updated_count} existing cards. " + f"Skipped {result.skipped_count} unchanged cards." ) print("Committing transaction.") - transaction.on_commit(lambda: print(success_message)) - return stats['newly_imported_count'], stats['updated_count'], False, success_message, stats['files_processed_count'], stats['skipped_count'] + transaction.on_commit(lambda: print(result.message)) + return result + + except requests.exceptions.RequestException as e: + # Handle network-related errors for the download + result.has_error = True + result.message = f"Failed to download card data from GitHub: {e}" + print(result.message) + return result except Exception as e: - # Any exception during the process will cause the transaction to roll back. + # Any other exception during the process will cause the transaction to roll back. error_detail = f"Error during import (file: {error_tracking['file_name']}, card: {error_tracking['card_id']}): {str(e)}" - halt_message = f"Import HALTED. All changes rolled back. Reason: {error_detail}" - print(halt_message) - # Return 0 for counts as the transaction is rolled back - return 0, 0, True, halt_message, stats['files_processed_count'], stats.get('skipped_count', 0) + result.has_error = True + result.message = ( + f"Import HALTED. All changes rolled back. Reason: {error_detail}" + ) + print(result.message) + return result -if admin.site.is_registered(CardSet_New): admin.site.unregister(CardSet_New) -if admin.site.is_registered(Pack_New): admin.site.unregister(Pack_New) -if admin.site.is_registered(Energy_New): admin.site.unregister(Energy_New) -if admin.site.is_registered(Attack_New): admin.site.unregister(Attack_New) -if admin.site.is_registered(Ability_New): admin.site.unregister(Ability_New) -if admin.site.is_registered(Rarity_New): admin.site.unregister(Rarity_New) -if admin.site.is_registered(CardType_New): admin.site.unregister(CardType_New) -if admin.site.is_registered(Card_New): admin.site.unregister(Card_New) -if admin.site.is_registered(AttackCost_New): admin.site.unregister(AttackCost_New) -if admin.site.is_registered(RarityMapping): admin.site.unregister(RarityMapping) - - -@admin.register(CardSet_New) +@admin.register(CardSet) class CardSetAdmin(TranslatableAdmin): - list_display = ('id', 'name', 'file_name') - readonly_fields = ('id', 'file_name', 'created_at', 'updated_at') - search_fields = ('translations__name',) - readonly_fields = ('id', 'created_at', 'updated_at', 'deleted_at') + list_display = ("id", "name", "file_name") + search_fields = ("translations__name",) + readonly_fields = ("id", "file_name", "created_at", "updated_at", "deleted_at") -@admin.register(Pack_New) + def get_queryset(self, request): + return super().get_queryset(request).prefetch_related("translations") + + +@admin.register(Pack) class PackAdmin(TranslatableAdmin): - list_display = ('id', 'full_name', 'name', 'cardset', 'hex_color') - list_filter = ('cardset',) - search_fields = ('translations__name', 'translations__full_name') - readonly_fields = ('id', 'created_at', 'updated_at') + list_display = ("id", "full_name", "name", "cardset", "hex_color") + list_filter = ("cardset",) + search_fields = ("translations__name", "translations__full_name") + readonly_fields = ("id", "created_at", "updated_at") -@admin.register(Energy_New) + def get_queryset(self, request): + return ( + super() + .get_queryset(request) + .select_related("cardset") + .prefetch_related("translations", "cardset__translations") + ) + + +@admin.register(Energy) class EnergyAdmin(TranslatableAdmin): - list_display = ('id', 'name') - search_fields = ('translations__name',) - readonly_fields = ('id', 'created_at', 'updated_at', 'deleted_at') + list_display = ("id", "name") + search_fields = ("translations__name",) + readonly_fields = ("id", "created_at", "updated_at", "deleted_at") -@admin.register(Attack_New) + def get_queryset(self, request): + return super().get_queryset(request).prefetch_related("translations") + + +@admin.register(Attack) class AttackAdmin(TranslatableAdmin): - list_display = ('id', 'name', 'damage', 'effect') - search_fields = ('translations__name',) - readonly_fields = ('id', 'created_at', 'updated_at', 'deleted_at') + list_display = ("id", "name", "damage", "effect") + search_fields = ("translations__name",) + readonly_fields = ("id", "created_at", "updated_at", "deleted_at") -@admin.register(Ability_New) + def get_queryset(self, request): + return super().get_queryset(request).prefetch_related("translations") + + +@admin.register(Ability) class AbilityAdmin(TranslatableAdmin): - list_display = ('id', 'name', 'effect') - search_fields = ('translations__name',) - readonly_fields = ('id', 'created_at', 'updated_at') + list_display = ("id", "name", "effect") + search_fields = ("translations__name",) + readonly_fields = ("id", "created_at", "updated_at") -@admin.register(Rarity_New) + def get_queryset(self, request): + return super().get_queryset(request).prefetch_related("translations") + + +@admin.register(Rarity) class RarityAdmin(TranslatableAdmin): - list_display = ('id', 'name', 'icon', 'level') - search_fields = ('translations__name',) - readonly_fields = ('id', 'created_at', 'updated_at', 'deleted_at') + list_display = ("id", "name", "icon", "level") + search_fields = ("translations__name",) + readonly_fields = ("id", "created_at", "updated_at", "deleted_at") -@admin.register(CardType_New) + def get_queryset(self, request): + return super().get_queryset(request).prefetch_related("translations") + + +@admin.register(CardType) class CardTypeAdmin(TranslatableAdmin): - list_display = ('id', 'name', 'subtype') - search_fields = ('translations__name', 'translations__subtype') - readonly_fields = ('id', 'created_at', 'updated_at', 'deleted_at') + list_display = ("id", "name", "subtype") + search_fields = ("translations__name", "translations__subtype") + readonly_fields = ("id", "created_at", "updated_at", "deleted_at") -@admin.register(Card_New) + def get_queryset(self, request): + return super().get_queryset(request).prefetch_related("translations") + + +@admin.register(Card) class CardAdmin(TranslatableAdmin): - list_display = ('id', 'cardnum', 'name', 'cardset', 'card_type', 'rarity', 'health', 'pkmn_type') - list_filter = ('cardset', 'card_type', 'rarity', 'pkmn_type', 'packs') - search_fields = ('id', 'translations__name', 'cardset__translations__name', 'packs__translations__name') - filter_horizontal = ('packs', 'abilities', 'attacks') - readonly_fields = ('id', 'cardnum', 'created_at', 'updated_at', 'deleted_at') + list_display = ( + "id", + "cardnum", + "name", + "cardset", + "card_type", + "rarity", + "health", + "pkmn_type", + ) + list_filter = ( + ("cardset", PrefetchedSortedRelatedFieldListFilter), + ("card_type", PrefetchedSortedRelatedFieldListFilter), + ("rarity", PrefetchedSortedRelatedFieldListFilter), + ("pkmn_type", PrefetchedSortedRelatedFieldListFilter), + ("packs", PrefetchedSortedRelatedFieldListFilter), + ) + search_fields = ( + "id", + "translations__name", + "cardset__translations__name", + "packs__translations__name", + ) + filter_horizontal = ("packs", "abilities", "attacks") + readonly_fields = ("id", "cardnum", "created_at", "updated_at", "deleted_at") + + def get_queryset(self, request): + qs = super().get_queryset(request) + return qs.select_related( + "cardset", "card_type", "rarity", "pkmn_type" + ).prefetch_related( + "translations", + "cardset__translations", + "card_type__translations", + "rarity__translations", + "pkmn_type__translations", + ) + + +admin.site.register(AttackCost) -admin.site.register(AttackCost_New) @admin.register(RarityMapping) class RarityMappingAdmin(admin.ModelAdmin): - list_display = ('original_name', 'mapped_name', 'icon', 'level', 'created_at', 'updated_at', 'deleted_at') - search_fields = ('original_name', 'mapped_name') - list_filter = ('level',) - readonly_fields = ('created_at', 'updated_at', 'deleted_at') + list_display = ( + "original_name", + "mapped_name", + "icon", + "level", + "created_at", + "updated_at", + "deleted_at", + ) + search_fields = ("original_name", "mapped_name") + list_filter = ("level",) + readonly_fields = ("created_at", "updated_at", "deleted_at") + def get_admin_urls(urls): def importer_view(request): context = { - 'title': 'Card Importer', - 'site_header': admin.site.site_header, - 'site_title': admin.site.site_title, - 'index_title': admin.site.index_title, - 'has_permission': admin.site.has_permission(request), - 'app_label': 'cards', + "title": "Card Importer", + "site_header": admin.site.site_header, + "site_title": admin.site.site_title, + "index_title": admin.site.index_title, + "has_permission": admin.site.has_permission(request), + "app_label": "cards", } - if request.method == 'POST': - new, updated, has_error, message_text, files_processed, skipped = perform_card_import_logic() + if request.method == "POST": + result = perform_card_import_logic() - if has_error: - messages.error(request, message_text + f" Files attempted before halt: {files_processed}.") + if result.has_error: + message = result.message + if result.files_processed_count > 0: + message += ( + f" Files attempted before halt: {result.files_processed_count}." + ) + messages.error(request, message) else: - messages.success(request, message_text) + messages.success(request, result.message) return HttpResponseRedirect(request.path_info) - - return render(request, 'admin/cards/importer_status.html', context) + + return render(request, "admin/cards/importer_status.html", context) custom_urls = [ - path('cards/import/', admin.site.admin_view(importer_view), name='cards_full_importer'), + path( + "cards/import/", + admin.site.admin_view(importer_view), + name="cards_full_importer", + ), ] return custom_urls + urls + original_get_urls = admin.site.get_urls + def new_get_urls(): urls = original_get_urls() return get_admin_urls(urls) -admin.site.get_urls = new_get_urls \ No newline at end of file + +admin.site.get_urls = new_get_urls + + +# Restore admin sidebar link for the importer +original_get_app_list = admin.site.get_app_list + + +def new_get_app_list(request): + app_list = original_get_app_list(request) + for app in app_list: + if app.get("app_label") == "cards": + app["models"].insert( + 0, + { + "name": "Full Card Importer", + "object_name": "fullcardimporter", + "admin_url": reverse("admin:cards_full_importer"), + "view_only": True, + }, + ) + break + return app_list + + +admin.site.get_app_list = new_get_app_list diff --git a/src/pkmntrade_club/cards/migrations/0001_initial.py b/src/pkmntrade_club/cards/migrations/0001_initial.py index 03a0132..1536cf4 100644 --- a/src/pkmntrade_club/cards/migrations/0001_initial.py +++ b/src/pkmntrade_club/cards/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 5.1 on 2025-05-10 01:22 +# Generated by Django 5.1 on 2025-06-15 03:44 import django.db.models.deletion +import parler.fields +import parler.models from django.db import migrations, models @@ -8,64 +10,733 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Card', + name="Ability", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=64)), - ('cardset', models.CharField(max_length=32)), - ('cardnum', models.IntegerField()), - ('style', models.CharField(max_length=128)), - ('rarity_icon', models.CharField(max_length=12)), - ('rarity_level', models.IntegerField()), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), + ("id", models.AutoField(primary_key=True, serialize=False)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), ], + options={ + "verbose_name": "Ability", + "verbose_name_plural": "Abilities", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), ), migrations.CreateModel( - name='Deck', + name="Attack", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=64)), - ('hex_color', models.CharField(max_length=9)), - ('cardset', models.CharField(max_length=8)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), + ("id", models.AutoField(primary_key=True, serialize=False)), + ( + "damage", + models.CharField( + blank=True, + help_text="Damage string, e.g., '40', '20x', '80+'.", + max_length=10, + null=True, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), ], + options={ + "verbose_name": "Attack", + "verbose_name_plural": "Attacks", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), ), migrations.CreateModel( - name='CardNameTranslation', + name="CardSet", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=64)), - ('language', models.CharField(max_length=64)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('card', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='name_translations', to='cards.card')), + ( + "id", + models.CharField( + help_text="The ID for the set, e.g., 'A1', 'A1a'.", + max_length=3, + primary_key=True, + serialize=False, + ), + ), + ( + "file_name", + models.CharField( + help_text="Original name of the JSON file, e.g., 'a1-genetic-apex.json'.", + max_length=32, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), ], + options={ + "verbose_name": "Card Set", + "verbose_name_plural": "Card Sets", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), + ), + migrations.CreateModel( + name="CardType", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ], + options={ + "verbose_name": "Card Type", + "verbose_name_plural": "Card Types", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), + ), + migrations.CreateModel( + name="Energy", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ], + options={ + "verbose_name": "Energy", + "verbose_name_plural": "Energies", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), + ), + migrations.CreateModel( + name="Rarity", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ("icon", models.CharField(max_length=12)), + ("level", models.PositiveIntegerField()), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ], + options={ + "verbose_name": "Rarity", + "verbose_name_plural": "Rarities", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), + ), + migrations.CreateModel( + name="RarityMapping", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ( + "original_name", + models.CharField( + help_text="The rarity name as it appears in the import source (e.g., JSON file).", + max_length=255, + unique=True, + ), + ), + ( + "mapped_name", + models.CharField( + help_text="The standardized rarity name to use in the system.", + max_length=32, + ), + ), + ( + "icon", + models.CharField( + help_text="The icon associated with this rarity.", max_length=12 + ), + ), + ( + "level", + models.PositiveIntegerField( + help_text="The level or order of this rarity." + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ], + options={ + "verbose_name": "Rarity Mapping", + "verbose_name_plural": "Rarity Mappings", + "ordering": ["original_name"], + }, + ), + migrations.CreateModel( + name="AttackCost", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "quantity", + models.PositiveIntegerField( + default=1, + help_text="Quantity of this energy type required for the attack.", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ( + "attack", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="cards.attack" + ), + ), + ( + "energy", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="cards.energy" + ), + ), + ], + options={ + "verbose_name": "Attack Cost", + "verbose_name_plural": "Attack Costs", + "unique_together": {("attack", "energy")}, + }, ), migrations.AddField( - model_name='card', - name='decks', - field=models.ManyToManyField(to='cards.deck'), + model_name="attack", + name="energy_cost", + field=models.ManyToManyField( + related_name="attacks", through="cards.AttackCost", to="cards.energy" + ), ), migrations.CreateModel( - name='DeckNameTranslation', + name="Pack", fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=64)), - ('language', models.CharField(max_length=64)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('deck', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='name_translations', to='cards.deck')), + ("id", models.AutoField(primary_key=True, serialize=False)), + ("hex_color", models.CharField(max_length=9)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ( + "cardset", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="packs", + to="cards.cardset", + ), + ), ], + options={ + "verbose_name": "Pack", + "verbose_name_plural": "Packs", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), ), - migrations.AlterUniqueTogether( - name='card', - unique_together={('cardset', 'cardnum')}, + migrations.CreateModel( + name="Card", + fields=[ + ("cardnum", models.AutoField(primary_key=True, serialize=False)), + ( + "id", + models.CharField( + db_index=True, + help_text="The unique ID from the JSON source, cardset-cardnum (e.g., 'a1-001').", + max_length=10, + ), + ), + ( + "checksum", + models.CharField( + blank=True, + db_index=True, + help_text="SHA256 checksum of the card data.", + max_length=64, + null=True, + ), + ), + ( + "health", + models.PositiveIntegerField( + blank=True, help_text="HP of the Pokémon.", null=True + ), + ), + ( + "retreat_cost", + models.PositiveIntegerField( + blank=True, + help_text="The number of retreat cost for the card.", + null=True, + ), + ), + ( + "style", + models.CharField( + blank=True, + help_text="Inline CSS style for the card, used for dynamic styling.", + max_length=255, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("deleted_at", models.DateTimeField(blank=True, null=True)), + ( + "abilities", + models.ManyToManyField( + blank=True, related_name="cards", to="cards.ability" + ), + ), + ( + "attacks", + models.ManyToManyField(related_name="cards", to="cards.attack"), + ), + ( + "cardset", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="cards", + to="cards.cardset", + ), + ), + ( + "card_type", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="cards", + to="cards.cardtype", + ), + ), + ( + "pkmn_type", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="cards_pkmn_type", + to="cards.energy", + ), + ), + ( + "weakness_type", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="cards_weakness_type", + to="cards.energy", + ), + ), + ( + "packs", + models.ManyToManyField(related_name="cards", to="cards.pack"), + ), + ( + "rarity", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="cards", + to="cards.rarity", + ), + ), + ], + options={ + "verbose_name": "Card", + "verbose_name_plural": "Cards", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), + ), + migrations.CreateModel( + name="AbilityTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ( + "name", + models.CharField( + help_text="The name of the ability.", max_length=32 + ), + ), + ( + "effect", + models.TextField(help_text="Description of the ability's effect."), + ), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="cards.ability", + ), + ), + ], + options={ + "verbose_name": "Ability Translation", + "db_table": "cards_ability_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + migrations.CreateModel( + name="AttackTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ( + "name", + models.CharField( + help_text="The name of the attack.", max_length=32 + ), + ), + ( + "effect", + models.TextField(help_text="Description of the attack's effect."), + ), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="cards.attack", + ), + ), + ], + options={ + "verbose_name": "Attack Translation", + "db_table": "cards_attack_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + migrations.CreateModel( + name="CardSetTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ( + "name", + models.CharField( + help_text="The full name of the set, e.g., 'Genetic Apex'.", + max_length=32, + ), + ), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="cards.cardset", + ), + ), + ], + options={ + "verbose_name": "Card Set Translation", + "db_table": "cards_cardset_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + migrations.CreateModel( + name="CardTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ( + "name", + models.CharField(help_text="The name of the card.", max_length=32), + ), + ( + "evolves_from_name", + models.CharField( + blank=True, + help_text="Name of the Pokémon this card evolves from.", + max_length=32, + null=True, + ), + ), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="cards.card", + ), + ), + ], + options={ + "verbose_name": "Card Translation", + "db_table": "cards_card_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + migrations.CreateModel( + name="CardTypeTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ( + "name", + models.CharField( + help_text="The name of the card type.", max_length=32 + ), + ), + ( + "subtype", + models.CharField( + blank=True, + help_text="The subtype of the card type.", + max_length=32, + null=True, + ), + ), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="cards.cardtype", + ), + ), + ], + options={ + "verbose_name": "Card Type Translation", + "db_table": "cards_cardtype_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + migrations.CreateModel( + name="EnergyTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ( + "name", + models.CharField( + help_text="The name of the energy.", max_length=32 + ), + ), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="cards.energy", + ), + ), + ], + options={ + "verbose_name": "Energy Translation", + "db_table": "cards_energy_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + migrations.CreateModel( + name="PackTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ( + "full_name", + models.CharField( + help_text="The full name of the pack, e.g., 'Genetic Apex: Mewtwo'.", + max_length=32, + ), + ), + ( + "name", + models.CharField( + help_text="The pack name itself, e.g., 'Mewtwo'.", max_length=32 + ), + ), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="cards.pack", + ), + ), + ], + options={ + "verbose_name": "Pack Translation", + "db_table": "cards_pack_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + migrations.CreateModel( + name="RarityTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ( + "name", + models.CharField( + help_text="The name of the rarity.", max_length=32 + ), + ), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="cards.rarity", + ), + ), + ], + options={ + "verbose_name": "Rarity Translation", + "db_table": "cards_rarity_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), ), ] diff --git a/src/pkmntrade_club/cards/models.py b/src/pkmntrade_club/cards/models.py index d443a6a..7e05f57 100644 --- a/src/pkmntrade_club/cards/models.py +++ b/src/pkmntrade_club/cards/models.py @@ -1,6 +1,35 @@ from django.db import models -from parler.models import TranslatableModel, TranslatedFields from django.utils.translation import gettext_lazy as _ +from parler.managers import TranslatableManager +from parler.models import TranslatableModel, TranslatedFields + + +class CardManager(TranslatableManager): + def with_details(self): + """ + Returns a Card queryset with all related fields pre-selected to avoid N+1 queries. + """ + return ( + self.get_queryset() + .select_related( + "rarity", + "cardset", + "card_type", + "pkmn_type", + "weakness_type", + ) + .prefetch_related( + "translations", + "rarity__translations", + "cardset__translations", + "card_type__translations", + "pkmn_type__translations", + "weakness_type__translations", + "attacks__translations", + "abilities__translations", + "packs__translations", + ) + ) class CardSet(TranslatableModel): @@ -29,8 +58,8 @@ class CardSet(TranslatableModel): deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Card Set (New)") - verbose_name_plural = _("Card Sets (New)") + verbose_name = _("Card Set") + verbose_name_plural = _("Card Sets") def __str__(self): return f"{self.id} - {self.name}" @@ -58,8 +87,8 @@ class Pack(TranslatableModel): deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Pack (New)") - verbose_name_plural = _("Packs (New)") + verbose_name = _("Pack") + verbose_name_plural = _("Packs") def __str__(self): return f"{self.full_name}" @@ -79,8 +108,8 @@ class Energy(TranslatableModel): deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Energy (New)") - verbose_name_plural = _("Energies (New)") + verbose_name = _("Energy") + verbose_name_plural = _("Energies") def __str__(self): return f"{self.name}" @@ -101,8 +130,8 @@ class AttackCost(models.Model): deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Attack Cost (New)") - verbose_name_plural = _("Attack Costs (New)") + verbose_name = _("Attack Cost") + verbose_name_plural = _("Attack Costs") unique_together = ("attack", "energy") def __str__(self): @@ -133,8 +162,8 @@ class Attack(TranslatableModel): deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Attack (New)") - verbose_name_plural = _("Attacks (New)") + verbose_name = _("Attack") + verbose_name_plural = _("Attacks") def __str__(self): return f"{self.name}" @@ -155,8 +184,8 @@ class Ability(TranslatableModel): deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Ability (New)") - verbose_name_plural = _("Abilities (New)") + verbose_name = _("Ability") + verbose_name_plural = _("Abilities") def __str__(self): return f"{self.name}" @@ -178,8 +207,8 @@ class Rarity(TranslatableModel): deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Rarity (New)") - verbose_name_plural = _("Rarities (New)") + verbose_name = _("Rarity") + verbose_name_plural = _("Rarities") def __str__(self): return f"{self.name}" @@ -205,8 +234,8 @@ class CardType(TranslatableModel): deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Card Type (New)") - verbose_name_plural = _("Card Types (New)") + verbose_name = _("Card Type") + verbose_name_plural = _("Card Types") def __str__(self): return f"{self.name}" @@ -217,6 +246,8 @@ class Card(TranslatableModel): Represents a single, unique digital printing of a Pokémon card. """ + objects = CardManager() + translations = TranslatedFields( name=models.CharField(max_length=32, help_text=_("The name of the card.")), evolves_from_name=models.CharField( @@ -271,13 +302,19 @@ class Card(TranslatableModel): attacks = models.ManyToManyField(Attack, related_name="cards") rarity = models.ForeignKey(Rarity, on_delete=models.CASCADE, related_name="cards") + style = models.CharField( + max_length=255, + blank=True, + help_text=_("Inline CSS style for the card, used for dynamic styling."), + ) + created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) deleted_at = models.DateTimeField(null=True, blank=True) class Meta: - verbose_name = _("Card (New)") - verbose_name_plural = _("Cards (New)") + verbose_name = _("Card") + verbose_name_plural = _("Cards") def __str__(self): return f"{self.id} {self.name}" diff --git a/src/pkmntrade_club/cards/signals.py b/src/pkmntrade_club/cards/signals.py index af9a8ee..64afbee 100644 --- a/src/pkmntrade_club/cards/signals.py +++ b/src/pkmntrade_club/cards/signals.py @@ -1,5 +1,9 @@ -from django.db.models.signals import m2m_changed +from django.core.cache import cache +from django.db.models.signals import m2m_changed, post_delete, post_save from django.dispatch import receiver + +from pkmntrade_club.trades.models import TradeOfferHaveCard, TradeOfferWantCard + from .models import Card @@ -34,24 +38,68 @@ def color_is_dark(bg_color): return brightness <= 200 -@receiver(m2m_changed, sender=Card.decks.through) +@receiver(m2m_changed, sender=Card.packs.through) def update_card_style(sender, instance, action, **kwargs): if action == "post_add": - decks = instance.decks.all() - num_decks = decks.count() - if num_decks == 1: - instance.style = "background-color: " + decks.first().hex_color + ";" - elif num_decks >= 2: - hex_colors = [deck.hex_color for deck in decks] - instance.style = ( - f"background: linear-gradient(to right, {', '.join(hex_colors)});" - ) - else: - instance.style = ( + packs = instance.packs.all() + num_packs = packs.count() + + style_parts = [] + + if num_packs == 0: + style_parts.append( "background: linear-gradient(to right, #AAAAAA, #AAAAAA, #AAAAAA);" ) - if not color_is_dark(decks.first().hex_color): - instance.style += "color: var(--color-gray-700); text-shadow: 0 0 0 var(--color-gray-700);" + style_parts.append("text-shadow: 0 0 0 #fff;") else: - instance.style += "text-shadow: 0 0 0 #fff;" + if num_packs == 1: + style_parts.append(f"background-color: {packs.first().hex_color};") + else: # num_packs >= 2 + hex_colors = [pack.hex_color for pack in packs] + gradient = f"linear-gradient(to right, {', '.join(hex_colors)})" + style_parts.append(f"background: {gradient};") + + if not color_is_dark(packs.first().hex_color): + style_parts.append("color: var(--color-gray-700);") + style_parts.append("text-shadow: 0 0 0 var(--color-gray-700);") + else: + style_parts.append("text-shadow: 0 0 0 #fff;") + + instance.style = "".join(style_parts) instance.save(update_fields=["style"]) + + +def invalidate_card_cache(instance): + """Invalidate cache for a card.""" + # Invalidate the card_badge cache + # The key is constructed as "card_badge_" + cache.delete(f"card_badge_{instance.pk}") + + # Invalidate card_multiselect cache by clearing all of them using a pattern. + # This is necessary as we can't easily reconstruct all possible keys in the signal. + cache.delete_pattern("card_multiselect_*") + + # Invalidate trade offers that contain this card in either have or want lists. + have_offers_pks = TradeOfferHaveCard.objects.filter(card=instance).values_list( + "trade_offer_id", flat=True + ) + want_offers_pks = TradeOfferWantCard.objects.filter(card=instance).values_list( + "trade_offer_id", flat=True + ) + + all_offer_pks = set(have_offers_pks) | set(want_offers_pks) + + for offer_pk in all_offer_pks: + cache.delete(f"trade_offer_{offer_pk}") + + +@receiver(post_save, sender=Card) +def on_card_save(sender, instance, **kwargs): + """Invalidate cache for a card when it's updated.""" + invalidate_card_cache(instance) + + +@receiver(post_delete, sender=Card) +def on_card_delete(sender, instance, **kwargs): + """Invalidate cache for a card when it's deleted.""" + invalidate_card_cache(instance) diff --git a/src/pkmntrade_club/cards/templatetags/card_badge.py b/src/pkmntrade_club/cards/templatetags/card_badge.py index adc2930..23c5d6c 100644 --- a/src/pkmntrade_club/cards/templatetags/card_badge.py +++ b/src/pkmntrade_club/cards/templatetags/card_badge.py @@ -1,8 +1,8 @@ from django import template from django.conf import settings from django.template.loader import render_to_string -from django.utils.safestring import mark_safe from django.urls import reverse_lazy +from django.utils.safestring import mark_safe register = template.Library() @@ -17,11 +17,12 @@ def card_badge(context, card, quantity=None, expanded=False): "quantity": quantity, "style": card.style, "name": card.name, - "rarity": card.rarity_icon, - "cardset": card.cardset, + "rarity": card.rarity.icon, + "cardset": card.cardset.id, "expanded": expanded, "cache_key": f"card_badge_{card.pk}_{quantity}_{expanded}", "url": url, + "CACHE_LONG_TIMEOUT": settings.CACHE_LONG_TIMEOUT, } context.update(tag_context) return context @@ -37,11 +38,11 @@ def card_badge_inline(card, quantity=None): "quantity": quantity, "style": card.style, "name": card.name, - "rarity": card.rarity_icon, + "rarity": card.rarity, "cardset": card.cardset, "expanded": True, "cache_key": f"card_badge_{card.pk}_{quantity}_{True}", - "CACHE_TIMEOUT": settings.CACHE_TIMEOUT, + "CACHE_LONG_TIMEOUT": settings.CACHE_LONG_TIMEOUT, "url": url, } html = render_to_string("templatetags/card_badge.html", tag_context) diff --git a/src/pkmntrade_club/cards/templatetags/card_multiselect.py b/src/pkmntrade_club/cards/templatetags/card_multiselect.py index 65b43a3..7f616ef 100644 --- a/src/pkmntrade_club/cards/templatetags/card_multiselect.py +++ b/src/pkmntrade_club/cards/templatetags/card_multiselect.py @@ -1,10 +1,12 @@ -import uuid -from django import template -from pkmntrade_club.cards.models import Card -from django.db.models.query import QuerySet -import json import hashlib +import json import logging +import uuid + +from django import template +from django.db.models.query import QuerySet + +from pkmntrade_club.cards.models import Card register = template.Library() @@ -18,7 +20,7 @@ def get_item(dictionary, key): @register.simple_tag def fetch_all_cards(): """Simple tag to fetch all Card objects.""" - return Card.objects.order_by("pk").all() + return Card.objects.with_details().order_by("id").all() @register.inclusion_tag("templatetags/card_multiselect.html", takes_context=True) diff --git a/src/pkmntrade_club/cards/views.py b/src/pkmntrade_club/cards/views.py index f4c1cf2..e5f7457 100644 --- a/src/pkmntrade_club/cards/views.py +++ b/src/pkmntrade_club/cards/views.py @@ -1,12 +1,13 @@ -from django.views.generic import ( - ListView, - DetailView, -) -from pkmntrade_club.cards.models import Card -from pkmntrade_club.trades.models import TradeOffer -from pkmntrade_club.common.mixins import ReusablePaginationMixin -from django.views import View from django.shortcuts import get_object_or_404, render +from django.views import View +from django.views.generic import ( + DetailView, + ListView, +) + +from pkmntrade_club.cards.models import Card +from pkmntrade_club.common.mixins import ReusablePaginationMixin +from pkmntrade_club.trades.models import TradeOffer class CardDetailView(DetailView): @@ -14,6 +15,24 @@ class CardDetailView(DetailView): template_name = "cards/card_detail.html" context_object_name = "card" + def get_queryset(self): + qs = super().get_queryset() + # Prefetch all related data and translations to avoid N+1 queries in the template. + return qs.select_related( + "cardset", "rarity", "card_type", "pkmn_type", "weakness_type" + ).prefetch_related( + "translations", + "cardset__translations", + "rarity__translations", + "card_type__translations", + "pkmn_type__translations", + "weakness_type__translations", + "packs__translations", + "abilities__translations", + "attacks__translations", + "attacks__energy_cost__energy__translations", + ) + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) card = self.get_object() @@ -94,18 +113,43 @@ class CardListView(ReusablePaginationMixin, ListView): def get_ordering(self): order = self.request.GET.get("order", "absolute") + group_by = self.request.GET.get("group_by") + + # When grouping, the ordering must match the grouping attribute. + if group_by == "cardset": + return ("cardset__name",) + elif group_by == "rarity": + # Order by level (desc) then by icon/name to keep groups together. + return "-rarity__level", "rarity__icon" + # Note: Grouping by 'pack' is complex due to M2M and would require a custom implementation. + if order == "alphabetical": return "name" elif order == "rarity": - return "-rarity_level" + 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() + # Handle both single string and tuple orderings + if isinstance(ordering, tuple): + qs = qs.order_by(*ordering) + else: + qs = qs.order_by(ordering) + return ( + qs.select_related("cardset", "rarity", "card_type", "pkmn_type") + .prefetch_related( + "translations", + "cardset__translations", + "rarity__translations", + "card_type__translations", + "pkmn_type__translations", + "packs__translations", + ) + .distinct() + ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -114,55 +158,14 @@ class CardListView(ReusablePaginationMixin, ListView): context["order"] = order context["group_by"] = group_by - if group_by in ("deck", "cardset", "rarity"): - full_qs = self.get_queryset() - all_cards = list(full_qs) - flat_cards = [] - if group_by == "deck": - 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_icon, - "sort_group": card.rarity_level, - "card": card, - } - ) - flat_cards.sort(key=lambda x: x["sort_group"], reverse=True) - - page_number = self.get_page_number() - self.per_page = 36 - page_flat_cards, pagination_context = self.paginate_data( - flat_cards, page_number - ) - - 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 - context["page_obj"] = pagination_context - context["total_cards"] = len(flat_cards) - context["object_list"] = full_qs - else: - page_number = self.get_page_number() - self.per_page = 36 - paginated_cards, pagination_context = self.paginate_data( - self.get_queryset(), page_number - ) - context["cards"] = paginated_cards - context["page_obj"] = pagination_context - context["object_list"] = self.get_queryset() + # Unified pagination logic for all cases. + # The complex manual grouping logic has been removed for performance. + # The template should now use the `regroup` template tag for display. + page_number = self.get_page_number() + self.per_page = 36 + queryset = self.get_queryset() + paginated_cards, pagination_context = self.paginate_data(queryset, page_number) + context["cards"] = paginated_cards + context["page_obj"] = pagination_context + context["object_list"] = queryset return context diff --git a/src/pkmntrade_club/common/context_processors.py b/src/pkmntrade_club/common/context_processors.py index b286201..4ee3c97 100644 --- a/src/pkmntrade_club/common/context_processors.py +++ b/src/pkmntrade_club/common/context_processors.py @@ -1,9 +1,23 @@ +import random + from django.conf import settings def cache_settings(request): + """ + Pass cache settings to the template context. + Applies jitter to the timeouts. + """ + jitter = settings.CACHE_JITTER return { - "CACHE_TIMEOUT": settings.CACHE_TIMEOUT, + "CACHE_SHORT_TIMEOUT": settings.CACHE_SHORT_TIMEOUT + + random.randint(-jitter, jitter), + "CACHE_MEDIUM_TIMEOUT": settings.CACHE_MEDIUM_TIMEOUT + + random.randint(-jitter, jitter), + "CACHE_LONG_TIMEOUT": settings.CACHE_LONG_TIMEOUT + + random.randint(-jitter, jitter), + "CACHE_DEFAULT_TIMEOUT": settings.CACHE_DEFAULT_TIMEOUT + + random.randint(-jitter, jitter), } diff --git a/src/pkmntrade_club/django_project/settings.py b/src/pkmntrade_club/django_project/settings.py index eb152d8..43ec668 100644 --- a/src/pkmntrade_club/django_project/settings.py +++ b/src/pkmntrade_club/django_project/settings.py @@ -1,10 +1,12 @@ -import socket -from pathlib import Path -import environ -import os import logging +import os +import socket import sys +from pathlib import Path + +import environ from django.utils.translation import gettext_lazy as _ + from pkmntrade_club._version import __version__, get_version_info # set default values to local dev values @@ -35,7 +37,11 @@ env = environ.Env( ACCOUNT_EMAIL_VERIFICATION=(str, "none"), SCHEME=(str, "http"), REDIS_URL=(str, "redis://localhost:6379"), - CACHE_TIMEOUT=(int, 604800), + CACHE_DEFAULT_TIMEOUT=(int, 60 * 5), # 5 minutes + CACHE_SHORT_TIMEOUT=(int, 60 * 5), # 5 minutes + CACHE_MEDIUM_TIMEOUT=(int, 60 * 60 * 1), # 1 hour + CACHE_LONG_TIMEOUT=(int, 60 * 60 * 24), # 24 hours + CACHE_JITTER=(int, 30), # 30 seconds TIME_ZONE=(str, "America/Los_Angeles"), ) @@ -92,7 +98,11 @@ environ.Env.read_env(os.path.join(BASE_DIR, ".env")) SCHEME = env("SCHEME") PUBLIC_HOST = env("PUBLIC_HOST") REDIS_URL = env("REDIS_URL") -CACHE_TIMEOUT = env("CACHE_TIMEOUT") +CACHE_DEFAULT_TIMEOUT = env("CACHE_DEFAULT_TIMEOUT") +CACHE_SHORT_TIMEOUT = env("CACHE_SHORT_TIMEOUT") +CACHE_MEDIUM_TIMEOUT = env("CACHE_MEDIUM_TIMEOUT") +CACHE_LONG_TIMEOUT = env("CACHE_LONG_TIMEOUT") +CACHE_JITTER = env("CACHE_JITTER") DISABLE_SIGNUPS = env("DISABLE_SIGNUPS") DISABLE_CACHE = env("DISABLE_CACHE") diff --git a/src/pkmntrade_club/home/views.py b/src/pkmntrade_club/home/views.py index 22c66af..29fc6f7 100644 --- a/src/pkmntrade_club/home/views.py +++ b/src/pkmntrade_club/home/views.py @@ -1,14 +1,17 @@ +import logging from collections import OrderedDict -from django.views.generic import TemplateView + from django.db.models import ( + Max, Sum, ) from django.db.models.functions import Coalesce +from django.views.generic import TemplateView + +from pkmntrade_club.cards.models import Card from pkmntrade_club.trades.models import ( TradeOffer, ) -from pkmntrade_club.cards.models import Card -import logging logger = logging.getLogger(__name__) @@ -22,8 +25,10 @@ class HomePageView(TemplateView): 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" + context["cards"] = ( + Card.objects.with_details() + .filter(rarity__level__lte=5) + .order_by("translations__name", "rarity__level") ) # Reuse base trade offer queryset for market stats @@ -33,9 +38,15 @@ class HomePageView(TemplateView): 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')}" - ) + latest_update = recent_offers_qs.aggregate(latest=Max("updated_at"))[ + "latest" + ] + if latest_update: + context["cache_key_recent_offers"] = ( + f"recent_offers_{latest_update.timestamp()}" + ) + else: + context["cache_key_recent_offers"] = "recent_offers_empty" except Exception as e: logger.error(f"Error fetching recent offers: {str(e)}") context["recent_offers"] = [] @@ -44,15 +55,22 @@ class HomePageView(TemplateView): # Most Offered Cards try: most_offered_cards_qs = ( - Card.objects.filter(tradeofferhavecard__isnull=False) - .filter(rarity_level__lte=5) + Card.objects.with_details() + .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')}" - ) + latest_update = most_offered_cards_qs.aggregate( + latest=Max("updated_at") + )["latest"] + if latest_update: + context["cache_key_most_offered_cards"] = ( + f"most_offered_{latest_update.timestamp()}" + ) + else: + context["cache_key_most_offered_cards"] = "most_offered_empty" except Exception as e: logger.error(f"Error fetching most offered cards: {str(e)}") context["most_offered_cards"] = [] @@ -60,15 +78,22 @@ class HomePageView(TemplateView): # Most Wanted Cards try: most_wanted_cards_qs = ( - Card.objects.filter(tradeofferwantcard__isnull=False) - .filter(rarity_level__lte=5) + Card.objects.with_details() + .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')}" - ) + latest_update = most_wanted_cards_qs.aggregate( + latest=Max("updated_at") + )["latest"] + if latest_update: + context["cache_key_most_wanted_cards"] = ( + f"most_wanted_{latest_update.timestamp()}" + ) + else: + context["cache_key_most_wanted_cards"] = "most_wanted_empty" except Exception as e: logger.error(f"Error fetching most wanted cards: {str(e)}") context["most_wanted_cards"] = [] @@ -76,16 +101,23 @@ class HomePageView(TemplateView): # Least Offered Cards try: least_offered_cards_qs = ( - Card.objects.filter(rarity_level__lte=5) + Card.objects.with_details() + .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')}" - ) + latest_update = least_offered_cards_qs.aggregate( + latest=Max("updated_at") + )["latest"] + if latest_update: + context["cache_key_least_offered_cards"] = ( + f"least_offered_{latest_update.timestamp()}" + ) + else: + context["cache_key_least_offered_cards"] = "least_offered_empty" except Exception as e: logger.error(f"Error fetching least offered cards: {str(e)}") context["least_offered_cards"] = [] @@ -94,10 +126,21 @@ class HomePageView(TemplateView): featured = OrderedDict() # Featured "All" offers remains fixed at the top try: - featured["All"] = base_offer_qs.order_by("created_at")[:6] + all_featured = base_offer_qs.order_by("created_at")[:6] + featured["All"] = all_featured + latest_update = all_featured.aggregate(latest=Max("updated_at"))[ + "latest" + ] + if latest_update: + context["cache_key_featured_offers"] = ( + f"featured_all_{latest_update.timestamp()}" + ) + else: + context["cache_key_featured_offers"] = "featured_all_empty" except Exception as e: logger.error(f"Error fetching 'All' featured offers: {str(e)}") featured["All"] = [] + context["cache_key_featured_offers"] = "featured_all_error" # *** we only show All Featured Offers for now, # *** we will add rarity-tabbed featured offers later @@ -121,23 +164,6 @@ class HomePageView(TemplateView): # 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 - # *** we will separate cache keys for each featured section later - 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 diff --git a/src/pkmntrade_club/theme/templates/base.html b/src/pkmntrade_club/theme/templates/base.html index 7098131..c97662f 100644 --- a/src/pkmntrade_club/theme/templates/base.html +++ b/src/pkmntrade_club/theme/templates/base.html @@ -138,6 +138,8 @@ + + {% block javascript %}{% endblock %} \ No newline at end of file diff --git a/src/pkmntrade_club/theme/templates/cards/card_detail.html b/src/pkmntrade_club/theme/templates/cards/card_detail.html index 96ed018..a45b270 100644 --- a/src/pkmntrade_club/theme/templates/cards/card_detail.html +++ b/src/pkmntrade_club/theme/templates/cards/card_detail.html @@ -5,7 +5,7 @@

{{card.name}}

-

{{ card.cardset }} #{{ card.cardnum }} • {{ card.rarity_icon }}

+

{{ card.cardset }} #{{ card.cardnum }} • {{ card.rarity.icon }}

diff --git a/src/pkmntrade_club/theme/templates/cards/card_list.html b/src/pkmntrade_club/theme/templates/cards/card_list.html index 155a68d..2ab713f 100644 --- a/src/pkmntrade_club/theme/templates/cards/card_list.html +++ b/src/pkmntrade_club/theme/templates/cards/card_list.html @@ -1,63 +1,48 @@ {% extends "base.html" %} {% load static card_badge %} + {% block content %} -
-
-
-

Cards

-
-
- - - - -
+
+

Card List

+ +
+
- -
- {% include "cards/_card_list.html" with cards=cards page_obj=page_obj %} + + {% if group_by == 'rarity' %} + {% regroup cards by rarity as grouped_list %} + {% for group in grouped_list %} +
+

{{ group.grouper.icon }} {{ group.grouper.name }}

+
+ {% for card in group.list %} + {% card_badge card expanded=True %} + {% endfor %} +
+
+ {% endfor %} + {% elif group_by == 'cardset' %} + {% regroup cards by cardset as grouped_list %} + {% for group in grouped_list %} +
+

{{ group.grouper.name }}

+
+ {% for card in group.list %} + {% card_badge card expanded=True %} + {% endfor %} +
+
+ {% endfor %} + {% else %} +
+ {% for card in cards %} + {% card_badge card expanded=True %} + {% endfor %} +
+ {% endif %} + +
+ {% include "templatetags/pagination_controls.html" with page_obj=page_obj %}
{% endblock %} \ No newline at end of file diff --git a/src/pkmntrade_club/theme/templates/home/_card_list.html b/src/pkmntrade_club/theme/templates/home/_card_list.html index 2669cca..0681317 100644 --- a/src/pkmntrade_club/theme/templates/home/_card_list.html +++ b/src/pkmntrade_club/theme/templates/home/_card_list.html @@ -3,6 +3,7 @@
{% for card in cards %} {% card_badge card quantity=card.offer_count expanded=True %} +
{{ card.rarity.icon }}
{% endfor %}
{% else %} diff --git a/src/pkmntrade_club/theme/templates/home/home.html b/src/pkmntrade_club/theme/templates/home/home.html index 00de406..445f940 100644 --- a/src/pkmntrade_club/theme/templates/home/home.html +++ b/src/pkmntrade_club/theme/templates/home/home.html @@ -45,7 +45,7 @@ Welcome

Card Stats

- {% cache CACHE_TIMEOUT most_offered_cards cache_key_most_offered_cards %} + {% cache CACHE_MEDIUM_TIMEOUT most_offered_cards cache_key_most_offered_cards %}
@@ -58,7 +58,7 @@ Welcome
{% endcache %} - {% cache CACHE_TIMEOUT most_wanted_cards cache_key_most_wanted_cards %} + {% cache CACHE_MEDIUM_TIMEOUT most_wanted_cards cache_key_most_wanted_cards %}
@@ -71,7 +71,7 @@ Welcome
{% endcache %} - {% cache CACHE_TIMEOUT least_offered_cards cache_key_least_offered_cards %} + {% cache CACHE_MEDIUM_TIMEOUT least_offered_cards cache_key_least_offered_cards %}
@@ -90,7 +90,7 @@ Welcome
- {% cache CACHE_TIMEOUT featured_offers cache_key_featured_offers %} + {% cache CACHE_MEDIUM_TIMEOUT featured_offers cache_key_featured_offers %}
Featured Offers
@@ -109,7 +109,7 @@ Welcome
{% endcache %} - {% cache CACHE_TIMEOUT recent_offers cache_key_recent_offers %} + {% cache CACHE_MEDIUM_TIMEOUT recent_offers cache_key_recent_offers %}
Recent Offers
diff --git a/src/pkmntrade_club/theme/templatetags/_card_multiselect_options.html b/src/pkmntrade_club/theme/templatetags/_card_multiselect_options.html index a1c0b3b..1361ea7 100644 --- a/src/pkmntrade_club/theme/templatetags/_card_multiselect_options.html +++ b/src/pkmntrade_club/theme/templatetags/_card_multiselect_options.html @@ -7,23 +7,25 @@ value="{{ card.pk }}:{{ selected_cards|get_item:card_id_str }}" data-card-id="{{ card.pk }}" data-quantity="{{ selected_cards|get_item:card_id_str }}" + data-rarity="{{ card.rarity.level }}" + data-cardset="{{ card.cardset.id }}" + data-packs="{% for pack in card.packs.all %}{{ pack.id }},{% endfor %}" selected data-html-content='
{{ card|card_badge_inline:selected_cards|get_item:card_id_str }}
' - data-name="{{ card.name }}" - data-rarity="{{ card.rarity_icon }}" - data-cardset="{{ card.cardset }}"> - {{ card.name }} {{ card.rarity_icon }} {{ card.cardset }} + data-name="{{ card.name }}"> + {{ card.name }} ({{ card.id }}) {% else %} {% endif %} {% endwith %} diff --git a/src/pkmntrade_club/theme/templatetags/card_badge.html b/src/pkmntrade_club/theme/templatetags/card_badge.html index 721bec8..f1f9774 100644 --- a/src/pkmntrade_club/theme/templatetags/card_badge.html +++ b/src/pkmntrade_club/theme/templatetags/card_badge.html @@ -1,5 +1,5 @@ {% load cache %} -{% cache CACHE_TIMEOUT card_badge cache_key %} +{% cache CACHE_LONG_TIMEOUT "card_badge" card.pk %}
{% if not expanded %} diff --git a/src/pkmntrade_club/theme/templatetags/card_multiselect.html b/src/pkmntrade_club/theme/templatetags/card_multiselect.html index f69fd1d..26b2920 100644 --- a/src/pkmntrade_club/theme/templatetags/card_multiselect.html +++ b/src/pkmntrade_club/theme/templatetags/card_multiselect.html @@ -5,7 +5,7 @@ {{ label }}