Files
skills/hub/kvb_hub/graph.py
T

80 lines
3.7 KiB
Python
Raw Normal View History

from __future__ import annotations
from collections import Counter
from .council import HUMAN, agent, ordered, subagent
from .names import Directory
from .orbits import affinity
from .records import shown
from .register import ENDED, parties, status_of
CHAIR = "Maintainer"
def parent_of(roles: dict[str, str], known: set[str], fallback: str) -> str:
for letter in "AR":
names = sorted(name for name, letters in roles.items() if letter in letters and name in known)
if names:
return names[0]
return fallback
def duties(name: str, task: str, row: dict, holders: dict[str, dict[str, dict]]) -> dict:
entry = {"task": task, "title": shown(row.get("title")), "status": status_of(row), "worker": False, "letters": ""}
return holders.setdefault(name, {}).setdefault(task, entry)
def edges(radio: list[dict], grant_rows: list[dict], chair: str) -> list[dict]:
counts: Counter[tuple[str, str, str]] = Counter()
for row in (row for row in radio if row["source"]):
for target in row["targets"] or ["*"]:
counts[(row["source"], target, row["class"])] += 1
for grant in grant_rows:
counts[(chair, grant["target"], "grant")] += 1
return [{"source": s, "target": t, "class": c, "count": n} for (s, t, c), n in sorted(counts.items())]
def graph(radio: list[dict], grant_rows: list[dict], tasks: dict[str, dict], claims: list[dict], end: float, directory: Directory) -> dict:
current = {task: row for task, row in sorted(tasks.items()) if status_of(row) not in ENDED}
chair = directory.key(CHAIR, end)
workers = {task: agent(row.get("worker")) for task, row in current.items()}
keys = {row["source"] for row in radio} | {key for row in radio for key in row["targets"]}
keys |= {claim["session"] for claim in claims if claim["agent"] and claim["session"] != "-"}
keys |= {directory.key(name, end) for name in workers.values() if not subagent(name)}
found = sorted(keys - {"", HUMAN, chair}, key=lambda key: (directory.label(key, end).lower(), key))
known = {chair, *found}
moons: dict[str, tuple[str, str]] = {}
holders: dict[str, dict[str, dict]] = {}
for task, row in current.items():
roles: dict[str, str] = {}
for name, letters in parties(row).items():
key = directory.key(name, end)
roles[key] = roles.get(key, "") + letters
worker = workers[task]
holder = directory.key(worker, end)
if subagent(worker):
parent = parent_of(roles, known, chair)
holder = f"{worker} @ {parent}"
moons[holder] = (parent, worker)
if holder:
duties(holder, task, row, holders)["worker"] = True
for key, letters in roles.items():
duties(key, task, row, holders)["letters"] += letters
plan = [(HUMAN, "operator", None, HUMAN), (chair, "chair", None, directory.label(chair, end))]
plan += [(key, "session", None, directory.label(key, end)) for key in found]
plan += [(moon, "subagent", *moons[moon]) for moon in ordered(set(moons))]
nodes = [
{
"id": key,
"kind": node_kind,
"parent": parent,
"label": label,
"names": directory.names(key) or [label],
"timeline": directory.timeline(key) or [{"name": label, "since": end}],
"tasks": list(holders.get(key, {}).values()),
"claims": [claim for claim in claims if claim["session"] == key],
}
for key, node_kind, parent, label in plan
]
layout = affinity(nodes, radio, tasks, claims, end, lambda name: directory.key(name, end))
return {"nodes": nodes, "edges": edges(radio, grant_rows, chair), "layout": layout}