Mercurial > hg > ltpdarepo
changeset 251:cbbc747949f9
Add new sql2dot tool to generate database diagram.
author | Daniele Nicolodi <nicolodi@science.unitn.it> |
---|---|
date | Thu, 26 Apr 2012 19:03:39 +0200 |
parents | 239c7d077f20 |
children | 00cbec2d663a |
files | buildout.cfg setup.py src/ltpdarepo/sql2dot.py |
diffstat | 3 files changed, 74 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/buildout.cfg Wed Feb 15 16:09:26 2012 +0100 +++ b/buildout.cfg Thu Apr 26 19:03:39 2012 +0200 @@ -14,7 +14,7 @@ [instance] recipe = zc.recipe.egg:script eggs = ${install:eggs} -scripts = run admin +scripts = run admin sql2dot arguments = '${buildout:directory}/etc/ltpdarepo' # generate wsgi application
--- a/setup.py Wed Feb 15 16:09:26 2012 +0100 +++ b/setup.py Thu Apr 26 19:03:39 2012 +0200 @@ -53,6 +53,7 @@ entry_points={ 'console_scripts': [ 'run = ltpdarepo:main', - 'admin = ltpdarepo.admin:main', ] + 'admin = ltpdarepo.admin:main', + 'sql2dot = ltpdarepo.sql2dot:main' ] } )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ltpdarepo/sql2dot.py Thu Apr 26 19:03:39 2012 +0200 @@ -0,0 +1,71 @@ +# Copyright 2011 Daniele Nicolodi <nicolodi@science.unitn.it> +# +# This software may be used and distributed according to the terms of +# the GNU General Public License version 3 or any later version. + +import sys +import sqlalchemy + +from operator import itemgetter + +FONTSIZE = 10 +NODEWIDTH = 150 +RANKSEP = 1.5 +CELLPADDING = 3 + +def dump(username, password, hostname, database, out=sys.stdout): + url = sqlalchemy.engine.url.URL('mysql', + username=username, password=password, + host=hostname, database=database) + engine = sqlalchemy.engine.create_engine(url) + inspector = sqlalchemy.engine.reflection.Inspector.from_engine(engine) + dot_database_tables(inspector, out) + + +def dot_database_tables(inspector, out): + out.write('digraph g {\n') + out.write(' splines=spline\n') + out.write(' overlap=false\n') + out.write(' rankdir=LR\n') + out.write(' ranksep={}\n'.format(RANKSEP)) + out.write(' node [shape=plaintext fontsize={}]\n'.format(FONTSIZE)) + tables = sorted(inspector.get_table_names() + inspector.get_view_names()) + for table in tables: + dot_table_structure(inspector, table, out) + out.write('}\n') + + +def dot_table_structure(inspector, table, out): + # each table is node + out.write(' "{}" [\n'.format(table)) + + # primary keys + pks = inspector.get_primary_keys(table) + + # table name + rows = [] + rows.append('<tr><td width="{}">{}</td></tr>'.format(NODEWIDTH, table)) + + # columns + for column in sorted(inspector.get_columns(table), key=itemgetter('name')): + port = column['name'] + name = '<font>{}</font>'.format(port) + if port in pks: + name = '<font color="firebrick">{}</font>'.format(port) + rows.append('<tr><td align="left" port="{}">{} : {}</td></tr>'.format(port, name, column['type'].__visit_name__)) + + out.write(' label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="{}">{}</table>>\n'.format(CELLPADDING, ''.join(rows))) + out.write(' ]\n') + + # foreign keys represented as node relations + fks = inspector.get_foreign_keys(table) + for fk in fks: + src = '{}:{}'.format(fk['referred_table'], ''.join(fk['referred_columns'])) + # special case to link xxdata tables to the ao table + if table.endswith('data'): + src = 'ao:obj_id' + out.write(' {} -> {}:{}\n'.format( + src, table, ''.join(fk['constrained_columns']))) + +def main(*args, **kwargs): + dump(*sys.argv[1:])