summaryrefslogtreecommitdiff
path: root/demo/graphviz
diff options
context:
space:
mode:
authorgnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
committergnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
commit7a4f561e851fdc7246d804c3abb6748b8a4199a6 (patch)
treed2afc3463fd49625a9be482012f5c3bfcf7c42b9 /demo/graphviz
downloadgnowsys-7a4f561e851fdc7246d804c3abb6748b8a4199a6.tar.gz
master trunk of gnowsys-studio
Diffstat (limited to 'demo/graphviz')
-rw-r--r--demo/graphviz/__init__.py0
-rw-r--r--demo/graphviz/admin.py89
-rw-r--r--demo/graphviz/interfaces.py38
-rw-r--r--demo/graphviz/management/__init__.py0
-rw-r--r--demo/graphviz/management/commands/__init__.py0
-rw-r--r--demo/graphviz/management/commands/modelviz.py173
-rw-r--r--demo/graphviz/models.py119
-rw-r--r--demo/graphviz/templates/graphviz/dot_file.dot6
-rw-r--r--demo/graphviz/urls.py26
-rw-r--r--demo/graphviz/views.py61
10 files changed, 512 insertions, 0 deletions
diff --git a/demo/graphviz/__init__.py b/demo/graphviz/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/demo/graphviz/__init__.py
diff --git a/demo/graphviz/admin.py b/demo/graphviz/admin.py
new file mode 100644
index 0000000..fb81042
--- /dev/null
+++ b/demo/graphviz/admin.py
@@ -0,0 +1,89 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from django.contrib import admin
+from models import *
+
+class GraphAdmin(admin.ModelAdmin):
+ list_display = ('slug', 'name', 'content_type', 'view_dot_file', 'download_dot_file', 'download_image')
+ list_filter = ('content_type',)
+admin.site.register(Graph, GraphAdmin)
+
+
+class NodeVisualAdmin(admin.ModelAdmin):
+ list_display = ('shape', 'graph', 'content_type')
+ list_filter = ('content_type', 'graph')
+admin.site.register(NodeVisual, NodeVisualAdmin)
+
+
+class ArrowVisualAdmin(admin.ModelAdmin):
+ list_display = ('shape', 'modifier', 'graph', 'content_type')
+ list_filter = ('content_type', 'graph')
+admin.site.register(ArrowVisual, ArrowVisualAdmin)
+
+
+class CacheImageAdmin(admin.ModelAdmin):
+ list_display = ('graph', 'graphic', 'valid', 'file', 'date')
+ list_filter = ('valid', 'graph')
+admin.site.register(CacheImage, CacheImageAdmin)
diff --git a/demo/graphviz/interfaces.py b/demo/graphviz/interfaces.py
new file mode 100644
index 0000000..f1c76c9
--- /dev/null
+++ b/demo/graphviz/interfaces.py
@@ -0,0 +1,38 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""
+"""
+
+
+class IGraphRoot:
+ def gv_edges(self, graph):
+ raise Exception('not implemented')
+ def gv_nodes(self, graph):
+ raise Exception('not implemented')
+
+class INode:
+ def gv_node_label(self, graph):
+ return unicode(self)
+
+class IEdge:
+ def gv_ends(self, graph):
+ ''' should returns (node_source, node_target)
+ '''
+ raise Exception('not implemented')
+ def gv_edge_label(self, graph):
+ return unicode(self)
diff --git a/demo/graphviz/management/__init__.py b/demo/graphviz/management/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/demo/graphviz/management/__init__.py
diff --git a/demo/graphviz/management/commands/__init__.py b/demo/graphviz/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/demo/graphviz/management/commands/__init__.py
diff --git a/demo/graphviz/management/commands/modelviz.py b/demo/graphviz/management/commands/modelviz.py
new file mode 100644
index 0000000..30d8421
--- /dev/null
+++ b/demo/graphviz/management/commands/modelviz.py
@@ -0,0 +1,173 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+#!/usr/bin/env python
+import getopt, sys
+
+from optparse import make_option
+
+from django.template import Template, Context
+from django.db import models
+from django.db.models import get_models
+from django.db.models.fields.related import \
+ ForeignKey, OneToOneField, ManyToManyField
+
+from django.core.management.base import AppCommand
+
+try:
+ from django.db.models.fields.generic import GenericRelation
+except ImportError:
+ from django.contrib.contenttypes.generic import GenericRelation
+
+head_template = """
+digraph name {
+ fontname = "Helvetica"
+ fontsize = 8
+
+ node [
+ fontname = "Helvetica"
+ fontsize = 8
+ shape = "plaintext"
+ ]
+ edge [
+ fontname = "Helvetica"
+ fontsize = 8
+ ]
+
+"""
+
+body_template = """
+ {% for model in models %}
+ {% for relation in model.relations %}
+ {{ relation.target }} [label=<
+ <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+ <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+ ><FONT FACE="Helvetica Bold" COLOR="white"
+ >{{ relation.target }}</FONT></TD></TR>
+ </TABLE>
+ >]
+ {{ model.name }} -> {{ relation.target }}
+ [label="{{ relation.name }}"] {{ relation.arrows }};
+ {% endfor %}
+ {% endfor %}
+
+ {% for model in models %}
+ {{ model.name }} [label=<
+ <TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
+ <TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"
+ ><FONT FACE="Helvetica Bold" COLOR="white"
+ >{{ model.name }}</FONT></TD></TR>
+
+ {% if not disable_fields %}
+ {% for field in model.fields %}
+ <TR><TD ALIGN="LEFT" BORDER="0"
+ ><FONT {% if field.blank %}COLOR="#7B7B7B" {% endif %}FACE="Helvetica Bold">{{ field.name }}</FONT
+ ></TD>
+ <TD ALIGN="LEFT"
+ ><FONT {% if field.blank %}COLOR="#7B7B7B" {% endif %}FACE="Helvetica Bold">{{ field.type }}</FONT
+ ></TD></TR>
+ {% endfor %}
+ {% endif %}
+ </TABLE>
+ >]
+ {% endfor %}
+"""
+
+tail_template = """
+}
+"""
+
+def generate_dot(app_labels, **kwargs):
+ disable_fields = kwargs.get('disable_fields', False)
+
+ dot = head_template
+
+ for app_label in app_labels:
+ app = models.get_app(app_label)
+ graph = Context({
+ 'name': '"%s"' % app.__name__,
+ 'disable_fields': disable_fields,
+ 'models': []
+ })
+
+ for appmodel in get_models(app):
+ model = {
+ 'name': appmodel.__name__,
+ 'fields': [],
+ 'relations': []
+ }
+
+ # model attributes
+ def add_attributes():
+ model['fields'].append({
+ 'name': field.name,
+ 'type': type(field).__name__,
+ 'blank': field.blank
+ })
+
+ for field in appmodel._meta.fields:
+ add_attributes()
+
+ if appmodel._meta.many_to_many:
+ for field in appmodel._meta.many_to_many:
+ add_attributes()
+
+ # relations
+ def add_relation(extras=""):
+ _rel = {
+ 'target': field.rel.to.__name__,
+ 'type': type(field).__name__,
+ 'name': field.name,
+ 'arrows': extras
+ }
+ if _rel not in model['relations']:
+ model['relations'].append(_rel)
+
+ for field in appmodel._meta.fields:
+ if isinstance(field, ForeignKey):
+ add_relation()
+ elif isinstance(field, OneToOneField):
+ add_relation("[arrowhead=none arrowtail=none]")
+
+ if appmodel._meta.many_to_many:
+ for field in appmodel._meta.many_to_many:
+ if isinstance(field, ManyToManyField):
+ add_relation("[arrowhead=normal arrowtail=normal]")
+ elif isinstance(field, GenericRelation):
+ add_relation(
+ '[style="dotted"] [arrowhead=normal arrowtail=normal]')
+ graph['models'].append(model)
+
+ t = Template(body_template)
+ dot += '\n' + t.render(graph)
+
+ dot += '\n' + tail_template
+
+ return dot
+
+class Command(AppCommand):
+ option_list = AppCommand.option_list + (
+ make_option('--disable_fields', default=False, dest='disable_fields',
+ help='Hide fields.'),
+ )
+ help = "Prints a Graphviz .dot file from models ."
+
+ def handle(self, *app_labels, **options):
+ disable_fields = options.get('disable_fields', False)
+ return generate_dot(app_labels, **options)
+
+
diff --git a/demo/graphviz/models.py b/demo/graphviz/models.py
new file mode 100644
index 0000000..68d86b2
--- /dev/null
+++ b/demo/graphviz/models.py
@@ -0,0 +1,119 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from django.db import models
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.contenttypes import generic
+
+class Graph(models.Model):
+ """ the object defined by the generic relation should have
+ a method gv_links(graph) that returns a sequence of links.
+ a method gv_nodes(graph) that returns a sequence of nodes.
+
+ a link should have a method gv_ends(graph) that returns a sequence
+ of nodes [start, end].
+
+ a link may have a method gv_visual(graph) that returns a ArrowVisual instance or None
+
+ a node may have a method gv_visual(graph) that returns
+ a NodeVisual instance or None.
+
+ links may have a gv_link_label(graph) method
+ nodes may have a gv_node_label(graph) method
+
+ see also interfaces.py module.
+ """
+ slug = models.SlugField(primary_key=True)
+ name = models.CharField(max_length=50)
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+
+ def view_dot_file(self):
+ return '<a href=/graphviz/dot_view/%s/>view</a>' % self.slug
+ view_dot_file.allow_tags = True
+
+ def download_dot_file(self):
+ return '<a href=/graphviz/dot_file/%s/>download</a>' % self.slug
+ download_dot_file.allow_tags = True
+
+ def download_image(self):
+ return '<a href=/graphviz/image_file/%s/>download (TODO)</a>' % self.slug
+ download_image.allow_tags = True
+
+ def __unicode__(self):
+ return self.name
+ class Meta:
+ verbose_name_plural = 'Graphes'
+
+NODE_SHAPE_CHOICES = (('box','Box'),
+ ('circle','Circle'),
+ ('doublecircle','Double circle'),
+ ('box3d','Box 3D'),
+ ('diamond','Diamond'),
+ )
+class NodeVisual(models.Model):
+ """ the content_type attribute is used to define the default Node
+ when no method gv_node is provided.
+ """
+ shape = models.CharField(max_length=50, default='circle', choices=NODE_SHAPE_CHOICES)
+ graph = models.ForeignKey(Graph)
+ content_type = models.ForeignKey(ContentType)
+
+ def __unicode__(self):
+ return self.shape
+
+ARROW_SHAPE_CHOICES = (('box','box'),
+ ('crow','crow'),
+ ('diamond','diamond'),
+ ('dot','dot'),
+ ('inv','inv'),
+ ('none','none'),
+ ('normal','normal'),
+ ('tee','tee'),
+ ('vee','vee'),
+ )
+ARROW_MODIFIER_CHOICES = (('l','left'),
+ ('r','right'),
+ ('o','non-filled'),
+ )
+class ArrowVisual(models.Model):
+ """ the content_type attribute is used to define the default Arrow
+ when no method gv_arrow is provided.
+ """
+ shape = models.CharField(max_length=50, default='normal', choices=ARROW_SHAPE_CHOICES)
+ modifier = models.CharField(max_length=1, default='r', choices=ARROW_MODIFIER_CHOICES)
+ graph = models.ForeignKey(Graph)
+ content_type = models.ForeignKey(ContentType)
+
+ def __unicode__(self):
+ return self.shape
+
+class CacheImage(models.Model):
+ date = models.DateTimeField(auto_now_add=True)
+ valid = models.BooleanField(default=True)
+ graph = models.ForeignKey(Graph)
+ file = models.ImageField(upload_to='graph_images')
+
+ def graphic(self):
+ return '<img name=image%d src=%s>' % (self.pk, self.file.url)
+ graphic.allow_tags = True
+
+ def __unicode__(self):
+ return self.graph.name
+ class Meta:
+ verbose_name_plural = 'Caches images'
diff --git a/demo/graphviz/templates/graphviz/dot_file.dot b/demo/graphviz/templates/graphviz/dot_file.dot
new file mode 100644
index 0000000..1b097ea
--- /dev/null
+++ b/demo/graphviz/templates/graphviz/dot_file.dot
@@ -0,0 +1,6 @@
+
+digraph G {
+ {% for edge in graph.edges %}
+ "{{ edge.start_node_label }}" -> "{{ edge.end_node_label }}" [label = "{{ edge.label }}"];
+ {% endfor %}
+} \ No newline at end of file
diff --git a/demo/graphviz/urls.py b/demo/graphviz/urls.py
new file mode 100644
index 0000000..5cf40c7
--- /dev/null
+++ b/demo/graphviz/urls.py
@@ -0,0 +1,26 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from django.conf.urls.defaults import *
+
+import views
+
+urlpatterns = patterns('',
+ (r'^dot_file/(?P<slug>.*)/$', views.dot_file, {'template':'graphviz/dot_file.dot'}),
+ (r'^dot_view/(?P<slug>.*)/$', views.dot_file, {'view':True, 'template':'graphviz/dot_file.dot'}),
+ (r'^image_file/(?P<slug>.*)/$', views.image_file),
+)
diff --git a/demo/graphviz/views.py b/demo/graphviz/views.py
new file mode 100644
index 0000000..aca8730
--- /dev/null
+++ b/demo/graphviz/views.py
@@ -0,0 +1,61 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from django.http import HttpResponse
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from models import Graph
+
+from django.template import Template
+
+from os import system
+from django.conf import settings
+
+import os
+from os.path import join
+_tmpdir = '/tmp' #os.environ['TMP']
+
+class TEdge:
+ def __init__(self, data, graph):
+ self.start_node, self.end_node = data.gv_ends(graph)
+ self.start_node_label = self.start_node.gv_node_label(graph)
+ self.end_node_label = self.end_node.gv_node_label(graph)
+ self.label = data.gv_edge_label(graph)
+class TGraph:
+ def __init__(self, graph):
+ self.edges = []
+ for e in graph.content_object.gv_edges(graph):
+ self.edges.append(TEdge(e, graph))
+
+def dot_file(request, slug, view=False, template='graphviz/dot_file.dot'):
+ graph = Graph.objects.get(slug=slug)
+ context = {'graph':TGraph(graph)}
+ response = render_to_response(template, context, mimetype='text/plain',
+ context_instance=RequestContext(request))
+ if not view:
+ response['Content-Disposition'] = 'attachment; filename=%s.dot' % slug
+ return response
+
+def image_file(request, slug, template='graphviz/dot_file.html'):
+ resp = dot_file(request, slug, template)
+ path_dot = join(_tmpdir, 'graphviz_tmp.dot')
+ tdot = open(path_dot, 'w')
+ tdot.write(resp.content)
+ tdot.close()
+ system('%s %s Tpng -o %s.png' % (settings.GRAPHVIZ_DOT_CMD, path_dot, path_dot))
+
+ return HttpResponse('image '+path_dot+'.png')