134 lines
4.0 KiB
Python
134 lines
4.0 KiB
Python
import re
|
|
from .models import Card, CardListing, Order, OrderItem, VaultItem
|
|
from django.db.models import Min
|
|
|
|
def add_to_vault(user, card, quantity=1):
|
|
"""
|
|
Adds a card to the user's vault.
|
|
"""
|
|
vault_item, created = VaultItem.objects.get_or_create(user=user, card=card)
|
|
if not created:
|
|
vault_item.quantity += quantity
|
|
else:
|
|
vault_item.quantity = quantity
|
|
vault_item.save()
|
|
|
|
def parse_deck_list(deck_text):
|
|
"""
|
|
Parses a deck list string and returns a list of dictionaries with 'quantity' and 'name'.
|
|
Format expected: "4 Lightning Bolt" or "1x Lightning Bolt"
|
|
"""
|
|
lines = deck_text.strip().split('\n')
|
|
parsed_cards = []
|
|
|
|
for line in lines:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
|
|
match = re.match(r'^(\d+)[xX]?\s+(.+)$', line)
|
|
if match:
|
|
quantity = int(match.group(1))
|
|
name = match.group(2).strip()
|
|
parsed_cards.append({'quantity': quantity, 'name': name})
|
|
else:
|
|
# Maybe just name? assume 1
|
|
parsed_cards.append({'quantity': 1, 'name': line})
|
|
|
|
return parsed_cards
|
|
|
|
def find_best_listings_for_deck(parsed_cards):
|
|
"""
|
|
Finds cheapest listings for the parsed cards.
|
|
Returns:
|
|
- found_items: list of {listing, quantity_needed, total_cost, card_name}
|
|
- missing_items: list of {name, quantity}
|
|
"""
|
|
found_items = []
|
|
missing_items = []
|
|
|
|
for item in parsed_cards:
|
|
name = item['name']
|
|
qty_needed = item['quantity']
|
|
|
|
# Find card (simple name match)
|
|
cards = Card.objects.filter(name__iexact=name)
|
|
if not cards.exists():
|
|
# Try contains
|
|
cards = Card.objects.filter(name__icontains=name)
|
|
|
|
if not cards.exists():
|
|
missing_items.append(item)
|
|
continue
|
|
|
|
# Find cheapest listing with stock
|
|
# We try to fill the quantity from multiple listings if needed
|
|
listings = CardListing.objects.filter(
|
|
card__in=cards,
|
|
quantity__gt=0
|
|
).order_by('price')
|
|
|
|
qty_remaining = qty_needed
|
|
|
|
for listing in listings:
|
|
if qty_remaining <= 0:
|
|
break
|
|
|
|
qty_to_take = min(listing.quantity, qty_remaining)
|
|
|
|
found_items.append({
|
|
'listing': listing,
|
|
'quantity': qty_to_take,
|
|
'card_name': listing.card.name,
|
|
'price': listing.price,
|
|
'total': listing.price * qty_to_take
|
|
})
|
|
|
|
qty_remaining -= qty_to_take
|
|
|
|
if qty_remaining > 0:
|
|
missing_items.append({'name': name, 'quantity': qty_remaining})
|
|
|
|
return found_items, missing_items
|
|
|
|
def get_user_collection(user):
|
|
"""
|
|
Returns a dict {card_name: quantity} of cards in user's vault.
|
|
"""
|
|
owned = {}
|
|
if not user.is_authenticated:
|
|
return owned
|
|
|
|
vault_items = VaultItem.objects.filter(user=user).select_related('card')
|
|
for item in vault_items:
|
|
owned[item.card.name] = item.quantity
|
|
|
|
return owned
|
|
|
|
def filter_deck_by_collection(parsed_cards, owned_cards):
|
|
"""
|
|
Subtracts owned quantities from parsed_cards.
|
|
Returns new list of parsed_cards.
|
|
"""
|
|
filtered = []
|
|
for item in parsed_cards:
|
|
name = item['name']
|
|
needed = item['quantity']
|
|
# Simple name match
|
|
owned_qty = 0
|
|
# Try exact match first
|
|
if name in owned_cards:
|
|
owned_qty = owned_cards[name]
|
|
else:
|
|
# Try case insensitive fallback
|
|
for key in owned_cards:
|
|
if key.lower() == name.lower():
|
|
owned_qty = owned_cards[key]
|
|
break
|
|
|
|
remaining = needed - owned_qty
|
|
if remaining > 0:
|
|
filtered.append({'name': name, 'quantity': remaining})
|
|
|
|
return filtered
|