Division between two annotations - django

I'm creating those two annotations as it follows:
cs = Champion.objects.all()
total_games = Match.objects.all().count()
cs = cs.annotate(
picked_games=Count(
expression='participants__match__id',
filter=Q(participants__role='BOTTOM'),
distinct=True
),
total_games=Value(str(total_games), output_field=IntegerField())
)
And everthing's alright until here. I fetch both the picked_games and total_games with correct results.
>>> cs.get(name='Jhin').picked_games
27544
>>> cs.get(name='Jhin').total_games
97410
However, if i try to divide one by another:
cs = cs.annotate(
pick_rate=ExpressionWrapper(
expression=F('picked_games') / F('total_games'),
output_field=FloatField()
)
)
This results on a 0:
>>> cs.get(name='Jhin').pick_rate
0.0
I don't understand what's problem here..
I can get the result if divide them externally, so why can't i get the result on a different column for the whole queryset?
>>> cs.get(name='Jhin').picked_games / cs.get(name='Jhin').total_games
0.28319474386613286

You should cast the numerator (or denominator) to a float before making the division, otherwise the PostgreSQL database will use integer division and thus truncate towards zero. You thus can work with:
from django.db.models import Count, F, FloatField, Q
from django.db.models.functions import Cast
total_games = Match.objects.all().count()
Champion.objects.annotate(
picked_games=Count(
expression='participants__match__id',
filter=Q(participants__role='BOTTOM'),
distinct=True
)
).annotate(
pick_rate=Cast('picked_games', output_field=FloatField()) / total_games
)

Related

Sympy: Is it possible use function collect() to IndexedBase variables?

I'm trying to use the function collect() to simplify mi expression . My desired result is
My code:
from sympy import *
#index
i = symbols('i' , integer = True )
#constants
a = symbols( 'a' )
#variables
alpha = IndexedBase('alpha', positive=True, domain=QQ)
index = (i, 1, 3)
rho = symbols( 'rho')
U = product( alpha[i]**(1/(rho-1)) , index )
U
:
My solution attempt:
U = U.subs(1/(rho-1),a)
collect(U,rho, evaluate=False)[1]
:
What I'm doing wrong?
You must be using a fairly old version of SymPy because in recent versions the form that you wanted arises automatically. In any case you should be able to use powsimp:
In [9]: U
Out[9]:
a a a
alpha[1] ⋅alpha[2] ⋅alpha[3]
In [10]: powsimp(U, force=True)
Out[10]:
a
(alpha[1]⋅alpha[2]⋅alpha[3])
https://docs.sympy.org/latest/tutorials/intro-tutorial/simplification.html#powsimp

Django RAW SQL query how to pass date comparison

I am working with Django Raw SQL as I want to build a relatively complex query,
As part of it, I have a case statement that says 'if the date on the record is < today then 1 else 0 - this works.
overdue_phases = task.objects.filter(orgid=orgid, taskid=taskidx).raw('''
SELECT '1' as id, case when research_due < "2022-12-01" AND research_status != "done" then 1 else 0 end as count
from app_task where taskid = "''' + taskidx + '''"''')
overdue_phases = task.objects.filter(orgid=orgid, taskid=taskidx).raw('''
SELECT '1' as id, case when research_due < "2022-12-01" AND research_status != "done" then 1 else 0 end as count
from app_task where taskid = "''' + taskidx + '''"''')
However, when I want to swap the hard coded date for TODAY() I can't make it work. I have tried passing a python date variable into the script (td = datetime.date.today()), but it doesn't return the right results!
Can anyone advise me please?
There is no need to do this. You can use condition expressions [Django-doc]:
from django.db.models import Case, Q, Value, When
overdue_phases = task.objects.filter(orgid=orgid, taskid=taskidx).annotate(
count=Case(
When(
~Q(research_status='done'),
research_due__lt='2022-12-01',
then=Value(1),
),
default=Value(0),
)
)
Or if a boolean is sufficient as well:
from django.db.models import Q
overdue_phases = task.objects.filter(orgid=orgid, taskid=taskidx).annotate(
count=~Q(research_status='done') & Q(research_due__lt='2012-12-01')
)
or for older versions of Django with an ExpressionWrapper [Django-doc]:
from django.db.models import BooleanField, ExpressionWrapper, Q
overdue_phases = task.objects.filter(orgid=orgid, taskid=taskidx).annotate(
count=ExpressionWrapper(
~Q(research_status='done') & Q(research_due__lt='2012-12-01'),
output_field=BooleanField(),
)
)
You can replace '2022-12-01' with date.today().

Django Calculate Mean Of 2 Fields Inside Multiple Objects

