How can i use ModelName.objects.aggregate(Sum('field_name')) here? - python

I need to use Sum() instead of
if name in ingredients.keys():
ingredients[name] += units
else:
ingredients[name] = units
but I have no idea how, because I have a long chain of relations. Here is my code:
def shopping_list_ingredients(request):
shopping_list = ShoppingList.objects.filter(user=request.user).all()
ingredients = {}
for item in shopping_list:
for x in item.recipe.recipe_ingredient.all():
name = f'{x.ingredient.title} ({x.ingredient.unit})'
units = x.count
if name in ingredients:
ingredients[name] += units
else:
ingredients[name] = units
download = []
for key, units in ingredients.items():
download.append(f'{key} - {units} \n')
return download

def shopping_list_ingredients(request):
shopping_list = ShoppingList.objects.filter(user=request.user).all()
ingredients = {}
for item in shopping_list:
#if item.recipe.recipe_ingredient.all().ingredient.unit is dict
values = item.recipe.recipe_ingredient.all().ingredient.unit.values()
total = sum(values)
#if it's list
total = sum(item.recipe.recipe_ingredient.all().ingredient.unit)

Related

Why won't these class attributes multiply?

I have to prompt the user for 2 items. For each Item the program needs to know the name, price, and quantity. Once received it outputs
'{Item name} {Item quantity} # ${Item price} = {Item total}'
The item total takes the price and multiplies it by the quantity. However I can't see the total in the output?'
class Item:
def __init__(self, name = 'none', price = 0, quantity = 0, total = 0):
self.item_name = name
self.item_price = price
self.item_quantity = quantity
self.total = price * quantity
def __add__(self, other):
return self.total + other.total
def print_item_cost(self):
return print('{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total))
#Grab first item
item_1 = Item()
print('Item 1\nEnter the item name:')
item_1.item_name = input()
print('Enter the item price:')
item_1.item_price = input()
print('Enter the item quanity:')
item_1.item_quantity = input()
#Grab second item
item_2 = Item()
print('\nItem 2\nEnter the item name:')
item_2.item_name = input()
print('Enter the item price:')
item_2.item_price = input()
print('Enter the item quanity:')
item_2.item_quantity = input()
#Output cost
print('TOTAL COST\n', item_1.print_item_cost(), item_2.print_item_cost())
You're creating your items using the empty parameter list:
item_1 = Item()
so that default values are used in __init__: price = 0, quantity = 0, thus self.total is calculated as 0. Later, you change price and quantity properties of existing object:
item_2.item_price = input()
item_2.item_quantity = input()
but this does not change total. What you probably should do is:
#Grab first item
print('Item 1')
name = input('Enter the item name:')
price = input('Enter the item price:')
quantity = input('Enter the item quanity:')
item_1 = Item(name, price, quantity)
(and the same for item_2)
What you are doing in the following function is that you are returning a print of the statement. What you need to do is just return the statement and the print statment from where you made the call will print the returned output. Change from this
def print_item_cost(self):
return print('{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total))
To this
def print_item_cost(self):
return('{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total))
Edit:
Your total value does not change and stays the same once its initialized so add a new calculate total method
def calculate_total(self):
self.total = self.price * self.quantity
And call this method calculate_total() for each object to recalculate the total
A couple of things, you are setting the total price during initialization, therefore you have to set the item_price and item_quantity at __init__. Another thing when you accept number input from user you have to parse it appropriate data type, because by default it's string.
This would work as you intended it
class Item:
def __init__(self, name = 'none', price = 0, quantity = 0, total = 0):
self.item_name = name
self.item_price = price
self.item_quantity = quantity
self.total = price * quantity
def __add__(self, other):
return self.total + other.total
def print_item_cost(self):
return ('{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total))
#Grab first item
print('Item 1\nEnter the item name:')
item_name = input()
print('Enter the item price:')
item_price = int(input())
print('Enter the item quanity:')
item_quantity = int(input())
item_1 = Item(item_name, item_price, item_quantity)
#Grab second item
print('\nItem 2\nEnter the item name:')
item_name = input()
print('Enter the item price:')
item_price = int(input())
print('Enter the item quanity:')
item_quantity = int(input())
item_2 = Item(item_name, item_price, item_quantity)
#Output cost
print('TOTAL COST\n', item_1.print_item_cost(), item_2.print_item_cost())
total must be calculated after you provide the actual values
input() returns string in python 3, should be converted to integers (or float)
changed the name of the print_item_cost function and removed print from the function; either print or return string but not both
class Item:
def __init__(self, name = 'none', price = 0, quantity = 0, total = 0):
self.item_name = name
self.item_price = price
self.item_quantity = quantity
# self.total = price * quantity
def total(self):
return self.item_price * self.item_quantity
def __add__(self, other):
return self.total + other.total
def item_cost_string(self):
return '{} {} # ${} = ${}'.format(self.item_name,
self.item_price,
self.item_quantity,
self.total())
#Grab first item
item_1 = Item()
print('Item 1\nEnter the item name:')
item_1.item_name = input()
print('Enter the item price:')
item_1.item_price = int(input())
print('Enter the item quanity:')
item_1.item_quantity = int(input())
#Grab second item
item_2 = Item()
print('\nItem 2\nEnter the item name:')
item_2.item_name = input()
print('Enter the item price:')
item_2.item_price = int(input())
print('Enter the item quanity:')
item_2.item_quantity = int(input())
#Output cost
print('TOTAL COST\n', item_1.item_cost_string(), '\n', item_2.item_cost_string())
When you do something like
self.total = price * quantity
It uses the current values of price and quantity to set the value of self.total when this line is executed. This does not define a formula that will automatically update self.total when the value of price or quantity changes.

User input from numbered list and returns the list

I have this program where it reads and displays a list, separated by commas. I also have each list numbered. I ask the user to select a number from that list. From the number they chose, I'm trying to return the actual list value that goes with it. I am having trouble returning just the list value for some reason.
So if I were to choose '2' (integer) as my input, it should return Customers.
Here is my code so far:
list1 = ['ID', 'Customers', 'Employees', 'Orders']
def display(list1):
counter = 0
record = []
for tables in list1:
counter += 1
record.append(tables)
print("%s. %s" % (counter, tables))
return record
def get_list(record):
print("\nPick a number:")
choose = input()
choose = int(choose)
x = []
if choose in record:
x.append(record)
print(x)
record = display(list1)
get_list(record)
You can use dict instead.
list1 = ['ID', 'Customers', 'Employees', 'Orders']
def display(list1):
counter = 0
record = {}
for tables in list1:
counter += 1
record[counter] = tables
print("%s. %s" % (counter, tables))
return record
def get_list(record):
print("\nPick a number:")
choose = input()
choose = int(choose)
if choose in record:
print(record[choose])
record = display(list1)
get_list(record)
Output:
1. ID
2. Customers
3. Employees
4. Orders
Pick a number:
2
Customers
And your record variable will look like:
{1: 'ID', 2: 'Customers', 3: 'Employees', 4: 'Orders'}
You need to use the choose index to select the item from record key via
print(record[int(choose)-1])
So the code will change to
list1 = ['ID', 'Customers', 'Employees', 'Orders']
def display(list1):
counter = 0
record = []
for tables in list1:
counter += 1
record.append(tables)
print("%s. %s" % (counter, tables))
return record
def get_list(record):
print("\nPick a number:")
choose = input()
choice = int(choose)-1
print(record[choice])
record = display(list1)
get_list(record)
You can also simplify your code as follows
li = ['ID', 'Customers', 'Employees', 'Orders']
def display(li):
#Iterate through the list using enumerate and print
for idx, tables in enumerate(li):
print("%s. %s" % (idx+1, tables))
def get_list(li):
choose = int(input("\nPick a number:"))-1
#If choose is not a valid index in list, print error and return empty string
if choose < 0 or choose > (len(li)-1):
print('Invalid Choice')
return ''
#Else return chosen string
return li[choose]
display(li)
print(get_list(li))
The output will then be
1. ID
2. Customers
3. Employees
4. Orders
Pick a number:
3
Employees
1. ID
2. Customers
3. Employees
4. Orders
Pick a number:0
Invalid Choice
You simply access the list index entered by the user
list1 = ['ID', 'Customers', 'Employees', 'Orders']
def display(list1):
counter = 0
record = []
for tables in list1:
counter += 1
record.append(tables)
print("%s. %s" % (counter, tables))
return record
def get_list(record):
print("\nPick a number:")
choose = input()
choose = int(choose)
print(record[choose-1])
record = display(list1)
get_list(record)
You can do this by single function see below code:
list1 = ['ID', 'Customers', 'Employees', 'Orders']
def display(list1):
maxnum = len(list1)
print("\nPick a number from 1 to %s:"%(maxnum+1))
choose = input()
choose = int(choose)
if choose > maxnum:
print("enter number less than %s"%(maxnum+1))
return
choose = choose-1
print (list1[choose])
display(list1)
In Python, List indexing started with 0, for getting required output you need to subtract input value by 1.
list1 = ['ID', 'Customers', 'Employees', 'Orders']
def display(list1):
counter = 0
record = []
for tables in list1:
counter += 1
record.append(tables)
print("%s. %s" % (counter, tables))
return record
def get_list(record):
print("\nPick a number:")
choose = input()
choose = int(choose)
print(record[choose - 1])
record = display(list1)
get_list(record)
Output
ID
Customers
Employees
Orders
Pick a number:
2
Customers

Adding and sorting elements of multidimensional list in a certain way

I am working on this
class Product:
def __init__(self, date=0, product_name=0,qty=0,supplier=0):
self.product_name = product_name
self.date = date
self.qty = qty
self.supplier= supplier
self.my_list = []
def purchase(self, date, product_name, qty, supplier_name ):
self.my_list.append([supplier_name,date,product_name,qty])
def calculation(self):
for i in self.my_list:
print(i)
choice=None
p=Product()
while True:
choice=int(input("1 for the add record\n2 For the display result.\n"))
if choice == 1:
product_name=input("Enter the product name\n")
qty = int(input("Enter the qty.\n"))
date= input("Enter the date")
supplier_name = input("Enter the supplier name.\n ")
p.purchase(date,product_name,qty, supplier_name)
elif choice == 2:
p.calculation()
after executing this i have added data like this... when we choose 2 number option i am having data like this
eg.[supplier_name, date, product_name, quantity]
[supplierA, 2019-01-01, pencil, 20]
[supplierA, 2019-01-01, pencil, 30]
[supplierA, 2018-02-02, pen, 20]
[supplierB, 2017-02-02, scale, 10]
[supplierB, 2017-10-10, scale, 20]
[supplierC, 2019-01-01, pencil,10]
[supplierC, 2019-01-01, pencil,10]
[supplierC, 2019-01-01, pencil,10]
I want to filter this data in a way that if date and product name are same, its quantity should be added. and it must be group by supplier name .I means only individual supplier's date and qty be added in
expected output is
Supplier A:
[2019-01-01, pencil, 50]
[2018-02-02, pen, 20]
Supplier B:
[2017-02-02, scale, 10]
[2017-10-10, scale, 20]
Supplier C:
[2019-01-01, pencil, 30]
i have tried with lambda and filter but could not able to make it. any idea how to make it possible?
this will work i think:
class Product:
#init
def __init__(self, date=0, product_name=0,qty=0,supplier=0):
self.product_name = product_name
self.date = date
self.qty = qty
self.supplier= supplier
#make self.my_dict
self.my_dict={}
def purchase(self, date, product_name, qty, supplier_name ):
#make a new key if needing
try:
#add the data to the list
self.my_dict[supplier_name].append([date,product_name,qty])
except:
#make a new list with data
self.my_dict[supplier_name] = [[date,product_name,qty]]
def calculation(self):
#getting keys
for i in list(self.my_dict):
#print key
print(i)
#get items
for k in self.my_dict[i]:
#print item
print(k)
choice=None
p=Product()
while True:
choice=int(input("1 for the add record\n2 For the display result.\n"))
if choice == 1:
product_name=input("Enter the product name\n")
qty = int(input("Enter the qty.\n"))
date= input("Enter the date")
supplier_name = input("Enter the supplier name.\n ")
p.purchase(date,product_name,qty, supplier_name)
elif choice == 2:
p.calculation()
you can also modify existing purchase method :
def purchase(self, date, product_name, qty, supplier_name ):
for item in self.my_list:
if item[2] == product_name and item[1] == date and item[0] == supplier_name:
item[3] = item[3] + qty
return
self.my_list.append([supplier_name,date,product_name,qty])
You could do this:
from itertools import groupby
from operator import itemgetter
def calculation(self):
# sort and group items by supplier_name, date, product_name
x = groupby(sorted(self.my_list, key=itemgetter(slice(None, 3))), itemgetter(slice(None, 3)))
# sum the quantities
y = [i + [sum(map(itemgetter(3), j))] for i, j in x]
# group items by supplier
z = [(i, list(map(itemgetter(slice(1, None)), j))) for i, j in groupby(y, itemgetter(0))]
# output
for supplier, values in z:
print("{0}:".format(supplier))
print("\n".join(map(str, values)))

Python program gives me range of items instead of most/least frequent item

I've created a GUI using wxFormBuilder that should allow a user to enter the names of "visitors to a business" into a list and then click one of two buttons to return the most frequent and least frequent visitors to the business. Unfortunately, when I click the button, it gives me the range of visitors, rather than the name of the most/least frequent visitor. I've attached a screenshot of the GUI I've created to help add a little clarity to the issue and have appended my code. Thanks in advance for your help with this.
Code:
import wx
import myLoopGUI
class MyLoopFrame(myLoopGUI.MyFrame1):
def __init__(self, parent):
myLoopGUI.MyFrame1.__init__(self, parent)
def clkAddData(self,parent):
if len(self.txtAddData.Value) != 0:
try:
myname = str(self.txtAddData.Value)
self.listMyData.Append(str(myname))
except:
wx.MessageBox("This has to be a name!")
else:
wx.MessageBox("This can't be empty")
def clkFindMost(self, parent):
word_amount = range(self.listMyData.GetCount())
numberofitems = self.listMyData.GetCount()
unique_words = []
for word in word_amount:
if word not in unique_words:
unique_words += [word]
word_frequencies = []
for word in unique_words:
word_frequencies += [float(word_amount.count(word))/len(word_amount)]
max_index = 0
frequent_words =[]
for i in range(len(unique_words)):
if word_frequencies[i] >= word_frequencies[max_index]:
max_index = i
frequent_words += [unique_words[max_index]]
self.txtResults.Value = str(frequent_words)
myApp = wx.App(False)
myFrame = MyLoopFrame(None)
myFrame.Show()
myApp.MainLoop()
#Yep_It's_Me was kind enough to chime in and offer another way forward:
import wx
import myLoopGUI
import commands
class MyLoopFrame(myLoopGUI.MyFrame1):
def __init__(self, parent):
myLoopGUI.MyFrame1.__init__(self, parent)
def clkAddData(self,parent):
if len(self.txtAddData.Value) != 0:
try:
myname = str(self.txtAddData.Value)
self.listMyData.Append(str(myname))
except:
wx.MessageBox("This has to be a name!")
else:
wx.MessageBox("This can't be empty")
def clkFindMost(self, parent):
self.listMyData = []
unique_names = set(self.listMyData)
frequencies = {}
for name in unique_names:
if frequencies.get[name]:
frequencies[name] += 1
else:
frequencies[name] = 0
v = list(frequencies.values())
k = list(frequencies.keys())
self.txtResults.Value = k.index(max(v))
def clkFindLeast(self, parent):
unique_names = set(self.listMyData)
frequencies = {}
for name in unique_names:
if frequencies.get(name):
frequencies[name] += 1
else:
frequencies[name] = 0
v = list(frequencies.values())
k = list(frequencies.keys())
self.txtResults.Value = k.index(min(v))
myApp = wx.App(False)
myFrame = MyLoopFrame(None)
myFrame.Show()
myApp.MainLoop()
But now I'm receiving a Value error:
ValueError: max() arg is an empty sequence
In relation to this line:
**self.txtResults.Value = k.index(max(v))**
Never mind! #Yep_It's_Me offered a fantastic solution. Thank you so much.
range(self.listMyData.getCount())
will give a list of integers from 0 to self.listMyData.getCount().
You want to count the occurrences of each unique item in self.listMyData yes?
Update: From the comments, self.listMyData is the textbox, not the list of names, get the list of names from the textbox as shown in this answer. Updated code.
# Get the list of names from the textbox
name_list = self.listMyData.GetStrings()
unique_names = set(name_list)
frequencies = {}
for name in unique_names:
if frequencies.get(name):
frequencies[name] += 1
else:
frequencies[name] = 0
Now frequencies will be a dict of names, with the value being the frequency of that name in self.listMyData
Then you can do:
counts = list(frequencies.values())
names = list(frequencies.keys())
max_count_index = counts.index(max(counts))
min_count_index = counts.index(min(counts))
most_frequent = names[max_count_index]
least_frequent = names[min_count_index]
Which will assign a list of integers to counts which represent the frequency of each name in names. Getting the index of both the maximum and minimum in counts tells us in index in names which has the most frequent and least frequent names, respectively. Then we can access them in the normal way.

Python trying to Refactor (DRY out) a long Control Flow

I am grabbing a lot of data from and SQL query that takes a long time to run. Since the SQL query takes so long to run, I am grabbing the data from the database in its most granular form. I then cycle through this data once and aggregate it in the forms that are useful to me.
My problem is that I am repeating myself over and over again. However, I am not sure of the best way to refactor this control flow. Thanks in advance!
def processClickOutData(cls, raw_data):
singles = {}
total={}
absolute_total = 0
channels = {}
singles_true = {}
total_true={}
channels_true = {}
absolute_total_true = 0
list_channels = set([])
list_tids = set([])
total_position = {}
total_position_true = {}
tid_position = {}
channel_position = {}
channel_position_true = {}
tid_position_true = {}
for row in raw_data:
gap=row[0]
count=row[1]
tid=row[2]
prefered=row[3]
channel=row[4]
position=row[5]
list_channels.add(channel)
list_tids.add(tid)
absolute_total += int(count)
if total.has_key(gap):
total[gap] += count
else:
total[gap] = count
if singles.has_key(gap) and singles[gap].has_key(tid):
singles[gap][tid] += count
elif singles.has_key(gap):
singles[gap][tid] = count
else:
singles[gap] = {}
singles[gap][tid] = count
if channels.has_key(gap) and channels[gap].has_key(channel):
channels[gap][channel] += count
elif channels.has_key(gap):
channels[gap][channel] = count
else:
channels[gap] = {}
channels[gap][channel] = count
if total_position.has_key(position):
total_position[position] += count
else:
total_position[position] = count
if tid_position.has_key(position) and tid_position[position].has_key(tid):
tid_position[position][tid] += count
elif tid_position.has_key(position):
tid_position[position][tid] = count
else:
tid_position[position] = {}
tid_position[position][tid] = count
if channel_position.has_key(position) and channel_position[position].has_key(channel):
channel_position[position][channel] += count
elif channel_position.has_key(position):
channel_position[position][channel] = count
else:
channel_position[position] = {}
channel_position[position][channel] = count
if prefered == 0:
absolute_total_true += count
if total_true.has_key(gap):
total_true[gap] += count
else:
total_true[gap] = count
if singles_true.has_key(gap) and singles_true[gap].has_key(tid):
singles_true[gap][tid] += count
elif singles_true.has_key(gap):
singles_true[gap][tid] = count
else:
singles_true[gap] = {}
singles_true[gap][tid] = count
if channels_true.has_key(gap) and channels_true[gap].has_key(channel):
channels_true[gap][channel] += count
elif channels_true.has_key(gap):
channels_true[gap][channel] = count
else:
channels_true[gap] = {}
channels_true[gap][channel] = count
if total_position_true.has_key(position):
total_position_true[position] += count
else:
total_position_true[position] = count
if tid_position_true.has_key(position) and tid_position_true[position].has_key(tid):
tid_position_true[position][tid] += count
elif tid_position_true.has_key(position):
tid_position_true[position][tid] = count
else:
tid_position_true[position] = {}
tid_position_true[position][tid] = count
if channel_position_true.has_key(position) and channel_position_true[position].has_key(channel):
channel_position_true[position][channel] += count
elif channel_position_true.has_key(position):
channel_position_true[position][channel] = count
else:
channel_position_true[position] = {}
channel_position_true[position][channel] = count
final_values = {"singles" : singles, "singles_true" : singles_true, "total" : total, "total_true": total_true, "absolute_total": absolute_total, "absolute_total_true": absolute_total_true, "channel_totals" : channels, "list_channels" : list_channels, "list_tids" : list_tids, "channel_totals_true" : channels_true,
"total_position" : total_position, "total_position_true" : total_position_true, "tid_position" : tid_position, "channel_position" : channel_position, "tid_position_true" : tid_position_true, "channel_position_true" : channel_position_true }
return final_values
The entire structure you're using to store the data is probably wrong, but since I don't know how you're using it, I can't help you with that.
You can get rid of all of those has_key() calls by using collections.defaultdict. Note thedict.has_key(key) is deprecated anyway, you should just use key in thedict instead.
Look at how I change the for loop too -- you can assign to names right in the for statement, no need to do it separately.
from collections import defaultdict
def processClickOutData(cls, raw_data):
absolute_total = 0
absolute_total_true = 0
list_channels = set()
list_tids = set()
total = defaultdict(int)
total_true = defaultdict(int)
total_position = defaultdict(int)
total_position_true = defaultdict(int)
def defaultdict_int():
return defaultdict(int)
singles = defaultdict(defaultdict_int)
singles_true = defaultdict(defaultdict_int)
channels = defaultdict(defaultdict_int)
channels_true = defaultdict(defaultdict_int)
tid_position = defaultdict(defaultdict_int)
tid_position_true = defaultdict(defaultdict_int)
channel_position = defaultdict(defaultdict_int)
channel_position_true = defaultdict(defaultdict_int)
for gap, count, prefered, channel, position in raw_data:
list_channels.add(channel)
list_tids.add(tid)
absolute_total += count
total[gap] += count
singles[gap][tid] += count
channels[gap][channel] += count
total_position[position] += count
tid_position[position][tid] += count
channel_position[position][channel] += count
if prefered == 0:
absolute_total_true += count
total_true[gap] += count
singles_true[gap][tid] += count
channels_true[gap][channel] += count
total_position_true[position] += count
tid_position_true[position][tid] += count
channel_position_true[position][channel] += count
final_values = {"singles" : singles, "singles_true" : singles_true, "total" : total, "total_true": total_true, "absolute_total": absolute_total, "absolute_total_true": absolute_total_true, "channel_totals" : channels, "list_channels" : list_channels, "list_tids" : list_tids, "channel_totals_true" : channels_true,
"total_position" : total_position, "total_position_true" : total_position_true, "tid_position" : tid_position, "channel_position" : channel_position, "tid_position_true" : tid_position_true, "channel_position_true" : channel_position_true }
return final_values
What this does is automatically fill in the correct default values if the keys don't exist. You've got two kinds here. Where you're adding ints, you want to start with 0 if it doesn't exist -- that's what int returns, hence defaultdict(int). Where you're adding a dictionary that adds ints, you need to use a function that returns a defaultdict(int) which is what defaultdict_int does.
Edit: Suggested alternate dictionary structure:
position = defaultdict(lambda: defaultdict(defaultdict_int))
gap = defaultdict(lambda: defaultdict(defaultdict_int))
absolute_total = 0
for gap, count, prefered, channel, position in raw_data:
absolute_total += count
posd = position[position]
posd.setdefault('total', 0)
posd['total'] += count
posd['tid'][tid] += count
posd['channel'][channel] += count
gapd = gap[gap]
gapd.setdefault('total', 0)
gapd['total'] += count
gapd['tid'][tid] += count
gapd['channel'][channel] += count
Do the same with the _true versions as well, and you've gone from 12 dicts to 4.

Categories

Resources