{ "info": { "author": "Pablo Recio", "author_email": "pablo@potatolondon.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python" ], "description": "# Reportato\n\n[![build-status-image]][travis]\n\nThe goal of Reportato is to provide a Django-ish approach to easily get CSV or\nGoogle Spreadsheet generated reports.\n\nThis is still a very alpha version, and this documentation may lie.\n\n**Note:** Generating a report can take a long time if the table you're\ngenerating your report from is large. You might want to avoid forcing your\nusers to experience this latency, e.g. by generating the report regularly on a\ncron and allowing them to download the most recent copy, or by fetching the\nreport via AJAX so you can show your user pictures of kittens while they wait.\n\n## Installation\n\nReportato is [hosted on PyPI](https://pypi.python.org/pypi/django-reportato) so\nyou can just install it using either:\n\n\n $ pip install django-reportato\n\nOr:\n\n\n $ easy_install django-reportato\n\nIf you prefer to use the development version of it, you can clone the repository\nand build it manually:\n\n $ git clone https://github.com/potatolondon/reportato.git\n $ cd reportato\n $ python setup.py install\n\n\n## Basic configuration\n\nWith Reportato, the way you declare how your report should look is similar to\nDjango's ModelForms:\n\n\n```python\n#### reporters.py\n\nfrom reportato.reporters import ModelReporter\n\nfrom .models import Journalist\n\nclass JournalistReporter(ModelReporter):\n\n class Meta:\n model = Journalist\n fields = ('first_name', 'last_name', 'email')\n custom_headers = {\n 'first_name': 'Different header'\n }\n\n def get_email_column(self, instance):\n return instance.email.replace('@', '< AT >')\n\n#### usage example\n>>> from journalists.models import Journalist\n>>> from journalists.reporters import JournalistReporter\n>>> reporter = JournalistReporter() # by default uses model.objects.all(), can use any queryset\n>>> reporter.get_header_row()\n[u'Different header', u'Last name', u'Email']\n>>> [row for row in reporter.get_rows()]\n[\n [u'Angelica', u'Edlund', u'angelicaedlund engadget.com'],\n [u'Arnold', u'Ofarrell', u'arnoldofarrell reddit.com'],\n # ...\n]\n```\n\n\n## Documentation\n\n### Model Reporters\n\nTo create a report for a given model, you just need to write a class that\nwill inherit from `reportato.reporters.ModelReporter`, and indicate which\nmodel you want to report on. A very simple example:\n\n\n```python\nfrom reportato.reporters import ModelReporter\n\nclass MyReport(ModelReporter):\n\n class Meta:\n model = MyModel\n```\n\n\nBy default this will generate reports with every field on `MyModel`. You can\nespecify which fields to include by using the `fields` variable:\n\n\n```python\n # ...\n class Meta:\n model = MyModel\n fields = ('field1', 'field2', 'field3')\n```\n\n\nThe header row will be generated based on the model field's `verbose_name` value,\nor a simple capitalization of the field name if it doesn't have a `verbose_name`.\nIf you want to override that, you can do it using `custom_headers`:\n\n\n```python\n # ...\n class Meta:\n model = MyModel\n custom_headers {\n 'field1': 'Very cool header'\n }\n```\n\n\nThe way reportato resolves each given field is very simple:\n\n* Checks if the reporter class has some method named `get_FIELDNAME_column(instance)`.\n If it does, uses it. Otherwise:\n* Checks if the given `FIELDNAME` is accessible directly through the instance.\n* Will raise a `reportato.reporters.UndefinedField` exception if neither\n of those are defined.\n\nWhat this means is that you can define whatever you want in your report as a field\nwhile you have the proper `get_FIELDNAME_column` method. And also will be able\nto use model's fields, decorators or even aggregated fields.\n\nAs an example:\n\n\n```python\n # ...\n def get_field1_column(self, instance):\n return instance.field1.replace('_', '-')\n```\n\n\nTo create the report, you need to instantiate the object using a list of objects\nor a queryset. If you do not pass one, it will take all the objects for the\ngiven model:\n\n\n```python\n>>> MyReport() # report with MyModel.objects.all()\n>>> MyReport(MyModel.objects.filter(something=something_else))\n```\n\n\n\nAdditionally, you can define what fields you want to see on your report during\ncreation time using the `visible_fields` parameter:\n\n >>> MyReport(visible_fields=['first_name', 'last_name'])\n\nThis way your reporter will only show those fields, even if you had more on\nyour Reporter definition. This allows you to build custom logic if the final\nuser wants to customise what columns he/she wants to see each time.\n\nOther methods:\n\n#### `get_header_row()`\n\nReturns an ordered list with the CSV headers\n\n#### `get_row(instance)`\n\nReturns a sorted dict with the value of each field for the given `instance`\n\n#### `get_rows()`\n\nReturns an iterable with an ordered list of the values for the given fields.\n\n### reportato.views.BaseCSVGeneratorView\n\nAre you a CBV fan? It's your lucky day, because `reportato` provides a base view\nfrom which you can inherit for building your own stuff.\n\n`BaseCSVGeneratorView` inherits from Django's `django.views.generic.ListView`\nmeaning that it needs a model or a queryset to work with. Internally,\n`BaseCSVGeneratorView` will use `get_queryset()` method to resolve the list\nof objects to report with.\n\n#### `reporter_class`\n\nClass attribute to define the class of the reporter you are going to use on\nthis view. Should inherit from `ModelReporter`. If you need to decide what\nreporter to use in execution time, override `get_reporter_class()` method\ninstead.\n\n#### `writer_class`\n\nClass attribute to define the CSV writer class. By default uses `UnicodeWriter`,\nas implemented on Python documentation.\n\n#### `WRITE_HEADER`\n\nFlag to determine whether to include the headers in the report or not.\n\n### `file_name`\n\nClass attribute to define the file name for the generated report. If you want\nsomething more dynamic, you should override `get_file_name()` method.\n\n### `write_csv()`\n\nMethod that receiving an input flow (`HttpResponse`, `io.BytesIO`...) uses\nreporter's method to write into such flow.\n\n## Further examples\n\n### Report as a Google Sheet\n\nThe initial plan was to add some features to automatically create spreadsheets\nfrom given reports. However, I changed my mind during implementation because\nit made certain assumptions about how the tool needed to implement OAuth and\nfelt that it was overkill.\n\nInstead, I'm providing some examples of how to do it using this library.\nThe basic undersanding is that we need to dump the CSV file into a `BytesIO` and\nupload it to Google Drive using `google-api-python-client`.\n\n\n```python\nimport io\n\n# google apiclient dependencies\nimport httplib2\nfrom apiclient.discovery import build\nfrom apiclient.http import MediaIoBaseUpload\n\nfrom reporters.views import BaseCSVGeneratorView\nfrom .models import MyModel\nfrom .reports import MyReport\n\nclass MyReportToSpreadsheet(BaseCSVGeneratorView):\n model = MyModel\n reporter_class = MyReport\n\n def get(self, request, *args, **kwargs):\n outfile = io.BytesIO()\n self.write_csv(outfile)\n # now outfile is filled with the CSV we want.\n # we need to fetch the user credentials using OAuth, not covering it here\n credentials = request.user.oauth_credentials\n http = credentials.authorize(httplib2.Http())\n\n service = build(\n serviceName='drive',\n version='v2',\n developerKey=settings.GOOGLE_API_KEY,\n http=http\n )\n\n media_body = MediaIoBaseUpload(\n outfile,\n mimetype='text/csv',\n resumable=False\n )\n\n body = {\n \"title\": title,\n \"description\": description,\n \"mimeType\": 'text/csv'\n }\n\n uploaded_file = service.files().insert(\n body=body,\n media_body=media_body,\n convert=True # to convert CSV's to Spreadsheet\n ).execute()\n\n return HttpResponseRedirect(uploaded_file['alternateLink'])\n```\n\n\n## Running tests\n\nRunning reportato tests is dead simple. First, you need a `virtualenv`. I\nhighly recommend to use [`virtualenvrapper`](http://virtualenvwrapper.readthedocs.org/en/latest/)\nfor it. Having it installed, you'll just need to:\n\n $ mkvirtualenv reportato # will create the virtualenv\n $ workon reportato # will activate the virtualenv\n # deactivate # will deactivate it\n\nIn order to run the tests we need `Django` and `Mock`:\n\n $ pip install Django Mock\n\nOnce those dependencies are installed, you can run the tests simply with:\n\n $ python runtests.py\n\n## Future plans\n\n* Add custom columns that aren't part of a model for adding aggregates\n* Make some sort of Mixin for making the upload to Google Sheets easier\n* Use `values_list` instead of composing the models for trying to be more efficient\n* Provide helpers for deferring the report generation to GAE task queues\n\n[build-status-image]: https://secure.travis-ci.org/potatolondon/reportato.png?branch=master\n[travis]: http://travis-ci.org/potatolondon/reportato?branch=master", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/potatolondon/reportato", "keywords": "django reports potato csv", "license": "BSD", "maintainer": null, "maintainer_email": null, "name": "django-reportato", "package_url": "https://pypi.org/project/django-reportato/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/django-reportato/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/potatolondon/reportato" }, "release_url": "https://pypi.org/project/django-reportato/1.0/", "requires_dist": null, "requires_python": null, "summary": "Very simple CSV reports with Django", "version": "1.0" }, "last_serial": 1278889, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "8a63eb39a95132aa1f1f4254313ec65d", "sha256": "fbe6b5a30bf2e25a0889c74cde0a019a09bd1c111410f9a2b7fd7528848a9edf" }, "downloads": -1, "filename": "django-reportato-1.0.tar.gz", "has_sig": false, "md5_digest": "8a63eb39a95132aa1f1f4254313ec65d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9255, "upload_time": "2014-10-22T15:06:25", "url": "https://files.pythonhosted.org/packages/52/b7/4868037f55168aff05808d3d740cdb06b4f08176a24665bf82ff29ca7469/django-reportato-1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8a63eb39a95132aa1f1f4254313ec65d", "sha256": "fbe6b5a30bf2e25a0889c74cde0a019a09bd1c111410f9a2b7fd7528848a9edf" }, "downloads": -1, "filename": "django-reportato-1.0.tar.gz", "has_sig": false, "md5_digest": "8a63eb39a95132aa1f1f4254313ec65d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9255, "upload_time": "2014-10-22T15:06:25", "url": "https://files.pythonhosted.org/packages/52/b7/4868037f55168aff05808d3d740cdb06b4f08176a24665bf82ff29ca7469/django-reportato-1.0.tar.gz" } ] }