@classmethod
def by_id(cls, db_conn: DatabaseConnection, id_: int | None,
create: bool = False) -> Process:
- """Collect all Processes and their connected VersionedAttributes."""
+ """Collect Process, its VersionedAttributes, and its child IDs."""
process = None
for row in db_conn.exec('SELECT * FROM processes '
'WHERE id = ?', (id_,)):
process.child_ids += [row[1]]
return process
- def children(self, db_conn: DatabaseConnection) -> list[Process]:
- """Return child Processes as determined by self.child_ids."""
- return [self.__class__.by_id(db_conn, id_) for id_ in self.child_ids]
+ def get_descendants(self, db_conn: DatabaseConnection) ->\
+ dict[int, dict[str, object]]:
+ """Return tree of descendant Processes"""
+ descendants = {}
+ for id_ in self.child_ids:
+ child = self.__class__.by_id(db_conn, id_)
+ descendants[id_] = {'process': child,
+ 'children': child.get_descendants(db_conn)}
+ return descendants
def save(self, db_conn: DatabaseConnection) -> None:
- """Add (or re-write) self and connected VersionedAttributes to DB."""
+ """Add (or re-write) self and connected VersionedAttributes to DB.
+
+ Also is the point at which descendancy recursion is checked.
+ """
+ def walk_descendants(node_id: int) -> None:
+ if node_id == self.id_:
+ raise BadFormatException('bad child selection: recursion')
+ descendant = self.by_id(db_conn, node_id)
+ for descendant_id in descendant.child_ids:
+ walk_descendants(descendant_id)
cursor = db_conn.exec('REPLACE INTO processes VALUES (?)', (self.id_,))
self.id_ = cursor.lastrowid
self.title.save(db_conn)
db_conn.exec('DELETE FROM process_children WHERE parent_id = ?',
(self.id_,))
for child_id in self.child_ids:
+ walk_descendants(child_id)
db_conn.exec('INSERT INTO process_children VALUES (?, ?)',
(self.id_, child_id))