There is data stored in JSON file with the following structure:
goods_data = {"year" : {"month" : {"day" : {"01" : {"name" : "some item", "price" : "10.01", "category" : "root | first | sub first | twice sub first", "02" : {"name" : "another item", "price" : "10.99", "category" : "root | first | sub first"} }}}}}
The task is to fill a QTreeView
in which categories must have tree structure (the first column). The second column must contain the total sum of purchases and percentage of the year for current category. The other columns are suppose to contain the total sum for each month for the current category.
The script I have written does what it is supposed to do but it takes too much time for the given data.
I understand that I include too many iterations inside each iteration, but I don't know the other way to do it. I think there is no reason to put all data that is being used, so I just have included the simplified dictionary.
How can I do it in more efficient and pythonic way?
#!/usr/bin/env python -tt
# -*- coding: utf-8 -*-
#from PySide.QtGui import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSlot
import time
import types
import json
import sys
reload(sys)
sys.setdefaultencoding('utf8')
from datetime import datetime
import collections
import operator
import sip
sip.setapi('QVariant', 2)
goods_data = {"2015" :
{
"09" : {
"01" : {
"1" : {
"name" : "Red apples", "price" : "10.01", "category" : "Food and Grocery | Fruit and Vegetables | Fruit | Apples"
},
"2" : {
"name" : "Green apples", "price" : "10.99", "category" : "Food and Grocery | Fruit and Vegetables | Fruit | Apples"
}
},
"15" : {
"1" :
{
"name" : "Blue apples", "price" : "10.01", "category" : "Food and Grocery | Fruit and Vegetables | Fruit | Apples"
},
"2" : {
"name" : "Black apples", "price" : "10.99", "category" : "Food and Grocery | Fruit and Vegetables | Fruit | Apples"
}}
},
"10" : {
"01" : {
"1" : {
"name" : "Oranges", "price" : "10", "category" : "Food and Grocery | Fruit and Vegetables | Fruit | Oranges"
},
"2" : {
"name" : "Oranges", "price" : "10", "category" : "Food and Grocery | Fruit and Vegetables | Fruit | Oranges"
}
},
"15" : {
"1" :
{
"name" : "Oranges", "price" : "10", "category" : "Food and Grocery | Fruit and Vegetables | Fruit | Oranges"
},
"2" : {
"name" : "Apples", "price" : "10", "category" : "Food and Grocery | Fruit and Vegetables | Fruit | Apples"
}}
}
}
}
#gj = 'goods.json'
#with open(gj) as f:
#goods_data = json.load(f)
@pyqtSlot(float, float)
def percentage(part,whole):
if whole>0:
return 100.00* float(part)/float(whole)
else:
return 0
@pyqtSlot(float, float)
def perc_diff(prev,curr):
return((float(curr)-prev)/abs(prev))*100.00
@pyqtSlot(dict, dict)
def update(d, u):
'''
returns updated dictionary (initial dict, updating dict)
'''
for k, v in u.iteritems():
if isinstance(v, collections.Mapping):
r = update(d.get(k, {}), v)
d[k] = r
else:
d[k] = u[k]
return d
class MainFrame(QWidget):
def __init__(self):
#super(MainFrame, self).__init__(parent)
QWidget.__init__(self)
self.statusBar = QStatusBar()
self.expences_cat_tree_stat = QTreeView()
self.expences_cat_tree_stat.setAutoExpandDelay(0)
self.expences_cat_tree_stat_model = QStandardItemModel()
self.expences_cat_tree_stat.setModel( self.expences_cat_tree_stat_model )
MainWindow = QVBoxLayout(self)
MainWindow.addWidget(self.expences_cat_tree_stat)
MainWindow.addWidget(self.statusBar)
self.statusBar.showMessage("READY")
self.setLayout(MainWindow)
self.stat_cat_tree = self.load_stat_cat_tree(goods_data, self.expences_cat_tree_stat_model)
@pyqtSlot(QObject, dict, QObject, dict, int, QStandardItemModel)
def TreeCatStat(self, children, parent, cat_dict, year, model):
for child in sorted(children):
child_item = QStandardItem(child)
data_row = list([child_item])
# adding items without data to the row
for i in range(model.columnCount()):
data_row.append(QStandardItem())
parent.appendRow(data_row)
# creating the path
varforpath = child_item.parent()
list_for_path = list([child_item.text()])
while varforpath.parent() is not None:
list_for_path.append(varforpath.text())
varforpath = varforpath.parent()
cat_path_str = str()
cat_path_str = ' > '.join(reversed(list_for_path))
self.statusBar.showMessage(cat_path_str)
# iterating categories
for sbct in sorted(cat_dict):
cat_tot_year = float()
# if iterated category starts with path created from item
if sbct.startswith(cat_path_str):
# iterating months
for i in range(2, model.columnCount()):
cat_tot_month = float()
# inside each month iterating categories
for kpd in sorted(cat_dict):
# if iterated category inside the month starts with path from item
if kpd.startswith(cat_path_str):
# iterating month of categories that starts with path from item
for mm in sorted(cat_dict[kpd][year]):
# if name of the month from header matches name of the month from dictionary
if model.headerData(i, Qt.Horizontal) == QDate.fromString(mm, "M").toString("MMMM"):
# iterating dates of month
for dd in sorted(cat_dict[kpd][year][mm]):
# iterating numbers of purchases
for ii in sorted(cat_dict[kpd][year][mm][dd]):
# iterating names of purchases
for itn in sorted(cat_dict[kpd][year][mm][dd][ii]):
cat_tot_month += cat_dict[kpd][year][mm][dd][ii][itn]['sum']
cat_tot_year += cat_dict[kpd][year][mm][dd][ii][itn]['sum']
model.setData(model.indexFromItem(data_row[i]), cat_tot_month)
model.setData(model.indexFromItem(data_row[1]), cat_tot_year)
whole = float(data_row[1].text())
# adding percentage to months
for val in range(2, model.columnCount()):
part = model.indexFromItem(data_row[val]).data()
str_out = str()
if part is not None:
part = float(part)
if part > 0:
perc_str = format(percentage(part, whole), ".2f")
str_out = str(format(part, ".2f")) + " (" + perc_str + "%)"
model.setData(model.indexFromItem(data_row[val]), str_out )
if isinstance(children, types.DictType):
self.TreeCatStat(children[child], child_item, cat_dict, year, model)
@pyqtSlot(QObject, dict, QStandardItemModel)
def load_stat_cat_tree(self, data, model):
start=time.clock()
res = dict()
ch_y = 2015
tree = self.expences_cat_tree_stat
exp_cat_tree_header = list(["Year / Category", "Total"])
month_perc_item_list = list()
overall_spent = float()
prev_month_perc =float()
one_month_sum_list = list()
cat_dict = dict()
initial_dict_creation_time_start = time.clock()
for yy in sorted(data):
year_item = QStandardItem(yy)
one_year_sum = float()
for mm in sorted(data[yy]):
date = QDate.fromString(mm, "M")
chosenMonth = date.toString("MMMM")
month_item = QStandardItem(chosenMonth)
one_month_sum = float()
for dd in sorted(data[yy][mm]):
one_day_sum = float()
day_str = QDate.fromString(dd +" " + mm +" " + yy, "dd MM yyyy")
day_str_out = day_str.toString("d, dddd")
day_item = QStandardItem(day_str_out)
day_item.setToolTip(day_str.toString("d.MM.yy, dddd"))
for ii in sorted(data[yy][mm][dd]):
item_pr = float(data[yy][mm][dd][ii]["price"])
item_name=data[yy][mm][dd][ii]["name"]
#item_descr=data[yy][mm][dd][ii]["descr"]
item_cat = data[yy][mm][dd][ii]["category"].replace("|", ">")
path = item_cat
if path not in cat_dict:
cat_dict[path] = {yy: {mm: {dd: {ii: {item_name: {"sum": item_pr} }}}}}
else:
if yy not in cat_dict[path]:
cat_dict[path][yy] = {}
if mm not in cat_dict[path][yy]:
cat_dict[path][yy][mm] = {}
if dd not in cat_dict[path][yy][mm]:
cat_dict[path][yy][mm][dd] = {}
if ii not in cat_dict[path][yy][mm][dd]:
cat_dict[path][yy][mm][dd][ii] = {}
if item_name not in cat_dict[path][yy][mm][dd][ii]:
cat_dict[path][yy][mm][dd][ii][item_name] = {"sum": item_pr}
if path:
d = t = {} # t is my temporary dictionary
for i in path.split(" > "):
t[i] = {}
t = t[i]
dct = update(res,d)
else:
dct = res
one_month_sum += float(item_pr)
one_year_sum += float(item_pr)
if one_month_sum > 0:
exp_cat_tree_header.append(month_item.text())
one_month_sum_list.append( float(one_month_sum))
initial_dict_creation_time_end = time.clock()
initial_dict_creation_time_diff = str(initial_dict_creation_time_end - initial_dict_creation_time_start)
print "initial dict creation time for " + yy + " -> " + initial_dict_creation_time_diff
if one_year_sum > 0:
year_row_list = [year_item, QStandardItem(str(format(one_year_sum, ".2f")))]
for tm in one_month_sum_list:
year_row_list.append(QStandardItem(str(format(tm, ".2f")) + " (" + format(percentage (tm, one_year_sum), ".2f") + "%)" ))
self.expences_cat_tree_stat_model.appendRow(year_row_list)
model.setHorizontalHeaderLabels(exp_cat_tree_header)
self.TreeCatStat(res, year_item, cat_dict, yy, model)
tree.expandAll()
c = 0
while c < len(exp_cat_tree_header):
tree.resizeColumnToContents(c)
c=c+1
tree.collapseAll()
item_to_expand = year_item
tree.setExpanded(model.indexFromItem(item_to_expand), True)
tree.scrollTo(model.indexFromItem(item_to_expand))
end = time.clock()
#print "%.2gs" % (end - start)
print "calculations:", str(end - start)
self.statusBar.showMessage("Calculations:" + str(end - start))
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainFrame()
main.show()
main.move(app.desktop().screen().rect().center() - main.rect().center())
sys.exit(app.exec_())
goods_data
? You’re missing to closing}
plus your code fails withKeyError: 'descr'
when they are added. \$\endgroup\$ – 301_Moved_Permanently Nov 22 '15 at 11:25