0.1dev release auf.django.export
authorOlivier Larchevêque <olivier.larcheveque@auf.org>
Fri, 10 Jun 2011 14:48:34 +0000 (10:48 -0400)
committerOlivier Larchevêque <olivier.larcheveque@auf.org>
Fri, 10 Jun 2011 14:48:34 +0000 (10:48 -0400)
17 files changed:
.gitignore [new file with mode: 0644]
CHANGES [new file with mode: 0644]
README [new file with mode: 0644]
auf/__init__.py [new file with mode: 0644]
auf/__init__.pyc [new file with mode: 0644]
auf/django/__init__.py [new file with mode: 0644]
auf/django/export/__init__.py [new file with mode: 0755]
auf/django/export/admin.py [new file with mode: 0644]
auf/django/export/locale/fr/LC_MESSAGES/django.mo [new file with mode: 0644]
auf/django/export/locale/fr/LC_MESSAGES/django.po [new file with mode: 0644]
auf/django/export/models.py [new file with mode: 0755]
auf/django/export/templates/admin/export/change_list.html [new file with mode: 0644]
auf/django/export/tests.py [new file with mode: 0755]
auf/django/export/views.py [new file with mode: 0755]
aufdjangoexport/__init__.py [new file with mode: 0644]
setup.cfg [new file with mode: 0644]
setup.py [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..499a075
--- /dev/null
@@ -0,0 +1,4 @@
+*.pyc
+*egg-info
+build
+dist
diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..940bd55
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,7 @@
+auf.django.export
+=================
+
+0.1
+---
+
+* Module creation, provide link to export as CSV in django admin (changelist_view)
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..3f82968
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+# Sample admin.py
+
+from auf.django.export.admin import ExportAdmin
+
+class DossierAdmin(ExportAdmin,):
+    # optional, by default loop on all one2one related objects (stop at 1 depth level)
+    csv_fields = ('name', 'country__name', )
diff --git a/auf/__init__.py b/auf/__init__.py
new file mode 100644 (file)
index 0000000..35cf25b
--- /dev/null
@@ -0,0 +1,5 @@
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except:
+    # bootstrapping
+    pass
diff --git a/auf/__init__.pyc b/auf/__init__.pyc
new file mode 100644 (file)
index 0000000..2bfd785
Binary files /dev/null and b/auf/__init__.pyc differ
diff --git a/auf/django/__init__.py b/auf/django/__init__.py
new file mode 100644 (file)
index 0000000..35cf25b
--- /dev/null
@@ -0,0 +1,5 @@
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except:
+    # bootstrapping
+    pass
diff --git a/auf/django/export/__init__.py b/auf/django/export/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/auf/django/export/admin.py b/auf/django/export/admin.py
new file mode 100644 (file)
index 0000000..355564b
--- /dev/null
@@ -0,0 +1,108 @@
+# -*- encoding: utf-8 -*-
+
+import csv
+from django.core.urlresolvers import reverse
+from django.contrib import admin
+from django.utils.functional import update_wrapper
+from django.http import Http404, HttpResponse, HttpResponseBadRequest
+
+# Create a notation similar to queryset filter
+SEPARATOR = u"__"
+
+# String return if lookup property failed
+EMPTY = u""
+
+class ExportAdmin(admin.ModelAdmin):
+    change_list_template = 'admin/export/change_list.html'
+
+    def changelist_view(self, request, extra_context=None):
+        """
+        Add export URL for the template
+        """
+        if extra_context is None:
+            extra_context = {}
+        extra_context.update({
+            'export_url' : reverse("admin:%s_%s_export" % (self.model._meta.app_label, self.model._meta.module_name))
+        })
+        print extra_context
+        return super(ExportAdmin, self).changelist_view(request, extra_context)
+
+
+    def get_urls(self):
+        from django.conf.urls.defaults import patterns, url
+
+        def wrap(view):
+            def wrapper(*args, **kwargs):
+                return self.admin_site.admin_view(view)(*args, **kwargs)
+            return update_wrapper(wrapper, view)
+
+        info = self.model._meta.app_label, self.model._meta.module_name
+
+        urlpatterns = patterns('',
+            url(r'^export/$',
+                wrap(self.export),
+                name='%s_%s_export' % info),
+        )
+        urlpatterns += super(ExportAdmin, self).get_urls()
+        return urlpatterns
+
+    def get_object_value(self, obj, header):
+        """
+        Function which work as a queryset filter access :
+        Father -- Kid (One2One father related_name = 'child')
+        father__name
+        father__child__name
+        It tries to resolve the property from header string, through
+        linked object if needed. If it can't load any value, empty
+        string is return.
+        """
+        segments = header.split(SEPARATOR)
+        segments.reverse()
+        prop = segments.pop()
+        if len(segments) == 0:
+            return getattr(obj, prop, EMPTY)
+        else:
+            try:
+                child = getattr(obj, prop, None)
+                return self.get_object_value(child, ".".join(segments))
+            except:
+                return EMPTY
+
+    def get_default_csv_fields(self):
+      fields_name = [f.name for f in  self.model._meta.fields]
+      for fk in self.model._meta._related_objects_cache.keys():
+          fields_name = fields_name + [u"%s%s%s" % (fk.var_name, SEPARATOR, f.name) for f in fk.model._meta.fields]
+      return fields_name
+
+    def export(self, request):
+        """
+        Generate HTTP response as CSV file from  csv_fields property.
+        This property is a list of string, which should have queryset filter notation style (with X2 underscore).
+        If there is no fields, the models is full introspected, stop at 1 depth level.
+        """
+        csv_fields = getattr(self, 'csv_fields', None)
+
+        if csv_fields is None:
+            csv_fields = self.get_default_csv_fields()
+
+        qs = self.queryset(request)
+
+        response = HttpResponse(mimetype='text/csv')
+        response['Content-Disposition'] = 'attachment; filename=%s.csv' % unicode(self.model._meta.verbose_name_plural)
+
+        writer = csv.writer(response)
+
+        headers = []
+        for attr in csv_fields:
+            headers.append(attr)
+        writer.writerow(headers)
+
+        for o in qs:
+            row = []
+            for attr in csv_fields:
+                value = self.get_object_value(o, attr)
+                row.append(value)
+            writer.writerow(row)
+
+        return response
+
diff --git a/auf/django/export/locale/fr/LC_MESSAGES/django.mo b/auf/django/export/locale/fr/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..cf99b0c
Binary files /dev/null and b/auf/django/export/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/auf/django/export/locale/fr/LC_MESSAGES/django.po b/auf/django/export/locale/fr/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..2154981
--- /dev/null
@@ -0,0 +1,27 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-06-10 14:33+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n>1;\n"
+
+#: templates/admin/export/change_list.html:8
+msgid "Export CSV"
+msgstr "Exporter en CSV"
+
+#: templates/admin/export/change_list.html:10
+#, python-format
+msgid "Add %(name)s"
+msgstr ""
diff --git a/auf/django/export/models.py b/auf/django/export/models.py
new file mode 100755 (executable)
index 0000000..71a8362
--- /dev/null
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.
diff --git a/auf/django/export/templates/admin/export/change_list.html b/auf/django/export/templates/admin/export/change_list.html
new file mode 100644 (file)
index 0000000..02f9703
--- /dev/null
@@ -0,0 +1,13 @@
+{% extends "admin/change_list.html" %}
+
+{% load i18n %}
+
+{% block object-tools %}
+    <ul class="object-tools">
+
+        <li><a href="{{ export_url }}">{% trans 'Export CSV' %}</a></li>
+               {% if has_add_permission %}
+            <li><a href="{{add_url}}{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{name}}{% endblocktrans %}</a></li>
+        {% endif %}
+    </ul>
+{% endblock %}
diff --git a/auf/django/export/tests.py b/auf/django/export/tests.py
new file mode 100755 (executable)
index 0000000..2247054
--- /dev/null
@@ -0,0 +1,23 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+class SimpleTest(TestCase):
+    def test_basic_addition(self):
+        """
+        Tests that 1 + 1 always equals 2.
+        """
+        self.failUnlessEqual(1 + 1, 2)
+
+__test__ = {"doctest": """
+Another way to test that 1 + 1 is equal to 2.
+
+>>> 1 + 1 == 2
+True
+"""}
+
diff --git a/auf/django/export/views.py b/auf/django/export/views.py
new file mode 100755 (executable)
index 0000000..60f00ef
--- /dev/null
@@ -0,0 +1 @@
+# Create your views here.
diff --git a/aufdjangoexport/__init__.py b/aufdjangoexport/__init__.py
new file mode 100644 (file)
index 0000000..792d600
--- /dev/null
@@ -0,0 +1 @@
+#
diff --git a/setup.cfg b/setup.cfg
new file mode 100644 (file)
index 0000000..01bb954
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..d998de4
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,27 @@
+from setuptools import setup, find_packages
+import sys, os
+
+name = 'auf.django.export'
+version = '0.1'
+
+setup(name=name,
+      version=version,
+      description="Provide easy way to export as CSV in Django Admin",
+      long_description="""\
+""",
+      classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      keywords='',
+      author='Olivier Larchev\xc3\xaaque',
+      author_email='olivier.larcheveque@auf.org',
+      url='',
+      license='GPL',
+      packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          # -*- Extra requirements: -*-
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )