Changeset - f7caf1435737
[Not reviewed]
Merge
2 11 4
Patrick Altman - 9 years ago 2015-08-03 13:01:44
paltman@gmail.com
Merge pull request #85 from miurahr/export_sponsor_zip

Enhanced sponsorship: export files
4 files changed with 502 insertions and 9 deletions:
0 comments (0 inline, 0 general)
symposion/sponsorship/management/commands/export_sponsors_data.py
Show inline comments
 
new file 100644
 
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"))
symposion/sponsorship/tests.py
Show inline comments
 
new file 100644
 
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")
symposion/sponsorship/urls.py
Show inline comments
...
 
@@ -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"),
 
)
symposion/sponsorship/views.py
Show inline comments
 
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 "
 
                                 "application. Please update your "
 
                                 "benefit details below.")
 
                messages.success(request, _("Thank you for your sponsorship "
 
                                            "application. Please update your "
 
                                            "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
0 comments (0 inline, 0 general)