Skip to content

Commit

Permalink
Close mozilla#116 - adding in a view to export user data as a CSV
Browse files Browse the repository at this point in the history
  • Loading branch information
rossbruniges committed Feb 18, 2013
1 parent 3c70ae6 commit dd01427
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 2 deletions.
3 changes: 3 additions & 0 deletions gameon/base/templates/403.html
Expand Up @@ -27,6 +27,9 @@ <h1 class="busta shout">Oops - looks like you've done something that you shouldn
<p>The Game On challenge hasn't opened yet, you can continue working on your game now but you won't be able to submit it until {{ request.challenge.start_date.strftime('%B %e, %Y') }}.</p>
{% endif %}
{% endif %}
{% if case == 'not_staff' %}
<p>Only members of the Mozilla Game On team are able to access this page.</p>
{% endif %}
</div>
<aside class="meta mini-col">
<h2 class="whimper">Here are some things you can do</h2>
Expand Down
48 changes: 48 additions & 0 deletions gameon/base/utils.py
@@ -1,6 +1,9 @@
import datetime
import hashlib
import os
import csv
import codecs
import cStringIO

from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.conf import settings
Expand Down Expand Up @@ -35,3 +38,48 @@ def get_paginator(queryset, page_number, items=settings.PAGINATOR_SIZE):
except (EmptyPage, InvalidPage):
paginated_query = paginator.page(paginator.num_pages)
return paginated_query


# In case we get unicode in the DB we use a custom reader able to handle it
class UTF8Recoder:
"""
Iterator that reads an encoded stream and reencodes the input to UTF-8
"""
def __init__(self, f, encoding):
self.reader = codecs.getreader(encoding)(f)

def __iter__(self):
return self

def next(self):
return self.reader.next().encode("utf-8")


class UnicodeWriter:
"""
A CSV writer which will write rows to CSV file "f",
which is encoded in the given encoding.
"""

def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
# Redirect output to a queue
self.queue = cStringIO.StringIO()
self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
self.stream = f
self.encoder = codecs.getincrementalencoder(encoding)()

def writerow(self, row):
self.writer.writerow([s.encode("utf-8") for s in row])
# Fetch UTF-8 output from the queue ...
data = self.queue.getvalue()
data = data.decode("utf-8")
# ... and reencode it into the target encoding
data = self.encoder.encode(data)
# write to the target stream
self.stream.write(data)
# empty queue
self.queue.truncate(0)

def writerows(self, rows):
for row in rows:
self.writerow(row)
8 changes: 8 additions & 0 deletions gameon/submissions/models.py
Expand Up @@ -4,6 +4,7 @@
from django.db import models
from django.core.validators import MaxLengthValidator
from django.contrib.auth.models import AnonymousUser
from django.core.urlresolvers import reverse
from django.conf import settings

from tower import ugettext_lazy as _
Expand Down Expand Up @@ -157,5 +158,12 @@ def get_entry_feature(self):
if self.thumbnail:
return '<img src="%s" alt=""/>' % self.get_image_src()

def get_absolute_url(self):
site_url = getattr(settings, 'SITE_URL', '')
entry_url = reverse('submissions.entry_single',
kwargs={'slug': self.slug})

return "%s%s" % (site_url, entry_url)

class Meta:
verbose_name_plural = 'Entries'
2 changes: 2 additions & 0 deletions gameon/submissions/urls.py
Expand Up @@ -5,6 +5,8 @@
urlpatterns = patterns('',
url(r'^apply/$', views.create, name='submissions.create_entry'),
url(r'^entries/all/$', views.list, name='submissions.entry_list'),
url(r'^entries/export/$', views.export_csv,
name='submissions.export_csv'),
url(r'^entries/in/(?P<category>[\w-]+)/$', views.list, name='submissions.entry_list'),
url(r'^entries/(?P<slug>[\w-]+)/$', views.single,
name='submissions.entry_single'),
Expand Down
43 changes: 41 additions & 2 deletions gameon/submissions/views.py
Expand Up @@ -2,14 +2,14 @@
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.conf import settings
from django.http import HttpResponseRedirect, Http404
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.template.defaultfilters import slugify
from django.contrib.auth.decorators import login_required

from tower import ugettext as _

from gameon.base.views import action_unavailable_response
from gameon.base.utils import get_page, get_paginator
from gameon.base.utils import get_page, get_paginator, UnicodeWriter
from gameon.submissions.models import Entry, Category
from gameon.submissions.forms import EntryForm, NewEntryForm

Expand Down Expand Up @@ -107,3 +107,42 @@ def single(request, slug, template='submissions/single.html'):
'entry': entry,
}
return render(request, template, data)


def export_csv(request, template='submissions/table.html'):
if not request.user.is_staff:
return action_unavailable_response(request, case='not_staff')

response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="gameon_entries.csv"'

entry_set = Entry.objects.all().order_by('-pk')

# using the custom Unicode writer so the view doesn't explode
writer = UnicodeWriter(response)
# Write out the header row
writer.writerow([
'GAME NAME',
'CATEGORY',
'GAME WEBSITE',
'GAME DESCRIPTION',
'GAME SUBMISSION URL',
'GAME VIDEO URL',
'SUBMITTED BY',
'SUBMITTED BIO',
'SUBMIT TO MARKETPLACE?'
])
for e in entry_set:
writer.writerow([
e.title,
e.category.name,
e.url,
e.description,
e.get_absolute_url(),
e.video_url,
e.created_by.display_name,
e.created_by.bio,
str(e.to_market),
])

return response

0 comments on commit dd01427

Please sign in to comment.