I'm trying to do a very simple math problem but I don't know how to convert it into python. Basically I need to calculate the mean entry price for a trade based on multiple "buy" entries. To do that all one needs to do is calculate
∑ (entry.amount * entry.price) / ∑ (entry.amount)
This should be the variable "total_entry_price2" in the end.
Where am I going wrong with the calculation? How Can I add all the ∑'s together?
Is this the best way to do it?
models.py
class Trade(models.Model):
...
class Entry(models.Model):
...
trade = models.ForeignKey(Trade, on_delete=models.CASCADE)
amount = models.FloatField()
price = models.FloatField()
entry_type = models.CharField(max_length=3, choices=ENTRY_TYPE_CHOICES, default=BUY)
views.py
#login_required
def trade_detail_view(request, pk):
logger.info('trade detail view')
if request.method == 'GET':
trade = get_object_or_404(Trade, pk=pk)
entries = Entry.objects.filter(trade=trade)
entries_buy = Entry.objects.filter(trade=trade, entry_type="buy")
patterns = Pattern.objects.filter(trade=trade)
for entry in entries_buy:
total_entry_price = Sum(entry.amount * entry.price)
total_entry_price2 = total_entry_price / entries_buy.aggregate(Sum('amount'))
print(total_entry_price2)
context = {
'trade': trade,
'entries': entries,
'patterns': patterns,
'max_amount': entries_buy.aggregate(Sum('amount')),
'total_fees': entries.aggregate(Sum('fee')),
'entry_price': entries_buy.aggregate(Avg('price'))
}
Current Terminal print:
Sum(Value(60.0)) / Value({'amount__sum': 40.0})
Sum(Value(10.0)) / Value({'amount__sum': 40.0})
Example data
The correct answer should be $1.75
(30 * 2 + 10 * 1) / 40 = 1.75
Final Solution (added upon from Oleg Russkin's Answer)
The revisions I did are as follows:
total_entry_cost = entries_buy.annotate(
s=F('amount') * F('price')
).aggregate(
total_entry_cost=ExpressionWrapper(
Sum(
Cast('s', output_field=models.FloatField())
) / Sum('amount'),
output_field=models.FloatField()
)
)['total_entry_cost']
print(total_entry_cost)
Example query to calculate required value.
Cast() to float may be avoided if the result of Sum is float, not an integer.
from django.db import models
from django.db.models import ExpressionWrapper, F, Sum
from django.db.models.functions import Cast
total_entry_price2 = Entry.objects.annotate(
s=F('amount')+F('price')
).aggregate(
price2=ExpressionWrapper(
Sum(
Cast('s',output_field=models.FloatField())
) / Sum('amount'),
output_field=models.FloatField()
)
)['price2']
# Actual result of the query is dictioanry
# so we get the key
# {'price2': 0.59633706227207}
Updated Answer By OP
This answer almost got us all the way. It was my fault not to be more clear on the exact answer I was looking for. I updated my question near the end to reflect it.
The revisions I did are as follows:
total_entry_cost = entries_buy.annotate(
s=F('amount') * F('price')
).aggregate(
total_entry_cost=ExpressionWrapper(
Sum(
Cast('s', output_field=models.FloatField())
) / Sum('amount'),
output_field=models.FloatField()
)
)['total_entry_cost']
print(total_entry_cost)

Django Sum Aggregation from Int to Float

So i save my fees in cents, but want to display them in Euro. The conversion problem is that it first divides the integer by 100 and then converts to a float. It should be the other way around. How can i do that?
The following code should illustrate my Problem:
>>> from django.db.models import F, FloatField, Sum
>>> from payment.models import Payment
>>>
>>> paymentlist = Payment.objects.all()
>>> result = paymentlist.annotate(
... fees_in_cents=Sum(F('fees'), output_field=FloatField())).annotate(
... fees_in_euro=Sum(F('fees')/100, output_field=FloatField()))
>>>
>>> print(result[0].fees_in_cents, result[0].fees_in_euro)
120.0 1.0
fees_in_euro should obviously be 1.20
Divide fees value by 100.0
Example:
result = paymentlist.annotate(
fees_in_cents=Sum(F('fees'), output_field=FloatField())).annotate(
fees_in_euro=Sum(F('fees') / 100.0, output_field=FloatField())
)

How make conditional expression on Django with default value of object?

See https://docs.djangoproject.com/en/dev/ref/models/conditional-expressions/
conditional expression with Case;
o = Object.annotate(
custom_price=Case(
When(price=1, then=Value(2)),
default=0,
output_field=DecimalField(),
)
)
How use set 'default' - current value of Object?
Now it writed only as const: 0
Want something like:
if price =1:
custom_price = 2
else:
custom_price = Object.price
F is used to perform this. So, your code should look like:
from django.db.models import Case, F, Value, When, DecimalField
o = Object.objects.annotate(custom_price=Case(
When(price=1, then=Value(2)),
default=F('price'),
output_field=DecimalField(),
)
)