Source code for subway.htree
"""
Forest of multiple trees of tasks
"""
from .exceptions import MatchError
[docs]class HTree:
"""
Trees structure for tasks recorded in history.json.
"""
[docs] def __init__(self, history):
self.history = history
self.roots_list = None
self.leaves_list = None
[docs] def match(self, idprefix):
results = []
for j, _ in self.history.items():
if j.startswith(idprefix):
results.append(j)
if not results:
raise MatchError("Job id prefix matches no job")
if len(results) > 1:
raise MatchError("Job id prefix matches multiple jobs")
return results[0]
[docs] def parent(self, jobid, n=1):
"""
n-level parent task id
:param jobid:
:param n: n<=0 for the ultimate root
:return:
"""
p1 = self.history[jobid]["prev"]
# print(p1)
if p1 is None:
if n > 0:
return
# raise ValueError("%s has no parent in that level" % jobid)
else:
return jobid
if n == 1:
return p1
return self.parent(p1, n - 1)
[docs] def roots(self):
"""
Find all jobs with no parent.
:return: List[str]. jobid list
"""
if self.roots_list is not None:
return self.roots_list
r = []
for jid, s in self.history.items():
if s["prev"] is None:
r.append(jid)
self.roots_list = r
return r
[docs] def root(self, jobid):
return self.parent(jobid, n=0)
[docs] def children(self, jobid, n=1):
"""
:param jobid: jobid or jobids list
:param n: n=0 for the lowest level
:return: list of children jobid
"""
c1s = []
if type(jobid) is not list:
jobid = [jobid]
for j in jobid:
c1 = self.history[j]["next"]
if c1:
c1s.extend(c1)
if n == 1:
return c1s
if not c1s and n <= 0:
return jobid
return self.children(c1s, n - 1)
[docs] def end(self, jobid):
"""
downstream of jobid as leaves
:param jobid:
:return: list
"""
r = []
for node in self.DFSvisit(jobid):
if not self.history[node]["next"]:
r.append(node)
return r
[docs] def DFSvisit(self, jobid):
"""
generator yield job by DFS across the tree rooted as ``jobid``
:param jobid: str.
:yield: Next job id by DFS.
"""
yield jobid
for nid in self.history[jobid]["next"]:
yield from self.DFSvisit(nid)
[docs] def BFSvisit(self, jobid):
pass # TODO: BFS
[docs] def leaves(self):
if self.leaves_list is not None:
return self.leaves_list
l = []
for jid, s in self.history.items():
if not s["next"]:
l.append(jid)
self.leaves_list = l
return l
[docs] def print_tree(self, jobid, file=None, _prefix="", _last=True, _show=0):
"""
print a tree to stdout with root as ``jobid``
:param jobid: str.
:param file: args for ``file=`` in ``print``. Default None to stdout.
:param _prefix:
:param _last:
:param _show:
:return:
"""
print(
_prefix,
"`- " if _last else "|- ",
jobid if _show == 0 else jobid[:_show],
sep="",
file=file,
)
_prefix += " " if _last else "| "
children = self.history[jobid]["next"]
child_count = len(children)
for i, child in enumerate(children):
_last = i == (child_count - 1)
self.print_tree(child, file, _prefix, _last, _show)
[docs] def print_trees(self, jobids, file=None, _prefix="", _last=True, _show=0):
for jobid in jobids:
self.print_tree(jobid, file, _prefix, _last, _show)