Ctrl K
ComunidadGuías…

mermaid.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
from __future__ import print_function

import re


SYSTEM_SCHEMAS = {
    "information_schema",
    "mysql",
    "performance_schema",
    "sys",
}


def cleanname(name):
    """Return a Mermaid-safe identifier fragment."""
    return re.sub(r"[^\d\w_]", "", str(name))


def split_table_name(full_name):
    parts = str(full_name).split(".", 1)
    if len(parts) != 2:
        return None, None
    return parts[0], parts[1]


def format_table_name(full_name):
    schema, table = split_table_name(full_name)
    if not schema or not table:
        return None

    normalized_schema = schema.lower()
    if normalized_schema in SYSTEM_SCHEMAS or normalized_schema.endswith("_sandbox"):
        return None

    return cleanname(schema + "_" + table)


def format_remarks(remarks):
    """Collapse remarks to one safe Mermaid quoted string."""
    value = " ".join(str(remarks).split())
    return value.replace("\\", "/").replace('"', "'")


def column_markers(column):
    markers = []
    if column.isPartOfPrimaryKey():
        markers.append("PK")
    if column.isPartOfForeignKey():
        markers.append("FK")
    if column.isPartOfUniqueIndex():
        markers.append("UK")
    return markers


def included_tables(catalog):
    tables = [table for table in catalog.tables if format_table_name(table.fullName)]
    return sorted(tables, key=lambda table: str(table.fullName).lower())


def validate_unique_names(tables):
    names = {}
    for table in tables:
        formatted_name = format_table_name(table.fullName)
        previous = names.get(formatted_name)
        if previous is not None and previous != str(table.fullName):
            raise ValueError(
                "Table names %s and %s both normalize to %s"
                % (previous, table.fullName, formatted_name)
            )
        names[formatted_name] = str(table.fullName)


def render(catalog):
    tables = included_tables(catalog)
    validate_unique_names(tables)
    included_full_names = {str(table.fullName) for table in tables}

    print("erDiagram")
    print("")

    for table in tables:
        print("  " + format_table_name(table.fullName) + " {")
        for column in table.columns:
            line = "    %s %s" % (
                cleanname(column.columnDataType.name),
                cleanname(column.name),
            )

            markers = column_markers(column)
            if markers:
                line += " " + ", ".join(markers)

            if column.hasRemarks():
                line += ' "' + format_remarks(column.remarks) + '"'

            print(line)
        print("  }")
        print("")

    relationships = set()
    for table in tables:
        for child_table in table.referencingTables:
            if str(child_table.fullName) not in included_full_names:
                continue
            relationships.add(
                (
                    format_table_name(table.fullName),
                    format_table_name(child_table.fullName),
                )
            )

    for parent_name, child_name in sorted(relationships):
        print(
            '  %s ||--o{ %s : "foreign key"'
            % (parent_name, child_name)
        )


# SchemaCrawler injects `catalog` into the script global namespace.
if "catalog" in globals():
    render(catalog)