Custom TruncFunc in Django ORM - python

I have a Django model with the following structure:
class BBPerformance(models.Model):
marketcap_change = models.FloatField(verbose_name="marketcap change", null=True, blank=True)
bb_change = models.FloatField(verbose_name="bestbuy change", null=True, blank=True)
created_at = models.DateTimeField(verbose_name="created at", auto_now_add=True)
updated_at = models.DateTimeField(verbose_name="updated at", auto_now=True)
I would like to have an Avg aggregate function on objects for every 3 days.
for example I write a queryset that do this aggregation for each day or with something like TruncDay function.
queryset = BBPerformance.objects.annotate(day=TruncDay('created_at')).values('day').annotate(marketcap_avg=Avg('marketcap_change'),bb_avg=Avg('bb_change')
How can I have a queryset of the aggregated value with 3-days interval and the index of the second day of that interval?

I guess it's impossible on DB level (and Trunc is DB level function) as only month, days weeks and so on are supported in Postgres and Oracle.
So what I would suggest is to use TruncDay and then add python code to group those by 3 days.

The following should work, although it's slightly ugly.
If you get the difference in days between each row's date and the min date you can then take the Mod of this difference to work out how many days you need to shift to get "middle" date. This middle date can then be grouped on using a values query
import datetime
from django.db.models import F, IntegerField, Avg, Min, DurationField, DateField
from django.db.models.functions import Cast, Mod, Extract
BBPerformance.objects.order_by(
'created_at'
).annotate(
diff=F('created_at__date') - BBPerformance.objects.aggregate(min=Min('created_at__date'))['min']
).annotate(
diff_days=Cast(Extract('diff', 'days'), output_field=IntegerField())
).annotate(
to_shift=Mod('diff_days', 3) - 1
).annotate(
grouped_date=Cast(F('created_at__date') - Cast(F('to_shift') * datetime.timedelta(days=1), output_field=DurationField()), output_field=DateField())
).order_by(
'grouped_date'
).values(
'grouped_date'
).annotate(
marketcap_avg=Avg('marketcap_change'),
bb_avg=Avg('bb_change')
)

Related

Django: Get Months since a certain datetime

I'm trying to get the number of months since a model is created.
My Model looks like this:
class Plan(models.Model):
title = models.CharField(max_length=50)
date_created = models.DateTimeField(default=timezone.now)
plan_type = models.IntegerField()
owner = models.ForeignKey(User, on_delete=models.CASCADE)
Now i want to make a method that returns the number of months since the date_created.
Tanks for any help :D
Comparing dates creates a datetime.timedelta object that you can use to get the difference between dates.
from datetime import timedelta
from django.utils.timezone import now
delta: timedelta = now() - plan.date_created
delta.days # Number of days between dates.
You can then use that value to convert it to months or years.
The other alternative would be a bit more complicated, but since DateTimeField returns a datetime.datetime object, you can also access the month number of the original date and compare it against todays date.
e.g.
from django.utils.timezone import now
month_diff = now().month - plan.date_created.month
The problem with the second alternative is that you then have to take into account if they are the same year or not, and if they are not you then have to take that into account when you get the month difference.
You can write a property in your model like
from django.utils import timezone
class Plan(models.Model):
title = models.CharField(max_length=50)
...
#property
def get_month(self):
return self.date_created.month - timezone.now().month
Then you can get the value like this
>>> Plan.objects.first().get_month
4

How to calculate DateTime fields in django

Suppose I have the following Date time fields and I would like to calculate the total time between them. Which is the best approach?
session_end_time = models.DateTimeField(null=True, blank=True)
discharged_at = models.DateTimeField(null=True, blank=True)
checked_in_at = models.DateTimeField(null=True, blank=True)
Django DateTime Fields are like datetime object of python, to calculate the total time between, you need to substract one from another one since they are same objects. This is an approach
result = datetime1 - datetime2
result.seconds # To have the output in seconds
In your case:
total_time = (checked_in_at - discharged_at).seconds
You can simply use - operator to calculate the time difference. The result will be a time delta object.
def time_diff(time1, time2):
"retun time2-time1 in 'seconds' "
if time1 and time2:
return (time2 - time1).seconds
return "one of the input is None"
This function returns the difference in seconds and it will handle TypeError exception if one of the input is a None type. (
You defined it as null=True in models)

Django:Find no of days between 2 dates and compare with sysdate and return if it matches

I want to know who is on leave today based on end_date and start_date which are DatetimeField by comparing values between end_date and start_date with sysdate and returning if it matches with sysdate.
models.py
class leave(models.Model):
employee = models.ForeignKey(employees, on_delete=models.CASCADE, default='1')
start_date = models.DateField()
end_date = models.DateField()
This will give you a queryset of leave instances:
import datetime
today = datetime.date.today()
leave.objects.filter(start_date__lt=today, end_date__gt=today)
Or use __lte and __gte if you want to include the boundaries, EG including leave instances that started today.
If you want employees, follow the foreign key:
employees.objects.filter(leave__set_date__lt=today, leave__end_date__gt=today)
If you don't have anything enforcing that there can't be overlapping leave instances for the sample employee, add .distinct() to the employees queryset.
Assuming that start_date, end_date and today are all date objects, you can check if someone is on leave this way: today >= start_date and today <= end_date.

How to group by date from timestamp field with Django ORM?

I have this MySQL specific query
SELECT SUM(trip_amount) as tp , DATE(FROM_UNIXTIME(request_time)) as timestamp
FROM trip_master
WHERE trip_master.date > 1493836200
AND trip_master.date < 1493922600
AND trip_master.id = 6
GROUP BY timestamp
Implemented this query in Django,
Trip.objects.filter(id=userid,date__gt = start,date__lt = end).annotate(trip_amount = Sum('trip_amount')).extra({'timestamp':'DATE(FROM_UNIXTIME(request_time))'})
As defined I have to convert time stamp to date to have grouping from dates. Has reached at the almost level but not getting group by dates.
Any help is much appreciated
Try:
from django.db.models.expressions import RawSQL
Trip.objects.filter(
id=userid,
date__gt=start,
date__lt=end
).annotate(
timestamp=RawSQL('DATE(FROM_UNIXTIME(request_time))', [])
).values(
'timestamp'
).annotate(
trip_amount=Sum('trip_amount')
)
See Group objects by dates

How do I get something like repeat customers in Django

My models look something like this:
class Customer(models.Model):
name = models.CharField(max_length=100)
class Order(models.Model):
customer = models.ForeignKey(Customer)
date = models.DateField()
total = models.DecimalField(max_digits=5, decimal_places=2)
I then have a queryset of orders:
from datetime import datetime
start_date = datetime(year=2009, month=6, day=1)
end_date = datetime(year=2009, month=11, day=1)
orders = Order.objects.filter(date__lte=end_date).filter(date__gte=start_date)
Now, I want to find out which customers made multiple orders between those times, how many orders they made, and what their average total is. I get the feeling that I should be using Django 1.1's new aggregation features, but I can't really wrap my head around it.
Always base your query around the object in which you are primarily interested in:
repeat_customers = Customer.objects.annotate(order_count=Count('order'))\
.filter(order_count__gt=1)
Then if you want to annotate with their totals (you could alternatively do this in the annotation above, I'm just separating the code for readability):
repeat_customers = repeat_customers.annotate(avg_total=Avg('order__total'))
This would be a good use for Django 1.1's annotate() functionality, which is part of aggregateion. Specifically, you'll probably want to use the values() function.

Categories

Resources