mirror of
https://github.com/jakobkordez/call-tester.git
synced 2025-05-16 08:40:28 +00:00
209 lines
5.1 KiB
Python
209 lines
5.1 KiB
Python
import xml.etree.ElementTree as ET
|
|
from sys import argv
|
|
from collections import defaultdict
|
|
import re
|
|
|
|
root = ET.parse(argv[1]).getroot()
|
|
|
|
entities = {}
|
|
prefixes = []
|
|
|
|
|
|
def capitalize(s: str):
|
|
s = s.lower()
|
|
for m in re.findall(r"\b\w+", s):
|
|
s = s.replace(m, m.capitalize(), 1)
|
|
return s
|
|
|
|
|
|
for child in root:
|
|
if child.tag == "entities":
|
|
for entity in child:
|
|
name = adif = None
|
|
for prop in entity:
|
|
if prop.tag == "name":
|
|
name = prop.text
|
|
if prop.tag == "adif":
|
|
adif = int(prop.text)
|
|
entities[adif] = capitalize(name)
|
|
if child.tag == "prefixes":
|
|
for prefix in child:
|
|
call = entity = end = None
|
|
for prop in prefix:
|
|
if prop.tag == "call":
|
|
call = prop.text
|
|
if prop.tag == "adif":
|
|
entity = int(prop.text)
|
|
if prop.tag == "end":
|
|
end = prop.text
|
|
if end and end < "2024-06-20":
|
|
continue
|
|
# if entity != 1:
|
|
# continue
|
|
prefixes.append((call, entity))
|
|
|
|
# print(len(prefixes), "prefixes found")
|
|
|
|
# Build trie
|
|
|
|
|
|
class Node:
|
|
counter = 0
|
|
|
|
def __init__(self):
|
|
Node.counter += 1
|
|
self.id = Node.counter
|
|
self.parent: Node = None
|
|
self.children: dict[str, Node] = {}
|
|
self.entity: int = None
|
|
|
|
|
|
root = Node()
|
|
allNodes = []
|
|
|
|
|
|
# Build trie
|
|
def insert(node: Node, prefix, entity):
|
|
if not prefix:
|
|
if node.entity and node.entity != entity:
|
|
print(f"Conflict: {node.entity} vs {entity}")
|
|
node.entity = entity
|
|
return
|
|
nextNode = node.children.get(prefix[0])
|
|
if not nextNode:
|
|
nextNode = Node()
|
|
nextNode.parent = node
|
|
node.children[prefix[0]] = nextNode
|
|
allNodes.append(nextNode)
|
|
insert(nextNode, prefix[1:], entity)
|
|
|
|
|
|
for call, entity in prefixes:
|
|
insert(root, call, entity)
|
|
|
|
|
|
# Merge nodes
|
|
# print("Merge start,", len(allNodes), "nodes")
|
|
|
|
|
|
def canMerge(node: Node, other: Node):
|
|
if node.entity != other.entity:
|
|
return False
|
|
l = set(node.children.keys()).union(other.children.keys())
|
|
return all(node.children.get(k) == other.children.get(k) for k in l)
|
|
|
|
|
|
def merge(first: Node, second: Node):
|
|
# if first.id > second.id:
|
|
# first, second = second, first
|
|
for k, c in second.children.items():
|
|
c.parent = first
|
|
for k, c in second.parent.children.items():
|
|
if c == second:
|
|
second.parent.children[k] = first
|
|
allNodes.remove(second)
|
|
|
|
|
|
anyChanged = True
|
|
while anyChanged:
|
|
anyChanged = False
|
|
i = 0
|
|
while i < len(allNodes):
|
|
node = allNodes[i]
|
|
for other in allNodes:
|
|
if node != other and canMerge(node, other):
|
|
anyChanged = True
|
|
merge(node, other)
|
|
break
|
|
else:
|
|
i += 1
|
|
|
|
for i in range(len(allNodes)):
|
|
for j in range(i + 1, len(allNodes)):
|
|
if canMerge(allNodes[i], allNodes[j]):
|
|
raise Exception("Merge not completed")
|
|
|
|
# for n in allNodes:
|
|
# print(n.children.items())
|
|
# print("Merge done,", len(allNodes), "nodes left")
|
|
|
|
# Save compiled trie
|
|
|
|
for node in [root, *allNodes]:
|
|
if node.entity:
|
|
print(f"{node.id}={node.entity}")
|
|
for c in set(node.children.values()):
|
|
print(
|
|
f"{node.id}-{''.join(sorted(k for k, v in node.children.items() if v == c))}-{c.id}"
|
|
)
|
|
|
|
# Print trie
|
|
|
|
|
|
def rangeToStr(a: str, b: str):
|
|
if a == b:
|
|
return a
|
|
if ord(b) - ord(a) > 1:
|
|
return f"{a}-{b}"
|
|
return f"{a}{b}"
|
|
|
|
|
|
def toRange(s: list[str]):
|
|
s = sorted(s)
|
|
ranges = [(s[0], s[0])]
|
|
for c in s[1:]:
|
|
if ord(c) == ord(ranges[-1][1]) + 1:
|
|
ranges[-1] = (ranges[-1][0], c)
|
|
else:
|
|
ranges.append((c, c))
|
|
return "".join(rangeToStr(a, b) for a, b in ranges)
|
|
|
|
|
|
defined = set()
|
|
|
|
|
|
def genGraphvizNode(node: Node):
|
|
label = "" if not node.entity else entities[node.entity]
|
|
shape = "circle" if not node.entity else "box"
|
|
return f' {node.id} [label="{label}" shape="{shape}"];'
|
|
|
|
|
|
def toGraphviz(node: Node, root=True):
|
|
ret = []
|
|
if node in defined:
|
|
return ret
|
|
defined.add(node)
|
|
|
|
ret.append(genGraphvizNode(node))
|
|
|
|
dd = defaultdict(list)
|
|
for k, c in node.children.items():
|
|
dd[c.id].append(k.replace("/", "_"))
|
|
ret += toGraphviz(c, False)
|
|
|
|
for k in sorted(dd.keys()):
|
|
ret.append(f' {node.id} -> {k} [label="{toRange(dd[k])}"];')
|
|
|
|
if root:
|
|
curr = node
|
|
while curr.parent:
|
|
ret.append(genGraphvizNode(curr.parent))
|
|
label = toRange(k for k, v in curr.parent.children.items() if v == curr)
|
|
ret.append(f' {curr.parent.id} -> {curr.id} [label="{label}"];')
|
|
curr = curr.parent
|
|
ret = "\n".join(["digraph {", *ret, "}"])
|
|
|
|
return ret
|
|
|
|
|
|
def traverse(s: str, node: Node = root):
|
|
if not s:
|
|
return node
|
|
nextNode = node.children.get(s[0])
|
|
if not nextNode:
|
|
return None
|
|
return traverse(s[1:], nextNode)
|
|
|
|
|
|
# print(toGraphviz(traverse("R")))
|