#!/usr/bin/env python3 import json from pathlib import Path DATA = Path("data") REPORTS = Path("reports") def versions(): return sorted( [d for d in DATA.iterdir() if d.is_dir()], key=lambda p: [int(x) for x in p.name.split(".")] ) def modules(): first = versions()[0] return sorted(f.stem for f in first.glob("*.json")) class Module(object): def __init__(self, module): self.name = module self.versionen = {} self.classes = {} self.operations = [] def read_data(self): for version in versions(): vers = version.name self.versionen[vers] = {} infile = version / f"{self.name}.json" if not infile.exists(): continue data = json.loads(infile.read_text()) for f in data["files"]: f['version'] = vers file = f['file'] classe = f.get('class', 'ohne') method = f.get('method', 'ohne') if classe not in self.versionen[vers]: self.versionen[vers][classe] = [] self.versionen[vers][classe].append(f) try: if vers not in self.classes[classe]: self.classes[classe][vers] = [] except KeyError: self.classes[classe] = {} self.classes[classe][vers] = [] self.classes[classe][vers].append(f) for o in f['operations']: o['class'] = classe o['method'] = method o['file'] = file o['version'] = vers self.operations.append(o) def operation_key(self, operation): # kwargs are intentionally ignored for now. # The key is only used to collapse repeated migration blocks; # it is not intended to establish semantic equality. return ( operation.get("phase"), operation.get("operation"), operation.get("object"), tuple(operation.get("args", [])), operation.get("condition"), ) def block_key(self, operations): return tuple(self.operation_key(op) for op in operations) def write_data(self): outfile = REPORTS / f"{self.name}.md" with outfile.open("w", encoding="utf8") as out: out.write(f"# {self.name}\n\n") # iterate all classes for c in sorted(self.classes): classe = self.classes[c] out.write(f"## Klasse {c}\n\n") last_blocks = None # iterate all Versions for k in sorted(classe.keys()): v = classe.get(k, None) if not v: continue if last_blocks == None: last_blocks = len(v) * [None,] out.write(f"### {k}\n\n") # iterate all files / occureces for i, m in enumerate(v): # repeating? key = self.block_key(m["operations"]) old_block = last_blocks[i] if key == old_block: continue elif old_block and key[:len(old_block)] == old_block: m["operations"] = m['operations'][len(old_block):] last_blocks[i] = key out.write(f"file: {m['file']} : {m['line']}\n") for op in m["operations"]: out.write( f"- **{op['phase']}** " f"`{op['operation']}`\n" ) if "condition" in op: out.write( f" - condition: `{op['condition']}`\n" ) if op["object"]: out.write( f" - object: `{op['object']}`\n" ) if op["args"]: out.write( f" - args: `{', '.join(op['args'])}`\n" ) if op["kwargs"]: out.write(" - kwargs:\n") for k, v in op["kwargs"].items(): out.write( f" - {k}: `{v}`\n" ) out.write("\n") out.write("\n") def main(): REPORTS.mkdir(exist_ok=True) for module in modules(): print(module) mod = Module(module) mod.read_data() mod.write_data() if __name__ == "__main__": main()