def download(request):
    # Load data
    users = User.objects.filter(is_staff=False).exclude(username='******')
    user_ids = [user.id for user in users]
    submissions = ChallengeSubmission.objects.all()
    challenges = Challenge.all_published()

    # Create the zip file
    zipped_file = io.BytesIO()
    with zipfile.ZipFile(zipped_file, 'w', zipfile.ZIP_DEFLATED) as zipped:
        user_header = ['id', 'username', 'first_name', 'last_name', 'email', 'classes']
        user_values = [[u.id, u.username, u.first_name, u.last_name, u.email, LIST_DELIMITER.join(c.name for c in u.class_set.all())] for u in users]
        zipped.writestr('users.csv', csv_str(user_header, user_values))

        submission_header = ['author_id', 'challenge_id', 'created', 'result']
        date_fmt = '%Y-%m-%d:%H:%M:%S'
        submission_values = [[s.author_id, s.challenge_id, s.created.strftime(date_fmt), s.result] for s in submissions if s.author_id in user_ids]
        zipped.writestr('submissions.csv', csv_str(submission_header, submission_values))

        challenge_header = ['id', 'title', 'problem', 'tags']
        challenge_values = [[c.id, c.title, c.problem, LIST_DELIMITER.join([str(t) for t in c.tags.all()])] for c in challenges]
        zipped.writestr('challenges.csv', csv_str(challenge_header, challenge_values))

    zipped_file.seek(0)
    response = HttpResponse(zipped_file, content_type='application/zip')
    response['Content-Disposition'] = 'attachment; filename=report_data.zip'
    return response
def _make_add_or_change_context(extra_context, class_id=None):
    extra_context = extra_context or {}
    challenges = Challenge.all_published()
    blocks = ChallengeBlock.objects.filter(block_class__id=class_id)
    unavailable_challenges_ids = set(
        [c['id'] for b in blocks for c in b.challenges.values()])
    available_challenges = [
        c for c in challenges if c.id not in unavailable_challenges_ids
    ]
    extra_context['challenges'] = challenges
    extra_context['blocks'] = blocks
    extra_context['available_challenges'] = available_challenges
    return extra_context