Skip to content

Instantly share code, notes, and snippets.

@dz0
Last active October 12, 2016 09:08
Show Gist options
  • Select an option

  • Save dz0/ef4bea4e6f4aaf21f084c06190efecf6 to your computer and use it in GitHub Desktop.

Select an option

Save dz0/ef4bea4e6f4aaf21f084c06190efecf6 to your computer and use it in GitHub Desktop.
def table_template(table):
from gluon.html import TR, TD, TABLE, TAG
def FONT(*args, **kwargs):
return TAG.font(*args, **kwargs)
def types(field):
f_type = field.type
if not isinstance(f_type,str):
return ' '
elif f_type == 'string':
return field.length
elif f_type == 'id':
return B('pk')
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
return B('fk')
else:
return ' '
def type_repr(field):
result = field.type
if 'reference' in field.type:
result = field.type.replace('reference ', '--> ')
if field.name.endswith('_id') and field.name[:-3]==result[len('--> '):] :
result = '--> '
return result
# This is horribe HTML but the only one graphiz understands
rows = []
cellpadding = 4
color = "#000000"
bgcolor = "#FFFFFF"
face = "Helvetica"
face_bold = "Helvetica Bold"
border = 0
rows.append(TR(TD(FONT(table, _face=face_bold, _color='blue'), # _size="20" doesn't work..
_colspan=3, _cellpadding=cellpadding,
_align="center", _bgcolor=bgcolor)))
for row in db[table]:
if is_interesting_field( row.name +" "+ row.type +" "+ str(types(row)) ):
rows.append(TR(TD(FONT(row.name, _color=color, _face=face_bold),
_align="left", _cellpadding=cellpadding,
_border=border),
TD(FONT(type_repr(row), _color=color, _face=face),
_align="left", _cellpadding=cellpadding,
_border=border),
TD(FONT(types(row), _color=color, _face=face),
_align="center", _cellpadding=cellpadding,
_border=border)))
return "< %s >" % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1,
_cellborder=0, _cellspacing=0)
).xml()
graph_view="""
{{if request.function=='graph_model':}}
<h2>{{=T("Graph Model")}}</h2>
{{if not pgv:}}
{{=T('pygraphviz library not found')}}
{{elif not databases:}}
{{=T("No databases in this application")}}
{{else:}}
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<i class="icon-download"></i> {{=T('Save model as...')}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['png'], vars=request.vars)}}">png</a></li>
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['svg'], vars=request.vars)}}">svg</a></li>
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['pdf'], vars=request.vars)}}">pdf</a></li>
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['ps'], vars=request.vars)}}">ps</a></li>
<li><a href="{{=URL('appadmin', 'bg_graph_model', args=['dot'], vars=request.vars)}}">dot</a></li>
</ul>
</div>
<br />
{{=IMG(_src=URL('appadmin', 'bg_graph_model', vars=request.vars))}}
{{pass}}
{{pass}}
"""
def is_interesting_field( fieldname ):
show_fields=(request.vars.show_fields or '').replace('%20', ' ').split()
if not show_fields: return True
for word in show_fields:
if word in fieldname:
return True # include
if word.startswith('-') and word[1:] in fieldname:
return False # or exclude
def is_important_force_exceptions_first(tablename): # DEPRECATED
# in views/appadmin.html forward vars=request.vars: =IMG(_src=URL('appadmin', 'bg_graph_model', vars=request.vars)
# graph_model?table_filters=...&field_filters=...&show_fields=fk%20pk
table_filters=(request.vars.table_filters or 'invoice sales_order -blind -good -batch -discount -shipment').replace('%20', ' ').split()
field_filters=(request.vars.field_filters or 'user').replace('%20', ' ').split()
# if request.vars.filters:
# filters=request.vars.filters.split()
# match by table name
excluding_filters = [word[1:] for word in table_filters if word.startswith('-')]
for word in excluding_filters:
if word in tablename:
return False # force exclude first
for word in table_filters:
if word in tablename:
return True # include
# match by field name
for field in db[tablename]:
for word in field_filters: # or one of it's fields' names
if word in field.name:
return True
def is_important(tablename):
# in views/appadmin.html forward vars=request.vars: =IMG(_src=URL('appadmin', 'bg_graph_model', vars=request.vars)
# graph_model?table_filters=...&field_filters=...
table_filters=(request.vars.table_filters or '-sales_order_good_ sales_order_good -blind -good -batch -discount -shipment invoice sales_order').replace('%20', ' ').split()
field_filters=(request.vars.field_filters or request.vars.table_filters or 'pk fk').replace('%20', ' ').split()
# if request.vars.filters:
# filters=request.vars.filters.split()
for word in table_filters:
if word in tablename:
return True # include
if word.startswith('-') and word[1:] in tablename:
return False # or exclude
# match by field name
for field in db[tablename]:
for word in field_filters: # or one of it's fields' names
if word in field.name:
return True # include
if word.startswith('-') and word[1:] in field.name:
return False # or exclude
def bg_graph_model():
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
subgraphs = dict()
for tablename in db.tables:
if hasattr(db[tablename],'_meta_graphmodel'):
meta_graphmodel = db[tablename]._meta_graphmodel
else:
meta_graphmodel = dict(group=request.application, color='#ECECEC')
group = meta_graphmodel['group'].replace(' ', '')
if not subgraphs.has_key(group):
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
subgraphs[group]['tables'].append(tablename)
else:
subgraphs[group]['tables'].append(tablename)
graph.add_node(tablename, name=tablename, shape='plaintext',
label=table_template(tablename)
if is_important(tablename) else tablename
)
for n, key in enumerate(subgraphs.iterkeys()):
graph.subgraph(nbunch=subgraphs[key]['tables'],
name='cluster%d' % n,
style='filled',
color=subgraphs[key]['meta']['color'],
label=subgraphs[key]['meta']['group'])
important_tables = set([])
for tablename in db.tables:
for field in db[tablename]:
f_type = field.type
if isinstance(f_type,str) and (
f_type.startswith('reference') or
f_type.startswith('list:reference')):
referenced_table = f_type.split()[1].split('.')[0]
n1 = graph.get_node(tablename)
n2 = graph.get_node(referenced_table)
if request.vars.neighbours=='0': # show only filtered, &neighbours=0
if is_important(tablename) : important_tables.add( tablename )
if is_important(referenced_table) : important_tables.add( referenced_table )
if is_important(tablename) and is_important(referenced_table):
graph.add_edge(n1, n2, color="#4C4C4C", label='')
else: # default: show neighbours
if is_important(tablename) or is_important(referenced_table):
important_tables.add( tablename )
important_tables.add( referenced_table )
graph.add_edge(n1, n2, color="#4C4C4C", label='')
# import rpdb2; rpdb2.start_embedded_debugger("a")
# from gluon.debug import dbg; dbg.set_trace() # stop here
for tablename in db.tables:
if not tablename in important_tables:
graph.delete_node( tablename )
graph.layout()
if not request.args:
# response.headers['Content-Type'] = 'image/png'
# return graph.draw(format='png', prog='dot')
response.headers['Content-Type'] = 'image/svg+xml'
return graph.draw(format='svg', prog='dot')
else:
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
if request.args(0) == 'dot':
return graph.string()
else:
return graph.draw(format=request.args(0), prog='dot')
# src= https://gist.github.com/dz0/ef4bea4e6f4aaf21f084c06190efecf6
def graph_model():
return dict(databases=databases, pgv=pgv)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment