class TripleCounter(dict):
def increment(self, keys, amount):
try:
a, b, c = keys
except ValueError as error:
msg = f"keys must be a tuple of length 3, got {keys!r}"
raise ValueError(msg) from error
if a not in self:
self[a] = {}
if b not in self[a]:
self[a][b] = Counter()
self[a][b][c] += amount
def tree(self):
result = {}
for a, sub_dict in self.items():
sub_counter = Counter()
for b, c_counter in sub_dict.items():
sub_counter[b] += c_counter.total()
a_count = sub_counter.total()
result[a] = Node(
a,
{
"count": a_count,
"type_counter": sub_counter,
"value_or_length_counter": sub_dict,
},
)
root = None
for key, node in result.items():
if not key:
root = node
continue
parent_key = key[:-1]
parent = result[parent_key]
node.parent = parent
parent.children.append(node)
return root
def add_object(self, d):
for keys in iter_object(d, []):
self.increment(keys, 1)
@classmethod
def from_objects(cls, objects):
counter = cls()
for obj in objects:
counter.add_object(obj)
return counter
def html(self):
with open(TEMPLATE_FILENAME) as infile:
html = infile.read()
with open(CSS_FILENAME) as infile:
css = infile.read()
tree = self.tree()
html = html.replace("{{style}}", css)
html = html.replace("{{tree}}", tree.html())
html = html.replace("{{details}}", tree.details_html())
return html