Improve price information
This commit is contained in:
parent
4ec808eeec
commit
6f62889e92
|
@ -11,6 +11,8 @@ class GamesConfig(AppConfig):
|
|||
name = "games"
|
||||
|
||||
def ready(self):
|
||||
import games.signals # noqa: F401
|
||||
|
||||
post_migrate.connect(schedule_tasks, sender=self)
|
||||
|
||||
|
||||
|
@ -26,6 +28,14 @@ def schedule_tasks(sender, **kwargs):
|
|||
next_run=now() + timedelta(seconds=30),
|
||||
)
|
||||
|
||||
if not Schedule.objects.filter(name="Update price per game").exists():
|
||||
schedule(
|
||||
"games.tasks.calculate_price_per_game",
|
||||
name="Update price per game",
|
||||
schedule_type=Schedule.MINUTES,
|
||||
next_run=now() + timedelta(seconds=30),
|
||||
)
|
||||
|
||||
from games.models import ExchangeRate
|
||||
|
||||
if not ExchangeRate.objects.exists():
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.1.5 on 2025-01-30 11:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='purchase',
|
||||
name='price_per_game',
|
||||
field=models.FloatField(null=True),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.1.5 on 2025-01-30 11:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('games', '0002_purchase_price_per_game'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='purchase',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 5.1.5 on 2025-01-30 11:57
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.db.models import Count
|
||||
|
||||
|
||||
def initialize_num_purchases(apps, schema_editor):
|
||||
Purchase = apps.get_model("games", "Purchase")
|
||||
purchases = Purchase.objects.annotate(num_games=Count("games"))
|
||||
|
||||
for purchase in purchases:
|
||||
purchase.num_purchases = purchase.num_games
|
||||
purchase.save(update_fields=["num_purchases"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("games", "0003_purchase_updated_at"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="purchase",
|
||||
name="num_purchases",
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.RunPython(initialize_num_purchases),
|
||||
]
|
|
@ -116,6 +116,8 @@ class Purchase(models.Model):
|
|||
price_currency = models.CharField(max_length=3, default="USD")
|
||||
converted_price = models.FloatField(null=True)
|
||||
converted_currency = models.CharField(max_length=3, null=True)
|
||||
price_per_game = models.FloatField(null=True)
|
||||
num_purchases = models.IntegerField(default=0)
|
||||
ownership_type = models.CharField(
|
||||
max_length=2, choices=OWNERSHIP_TYPES, default=DIGITAL
|
||||
)
|
||||
|
@ -130,19 +132,16 @@ class Purchase(models.Model):
|
|||
related_name="related_purchases",
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
@property
|
||||
def standardized_price(self):
|
||||
return (
|
||||
f"{floatformat(self.converted_price)} {self.converted_currency}"
|
||||
f"{floatformat(self.converted_price, 0)} {self.converted_currency}"
|
||||
if self.converted_price
|
||||
else None
|
||||
)
|
||||
|
||||
@property
|
||||
def num_purchases(self):
|
||||
return self.games.count()
|
||||
|
||||
@property
|
||||
def has_one_item(self):
|
||||
return self.games.count() == 1
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
from django.db.models.signals import m2m_changed
|
||||
from django.dispatch import receiver
|
||||
from django.utils.timezone import now
|
||||
|
||||
from games.models import Purchase
|
||||
|
||||
|
||||
@receiver(m2m_changed, sender=Purchase.games.through)
|
||||
def update_num_purchases(sender, instance, **kwargs):
|
||||
instance.num_purchases = instance.games.count()
|
||||
instance.updated_at = now()
|
||||
instance.save(update_fields=["num_purchases"])
|
|
@ -1,4 +1,8 @@
|
|||
import requests
|
||||
from django.db.models import ExpressionWrapper, F, FloatField, Q
|
||||
from django.template.defaultfilters import floatformat
|
||||
from django.utils.timezone import now
|
||||
from django_q.models import Task
|
||||
|
||||
from games.models import ExchangeRate, Purchase
|
||||
|
||||
|
@ -51,7 +55,7 @@ def convert_prices():
|
|||
currency_from=currency_from,
|
||||
currency_to=currency_to,
|
||||
year=year,
|
||||
rate=rate,
|
||||
rate=floatformat(rate, 2),
|
||||
)
|
||||
else:
|
||||
print("Could not get an exchange rate.")
|
||||
|
@ -61,5 +65,24 @@ def convert_prices():
|
|||
)
|
||||
if exchange_rate:
|
||||
save_converted_info(
|
||||
purchase, purchase.price * exchange_rate.rate, currency_to
|
||||
purchase,
|
||||
floatformat(purchase.price * exchange_rate.rate, 0),
|
||||
currency_to,
|
||||
)
|
||||
|
||||
|
||||
def calculate_price_per_game():
|
||||
try:
|
||||
last_task = Task.objects.filter(group="Update price per game").first()
|
||||
last_run = last_task.started
|
||||
except Task.DoesNotExist or AttributeError:
|
||||
last_run = now()
|
||||
purchases = Purchase.objects.filter(converted_price__isnull=False).filter(
|
||||
Q(updated_at__gte=last_run) | Q(price_per_game__isnull=True)
|
||||
)
|
||||
print(f"Updating {purchases.count()} purchases.")
|
||||
purchases.update(
|
||||
price_per_game=ExpressionWrapper(
|
||||
F("converted_price") / F("num_purchases"), output_field=FloatField()
|
||||
)
|
||||
)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<span title="Price is a result of conversion and rounding." class="decoration-dotted underline">{{ slot }}</span>
|
|
@ -20,13 +20,12 @@
|
|||
</a>
|
||||
</div>
|
||||
<div>
|
||||
Price:
|
||||
{% if purchase.converted_price %}
|
||||
{{ purchase.converted_price | floatformat }} {{ purchase.converted_currency }}
|
||||
{% else %}
|
||||
None
|
||||
{% endif %}
|
||||
({{ purchase.price | floatformat }} {{ purchase.price_currency }})
|
||||
<p>
|
||||
Price:
|
||||
<c-price-converted>{{ purchase.standardized_price }}</c-price-converted>
|
||||
({{ purchase.price | floatformat:2 }} {{ purchase.price_currency }})
|
||||
</p>
|
||||
<p>Price per game: <c-price-converted>{{ purchase.price_per_game | floatformat:0 }} {{ purchase.converted_currency }}</c-price-converted> </p>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-base">Items:</h2>
|
||||
|
|
|
@ -47,7 +47,7 @@ INSTALLED_APPS = [
|
|||
|
||||
Q_CLUSTER = {
|
||||
"name": "DjangoQ",
|
||||
"workers": 4,
|
||||
"workers": 1,
|
||||
"recycle": 500,
|
||||
"timeout": 60,
|
||||
"retry": 120,
|
||||
|
|
Loading…
Reference in New Issue