Django and chart.js - one bar per 'date' in model - python

I have a model that contains a date and a number. There are multiple inputs from the same date that I want to merge into 1 bar on the chart. How can I show each date only once but add up all of the totalPacks under that date?
model:
Views:
def homepage(request):
labels = []
data = []
queryset = DailyCountsModel.objects.order_by('-date')
for jobs in queryset:
labels.append(jobs.date)
data.append(jobs.totalPacks)
return render(request,'index.html', {
'labels': labels,
'data': data,
})
Currently this chart will show one bar per entry.. I can't think of how I could do this. Any ideas? I'm guessing somehow I would need to check to see how many items they are with the 'date' of 2021-08-23 and add up the 'totalPacks', I'm just not sure how I would do this

from django.db.models import Count
result = (DailyCountsModel.objects
.values('totalPacks', 'date')
.annotate(dcount=Count('totalPacks'))
.order_by('-date')
)

Related

Cannot filter my Django model depence on created_date

I am trying to do a chart. My database has created_date. I am getting product data every day about 150 times and I want to see a daily increase and decrease of my data. I have no problem with my front end and Django-template (I try manual data and it works well) I just want to see the last 7 days chart.
When I use Products.objects.filter(created_dates=days) filter method I am getting empty Queryset.
I already try created_dates__gte=startdate,created_dates__lte=enddate it return empty Queryset to.
I also try created_dates__range to there is no answer too.
I just get data from created_dates__gte=days but I don't want these data.
view.py
from datetime import date,timedelta
import datetime
def data_chart(request):
data = []
week_days = [datetime.datetime.now().date()-timedelta(days=i) for i in range (1,7)]
for days in week_days:
product_num = Products.objects.filter(created_dates=days)
date =days.strftime("%d.%m")
item = {"day": date,"value":len(product_num)}
data.append(item)
return render(request, 'chartpage.html', {'data': data})
In my database, I have thousands of data and my daily data about 150. My created_dates column format like this.
created_dates col:
2020-10-19 09:39:19.894184
So what is wrong with my code?. Could you please help?
You are trying to compare DateTimeField type (created_dates) with Date type (week_days is list of days) so maybe You should try __date lookup.
product_num = Products.objects.filter(created_dates__date=days)
https://docs.djangoproject.com/en/3.0/ref/models/querysets/#date
Furthermore maybe You should consider start using Count() database function with group by instead of iterating over days.
Here is great explanation:
https://stackoverflow.com/a/19102493/5160341
You should be able to do this with a single aggregation query:
import datetime
from django.db.models import Count
def data_chart(request):
cutoff = datetime.date.today() - datetime.timedelta(days=7)
raw_data = (
Products.objects.filter(created_dates__gte=cutoff)
.values_list("created_dates__date")
.annotate(count=Count("id"))
.values_list("created_dates__date", "count")
)
data = [{"day": str(date), "value": value} for (date, value) in raw_data]
return render(request, "chartpage.html", {"data": data})

For filtering data in Django, build dynamic query for multiple columns

I have to filter data from model based on the run time values. I am getting 5 values via query string. My querystring is like below:
http://127.0.0.1:8000/personal/search/?month=&year=&account=&deliveryManagedFrom=&marketmName=
So, I want to include all or none of the values in the filter so that it displays the desired result. Below is the filter query which I am writing:
sum_tt_count = NetworkRelatedInformation.objects.filter(month=month, year=year, account_id=account, account__deliveryManagedFrom=deliveryManagedFrom, account__marketName=market).aggregate(Sum('tt_count'))
totalttcount = sum_tt_count['tt_count__sum']
It is working well in case, all the values have been provided.
In case, if any value is blank, it should not consider that value and display output as per other filter criteria.
Pls suggest how to implement an OR filter with 5 data inputs. It is not necessary that all 5 data inputs have values . So the value can be None or the value in the querystring
Filter the request for non-empty values and then use dictionary expansion to do the query.
q = {k:v for k, v in request.GET.items() if v}
sum_tt_count = NetworkRelatedInformation.objects.filter(**q).aggregate(Sum('tt_count'))
You can do it using Q object
from django.db.models import Q
NetworkRelatedInformation.objects.filter(Q(month__isnull=True) | Q(month=month), Q(year__isnull=True) | Q(year=year)).aggregate(Sum('tt_count'))
For handling the None values i have to explicitly write the below code.
account = request.GET.get('account')
if account is '':
account = None
month = request.GET.get('month')
if month is '':
month = None
year = request.GET.get('year')
if year is '':
year = None
sum_alarm_count = NetworkRelatedInformation.objects.filter(Q(month=month) | Q(year=year) | Q(account_id=account)) \
.aggregate(Sum('alarm_count'))
totalalarmcount = sum_alarm_count['alarm_count__sum']

