progress on conversion to tailwind
This commit is contained in:
parent
6a872124c6
commit
6e2843c60e
110 changed files with 4997 additions and 1691 deletions
247
trades/forms.py
247
trades/forms.py
|
|
@ -1,61 +1,15 @@
|
|||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from .models import TradeOffer
|
||||
from friend_codes.models import FriendCode
|
||||
from .models import TradeOffer, TradeAcceptance
|
||||
from accounts.models import FriendCode
|
||||
from cards.models import Card
|
||||
from django.forms import ModelForm
|
||||
from trades.models import TradeOfferHaveCard, TradeOfferWantCard
|
||||
|
||||
class TradeOfferUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = TradeOffer
|
||||
# We now only edit the `state` field
|
||||
fields = ["state"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Expects additional keyword arguments:
|
||||
- friend_codes: a list of friend code objects for the current user.
|
||||
This initializer filters the available state choices based on:
|
||||
- The current state's allowed transition.
|
||||
- Which party (initiated_by or accepted_by) is acting.
|
||||
"""
|
||||
friend_codes = kwargs.pop("friend_codes")
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
instance = self.instance
|
||||
allowed_state = None
|
||||
|
||||
# Define permitted transitions based on the current state and user role:
|
||||
if instance.state == TradeOffer.State.INITIATED:
|
||||
# Allow the accepted_by party to accept the trade.
|
||||
if instance.accepted_by in friend_codes:
|
||||
allowed_state = TradeOffer.State.ACCEPTED
|
||||
elif instance.state == TradeOffer.State.ACCEPTED:
|
||||
# Allow the initiated_by party to mark the trade as sent.
|
||||
if instance.initiated_by in friend_codes:
|
||||
allowed_state = TradeOffer.State.SENT
|
||||
elif instance.state == TradeOffer.State.SENT:
|
||||
# Allow the accepted_by party to mark the trade as received.
|
||||
if instance.accepted_by in friend_codes:
|
||||
allowed_state = TradeOffer.State.RECEIVED
|
||||
|
||||
if allowed_state:
|
||||
# Limit the `state` field's choices to only the permitted transition.
|
||||
label = dict(TradeOffer.State.choices)[allowed_state]
|
||||
self.fields["state"].choices = [(allowed_state, label)]
|
||||
else:
|
||||
# If no valid transition is available for this user, remove the field.
|
||||
self.fields.pop("state")
|
||||
|
||||
def clean_have_cards(self):
|
||||
have_cards = self.cleaned_data.get("have_cards")
|
||||
if have_cards:
|
||||
for card in have_cards.all():
|
||||
if card.rarity not in ALLOWED_RARITIES:
|
||||
# Raising a ValidationError here will cause this error message to be shown beneath the 'have_cards' field.
|
||||
raise ValidationError(
|
||||
f"The card '{card}' has an invalid rarity: {card.rarity}. Allowed rarities are: {', '.join(ALLOWED_RARITIES)}."
|
||||
)
|
||||
return have_cards
|
||||
class NoValidationMultipleChoiceField(forms.MultipleChoiceField):
|
||||
def validate(self, value):
|
||||
# Override the validation to skip checking against defined choices
|
||||
pass
|
||||
|
||||
class TradeOfferAcceptForm(forms.Form):
|
||||
friend_code = forms.ModelChoiceField(
|
||||
|
|
@ -68,3 +22,188 @@ class TradeOfferAcceptForm(forms.Form):
|
|||
friend_codes = kwargs.pop("friend_codes")
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["friend_code"].queryset = friend_codes
|
||||
|
||||
class TradeAcceptanceCreateForm(forms.ModelForm):
|
||||
"""
|
||||
Form for creating a TradeAcceptance.
|
||||
Expects the caller to pass:
|
||||
- trade_offer: the instance of TradeOffer this acceptance is for.
|
||||
- friend_codes: a queryset of FriendCode objects for the current user.
|
||||
It filters available requested and offered cards based on what's still available.
|
||||
"""
|
||||
class Meta:
|
||||
model = TradeAcceptance
|
||||
fields = ["accepted_by", "requested_card", "offered_card"]
|
||||
|
||||
def __init__(self, *args, trade_offer=None, friend_codes=None, **kwargs):
|
||||
if trade_offer is None:
|
||||
raise ValueError("trade_offer must be provided to filter choices.")
|
||||
super().__init__(*args, **kwargs)
|
||||
self.trade_offer = trade_offer
|
||||
|
||||
# Filter accepted_by to those friend codes that belong to the current user.
|
||||
if friend_codes is None:
|
||||
raise ValueError("friend_codes must be provided")
|
||||
self.fields["accepted_by"].queryset = friend_codes
|
||||
|
||||
active_states = [
|
||||
TradeAcceptance.AcceptanceState.ACCEPTED,
|
||||
TradeAcceptance.AcceptanceState.SENT,
|
||||
TradeAcceptance.AcceptanceState.RECEIVED,
|
||||
TradeAcceptance.AcceptanceState.COMPLETED,
|
||||
]
|
||||
|
||||
# Build available requested_card choices from the TradeOffer's "have" side.
|
||||
available_requested_ids = []
|
||||
for through_obj in trade_offer.trade_offer_have_cards.all():
|
||||
active_count = trade_offer.acceptances.filter(
|
||||
requested_card=through_obj.card,
|
||||
state__in=active_states
|
||||
).count()
|
||||
if active_count < through_obj.quantity:
|
||||
available_requested_ids.append(through_obj.card.id)
|
||||
self.fields["requested_card"].queryset = Card.objects.filter(id__in=available_requested_ids)
|
||||
|
||||
# Similarly, build available offered_card choices from the TradeOffer's "want" side.
|
||||
available_offered_ids = []
|
||||
for through_obj in trade_offer.trade_offer_want_cards.all():
|
||||
active_count = trade_offer.acceptances.filter(
|
||||
offered_card=through_obj.card,
|
||||
state__in=active_states
|
||||
).count()
|
||||
if active_count < through_obj.quantity:
|
||||
available_offered_ids.append(through_obj.card.id)
|
||||
self.fields["offered_card"].queryset = Card.objects.filter(id__in=available_offered_ids)
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Ensure the instance has its trade_offer set before model-level validation occurs.
|
||||
This prevents errors when the model clean() method attempts to access trade_offer.
|
||||
"""
|
||||
self.instance.trade_offer = self.trade_offer
|
||||
return super().clean()
|
||||
|
||||
class TradeAcceptanceUpdateForm(forms.ModelForm):
|
||||
"""
|
||||
Form for updating the state of an existing TradeAcceptance.
|
||||
Based on the current state and which party is acting (initiator vs. acceptor),
|
||||
this form limits available state transitions.
|
||||
"""
|
||||
class Meta:
|
||||
model = TradeAcceptance
|
||||
fields = ["state"]
|
||||
|
||||
def __init__(self, *args, friend_codes=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
instance = self.instance
|
||||
allowed_choices = []
|
||||
|
||||
# Allowed transitions for a TradeAcceptance:
|
||||
# - From ACCEPTED:
|
||||
# • If the initiator is acting, allow SENT and REJECTED_BY_INITIATOR.
|
||||
# • If the acceptor is acting, allow REJECTED_BY_ACCEPTOR.
|
||||
# - From SENT:
|
||||
# • If the acceptor is acting, allow RECEIVED and REJECTED_BY_ACCEPTOR.
|
||||
# • If the initiator is acting, allow REJECTED_BY_INITIATOR.
|
||||
# - From RECEIVED:
|
||||
# • If the initiator is acting, allow COMPLETED and REJECTED_BY_INITIATOR.
|
||||
# • If the acceptor is acting, allow REJECTED_BY_ACCEPTOR.
|
||||
if friend_codes is None:
|
||||
raise ValueError("friend_codes must be provided")
|
||||
if instance.state == TradeAcceptance.AcceptanceState.ACCEPTED:
|
||||
if instance.trade_offer.initiated_by in friend_codes:
|
||||
allowed_choices = [
|
||||
(TradeAcceptance.AcceptanceState.SENT, "Sent"),
|
||||
(TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, "Rejected by Initiator"),
|
||||
]
|
||||
elif instance.accepted_by in friend_codes:
|
||||
allowed_choices = [
|
||||
(TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, "Rejected by Acceptor"),
|
||||
]
|
||||
elif instance.state == TradeAcceptance.AcceptanceState.SENT:
|
||||
if instance.accepted_by in friend_codes:
|
||||
allowed_choices = [
|
||||
(TradeAcceptance.AcceptanceState.RECEIVED, "Received"),
|
||||
(TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, "Rejected by Acceptor"),
|
||||
]
|
||||
elif instance.trade_offer.initiated_by in friend_codes:
|
||||
allowed_choices = [
|
||||
(TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, "Rejected by Initiator"),
|
||||
]
|
||||
elif instance.state == TradeAcceptance.AcceptanceState.RECEIVED:
|
||||
if instance.trade_offer.initiated_by in friend_codes:
|
||||
allowed_choices = [
|
||||
(TradeAcceptance.AcceptanceState.COMPLETED, "Completed"),
|
||||
(TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR, "Rejected by Initiator"),
|
||||
]
|
||||
elif instance.accepted_by in friend_codes:
|
||||
allowed_choices = [
|
||||
(TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR, "Rejected by Acceptor"),
|
||||
]
|
||||
if allowed_choices:
|
||||
self.fields["state"].choices = allowed_choices
|
||||
else:
|
||||
self.fields.pop("state")
|
||||
|
||||
class TradeOfferCreateForm(ModelForm):
|
||||
# Override the default fields to capture quantity info in the format 'card_id:quantity'
|
||||
have_cards = NoValidationMultipleChoiceField(widget=forms.SelectMultiple, required=True)
|
||||
want_cards = NoValidationMultipleChoiceField(widget=forms.SelectMultiple, required=True)
|
||||
|
||||
class Meta:
|
||||
model = TradeOffer
|
||||
fields = ["want_cards", "have_cards", "initiated_by"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Populate choices from Card model
|
||||
cards = Card.objects.order_by("name", "rarity__pk")
|
||||
choices = [(str(card.pk), card.name) for card in cards]
|
||||
self.fields["have_cards"].choices = choices
|
||||
self.fields["want_cards"].choices = choices
|
||||
|
||||
def clean_have_cards(self):
|
||||
data = self.data.getlist("have_cards")
|
||||
parsed = {}
|
||||
for item in data:
|
||||
parts = item.split(':')
|
||||
card_id = parts[0]
|
||||
try:
|
||||
quantity = int(parts[1]) if len(parts) > 1 else 1
|
||||
except ValueError:
|
||||
raise forms.ValidationError(f"Invalid quantity provided in {item}")
|
||||
parsed[card_id] = parsed.get(card_id, 0) + quantity
|
||||
return parsed
|
||||
|
||||
def clean_want_cards(self):
|
||||
data = self.data.getlist("want_cards")
|
||||
parsed = {}
|
||||
for item in data:
|
||||
parts = item.split(':')
|
||||
card_id = parts[0]
|
||||
try:
|
||||
quantity = int(parts[1]) if len(parts) > 1 else 1
|
||||
except ValueError:
|
||||
raise forms.ValidationError(f"Invalid quantity provided in {item}")
|
||||
parsed[card_id] = parsed.get(card_id, 0) + quantity
|
||||
return parsed
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit=False)
|
||||
if commit:
|
||||
instance.save()
|
||||
# Clear any existing through model entries in case of update
|
||||
TradeOfferHaveCard.objects.filter(trade_offer=instance).delete()
|
||||
TradeOfferWantCard.objects.filter(trade_offer=instance).delete()
|
||||
|
||||
# Create through entries for have_cards
|
||||
for card_id, quantity in self.cleaned_data["have_cards"].items():
|
||||
card = Card.objects.get(pk=card_id)
|
||||
TradeOfferHaveCard.objects.create(trade_offer=instance, card=card, quantity=quantity)
|
||||
|
||||
# Create through entries for want_cards
|
||||
for card_id, quantity in self.cleaned_data["want_cards"].items():
|
||||
card = Card.objects.get(pk=card_id)
|
||||
TradeOfferWantCard.objects.create(trade_offer=instance, card=card, quantity=quantity)
|
||||
|
||||
return instance
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue