#!/usr/bin/env -S uv run --script # /// script # dependencies = [ # "networkx", # "jinja2", # ] # /// import os import ast import sys import json import networkx as nx from jinja2 import Template def find_manifest_files(base_path): manifest_files = [] for root, _, files in os.walk(base_path): if '__manifest__.py' in files: manifest_files.append(os.path.join(root, '__manifest__.py')) print(f"Found {len(manifest_files)} __manifest__.py files") return manifest_files def extract_dependencies(manifest_path): try: with open(manifest_path, 'r', encoding='utf-8') as f: content = f.read() manifest_dict = ast.literal_eval(content) module_name = os.path.basename(os.path.dirname(manifest_path)) depends = manifest_dict.get('depends', []) return module_name, depends except Exception as e: print(f"Error parsing {manifest_path}: {str(e)}") return None, [] def build_graph(module_path): G = nx.DiGraph() manifest_files = find_manifest_files(module_path) if not manifest_files: print("Error: No __manifest__.py files found") sys.exit(1) for manifest_file in manifest_files: module_name, depends = extract_dependencies(manifest_file) if module_name: G.add_node(module_name) for dep in depends: if dep: G.add_edge(module_name, dep) print(f"Graph built with {len(G.nodes())} modules and {len(G.edges())} dependencies") return G def generate_visjs_html(G, output_file='tmp/odoo-module-dependencies.html'): """Generate a self-contained HTML with Vis.js visualization.""" nodes = [] for node in G.nodes(): nodes.append({ 'id': node, 'label': node, 'color': '#4CAF50' if node.startswith('base') else '#2196F3', 'shape': 'box', 'margin': 10, 'font': {'size': 14} }) edges = [{'from': u, 'to': v, 'arrows': 'to'} for u, v in G.edges()] template = Template(''' Odoo Module Dependencies

Odoo Module Dependencies

Click on any module
''') with open(output_file, 'w', encoding='utf-8') as f: f.write(template.render(nodes=nodes, edges=edges)) print(f"Visualization saved to {os.path.abspath(output_file)}") def generate_mermaid_file(G, output_file='tmp/odoo-module-dependencies.mmd'): """Generate a Mermaid file from the dependency graph.""" lines = ["graph TD"] for u, v in G.edges(): lines.append(f" {u} --> {v}") with open(output_file, 'w', encoding='utf-8') as f: f.write('\n'.join(lines)) print(f"Mermaid diagram saved to {os.path.abspath(output_file)}") def main(): module_path = sys.argv[1] if len(sys.argv) > 1 else './odoo/addons' print(f"Analyzing modules in: {module_path}") G = build_graph(module_path) generate_visjs_html(G) generate_mermaid_file(G) if __name__ == '__main__': main()