X-Git-Url: https://plomlompom.com/repos/?a=blobdiff_plain;f=plomtask%2Ftodos.py;h=ff6cdcb1e96144743f828ca5fea14072988be10a;hb=8e1a5416151dbcf506f2435823362e21d85aed2d;hp=d060e230381d69dc53cacde6ab28fc37e6afefe6;hpb=c2004503dc42449f1fa129b8e56eeef0a6df4712;p=plomtask diff --git a/plomtask/todos.py b/plomtask/todos.py index d060e23..ff6cdcb 100644 --- a/plomtask/todos.py +++ b/plomtask/todos.py @@ -1,5 +1,6 @@ """Actionables.""" from __future__ import annotations +from dataclasses import dataclass from typing import Any from sqlite3 import Row from plomtask.db import DatabaseConnection, BaseModel @@ -9,6 +10,15 @@ from plomtask.exceptions import (NotFoundException, BadFormatException, HandledException) +@dataclass +class TodoStepsNode: + """Collects what's useful to know for Todo/Condition tree display.""" + item: Todo | Condition + is_todo: bool + children: list[TodoStepsNode] + seen: bool + + class Todo(BaseModel, ConditionsRelations): """Individual actionable.""" @@ -134,13 +144,45 @@ class Todo(BaseModel, ConditionsRelations): for condition in self.disables: condition.is_active = False + def get_step_tree(self, seen_todos: set[int], + seen_conditions: set[int]) -> TodoStepsNode: + """Return tree of depended-on Todos and Conditions.""" + + def make_node(step: Todo | Condition) -> TodoStepsNode: + assert isinstance(step.id_, int) + is_todo = isinstance(step, Todo) + children = [] + if is_todo: + assert isinstance(step, Todo) + seen = step.id_ in seen_todos + seen_todos.add(step.id_) + potentially_enabled = set() + for child in step.children: + for condition in child.enables: + potentially_enabled.add(condition) + children += [make_node(child)] + for condition in [c for c in step.conditions + if (not c.is_active) + and (c not in potentially_enabled)]: + children += [make_node(condition)] + else: + assert isinstance(step, Condition) + seen = step.id_ in seen_conditions + seen_conditions.add(step.id_) + return TodoStepsNode(step, is_todo, children, seen) + + node = make_node(self) + return node + def add_child(self, child: Todo) -> None: - """Add child to self.children, guard against recursion""" + """Add child to self.children, avoid recursion, update parenthoods.""" + def walk_steps(node: Todo) -> None: if node.id_ == self.id_: raise BadFormatException('bad child choice causes recursion') for child in node.children: walk_steps(child) + if self.id_ is None: raise HandledException('Can only add children to saved Todos.') if child.id_ is None: @@ -151,6 +193,13 @@ class Todo(BaseModel, ConditionsRelations): self.children += [child] child.parents += [self] + def remove_child(self, child: Todo) -> None: + """Remove child from self.children, update counter relations.""" + if child not in self.children: + raise HandledException('Cannot remove un-parented child.') + self.children.remove(child) + child.parents.remove(self) + def save(self, db_conn: DatabaseConnection) -> None: """Write self and children to DB and its cache.""" if self.process.id_ is None: