From b2dde55f2ccf337e2b07aac8b62c24791c4975c9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 25 Sep 2023 20:59:39 +0530 Subject: [PATCH] refactor: ability to build and load tree from DB --- .../bisect_accounting_statements.js | 8 +- .../bisect_accounting_statements.json | 43 +++- .../bisect_accounting_statements.py | 227 ++++++++++++------ 3 files changed, 205 insertions(+), 73 deletions(-) diff --git a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js index 69bd56e73f..732b2b0f9c 100644 --- a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js +++ b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js @@ -14,15 +14,15 @@ frappe.ui.form.on("Bisect Accounting Statements", { frm.add_custom_button(__('Up'), () => frm.trigger("move_up") ); - frm.add_custom_button(__('Bisect'), () => - frm.trigger("bisect") + frm.add_custom_button(__('Build Tree'), () => + frm.trigger("build_tree") ); // frm.change_custom_button_type(__('Bisect'), null, 'primary'); }, - bisect(frm) { + build_tree(frm) { frm.call({ doc: frm.doc, - method: 'bisect', + method: 'build_tree', callback: (r) => { console.log(r); } diff --git a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json index b2f3c4bb90..e0b84adeeb 100644 --- a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json +++ b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json @@ -11,7 +11,14 @@ "column_break_qxbi", "to_date", "column_break_iwny", - "algorithm" + "algorithm", + "section_break_lwr2", + "current_from_date", + "column_break_uuic", + "current_to_date", + "section_break_zbty", + "current_node", + "tree" ], "fields": [ { @@ -38,13 +45,45 @@ { "fieldname": "column_break_iwny", "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_zbty", + "fieldtype": "Section Break" + }, + { + "fieldname": "tree", + "fieldtype": "JSON", + "label": "Tree" + }, + { + "fieldname": "current_node", + "fieldtype": "JSON", + "label": "Current Node" + }, + { + "fieldname": "section_break_lwr2", + "fieldtype": "Section Break" + }, + { + "fieldname": "current_from_date", + "fieldtype": "Date", + "label": "Current From Date" + }, + { + "fieldname": "current_to_date", + "fieldtype": "Date", + "label": "Current To Date" + }, + { + "fieldname": "column_break_uuic", + "fieldtype": "Column Break" } ], "hide_toolbar": 1, "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-09-25 10:50:53.887235", + "modified": "2023-09-25 17:05:13.384320", "modified_by": "Administrator", "module": "Accounts", "name": "Bisect Accounting Statements", diff --git a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py index 3582033abc..7c68f18775 100644 --- a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py +++ b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py @@ -2,6 +2,7 @@ # For license information, please see license.txt import datetime +import json from collections import deque from math import floor @@ -10,85 +11,162 @@ from dateutil.relativedelta import relativedelta from frappe import _ from frappe.model.document import Document from frappe.utils import getdate +from frappe.utils.data import DATETIME_FORMAT, guess_date_format class Node(object): - def __init__(self): - self.parent = None - self.left_child = None - self.right_child = None - - self.current_period = None - self.difference = 0.0 - self.profit_and_loss_summary = 0.0 - self.balance_sheet_summary = 0.0 - - def update_parent(self): - pass - - def update_left_child(self): - pass - - def update_right_child(self): - pass - - def make_node( + def __init__( self, parent: int = None, period: (datetime, datetime) = None, left: int = None, right: int = None, ): - current_period = period + self.parent = parent + self.left_child = left + self.right_child = right + + self.period = period + self.difference = 0.0 + self.profit_and_loss_summary = 0.0 + self.balance_sheet_summary = 0.0 + + def as_dict(self): + return dict( + parent=self.parent, + left_child=self.left_child, + right_child=self.right_child, + period=(self.period[0].strftime(DATETIME_FORMAT), self.period[1].strftime(DATETIME_FORMAT)), + difference=self.difference, + profit_and_loss_summary=self.profit_and_loss_summary, + balance_sheet_summary=self.balance_sheet_summary, + ) + + def __repr__(self): + return f"Node (parent: {self.parent}, left_child: {self.left_child}, right_child: {self.right_child}, period: {self.period})" + + +class BTree(object): + def __init__(self): + self.btree = [] + self.current_node = None + + def as_list(self): + lst = [] + for x in self.btree: + lst.append(x.as_dict()) + return lst + + def bfs(self, from_date: datetime, to_date: datetime): + root_node = Node(parent=None, period=(getdate(from_date), getdate(to_date))) + root_node.parent = None + + # add root node to tree + self.btree.append(root_node) + cur_node = root_node + period_list = deque([root_node]) + + periods = [] + while period_list: + cur_node = period_list.popleft() + cur_node_index = len(self.btree) - 1 + + delta = cur_node.period[1] - cur_node.period[0] + if delta.days == 0: + continue + else: + cur_floor = floor(delta.days / 2) + left = (cur_node.period[0], (cur_node.period[0] + relativedelta(days=+cur_floor))) + left_node = Node(parent=cur_node_index, period=left) + self.btree.append(left_node) + cur_node.left_child = len(self.btree) - 1 + period_list.append(left_node) + + right = ((cur_node.period[0] + relativedelta(days=+(cur_floor + 1))), cur_node.period[1]) + right_node = Node(parent=cur_node_index, period=right) + self.btree.append(right_node) + cur_node.right_child = len(self.btree) - 1 + period_list.append(right_node) + + def dfs(self, from_date: datetime, to_date: datetime): + root_node = Node(parent=None, period=(getdate(from_date), getdate(to_date))) + root_node.parent = None + + # add root node to tree + self.btree.append(root_node) + cur_node = root_node + period_list = [root_node] + + periods = [] + while period_list: + cur_node = period_list.pop() + cur_node_index = len(self.btree) - 1 + + delta = cur_node.period[1] - cur_node.period[0] + if delta.days == 0: + continue + else: + cur_floor = floor(delta.days / 2) + left = (cur_node.period[0], (cur_node.period[0] + relativedelta(days=+cur_floor))) + left_node = Node(parent=cur_node_index, period=left) + self.btree.append(left_node) + cur_node.left_child = len(self.btree) - 1 + period_list.append(left_node) + + right = ((cur_node.period[0] + relativedelta(days=+(cur_floor + 1))), cur_node.period[1]) + right_node = Node(parent=cur_node_index, period=right) + self.btree.append(right_node) + cur_node.right_child = len(self.btree) - 1 + period_list.append(right_node) + + def load_tree(self, tree: list, current_node: dict): + self.btree = [] + tree = json.loads(tree) + for x in tree: + x = frappe._dict(x) + n = Node(x.parent, x.period, x.left_child, x.right_child) + n.period = x.period + n.difference = x.difference + x.profit_and_loss_summary = x.profit_and_loss_summary + x.balance_sheet_summary = x.balance_sheet_summary + self.btree.append(n) + + current_node = frappe._dict(json.loads(current_node)) + n = Node( + current_node.parent, current_node.period, current_node.left_child, current_node.right_child + ) + n.period = current_node.period + n.difference = current_node.difference + n.profit_and_loss_summary = current_node.profit_and_loss_summary + n.balance_sheet_summary = current_node.balance_sheet_summary + self.current_node = n + + def build_tree(self, from_date: datetime, to_date: datetime, alogrithm: str): + if alogrithm == "BFS": + self.bfs(from_date, to_date) + + if alogrithm == "DFS": + self.dfs(from_date, to_date) + + # set root as current node + self.current_node = self.btree[0] + + def bisec_left(self): + pass + + def bisect_right(self): + pass + + def move_up(self): + pass class BisectAccountingStatements(Document): - def bfs(self): - period_list = deque([(getdate(self.from_date), getdate(self.to_date))]) - periods = [] - dates = [] - while period_list: - cur_frm_date, cur_to_date = period_list.popleft() - delta = cur_to_date - cur_frm_date - periods.append((cur_frm_date, cur_to_date, delta)) - if delta.days == 0: - continue - else: - cur_floor = floor(delta.days / 2) - left = (cur_frm_date, (cur_frm_date + relativedelta(days=+cur_floor))) - right = ((cur_frm_date + relativedelta(days=+(cur_floor + 1))), cur_to_date) - period_list.append(left) - period_list.append(right) - return periods - - def dfs(self): - period_list = [(getdate(self.from_date), getdate(self.to_date))] - periods = [] - while period_list: - cur_frm_date, cur_to_date = period_list.pop() - delta = cur_to_date - cur_frm_date - periods.append((cur_frm_date, cur_to_date, delta)) - if delta.days == 0: - continue - else: - cur_floor = floor(delta.days / 2) - left = (cur_frm_date, (cur_frm_date + relativedelta(days=+cur_floor))) - right = ((cur_frm_date + relativedelta(days=+(cur_floor + 1))), cur_to_date) - period_list.append(left) - period_list.append(right) - return periods - - @frappe.whitelist() - def bisect(self): - if self.algorithm == "BFS": - periods = self.bfs() - - if self.algorithm == "DFS": - periods = self.dfs() - - print("Periods: ", len(periods)) - for x in periods: - print(x) + def __init__(self, *args, **kwargs): + super(BisectAccountingStatements, self).__init__(*args, **kwargs) + if self.tree and self.current_node: + self.tree_instance = BTree() + self.tree_instance.load_tree(self.tree, self.current_node) def validate(self): self.validate_dates() @@ -100,3 +178,18 @@ class BisectAccountingStatements(Document): frappe.bold(self.from_date), frappe.bold(self.to_date) ) ) + + @frappe.whitelist() + def build_tree(self): + self.tree_instance = BTree() + self.tree_instance.build_tree(self.from_date, self.to_date, self.algorithm) + print("printing tree") + for x in self.tree_instance.btree: + print(x) + + print("Root", self.tree_instnace.current_node) + + self.tree = json.dumps(self.tree_instance.as_list()) + self.current_node = json.dumps(self.tree_intance.btree[0].as_dict()) + + print(guess_date_format(json.loads(self.current_node)["period"][0]))