Skip to content

Commit

Permalink
fix #159
Browse files Browse the repository at this point in the history
  • Loading branch information
= committed Sep 16, 2024
1 parent 7f9fe60 commit 15e45c8
Show file tree
Hide file tree
Showing 27 changed files with 1,565 additions and 701 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ NGINX_CONF_LOCATION='/config/nginx/site-confs/default.conf' # leave unset for de
SECRET_KEY='secret'
DATABASE_PASSWORD='secret'
DATABASE_ROOT_PASSWORD="secret"
VAPID_PUBLIC_KEY="public-key"
VAPID_PRIVATE_KEY="private-key"
POST_OFFICE_EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend"
# if POST_OFFICE_EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend', set the following
EMAIL_USE_TLS='True'
Expand Down
58 changes: 52 additions & 6 deletions auctions/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Invoice,
Lot,
LotHistory,
User,
UserBan,
UserData,
UserInterestCategory,
Expand Down Expand Up @@ -191,12 +192,7 @@ def bid_on_lot(lot, user, amount):
auctiontos_winner = AuctionTOS.objects.filter(auction=lot.auction, user=user).first()
if auctiontos_winner:
lot.auctiontos_winner = auctiontos_winner
invoice, created = Invoice.objects.get_or_create(
auctiontos_user=lot.auctiontos_winner,
auction=lot.auction,
defaults={},
)
invoice.recalculate
lot.create_update_invoices
lot.winning_price = lot.buy_now_price
lot.buy_now_used = True
# this next line makes the lot end immediately after buy now is used
Expand Down Expand Up @@ -526,3 +522,53 @@ def error_message(self, event):
# Receive message from room group
def chat_message(self, event):
self.send(text_data=json.dumps(event))


class UserConsumer(WebsocketConsumer):
"""This is ready to use and corresponding code to connect added (commented out) to base.html
You can use userdata.send_websocket_message to message the user, like this:
result = {
"type": "toast",
"message": "Hello world!",
}
user.userdata.send_websocket_message(result)
It would make a good messaging system for some stuff like chat messages,
but at this time it does not seem like a good idea
"""

def connect(self):
try:
self.pk = self.scope["url_route"]["kwargs"]["user_pk"]
user_for = User.objects.filter(pk=self.pk).first()
self.user = self.scope["user"]
self.user_notification_channel = f"user_{self.pk}"
if not user_for or user_for != self.user:
self.close()
else:
self.accept()
# Add to the group after accepting the connection
async_to_sync(self.channel_layer.group_add)(self.user_notification_channel, self.channel_name)

# Send a message after accepting the connection
# async_to_sync(self.channel_layer.group_send)(
# self.user_notification_channel,
# {"type": "toast", "message": 'Welcome!', 'bg': 'success'},
# )
except Exception as e:
print(e)
self.close()

def disconnect(self, close_code):
# Leave room group
async_to_sync(self.channel_layer.group_discard)(self.user_notification_channel, self.channel_name)
print("disconnected")

# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data)
print(text_data_json)

def toast(self, event):
message = event["message"]
bg = event.get("bg", "info")
self.send(text_data=json.dumps({"type": "toast", "message": message, "bg": bg}))
16 changes: 14 additions & 2 deletions auctions/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,7 @@ class Meta:
"date_online_bidding_starts",
"allow_deleting_bids",
"auto_add_images",
"message_users_when_lots_sell",
]
widgets = {
"date_start": DateTimePickerInput(),
Expand Down Expand Up @@ -1581,6 +1582,8 @@ def __init__(self, *args, **kwargs):
].help_text = "This should be 1-24 hours before the end of your auction"
self.fields["allow_bidding_on_lots"].help_text = "Leave this checked or people won't be able to bid!"
self.fields["allow_bidding_on_lots"].widget = forms.HiddenInput()
self.fields["message_users_when_lots_sell"].widget = forms.HiddenInput()
self.fields["advanced_lot_adding"].widget = forms.HiddenInput()
# self.fields['pre_register_lot_entry_fee_discount'].widget=forms.HiddenInput()
self.fields["pre_register_lot_discount_percent"].widget = forms.HiddenInput()
# self.fields['set_lot_winners_url'].widget=forms.HiddenInput()
Expand Down Expand Up @@ -1777,17 +1780,21 @@ def __init__(self, *args, **kwargs):
"buy_now",
css_class="col-md-3",
),
# Div('set_lot_winners_url', css_class='col-md-3',),
Div(
"promote_this_auction",
"message_users_when_lots_sell",
css_class="col-md-3",
),
# Div('set_lot_winners_url', css_class='col-md-3',),
PrependedAppendedText(
"tax",
"",
"%",
wrapper_class="col-md-3",
),
Div(
"promote_this_auction",
css_class="col-md-3",
),
css_class="row",
),
Submit("submit", "Save", css_class="create-update-auction btn-success"),
Expand Down Expand Up @@ -2473,6 +2480,7 @@ class Meta:
"username_visible",
"share_lot_images",
"auto_add_images",
"push_notifications_when_lots_sell",
)

