From 9e9a047b304f4e68076d00bd346e99e1e0c1a738 Mon Sep 17 00:00:00 2001 From: Kyle Miracle Date: Mon, 6 Apr 2015 17:38:27 -0400 Subject: [PATCH 1/7] adding first tests --- test_math_tree.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test_math_tree.py diff --git a/test_math_tree.py b/test_math_tree.py new file mode 100644 index 0000000..ed79df1 --- /dev/null +++ b/test_math_tree.py @@ -0,0 +1,21 @@ +#!/usr/bin/python + +from math_tree import Node, NumberNode, OperatorNode + +import unittest + + +class TreeGraph(unittest.TestCase): + def test_create_node(self): + node = Node("test") + self.assertEquals(node.val, "test") + + def test_create_operator_node(self): + node = OperatorNode("+") + self.assertEquals("+", node.val) + + def test_create_number_node(self): + node = NumberNode(2) + self.assertEquals(node.val, 2) + + From 07cc5635539010b952a3a24173f3714d6698a8bd Mon Sep 17 00:00:00 2001 From: Kyle Miracle Date: Mon, 6 Apr 2015 18:10:33 -0400 Subject: [PATCH 2/7] added Node classes and some tests --- math_tree.py | 65 +++++++++++++++++++++++++++++++++++++++++++++++ test_math_tree.py | 13 +++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 math_tree.py diff --git a/math_tree.py b/math_tree.py new file mode 100644 index 0000000..a956e62 --- /dev/null +++ b/math_tree.py @@ -0,0 +1,65 @@ +#!/usr/bin/python + +from numbers import Number + + +class InvalidOperator(Exception): + pass + + +class InvalidNode(Exception): + pass + + +class InvalidNumber(Exception): + pass + + +class Node(object): + """ + Object with basic Node properties that will be extended with OperatorNode and NumberNode. + Can contain any arbitrary number of Nodes. + """ + def __init__(self, val): + if val == "": + raise InvalidNode("Node cannot be empty") + + self.val = val + self.children = [] + + def add_child(self, child): + self.children.append(child) + + def __str__(self): + return str(self.val) + + +class OperatorNode(Node): + """ + OperatorNode has a value of +, -, /, or * + calculate() will return the value of the tree under the node + """ + operators = ["+", "-", "/", "*"] + + def __init__(self, val): + if val not in self.operators: + raise InvalidOperator("Invalid Operator. Must be one of [+ - / *]") + else: + super(OperatorNode, self).__init__(val) + + +class NumberNode(Node): + """ + A node that stores a number instead of an operator. + """ + def __init__(self, val): + if not isinstance(val, Number): + raise InvalidNumber + + super(NumberNode, self).__init__(val) + + def __int__(self): + return int(self.val) + + + pass diff --git a/test_math_tree.py b/test_math_tree.py index ed79df1..8e13bec 100644 --- a/test_math_tree.py +++ b/test_math_tree.py @@ -1,6 +1,6 @@ #!/usr/bin/python -from math_tree import Node, NumberNode, OperatorNode +from math_tree import * import unittest @@ -10,12 +10,23 @@ def test_create_node(self): node = Node("test") self.assertEquals(node.val, "test") + def test_create_node_exception(self): + self.assertRaises(InvalidNode, Node, "") + def test_create_operator_node(self): node = OperatorNode("+") self.assertEquals("+", node.val) + def test_create_op_node_exception(self): + self.assertRaises(InvalidOperator, OperatorNode, "a") + def test_create_number_node(self): node = NumberNode(2) self.assertEquals(node.val, 2) + def test_create_number_node_exception(self): + self.assertRaises(InvalidNumber, NumberNode, "a") + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 5c54273669d36ed5a3db79e7d87eab6126ea4dae Mon Sep 17 00:00:00 2001 From: Kyle Miracle Date: Mon, 6 Apr 2015 18:18:12 -0400 Subject: [PATCH 3/7] add first test to calculate a node --- math_tree.py | 3 +++ test_math_tree.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/math_tree.py b/math_tree.py index a956e62..3a753e4 100644 --- a/math_tree.py +++ b/math_tree.py @@ -47,6 +47,9 @@ def __init__(self, val): else: super(OperatorNode, self).__init__(val) + def calculate(self): + return 0 + class NumberNode(Node): """ diff --git a/test_math_tree.py b/test_math_tree.py index 8e13bec..056af10 100644 --- a/test_math_tree.py +++ b/test_math_tree.py @@ -27,6 +27,38 @@ def test_create_number_node(self): def test_create_number_node_exception(self): self.assertRaises(InvalidNumber, NumberNode, "a") + def test_create_tree(self): + """ + + + / \ + 3 - + / \ + + 5 + / \ + 4 10 + + answer: 12 + + """ + root_node = OperatorNode("+") + a_node = NumberNode(3) + b_node = OperatorNode("-") + c_node = OperatorNode("+") + d_node = NumberNode(5) + e_node = NumberNode(4) + f_node = NumberNode(10) + + root_node.add_child(a_node) + root_node.add_child(b_node) + + b_node.add_child(c_node) + b_node.add_child(d_node) + + c_node.add_child(e_node) + c_node.add_child(f_node) + + self.assertEquals(root_node.calculate(), 12) + if __name__ == '__main__': unittest.main() \ No newline at end of file From 647e844fc11ce94334162660fb321bbb120fad39 Mon Sep 17 00:00:00 2001 From: Kyle Miracle Date: Mon, 6 Apr 2015 20:24:01 -0400 Subject: [PATCH 4/7] added tests to calculate tree and method to calculate tree --- math_tree.py | 45 ++++++++++++++-- test_math_tree.py | 128 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 7 deletions(-) diff --git a/math_tree.py b/math_tree.py index 3a753e4..0af4640 100644 --- a/math_tree.py +++ b/math_tree.py @@ -4,14 +4,17 @@ class InvalidOperator(Exception): + """ An invalid operator was passed """ pass class InvalidNode(Exception): + """ The node was created with no value """ pass class InvalidNumber(Exception): + """ The node was not passed a number """ pass @@ -33,6 +36,9 @@ def add_child(self, child): def __str__(self): return str(self.val) + def __repr__(self): + return self.__str__() + class OperatorNode(Node): """ @@ -43,12 +49,40 @@ class OperatorNode(Node): def __init__(self, val): if val not in self.operators: - raise InvalidOperator("Invalid Operator. Must be one of [+ - / *]") - else: - super(OperatorNode, self).__init__(val) + raise InvalidOperator("Invalid Operator. Must be one of set(+ - / *)") + + self._operator = val + super(OperatorNode, self).__init__(val) + + @property + def operator(self): + return self._operator def calculate(self): - return 0 + """ + Recursively walk through all children in OperatorNode and solve until only + a NumberNode is left. + """ + def calculate_all_children(children, operator): + num_children = [] + for node in children: + if isinstance(node, OperatorNode): + num_children.append(calculate_all_children(node.children, node.operator)) + else: + num_children.append(node) + return self.solve_expression(num_children, operator) + + return calculate_all_children(self.children, self.operator) + + def solve_expression(self, number_nodes, operator): + """ + Create an expression to calculate with eval() + """ + expr = (" %s " % operator).join([str(float(n)) for n in number_nodes]) + print expr + result = eval(expr) + print result + return NumberNode(result) class NumberNode(Node): @@ -64,5 +98,6 @@ def __init__(self, val): def __int__(self): return int(self.val) + def __float__(self): + return float(self.val) - pass diff --git a/test_math_tree.py b/test_math_tree.py index 056af10..467b28e 100644 --- a/test_math_tree.py +++ b/test_math_tree.py @@ -27,7 +27,7 @@ def test_create_number_node(self): def test_create_number_node_exception(self): self.assertRaises(InvalidNumber, NumberNode, "a") - def test_create_tree(self): + def test_calculate_tree1(self): """ + / \ @@ -57,8 +57,132 @@ def test_create_tree(self): c_node.add_child(e_node) c_node.add_child(f_node) - self.assertEquals(root_node.calculate(), 12) + self.assertEquals(float(root_node.calculate()), float(12)) + def test_calculate_tree2(self): + """ + + + / \ + 3 - 3 + 10 = 13 + / \ + + 5 15 - 5 = 10 + / \ + - 10 5 + 10 = 15 + / \ + 9 + 9 - 4 = 5 + / \ + - 6 -2 + 6 = 4 + / \ + 2 4 2 - 4 = -2 + + """ + root_node = OperatorNode("+") + a = NumberNode(3) + b = OperatorNode("-") + c = OperatorNode("+") + d = NumberNode(5) + e = OperatorNode("-") + f = NumberNode(10) + g = NumberNode(9) + h = OperatorNode("+") + i = OperatorNode("-") + j = NumberNode(6) + k = NumberNode(2) + l = NumberNode(4) + + root_node.add_child(a) + root_node.add_child(b) + + b.add_child(c) + b.add_child(d) + + c.add_child(e) + c.add_child(f) + + e.add_child(g) + e.add_child(h) + + h.add_child(i) + h.add_child(j) + + i.add_child(k) + i.add_child(l) + + self.assertEquals(float(root_node.calculate()), float(13)) + + def test_calculate_tree3(self): + """ + - + / \ + 3 * 3 - 150 = -147 + / \ + * 5 30 * 5 = 150 + / \ + (div) 10 3 * 10 = 30 + / \ + 9 3 9 / 3 = 3 + + """ + root_node = OperatorNode("-") + a = NumberNode(3) + b = OperatorNode("*") + c = OperatorNode("*") + d = NumberNode(5) + e = OperatorNode("/") + f = NumberNode(10) + g = NumberNode(9) + h = NumberNode(3) + + root_node.add_child(a) + root_node.add_child(b) + + b.add_child(c) + b.add_child(d) + + c.add_child(e) + c.add_child(f) + + e.add_child(g) + e.add_child(h) + + self.assertEquals(float(root_node.calculate()), float(-147)) + + def test_calculate_tree4(self): + """ + (div) + / \ + 3 * 3 / 150 = -147 + / \ + * 5 30 * 5 = 150 + / \ + (div) 10 3 * 10 = 30 + / \ + 9 3 9 / 3 = 3 + + """ + root_node = OperatorNode("/") + a = NumberNode(3) + b = OperatorNode("*") + c = OperatorNode("*") + d = NumberNode(5) + e = OperatorNode("/") + f = NumberNode(10) + g = NumberNode(9) + h = NumberNode(3) + + root_node.add_child(a) + root_node.add_child(b) + + b.add_child(c) + b.add_child(d) + + c.add_child(e) + c.add_child(f) + + e.add_child(g) + e.add_child(h) + + self.assertEquals(float(root_node.calculate()), 0.02) if __name__ == '__main__': unittest.main() \ No newline at end of file From 5da8998aa902735211ce00de3c8848fe77f8c397 Mon Sep 17 00:00:00 2001 From: Kyle Miracle Date: Mon, 6 Apr 2015 22:02:37 -0400 Subject: [PATCH 5/7] added test to create tree from dict and Tree class --- math_tree.py | 41 ++++++++++++++++++++++++++++++++++++++--- test_math_tree.py | 15 +++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/math_tree.py b/math_tree.py index 0af4640..f5f2396 100644 --- a/math_tree.py +++ b/math_tree.py @@ -18,6 +18,10 @@ class InvalidNumber(Exception): pass +class InvalidTree(Exception): + """ The tree dictionary didn't have the proper structure """ + + class Node(object): """ Object with basic Node properties that will be extended with OperatorNode and NumberNode. @@ -43,7 +47,7 @@ def __repr__(self): class OperatorNode(Node): """ OperatorNode has a value of +, -, /, or * - calculate() will return the value of the tree under the node + calculate() will return the value of all nodes under the root node. """ operators = ["+", "-", "/", "*"] @@ -79,9 +83,7 @@ def solve_expression(self, number_nodes, operator): Create an expression to calculate with eval() """ expr = (" %s " % operator).join([str(float(n)) for n in number_nodes]) - print expr result = eval(expr) - print result return NumberNode(result) @@ -101,3 +103,36 @@ def __int__(self): def __float__(self): return float(self.val) + +class Tree(OperatorNode): + """ + Create a Tree object from a dictionary. + """ + def __init__(self, tree_dict): + if len(tree_dict.keys()) > 1: + raise InvalidTree("Tree can only have one root node") + + super(Tree, self).__init__(tree_dict.keys()[0]) + + for node in self.tree(tree_dict).children: + self.add_child(node) + + def tree(self, tree): + def walk_tree(new_tree): + op_node = OperatorNode(new_tree.keys()[0]) + for item in new_tree[new_tree.keys()[0]]: + if isinstance(item, dict): + op_node.add_child(walk_tree(item)) + else: + op_node.add_child(NumberNode(item)) + return op_node + + root = tree.keys()[0] + root_node = OperatorNode(root) + for child in tree[root]: + if isinstance(child, dict): + root_node.add_child(walk_tree(child)) + else: + root_node.add_child(NumberNode(child)) + + return root_node \ No newline at end of file diff --git a/test_math_tree.py b/test_math_tree.py index 467b28e..bf17707 100644 --- a/test_math_tree.py +++ b/test_math_tree.py @@ -184,5 +184,20 @@ def test_calculate_tree4(self): self.assertEquals(float(root_node.calculate()), 0.02) + def test_create_tree_from_dict(self): + # + + # / \ + # 2 - 2 + -40 = -38 + # / \ + # 4 + 4 - 44 = -40 + # / \ + # - * -1 + 45 = 44 + # / \ / \ + # 7 8 9 5 7 - 8 = -1 9 * 5 = 45 + + tree_dict = {"+": [2, {"-": [4, {"+": [{"-": [7, 8]}, {"*": [9, 5]}]}]}]} + tree = Tree(tree_dict) + self.assertEquals(float(tree.calculate()), float(-38)) + if __name__ == '__main__': unittest.main() \ No newline at end of file From 528c955ac5f02f5714e6dfede2ced34ea8e0b8d8 Mon Sep 17 00:00:00 2001 From: Kyle Miracle Date: Tue, 7 Apr 2015 20:19:29 -0400 Subject: [PATCH 6/7] add sql answer --- answer.sql | 9 ++++++ math_tree.py | 22 ++++++------- test_math_tree.py | 80 ++++++++++++++++++++++++++++++++--------------- 3 files changed, 75 insertions(+), 36 deletions(-) create mode 100644 answer.sql diff --git a/answer.sql b/answer.sql new file mode 100644 index 0000000..9ba083c --- /dev/null +++ b/answer.sql @@ -0,0 +1,9 @@ +SELECT projects.project_name as Project, SUM(employees.salary) as Total, AVG(employees.age) as "Avg" +FROM employees +JOIN employee_projects on employees.id = employee_projects.employee_id +JOIN projects on projects.project_id = employee_projects.project_id +GROUP BY projects.project_id; + +/* I would index employees.id, employee_projects.employee_id, employee_projects.project_id, +and project.project_id +*/ \ No newline at end of file diff --git a/math_tree.py b/math_tree.py index f5f2396..a92551c 100644 --- a/math_tree.py +++ b/math_tree.py @@ -20,6 +20,12 @@ class InvalidNumber(Exception): class InvalidTree(Exception): """ The tree dictionary didn't have the proper structure """ + pass + + +class NoChildren(Exception): + """ Tried to calculate node with no children """ + pass class Node(object): @@ -30,7 +36,6 @@ class Node(object): def __init__(self, val): if val == "": raise InvalidNode("Node cannot be empty") - self.val = val self.children = [] @@ -40,9 +45,6 @@ def add_child(self, child): def __str__(self): return str(self.val) - def __repr__(self): - return self.__str__() - class OperatorNode(Node): """ @@ -53,8 +55,7 @@ class OperatorNode(Node): def __init__(self, val): if val not in self.operators: - raise InvalidOperator("Invalid Operator. Must be one of set(+ - / *)") - + raise InvalidOperator("Invalid Operator. Must be in set(+ - / *)") self._operator = val super(OperatorNode, self).__init__(val) @@ -67,6 +68,9 @@ def calculate(self): Recursively walk through all children in OperatorNode and solve until only a NumberNode is left. """ + if len(self.children) < 2: + raise NoChildren("Must have at least two children NumberNodes to calculate") + def calculate_all_children(children, operator): num_children = [] for node in children: @@ -75,7 +79,6 @@ def calculate_all_children(children, operator): else: num_children.append(node) return self.solve_expression(num_children, operator) - return calculate_all_children(self.children, self.operator) def solve_expression(self, number_nodes, operator): @@ -93,8 +96,7 @@ class NumberNode(Node): """ def __init__(self, val): if not isinstance(val, Number): - raise InvalidNumber - + raise InvalidNumber("Value must be a number") super(NumberNode, self).__init__(val) def __int__(self): @@ -111,9 +113,7 @@ class Tree(OperatorNode): def __init__(self, tree_dict): if len(tree_dict.keys()) > 1: raise InvalidTree("Tree can only have one root node") - super(Tree, self).__init__(tree_dict.keys()[0]) - for node in self.tree(tree_dict).children: self.add_child(node) diff --git a/test_math_tree.py b/test_math_tree.py index bf17707..b7841a5 100644 --- a/test_math_tree.py +++ b/test_math_tree.py @@ -18,14 +18,23 @@ def test_create_operator_node(self): self.assertEquals("+", node.val) def test_create_op_node_exception(self): - self.assertRaises(InvalidOperator, OperatorNode, "a") + self.assertRaises(InvalidOperator, OperatorNode, "A") def test_create_number_node(self): node = NumberNode(2) self.assertEquals(node.val, 2) def test_create_number_node_exception(self): - self.assertRaises(InvalidNumber, NumberNode, "a") + self.assertRaises(InvalidNumber, NumberNode, "A") + + def test_calculate_node_no_children(self): + node = OperatorNode("+") + self.assertRaises(NoChildren, node.calculate) + + def test_calculate_node_one_child(self): + node = OperatorNode("+") + node.add_child(NumberNode(2)) + self.assertRaises(NoChildren, node.calculate) def test_calculate_tree1(self): """ @@ -47,13 +56,10 @@ def test_calculate_tree1(self): d_node = NumberNode(5) e_node = NumberNode(4) f_node = NumberNode(10) - root_node.add_child(a_node) root_node.add_child(b_node) - b_node.add_child(c_node) b_node.add_child(d_node) - c_node.add_child(e_node) c_node.add_child(f_node) @@ -92,19 +98,14 @@ def test_calculate_tree2(self): root_node.add_child(a) root_node.add_child(b) - b.add_child(c) b.add_child(d) - c.add_child(e) c.add_child(f) - e.add_child(g) e.add_child(h) - h.add_child(i) h.add_child(j) - i.add_child(k) i.add_child(l) @@ -135,13 +136,10 @@ def test_calculate_tree3(self): root_node.add_child(a) root_node.add_child(b) - b.add_child(c) b.add_child(d) - c.add_child(e) c.add_child(f) - e.add_child(g) e.add_child(h) @@ -172,28 +170,60 @@ def test_calculate_tree4(self): root_node.add_child(a) root_node.add_child(b) - b.add_child(c) b.add_child(d) - c.add_child(e) c.add_child(f) - e.add_child(g) e.add_child(h) self.assertEquals(float(root_node.calculate()), 0.02) + def test_calculate_tree5(self): + """ + + + / | \ + 3 - 3 + / \ + + 5 + / / \ + 5 4 10 + + answer: 20 + + """ + root_node = OperatorNode("+") + a_node = NumberNode(3) + b_node = OperatorNode("-") + c_node = OperatorNode("+") + d_node = NumberNode(5) + e_node = NumberNode(4) + f_node = NumberNode(10) + g_node = NumberNode(3) + h_node = NumberNode(5) + root_node.add_child(a_node) + root_node.add_child(b_node) + root_node.add_child(g_node) + b_node.add_child(c_node) + b_node.add_child(d_node) + c_node.add_child(e_node) + c_node.add_child(f_node) + c_node.add_child(h_node) + + self.assertEquals(float(root_node.calculate()), float(20)) + def test_create_tree_from_dict(self): - # + - # / \ - # 2 - 2 + -40 = -38 - # / \ - # 4 + 4 - 44 = -40 - # / \ - # - * -1 + 45 = 44 - # / \ / \ - # 7 8 9 5 7 - 8 = -1 9 * 5 = 45 + """ + + + / \ + 2 - + / \ + 4 + + / \ + - * + / \ / \ + 7 8 9 5 + """ tree_dict = {"+": [2, {"-": [4, {"+": [{"-": [7, 8]}, {"*": [9, 5]}]}]}]} tree = Tree(tree_dict) From 0a552694701d5c653796432aaf3e671f84043aee Mon Sep 17 00:00:00 2001 From: Kyle Miracle Date: Tue, 7 Apr 2015 20:33:42 -0400 Subject: [PATCH 7/7] clean up code --- math_tree.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/math_tree.py b/math_tree.py index a92551c..20fecac 100644 --- a/math_tree.py +++ b/math_tree.py @@ -71,15 +71,15 @@ def calculate(self): if len(self.children) < 2: raise NoChildren("Must have at least two children NumberNodes to calculate") - def calculate_all_children(children, operator): - num_children = [] + def calc_children(children, operator): + number_nodes = [] for node in children: if isinstance(node, OperatorNode): - num_children.append(calculate_all_children(node.children, node.operator)) + number_nodes.append(calc_children(node.children, node.operator)) else: - num_children.append(node) - return self.solve_expression(num_children, operator) - return calculate_all_children(self.children, self.operator) + number_nodes.append(node) + return self.solve_expression(number_nodes, operator) + return calc_children(self.children, self.operator) def solve_expression(self, number_nodes, operator): """