> Hello,
>
> Markus Becker schrieb:
> > Hi,
> >
> > last week I started something in this direction. Similarly to admin.py
> > (which describes how to administrate the data) there is a graph.py,
> > which describes how to graph the data. It is still very rough on the
> > edges, but you can use matplotlib in graph.py to create a plot of the
> > data. Possibly one could also create predefined classes, that create
> > specific matplotlib plots without any code in graph.py except for
> > annotations, e.g. which data to use as x or y data.
> >
> > If you are interested, I could send you a svn diff on trunk and a
> > sample project. I would be interested in hearing comments on this and
> > see whether it could be merged into Django.
>
> I am interested in this. (I am not quite shure it is what i need for
> what i want to do.)

Possibly not. You can interface Django already with matplotlib (see 
http://www.scipy.org/Cookbook/Matplotlib/Django), this is however very 
individual to each graph. I have done this previously as well.

Here, I am trying to create a generic infrastructure for plotting data, more 
or less directly from the model. Assuming you have a model named SampleData: 
Similar to the class SampleDataOptions(admin.ModelAdmin), you could then have 
the class SampleDataGraph(mpl.MplPlot), which describes how to use the data 
for plotting.

Attached are 2 things:
1) django_contrib_graph.svndiff: svn diff from django trunk Revision: 11368 
adding a prototype of a generic plotting infrastructure for Django.
2) dj-mpl.tgz: A sample Django project based on above infrastructure changes.

So, if this helps you: Fine. If not, hopefully it might be of interest to the 
Django developers as a (limited) prototype of such a plotting module, showing 
the viability. I would like to hear about other design options.

BR,
Markus

> I want to create a page that plots data from a lightmeter-network. The aim
> is to monitor lightpollution. So far i have python-skripts (by Dr.
> Wuchterl, an Astronomer in Jena) that can plot this using matplotlib. My
> aim is to make this accesible to regular users that might want to plot how
> light or dark for instance last week was in Berlin.
>
[...]
>
> greetings
>
> jan

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Attachment: dj-mpl.tgz
Description: application/compressed-tar

Index: django/contrib/admin/sites.py
===================================================================
--- django/contrib/admin/sites.py	(revision 11368)
+++ django/contrib/admin/sites.py	(working copy)
@@ -368,6 +368,7 @@
             'app_list': app_list,
             'root_path': self.root_path,
         }
