def test_change_link_type(self): self.goals = self.build(open_(1, "Root", [2], select=previous), open_(2, "Top", [], select=selected)) assert self.goals.q(keys="name,edge") == { 1: { "name": "Root", "edge": [(2, EdgeType.PARENT)] }, 2: { "name": "Top", "edge": [] }, } self.goals.accept(ToggleLink()) assert self.goals.q(keys="name,edge") == { 1: { "name": "Root", "edge": [(2, EdgeType.BLOCKER)] }, 2: { "name": "Top", "edge": [] }, } self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT)) assert self.goals.q(keys="name,edge") == { 1: { "name": "Root", "edge": [(2, EdgeType.PARENT)] }, 2: { "name": "Top", "edge": [] }, }
def build_actions(command): simple_commands = { "c": ToggleClose(), "d": Delete(), "h": HoldSelect(), "k": ToggleLink(edge_type=EdgeType.PARENT), "l": ToggleLink(), "n": ToggleOpenView(), "p": ToggleProgress(), "t": ToggleSwitchableView(), "z": ToggleZoom(), } if command and all(c in "1234567890" for c in command): return [Select(int(c)) for c in command] if command.startswith("a "): return [Add(command[2:])] if command.startswith("i "): return [Insert(command[2:])] if command.startswith("r "): return [Rename(command[2:])] if command.startswith("f"): return [FilterBy(command[1:].lstrip())] if command.startswith("` "): return [ToggleAutoLink(command[2:])] if command in simple_commands: return [simple_commands[command]] return []
def test_add_link_between_goals(self): self.goals = self.build( open_(1, "Root", [2, 3]), open_(2, "A", select=previous), open_(3, "B", select=selected), ) assert self.goals.q(keys="switchable,edge") == { 1: { "switchable": False, "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)], }, 2: { "switchable": True, "edge": [] }, 3: { "switchable": True, "edge": [] }, } self.goals.accept(ToggleLink()) assert self.goals.q(keys="switchable,edge") == { 1: { "switchable": False, "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)], }, 2: { "switchable": False, "edge": [(3, EdgeType.BLOCKER)] }, 3: { "switchable": True, "edge": [] }, }
def test_message_on_link_to_self(self): self.goals = self.build( open_(1, "Root", [2]), open_(2, "Middle", [3]), open_(3, "Top", [], select=selected), ) self.goals.accept(ToggleLink()) assert len(self.messages) == 1
def test_no_message_on_allowed_link(self): self.goals = self.build( open_(1, "Root", [2], select=previous), open_(2, "Middle", [3]), open_(3, "Top", [], select=selected), ) self.goals.accept(ToggleLink()) assert self.messages == []
def test_message_when_remove_last_link(self): self.goals = self.build( open_(1, "Root", [2]), open_(2, "Middle", [3], select=previous), open_(3, "Top", [], select=selected), ) self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT)) assert len(self.messages) == 1
def accept_Add(self, command: Add) -> bool: add_to = command.add_to or self.selection if add_to in self.closed: self.error("A new subgoal cannot be added to the closed one") return False next_id = self._add_no_link(command.name) self.accept_ToggleLink(ToggleLink(add_to, next_id, command.edge_type)) return True
def test_no_message_when_remove_not_last_link(self): self.goals = self.build( open_(1, "Root", [2, 3], select=previous), open_(2, "Middle", blockers=[3]), open_(3, "Top", [], select=selected), ) self.goals.accept(ToggleLink()) assert self.messages == []
def test_message_when_closed_goal_is_blocked_by_open_one(self): self.goals = self.build( open_(1, "Root", [2, 3]), clos_(2, "Middle", [], select=previous), open_(3, "Top", [], select=selected), ) self.goals.accept(ToggleLink()) assert len(self.messages) == 1
def _make_links(self, matching_goals: List[int], target_goal: int) -> None: if not matching_goals: return self_children: Dict[int, List[int]] = { goal_id: [e[0] for e in attrs["edge"]] for goal_id, attrs in self.goaltree.q("edge").items() } for add_to in matching_goals: if target_goal not in self_children[add_to]: self.goaltree.accept( ToggleLink(add_to, target_goal, EdgeType.BLOCKER))
def test_change_link_type_events(self): self.goals = self.build( open_(1, "Root", [2, 3]), open_(2, "Lower", blockers=[3], select=previous), open_(3, "Upper", [], select=selected), ) self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT)) assert self.goals.events()[-4] == ("link", 2, 3, EdgeType.PARENT) assert self.goals.events()[-3] == ("unlink", 2, 3, EdgeType.BLOCKER) assert self.goals.events()[-2] == ("link", 1, 3, EdgeType.BLOCKER) assert self.goals.events()[-1] == ("unlink", 1, 3, EdgeType.PARENT)
def toggle_link(self, b, d): event("toggle link") goal_keys = sorted(list(k for k in self.goaltree.q().keys() if k > 0)) assume(len(goal_keys) > 1) selection = d.draw(sampled_from(goal_keys)) goal_keys.remove(selection) prev_selection = d.draw(sampled_from(goal_keys)) event("valid toggle link") edge_type = EdgeType.PARENT if b else EdgeType.BLOCKER self._accept( ToggleLink(lower=prev_selection, upper=selection, edge_type=edge_type))
def test_remove_link_between_goals(self): self.goals = self.build( open_(1, "Root", [2, 3]), open_(2, "A", blockers=[3], select=previous), open_(3, "B", select=selected), ) self.goals.accept(ToggleLink(edge_type=EdgeType.BLOCKER)) assert self.goals.q(keys="edge,switchable") == { 1: { "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)], "switchable": False, }, 2: { "edge": [], "switchable": True }, 3: { "edge": [], "switchable": True }, }
def test_new_parent_link_replaces_old_one_when_changed_from_blocker(self): self.goals = self.build( open_(1, "Root", [2, 3]), open_(2, "A", select=selected), open_(3, "B", blockers=[2], select=previous), ) self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT)) assert self.goals.q("name,edge") == { 1: { "name": "Root", "edge": [(2, EdgeType.BLOCKER), (3, EdgeType.PARENT)] }, 2: { "name": "A", "edge": [] }, 3: { "name": "B", "edge": [(2, EdgeType.PARENT)] }, }
def test_new_parent_link_replaces_old_one(self): self.goals = self.build( open_(1, "Root", [2, 3]), open_(2, "Old parent", [4]), open_(3, "New parent", select=previous), open_(4, "Child", select=selected), ) self.goals.accept(ToggleLink(edge_type=EdgeType.PARENT)) assert self.goals.q(keys="edge") == { 1: { "edge": [(2, EdgeType.PARENT), (3, EdgeType.PARENT)] }, 2: { "edge": [(4, EdgeType.BLOCKER)] }, 3: { "edge": [(4, EdgeType.PARENT)] }, 4: { "edge": [] }, }
def test_no_loops_allowed(self): self.goals = self.build( open_(1, "Root", [2], select=selected), open_(2, "step", [3]), open_(3, "next", [4]), open_(4, "more", select=previous), ) self.goals.accept(ToggleLink()) assert self.goals.q(keys="edge") == { 1: { "edge": [(2, EdgeType.PARENT)] }, 2: { "edge": [(3, EdgeType.PARENT)] }, 3: { "edge": [(4, EdgeType.PARENT)] }, 4: { "edge": [] }, }
def test_save_and_load(): file_name = NamedTemporaryFile().name goals = Enumeration(all_layers(Goals("Root"))) goals.accept_all( Add("Top"), Add("Middle"), Select(3), HoldSelect(), Select(2), ToggleLink(), Add("Closed"), Select(4), ToggleClose(), Select(2), ToggleZoom(), ) save(goals, file_name) new_goals = load(file_name) goals.accept_all(ToggleOpenView()) new_goals.accept_all(ToggleOpenView()) assert goals.q(keys="open,name,edge,select,switchable") == new_goals.q( keys="open,name,edge,select,switchable")
def test_restore_goals_from_db(): file_name = NamedTemporaryFile().name with sqlite3.connect(file_name) as conn: run_migrations(conn) setup_sample_db(conn) actual_goals = load(file_name) actual_goals.accept(ToggleOpenView()) expected_goals = Goals("Root") expected_goals.accept_all( Add("A"), Add("B"), Select(2), HoldSelect(), Select(3), ToggleLink(), Select(3), ToggleClose(), Select(1), HoldSelect(), Select(2), ) keys = "name,edge,open,select" assert expected_goals.q(keys=keys) == actual_goals.q(keys=keys) assert not actual_goals.events()
def test_link_events(self): self.goals.accept_all(Add("Next"), Add("More"), Select(2), HoldSelect(), Select(3), ToggleLink()) assert self.goals.events()[-1] == ("link", 2, 3, EdgeType.BLOCKER) self.goals.accept(ToggleLink()) assert self.goals.events()[-1] == ("unlink", 2, 3, EdgeType.BLOCKER)
def _switchable(self, key: int) -> bool: if key in self.closed: if back_edges := self._back_edges(key): return any(e.source not in self.closed for e in back_edges) return True return all(x.target in self.closed for x in self._forward_edges(key)) def accept_Insert(self, command: Insert): if (lower := self.previous_selection) == (upper := self.selection): self.error( "A new goal can be inserted only between two different goals") return edge_type = self.edges_forward[lower].get(upper, EdgeType.BLOCKER) if self.accept_Add(Add(command.name, lower, edge_type)): key = len(self.goals) self.accept_ToggleLink(ToggleLink(key, upper, edge_type)) if self._has_link(lower, upper): self.accept_ToggleLink(ToggleLink(lower, upper)) def accept_Rename(self, command: Rename): goal_id = command.goal_id or self.selection self.goals[goal_id] = command.new_name self._events.append(("rename", command.new_name, goal_id)) def accept_ToggleClose(self, command: ToggleClose) -> None: if self.selection in self.closed: if self._may_be_reopened(): self.closed.remove(self.selection) self._events.append(("toggle_close", True, self.selection)) else: self.error(
def test_no_link_to_self_is_allowed(self): self.goals.accept(ToggleLink()) assert self.goals.q(keys="edge") == {1: {"edge": []}}
def keyPressEvent(self, event): key_handlers = { Qt.Key_1: self.select_number(1), Qt.Key_2: self.select_number(2), Qt.Key_3: self.select_number(3), Qt.Key_4: self.select_number(4), Qt.Key_5: self.select_number(5), Qt.Key_6: self.select_number(6), Qt.Key_7: self.select_number(7), Qt.Key_8: self.select_number(8), Qt.Key_9: self.select_number(9), Qt.Key_0: self.select_number(0), Qt.Key_A: self.start_edit("Add new goal", self.emit_add), Qt.Key_C: self.with_refresh(self.goals.accept, ToggleClose()), Qt.Key_D: self.with_refresh(self.goals.accept, Delete()), Qt.Key_F: self.start_edit( "Filter by substring (leave empty to reset filtration)", self.emit_filter, ), Qt.Key_I: self.start_edit("Insert new goal", self.emit_insert), Qt.Key_K: self.with_refresh(self.goals.accept, ToggleLink(edge_type=EdgeType.PARENT)), Qt.Key_L: self.with_refresh(self.goals.accept, ToggleLink()), Qt.Key_N: self.with_refresh(self.toggle_open_view, True), Qt.Key_O: self.show_open_dialog, Qt.Key_P: self.with_refresh(self.toggle_progress_view, True), Qt.Key_Q: self.quit_app.emit, Qt.Key_R: self.start_edit("Rename goal", self.emit_rename, self._current_goal_label), Qt.Key_T: self.with_refresh(self.toggle_switchable_view, True), Qt.Key_Z: self.toggle_zoom, Qt.Key_QuoteLeft: self.start_edit( "Auto link by keyword (leave empty to reset auto link)", self.emit_autolink, ), Qt.Key_Escape: self.cancel_edit, Qt.Key_Minus: self.with_refresh(self.change_columns, -1), Qt.Key_Plus: self.with_refresh(self.change_columns, 1), Qt.Key_Slash: self.show_keys_help, Qt.Key_Space: self.with_refresh(self.goals.accept, HoldSelect()), } if event.key() in key_handlers: key_handlers[event.key()]() else: super().keyPressEvent(event)