Move profile and settings into the new unified dashboard, showing user info in one place

This commit is contained in:
badblocks 2025-03-31 22:20:59 -07:00
parent 2d826734a0
commit 7edefe23c3
37 changed files with 726 additions and 500 deletions

View file

@ -3,6 +3,12 @@ from django.dispatch import receiver
from django.db.models import F
from trades.models import TradeOfferHaveCard, TradeOfferWantCard, TradeAcceptance
from django.db import transaction
from accounts.models import CustomUser
from datetime import timedelta
from django.utils import timezone
import uuid
import hashlib
from django.conf import settings
ACTIVE_STATES = [
TradeAcceptance.AcceptanceState.ACCEPTED,
@ -80,4 +86,170 @@ def trade_acceptance_post_delete(sender, instance, **kwargs):
trade_offer = instance.trade_offer
adjust_qty_for_trade_offer(trade_offer, instance.requested_card, side='have', delta=delta)
adjust_qty_for_trade_offer(trade_offer, instance.offered_card, side='want', delta=delta)
update_trade_offer_closed_status(trade_offer)
update_trade_offer_closed_status(trade_offer)
@receiver(post_save, sender=TradeAcceptance)
def trade_acceptance_email_notification(sender, instance, created, **kwargs):
# Only proceed if the update was triggered by an acting user.
if not hasattr(instance, "_actioning_user"):
print("No actioning user")
return
# check if were in debug mode
if settings.DEBUG:
print("DEBUG: skipping email notification in debug mode")
return
acting_user = instance._actioning_user
state = instance.state
if state == TradeAcceptance.AcceptanceState.ACCEPTED:
state = "accepted"
elif state == TradeAcceptance.AcceptanceState.SENT:
state = "sent"
elif state == TradeAcceptance.AcceptanceState.RECEIVED:
state = "received"
elif state == TradeAcceptance.AcceptanceState.THANKED_BY_INITIATOR:
state = "thanked_by_initiator"
elif state == TradeAcceptance.AcceptanceState.THANKED_BY_ACCEPTOR:
state = "thanked_by_acceptor"
elif state == TradeAcceptance.AcceptanceState.THANKED_BY_BOTH:
state = "thanked_by_both"
elif state == TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR:
state = "rejected_by_initiator"
elif state == TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR:
state = "rejected_by_acceptor"
else:
return
print("state", state)
print("acting_user", acting_user)
# Determine the non-acting party:
if instance.trade_offer.initiated_by == acting_user:
# The initiator made the change; notify the acceptor.
recipient_user = instance.accepted_by.user
email_template = "trades/email/trade_update_" + state + ".txt"
email_subject = "[PKMN Trade Club] Trade Update"
else:
# The acceptor made the change; notify the initiator.
recipient_user = instance.trade_offer.initiated_by.user
email_template = "trades/email/trade_update_" + state + ".txt"
email_subject = "[PKMN Trade Club] Trade Update"
is_initiator = instance.trade_offer.initiated_by == acting_user
from django.template.loader import render_to_string
email_context = {
"has_card": instance.requested_card,
"want_card": instance.offered_card,
"hash": instance.hash,
"acting_user": acting_user.username,
"recipient_user": recipient_user.username,
"acting_user_friend_code": instance.trade_offer.initiated_by.friend_code if is_initiator else instance.accepted_by.friend_code,
"is_initiator": is_initiator,
"pk": instance.pk,
}
print("email_context", email_context)
email_body = render_to_string(email_template, email_context)
from django.core.mail import send_mail
send_mail(
email_subject,
email_body,
None, # Django will use DEFAULT_FROM_EMAIL from settings
[recipient_user.email],
)
# Clean up the temporary attribute.
del instance._actioning_user
@receiver(post_save, sender=TradeAcceptance)
def trade_acceptance_reputation_update(sender, instance, created, **kwargs):
"""
Update the denormalized reputation score on the user model based on
state transitions for TradeAcceptance.
- THANKED_BY_BOTH: both the initiator and the acceptor receive +1 when transitioning
into this state, and -1 when leaving it.
- REJECTED_BY_INITIATOR: only the acceptor gets -1 when transitioning into it (and +1 when leaving it).
- REJECTED_BY_ACCEPTOR: only the initiator gets -1 when transitioning into it (and +1 when leaving it).
Creation events are ignored because trade acceptances are never created with a terminal state.
"""
if created:
return # No action on creation as terminal states are not expected.
thanks_delta = 0
rejection_delta_initiator = 0 # Delta for the initiator's reputation
rejection_delta_acceptor = 0 # Delta for the acceptor's reputation
old_state = getattr(instance, '_old_state', None)
if old_state is None:
return
# Handle THANKED_BY_BOTH transitions
if old_state != TradeAcceptance.AcceptanceState.THANKED_BY_BOTH and instance.state == TradeAcceptance.AcceptanceState.THANKED_BY_BOTH:
thanks_delta = 1
elif old_state == TradeAcceptance.AcceptanceState.THANKED_BY_BOTH and instance.state != TradeAcceptance.AcceptanceState.THANKED_BY_BOTH:
thanks_delta = -1
# Handle REJECTED_BY_INITIATOR transitions (affects the acceptor)
if old_state != TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR and instance.state == TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR:
rejection_delta_acceptor = -1
elif old_state == TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR and instance.state != TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR:
rejection_delta_acceptor = 1
# Handle REJECTED_BY_ACCEPTOR transitions (affects the initiator)
if old_state != TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR and instance.state == TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR:
rejection_delta_initiator = -1
elif old_state == TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR and instance.state != TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR:
rejection_delta_initiator = 1
# Apply reputation updates:
# For THANKED_BY_BOTH, update both users.
if thanks_delta:
CustomUser.objects.filter(pk=instance.trade_offer.initiated_by.user.pk).update(
reputation_score=F("reputation_score") + thanks_delta
)
CustomUser.objects.filter(pk=instance.accepted_by.user.pk).update(
reputation_score=F("reputation_score") + thanks_delta
)
# For REJECTED_BY_INITIATOR, update only the acceptor.
if rejection_delta_acceptor:
CustomUser.objects.filter(pk=instance.accepted_by.user.pk).update(
reputation_score=F("reputation_score") + rejection_delta_acceptor
)
# For REJECTED_BY_ACCEPTOR, update only the initiator.
if rejection_delta_initiator:
CustomUser.objects.filter(pk=instance.trade_offer.initiated_by.user.pk).update(
reputation_score=F("reputation_score") + rejection_delta_initiator
)
@receiver(post_delete, sender=TradeAcceptance)
def trade_acceptance_reputation_delete(sender, instance, **kwargs):
"""
When a TradeAcceptance is deleted, adjust the reputation score for the
affected user(s) by reversing any reputation changes previously applied.
- If the deleted instance was in THANKED_BY_BOTH: subtract 1 from both parties.
- If it was in REJECTED_BY_INITIATOR: add 1 to the acceptor.
- If it was in REJECTED_BY_ACCEPTOR: add 1 to the initiator.
"""
if instance.state == TradeAcceptance.AcceptanceState.THANKED_BY_BOTH:
CustomUser.objects.filter(pk=instance.trade_offer.initiated_by.user.pk).update(
reputation_score=F("reputation_score") - 1
)
CustomUser.objects.filter(pk=instance.accepted_by.user.pk).update(
reputation_score=F("reputation_score") - 1
)
if instance.state == TradeAcceptance.AcceptanceState.REJECTED_BY_INITIATOR:
CustomUser.objects.filter(pk=instance.accepted_by.user.pk).update(
reputation_score=F("reputation_score") + 1
)
if instance.state == TradeAcceptance.AcceptanceState.REJECTED_BY_ACCEPTOR:
CustomUser.objects.filter(pk=instance.trade_offer.initiated_by.user.pk).update(
reputation_score=F("reputation_score") + 1
)