def __init__(self, user, *args, **kwargs):
Expand Down Expand Up @@ -2510,6 +2518,10 @@ def __init__(self, user, *args, **kwargs):
# Div('use_list_view',css_class='col-md-4',),
# Div('use_dark_theme',css_class='col-md-4',),
# Div('show_ads',css_class='col-md-3',),
Div(
"push_notifications_when_lots_sell",
css_class="col-md-6",
),
css_class="row",
),
HTML("<h4>Notifications</h4><br>"),
Expand Down
122 changes: 12 additions & 110 deletions auctions/management/commands/endauctions.py
Original file line number Diff line number Diff line change
@@ -1,57 +1,21 @@
import datetime

import channels.layers
from asgiref.sync import async_to_sync
from django.contrib.sites.models import Site
from django.core.management.base import BaseCommand
from django.db.models import F
from django.utils import timezone
from easy_thumbnails.files import get_thumbnailer
from post_office import mail

from auctions.models import AuctionTOS, Invoice, Lot, LotHistory, LotImage


def sendWarning(lot_number, result):
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)(f"lot_{lot_number}", result)


def fix_seller_info(part_of_auction):
"""This function is really not needed - it's been run once to update all existing lots.
Fill out the user if auctiontos_seller is set, or auctiontos_seller if the user is set."""
# first, let's fill out the seller info (1 of 2)
needs_auctiontos_seller_filled_out = part_of_auction.filter(
auctiontos_seller__isnull=True, auction__auctiontos__user=F("user")
)
for lot in needs_auctiontos_seller_filled_out:
auctionTOS = AuctionTOS.objects.filter(user=lot.user, auction=lot.auction).first()
lot.auctiontos_seller = auctionTOS
lot.save() # presave receiver will make an invoice for this lot/seller

# next, let's fill out the user info (2 of 2)
# user when auctiontos filled out - handy for when users are part of an auction (auctiontos__user__isnull=True), but then later sign up
# User is set for auctiontos on user sign in event - see models.user_logged_in_callback()
needs_user_filled_out = part_of_auction.filter(
auctiontos_seller__isnull=False,
user__isnull=True,
auctiontos_seller__user__isnull=False,
auction__auctiontos=F("auctiontos_seller"),
)
for lot in needs_user_filled_out:
user = lot.auctiontos_seller.user
print("setting user based on auction tos:")
print(lot, lot.auctiontos_seller, user)
lot.user = user
lot.save()
from auctions.models import Lot, LotHistory, LotImage


def declare_winners_on_lots(lots):
"""Set the winner and winning price on all lots"""
for lot in lots:
if lot.ended:
# note - lots that are part of in-person auctions will only get here if a winner and price is set
# they always have lot.ended = False
# note - lots that are part of in-person auctions will not get here
# if they are active, they always have lot.ended = False, and if they are sold,
# the method that sells them should set active=False, so they won't be filtered here
# But, see https://github.com/iragm/fishauctions/issues/116
try:
lot.active = False
Expand All @@ -60,21 +24,18 @@ def declare_winners_on_lots(lots):
# lots will be bought via buy now will get here
# as well as in-person auction lots whose winner has been set manually
# We still need to make those active=False, done above, and then save, below
# I don't think the above is true anymore, lots bought with buy now should also be made inactive
pass
else:
info = None
if (
lot.high_bidder
): # and not lot.sold: # not sure what that was here, we already filter this in the if above
lot.winner = lot.high_bidder
lot.winning_price = lot.high_bid
if lot.high_bidder:
lot.sell_to_online_high_bidder
info = "LOT_END_WINNER"
bidder = lot.high_bidder
high_bidder_pk = lot.high_bidder.pk
high_bidder_name = str(lot.high_bidder_display)
current_high_bid = lot.high_bid
message = f"Won by {lot.high_bidder_display}"
lot.save()
# at this point, the lot should have a winner filled out if it's sold. If it still doesn't:
if not lot.sold:
high_bidder_pk = None
Expand All @@ -92,38 +53,15 @@ def declare_winners_on_lots(lots):
"current_high_bid": current_high_bid,
}
if info:
sendWarning(lot.lot_number, result)
lot.send_websocket_message(result)
LotHistory.objects.create(
lot=lot,
user=bidder,
message=message,
changed_price=True,
current_price=lot.high_bid,
)
# if this is part of an auction, update invoices
if lot.sold and lot.auction:
if lot.auctiontos_winner:
auctiontos_winner = lot.auctiontos_winner
else:
# look for the TOS and create the invoice
auctiontos_winner = AuctionTOS.objects.filter(auction=lot.auction, user=lot.high_bidder).first()
if auctiontos_winner:
lot.auctiontos_winner = auctiontos_winner
lot.save()
if lot.auctiontos_winner:
invoice, created = Invoice.objects.get_or_create(
auctiontos_user=lot.auctiontos_winner,
auction=lot.auction,
defaults={},
)
invoice.recalculate
if lot.auctiontos_seller:
invoice, created = Invoice.objects.get_or_create(
auctiontos_user=lot.auctiontos_seller,
auction=lot.auction,
defaults={},
)
invoice.recalculate
lot.create_update_invoices
# logic to email winner and buyer for lots not in an auction
if lot.winner and not lot.auction:
current_site = Site.objects.get_current()
Expand Down Expand Up @@ -207,48 +145,12 @@ def declare_winners_on_lots(lots):
"pk": -1,
"username": "System",
}
sendWarning(lot.lot_number, result)


