Compare objects from two models in Django - python

I have two models PriceActual and PriceBenchmark having fields date and price.
I want to compare the actual prices with the benchmark prices.
I'm not interested in benchmark prices with dates which is not present in the actual prices. So if PriceActual only has objects from the last week, I only want to query objects from PriceBenchmark also within the last week.
I guess it's something like
actual = PriceActual.objects.all()
benchmark = PriceActual.objects.filter(date__in=actual)
Edit
The models are really simple
class PriceActual(models.Model):
date = DateField()
price = DecimalField()
class PriceBenchmark(models.Model):
date = DateField()
price = DecimalField()

Maybe not the most efficient...but you could always create a list of dates:
actual = PriceActual.objects.all()
actual_dates = [x.date for x in actual]
benchmark = PriceActual.objects.filter(date__in=actual_dates)

Related

Django queryset : Calculate monthly average

I have a sales model and i want to calculate the (Number of transactions)/(num of days) when grouped by month, week, year.
class SaleItem(models.Model):
id = models.UUIDField(default=uuid4, primary_key=True)
bill = models.ForeignKey()
item = models.ForeignKey('item')
quantity = models.PositiveSmallIntegerField()
price = models.DecimalField(max_digits=13, decimal_places=3, default=0)
So if sales is grouped by month then this becomes (# transcations/# days in that month) for each month. Now if the sales if grouped by year this becomes (# transcations/# days in that year)
Currently i can get the number of transactions
aggregate = 'month' # parameter
# get number of transactions
SaleItem.objects.annotate(date=Trunc('bill__date', aggregate)).values('date').annotate(sales=Count('bill', distinct=True))
But how can i divide each count by the number of days in that group?
Doing it in SQL is possible (and not even that difficult). Getting the number of days in a month is RDBMS-specific though, and there is no generic Django database function to shield you from the various SQL implementations.
Django makes it very easy to wrap your own functions around SQL functions. For instance, for SQLite, you can define
class DaysInMonth(Func):
output_field = IntegerField()
def as_sqlite(self, compiler, connection):
return super().as_sql(
compiler,
connection,
function='strftime',
template='''
%(function)s("%%%%d",
%(expressions)s,
"start of month",
"+1 month",
"-1 day")
''',
)
Then you can use DaysInMonth() to divide your count by the number of days:
qs = (
SaleItem.objects
.annotate(date=Trunc('bill__date', aggregate))
.values('date')
.annotate(
sales = Count('bill', distinct=True),
sales_per_day = F('sales') / DaysInMonth('date')
)
)
If a rounded-down integer is not sufficient and you need a decimal result, this is another hoop to jump through:
sales_per_day=ExpressionWrapper(
Cast('sales', FloatField()) / DaysInMonth(F('date')),
DecimalField()
)
If, heaven forbid, you want to round in the database rather than in your template, you need another custom function:
class Round(Func):
function = 'ROUND'
output_field = FloatField()
arity = 2
sales_per_day=Round(
Cast('sales', FloatField()) / DaysInMonth(F('date')),
2 # decimal precision
)
So Django is really flexible, but as Willem said doing it in Python would save you some pain without losing significant performance (if any at all).

Django list of timestamps from DateTimeFields in output of group-by

I have a model that looks like the following (some irrelevant fields have been omitted):
class Note(models.Model):
enterprise_id = models.IntegerField(db_index=True)
field_id = models.IntegerField()
activity = models.TextField(choices=ACTIVITY_CHOICES)
user_date = models.DateTimeField()
I would like to (in SQL terms) group by the combination of (enterprise_id, field_id, activity, year of user_date) and then for each group, list the user_dates from Notes that went into it. The following queryset works, but the array_agg list ends up being a string such as [datetime.datetime(2017,1,1,0,0,0),...] or similar, which is not very easily parsable.
Is there a way that I can ArrayAgg the unix timestamp from the user_date field, rather than the datetime object itself? Or failing that, ArrayAgg the tuple of (year, month, day) so it can be parsed?
qs = self.get_queryset()\
.annotate(year=ExtractYear('user_date'))\
.values('activity', 'enterprise_id', 'field_id', 'year')\
.order_by('activity', 'enterprise_id', 'field_id', 'year') \
.annotate(dates=ArrayAgg('user_date'))
If you get a list of datetime objects, you can use a map to parse it.
dates = list(map(lambda date: date.strftime('%d-%m-%Y'), dates))

Django filter using two conditions

I am developing a web app using django. Among with other tables, I have a table called GeneralContract, which has issueDate and Expiration Date as date fields.
I want to find out the profit of an insurance agent in my case would get from these contracts between a period. For example, if the date range is 15 January 2015 - 25 February 2015 I would like to filter all GeneralContract objects who are issued ANY year between this period.
(i.e. issueDate__month = Date1.month AND IssueDate.day_gte= Date1.day) AND (IssueDate__month = Date2.month AND IssueDate.day_lte = Date2.day) ??
I tried the following but it is not giving me the results I wanted and I am not sure if I am writing this in the correct syntax or if my logic is wrong.
criterion1 = Q(issuedate__month=date1.month)
criterion2 = Q(issuedate__day__gte=date1.day)
criterion3 = Q(issuedate__month=date2.month)
criterion4 = Q(issuedate__day__lte=date2.day)
criterionA = criterion1 & criterion2
criterionB = criterion3 & criterion4
criterionC = criterionA & criterionB
currentGenProfits = GeneralContract.objects.filter(criterionC, cancelled=False)
Is this the right way of doing this filtering logic?
You can't do that because if date1.day = 5 and date2.day = 4 you will have no result, you must check the month and the date together, syntax is right but logic is wrong.
I may suggest to start by taking the biggest set and then apply filtering on it
Start with filtering between the two month and then remove from the queryset objects which are on the first month but before the min day and remove from the queryset objects which are on the last month but after the max day, i think that can do the job.

Django query count unique NEW results per day

I'm keeping statistics of my app in a database in a model that looks like this
class MyStats():
event_code = django.db.models.PositiveSmallIntegerField()
timestamp = django.db.models.DateTimeField()
timestamp_date = django.db.models.DateField()
device_id = django.db.models.CharField(max_length=32)
I would like to use this data to determine, for each day, how many NEW app installations I have.
I got as far as this:
MyStats.objects.order_by('-timestamp_date').values('timestamp_date').annotate(count_total=Count('device_id', distinct=True))
But what it seems to give me is the amount of unique users per DAY, which is not desired. Any hints?
"New" really means "Occurring after x_timedelta ago":
import datetime
x_timedelta = datetime.timedelta(days=1)
x_timedelta_ago = datetime.datetime.now() - x_timedelta
your_query = MyStats.objects.filter(timestamp_date__gt=x_timedelta_ago)
your_query_distinct = your_query.order_by('-timestamp').annotate(count_total=Count('device_id', distinct=True)).values()

Django Group By Weekday?

I'm using Django 1.5.1, Python 3.3.x, and can't use raw queries for this.
Is there a way to get a QuerySet grouped by weekday, for a QuerySet that uses a date __range filter? I'm trying to group results by weekday, for a query that ranges between any two dates (could be as much as a year apart). I know how to get rows that match a weekday, but that would require pounding the DB with 7 queries just to find out the data for each weekday.
I've been trying to figure this out for a couple hours by trying different tweaks with the __week_day filter, but nothing's working. Even Googling doesn't help, which makes me wonder if this is even possible. Any Django guru's here know how, if it is possible to do?
Since extra is deprecated, here is a new way of grouping on the day of the week using ExtractDayOfWeek.
from django.db.models.functions import ExtractWeekDay
YourObjects.objects
.annotate(weekday=ExtractWeekDay('timestamp'))
.values('weekday')
.annotate(count=Count('id'))
.values('weekday', 'count')
This will return a result like:
[{'weekday': 1, 'count': 534}, {'weekday': 2, 'count': 574},.......}
It is also important to note that 1 = Sunday and Saturday = 7
Well man I did an algorithm this one brings you all the records since the beginning of the week (Monday) until today
for example if you have a model like this in your app:
from django.db import models
class x(models.Model):
date = models.DateField()
from datetime import datetime
from myapp.models import x
start_date = datetime.date(datetime.now())
week = start_date.isocalendar()[1]
day_week =start_date.isoweekday()
days_quited = 0
less_days = day_week
while less_days != 1:
days_quited += 1
less_days -= 1
week_begin = datetime.date(datetime(start_date.year,start_date.month,start_date.day-days_quited))
records = x.objects.filter(date__range=(week_begin, datetime.date(datetime.now())))
And if you add some records in the admin with a range between June 17 (Monday) and June 22 (today) you will see all those records, and if you add more records with the date of tomorrow for example or with the date of the next Monday you will not see those records.
If you want the records of other week unntil now you only have to put this:
start_date = datetime.date(datetime(year, month, day))
records = x.objects.filter(date__range=(week_begin, datetime.date(datetime.now())))
Hope this helps! :D
You need to add an extra weekday field to the selection, then group by that in the sum or average aggregation. Note that this becomes a database specific query, because the 'extra' notation becomes passed through to the DB select statement.
Given the model:
class x(models.Model):
date = models.DateField()
value = models.FloatField()
Then, for mysql, with a mapping of the ODBC weekday to the python datetime weekday:
x.objects.extra(select={'weekday':"MOD(dayofweek(date)+5,7)"}).values('weekday').annotate(weekday_value=Avg('value'), weekday_value_std=StdDev('value'))
Note that if you do not need to convert the MySql ODBC weekday (1 = Sunday, 2 = Monday...) to python weekday (Monday is 0 and Sunday is 6), then you do not need to do the modulo.
For model like this:
class A(models.Model):
date = models.DateField()
value = models.FloatField()
You can use query:
weekday = {"w": """strftime('%%w', date)"""}
qs = A.objects.extra(select=weekday).values('w').annotate(stat = Sum("value")).order_by()

Categories