Python Pygal chart pulling data from database not matching values to labels

I am working on my first project and I am using Pygal to visualize some data from a database.
I am using the latest version of Python (3.6.5), Flask, Pygal and my IDE is Pycharm
The project is a simple budget application in which one enters the planned budget for a monthly expenditure and then the actual amounts for that expense/item (e.g. monthly car expenses, like gas).
I use Pygal to show 2 bar charts. The first one (which works as intended) shows a total planned amounts vs total actual amounts:
The second chart shows the planned vs actual per expense/item (e.g. monthly car expenses, like gas)
The issue I am facing is that the chart mixes up the labels and amounts. They show up in the chart based on the order they are entered, not on the item type.
For example:
In the above image, the are 3 items: Masina (car), Salariu (salary) and Economii (savings).
The amount represented by the blue column (the actual amount) should show up under the label Economii, not under Masina and it should not matter that I have entered it as the first actual in the database.
Also, adding more actual amounts for the same expenses item (Economii in our case) in the database simply adds more columns and it does not sum it up on the same column:
This is the database query function I am using:
def GraphData():
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(BASE_DIR, 'budget_db.db')
with sqlite3.connect(db_path) as db:
c = db.cursor()
d = db.cursor()
c.execute('SELECT title, category, name, planned_amount_month FROM Post')
d.execute('SELECT title_actual, category_actual, actual_amount_name, actual_amount FROM ActualPost')
data_planned = c.fetchall()
data_actual = d.fetchall()
return data_planned, data_actual
Below is the route I have created for both of the charts. The labels are pulled from the title_planned list and I have a feeling that the issue I am facing is because I am creating lists and I am appending them.
I think I should create dictionaries, but I have not idea how without messing everything else up:
#posts.route("/home")
def graphing():
data_planned, data_actual = GraphData()
title_planned = []
value_planned = []
title_actual = []
value_actual = []
for planned_row in data_planned:
title_planned.append(planned_row[2])
value_planned.append(planned_row[3])
for actual_row in data_actual:
title_actual.append(actual_row[2])
value_actual.append(actual_row[3])
graph = pygal.Bar(title=u'Total Planned Values vs Total Actual Values')
graph.add('Planned', [{'value': sum(value_planned), 'label': 'Total for Planned Budget:'}])
graph.add('Actual', [{'value': sum(value_actual), 'label': 'Total for Actual Amounts:'}])
graph_data = graph.render_data_uri()
graph_all = pygal.Bar(title=u'Planned Budget per item vs Actual Amounts per item')
graph_all.x_labels = title_planned
graph_all.add('Planned', value_planned)
graph_all.add('Actual', value_actual)
graph_all_data = graph_all.render_data_uri()
return render_template('home.html', graph_data=graph_data, graph_all_data=graph_all_data)
Edit:
I have been trying to do it using 2 dictionaries in the route with the expense item as dict key (title_planned_sum / title_actual_sum) and the amount as dict value (value_planned_sum / value_actual_sum):
tv_planned = dict(zip(title_planned, value_planned))
tv_planned_sum = {title_planned_sum: sum(value_planned_sum) for title_planned_sum, value_planned_sum in tv_planned.items()}
tv_actual = dict(zip(title_actual, value_actual))
tv_actual_sum = {title_actual_sum: sum(value_actual_sum) for title_actual_sum, value_actual_sum in tv_actual.items()}
Here is the full route:
#posts.route("/home")
def graphing():
data_planned, data_actual = GraphData()
title_planned = []
value_planned = []
title_actual = []
value_actual = []
for planned_row in data_planned:
title_planned.append(planned_row[2])
value_planned.append(planned_row[3])
for actual_row in data_actual:
title_actual.append(actual_row[2])
value_actual.append(actual_row[3])
tv_planned = dict(zip(title_planned, value_planned))
tv_planned_sum = {title_planned_sum: sum(value_planned_sum) for title_planned_sum, value_planned_sum in tv_planned.items()}
tv_actual = dict(zip(title_actual, value_actual))
tv_actual_sum = {title_actual_sum: sum(value_actual_sum) for title_actual_sum, value_actual_sum in tv_actual.items()}
graph = pygal.Bar(title=u'Total Planned Values vs Total Actual Values')
graph.add('Planned', [{'value': sum(value_planned), 'label': 'Total for Planned Budget:'}])
graph.add('Actual', [{'value': sum(value_actual), 'label': 'Total for Actual Amounts:'}])
graph_data = graph.render_data_uri()
graph_all = pygal.Bar(title=u'Planned Budget per item vs Actual Amounts per item')
graph_all.x_labels = title_planned
graph_all.add('Planned', tv_planned_sum)
graph_all.add('Actual', tv_actual_sum)
graph_all_data = graph_all.render_data_uri()
return render_template('home.html', graph_data=graph_data, graph_all_data=graph_all_data)
But of course, now I am getting this debug error:
TypeError: 'float' object is not iterable
And this is because, in the 2 dictionaries I am trying to work with, I am trying to sum the values for each key that receives multiple values with sum(value_planned_sum).
I got it working!
In the end I realized I was over-complicating my question, so I pulled out a pen and paper and started doing some pseudo-code to see where was the first step in my code where I could comfortably calculate the floats sum that was causing me head-aches.
Step 1: In my route, I could create a list containing nested tuples (with 2 elements each: str and float)
Step 2: Now, if some of the tuples had the same elements on index [0], how do I sum the float elements on index [1]?
So, I asked the this question on reddit.com/r/learnpython and the user diesch gave me an idea that I could use successfully: to import the itertools package and from it, to use groupby().
Here is how my route code looks now:
#posts.route("/home")
def graphing():
data_planned, data_actual = GraphData()
title_planned = []
value_planned = []
title_actual = []
value_actual = []
planned = []
actual = []
for planned_row in data_planned:
title_planned.append(planned_row[2])
value_planned.append(planned_row[3])
planned_list = zip(title_planned, value_planned)
for key, group in itertools.groupby(sorted(planned_list), lambda x: x[0]):
asum = 0
for i in group:
asum += i[1]
planned.append((key, asum))
planned_dict = dict(planned)
for actual_row in data_actual:
title_actual.append(actual_row[2])
value_actual.append(actual_row[3])
actual_list = zip(title_actual, value_actual)
for key, group in itertools.groupby(sorted(actual_list), lambda x: x[0]):
asum = 0
for i in group:
asum += i[1]
actual.append((key, asum))
actual_dict = dict(actual)
graph = pygal.Bar(title=u'Total Planned Values vs Total Actual Values')
graph.add('Planned', [{'value': sum(value_planned), 'label': 'Total for Planned Budget:'}])
graph.add('Actual', [{'value': sum(value_actual), 'label': 'Total for Actual Amounts:'}])
graph_data = graph.render_data_uri()
graph_all = pygal.Bar(title=u'Planned Budget per item vs Actual Amounts per item')
graph_all.x_labels = title_planned
graph_all.add('Planned', planned_dict)
graph_all.add('Actual', actual_dict)
graph_all_data = graph_all.render_data_uri()
return render_template('home.html', graph_data=graph_data, graph_all_data=graph_all_data)