def fix_winner_info(part_of_auction):
"""set winner if auctiontos_winner is set, and auctiontos_winner if winner is set. It's a good idea to run this AFTER declaring winners"""
# set auctiontos_winner if only winner is set
needs_auctiontos_winner_filled_out = part_of_auction.filter(
winner_isnull=False,
auctiontos_winner__isnull=True,
auction__auctiontos__user=F("winner"),
)
for lot in needs_auctiontos_winner_filled_out:
auctionTOS = AuctionTOS.objects.filter(user=lot.winner, auction=lot.auction).first()
lot.auctiontos_winner = auctionTOS
lot.save()
invoice, created = Invoice.objects.get_or_create(
auctiontos_user=lot.auctiontos_winner, auction=lot.auction, defaults={}
)

# declate winners where there is already an auctiontos_winner (hopefully rare - but see models.user_logged_in_callback()
needs_winner_filled_out = part_of_auction.filter(
auctiontos_winner__isnull=False,
winner__isnull=True,
auctiontos_winner__user__isnull=False,
auction__auctiontos=F("auctiontos_winner"),
)
for lot in needs_winner_filled_out:
winner = lot.auctiontos_seller.user
print("setting winner based on auction tos:")
print(lot, lot.auctiontos_winner, winner)
# fixme - this code is not yet fully tested, no saving yet!!
# lot.winner = winner
# lot.save()
lot.send_websocket_message(result)


class Command(BaseCommand):
help = "Sets the winner, and winning price on all ended lots. Send lot ending soon and lot ended messages to websocket connected users. Sets active to false on lots"

def handle(self, *args, **options):
lots = Lot.objects.filter(is_deleted=False, banned=False, deactivated=False)
# part_of_auction = lots.filter(auction__isnull=False)

# fix_seller_info(part_of_auction)
declare_winners_on_lots(lots.filter(active=True))
# fix_winner_info(part_of_auction)
lots = Lot.objects.filter(active=True, is_deleted=False, banned=False, deactivated=False)
declare_winners_on_lots(lots)
19 changes: 19 additions & 0 deletions auctions/management/commands/webpush_notifications_deduplicate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.core.management.base import BaseCommand
from django.db.models import Count, Min
from webpush.models import SubscriptionInfo


class Command(BaseCommand):
help = "Duplicate subscriptions can be created, this removes them. See https://github.com/safwanrahman/django-webpush/issues/135"

def handle(self, *args, **options):
# Step 1: Annotate each WebpushSubscriptionInfo with the minimum id for each group of auth and p256dh.
duplicates = (
SubscriptionInfo.objects.values("auth", "p256dh")
.annotate(min_id=Min("id"), count_id=Count("id"))
.filter(count_id__gt=1)
)
# Step 2: Collect the IDs of the entries with the lowest id in each group.
min_ids_to_delete = [entry["min_id"] for entry in duplicates]
# Step 3: Delete these entries.
SubscriptionInfo.objects.filter(id__in=min_ids_to_delete).delete()
Loading

0 comments on commit 15e45c8

Please sign in to comment.