Permalink
Browse files

Initial commit for djangorestframework-csv

  • Loading branch information...
1 parent d7b453a commit 7b78883e3c147dd9bb1c2071f10c1fc1747fa68b @mjumbewu committed Jan 14, 2013
View
1 .gitignore
@@ -3,3 +3,4 @@ MANIFEST
dist/
build/
*.egg-info/
+*~
View
100 README.md
@@ -1,100 +0,0 @@
-Django Reusable Apps
-====================
-
-**Simple template for reusable apps with Django.**
-
-**Author:** Tom Christie, [Follow me on Twitter][1].
-
-Overview
-========
-
-A simple template for creating reusable apps with Django.
-
-Includes:
-
-1. A `setup.py` that makes your life easy and doesn't suck.
-2. A simple layout that lets you run the app's tests without installing it into an existing project.
-3. A sample `.travis.yml` config file.
-
-Creating a new app
-==================
-
-You'll want to clone this project, then create a fresh git repo for it:
-
- git clone git://github.com/dabapps/django-reusable-app.git my-project-name
- cd my-project-name
- mv myproject packagename
- rm -rf .git
- git init
-
-Edit `testsettings.py` and update the app name in INSTALLED_APPS.
-Edit `setup.py` and update the settings at the top of the file.
-Edit the `README`.
-
-Pushing releases to PyPI
-========================
-
-To register your package on PyPI:
-
- ./setup.py register
-
-To publish a new version of your app to PyPI, set the `__version__` string in
-your package's `__init__.py`, then run:
-
- ./setup.py publish
-
-Running the tests
-=================
-
-To run the tests against the current environment:
-
- ./manage.py test
-
-Changelog
-=========
-
-1.1.0
------
-
-* Remove tox, add travis
-* Remove seperate LICENSE
-
-1.0.1
------
-
-* Include author_email in setup.py
-* Move testsettings into top level dir
-* Update tox to test against Django 1.3, 1.4 (From 1.2, 1.3)
-
-1.0.0
------
-
-* Initial release
-
-License
-=======
-
-Copyright © DabApps.
-
-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.
-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 HOLDER 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.
-
-[1]: http://twitter.com/_tomchristie
View
72 README.rst
@@ -0,0 +1,72 @@
+=======================
+djangorestframework-csv
+=======================
+
+|build status|_
+
+.. |build status| image:: https://secure.travis-ci.org/mjumbewu/django-rest-framework-csv.png
+.. _build status: https://secure.travis-ci.org/mjumbewu/django-rest-framework-csv
+
+**CSV Tools for Django REST Framework**
+
+**Author:** Mjumbe Wawatu Poe, `Follow me on Twitter <http://www.twitter.com/mjumbewu>`_.
+
+Usage
+-----
+
+*views.py*::
+
+ from rest_framework.views import APIView
+ from rest_framework.settings import api_settings
+ from rest_framework_csv import CSVRenderer
+
+ class MyView (APIView):
+ renderer_classes = (CSVRenderer, ) + api_settings.DEFAULT_RENDERER_CLASSES
+ ...
+
+For more information about using renderers with Django REST Framework, see the
+`API Guide <http://django-rest-framework.org/api-guide/renderers.html>`_ or the
+`Tutorial <http://django-rest-framework.org/tutorial/1-serialization.html>`_.
+
+Running the tests
+-----------------
+
+To run the tests against the current environment:
+
+ ./manage.py test
+
+
+Changelog
+=========
+
+1.0.0
+-----
+
+* Initial release
+
+
+License
+=======
+
+Copyright © Mjumbe Wawatu Poe.
+
+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.
+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 HOLDER 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.
View
6 myproject/tests.py
@@ -1,6 +0,0 @@
-from django.test import TestCase
-
-
-class TestStuff(TestCase):
- def test_something(self):
- self.assertEquals(2, 1 + 1)
View
3 requirements.txt
@@ -1 +1,2 @@
-Django>=1.3
+Django>=1.3
+djangorestframework
View
0 myproject/__init__.py → rest_framework_csv/__init__.py
File renamed without changes.
View
0 myproject/models.py → rest_framework_csv/models.py
File renamed without changes.
View
128 rest_framework_csv/renderers.py
@@ -0,0 +1,128 @@
+import csv
+from collections import defaultdict
+from rest_framework.renderers import *
+from StringIO import StringIO
+
+class CSVRenderer(BaseRenderer):
+ """
+ Renderer which serializes to CSV
+ """
+
+ media_type = 'text/csv'
+ format = 'csv'
+ level_sep = '.'
+
+ def render(self, data, media_type=None, renderer_context=None):
+ """
+ Renders serialized *data* into CSV. For a dictionary:
+ """
+ if data is None:
+ return ''
+
+ table = self.tablize(data)
+ csv_buffer = StringIO()
+ csv_writer = csv.writer(csv_buffer)
+ for row in table:
+ # Assume that strings should be encoded as UTF-8
+ csv_writer.writerow([
+ elem.encode('utf-8') if isinstance(elem, basestring) else elem
+ for elem in row
+ ])
+
+ return csv_buffer.getvalue()
+
+ def tablize(self, data):
+ """
+ Convert a list of data into a table.
+ """
+ if data:
+
+ # First, flatten the data (i.e., convert it to a list of
+ # dictionaries that are each exactly one level deep). The key for
+ # each item designates the name of the column that the item will
+ # fall into.
+ data = self.flatten_data(data)
+
+ # Get the set of all unique headers, and sort them.
+ headers = set()
+ for item in data:
+ headers.update(item.keys())
+ headers = sorted(headers)
+
+ # Create a row for each dictionary, filling in columns for which the
+ # item has no data with None values.
+ rows = []
+ for item in data:
+ row = []
+ for key in headers:
+ row.append(item.get(key, None))
+ rows.append(row)
+
+ # Return your "table", with the headers as the first row.
+ return [headers] + rows
+
+ else:
+ return []
+
+ def flatten_data(self, data):
+ """
+ Convert the given data collection to a list of dictionaries that are
+ each exactly one level deep. The key for each value in the dictionaries
+ designates the name of the column that the value will fall into.
+ """
+ flat_data = []
+ for item in data:
+ flat_item = self.flatten_item(item)
+ flat_data.append(flat_item)
+
+ return flat_data
+
+ def flatten_item(self, item):
+ if isinstance(item, list):
+ flat_item = self.flatten_list(item)
+ elif isinstance(item, dict):
+ flat_item = self.flatten_dict(item)
+ else:
+ flat_item = {'': item}
+
+ return flat_item
+
+ def nest_flat_item(self, flat_item, prefix):
+ """
+ Given a "flat item" (a dictionary exactly one level deep), nest all of
+ the column headers in a namespace designated by prefix. For example:
+
+ header... | with prefix... | becomes...
+ -----------|----------------|----------------
+ 'lat' | 'location' | 'location.lat'
+ '' | '0' | '0'
+ 'votes.1' | 'user' | 'user.votes.1'
+
+ """
+ nested_item = {}
+ for header, val in flat_item.iteritems():
+ nested_header = self.level_sep.join([prefix, header]) if header else prefix
+ nested_item[nested_header] = val
+ return nested_item
+
+ def flatten_list(self, l):
+ flat_list = {}
+ for index, item in enumerate(l):
+ index = str(index)
+ flat_item = self.flatten_item(item)
+ nested_item = self.nest_flat_item(flat_item, index)
+ flat_list.update(nested_item)
+ return flat_list
+
+ def flatten_dict(self, d):
+ flat_dict = {}
+ for key, item in d.iteritems():
+ key = str(key)
+ flat_item = self.flatten_item(item)
+ nested_item = self.nest_flat_item(flat_item, key)
+ flat_dict.update(nested_item)
+ return flat_dict
+
+
+class CSVRendererWithUnderscores (CSVRenderer):
+ level_sep = '_'
View
66 rest_framework_csv/tests.py
@@ -0,0 +1,66 @@
+#-*- coding:utf-8 -*-
+
+from django.test import TestCase
+from .renderers import CSVRenderer
+
+class TestCSVRenderer (TestCase):
+
+ def test_tablize_a_list_with_no_elements(self):
+ renderer = CSVRenderer()
+
+ flat = renderer.tablize([])
+ self.assertEqual(flat, [])
+
+ def test_tablize_a_list_with_atomic_elements(self):
+ renderer = CSVRenderer()
+
+ flat = renderer.tablize([1, 2, 'hello'])
+ self.assertEqual(flat, [['' ],
+ [1 ],
+ [2 ],
+ ['hello']])
+
+
+ def test_tablize_a_list_with_list_elements(self):
+ renderer = CSVRenderer()
+
+ flat = renderer.tablize([[1, 2, 3],
+ [4, 5],
+ [6, 7, [8, 9]]])
+ self.assertEqual(flat, [['0' , '1' , '2' , '2.0' , '2.1'],
+ [1 , 2 , 3 , None , None ],
+ [4 , 5 , None , None , None ],
+ [6 , 7 , None , 8 , 9 ]])
+
+ def test_tablize_a_list_with_dictionary_elements(self):
+ renderer = CSVRenderer()
+
+ flat = renderer.tablize([{'a': 1, 'b': 2},
+ {'b': 3, 'c': {'x': 4, 'y': 5}}])
+ self.assertEqual(flat, [['a' , 'b' , 'c.x' , 'c.y' ],
+ [1 , 2 , None , None ],
+ [None, 3 , 4 , 5 ]])
+
+ def test_tablize_a_list_with_mixed_elements(self):
+ renderer = CSVRenderer()
+
+ flat = renderer.tablize([{'a': 1, 'b': 2},
+ {'b': 3, 'c': [4, 5]},
+ 6])
+ self.assertEqual(flat, [['' , 'a' , 'b' , 'c.0' , 'c.1'],
+ [None, 1 , 2 , None , None ],
+ [None, None, 3 , 4 , 5 ],
+ [6 , None, None, None , None ]])
+
+ def test_tablize_a_list_with_unicode_elements(self):
+ renderer = CSVRenderer()
+
+ flat = renderer.tablize([{u'a': 1, u'b': u'hello\u2014goodbye'}])
+ self.assertEqual(flat, [[u'a', u'b' ],
+ [1 , u'hello—goodbye']])
+
+ def test_render_a_list_with_unicode_elements(self):
+ renderer = CSVRenderer()
+
+ dump = renderer.render([{u'a': 1, u'b': u'hello\u2014goodbye', u'c': 'http://example.com/'}])
+ self.assertEqual(dump, (u'a,b,c\r\n1,hello—goodbye,http://example.com/\r\n').encode('utf-8'))
View
28 setup.py
@@ -7,14 +7,14 @@
import sys
-name = 'django-reusable-app'
-package = 'myproject'
-description = ''
-url = 'http://github.com/.../.../'
-author = ''
-author_email = ''
+name = 'djangorestframework-csv'
+package = 'rest_framework_csv'
+description = 'CSV Tools for Django REST Framework'
+url = 'https://github.com/mjumbewu/django-rest-framework-csv'
+author = 'Mjumbe Wawatu Ukweli'
+author_email = 'mjumbewu@gmail.com'
license = 'BSD'
-install_requires = []
+install_requires = ['djangorestframework']
def get_version(package):
@@ -69,5 +69,17 @@ def get_package_data(package):
author_email=author_email,
packages=get_packages(package),
package_data=get_package_data(package),
- install_requires=install_requires
+ install_requires=install_requires,
+ classifiers=[
+ "Development Status :: 4 - Beta",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: BSD License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.6",
+ "Programming Language :: Python :: 2.7",
+ "Framework :: Django",
+ ],
)
View
2 testsettings.py
@@ -6,5 +6,5 @@
}
INSTALLED_APPS = (
- 'myproject',
+ 'rest_framework_csv',
)

0 comments on commit 7b78883

Please sign in to comment.