+        print "Extra", extra_context
         context.update(extra_context or {})
         context_instance = template.RequestContext(request, current_app=self.name)
         return render_to_response(self.index_template or 'admin/index.html', context,
Index: django/contrib/graph/MplGraphCreator.py
===================================================================
--- django/contrib/graph/MplGraphCreator.py	(revision 0)
+++ django/contrib/graph/MplGraphCreator.py	(revision 0)
@@ -0,0 +1,54 @@
+from django.http import Http404, HttpResponse
+from django.contrib import graph
+
+from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
+from matplotlib.figure import Figure
+
+import random # TODO: remove after debugging
+
+class MplGraphCreator(object):
+    def __init__(self):
+        pass
+
+    def render(self, context):
+        "Display stage -- can be called many times"
+
+        print "Context", context
+        print "Object list", context['object_list']
+
+        if context['object_list'][0] != None:
+            gm = graph.site.get_graph_for_model(context['object_list'][0].__class__)
+            gm.clear_data()
+            #print gm
+
+            fig = Figure()
+            ax = gm.plot_setup(fig)
+
+            for obj in context['object_list']:
+                gm.add_data(obj)
+
+            gm.plot_func(ax)
+
+            canvas = FigureCanvas(fig)
+
+            hr = HttpResponse(content_type='image/png')
+            canvas.print_png(hr)
+
+            return hr
+        else:
+            raise Http404
+
+#         fig=Figure()
+#         ax=fig.add_subplot(111)
+#         x=[]
+#         y=[]
+
+#         for i in range(10):
+#             x.append(i)
+#             y.append(random.randint(0, 100))
+#         ax.plot(x, y, '-')
+
+#         canvas=FigureCanvas(fig)
+#         canvas.print_png(hr)
+
+#        return hr
Index: django/contrib/graph/__init__.py
===================================================================
--- django/contrib/graph/__init__.py	(revision 0)
+++ django/contrib/graph/__init__.py	(revision 0)
@@ -0,0 +1,58 @@
+#from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
+#from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL
+#from django.contrib.admin.options import StackedInline, TabularInline
+from django.contrib.graph.sites import GraphInfoDB, site
+from django.utils.importlib import import_module
+
+# A flag to tell us if autodiscover is running.  autodiscover will set this to
+# True while running, and False when it finishes.
+LOADING = False
+
+def autodiscover():
+    """
+    Auto-discover INSTALLED_APPS graph.py modules and fail silently when
+    not present. This forces an import on them to register any admin bits they
+    may want.
+    """
+    # Bail out if autodiscover didn't finish loading from a previous call so
+    # that we avoid running autodiscover again when the URLconf is loaded by
+    # the exception handler to resolve the handler500 view.  This prevents an
+    # admin.py module with errors from re-registering models and raising a
+    # spurious AlreadyRegistered exception (see #8245).
+    global LOADING
+    if LOADING:
+        return
+    LOADING = True
+
+    import imp
+    from django.conf import settings
+
+    for app in settings.INSTALLED_APPS:
+        # For each app, we need to look for an admin.py inside that app's
+        # package. We can't use os.path here -- recall that modules may be
+        # imported different ways (think zip files) -- so we need to get
+        # the app's __path__ and look for admin.py on that path.
+
+        # Step 1: find out the app's __path__ Import errors here will (and
+        # should) bubble up, but a missing __path__ (which is legal, but weird)
+        # fails silently -- apps that do weird things with __path__ might
+        # need to roll their own admin registration.
+        try:
+            app_path = import_module(app).__path__
+        except AttributeError:
+            continue
+
+        # Step 2: use imp.find_module to find the app's admin.py. For some
+        # reason imp.find_module raises ImportError if the app can't be found
+        # but doesn't actually try to import the module. So skip this app if
+        # its admin.py doesn't exist
+        try:
+            imp.find_module('graph', app_path)
+        except ImportError:
+            continue
+
+        # Step 3: import the app's admin file. If this has errors we want them
+        # to bubble up.
+        import_module("%s.graph" % app)
+    # autodiscover was successful, reset loading flag.
+    LOADING = False
Index: django/contrib/graph/graph_detail.py
===================================================================
--- django/contrib/graph/graph_detail.py	(revision 0)
+++ django/contrib/graph/graph_detail.py	(revision 0)
@@ -0,0 +1,44 @@
+from django.template import loader, RequestContext
+from django.http import Http404, HttpResponse
+
+#from django.core.xheaders import populate_xheaders
+#from django.core.paginator import Paginator, InvalidPage
+#from django.core.exceptions import ObjectDoesNotExist
+
+from django.contrib.graph import MplGraphCreator as mplgc
+
+def object_graph(request, queryset, paginate_by=None, page=None,
+                 allow_empty=True, template_name=None,
+                 template_loader=loader,
+                 extra_context=None, context_processors=None,
+                 template_object_name='object',
+                 mimetype=None):
+    """
+    Generic graph of objects.
+    """
+    #Templates: ``<app_label>/<model_name>_list.html``
+    #Context:
+    #    TBD
+    #"""
+
+    if extra_context is None: extra_context = {}
+    queryset = queryset._clone()
+
+    c = RequestContext(request, {
+            '%s_list' % template_object_name: queryset,
+            'paginator': None,
+            'page_obj': None,
+            'is_paginated': False,
+            }, context_processors)
+
+    if not allow_empty and len(queryset) == 0:
+        raise Http404
+
+    for key, value in extra_context.items():
+        if callable(value):
+            c[key] = value()
+        else:
+            c[key] = value
+
+    gc = mplgc.MplGraphCreator()
+    return gc.render(c)
Index: django/contrib/graph/sites.py
===================================================================
--- django/contrib/graph/sites.py	(revision 0)
+++ django/contrib/graph/sites.py	(revision 0)
@@ -0,0 +1,279 @@
+import re
+from django import http, template
+#from django.contrib.admin import ModelAdmin
+#from django.contrib.admin import actions
+#from django.contrib.auth import authenticate, login
+from django.db.models.base import ModelBase
+from django.core.exceptions import ImproperlyConfigured
+from django.core.urlresolvers import reverse
+from django.shortcuts import render_to_response
+from django.utils.functional import update_wrapper
+from django.utils.safestring import mark_safe
+from django.utils.text import capfirst
+from django.utils.translation import ugettext_lazy, ugettext as _
+from django.views.decorators.cache import never_cache
+from django.conf import settings
+try:
+    set
+except NameError:
+    from sets import Set as set     # Python 2.3 fallback
+
+class AlreadyRegistered(Exception):
+    pass
+
+class NotRegistered(Exception):
+    pass
+
+class GraphInfoDB(object):
+    """
+    An GraphInfoDB object encapsulates an instance of the Django graph
+    application, ready to be hooked in to your URLconf. Models are
+    registered with the GraphInfoDB using the register() method, and the
+    root() method can then be used as a Django view function that
+    presents a full graph interface for the collection of registered
+    models.
+    """
+
+    index_template = None
+    app_index_template = None
+
+    def __init__(self, name=None, app_name='graph'):
+        self._registry = {} # model_class class -> graph_class instance
+        self.root_path = None
+        if name is None:
+            self.name = 'graph'
+        else:
+            self.name = name
+        self.app_name = app_name
+
+    def register(self, model_or_iterable, graph_class=None, **options):
+        """
+        Registers the given model(s) with the given graph class.
+
+        The model(s) should be Model classes, not instances.
+
+        If an graph class isn't given, it will use ModelAdmin (the default
+        graph options). If keyword arguments are given -- e.g., list_display --
+        they'll be applied as options to the graph class.
+
+        If a model is already registered, this will raise AlreadyRegistered.
+        """
+        if not graph_class:
+            graph_class = ModelGraph
+
+        # Don't import the humongous validation code unless required
+        #if graph_class and settings.DEBUG:
+        #    from django.contrib.admin.validation import validate
+        #else:
+        #    validate = lambda model, graphclass: None
+        validate = lambda model, graphclass: None
+
+        if isinstance(model_or_iterable, ModelBase):
+            model_or_iterable = [model_or_iterable]
+        for model in model_or_iterable:
+            if model in self._registry:
+                raise AlreadyRegistered('The model %s is already registered' % model.__name__)
+
+            # If we got **options then dynamically construct a subclass of
+            # graph_class with those **options.
+            if options:
+                # For reasons I don't quite understand, without a __module__
+                # the created class appears to "live" in the wrong place,
+                # which causes issues later on.
+                options['__module__'] = __name__
+                graph_class = type("%sGraph" % model.__name__, (graph_class,), options)
+
+            # Validate (which might be a no-op)
+            validate(graph_class, model)
+
+            # Instantiate the graph class to save in the registry
+            print "Reg. Model:", model
+            self._registry[model] = graph_class(model, self)
+            print "Registry:", self._registry
+
+    def unregister(self, model_or_iterable):
+        """
+        Unregisters the given model(s).
+
+        If a model isn't already registered, this will raise NotRegistered.
+        """
+        if isinstance(model_or_iterable, ModelBase):
+            model_or_iterable = [model_or_iterable]
+        for model in model_or_iterable:
+            if model not in self._registry:
+                raise NotRegistered('The model %s is not registered' % model.__name__)
+            del self._registry[model]
+
+    def get_graph_for_model(self, model_or_iterable):
+        """
+        Get the given graph model for the data model.
+
+        If a model isn't already registered, this will raise NotRegistered.
+        """
+        if isinstance(model_or_iterable, ModelBase):
+            model_or_iterable = [model_or_iterable]
+        for model in model_or_iterable:
+            if model not in self._registry:
+                raise NotRegistered('The model %s is not registered' % model.__name__)
+            return self._registry[model]
+
+    def check_dependencies(self):
+        """
+        Check that all things needed to run the graph have been correctly installed.
+
+        The default implementation checks that LogEntry, ContentType and the
+        auth context processor are installed.
+        """
+        from django.contrib.admin.models import LogEntry
+        from django.contrib.contenttypes.models import ContentType
+
+        if not LogEntry._meta.installed:
+            raise ImproperlyConfigured("Put 'django.contrib.graph' in your INSTALLED_APPS setting in order to use the admin application.")
+        if not ContentType._meta.installed:
+            raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in your INSTALLED_APPS setting in order to use the admin application.")
+        if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
+            raise ImproperlyConfigured("Put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
+
+    def graph_view(self, view, cacheable=False):
+        """
+        Decorator to create a graph view attached to this ``GraphInfoDB``. This
+        wraps the view.
+        """
+        def inner(request, *args, **kwargs):
+            return view(request, *args, **kwargs)
+        if not cacheable:
+            inner = never_cache(inner)
+        return update_wrapper(inner, view)
+
+    def get_urls(self):
+        from django.conf.urls.defaults import patterns, url, include
+
+        def wrap(view, cacheable=False):
+            def wrapper(*args, **kwargs):
+                return self.graph_view(view, cacheable)(*args, **kwargs)
+            return update_wrapper(wrapper, view)
+
+        # Graph-site-wide views.
+        urlpatterns = patterns('',
+            url(r'^$',
+                wrap(self.index),
+                name='index'),
+            url(r'^jsi18n/$',
+                wrap(self.i18n_javascript, cacheable=True),
+                name='jsi18n'),
+#            url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
+#                'django.views.defaults.shortcut'),
+#            url(r'^(?P<app_label>\w+)/$',
+#                wrap(self.app_index),
+#                name='app_list')
+        )
+
+        # Add in each model's views.
+        for model, model_graph in self._registry.iteritems():
+            urlpatterns += patterns('',
+                url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
+                    include(model_graph.urls))
+            )
+        return urlpatterns
+
+    def urls(self):
+        return self.get_urls(), self.app_name, self.name
+    urls = property(urls)
+
+    def i18n_javascript(self, request):
+        """
+        Displays the i18n JavaScript that the Django graph requires.
+
+        This takes into account the USE_I18N setting. If it's set to False, the
+        generated JavaScript will be leaner and faster.
+        """
+        if settings.USE_I18N:
+            from django.views.i18n import javascript_catalog
+        else:
+            from django.views.i18n import null_javascript_catalog as javascript_catalog
+        return javascript_catalog(request, packages='django.conf')
+
+    def index(self, request, extra_context=None):
+        """
+        Displays the main graph index page, which lists all of the installed
+        apps that have been registered in this site.
+        """
+        app_dict = {}
+        user = request.user
+        for model, model_graph in self._registry.items():
+            app_label = model._meta.app_label
+
+            model_dict = {
+                'name': capfirst(model._meta.verbose_name_plural),
+                'graph_url': mark_safe('%s/%s/' % (app_label, model.__name__.lower())),
+                }
+            if app_label in app_dict:
+                app_dict[app_label]['models'].append(model_dict)
+            else:
+                app_dict[app_label] = {
+                    'name': app_label.title(),
+                    'app_url': app_label + '/',
+                    'models': [model_dict],
+                    }
+
+        # Sort the apps alphabetically.
+        app_list = app_dict.values()
+        app_list.sort(lambda x, y: cmp(x['name'], y['name']))
+        print app_list
+
+        # Sort the models alphabetically within each app.
+        for app in app_list:
+            app['models'].sort(lambda x, y: cmp(x['name'], y['name']))
+
+        context = {
+            'title': _('Registered graph models'),
+            'app_list': app_list,
+            'root_path': self.root_path,
+        }
+        context.update(extra_context or {})
+        context_instance = template.RequestContext(request, current_app=self.name)
+        return render_to_response(self.index_template or 'graph/index.html', context,
+            context_instance=context_instance
+        )
+    index = never_cache(index)
+
+    def graph_index(self, request, app_label, extra_context=None):
+        user = request.user
+        app_dict = {}
+        for model, model_graph in self._registry.items():
+            if app_label == model._meta.app_label:
+                model_dict = {
+                    'name': capfirst(model._meta.verbose_name_plural),
+                    'graph_url': '%s/' % model.__name__.lower(),
+                    }
+            if app_dict:
+                app_dict['models'].append(model_dict),
+            else:
+                # First time around, now that we know there's
+                # something to display, add in the necessary meta
+                # information.
+                app_dict = {
+                    'name': app_label.title(),
+                    'app_url': '',
+                    'models': [model_dict],
+                    }
+        if not app_dict:
+            raise http.Http404('The requested graph page does not exist.')
+        # Sort the models alphabetically within each app.
+        app_dict['models'].sort(lambda x, y: cmp(x['name'], y['name']))
+        context = {
+            'title': _('%s administration') % capfirst(app_label),
+            'app_list': [app_dict],
+            'root_path': self.root_path,
+        }
+        context.update(extra_context or {})
+        context_instance = template.RequestContext(request, current_app=self.name)
+        return render_to_response(self.app_index_template or ('graph/%s/app_index.html' % app_label,
+            'graph/app_index.html'), context,
+            context_instance=context_instance
+        )
+
+# This global object represents the default graph site, for the common
+# case.  You can instantiate GraphInfoDB in your own code to create a
+# custom graph site.
+site = GraphInfoDB()
Index: django/contrib/graph/templates/graph/index.html
===================================================================
--- django/contrib/graph/templates/graph/index.html	(revision 0)
+++ django/contrib/graph/templates/graph/index.html	(revision 0)
@@ -0,0 +1,80 @@
+{% extends "admin/base_site.html" %}
+{% load i18n %}
+
+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% load adminmedia %}{% admin_media_prefix %}css/dashboard.css" />{% endblock %}
+
+{% block coltype %}colMS{% endblock %}
+
+{% block bodyclass %}dashboard{% endblock %}
+
+{% block breadcrumbs %}{% endblock %}
+
+{% block content %}
+<div id="content-main">
+
+{% if app_list %}
+    {% for app in app_list %}
+        <div class="module">
+        <table summary="{% blocktrans with app.name as name %}Models available in the {{ name }} application.{% endblocktrans %}">
+        <caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption>
+        {% for model in app.models %}
+            <tr>
+            {% if model.perms.change %}
+                <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
+            {% else %}
+                <th scope="row">{{ model.name }}</th>
+            {% endif %}
+
+            {% if model.perms.add %}
+                <td><a href="{{ model.admin_url }}add/" class="addlink">{% trans 'Add' %}</a></td>
+            {% else %}
+                <td>&nbsp;</td>
+            {% endif %}
+
+            {% if model.perms.change %}
+                <td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
+            {% else %}
+                <td>&nbsp;</td>
+            {% endif %}
+            </tr>
+        {% endfor %}
+        </table>
+        </div>
+    {% endfor %}
+{% else %}
+    <p>{% trans "You don't have permission to edit anything." %}</p>
+{% endif %}
+</div>
+{% endblock %}
+
+{% block sidebar %}
+<div id="content-related">
+    <div class="module" id="recent-actions-module">
+        <h2>{% trans 'Recent Actions' %}</h2>
+        <h3>{% trans 'My Actions' %}</h3>
+            {% load log %}
+            {% get_admin_log 10 as admin_log for_user user %}
+            {% if not admin_log %}
+            <p>{% trans 'None available' %}</p>
+            {% else %}
+            <ul class="actionlist">
+            {% for entry in admin_log %}
+            <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
+                {% if entry.is_deletion %}
+                    {{ entry.object_repr }}
+                {% else %}
+                    <a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
+                {% endif %}
+                <br/>
+                {% if entry.content_type %}
+                    <span class="mini quiet">{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}</span>
+                {% else %}
+                    <span class="mini quiet">{% trans 'Unknown content' %}</span>
+                {% endif %}
+            </li>
+            {% endfor %}
+            </ul>
+            {% endif %}
+    </div>
+</div>
+{% endblock %}
Index: django/contrib/graph/MplPlot.py
===================================================================
--- django/contrib/graph/MplPlot.py	(revision 0)
+++ django/contrib/graph/MplPlot.py	(revision 0)
@@ -0,0 +1,11 @@
+class MplPlot(object):
+    plotfunc = None
+
+    def __init__(self, model, graph_site):
+        self.model = model
+        self.opts = model._meta
+        self.graph_site = graph_site
+
+        print "Model:", model
+        print "Meta:", model._meta
+        print "Site:", graph_site

Reply via email to