Merge pull request #85 from miurahr/export_sponsor_zip
Enhanced sponsorship: export files
This commit is contained in:
commit
f7caf14357
4 changed files with 502 additions and 9 deletions
|
@ -0,0 +1,77 @@
|
|||
import csv
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
|
||||
from contextlib import closing
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.template.defaultfilters import slugify
|
||||
|
||||
from sotmjp.sponsorship.models import Sponsor
|
||||
|
||||
|
||||
def zipdir(basedir, archivename):
|
||||
assert os.path.isdir(basedir)
|
||||
with closing(zipfile.ZipFile(archivename, "w", zipfile.ZIP_DEFLATED)) as z:
|
||||
for root, dirs, files in os.walk(basedir):
|
||||
#NOTE: ignore empty directories
|
||||
for fn in files:
|
||||
absfn = os.path.join(root, fn)
|
||||
zfn = absfn[len(basedir) + len(os.sep):] # XXX: relative path
|
||||
z.write(absfn, zfn)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
try:
|
||||
os.makedirs(os.path.join(os.getcwd(), "build"))
|
||||
except:
|
||||
pass
|
||||
|
||||
csv_file = csv.writer(
|
||||
open(os.path.join(os.getcwd(), "build", "sponsors.csv"), "wb")
|
||||
)
|
||||
csv_file.writerow(["Name", "URL", "Level", "Description"])
|
||||
|
||||
for sponsor in Sponsor.objects.all():
|
||||
path = os.path.join(os.getcwd(), "build", slugify(sponsor.name))
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
data = {
|
||||
"name": sponsor.name,
|
||||
"url": sponsor.external_url,
|
||||
"level": sponsor.level.name,
|
||||
"description": "",
|
||||
}
|
||||
for sponsor_benefit in sponsor.sponsor_benefits.all():
|
||||
if sponsor_benefit.benefit_id == 2:
|
||||
data["description"] = sponsor_benefit.text
|
||||
if sponsor_benefit.benefit_id == 1:
|
||||
if sponsor_benefit.upload:
|
||||
data["ad"] = sponsor_benefit.upload.path
|
||||
if sponsor_benefit.benefit_id == 7:
|
||||
if sponsor_benefit.upload:
|
||||
data["logo"] = sponsor_benefit.upload.path
|
||||
|
||||
if "ad" in data:
|
||||
ad_path = data.pop("ad")
|
||||
shutil.copy(ad_path, path)
|
||||
if "logo" in data:
|
||||
logo_path = data.pop("logo")
|
||||
shutil.copy(logo_path, path)
|
||||
|
||||
csv_file.writerow([
|
||||
data["name"].encode("utf-8"),
|
||||
data["url"].encode("utf-8"),
|
||||
data["level"].encode("utf-8"),
|
||||
data["description"].encode("utf-8")
|
||||
])
|
||||
|
||||
zipdir(
|
||||
os.path.join(os.getcwd(), "build"),
|
||||
os.path.join(os.getcwd(), "sponsors.zip"))
|
307
symposion/sponsorship/tests.py
Normal file
307
symposion/sponsorship/tests.py
Normal file
|
@ -0,0 +1,307 @@
|
|||
from cStringIO import StringIO
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from zipfile import ZipFile
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from pycon.sponsorship.models import Benefit, Sponsor, SponsorBenefit,\
|
||||
SponsorLevel
|
||||
from symposion.conference.models import current_conference
|
||||
|
||||
|
||||
class TestSponsorZipDownload(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(username='joe',
|
||||
email='joe@example.com',
|
||||
password='joe')
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
self.url = reverse("sponsor_zip_logos")
|
||||
self.assertTrue(self.client.login(username='joe@example.com',
|
||||
password='joe'))
|
||||
|
||||
# we need a sponsor
|
||||
conference = current_conference()
|
||||
self.sponsor_level = SponsorLevel.objects.create(
|
||||
conference=conference, name="Lead", cost=1)
|
||||
self.sponsor = Sponsor.objects.create(
|
||||
name="Big Daddy",
|
||||
level=self.sponsor_level,
|
||||
active=True,
|
||||
)
|
||||
|
||||
# Create our benefits, of various types
|
||||
self.text_benefit = Benefit.objects.create(name="text", type="text")
|
||||
self.file_benefit = Benefit.objects.create(name="file", type="file")
|
||||
# These names must be spelled exactly this way:
|
||||
self.weblogo_benefit = Benefit.objects.create(name="Web logo", type="weblogo")
|
||||
self.printlogo_benefit = Benefit.objects.create(name="Print logo", type="file")
|
||||
self.advertisement_benefit = Benefit.objects.create(name="Advertisement", type="file")
|
||||
|
||||
def validate_response(self, rsp, names_and_sizes):
|
||||
# Ensure a response from the view looks right, contains a valid
|
||||
# zip archive, has files with the right names and sizes.
|
||||
self.assertEqual("application/zip", rsp['Content-type'])
|
||||
prefix = settings.CONFERENCE_URL_PREFIXES[settings.CONFERENCE_ID]
|
||||
|
||||
self.assertEqual(
|
||||
'attachment; filename="pycon_%s_sponsorlogos.zip"' % prefix,
|
||||
rsp['Content-Disposition'])
|
||||
zipfile = ZipFile(StringIO(rsp.content), "r")
|
||||
# Check out the zip - testzip() returns None if no errors found
|
||||
self.assertIsNone(zipfile.testzip())
|
||||
# Compare contents to what is expected
|
||||
infolist = zipfile.infolist()
|
||||
self.assertEqual(len(names_and_sizes), len(infolist))
|
||||
for info, name_and_size in zip(infolist, names_and_sizes):
|
||||
name, size = name_and_size
|
||||
self.assertEqual(name, info.filename)
|
||||
self.assertEqual(size, info.file_size)
|
||||
|
||||
def make_temp_file(self, name, size=0):
|
||||
# Create a temp file with the given name and size under self.temp_dir
|
||||
path = os.path.join(self.temp_dir, name)
|
||||
with open(path, "wb") as f:
|
||||
f.write(size * "x")
|
||||
|
||||
def test_must_be_logged_in(self):
|
||||
# Must be logged in to use the view
|
||||
# If not logged in, doesn't redirect, just serves up a login view
|
||||
self.client.logout()
|
||||
rsp = self.client.get(self.url)
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
self.assertIn("""<body class="login">""", rsp.content)
|
||||
|
||||
def test_must_be_staff(self):
|
||||
# Only staff can use the view
|
||||
# If not staff, doesn't show error, just serves up a login view
|
||||
# Also, the dashboard doesn't show the download button
|
||||
self.user.is_staff = False
|
||||
self.user.save()
|
||||
rsp = self.client.get(self.url)
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
self.assertIn("""<body class="login">""", rsp.content)
|
||||
rsp = self.client.get(reverse('dashboard'))
|
||||
self.assertNotIn(self.url, rsp.content)
|
||||
|
||||
def test_no_files(self):
|
||||
# If there are no sponsor files, we still work
|
||||
# And the dashboard shows our download button
|
||||
rsp = self.client.get(self.url)
|
||||
self.validate_response(rsp, [])
|
||||
rsp = self.client.get(reverse('dashboard'))
|
||||
self.assertIn(self.url, rsp.content)
|
||||
|
||||
def test_different_benefit_types(self):
|
||||
# We only get files from the benefits named "Print logo" and "Web logo"
|
||||
# And we ignore any non-existent files
|
||||
try:
|
||||
# Create a temp dir for media files
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
with override_settings(MEDIA_ROOT=self.temp_dir):
|
||||
|
||||
# Give our sponsor some benefits
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.text_benefit,
|
||||
text="Foo!"
|
||||
)
|
||||
|
||||
self.make_temp_file("file1", 10)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.file_benefit,
|
||||
upload="file1"
|
||||
)
|
||||
|
||||
self.make_temp_file("file2", 20)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.weblogo_benefit,
|
||||
upload="file2"
|
||||
)
|
||||
|
||||
# Benefit whose file is missing from the disk
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.weblogo_benefit,
|
||||
upload="file3"
|
||||
)
|
||||
|
||||
# print logo benefit
|
||||
self.make_temp_file("file4", 40)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.printlogo_benefit,
|
||||
upload="file4"
|
||||
)
|
||||
|
||||
self.make_temp_file("file5", 50)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.advertisement_benefit,
|
||||
upload="file5"
|
||||
)
|
||||
|
||||
rsp = self.client.get(self.url)
|
||||
expected = [
|
||||
('web_logos/lead/big_daddy/file2', 20),
|
||||
('print_logos/lead/big_daddy/file4', 40),
|
||||
('advertisement/lead/big_daddy/file5', 50)
|
||||
]
|
||||
self.validate_response(rsp, expected)
|
||||
finally:
|
||||
if hasattr(self, 'temp_dir'):
|
||||
# Clean up any temp media files
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_file_org(self):
|
||||
# The zip file is organized into directories:
|
||||
# {print_logos,web_logos,advertisement}/<sponsor_level>/<sponsor_name>/<filename>
|
||||
|
||||
# Add another sponsor at a different sponsor level
|
||||
conference = current_conference()
|
||||
self.sponsor_level2 = SponsorLevel.objects.create(
|
||||
conference=conference, name="Silly putty", cost=1)
|
||||
self.sponsor2 = Sponsor.objects.create(
|
||||
name="Big Mama",
|
||||
level=self.sponsor_level2,
|
||||
active=True,
|
||||
)
|
||||
#
|
||||
try:
|
||||
# Create a temp dir for media files
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
with override_settings(MEDIA_ROOT=self.temp_dir):
|
||||
|
||||
# Give our sponsors some benefits
|
||||
self.make_temp_file("file1", 10)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.weblogo_benefit,
|
||||
upload="file1"
|
||||
)
|
||||
# print logo benefit
|
||||
self.make_temp_file("file2", 20)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.printlogo_benefit,
|
||||
upload="file2"
|
||||
)
|
||||
# Sponsor 2
|
||||
self.make_temp_file("file3", 30)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor2,
|
||||
benefit=self.weblogo_benefit,
|
||||
upload="file3"
|
||||
)
|
||||
# print logo benefit
|
||||
self.make_temp_file("file4", 42)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor2,
|
||||
benefit=self.printlogo_benefit,
|
||||
upload="file4"
|
||||
)
|
||||
# ad benefit
|
||||
self.make_temp_file("file5", 55)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor2,
|
||||
benefit=self.advertisement_benefit,
|
||||
upload="file5"
|
||||
)
|
||||
|
||||
rsp = self.client.get(self.url)
|
||||
expected = [
|
||||
('web_logos/lead/big_daddy/file1', 10),
|
||||
('web_logos/silly_putty/big_mama/file3', 30),
|
||||
('print_logos/lead/big_daddy/file2', 20),
|
||||
('print_logos/silly_putty/big_mama/file4', 42),
|
||||
('advertisement/silly_putty/big_mama/file5', 55),
|
||||
]
|
||||
self.validate_response(rsp, expected)
|
||||
finally:
|
||||
if hasattr(self, 'temp_dir'):
|
||||
# Clean up any temp media files
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
|
||||
class TestBenefitValidation(TestCase):
|
||||
"""
|
||||
It should not be possible to save a SponsorBenefit if it has the
|
||||
wrong kind of data in it - e.g. a text-type benefit cannot have
|
||||
an uploaded file, and vice-versa.
|
||||
"""
|
||||
def setUp(self):
|
||||
# we need a sponsor
|
||||
conference = current_conference()
|
||||
self.sponsor_level = SponsorLevel.objects.create(
|
||||
conference=conference, name="Lead", cost=1)
|
||||
self.sponsor = Sponsor.objects.create(
|
||||
name="Big Daddy",
|
||||
level=self.sponsor_level,
|
||||
)
|
||||
|
||||
# Create our benefit types
|
||||
self.text_type = Benefit.objects.create(name="text", type="text")
|
||||
self.file_type = Benefit.objects.create(name="file", type="file")
|
||||
self.weblogo_type = Benefit.objects.create(name="log", type="weblogo")
|
||||
self.simple_type = Benefit.objects.create(name="simple", type="simple")
|
||||
|
||||
def validate(self, should_work, benefit_type, upload, text):
|
||||
obj = SponsorBenefit(
|
||||
benefit=benefit_type,
|
||||
sponsor=self.sponsor,
|
||||
upload=upload,
|
||||
text=text
|
||||
)
|
||||
if should_work:
|
||||
obj.save()
|
||||
else:
|
||||
with self.assertRaises(ValidationError):
|
||||
obj.save()
|
||||
|
||||
def test_text_has_text(self):
|
||||
self.validate(True, self.text_type, upload=None, text="Some text")
|
||||
|
||||
def test_text_has_upload(self):
|
||||
self.validate(False, self.text_type, upload="filename", text='')
|
||||
|
||||
def test_text_has_both(self):
|
||||
self.validate(False, self.text_type, upload="filename", text="Text")
|
||||
|
||||
def test_file_has_text(self):
|
||||
self.validate(False, self.file_type, upload=None, text="Some text")
|
||||
|
||||
def test_file_has_upload(self):
|
||||
self.validate(True, self.file_type, upload="filename", text='')
|
||||
|
||||
def test_file_has_both(self):
|
||||
self.validate(False, self.file_type, upload="filename", text="Text")
|
||||
|
||||
def test_weblogo_has_text(self):
|
||||
self.validate(False, self.weblogo_type, upload=None, text="Some text")
|
||||
|
||||
def test_weblogo_has_upload(self):
|
||||
self.validate(True, self.weblogo_type, upload="filename", text='')
|
||||
|
||||
def test_weblogo_has_both(self):
|
||||
self.validate(False, self.weblogo_type, upload="filename", text="Text")
|
||||
|
||||
def test_simple_has_neither(self):
|
||||
self.validate(True, self.simple_type, upload=None, text='')
|
||||
|
||||
def test_simple_has_text(self):
|
||||
self.validate(True, self.simple_type, upload=None, text="Some text")
|
||||
|
||||
def test_simple_has_upload(self):
|
||||
self.validate(False, self.simple_type, upload="filename", text='')
|
||||
|
||||
def test_simple_has_both(self):
|
||||
self.validate(False, self.simple_type, upload="filename", text="Text")
|
|
@ -7,5 +7,6 @@ urlpatterns = patterns(
|
|||
url(r"^$", TemplateView.as_view(template_name="sponsorship/list.html"), name="sponsor_list"),
|
||||
url(r"^apply/$", "sponsor_apply", name="sponsor_apply"),
|
||||
url(r"^add/$", "sponsor_add", name="sponsor_add"),
|
||||
url(r"^ziplogos/$", "sponsor_zip_logo_files", name="sponsor_zip_logos"),
|
||||
url(r"^(?P<pk>\d+)/$", "sponsor_detail", name="sponsor_detail"),
|
||||
)
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
from django.http import Http404
|
||||
from cStringIO import StringIO
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from zipfile import ZipFile, ZipInfo
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import render_to_response, redirect, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from symposion.sponsorship.forms import SponsorApplicationForm, SponsorDetailsForm, \
|
||||
SponsorBenefitsFormSet
|
||||
from symposion.sponsorship.models import Sponsor, SponsorBenefit
|
||||
from symposion.sponsorship.forms import SponsorApplicationForm, \
|
||||
SponsorDetailsForm, SponsorBenefitsFormSet
|
||||
from symposion.sponsorship.models import Benefit, Sponsor, SponsorBenefit, \
|
||||
SponsorLevel
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -18,13 +33,13 @@ def sponsor_apply(request):
|
|||
sponsor = form.save()
|
||||
if sponsor.sponsor_benefits.all():
|
||||
# Redirect user to sponsor_detail to give extra information.
|
||||
messages.success(request, "Thank you for your sponsorship "
|
||||
messages.success(request, _("Thank you for your sponsorship "
|
||||
"application. Please update your "
|
||||
"benefit details below.")
|
||||
"benefit details below."))
|
||||
return redirect("sponsor_detail", pk=sponsor.pk)
|
||||
else:
|
||||
messages.success(request, "Thank you for your sponsorship "
|
||||
"application.")
|
||||
messages.success(request, _("Thank you for your sponsorship "
|
||||
"application."))
|
||||
return redirect("dashboard")
|
||||
else:
|
||||
form = SponsorApplicationForm(user=request.user)
|
||||
|
@ -87,3 +102,96 @@ def sponsor_detail(request, pk):
|
|||
"form": form,
|
||||
"formset": formset,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@staff_member_required
|
||||
def sponsor_export_data(request):
|
||||
sponsors = []
|
||||
data = ""
|
||||
|
||||
for sponsor in Sponsor.objects.order_by("added"):
|
||||
d = {
|
||||
"name": sponsor.name,
|
||||
"url": sponsor.external_url,
|
||||
"level": (sponsor.level.order, sponsor.level.name),
|
||||
"description": "",
|
||||
}
|
||||
for sponsor_benefit in sponsor.sponsor_benefits.all():
|
||||
if sponsor_benefit.benefit_id == 2:
|
||||
d["description"] = sponsor_benefit.text
|
||||
sponsors.append(d)
|
||||
|
||||
def izip_longest(*args):
|
||||
fv = None
|
||||
|
||||
def sentinel(counter=([fv] * (len(args) - 1)).pop):
|
||||
yield counter()
|
||||
iters = [itertools.chain(it, sentinel(), itertools.repeat(fv)) for it in args]
|
||||
try:
|
||||
for tup in itertools.izip(*iters):
|
||||
yield tup
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
def pairwise(iterable):
|
||||
a, b = itertools.tee(iterable)
|
||||
b.next()
|
||||
return izip_longest(a, b)
|
||||
|
||||
def level_key(s):
|
||||
return s["level"]
|
||||
|
||||
for level, level_sponsors in itertools.groupby(sorted(sponsors, key=level_key), level_key):
|
||||
data += "%s\n" % ("-" * (len(level[1]) + 4))
|
||||
data += "| %s |\n" % level[1]
|
||||
data += "%s\n\n" % ("-" * (len(level[1]) + 4))
|
||||
for sponsor, next in pairwise(level_sponsors):
|
||||
description = sponsor["description"].strip()
|
||||
description = description if description else "-- NO DESCRIPTION FOR THIS SPONSOR --"
|
||||
data += "%s\n\n%s" % (sponsor["name"], description)
|
||||
if next is not None:
|
||||
data += "\n\n%s\n\n" % ("-" * 80)
|
||||
else:
|
||||
data += "\n\n"
|
||||
|
||||
return HttpResponse(data, content_type="text/plain;charset=utf-8")
|
||||
|
||||
|
||||
@staff_member_required
|
||||
def sponsor_zip_logo_files(request):
|
||||
"""Return a zip file of sponsor web and print logos"""
|
||||
|
||||
zip_stringio = StringIO()
|
||||
zipfile = ZipFile(zip_stringio, "w")
|
||||
try:
|
||||
benefits = Benefit.objects.all()
|
||||
for benefit in benefits:
|
||||
dir_name = benefit.name.lower().replace(" ", "_").replace('/', '_')
|
||||
for level in SponsorLevel.objects.all():
|
||||
level_name = level.name.lower().replace(" ", "_").replace('/', '_')
|
||||
for sponsor in Sponsor.objects.filter(level=level, active=True):
|
||||
sponsor_name = sponsor.name.lower().replace(" ", "_").replace('/', '_')
|
||||
full_dir = "/".join([dir_name, level_name, sponsor_name])
|
||||
for sponsor_benefit in SponsorBenefit.objects.filter(
|
||||
benefit=benefit,
|
||||
sponsor=sponsor,
|
||||
active=True,
|
||||
).exclude(upload=''):
|
||||
if os.path.exists(sponsor_benefit.upload.path):
|
||||
modtime = time.gmtime(os.stat(sponsor_benefit.upload.path).st_mtime)
|
||||
with open(sponsor_benefit.upload.path, "rb") as f:
|
||||
fname = os.path.split(sponsor_benefit.upload.name)[-1]
|
||||
zipinfo = ZipInfo(filename=full_dir + "/" + fname,
|
||||
date_time=modtime)
|
||||
zipfile.writestr(zipinfo, f.read())
|
||||
else:
|
||||
log.debug("No such sponsor file: %s" % sponsor_benefit.upload.path)
|
||||
finally:
|
||||
zipfile.close()
|
||||
|
||||
response = HttpResponse(zip_stringio.getvalue(),
|
||||
content_type="application/zip")
|
||||
prefix = settings.CONFERENCE_URL_PREFIXES[settings.CONFERENCE_ID]
|
||||
response['Content-Disposition'] = \
|
||||
'attachment; filename="%s_sponsorlogos.zip"' % prefix
|
||||
return response
|
||||
|
|
Loading…
Reference in a new issue