From 6bdb8a2cf13130738d64335f91f693e4227fec38 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 17 Sep 2020 20:18:00 -0700 Subject: [PATCH] add assignee to budget_graph.Node --- src/budget_sync/budget_graph.py | 32 +++++++++++++++- src/budget_sync/test/mock_bug.py | 7 +++- src/budget_sync/test/test_budget_graph.py | 38 ++++++++++++++++--- src/budget_sync/test/test_mock_bug.py | 9 +++-- .../test/test_write_budget_markdown.py | 4 +- 5 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/budget_sync/budget_graph.py b/src/budget_sync/budget_graph.py index 322b240..074447d 100644 --- a/src/budget_sync/budget_graph.py +++ b/src/budget_sync/budget_graph.py @@ -30,6 +30,16 @@ class BudgetGraphPayeesParseError(BudgetGraphParseError): f"bug #{self.bug_id}: {self.msg}" +class BudgetGraphUnknownAssignee(BudgetGraphParseError): + def __init__(self, bug_id: int, assignee: str): + super().__init__(bug_id) + self.assignee = assignee + + def __str__(self): + return f"Bug #{self.bug_id} is assigned to an unknown person: " \ + f"{self.assignee!r}" + + class BudgetGraphLoopError(BudgetGraphBaseError): def __init__(self, bug_ids: List[int]): self.bug_ids = bug_ids @@ -253,6 +263,15 @@ class Node: new_err = BudgetGraphUnknownStatus(self.bug.id, self.bug.status) raise new_err.with_traceback(sys.exc_info()[2]) + @cached_property + def assignee(self) -> Person: + try: + return self.graph.config.all_names[self.bug.assigned_to] + except KeyError: + raise BudgetGraphUnknownAssignee(self.bug.id, + self.bug.assigned_to) \ + .with_traceback(sys.exc_info()[2]) + @cached_property def bug_url(self) -> str: return f"{self.graph.config.bugzilla_url_stripped}/show_bug.cgi?" \ @@ -351,6 +370,10 @@ class Node: status = repr(self.status) except BudgetGraphBaseError: status = f"" + try: + assignee = f"Person<{self.assignee.identifier!r}>" + except BudgetGraphBaseError: + assignee = f"" immediate_children = [] for i in self.immediate_children: immediate_children.append(_NodeSimpleReprWrapper(i)) @@ -369,7 +392,8 @@ class Node: f"milestone={milestone}, " f"immediate_children={immediate_children!r}, " f"payments={payments!r}, " - f"status={status})") + f"status={status}, " + f"assignee={assignee})") class BudgetGraphError(BudgetGraphBaseError): @@ -525,6 +549,12 @@ class BudgetGraph: except BudgetGraphBaseError as e: errors.append(e) + try: + # check for assignee errors + node.assignee + except BudgetGraphBaseError as e: + errors.append(e) + if node.milestone_str != root.milestone_str: errors.append(BudgetGraphMilestoneMismatch( node.bug.id, root.bug.id)) diff --git a/src/budget_sync/test/mock_bug.py b/src/budget_sync/test/mock_bug.py index e63ab46..17be594 100644 --- a/src/budget_sync/test/mock_bug.py +++ b/src/budget_sync/test/mock_bug.py @@ -11,7 +11,8 @@ class MockBug: cf_nlnet_milestone: Optional[str] = None, cf_payees_list: str = "", summary: str = "", - status: Union[str, BugStatus] = BugStatus.CONFIRMED): + status: Union[str, BugStatus] = BugStatus.CONFIRMED, + assigned_to: str = "user@example.com"): self.id = bug_id self.__budget_parent = cf_budget_parent self.cf_budget = cf_budget @@ -22,6 +23,7 @@ class MockBug: self.cf_payees_list = cf_payees_list self.summary = summary self.status = str(status) + self.assigned_to = assigned_to @property def cf_budget_parent(self) -> int: @@ -52,4 +54,5 @@ class MockBug: f"cf_nlnet_milestone={self.cf_nlnet_milestone!r}, " f"cf_payees_list={self.cf_payees_list!r}, " f"summary={self.summary!r}, " - f"status={status!r})") + f"status={status!r}, " + f"assigned_to={self.assigned_to!r})") diff --git a/src/budget_sync/test/test_budget_graph.py b/src/budget_sync/test/test_budget_graph.py index 60ce3d0..9a71119 100644 --- a/src/budget_sync/test/test_budget_graph.py +++ b/src/budget_sync/test/test_budget_graph.py @@ -8,7 +8,7 @@ from budget_sync.budget_graph import ( BudgetGraphNegativePayeeMoney, BudgetGraphPayeesParseError, BudgetGraphPayeesMoneyMismatch, BudgetGraphUnknownMilestone, BudgetGraphDuplicatePayeesForTask, BudgetGraphIncorrectRootForMilestone, - BudgetGraphUnknownStatus) + BudgetGraphUnknownStatus, BudgetGraphUnknownAssignee) from budget_sync.money import Money from budget_sync.util import BugStatus from typing import List, Type @@ -60,6 +60,12 @@ class TestErrorFormatting(unittest.TestCase): "failed to parse status field of bug " "#123: unknown status: 'fake status'") + def test_budget_graph_unknown_assignee(self): + self.assertEqual(str(BudgetGraphUnknownAssignee( + 123, "unknown@example.com")), + "Bug #123 is assigned to an unknown person:" + " 'unknown@example.com'") + def test_budget_graph_money_mismatch(self): self.assertEqual(str( BudgetGraphMoneyMismatchForBudgetExcludingSubtasks( @@ -146,9 +152,11 @@ EXAMPLE_CONFIG = Config.from_str( aliases = ["person1_alias1", "alias1"] output_markdown_file = "person1.mdwn" [people."person2"] + email = "person2@example.com" aliases = ["person1_alias2", "alias2", "person 2"] output_markdown_file = "person2.mdwn" [people."person3"] + email = "user@example.com" output_markdown_file = "person3.mdwn" [milestones] "milestone 1" = { canonical_bug_id = 1 } @@ -188,7 +196,8 @@ class TestBudgetGraph(unittest.TestCase): "milestone_str='milestone 1', milestone=Milestone(config=..., " "identifier='milestone 1', canonical_bug_id=1), " "immediate_children=[#2], payments=[], " - "status=BugStatus.CONFIRMED), Node(graph=..., id=#2, root=#1, " + "status=BugStatus.CONFIRMED, assignee=Person<'person3'>), " + "Node(graph=..., id=#2, root=#1, " "parent=#1, budget_excluding_subtasks=10, " "budget_including_subtasks=10, " "fixed_budget_excluding_subtasks=10, " @@ -196,8 +205,10 @@ class TestBudgetGraph(unittest.TestCase): "milestone_str='milestone 1', milestone=Milestone(config=..., " "identifier='milestone 1', canonical_bug_id=1), " "immediate_children=[], payments=[], " - "status=BugStatus.CONFIRMED)], roots=[#1]}") - bg = BudgetGraph([MockBug(bug_id=1, status="blah")], + "status=BugStatus.CONFIRMED, assignee=Person<'person3'>)], " + "roots=[#1]}") + bg = BudgetGraph([MockBug(bug_id=1, status="blah", + assigned_to="unknown@example.com")], EXAMPLE_CONFIG) self.assertEqual( repr(bg), @@ -206,7 +217,9 @@ class TestBudgetGraph(unittest.TestCase): "fixed_budget_excluding_subtasks=0, " "fixed_budget_including_subtasks=0, milestone_str=None, " "milestone=None, immediate_children=[], payments=[], " - "status=)], roots=[#1]}") + "status=, " + "assignee=)], " + "roots=[#1]}") def test_empty(self): bg = BudgetGraph([], EXAMPLE_CONFIG) @@ -1243,6 +1256,21 @@ class TestBudgetGraph(unittest.TestCase): self.assertErrorTypesMatches(bg.get_errors(), []) self.assertEqual(bg.nodes[1].status, status) + def test_assignee(self): + bg = BudgetGraph([MockBug(bug_id=1, assigned_to="blah")], + EXAMPLE_CONFIG) + errors = bg.get_errors() + self.assertErrorTypesMatches(errors, + [BudgetGraphUnknownAssignee]) + self.assertEqual(errors[0].bug_id, 1) + self.assertEqual(errors[0].assignee, "blah") + bg = BudgetGraph([MockBug(bug_id=1, + assigned_to="person2@example.com")], + EXAMPLE_CONFIG) + self.assertErrorTypesMatches(bg.get_errors(), []) + self.assertEqual(bg.nodes[1].assignee, + EXAMPLE_CONFIG.people["person2"]) + if __name__ == "__main__": unittest.main() diff --git a/src/budget_sync/test/test_mock_bug.py b/src/budget_sync/test/test_mock_bug.py index fd3624f..435280a 100644 --- a/src/budget_sync/test/test_mock_bug.py +++ b/src/budget_sync/test/test_mock_bug.py @@ -31,7 +31,8 @@ class TestMockBug(unittest.TestCase): "MockBug(bug_id=12, cf_budget_parent=None, cf_budget='0', " "cf_total_budget='0', cf_nlnet_milestone='---', " "cf_payees_list='', summary='', " - "status=BugStatus.CONFIRMED)") + "status=BugStatus.CONFIRMED, " + "assigned_to='user@example.com')") bug = MockBug(bug_id=34, cf_budget_parent=1, cf_budget="45", @@ -39,12 +40,14 @@ class TestMockBug(unittest.TestCase): cf_nlnet_milestone="abc", cf_payees_list="# a", summary="blah blah", - status="blah") + status="blah", + assigned_to="fake-email@example.com") self.assertEqual( repr(bug), "MockBug(bug_id=34, cf_budget_parent=1, cf_budget='45', " "cf_total_budget='23', cf_nlnet_milestone='abc', " - "cf_payees_list='# a', summary='blah blah', status='blah')") + "cf_payees_list='# a', summary='blah blah', status='blah', " + "assigned_to='fake-email@example.com')") def test_cf_budget_parent(self): bug = MockBug(bug_id=1, cf_budget_parent=None) diff --git a/src/budget_sync/test/test_write_budget_markdown.py b/src/budget_sync/test/test_write_budget_markdown.py index 8ff5956..1691edc 100644 --- a/src/budget_sync/test/test_write_budget_markdown.py +++ b/src/budget_sync/test/test_write_budget_markdown.py @@ -14,6 +14,7 @@ class TestWriteBudgetMarkdown(unittest.TestCase): bugzilla_url = "https://bugzilla.example.com/" [milestones] [people."person1"] + email = "person1@example.com" output_markdown_file = "person1.mdwn" [people."person2"] output_markdown_file = "person2.mdwn" @@ -25,7 +26,8 @@ class TestWriteBudgetMarkdown(unittest.TestCase): cf_total_budget="0", cf_nlnet_milestone=None, cf_payees_list="", - summary=""), + summary="", + assigned_to="person1@example.com"), ], config) self.assertEqual([], budget_graph.get_errors()) with make_filesystem_and_report_if_error(self) as filesystem: -- 2.30.2