Last bit of major changes
Closes #1 Closes #5 Closes #6 Closes #8 Closes #9 Closes #10
This commit is contained in:
@@ -1,139 +1,201 @@
|
||||
import random
|
||||
import requests
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.text import slugify
|
||||
from django.contrib.auth import get_user_model
|
||||
from store.models import Game, Set, Card, CardListing
|
||||
from faker import Faker
|
||||
from store.models import Game, Set, Card, CardListing, Seller
|
||||
|
||||
User = get_user_model()
|
||||
fake = Faker()
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Populate database with MTG data from Scryfall and fake data for other games'
|
||||
help = 'Populate database with specific test stores and listings, leveraging external scripts for card data.'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.stdout.write('Starting database population...')
|
||||
|
||||
# Create Games
|
||||
mtg, _ = Game.objects.get_or_create(name='Magic: The Gathering', slug='mtg')
|
||||
pokemon, _ = Game.objects.get_or_create(name='Pokemon TCG', slug='pokemon')
|
||||
lorcana, _ = Game.objects.get_or_create(name='Disney Lorcana', slug='lorcana')
|
||||
|
||||
# Populate MTG (Real Data)
|
||||
self.populate_mtg(mtg)
|
||||
|
||||
# Populate Pokemon (Fake Data)
|
||||
self.populate_fake_game(pokemon, 'Pokemon')
|
||||
|
||||
# Populate Lorcana (Fake Data)
|
||||
self.populate_fake_game(lorcana, 'Lorcana')
|
||||
|
||||
# Create Superuser
|
||||
if not User.objects.filter(username='admin').exists():
|
||||
User.objects.create_superuser('admin', 'admin@example.com', 'admin')
|
||||
self.stdout.write(self.style.SUCCESS('Created superuser: admin/admin'))
|
||||
# 1. Ensure Games and Cards exist
|
||||
self.ensure_games_populated()
|
||||
|
||||
# Create Demo Users
|
||||
self.create_demo_users()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Database populated successfully!'))
|
||||
# 2. Get Game Objects
|
||||
try:
|
||||
mtg = Game.objects.get(slug='magic-the-gathering')
|
||||
pokemon = Game.objects.get(slug='pokemon-tcg')
|
||||
lorcana = Game.objects.get(slug='disney-lorcana')
|
||||
except Game.DoesNotExist as e:
|
||||
self.stdout.write(self.style.ERROR(f"Missing a required game after population attempts! {e}"))
|
||||
return
|
||||
|
||||
def populate_mtg(self, game):
|
||||
self.stdout.write('Fetching MTG data from Scryfall...')
|
||||
# Get a few sets
|
||||
# 3. Create Stores
|
||||
self.create_stores_and_listings(mtg, pokemon, lorcana)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Database population complete!'))
|
||||
|
||||
def ensure_games_populated(self):
|
||||
# MTG - Native (kept simple from original) or call a script if we had one.
|
||||
# Since the original had MTG logic, I'll keep a simplified version here or call it if I haven't deleted it.
|
||||
# But wait, I'm replacing the whole file. I should probably keep the MTG fetcher or rely on what's there?
|
||||
# The prompt implies I should just "overhaul" it. I'll re-implement a robust MTG fetcher or reuse the old one's logic if I want
|
||||
# but to be safe and clean, I'll just check if cards exist, if not, fetch some.
|
||||
|
||||
# Check MTG
|
||||
if not Game.objects.filter(slug='magic-the-gathering').exists() or not Card.objects.filter(set__game__slug='magic-the-gathering').exists():
|
||||
self.stdout.write("Populating MTG Cards...")
|
||||
self.populate_mtg()
|
||||
else:
|
||||
self.stdout.write("MTG cards found, skipping population.")
|
||||
|
||||
# Check Pokemon
|
||||
if not Game.objects.filter(slug='pokemon-tcg').exists() or not Card.objects.filter(set__game__slug='pokemon-tcg').exists():
|
||||
self.stdout.write("Populating Pokemon Cards via script...")
|
||||
call_command('populate_pokemon_cards')
|
||||
else:
|
||||
self.stdout.write("Pokemon cards found, skipping population.")
|
||||
|
||||
# Check Lorcana
|
||||
if not Game.objects.filter(slug='disney-lorcana').exists() or not Card.objects.filter(set__game__slug='disney-lorcana').exists():
|
||||
self.stdout.write("Populating Lorcana Cards via script...")
|
||||
call_command('populate_lorcana_cards')
|
||||
else:
|
||||
self.stdout.write("Lorcana cards found, skipping population.")
|
||||
|
||||
def populate_mtg(self):
|
||||
# Simplified reused logic from original
|
||||
game, _ = Game.objects.get_or_create(
|
||||
slug='magic-the-gathering',
|
||||
defaults={'name': 'Magic: The Gathering'}
|
||||
)
|
||||
sets_api = "https://api.scryfall.com/sets"
|
||||
try:
|
||||
resp = requests.get(sets_api).json()
|
||||
# Pick top 3 recent expansion sets
|
||||
target_sets = [s for s in resp['data'] if s['set_type'] == 'expansion'][:3]
|
||||
|
||||
for s_data in target_sets:
|
||||
set_obj, created = Set.objects.get_or_create(
|
||||
set_obj, _ = Set.objects.get_or_create(
|
||||
game=game,
|
||||
name=s_data['name'],
|
||||
code=s_data['code'],
|
||||
defaults={'release_date': s_data.get('released_at')}
|
||||
)
|
||||
|
||||
if created:
|
||||
self.stdout.write(f"Created set: {set_obj.name}")
|
||||
# Fetch cards for this set
|
||||
cards_url = s_data['search_uri']
|
||||
cards_resp = requests.get(cards_url).json()
|
||||
cards_url = s_data['search_uri']
|
||||
cards_resp = requests.get(cards_url).json()
|
||||
for card_data in cards_resp.get('data', [])[:50]:
|
||||
image = None
|
||||
if 'image_uris' in card_data:
|
||||
image = card_data['image_uris'].get('normal')
|
||||
elif 'card_faces' in card_data and 'image_uris' in card_data['card_faces'][0]:
|
||||
image = card_data['card_faces'][0]['image_uris'].get('normal')
|
||||
|
||||
for card_data in cards_resp.get('data', [])[:20]: # Limit to 20 cards per set to be fast
|
||||
if 'image_uris' in card_data:
|
||||
image = card_data['image_uris'].get('normal')
|
||||
elif 'card_faces' in card_data and 'image_uris' in card_data['card_faces'][0]:
|
||||
image = card_data['card_faces'][0]['image_uris'].get('normal')
|
||||
else:
|
||||
continue
|
||||
if not image: continue
|
||||
|
||||
card, _ = Card.objects.get_or_create(
|
||||
set=set_obj,
|
||||
name=card_data['name'],
|
||||
collector_number=card_data['collector_number'],
|
||||
defaults={
|
||||
'rarity': card_data['rarity'].capitalize(),
|
||||
'image_url': image,
|
||||
'scryfall_id': card_data['id'],
|
||||
'tcgplayer_id': card_data.get('tcgplayer_id'),
|
||||
}
|
||||
)
|
||||
|
||||
# Create Listings
|
||||
self.create_listings_for_card(card)
|
||||
|
||||
Card.objects.get_or_create(
|
||||
set=set_obj,
|
||||
name=card_data['name'],
|
||||
collector_number=card_data['collector_number'],
|
||||
defaults={
|
||||
'rarity': card_data['rarity'].capitalize(),
|
||||
'image_url': image,
|
||||
'scryfall_id': card_data['id'],
|
||||
'tcgplayer_id': card_data.get('tcgplayer_id'),
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.ERROR(f"Failed to fetch MTG data: {e}"))
|
||||
self.stdout.write(self.style.ERROR(f"Error populating MTG: {e}"))
|
||||
|
||||
def populate_fake_game(self, game, prefix):
|
||||
self.stdout.write(f'Generating data for {game.name}...')
|
||||
for i in range(3): # 3 Sets
|
||||
set_name = f"{prefix} Set {i+1}"
|
||||
set_obj, _ = Set.objects.get_or_create(
|
||||
game=game,
|
||||
name=set_name,
|
||||
code=f"{prefix[:3].upper()}{i+1}",
|
||||
defaults={'release_date': fake.date_between(start_date='-2y', end_date='today')}
|
||||
def create_stores_and_listings(self, mtg, pokemon, lorcana):
|
||||
|
||||
# Define Store Configs
|
||||
stores_config = [
|
||||
# Store 1: 100 Magic the gathering single card listings
|
||||
{
|
||||
'name': 'Mystic Magic',
|
||||
'slug': 'mystic-magic',
|
||||
'listings': [(mtg, 100)]
|
||||
},
|
||||
# Store 2: 80 lorcana single card listings
|
||||
{
|
||||
'name': 'Inkborn Illumineers',
|
||||
'slug': 'inkborn-illumineers',
|
||||
'listings': [(lorcana, 80)]
|
||||
},
|
||||
# Store 3: 200 pokemon single card listings
|
||||
{
|
||||
'name': 'Poke Mart',
|
||||
'slug': 'poke-mart',
|
||||
'listings': [(pokemon, 200)]
|
||||
},
|
||||
# Store 4: 50 Magic the gathering and lorcana listings (Split 25/25)
|
||||
{
|
||||
'name': 'Wizards and Wanderers',
|
||||
'slug': 'wizards-and-wanderers',
|
||||
'listings': [(mtg, 25), (lorcana, 25)]
|
||||
},
|
||||
# Store 5: 40 Magic the gathering and 20 pokemon listings
|
||||
{
|
||||
'name': 'Mana & Mons',
|
||||
'slug': 'mana-and-mons',
|
||||
'listings': [(mtg, 40), (pokemon, 20)]
|
||||
},
|
||||
# Store 6: 100 lorcana and 10 pokemon listings
|
||||
{
|
||||
'name': 'Disney Duelists',
|
||||
'slug': 'disney-duelists',
|
||||
'listings': [(lorcana, 100), (pokemon, 10)]
|
||||
},
|
||||
# Store 7: 100 cards for all three games (Split ~33 each)
|
||||
{
|
||||
'name': 'The Collector Trove',
|
||||
'slug': 'collector-trove',
|
||||
'listings': [(mtg, 33), (lorcana, 33), (pokemon, 34)]
|
||||
}
|
||||
]
|
||||
|
||||
for config in stores_config:
|
||||
self.stdout.write(f"Setting up store: {config['name']}...")
|
||||
|
||||
# Create User and Seller
|
||||
username = config['slug'].replace('-', '')
|
||||
user, created = User.objects.get_or_create(username=username, defaults={'email': f"{username}@example.com"})
|
||||
if created:
|
||||
user.set_password('password')
|
||||
user.save()
|
||||
|
||||
seller, _ = Seller.objects.get_or_create(
|
||||
user=user,
|
||||
defaults={
|
||||
'store_name': config['name'],
|
||||
'slug': config['slug'],
|
||||
'description': f"Welcome to {config['name']}!",
|
||||
'contact_email': f"{username}@example.com"
|
||||
}
|
||||
)
|
||||
|
||||
for j in range(15): # 15 Cards per set
|
||||
card, _ = Card.objects.get_or_create(
|
||||
set=set_obj,
|
||||
name=f"{prefix} Monster {fake.word().capitalize()}",
|
||||
defaults={
|
||||
'rarity': random.choice(['Common', 'Uncommon', 'Rare', 'Ultra Rare']),
|
||||
'image_url': f"https://placehold.co/400x600?text={prefix}+{j}",
|
||||
'collector_number': str(j+1)
|
||||
}
|
||||
)
|
||||
self.create_listings_for_card(card)
|
||||
# Create Listings
|
||||
for game, count in config['listings']:
|
||||
self.create_listings_for_store(seller, game, count)
|
||||
|
||||
def create_listings_for_card(self, card):
|
||||
# Create 1-5 listings per card with different conditions
|
||||
for _ in range(random.randint(1, 4)):
|
||||
def create_listings_for_store(self, seller, game, count):
|
||||
cards = list(Card.objects.filter(set__game=game))
|
||||
if not cards:
|
||||
self.stdout.write(self.style.WARNING(f"No cards found for {game.name}, cannot create listings."))
|
||||
return
|
||||
|
||||
listing_count = 0
|
||||
while listing_count < count:
|
||||
card = random.choice(cards)
|
||||
|
||||
# Randomize attributes
|
||||
price = round(random.uniform(1.00, 150.00), 2)
|
||||
quantity = random.randint(1, 10)
|
||||
condition = random.choice(['NM', 'LP', 'MP', 'HP'])
|
||||
price = round(random.uniform(0.50, 100.00), 2)
|
||||
|
||||
CardListing.objects.create(
|
||||
card=card,
|
||||
condition=condition,
|
||||
seller=seller,
|
||||
price=price,
|
||||
quantity=random.randint(1, 20),
|
||||
market_price=price, # Simplified
|
||||
is_foil=random.choice([True, False])
|
||||
quantity=quantity,
|
||||
condition=condition,
|
||||
status='listed'
|
||||
)
|
||||
|
||||
def create_demo_users(self):
|
||||
# Create a Pro user
|
||||
if not User.objects.filter(username='prouser').exists():
|
||||
u = User.objects.create_user('prouser', 'pro@example.com', 'password')
|
||||
u.profile.is_pro = True
|
||||
u.profile.save()
|
||||
self.stdout.write("Created prouser/password")
|
||||
listing_count += 1
|
||||
|
||||
# Create a Basic user
|
||||
if not User.objects.filter(username='basicuser').exists():
|
||||
User.objects.create_user('basicuser', 'basic@example.com', 'password')
|
||||
self.stdout.write("Created basicuser/password")
|
||||
self.stdout.write(f" - Created {count} listings for {game.name}")
|
||||
|
||||
Reference in New Issue
Block a user