diff --git a/core/helpers/email_template.py b/core/helpers/email_template.py index 05b7325..519b5bb 100644 --- a/core/helpers/email_template.py +++ b/core/helpers/email_template.py @@ -29,6 +29,7 @@ class EmailTemplate: context['site_name'] = settings.SITE_NAME context['site_url'] = settings.SITE_URL context['escrow_period'] = settings.ESCROW_PERIOD_DAYS + context['stale_period'] = settings.STALE_PERIOD_DAYS subject = render_to_string( template_name=f'{tpl_path}/notify/{scenario}/{role}/subject.txt', diff --git a/core/tests/__init__.py b/core/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/tests/test_helpers.py b/core/tests/test_helpers.py new file mode 100644 index 0000000..206eb9a --- /dev/null +++ b/core/tests/test_helpers.py @@ -0,0 +1,68 @@ +from secrets import token_urlsafe +from monero.seed import Seed +from django.test import TestCase +from django.contrib.auth.models import User +from django.test.client import Client +from django.shortcuts import reverse +from bids.models import ItemBid +from items.models import Item +from core.helpers.email_template import EmailTemplate +from sales.models import ItemSale + + +class ItemBidModelsTestCase(TestCase): + def setUp(self): + self.client = Client() + self.seller_password = token_urlsafe(32) + self.buyer_password = token_urlsafe(32) + + self.seller = User.objects.create_user( + 'seller', self.seller_password + ) + self.buyer = User.objects.create_user( + 'buyer', self.buyer_password + ) + self.payout_address = Seed().public_address(net='stagenet') + self.return_address = Seed().public_address(net='stagenet') + self.whereabouts = 'Los Angeles, CA' + + self.test_item = Item.objects.create( + owner=self.seller, + name='Test Item', + description='Test item', + ask_price_xmr=0.3, + payout_address=self.payout_address, + whereabouts=self.whereabouts + ) + + + def test_email_template_helper_sends_successfully_for_(self): + e = EmailTemplate( + item=self.test_item, + scenario='item_has_bids', + role='seller' + ) + res = e.send() + self.assertEqual(res, 1) + + def test_email_template_helper_sends_successfully_for_aaa(self): + new_bid = ItemBid.objects.create( + item=self.test_item, + bidder=self.buyer, + bid_price_xmr=0.1, + return_address=self.return_address + ) + self.client.login(username=self.seller.username, password=self.seller_password) + response = self.client.get(reverse('accept_bid', args=[new_bid.id])) + item_sale = ItemSale.objects.filter(item=self.test_item, bid=new_bid).first() + updated_bid = ItemBid.objects.get(id=new_bid.id) + self.assertTrue(updated_bid.accepted) + self.assertFalse(updated_bid.item.available) + + e = EmailTemplate( + item=item_sale, + scenario='item_shipped', + role='buyer' + ) + res = e.send() + self.assertEqual(res, 1) diff --git a/items/tasks/cleanup.py b/items/tasks/cleanup.py index 3f89b58..9b495d1 100644 --- a/items/tasks/cleanup.py +++ b/items/tasks/cleanup.py @@ -1,28 +1,16 @@ from logging import getLogger -from datetime import timedelta -from django.utils import timezone as tz from django.conf import settings from huey import crontab from huey.contrib.djhuey import periodic_task -from items.models import Item -from core.helpers.email_template import EmailTemplate +from items.tasks.common import get_items_past_days logger = getLogger('django.server') -@periodic_task(crontab(minute='0', hour='*/21')) -def close_stale_items(): - time_delta = tz.now() - timedelta(days=settings.ESCROW_PERIOD_DAYS) - items = Item.objects.filter(list_date__lt=time_delta, available=True) - +@periodic_task(crontab(minute='0', hour='0', day='*')) +def delete_expired_items(): + expired_days = settings.ESCROW_PERIOD_DAYS + items = get_items_past_days(expired_days) for item in items: - logger.info(f'[INFO] Found stale item #{item.id} (older than {settings.ESCROW_PERIOD_DAYS} days).') - if item.bids: - email_template = EmailTemplate( - item=item, - scenario='item_stale_with_bids', - role='seller' - ) - email_template.send() - - + logger.info(f'[INFO] Found expired item #{item.id} (older than {expired_days} days).') + item.delete() diff --git a/items/tasks/common.py b/items/tasks/common.py new file mode 100644 index 0000000..7f05472 --- /dev/null +++ b/items/tasks/common.py @@ -0,0 +1,10 @@ +from datetime import timedelta +from django.utils import timezone as tz +from django.conf import settings +from items.models import Item + + +def get_items_past_days(days=settings.ESCROW_PERIOD_DAYS) -> list: + time_delta = tz.now() - timedelta(days=days) + items = Item.objects.filter(list_date__lt=time_delta, available=True) + return items diff --git a/items/tasks/notifications.py b/items/tasks/notifications.py index 48f7358..128ce3e 100644 --- a/items/tasks/notifications.py +++ b/items/tasks/notifications.py @@ -1,8 +1,10 @@ from logging import getLogger from huey import crontab from huey.contrib.djhuey import periodic_task +from django.conf import settings from core.helpers.email_template import EmailTemplate from items.models import Item +from items.tasks.common import get_items_past_days logger = getLogger('django.server') @@ -18,4 +20,18 @@ def notify_seller_of_item_bids(): scenario='item_has_bids', role='seller' ) - email_template.send() \ No newline at end of file + email_template.send() + +@periodic_task(crontab(minute='0', hour='0', day='*/3')) +def notify_seller_of_stale_items(): + stale_days = settings.ESCROW_PERIOD_DAYS - 7 + items = get_items_past_days(stale_days) + for item in items: + logger.info(f'[INFO] Found stale item #{item.id} (older than {stale_days} days).') + if item.bids: + email_template = EmailTemplate( + item=item, + scenario='item_stale_with_bids', + role='seller' + ) + email_template.send() diff --git a/web/templates/items/notify/item_stale_with_bids/seller/body.txt b/web/templates/items/notify/item_stale_with_bids/seller/body.txt index 0acd236..e3a30e2 100644 --- a/web/templates/items/notify/item_stale_with_bids/seller/body.txt +++ b/web/templates/items/notify/item_stale_with_bids/seller/body.txt @@ -1,6 +1,6 @@ Bad News {{ item.owner }}, -Your item, "{{ item.name }}" (#{{ item.id }}), has been listed on the auction house for over {{ escrow_period }} days. There are currently {{ item.bids.values | length }} bid(s) posted to it. +Your item, "{{ item.name }}" (#{{ item.id }}), has been listed on the auction house for over {{ stale_period }} days. There are currently {{ item.bids.values | length }} bid(s) posted to it. We'll provide you with a few more hours to either accept a bid or delete the item from the auction. Please do something soon. diff --git a/xmrauctions/settings.py b/xmrauctions/settings.py index c3b1e9d..b3b98b4 100644 --- a/xmrauctions/settings.py +++ b/xmrauctions/settings.py @@ -27,6 +27,7 @@ DEBUG = os.environ.get('DEBUG', False) ALLOWED_HOSTS = str(os.environ['ALLOWED_HOSTS']).split(',') ALLOWED_HOSTS.append('gunicorn') ESCROW_PERIOD_DAYS = os.environ.get('ESCROW_PERIOD_DAYS', 30) +STALE_PERIOD_DAYS = os.environ.get('STALE_PERIOD_DAYS', 21) PLATFORM_FEE_PERCENT = os.environ.get('PLATFORM_FEE_PERCENT', 0) BLOCK_CONFIRMATIONS_RCV = os.environ.get('BLOCK_CONFIRMATIONS_RCV', 3) BLOCK_EXPLORER = 'https://community.xmr.to/explorer/%s'