Venkat V
Fresh start with clean LFS tracking
c2fb848
# flowchart_builder.py
# Arrow and graph logic for converting detected flowchart elements to structured JSON
from shapely.geometry import box, Point
from collections import defaultdict, deque
def map_arrows(nodes, arrows):
"""
Matches arrows to nodes based on geometric endpoints.
Returns a list of (source_id, target_id, label) edges.
"""
for node in nodes:
node["shape"] = box(*node["bbox"])
edges = []
for arrow in arrows:
tail_point = Point(arrow["tail"])
head_point = Point(arrow["head"])
label = arrow.get("label", "")
source = next((n["id"] for n in nodes if n["shape"].contains(tail_point)), None)
target = next((n["id"] for n in nodes if n["shape"].contains(head_point)), None)
if source and target and source != target:
edges.append((source, target, label))
return edges
def detect_node_type(text):
"""
Heuristic-based type detection from node text.
"""
text_lower = text.lower()
if "start" in text_lower:
return "start"
if "end" in text_lower or "full" in text_lower:
return "end"
if "?" in text or "yes" in text_lower or "no" in text_lower:
return "decision"
return "process"
def build_flowchart_json(nodes, edges):
"""
Constructs flowchart JSON structure with parent and branching info.
"""
graph = {}
reverse_links = defaultdict(list)
edge_labels = {}
for node in nodes:
text = node.get("text", "").strip()
graph[node["id"]] = {
"text": text,
"type": node.get("type") or detect_node_type(text),
"next": []
}
for src, tgt, label in edges:
graph[src]["next"].append(tgt)
reverse_links[tgt].append(src)
edge_labels[(src, tgt)] = label.lower().strip()
start_nodes = [nid for nid in graph if len(reverse_links[nid]) == 0]
flowchart_json = {
"start": start_nodes[0] if start_nodes else None,
"steps": []
}
visited = set()
queue = deque(start_nodes)
while queue:
curr = queue.popleft()
if curr in visited:
continue
visited.add(curr)
node = graph[curr]
step = {
"id": curr,
"text": node["text"],
"type": node["type"]
}
parents = reverse_links[curr]
if len(parents) == 1:
step["parent"] = parents[0]
elif len(parents) > 1:
step["parents"] = parents
next_nodes = node["next"]
if node["type"] == "decision" and len(next_nodes) >= 2:
step["branches"] = {}
for tgt in next_nodes:
label = edge_labels.get((curr, tgt), "")
if "yes" in label:
step["branches"]["yes"] = tgt
elif "no" in label:
step["branches"]["no"] = tgt
else:
step["branches"].setdefault("unknown", []).append(tgt)
queue.extend(next_nodes)
elif len(next_nodes) == 1:
step["next"] = next_nodes[0]
queue.append(next_nodes[0])
elif len(next_nodes) > 1:
step["next"] = next_nodes
queue.extend(next_nodes)
flowchart_json["steps"].append(step)
return flowchart_json