---
+++
@@ -8,7 +8,7 @@
def render(self, gv_file):
if self.exists():
return
- cmd = ['dot', '-Tpng', '-o', self.filename, gv_file]
+ cmd = ['dot', '-Tcmapx', '-o' + self.filename + '.map', '-Tpng', '-o' + self.filename, gv_file]
try:
output = subprocess.check_output(cmd)
---
+++
@@ -6,101 +6,66 @@
splines = true;
node [shape=plaintext];
- subgraph {
- rank = sink;
- label = "Legend table";
- graph[style=solid];
- bgcolor=gray;
-
- // Legend
- legend [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TR>
- <TD BGCOLOR="white"><B>Legend (size without indexes/total size/lines count)</B></TD>
- <TD >I'</TD>
- <TD >NN'</TD>
- <TD >U'</TD>
- </TR>
- <TR>
- <TD ALIGN="left" BGCOLOR="gray">column</TD>
- <TD BGCOLOR="gray"></TD>
- <TD BGCOLOR="gray"></TD>
- <TD BGCOLOR="gray"></TD>
- <TD ALIGN="right" BGCOLOR="gray">data type</TD>
- </TR>
- <TR>
- <TD ALIGN="left" BGCOLOR="limegreen">column_with_a_foreign_key</TD>
- <TD BGCOLOR="limegreen"></TD>
- <TD BGCOLOR="limegreen"></TD>
- <TD BGCOLOR="limegreen"></TD>
- <TD ALIGN="right" PORT="legend_foreign_key" BGCOLOR="limegreen">data type</TD>
- </TR>
- <TR>
- <TD ALIGN="left" BGCOLOR="gray">I', NN', U': Index, Non null, Unique</TD>
- <TD BGCOLOR="gray"></TD>
- <TD BGCOLOR="gray"></TD>
- <TD BGCOLOR="gray"></TD>
- <TD ALIGN="right" BGCOLOR="gray"></TD>
- </TR>
- </TABLE>>];
- legend2 [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TR>
- <TD BGCOLOR="firebrick1"><B>Example table with errors</B></TD>
- <TD BGCOLOR="firebrick1">I</TD>
- <TD BGCOLOR="firebrick1">NN</TD>
- <TD BGCOLOR="firebrick1">U</TD>
- <TD BGCOLOR="firebrick1"></TD>
- <TD BGCOLOR="firebrick1">table errors</TD>
- </TR>
- <TR>
- <TD ALIGN="left" PORT="legend_primary_key" BGCOLOR="firebrick1">column_with_a_primary_key</TD>
- <TD BGCOLOR="firebrick1"></TD>
- <TD BGCOLOR="firebrick1"></TD>
- <TD BGCOLOR="firebrick1"></TD>
- <TD ALIGN="right" BGCOLOR="firebrick1">data type</TD>
- <TD ALIGN="left" BGCOLOR="firebrick1">column errors</TD>
- </TR>
- </TABLE>>];
-
- // Link the two table
- // :e / :w to tell to link from the East border of the cell to the West border
- // of the other cell
- legend:legend_foreign_key:e -> legend2:legend_primary_key:w;
- image -> legend [style=invis];
- }
"""
GV_FOOTER = """
}
"""
GV_SEPARATOR = '__42deadbeef__'
+
class GV(OutputFile):
- def __init__(self, filename):
+ def __init__(self, filename, minimap=False):
super(GV, self).__init__(filename)
- self.tables = {}
+ self.tables = []
+ self.minimap = minimap
def add_header(self):
self.write(GV_HEADER)
- def add_table(self, name, errors, sizes, keys, indexes, columns):
+ def add_table(self, name, errors, sizes, keys, indexes, columns, highlight=False):
+ if name in self.tables:
+ return
+ self.tables.append(name)
sizes = sizes[:2] + [sizes[4]]
- # Display the table in red when there is no entries
+
color = 'white'
if len(errors):
color = 'firebrick1'
- table = """{name} [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ if self.minimap:
+ fontsize = 'fontsize="7"'
+ elif highlight:
+ fontsize = 'fontsize="16"'
+ else:
+ fontsize = 'fontsize="10"'
+
+ url = ''
+ if not highlight:
+ url = 'URL="%s.html"' % name
+
+ table = """{name} [ {url} {fontsize} label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR>
<TD PORT="{name}" BGCOLOR="{color}"><B>{name} ({sizes})</B></TD>
- <TD BGCOLOR="{color}">I</TD>
- <TD BGCOLOR="{color}">NN</TD>
- <TD BGCOLOR="{color}">U</TD>
- <TD BGCOLOR="{color}"></TD>
- <TD ALIGN="left" BGCOLOR="{color}">{errors}</TD>
- </TR>""".format(name=name,
+ """
+ if not self.minimap:
+ table += """
+ <TD BGCOLOR="{color}">I</TD>
+ <TD BGCOLOR="{color}">NN</TD>
+ <TD BGCOLOR="{color}">U</TD>
+ <TD BGCOLOR="{color}"></TD>
+ <TD ALIGN="left" BGCOLOR="{color}">{errors}</TD>"""
+ table += '</TR>'
+ table = table.format(name=name,
color=color,
sizes='/'.join([str(n) for n in sizes]),
- errors=', '.join(errors))
+ errors=', '.join(errors),
+ fontsize=fontsize,
+ url=url)
self.write(table)
+
+ if self.minimap:
+ self.write("</TABLE>>];")
+ return
for col in columns:
col_id = "%s%s%s" % (name, GV_SEPARATOR, col.name)
@@ -139,24 +104,36 @@
self.write("</TABLE>>];")
def add_constraint(self, src, dst):
- self.write("%s:%s1:e -> %s:%s:w;" % (src[0], GV_SEPARATOR.join(src),
- dst[0], GV_SEPARATOR.join(dst)))
+ if self.minimap:
+ self.write("%s -> %s;" % (src[0], dst[0]))
+ else:
+ self.write("%s:%s1:e -> %s:%s:w;" % (src[0], GV_SEPARATOR.join(src),
+ dst[0], GV_SEPARATOR.join(dst)))
def add_missing_constraint(self, table, column, other_table, error):
- self.write('%s:%s1:e -> %s [label="%s" fontcolor="red", color="red"];' % (table, GV_SEPARATOR.join([table, column]),
+ if self.minimap:
+ self.write('%s -> %s [fontcolor="red", color="red"];' % (table, other_table))
+ else:
+ self.write('%s:%s1:e -> %s [label="%s" fontcolor="red", color="red"];' % (table, GV_SEPARATOR.join([table, column]),
other_table, error))
def add_inherited(self, src, dst):
# :s for the South side of the table
- self.write('%s -> %s:%s [label="inherits" fontcolor=limegreen color=limegreen];' % (dst, src, src))
+ if self.minimap:
+ self.write('%s -> %s [fontcolor="limegreen" color="limegreen"];' % (dst, src))
+ else:
+ self.write('%s -> %s:n [label="inherits" fontcolor="limegreen" color="limegreen"];' % (dst, src))
def add_duplicate(self, src, dst, duplicate_type):
- self.write('%s -> %s:%s [label="%s" fontcolor=red color=red];' % (src, dst, dst, ', '.join(duplicate_type)))
+ if self.minimap:
+ self.write('%s -> %s [fontcolor="red" color="red"];' % (src, dst))
+ else:
+ self.write('%s -> %s:n [label="%s" fontcolor="red" color="red"];' % (src, dst, ', '.join(duplicate_type)))
def add_footer(self):
self.write(GV_FOOTER)
def add_namespace(self, namespace, tables):
- self.write('%s [label="%s", shape=square, fontsize=64];' % (namespace, namespace.title()))
+ self.write('%s [label="%s", shape="square"];' % (namespace, namespace.title()))
for table in tables:
- self.write('%s -> %s:%s [style="dashed"];' % (namespace, table, table))
+ self.write('%s -> %s [style="dashed"];' % (namespace, table))
---
+++
@@ -1,7 +1,7 @@
import os
from db_size import humanize
-from output_file import OutputFile
+from output_file import OutputFile, OUTPUT_DIR
HTML_BODY = """
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
@@ -32,7 +32,25 @@
self.write(HTML_FOOTER)
-class HtmlFile(OutputFile):
+class TableFile(OutputFile):
+ @staticmethod
+ def get_table_html(img):
+ map_file = img + '.map'
+ img = os.path.basename(img)
+ html = '<img src="%s" usemap="#mainmap" /><map id="mainmap" name="mainmap">' % img
+ map_file = open(map_file, 'r')
+ html += map_file.read()
+ map_file.close()
+ html += '</map>'
+ return html
+
+ def render(self):
+ self.write(HTML_BODY % self.filename)
+ self.write('<a href="index.html">Back...</a><br/>')
+ self.write(TableFile.get_table_html(self.filename.replace('.html', '.png')))
+ self.write(HTML_FOOTER)
+
+class IndexFile(OutputFile):
def render(self, objects, db_size):
for obj in objects:
for _obj in obj:
@@ -66,9 +84,11 @@
for table in tables:
self.write('<tr>')
- for _table in table:
+ for no, _table in enumerate(table):
+ if no == 0:
+ _table = '<a href="%s.html" >%s</a>' % (_table, _table)
self.write('<td>%s</td>' % _table)
self.write('</tr>')
self.write('</table>')
-
+ self.write(TableFile.get_table_html(os.path.join(OUTPUT_DIR, 'map.png')))
self.write(HTML_FOOTER)
---
+++
@@ -8,7 +8,7 @@
from db_size import DBSize
from dot import DotFile
from gv import GV
-from html import HtmlFile
+from html import IndexFile, TableFile
from output_file import OutputFile
@@ -27,55 +27,85 @@
OutputFile.create_outputdir()
- gv = GV('relationship.gv')
+ gv_map = GV('map.gv', minimap=True)
+ gv_tables = {}
+ tables = {}
+
db_size = DBSize()
# DB size pies
for table in db.get_tables():
sizes = db.get_table_size(table)
db_size.add_table(table, sizes)
+ gv_tables[table] = GV(table + '.gv')
+ gv_tables[table].add_header()
+
imgs = db_size.render()
# Tables
- if not gv.exists():
- gv.add_header()
+ if not gv_map.exists():
+ gv_map.add_header()
+
for table in db.get_tables():
columns = db.get_columns(table)
sizes = db.get_table_size(table)
keys = db.get_table_keys(table)
indexes = db.get_table_index(table)
errors = db.get_table_errors(table)
- gv.add_table(table, errors, sizes, keys, indexes, columns)
+ gv_map.add_table(table, errors, sizes, keys, indexes, columns)
+ gv_tables[table].add_table(table, errors, sizes, keys, indexes, columns, True)
+ tables[table] = (table, errors, sizes, keys, indexes, columns)
# Constraints
for table in db.get_tables():
for src, dst in db.get_foreign_keys(table):
- gv.add_constraint(src, dst)
+ gv_tables[table].add_table(*tables[dst[0]])
+ gv_tables[table].add_constraint(src, dst)
+ gv_tables[dst[0]].add_table(*tables[table])
+ gv_tables[dst[0]].add_constraint(src, dst)
+ gv_map.add_constraint(src, dst)
+
# Missing constraints
for table in db.get_tables():
for constraint in db.get_missing_constraints(table):
- gv.add_missing_constraint(*constraint)
+ gv_tables[table].add_table(*tables[constraint[2]])
+ gv_tables[table].add_missing_constraint(*constraint)
+ gv_tables[constraint[2]].add_table(*tables[table])
+ gv_tables[constraint[2]].add_missing_constraint(*constraint)
+ gv_map.add_missing_constraint(*constraint)
# Inheritance link
for src, dst in db.get_inherited_tables():
- gv.add_inherited(src, dst)
+ gv_map.add_inherited(src, dst)
+ gv_tables[src].add_table(*tables[dst])
+ gv_tables[src].add_inherited(src, dst)
+ gv_tables[dst].add_table(*tables[src])
+ gv_tables[dst].add_inherited(src, dst)
- for tables, duplicate_type in db.get_duplicated_tables().items():
- src, dst = tables.split('/')
- gv.add_duplicate(src, dst, duplicate_type)
+ for _tables, duplicate_type in db.get_duplicated_tables().items():
+ src, dst = _tables.split('/')
+ gv_map.add_duplicate(src, dst, duplicate_type)
+ gv_tables[src].add_table(*tables[dst])
+ gv_tables[src].add_duplicate(src, dst, duplicate_type)
+ gv_tables[dst].add_table(*tables[src])
+ gv_tables[dst].add_duplicate(src, dst, duplicate_type)
namespaces = db.get_namespaces().items()
if len(namespaces) != 1:
# Show namespaces only when there are more than one
for namespace, tables in namespaces:
- gv.add_namespace(namespace, tables)
+ gv_map.add_namespace(namespace, tables)
- gv.add_footer()
+ for gv in [gv_map] + list(gv_tables.values()):
+ gv.add_footer()
+ gv.close()
- gv.close()
- dot = DotFile('relationship.png')
- dot.render(gv.filename)
+ dot = DotFile(gv.basename.replace('.gv', '.png'))
+ dot.render(gv.filename)
+
+ table_html = TableFile(gv.basename.replace('.gv', '.html'))
+ table_html.render()
# Generate HTML files
- html_file = HtmlFile('index.html')
+ html_file = IndexFile('index.html')
html_file.render(imgs, db_size)
---
+++
@@ -6,6 +6,7 @@
class OutputFile:
def __init__(self, filename):
self.fd = None
+ self.basename = filename
self.filename = os.path.join(OUTPUT_DIR, filename)
@staticmethod
Generated with KisssPM