Custom x-Axis name from different fields in django-chartit

I have model like
class MyModel(models.Model):
date = models.DateField()
type = models.CharField(max_length=10)
n_1 = models.IntegerField()
n_2 = models.IntegerField()
Date is unique and type is not.
I want to create chart with X-Axis labels like "Type (Date)" i.e. "FirstType (2014-10-02)", so I want to combine data from two model fields in axis label. Can you please advice me how can I do it?
It is not required to pass the model objects directly to chartit. You can transform them into dicts which then have the appropriate values:
data = [
{'label': "{0} ({1})".format(o.date, o.type), 'value': o.n_1}
for o in MyModel.objects.all()
]
See the documentation for the library you have chosen to use for more information.

how to edit the form with data from the models

views.py
Editrow = KEBReading.objects.get(id=id)
print Editrow.datetime_reading
event_full_datetime=datetime(Editrow.datetime_reading.year,
Editrow.datetime_reading.month,
Editrow.datetime_reading.day,
Editrow.datetime_reading.hour,
Editrow.datetime_reading.minute,
Editrow.datetime_reading.second)
date = event_full_datetime.year, event_full_datetime.month, event_full_datetime.day
time = event_full_datetime.hour, event_full_datetime.minute
print date
print time
form = KEBReading_form(instance=Editrow)
in my models i have a datetime field. but in my forms i have a separate date and time field. but when i want to edit a row my variable Editrow has datetime value how do i populate the form with separate date and time while passing the instance(Editrow)
form = KEBReading_form(instance=Editrow)
First, you should understand how to use widgets for form fields. Then, just use SplitDateTimeWidget.

Categories

Resources