<commit_before>import csv
import logging
import re

from smtplib import SMTPException

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mass_mail
from django.core.urlresolvers import reverse
from django.http.response import HttpResponse, HttpResponseForbidden
from django.shortcuts import redirect, render, get_object_or_404
from django.template import Template, Context
from django.utils.translation import ugettext as _

from .forms import FinancialAidApplicationForm, MessageForm, \
    FinancialAidReviewForm, ReviewerMessageForm, BulkEmailForm, ReceiptForm
from .models import FinancialAidApplication, FinancialAidMessage, \
    FinancialAidReviewData, STATUS_CHOICES, Receipt
from .utils import applications_open, email_address, email_context, \
    has_application, is_reviewer, send_email_message

log = logging.getLogger(__name__)

@login_required
def finaid_edit(request):
    """Apply for, or edit application for, financial aid"""

    if not applications_open():
        messages.add_message(request, messages.ERROR,
                             _('Financial aid applications are not open '
                               'at this time'))
        return redirect("dashboard")

    if has_application(request.user):
        application = request.user.financial_aid
        applying = False
    else:
        application = FinancialAidApplication(user=request.user)
        applying = True

    form = FinancialAidApplicationForm(request.POST or None,
                                       instance=application)
    if form.is_valid():
        form.save()

        context = email_context(request, application)

        # Let user know we got it by emailing them
        # Also notify the committee
        template_name = "applicant/" + \
                        ("submitted" if applying else "edited")
        send_email_message(template_name,
                           from_=email_address(),
                           to=[request.user.email],
                           context=context)
        template_name = "reviewer/" + \
                        ("submitted" if applying else "edited")
        send_email_message(template_name,
                           from_=request.user.email,
                           to=[email_address()],
                           context=context)

        # Also display a message to them
        messages.add_message(request, messages.INFO,
                             _(u"Application submitted"))

        return redirect("dashboard")

    return render(request, "finaid/edit.html", {
        "form": form,
        "applying": applying,
    })

@login_required
def finaid_review(request, pks=None):
    """Starting view for reviewers - list the applications"""
    # On a POST the pks are in the form.
    # On a GET there might be pks in the URL.

    if not is_reviewer(request.user):
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    if request.method == 'POST':
        # They want to do something to bulk applicants
        # Find the checkboxes they checked
        regex = re.compile(r'^finaid_application_(.*)$')
        pk_list = []
        for field_name in request.POST:
            m = regex.match(field_name)
            if m:
                pk_list.append(m.group(1))
        if not pk_list:
            messages.add_message(
                request, messages.ERROR,
                _(u"Please select at least one application"))
            return redirect(request.path)

        if 'email_action' in request.POST:
            # They want to email applicants
            pks = ",".join(pk_list)
            return redirect('finaid_email', pks=pks)
        elif 'message_action' in request.POST:
            # They want to attach a message to applications
            pks = ",".join(pk_list)
            return redirect('finaid_message', pks=pks)
        elif 'status_action' in request.POST:
            # They want to change applications' statuses
            applications = FinancialAidApplication.objects.filter(pk__in=pk_list)\
                .select_related('review')
            status = int(request.POST['status'])
            count = 0
            for application in applications:
                try:
                    review = application.review
                except FinancialAidReviewData.DoesNotExist:
                    review = FinancialAidReviewData(application=application)
                if review.status != status:
                    review.status = status
                    review.save()
                    count += 1
            messages.info(request,
                          "Updated %d application status%s" % (count, "" if count == 1 else "es"))
            pks = ",".join(pk_list)
            return redirect(reverse('finaid_review', kwargs=dict(pks=pks)))
        else:
            messages.error(request, "WHAT?")
    else:
        # GET - pks are in the URL.  maybe.
        pk_list = pks.split(",") if pks else []

    return render(request, "finaid/application_list.html", {
        "applications": FinancialAidApplication.objects.all().select_related('review'),
        "status_options": STATUS_CHOICES,
        "pks": [int(pk) for pk in pk_list],
    })

@login_required
def finaid_message(request, pks):
    """Add a message to some applications"""
    if not is_reviewer(request.user):
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    applications = FinancialAidApplication.objects.filter(pk__in=pks.split(","))\
        .select_related('user')
    if not applications.exists():
        messages.add_message(request, messages.ERROR, _(u"No applications selected"))
        return redirect('finaid_review')

    if request.method == 'POST':
        for application in applications:
            message = FinancialAidMessage(user=request.user,
                                          application=application)
            message_form = ReviewerMessageForm(request.POST, instance=message)
            if message_form.is_valid():
                message = message_form.save()
                # Send notice to reviewers/pycon-aid alias, and the applicant if visible
                context = email_context(request, application, message)
                send_email_message("reviewer/message",
                                   # From whoever is logged in clicking the buttons
                                   from_=request.user.email,
                                   to=[email_address()],
                                   context=context,
                                   headers={'Reply-To': email_address()}
                                   )
                # If visible to applicant, notify them as well
                if message.visible:
                    send_email_message("applicant/message",
                                       from_=request.user.email,
                                       to=[application.user.email],
                                       context=context,
                                       headers={'Reply-To': email_address()}
                                       )
            messages.add_message(request, messages.INFO, _(u"Messages sent"))
        return redirect(reverse('finaid_review', kwargs=dict(pks=pks)))
    else:
        message_form = ReviewerMessageForm()

    return render(request, "finaid/reviewer_message.html", {
        'applications': applications,
        'form': message_form,
    })

@login_required
def finaid_email(request, pks):
    if not is_reviewer(request.user):
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    applications = FinancialAidApplication.objects.filter(pk__in=pks.split(","))\
        .select_related('user')
    emails = [app.user.email for app in applications]

    form = None
    if request.method == 'POST':
        form = BulkEmailForm(request.POST)
        if form.is_valid():
            subject = form.cleaned_data['subject']
            from_email = email_address()
            template_text = form.cleaned_data['template'].template
            template = Template(template_text)
            # emails will be the datatuple arg to send_mail_mail
            emails = []
            for application in applications:
                try:
                    review = application.review
                except FinancialAidReviewData.DoesNotExist:
                    review = None

                ctx = {
                    'application': application,
                    'review': review,
                }
                text = template.render(Context(ctx))
                emails.append((subject, text, from_email,
                               [application.user.email]))
            try:
                send_mass_mail(emails)
            except SMTPException:
                log.exception("ERROR sending financial aid emails")
                messages.add_message(request, messages.ERROR,
                                     _(u"There was some error sending emails, "
                                       u"not all of them might have made it"))
            else:
                messages.add_message(request, messages.INFO, _(u"Emails sent"))
            return redirect(reverse('finaid_review', kwargs=dict(pks=pks)))

    ctx = {
        'form': form or BulkEmailForm(),
        'users': [app.user for app in applications]
    }
    return render(request, "finaid/email.html", ctx)

@login_required
def finaid_review_detail(request, pk):
    """Review a particular application"""
    application = get_object_or_404(FinancialAidApplication, pk=pk)

    # Redirect a a reviewer who is attempting to access FA application detail
    # page to their edit page
    if is_reviewer(request.user) and request.user == application.user:
            return redirect("finaid_edit")

    if not is_reviewer(request.user):
        # Redirect a non reviewer to their FA edit page
        if has_application(request.user):
            return redirect("finaid_edit")
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    try:
        review_data = application.review
    except FinancialAidReviewData.DoesNotExist:
        review_data = FinancialAidReviewData(application=application)

    message_form = None
    review_form = None

    if request.method == 'POST':
        if 'message_submit' in request.POST:
            message = FinancialAidMessage(user=request.user,
                                          application=application)
            message_form = ReviewerMessageForm(request.POST, instance=message)
            if message_form.is_valid():
                message = message_form.save()
                # Send notice to the reviewers alias
                # If the message is visible, also send to the applicant
                context = email_context(request, application, message)
                # Notify reviewers
                send_email_message("reviewer/message",
                                   from_=request.user.email,
                                   to=[email_address()],
                                   context=context)
                # If visible to applicant, notify them as well
                if message.visible:
                    send_email_message("applicant/message",
                                       from_=request.user.email,
                                       to=[application.user.email],
                                       context=context)
                messages.add_message(
                    request, messages.INFO,
                    _(u"Message has been added to the application, and recipients notified by email."))
                return redirect(request.path)
        elif 'review_submit' in request.POST:
            review_form = FinancialAidReviewForm(request.POST,
                                                 instance=review_data)
            if review_form.is_valid():
                review_data = review_form.save()
                return redirect(reverse("finaid_review"))
        else:
            log.error("finaid_review_detail posted with unknown form: %r"
                      % request.POST)
            return HttpResponseForbidden("HEY WHY WAS THIS POSTED")

    # Create initial forms if needed
    message_form = message_form or ReviewerMessageForm()
    review_form = review_form or FinancialAidReviewForm(instance=review_data)

    context = {
        "application": application,
        "message_form": message_form,
        "review_form": review_form,
        "review_messages": FinancialAidMessage.objects.filter(
            application=application
        )
    }
    return render(request, "finaid/review.html", context)

@login_required
def finaid_status(request):
    """
    Show an applicant the status of their application.
    Allow them to see messages from the reviewers and to submit
    messages to them.
    """
    if not has_application(request.user):
        messages.add_message(request, messages.ERROR,
                             _(u'You have not applied for financial aid'))
        return redirect("dashboard")

    if request.method == 'POST':
        application = request.user.financial_aid
        message = FinancialAidMessage(user=request.user,
                                      application=application,
                                      visible=True)
        message_form = MessageForm(request.POST, instance=message)
        if message_form.is_valid():
            message = message_form.save()

            # Send notice to the reviewers/pycon-aid alias
            # (applicant submitted this message so no need to tell them)
            context = email_context(request, application, message)
            send_email_message("reviewer/message",
                               from_=request.user.email,
                               to=[email_address()],
                               context=context)

            return redirect(request.path)
    else:
        message_form = MessageForm()

    # Only show the applicant messages that are supposed to be visible to the
    # applicant
    visible_messages = FinancialAidMessage.objects.filter(
        application=request.user.financial_aid,
        visible=True
    )

    return render(request, "finaid/status.html", {
        'application': request.user.financial_aid,
        'visible_messages': visible_messages,
        'form': message_form,
    })

@login_required
def finaid_download_csv(request):
    # Download financial aid application data as a .CSV file

    if not is_reviewer(request.user):
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    # Fields to include
    application_field_names = ['id'] + [
        f.attname for f, model in FinancialAidApplication._meta.get_fields_with_model()
        if f.attname not in ['id', 'review']
    ] + ['email', 'user']
    reviewdata_field_names = [
        f.attname for f, model in FinancialAidReviewData._meta.get_fields_with_model()
        if f.attname not in ['application', 'id', 'last_update']
    ]

    # For these fields, use the get_FIELDNAME_display() method so we get
    # the name of the choice (or other custom string) instead of the internal value
    use_display_method = [
        'cash_check',
        'last_update',
        'presenting',
        'status',
        'travel_cash_check',
    ]

    def get_value(name, object):
        # Get a value from an application or review, using get_NAME_display
        # if appropriate, then forcing to a unicode string and encoding in
        # UTF-8 for CSV
        if name in use_display_method:
            display_method = getattr(object, "get_%s_display" % name)
            value = display_method()
        elif name == 'email':
            value = object.user.email
        else:
            value = getattr(object, name)
        return unicode(value).encode('utf-8')

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

    writer = csv.DictWriter(
        response,
        fieldnames=application_field_names+reviewdata_field_names
    )
    writer.writeheader()

    default_review_data = FinancialAidReviewData()
    apps = FinancialAidApplication.objects.all().select_related('review')
    for application in apps.order_by('pk'):
        # They won't all have review data, so use the default values if they don't
        try:
            review = application.review
        except FinancialAidReviewData.DoesNotExist:
            review = default_review_data

        # Write the data for this application.
        data = {}
        for name in application_field_names:
            data[name] = get_value(name, application)
        for name in reviewdata_field_names:
            data[name] = get_value(name, review)
        writer.writerow(data)

    return response

@login_required
def receipt_form(request):
    receipts = Receipt.objects.filter(user__username=request.user.username)

    if request.method == 'POST':
        form = ReceiptForm(request.POST, request.FILES)

        if form.is_valid():
            form.instance.user = request.user
            form.instance.application = request.user.financial_aid
            form.save()

            # Display a message to user
            messages.add_message(request, messages.INFO,
                                _(u"Receipt submitted"))

            return render(request, "finaid/receipt_form.html", {
                'form' : ReceiptForm(),
                'receipts': receipts
            })
    else:
        form = ReceiptForm()
    return render(request, "finaid/receipt_form.html", {
        'form': form,
        'receipts': receipts
    })

from django.shortcuts import get_object_or_404
@login_required
def receipt_delete(request, pk):
    receipt = get_object_or_404(Receipt, pk=pk).delete()
    return redirect("receipt_form")
<commit_msg>remove unnecessary lines<commit_after>import csv
import logging
import re

from smtplib import SMTPException

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mass_mail
from django.core.urlresolvers import reverse
from django.http.response import HttpResponse, HttpResponseForbidden
from django.shortcuts import redirect, render, get_object_or_404
from django.template import Template, Context
from django.utils.translation import ugettext as _

from .forms import FinancialAidApplicationForm, MessageForm, \
    FinancialAidReviewForm, ReviewerMessageForm, BulkEmailForm, ReceiptForm
from .models import FinancialAidApplication, FinancialAidMessage, \
    FinancialAidReviewData, STATUS_CHOICES, Receipt
from .utils import applications_open, email_address, email_context, \
    has_application, is_reviewer, send_email_message

log = logging.getLogger(__name__)

@login_required
def finaid_edit(request):
    """Apply for, or edit application for, financial aid"""

    if not applications_open():
        messages.add_message(request, messages.ERROR,
                             _('Financial aid applications are not open '
                               'at this time'))
        return redirect("dashboard")

    if has_application(request.user):
        application = request.user.financial_aid
        applying = False
    else:
        application = FinancialAidApplication(user=request.user)
        applying = True

    form = FinancialAidApplicationForm(request.POST or None,
                                       instance=application)
    if form.is_valid():
        form.save()

        context = email_context(request, application)

        # Let user know we got it by emailing them
        # Also notify the committee
        template_name = "applicant/" + \
                        ("submitted" if applying else "edited")
        send_email_message(template_name,
                           from_=email_address(),
                           to=[request.user.email],
                           context=context)
        template_name = "reviewer/" + \
                        ("submitted" if applying else "edited")
        send_email_message(template_name,
                           from_=request.user.email,
                           to=[email_address()],
                           context=context)

        # Also display a message to them
        messages.add_message(request, messages.INFO,
                             _(u"Application submitted"))

        return redirect("dashboard")

    return render(request, "finaid/edit.html", {
        "form": form,
        "applying": applying,
    })

@login_required
def finaid_review(request, pks=None):
    """Starting view for reviewers - list the applications"""
    # On a POST the pks are in the form.
    # On a GET there might be pks in the URL.

    if not is_reviewer(request.user):
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    if request.method == 'POST':
        # They want to do something to bulk applicants
        # Find the checkboxes they checked
        regex = re.compile(r'^finaid_application_(.*)$')
        pk_list = []
        for field_name in request.POST:
            m = regex.match(field_name)
            if m:
                pk_list.append(m.group(1))
        if not pk_list:
            messages.add_message(
                request, messages.ERROR,
                _(u"Please select at least one application"))
            return redirect(request.path)

        if 'email_action' in request.POST:
            # They want to email applicants
            pks = ",".join(pk_list)
            return redirect('finaid_email', pks=pks)
        elif 'message_action' in request.POST:
            # They want to attach a message to applications
            pks = ",".join(pk_list)
            return redirect('finaid_message', pks=pks)
        elif 'status_action' in request.POST:
            # They want to change applications' statuses
            applications = FinancialAidApplication.objects.filter(pk__in=pk_list)\
                .select_related('review')
            status = int(request.POST['status'])
            count = 0
            for application in applications:
                try:
                    review = application.review
                except FinancialAidReviewData.DoesNotExist:
                    review = FinancialAidReviewData(application=application)
                if review.status != status:
                    review.status = status
                    review.save()
                    count += 1
            messages.info(request,
                          "Updated %d application status%s" % (count, "" if count == 1 else "es"))
            pks = ",".join(pk_list)
            return redirect(reverse('finaid_review', kwargs=dict(pks=pks)))
        else:
            messages.error(request, "WHAT?")
    else:
        # GET - pks are in the URL.  maybe.
        pk_list = pks.split(",") if pks else []

    return render(request, "finaid/application_list.html", {
        "applications": FinancialAidApplication.objects.all().select_related('review'),
        "status_options": STATUS_CHOICES,
        "pks": [int(pk) for pk in pk_list],
    })

@login_required
def finaid_message(request, pks):
    """Add a message to some applications"""
    if not is_reviewer(request.user):
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    applications = FinancialAidApplication.objects.filter(pk__in=pks.split(","))\
        .select_related('user')
    if not applications.exists():
        messages.add_message(request, messages.ERROR, _(u"No applications selected"))
        return redirect('finaid_review')

    if request.method == 'POST':
        for application in applications:
            message = FinancialAidMessage(user=request.user,
                                          application=application)
            message_form = ReviewerMessageForm(request.POST, instance=message)
            if message_form.is_valid():
                message = message_form.save()
                # Send notice to reviewers/pycon-aid alias, and the applicant if visible
                context = email_context(request, application, message)
                send_email_message("reviewer/message",
                                   # From whoever is logged in clicking the buttons
                                   from_=request.user.email,
                                   to=[email_address()],
                                   context=context,
                                   headers={'Reply-To': email_address()}
                                   )
                # If visible to applicant, notify them as well
                if message.visible:
                    send_email_message("applicant/message",
                                       from_=request.user.email,
                                       to=[application.user.email],
                                       context=context,
                                       headers={'Reply-To': email_address()}
                                       )
            messages.add_message(request, messages.INFO, _(u"Messages sent"))
        return redirect(reverse('finaid_review', kwargs=dict(pks=pks)))
    else:
        message_form = ReviewerMessageForm()

    return render(request, "finaid/reviewer_message.html", {
        'applications': applications,
        'form': message_form,
    })

@login_required
def finaid_email(request, pks):
    if not is_reviewer(request.user):
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    applications = FinancialAidApplication.objects.filter(pk__in=pks.split(","))\
        .select_related('user')
    emails = [app.user.email for app in applications]

    form = None
    if request.method == 'POST':
        form = BulkEmailForm(request.POST)
        if form.is_valid():
            subject = form.cleaned_data['subject']
            from_email = email_address()
            template_text = form.cleaned_data['template'].template
            template = Template(template_text)
            # emails will be the datatuple arg to send_mail_mail
            emails = []
            for application in applications:
                try:
                    review = application.review
                except FinancialAidReviewData.DoesNotExist:
                    review = None

                ctx = {
                    'application': application,
                    'review': review,
                }
                text = template.render(Context(ctx))
                emails.append((subject, text, from_email,
                               [application.user.email]))
            try:
                send_mass_mail(emails)
            except SMTPException:
                log.exception("ERROR sending financial aid emails")
                messages.add_message(request, messages.ERROR,
                                     _(u"There was some error sending emails, "
                                       u"not all of them might have made it"))
            else:
                messages.add_message(request, messages.INFO, _(u"Emails sent"))
            return redirect(reverse('finaid_review', kwargs=dict(pks=pks)))

    ctx = {
        'form': form or BulkEmailForm(),
        'users': [app.user for app in applications]
    }
    return render(request, "finaid/email.html", ctx)

@login_required
def finaid_review_detail(request, pk):
    """Review a particular application"""
    application = get_object_or_404(FinancialAidApplication, pk=pk)

    # Redirect a a reviewer who is attempting to access FA application detail
    # page to their edit page
    if is_reviewer(request.user) and request.user == application.user:
            return redirect("finaid_edit")

    if not is_reviewer(request.user):
        # Redirect a non reviewer to their FA edit page
        if has_application(request.user):
            return redirect("finaid_edit")
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    try:
        review_data = application.review
    except FinancialAidReviewData.DoesNotExist:
        review_data = FinancialAidReviewData(application=application)

    message_form = None
    review_form = None

    if request.method == 'POST':
        if 'message_submit' in request.POST:
            message = FinancialAidMessage(user=request.user,
                                          application=application)
            message_form = ReviewerMessageForm(request.POST, instance=message)
            if message_form.is_valid():
                message = message_form.save()
                # Send notice to the reviewers alias
                # If the message is visible, also send to the applicant
                context = email_context(request, application, message)
                # Notify reviewers
                send_email_message("reviewer/message",
                                   from_=request.user.email,
                                   to=[email_address()],
                                   context=context)
                # If visible to applicant, notify them as well
                if message.visible:
                    send_email_message("applicant/message",
                                       from_=request.user.email,
                                       to=[application.user.email],
                                       context=context)
                messages.add_message(
                    request, messages.INFO,
                    _(u"Message has been added to the application, and recipients notified by email."))
                return redirect(request.path)
        elif 'review_submit' in request.POST:
            review_form = FinancialAidReviewForm(request.POST,
                                                 instance=review_data)
            if review_form.is_valid():
                review_data = review_form.save()
                return redirect(reverse("finaid_review"))
        else:
            log.error("finaid_review_detail posted with unknown form: %r"
                      % request.POST)
            return HttpResponseForbidden("HEY WHY WAS THIS POSTED")

    # Create initial forms if needed
    message_form = message_form or ReviewerMessageForm()
    review_form = review_form or FinancialAidReviewForm(instance=review_data)

    context = {
        "application": application,
        "message_form": message_form,
        "review_form": review_form,
        "review_messages": FinancialAidMessage.objects.filter(
            application=application
        )
    }
    return render(request, "finaid/review.html", context)

@login_required
def finaid_status(request):
    """
    Show an applicant the status of their application.
    Allow them to see messages from the reviewers and to submit
    messages to them.
    """
    if not has_application(request.user):
        messages.add_message(request, messages.ERROR,
                             _(u'You have not applied for financial aid'))
        return redirect("dashboard")

    if request.method == 'POST':
        application = request.user.financial_aid
        message = FinancialAidMessage(user=request.user,
                                      application=application,
                                      visible=True)
        message_form = MessageForm(request.POST, instance=message)
        if message_form.is_valid():
            message = message_form.save()

            # Send notice to the reviewers/pycon-aid alias
            # (applicant submitted this message so no need to tell them)
            context = email_context(request, application, message)
            send_email_message("reviewer/message",
                               from_=request.user.email,
                               to=[email_address()],
                               context=context)

            return redirect(request.path)
    else:
        message_form = MessageForm()

    # Only show the applicant messages that are supposed to be visible to the
    # applicant
    visible_messages = FinancialAidMessage.objects.filter(
        application=request.user.financial_aid,
        visible=True
    )

    return render(request, "finaid/status.html", {
        'application': request.user.financial_aid,
        'visible_messages': visible_messages,
        'form': message_form,
    })

@login_required
def finaid_download_csv(request):
    # Download financial aid application data as a .CSV file

    if not is_reviewer(request.user):
        return HttpResponseForbidden(_(u"Not authorized for this page"))

    # Fields to include
    application_field_names = ['id'] + [
        f.attname for f, model in FinancialAidApplication._meta.get_fields_with_model()
        if f.attname not in ['id', 'review']
    ] + ['email', 'user']
    reviewdata_field_names = [
        f.attname for f, model in FinancialAidReviewData._meta.get_fields_with_model()
        if f.attname not in ['application', 'id', 'last_update']
    ]

    # For these fields, use the get_FIELDNAME_display() method so we get
    # the name of the choice (or other custom string) instead of the internal value
    use_display_method = [
        'cash_check',
        'last_update',
        'presenting',
        'status',
        'travel_cash_check',
    ]

    def get_value(name, object):
        # Get a value from an application or review, using get_NAME_display
        # if appropriate, then forcing to a unicode string and encoding in
        # UTF-8 for CSV
        if name in use_display_method:
            display_method = getattr(object, "get_%s_display" % name)
            value = display_method()
        elif name == 'email':
            value = object.user.email
        else:
            value = getattr(object, name)
        return unicode(value).encode('utf-8')

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

    writer = csv.DictWriter(
        response,
        fieldnames=application_field_names+reviewdata_field_names
    )
    writer.writeheader()

    default_review_data = FinancialAidReviewData()
    apps = FinancialAidApplication.objects.all().select_related('review')
    for application in apps.order_by('pk'):
        # They won't all have review data, so use the default values if they don't
        try:
            review = application.review
        except FinancialAidReviewData.DoesNotExist:
            review = default_review_data

        # Write the data for this application.
        data = {}
        for name in application_field_names:
            data[name] = get_value(name, application)
        for name in reviewdata_field_names:
            data[name] = get_value(name, review)
        writer.writerow(data)

    return response

@login_required
def receipt_form(request):
    receipts = Receipt.objects.filter(user__username=request.user.username)

    if request.method == 'POST':
        form = ReceiptForm(request.POST, request.FILES)

        if form.is_valid():
            form.instance.user = request.user
            form.instance.application = request.user.financial_aid
            form.save()

            # Display a message to user
            messages.add_message(request, messages.INFO,
                                _(u"Receipt submitted"))

    else:
        form = ReceiptForm()
    return render(request, "finaid/receipt_form.html", {
        'form': form,
        'receipts': receipts
    })

@login_required
def receipt_delete(request, pk):
    receipt = get_object_or_404(Receipt, pk=pk).delete()
    return redirect("receipt_form")
<|endoftext|>


<commit_before>"""Collections
"""

from __future__ import absolute_import

import random

from .utils import make_callback, iter_callback, iter_

def at(collection, *indexes):  # pylint: disable=invalid-name
    """Creates an array of elements from the specified indexes, or keys, of the
    collection. Indexes may be specified as individual arguments or as arrays
    of indexes.

    :param iterable collection: the collection to iterate over
    :param mixed indexes: the indexes of `collection` to retrieve, specified as
                          individual indexes or arrays of indexes
    """
    # FIXME: Resolve circular imports
    from .arrays import flatten

    indexes = flatten(indexes)
    return [collection[i] for i in indexes]

def contains(collection, target, from_index=0):
    """Checks if a given value is present in a collection using strict equality
    for comparisons, i.e. ===. If `from_index` is negative, it is used as the
    offset from the end of the collection.
    """
    if isinstance(collection, dict):
        collection = collection.values()
    else:
        # only makes sense to do this if `collection` is not a dict
        collection = collection[from_index:]

    return target in collection

include = contains

def count_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of `collection` through the callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        key = cbk(value)

        ret.setdefault(key, 0)
        ret[key] += 1

    return ret

def every(collection, callback=None):
    """Checks if the callback returns a truthy value for all elements of a
    collection. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: boolean
    """

    if callback:
        cbk = make_callback(callback)
        collection = [cbk(item) for item in collection]

    return all(collection)

all_ = every

def filter_(collection, callback=None):
    """Iterates over elements of a collection, returning an list of all
    elements the callback returns truthy for.
    """
    if callback is None:
        callback = lambda item, *args: item

    return [value
            for is_true, value, _, _ in iter_callback(collection, callback)
            if is_true]

select = filter_

def find(collection, callback=None):
    """Iterates over elements of a collection, returning the first element that
    the callback returns truthy for.
    """
    found = None
    for is_true, _, key, _ in iter_callback(collection, callback):
        if is_true:
            found = collection[key]
            # only return first found item
            break

    return found

detect = find
find_where = find

def find_last(collection, callback=None):
    """This method is like :func:`find` except that it iterates over elements
    of a `collection` from right to left.
    """
    return find(list(reversed(collection)), callback)

def for_each(collection, callback):
    """Iterates over elements of a collection, executing the callback for each
    element.
    """
    for ret, _, _, _ in _iter_callback(collection, callback):
        if ret is False:
            break

    return collection

each = for_each

def for_each_right(collection, callback):
    """This method is like :func:`for_each` except that it iterates over
    elements of a `collection` from right to left.
    """
    if isinstance(collection, dict):
        iterator = collection  # Dicts have no order, nothing to be done.
    else:
        iterator = reversed(collection)

    for_each(iterator, callback)

    return collection

each_right = for_each_right

def group_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of a `collection` through the callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        key = cbk(value)
        ret.setdefault(key, [])
        ret[key].append(value)

    return ret

def index_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of the collection through the given callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        ret[cbk(value)] = value

    return ret

def invoke(collection, method_name, *args):
    """Invokes the method named by `method_name` on each element in the
    `collection` returning a list of the results of each invoked method.
    """
    lst = []

    for item in collection:
        if callable(method_name):
            result = method_name(item, *args)
        else:
            result = getattr(item, method_name)(*args)

        if result is None:
            lst.append(item)
        else:
            lst.append(result)

    return lst

def map_(collection, callback=None):
    """Creates an array of values by running each element in the collection
    through the callback. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: list
    """
    if not callback:
        callback = lambda value, *args: value

    return [result[0] for result in iter_callback(collection, callback)]

collect = map_

def max_(collection, callback=None):
    """Retrieves the maximum value of a `collection`."""
    if not callback:
        if isinstance(collection, dict):
            return max(collection.values())
        else:
            return max(collection)

    return max(collection, key=make_callback(callback))

def min_(collection, callback=None):
    """Retrieves the minimum value of a `collection`."""
    if not callback:
        if isinstance(collection, dict):
            return min(collection.values())
        else:
            return min(collection)

    return min(collection, key=make_callback(callback))

def pluck(collection, key):
    """Retrieves the value of a specified property from all elements in the
    collection.

    :param collection: a list of dicts
    :param key: the key value to pluck
    :rtype: list
    """
    # TODO: Do we want to use get() and return None if missing or error out?
    return [item.get(key) for item in collection]

def reduce_(collection, callback=None, accumulator=None):
    """Reduces a collection to a value which is the accumulated result of
    running each element in the collection through the callback, where each
    successive callback execution consumes the return value of the previous
    execution.
    """
    iterable = iter_(collection)

    if accumulator is None:
        try:
            _, accumulator = next(iterable)
        except StopIteration:
            raise TypeError(
                'reduce_() of empty sequence with no initial value')

    result = accumulator

    if callback is None:
        callback = lambda item, *args: item

    for index, item in iterable:
        result = callback(result, item, index)

    return result

foldl = reduce_
inject = reduce_

def reduce_right(collection, callback=None, accumulator=None):
    """This method is like :func:`reduce_` except that it iterates over
    elements of a `collection` from right to left.
    """
    if not isinstance(collection, dict):
        collection = sorted(collection, reverse=True)
    return reduce_(collection, callback, accumulator)

foldr = reduce_right

def reject(collection, callback=None):
    """The opposite of :func:`filter_` this method returns the elements of a
    collection that the callback does **not** return truthy for.
    """
    if callback is None:
        callback = lambda item, *args: item

    return [value
            for is_true, value, _, _ in iter_callback(collection, callback)
            if not is_true]

def sample(collection, n=None):
    """Retrieves a random element or `n` random elements from a `collection`.
    """
    num = min(n or 1, len(collection))
    sampled = random.sample(collection, num)
    return sampled[0] if n is None else sampled

def shuffle(collection):
    """Creates a list of shuffled values, using a version of the Fisher-Yates
    shuffle.
    """
    # Make copy of collection since random.shuffle works on list in-place.
    collection = list(collection)

    # NOTE: random.shuffle uses Fisher-Yates.
    random.shuffle(collection)

    return collection

def size(collection):
    """Gets the size of the `collection` by returning `len(collection)` for
    iterable objects.
    """
    return len(collection)

def some(collection, callback=None):
    """Checks if the callback returns a truthy value for any element of a
    collection. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: boolean
    """

    if callback:
        cbk = make_callback(callback)
        collection = [cbk(item) for item in collection]

    return any(collection)

any_ = some

def sort_by(collection, callback):
    """Creates a list of elements, sorted in ascending order by the results of
    running each element in a `collection` through the callback.
    """
    return sorted(collection, key=make_callback(callback))

def to_list(collection):
    """Converts the collection to a list."""
    return list(collection)

def where(collection, properties):
    """Examines each element in a collection, returning an array of all
    elements that have the given properties.

    :param collection: a list of dicts
    :param properties: the dict of property values to filter by
    :rtype: list
    """
    return [item for item in collection if _where(item, properties)]

def _where(superset, subset):
    """Helper function for where()"""
    return all(item in superset.items() for item in subset.items())
<commit_msg>Fix typo<commit_after>"""Collections
"""

from __future__ import absolute_import

import random

from .utils import make_callback, iter_callback, iter_

def at(collection, *indexes):  # pylint: disable=invalid-name
    """Creates an array of elements from the specified indexes, or keys, of the
    collection. Indexes may be specified as individual arguments or as arrays
    of indexes.

    :param iterable collection: the collection to iterate over
    :param mixed indexes: the indexes of `collection` to retrieve, specified as
                          individual indexes or arrays of indexes
    """
    # FIXME: Resolve circular imports
    from .arrays import flatten

    indexes = flatten(indexes)
    return [collection[i] for i in indexes]

def contains(collection, target, from_index=0):
    """Checks if a given value is present in a collection using strict equality
    for comparisons, i.e. ===. If `from_index` is negative, it is used as the
    offset from the end of the collection.
    """
    if isinstance(collection, dict):
        collection = collection.values()
    else:
        # only makes sense to do this if `collection` is not a dict
        collection = collection[from_index:]

    return target in collection

include = contains

def count_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of `collection` through the callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        key = cbk(value)

        ret.setdefault(key, 0)
        ret[key] += 1

    return ret

def every(collection, callback=None):
    """Checks if the callback returns a truthy value for all elements of a
    collection. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: boolean
    """

    if callback:
        cbk = make_callback(callback)
        collection = [cbk(item) for item in collection]

    return all(collection)

all_ = every

def filter_(collection, callback=None):
    """Iterates over elements of a collection, returning an list of all
    elements the callback returns truthy for.
    """
    if callback is None:
        callback = lambda item, *args: item

    return [value
            for is_true, value, _, _ in iter_callback(collection, callback)
            if is_true]

select = filter_

def find(collection, callback=None):
    """Iterates over elements of a collection, returning the first element that
    the callback returns truthy for.
    """
    found = None
    for is_true, _, key, _ in iter_callback(collection, callback):
        if is_true:
            found = collection[key]
            # only return first found item
            break

    return found

detect = find
find_where = find

def find_last(collection, callback=None):
    """This method is like :func:`find` except that it iterates over elements
    of a `collection` from right to left.
    """
    return find(list(reversed(collection)), callback)

def for_each(collection, callback):
    """Iterates over elements of a collection, executing the callback for each
    element.
    """
    for ret, _, _, _ in iter_callback(collection, callback):
        if ret is False:
            break

    return collection

each = for_each

def for_each_right(collection, callback):
    """This method is like :func:`for_each` except that it iterates over
    elements of a `collection` from right to left.
    """
    if isinstance(collection, dict):
        iterator = collection  # Dicts have no order, nothing to be done.
    else:
        iterator = reversed(collection)

    for_each(iterator, callback)

    return collection

each_right = for_each_right

def group_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of a `collection` through the callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        key = cbk(value)
        ret.setdefault(key, [])
        ret[key].append(value)

    return ret

def index_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of the collection through the given callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        ret[cbk(value)] = value

    return ret

def invoke(collection, method_name, *args):
    """Invokes the method named by `method_name` on each element in the
    `collection` returning a list of the results of each invoked method.
    """
    lst = []

    for item in collection:
        if callable(method_name):
            result = method_name(item, *args)
        else:
            result = getattr(item, method_name)(*args)

        if result is None:
            lst.append(item)
        else:
            lst.append(result)

    return lst

def map_(collection, callback=None):
    """Creates an array of values by running each element in the collection
    through the callback. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: list
    """
    if not callback:
        callback = lambda value, *args: value

    return [result[0] for result in iter_callback(collection, callback)]

collect = map_

def max_(collection, callback=None):
    """Retrieves the maximum value of a `collection`."""
    if not callback:
        if isinstance(collection, dict):
            return max(collection.values())
        else:
            return max(collection)

    return max(collection, key=make_callback(callback))

def min_(collection, callback=None):
    """Retrieves the minimum value of a `collection`."""
    if not callback:
        if isinstance(collection, dict):
            return min(collection.values())
        else:
            return min(collection)

    return min(collection, key=make_callback(callback))

def pluck(collection, key):
    """Retrieves the value of a specified property from all elements in the
    collection.

    :param collection: a list of dicts
    :param key: the key value to pluck
    :rtype: list
    """
    # TODO: Do we want to use get() and return None if missing or error out?
    return [item.get(key) for item in collection]

def reduce_(collection, callback=None, accumulator=None):
    """Reduces a collection to a value which is the accumulated result of
    running each element in the collection through the callback, where each
    successive callback execution consumes the return value of the previous
    execution.
    """
    iterable = iter_(collection)

    if accumulator is None:
        try:
            _, accumulator = next(iterable)
        except StopIteration:
            raise TypeError(
                'reduce_() of empty sequence with no initial value')

    result = accumulator

    if callback is None:
        callback = lambda item, *args: item

    for index, item in iterable:
        result = callback(result, item, index)

    return result

foldl = reduce_
inject = reduce_

def reduce_right(collection, callback=None, accumulator=None):
    """This method is like :func:`reduce_` except that it iterates over
    elements of a `collection` from right to left.
    """
    if not isinstance(collection, dict):
        collection = sorted(collection, reverse=True)
    return reduce_(collection, callback, accumulator)

foldr = reduce_right

def reject(collection, callback=None):
    """The opposite of :func:`filter_` this method returns the elements of a
    collection that the callback does **not** return truthy for.
    """
    if callback is None:
        callback = lambda item, *args: item

    return [value
            for is_true, value, _, _ in iter_callback(collection, callback)
            if not is_true]

def sample(collection, n=None):
    """Retrieves a random element or `n` random elements from a `collection`.
    """
    num = min(n or 1, len(collection))
    sampled = random.sample(collection, num)
    return sampled[0] if n is None else sampled

def shuffle(collection):
    """Creates a list of shuffled values, using a version of the Fisher-Yates
    shuffle.
    """
    # Make copy of collection since random.shuffle works on list in-place.
    collection = list(collection)

    # NOTE: random.shuffle uses Fisher-Yates.
    random.shuffle(collection)

    return collection

def size(collection):
    """Gets the size of the `collection` by returning `len(collection)` for
    iterable objects.
    """
    return len(collection)

def some(collection, callback=None):
    """Checks if the callback returns a truthy value for any element of a
    collection. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: boolean
    """

    if callback:
        cbk = make_callback(callback)
        collection = [cbk(item) for item in collection]

    return any(collection)

any_ = some

def sort_by(collection, callback):
    """Creates a list of elements, sorted in ascending order by the results of
    running each element in a `collection` through the callback.
    """
    return sorted(collection, key=make_callback(callback))

def to_list(collection):
    """Converts the collection to a list."""
    return list(collection)

def where(collection, properties):
    """Examines each element in a collection, returning an array of all
    elements that have the given properties.

    :param collection: a list of dicts
    :param properties: the dict of property values to filter by
    :rtype: list
    """
    return [item for item in collection if _where(item, properties)]

def _where(superset, subset):
    """Helper function for where()"""
    return all(item in superset.items() for item in subset.items())
<|endoftext|>


<commit_before>"""Collections
"""

from __future__ import absolute_import

import random

from .utilities import matches, property_, callback as make_callback
from .utils import iter_callback, iter_

def at(collection, *indexes):  # pylint: disable=invalid-name
    """Creates an array of elements from the specified indexes, or keys, of the
    collection. Indexes may be specified as individual arguments or as arrays
    of indexes.

    :param iterable collection: the collection to iterate over
    :param mixed indexes: the indexes of `collection` to retrieve, specified as
                          individual indexes or arrays of indexes
    """
    # FIXME: Resolve circular imports
    from .arrays import flatten

    indexes = flatten(indexes)
    return [collection[i] for i in indexes]

def contains(collection, target, from_index=0):
    """Checks if a given value is present in a collection using strict equality
    for comparisons, i.e. ===. If `from_index` is negative, it is used as the
    offset from the end of the collection.
    """
    if isinstance(collection, dict):
        collection = collection.values()
    else:
        # only makes sense to do this if `collection` is not a dict
        collection = collection[from_index:]

    return target in collection

include = contains

def count_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of `collection` through the callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        key = cbk(value)

        ret.setdefault(key, 0)
        ret[key] += 1

    return ret

def every(collection, callback=None):
    """Checks if the callback returns a truthy value for all elements of a
    collection. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: boolean
    """

    if callback:
        cbk = make_callback(callback)
        collection = [cbk(item) for item in collection]

    return all(collection)

all_ = every

def filter_(collection, callback=None):
    """Iterates over elements of a collection, returning an list of all
    elements the callback returns truthy for.
    """
    if callback is None:
        callback = lambda item, *args: item

    return [value
            for is_true, value, _, _ in iter_callback(collection, callback)
            if is_true]

select = filter_

def find(collection, callback=None):
    """Iterates over elements of a collection, returning the first element that
    the callback returns truthy for.
    """
    found = None
    for is_true, _, key, _ in iter_callback(collection, callback):
        if is_true:
            found = collection[key]
            # only return first found item
            break

    return found

detect = find
find_where = find

def find_last(collection, callback=None):
    """This method is like :func:`find` except that it iterates over elements
    of a `collection` from right to left.
    """
    return find(list(reversed(collection)), callback)

def for_each(collection, callback):
    """Iterates over elements of a collection, executing the callback for each
    element.
    """
    for ret, _, _, _ in iter_callback(collection, callback):
        if ret is False:
            break

    return collection

each = for_each

def for_each_right(collection, callback):
    """This method is like :func:`for_each` except that it iterates over
    elements of a `collection` from right to left.
    """
    if isinstance(collection, dict):
        # Dicts have no order, nothing to be done.
        iterator = collection
    else:
        iterator = reversed(collection)

    for_each(iterator, callback)

    return collection

each_right = for_each_right

def group_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of a `collection` through the callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        key = cbk(value)
        ret.setdefault(key, [])
        ret[key].append(value)

    return ret

def index_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of the collection through the given callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        ret[cbk(value)] = value

    return ret

def invoke(collection, method_name, *args):
    """Invokes the method named by `method_name` on each element in the
    `collection` returning a list of the results of each invoked method.
    """
    lst = []

    for item in collection:
        if callable(method_name):
            result = method_name(item, *args)
        else:
            result = getattr(item, method_name)(*args)

        if result is None:
            lst.append(item)
        else:
            lst.append(result)

    return lst

def map_(collection, callback=None):
    """Creates an array of values by running each element in the collection
    through the callback. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: list
    """
    if not callback:
        callback = lambda value, *args: value

    return [result[0] for result in iter_callback(collection, callback)]

collect = map_

def max_(collection, callback=None):
    """Retrieves the maximum value of a `collection`."""
    if not callback:
        if isinstance(collection, dict):
            return max(collection.values())
        else:
            return max(collection)

    return max(collection, key=make_callback(callback))

def min_(collection, callback=None):
    """Retrieves the minimum value of a `collection`."""
    if not callback:
        if isinstance(collection, dict):
            return min(collection.values())
        else:
            return min(collection)

    return min(collection, key=make_callback(callback))

def pluck(collection, key):
    """Retrieves the value of a specified property from all elements in the
    collection.

    :param collection: a list of dicts
    :param key: the key value to pluck
    :rtype: list
    """
    # TODO: Do we want to use get() and return None if missing or error out?
    return map_(collection, property_(key))

def reduce_(collection, callback=None, accumulator=None):
    """Reduces a collection to a value which is the accumulated result of
    running each element in the collection through the callback, where each
    successive callback execution consumes the return value of the previous
    execution.
    """
    iterable = iter_(collection)

    if accumulator is None:
        try:
            _, accumulator = next(iterable)
        except StopIteration:
            raise TypeError(
                'reduce_() of empty sequence with no initial value')

    result = accumulator

    if callback is None:
        callback = lambda item, *args: item

    for index, item in iterable:
        result = callback(result, item, index)

    return result

foldl = reduce_
inject = reduce_

def reduce_right(collection, callback=None, accumulator=None):
    """This method is like :func:`reduce_` except that it iterates over
    elements of a `collection` from right to left.
    """
    if not isinstance(collection, dict):
        collection = sorted(collection, reverse=True)
    return reduce_(collection, callback, accumulator)

foldr = reduce_right

def reject(collection, callback=None):
    """The opposite of :func:`filter_` this method returns the elements of a
    collection that the callback does **not** return truthy for.
    """
    if callback is None:
        callback = lambda item, *args: item

    return [value
            for is_true, value, _, _ in iter_callback(collection, callback)
            if not is_true]

def sample(collection, n=None):
    """Retrieves a random element or `n` random elements from a `collection`.
    """
    num = min(n or 1, len(collection))
    sampled = random.sample(collection, num)
    return sampled[0] if n is None else sampled

def shuffle(collection):
    """Creates a list of shuffled values, using a version of the Fisher-Yates
    shuffle.
    """
    # Make copy of collection since random.shuffle works on list in-place.
    collection = list(collection)

    # NOTE: random.shuffle uses Fisher-Yates.
    random.shuffle(collection)

    return collection

def size(collection):
    """Gets the size of the `collection` by returning `len(collection)` for
    iterable objects.
    """
    return len(collection)

def some(collection, callback=None):
    """Checks if the callback returns a truthy value for any element of a
    collection. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: boolean
    """

    if callback:
        cbk = make_callback(callback)
        collection = [cbk(item) for item in collection]

    return any(collection)

any_ = some

def sort_by(collection, callback):
    """Creates a list of elements, sorted in ascending order by the results of
    running each element in a `collection` through the callback.
    """
    return sorted(collection, key=make_callback(callback))

def to_list(collection):
    """Converts the collection to a list."""
    return list(collection)

def where(collection, properties):
    """Examines each element in a collection, returning an array of all
    elements that have the given properties.

    :param collection: a list of dicts
    :param properties: the dict of property values to filter by
    :rtype: list
    """
    return filter_(collection, matches(properties))
<commit_msg>Resolve circular imports.<commit_after>"""Collections
"""

from __future__ import absolute_import

import random

from .arrays import flatten
from .utilities import matches, property_, callback as make_callback
from .utils import iter_callback, iter_

def at(collection, *indexes):  # pylint: disable=invalid-name
    """Creates an array of elements from the specified indexes, or keys, of the
    collection. Indexes may be specified as individual arguments or as arrays
    of indexes.

    :param iterable collection: the collection to iterate over
    :param mixed indexes: the indexes of `collection` to retrieve, specified as
                          individual indexes or arrays of indexes
    """
    indexes = flatten(indexes)
    return [collection[i] for i in indexes]

def contains(collection, target, from_index=0):
    """Checks if a given value is present in a collection using strict equality
    for comparisons, i.e. ===. If `from_index` is negative, it is used as the
    offset from the end of the collection.
    """
    if isinstance(collection, dict):
        collection = collection.values()
    else:
        # only makes sense to do this if `collection` is not a dict
        collection = collection[from_index:]

    return target in collection

include = contains

def count_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of `collection` through the callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        key = cbk(value)

        ret.setdefault(key, 0)
        ret[key] += 1

    return ret

def every(collection, callback=None):
    """Checks if the callback returns a truthy value for all elements of a
    collection. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: boolean
    """

    if callback:
        cbk = make_callback(callback)
        collection = [cbk(item) for item in collection]

    return all(collection)

all_ = every

def filter_(collection, callback=None):
    """Iterates over elements of a collection, returning an list of all
    elements the callback returns truthy for.
    """
    if callback is None:
        callback = lambda item, *args: item

    return [value
            for is_true, value, _, _ in iter_callback(collection, callback)
            if is_true]

select = filter_

def find(collection, callback=None):
    """Iterates over elements of a collection, returning the first element that
    the callback returns truthy for.
    """
    found = None
    for is_true, _, key, _ in iter_callback(collection, callback):
        if is_true:
            found = collection[key]
            # only return first found item
            break

    return found

detect = find
find_where = find

def find_last(collection, callback=None):
    """This method is like :func:`find` except that it iterates over elements
    of a `collection` from right to left.
    """
    return find(list(reversed(collection)), callback)

def for_each(collection, callback):
    """Iterates over elements of a collection, executing the callback for each
    element.
    """
    for ret, _, _, _ in iter_callback(collection, callback):
        if ret is False:
            break

    return collection

each = for_each

def for_each_right(collection, callback):
    """This method is like :func:`for_each` except that it iterates over
    elements of a `collection` from right to left.
    """
    if isinstance(collection, dict):
        # Dicts have no order, nothing to be done.
        iterator = collection
    else:
        iterator = reversed(collection)

    for_each(iterator, callback)

    return collection

each_right = for_each_right

def group_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of a `collection` through the callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        key = cbk(value)
        ret.setdefault(key, [])
        ret[key].append(value)

    return ret

def index_by(collection, callback):
    """Creates an object composed of keys generated from the results of running
    each element of the collection through the given callback.
    """
    ret = dict()
    cbk = make_callback(callback)

    for value in collection:
        ret[cbk(value)] = value

    return ret

def invoke(collection, method_name, *args):
    """Invokes the method named by `method_name` on each element in the
    `collection` returning a list of the results of each invoked method.
    """
    lst = []

    for item in collection:
        if callable(method_name):
            result = method_name(item, *args)
        else:
            result = getattr(item, method_name)(*args)

        if result is None:
            lst.append(item)
        else:
            lst.append(result)

    return lst

def map_(collection, callback=None):
    """Creates an array of values by running each element in the collection
    through the callback. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: list
    """
    if not callback:
        callback = lambda value, *args: value

    return [result[0] for result in iter_callback(collection, callback)]

collect = map_

def max_(collection, callback=None):
    """Retrieves the maximum value of a `collection`."""
    if not callback:
        if isinstance(collection, dict):
            return max(collection.values())
        else:
            return max(collection)

    return max(collection, key=make_callback(callback))

def min_(collection, callback=None):
    """Retrieves the minimum value of a `collection`."""
    if not callback:
        if isinstance(collection, dict):
            return min(collection.values())
        else:
            return min(collection)

    return min(collection, key=make_callback(callback))

def pluck(collection, key):
    """Retrieves the value of a specified property from all elements in the
    collection.

    :param collection: a list of dicts
    :param key: the key value to pluck
    :rtype: list
    """
    # TODO: Do we want to use get() and return None if missing or error out?
    return map_(collection, property_(key))

def reduce_(collection, callback=None, accumulator=None):
    """Reduces a collection to a value which is the accumulated result of
    running each element in the collection through the callback, where each
    successive callback execution consumes the return value of the previous
    execution.
    """
    iterable = iter_(collection)

    if accumulator is None:
        try:
            _, accumulator = next(iterable)
        except StopIteration:
            raise TypeError(
                'reduce_() of empty sequence with no initial value')

    result = accumulator

    if callback is None:
        callback = lambda item, *args: item

    for index, item in iterable:
        result = callback(result, item, index)

    return result

foldl = reduce_
inject = reduce_

def reduce_right(collection, callback=None, accumulator=None):
    """This method is like :func:`reduce_` except that it iterates over
    elements of a `collection` from right to left.
    """
    if not isinstance(collection, dict):
        collection = sorted(collection, reverse=True)
    return reduce_(collection, callback, accumulator)

foldr = reduce_right

def reject(collection, callback=None):
    """The opposite of :func:`filter_` this method returns the elements of a
    collection that the callback does **not** return truthy for.
    """
    if callback is None:
        callback = lambda item, *args: item

    return [value
            for is_true, value, _, _ in iter_callback(collection, callback)
            if not is_true]

def sample(collection, n=None):
    """Retrieves a random element or `n` random elements from a `collection`.
    """
    num = min(n or 1, len(collection))
    sampled = random.sample(collection, num)
    return sampled[0] if n is None else sampled

def shuffle(collection):
    """Creates a list of shuffled values, using a version of the Fisher-Yates
    shuffle.
    """
    # Make copy of collection since random.shuffle works on list in-place.
    collection = list(collection)

    # NOTE: random.shuffle uses Fisher-Yates.
    random.shuffle(collection)

    return collection

def size(collection):
    """Gets the size of the `collection` by returning `len(collection)` for
    iterable objects.
    """
    return len(collection)

def some(collection, callback=None):
    """Checks if the callback returns a truthy value for any element of a
    collection. The callback is invoked with three arguments:
    (value, index|key, collection). If a property name is passed for callback,
    the created "_.pluck" style callback will return the property value of the
    given element. If an object is passed for callback, the created "_.where"
    style callback will return true for elements that have the properties of
    the given object, else false.

    :param iterable collection: the collection to iterate over
    :param mixed callback: function called per iteration
    :rtype: boolean
    """

    if callback:
        cbk = make_callback(callback)
        collection = [cbk(item) for item in collection]

    return any(collection)

any_ = some

def sort_by(collection, callback):
    """Creates a list of elements, sorted in ascending order by the results of
    running each element in a `collection` through the callback.
    """
    return sorted(collection, key=make_callback(callback))

def to_list(collection):
    """Converts the collection to a list."""
    return list(collection)

def where(collection, properties):
    """Examines each element in a collection, returning an array of all
    elements that have the given properties.

    :param collection: a list of dicts
    :param properties: the dict of property values to filter by
    :rtype: list
    """
    return filter_(collection, matches(properties))
<|endoftext|>


<commit_before>#!python

import os
import shutil
import subprocess
import sys

buildDir = "../csl";

pages = [
    {
        "page" : "visualEditor",
        "jsFiles" : [
            "src/citationEngine.js",
            "src/exampleData.js",
            "src/diff.js",
            "src/debug.js",
            "src/cslParser.js",
            "src/Iterator.js",
            "src/cslNode.js",
            "src/cslData.js",
            "src/schema.js",

            "src/notificationBar.js",
            "src/editReferences.js",
            "src/NodePathView.js",
            "src/MultiComboBox.js",
            "src/propertyPanel.js",
            "src/sortPropertyPanel.js",
            "src/infoPropertyPanel.js",
            "src/editNodeButton.js",
            "src/smartTree.js",
            "src/Titlebar.js",
            "src/ViewController.js",
            "src/controller.js",
            "src/visualEditor.js"
        ]
    },
    {
        "page" : "codeEditor",
        "jsFiles" : [
            "src/debug.js",
            "src/citationEngine.js",
            "src/exampleData.js",
            "src/diff.js",
            "src/cslParser.js",
            "src/cslData.js",
            "src/codeEditor.js"
        ]
    },
    {
        "page" : "searchByExample",
        "jsFiles" : [
            "src/xmlUtility.js",
            "src/citationEngine.js",
            "server/config.js",
            "generated/exampleCitationsEnc.js",

            "src/debug.js",
            "src/diff.js",
            "src/cslParser.js",
            "src/cslData.js",
            "src/searchResults.js",
            "src/searchByExample.js",
            "src/analytics.js"
        ]
    },
    {
        "page" : "searchByName",
        "jsFiles" : [
            "src/debug.js",
            "generated/exampleCitationsEnc.js",
            "src/cslParser.js",
            "src/cslData.js",
            "src/searchResults.js",
            "src/searchByName.js"
        ]
    },
    {
        "page" : "test",
        "jsFiles" : [
            "src/debug.js",
            "src/schema.js",
            "src/test_schema.js",
            "src/controller.js",
            "src/test_controller.js",
            "src/cslParser.js",
            "src/test_cslParser.js",
            "src/Iterator.js",
            "src/test_Iterator.js",
            "src/cslNode.js",
            "src/test_cslNode.js",
            "src/cslData.js",
            "src/test_cslData.js",
            "src/cslTreeView.js",
            "src/test_cslTreeView.js",
            "src/xmlUtility.js",
            "src/test_xmlUtility.js",
            "src/smartTree.js",
            "src/test_smartTree.js",
            "src/editNodeButton.js",
            "src/test_editNodeButton.js"
        ]
    },
    {
        "page" : "home",
        "jsFiles" : []
    },
    {
        "page" : "about",
        "jsFiles" : []
    }
]

directoriesToCopy = [
    'html',
    'src',
    'content',
    'server',
    'external',
    'generated'
]

filesToCopy = [
    'index.php',
    'getFromOtherWebsite.php',
    '.htaccess',
    '404page.html'
]

def ignored_files(adir, filenames):
    return [filename for filename in filenames if filename == '.git']

# clean build dir
if os.path.exists(buildDir):
    shutil.rmtree(buildDir)

os.makedirs(buildDir)
os.makedirs(buildDir + '/css')

gitCommit = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE).communicate()[0].replace('\n','')
print 'building from commit ', gitCommit

# find all css files
cssFiles = []
for filename in os.listdir('css'):
    if filename.endswith('.css'):
        filebase = filename.replace('.css','')
        cssFiles.append(filebase)
        shutil.copyfile('css/' + filename, buildDir + '/css/' + filebase + '-' + gitCommit + '.css')

for page in pages:
    phpFilepath = page['page'] + '/index.php'
    outputPath = buildDir + '/' + page['page']

    combinedFilename = 'CSLEDIT.' + page['page'] + '-' + gitCommit + '.js'

    if not os.path.exists(outputPath):
        os.makedirs(outputPath)

    # create concatenated file
    combinedFile = open(outputPath + '/' + combinedFilename, 'w+')
    combinedFile.write('"use strict";')

    for jsFile in page['jsFiles']:
        containsUseStrict = False

        for line in open(jsFile):
            if line.find('"use strict";') != -1:
                containsUseStrict = True
            else:
                combinedFile.write(line)

        if not containsUseStrict:
           print 'ERROR: no "use strict"; in ', jsFile
           sys.exit(1)

    inFile = open(phpFilepath)
    outFile = open(outputPath + '/index.php', 'w+')

    # strip existing references to the javascript source files and
    # in place of the last one write the concatenated file
    foundLines = []
    for line in inFile:
        useLine = True
        containsUseStrict = {}

        for cssFile in cssFiles:
            line = line.replace('"../css/' + cssFile + '.css"', '"../css/' + cssFile + '-' + gitCommit + '.css"')

        for jsFile in page['jsFiles']:
            beforeText = '\t<script type="text/javascript" src="'
            afterText = '"></script>'

            if line.find(beforeText + '../' + jsFile + afterText) != -1:
                useLine = False
                foundLines.append(jsFile)
                if len(foundLines) == len(page['jsFiles']):
                    outFile.write(beforeText + combinedFilename + afterText)

        if useLine:
            outFile.write(line)

    if set(foundLines) != set(page['jsFiles']):
        print "ERROR: didn't find all source file references in ", phpFilepath
        print "missing references = ", set(page['jsFiles']) - set(foundLines)
        sys.exit(1)

    inFile.close()
    outFile.close()

# copy other resources:
for dir in directoriesToCopy:
    shutil.copytree(dir, buildDir + '/' + dir, ignore=ignored_files)

for file in filesToCopy:
    shutil.copyfile(file, buildDir + '/' + file)

<commit_msg>remove unused file from deploy script<commit_after>#!python

import os
import shutil
import subprocess
import sys

buildDir = "../csl";

pages = [
    {
        "page" : "visualEditor",
        "jsFiles" : [
            "src/citationEngine.js",
            "src/exampleData.js",
            "src/diff.js",
            "src/debug.js",
            "src/cslParser.js",
            "src/Iterator.js",
            "src/cslNode.js",
            "src/cslData.js",
            "src/schema.js",

            "src/notificationBar.js",
            "src/editReferences.js",
            "src/NodePathView.js",
            "src/MultiComboBox.js",
            "src/propertyPanel.js",
            "src/sortPropertyPanel.js",
            "src/infoPropertyPanel.js",
            "src/editNodeButton.js",
            "src/smartTree.js",
            "src/Titlebar.js",
            "src/ViewController.js",
            "src/controller.js",
            "src/visualEditor.js"
        ]
    },
    {
        "page" : "codeEditor",
        "jsFiles" : [
            "src/debug.js",
            "src/citationEngine.js",
            "src/exampleData.js",
            "src/diff.js",
            "src/cslParser.js",
            "src/cslData.js",
            "src/codeEditor.js"
        ]
    },
    {
        "page" : "searchByExample",
        "jsFiles" : [
            "src/xmlUtility.js",
            "src/citationEngine.js",
            "server/config.js",
            "generated/exampleCitationsEnc.js",

            "src/debug.js",
            "src/diff.js",
            "src/cslParser.js",
            "src/cslData.js",
            "src/searchResults.js",
            "src/searchByExample.js",
            "src/analytics.js"
        ]
    },
    {
        "page" : "searchByName",
        "jsFiles" : [
            "src/debug.js",
            "generated/exampleCitationsEnc.js",
            "src/cslParser.js",
            "src/cslData.js",
            "src/searchResults.js",
            "src/searchByName.js"
        ]
    },
    {
        "page" : "test",
        "jsFiles" : [
            "src/debug.js",
            "src/schema.js",
            "src/test_schema.js",
            "src/controller.js",
            "src/test_controller.js",
            "src/cslParser.js",
            "src/test_cslParser.js",
            "src/Iterator.js",
            "src/test_Iterator.js",
            "src/cslNode.js",
            "src/test_cslNode.js",
            "src/cslData.js",
            "src/test_cslData.js",
            "src/xmlUtility.js",
            "src/test_xmlUtility.js",
            "src/smartTree.js",
            "src/test_smartTree.js",
            "src/editNodeButton.js",
            "src/test_editNodeButton.js"
        ]
    },
    {
        "page" : "home",
        "jsFiles" : []
    },
    {
        "page" : "about",
        "jsFiles" : []
    }
]

directoriesToCopy = [
    'html',
    'src',
    'content',
    'server',
    'external',
    'generated'
]

filesToCopy = [
    'index.php',
    'getFromOtherWebsite.php',
    '.htaccess',
    '404page.html'
]

def ignored_files(adir, filenames):
    return [filename for filename in filenames if filename == '.git']

# clean build dir
if os.path.exists(buildDir):
    shutil.rmtree(buildDir)

os.makedirs(buildDir)
os.makedirs(buildDir + '/css')

gitCommit = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE).communicate()[0].replace('\n','')
print 'building from commit ', gitCommit

# find all css files
cssFiles = []
for filename in os.listdir('css'):
    if filename.endswith('.css'):
        filebase = filename.replace('.css','')
        cssFiles.append(filebase)
        shutil.copyfile('css/' + filename, buildDir + '/css/' + filebase + '-' + gitCommit + '.css')

for page in pages:
    phpFilepath = page['page'] + '/index.php'
    outputPath = buildDir + '/' + page['page']

    combinedFilename = 'CSLEDIT.' + page['page'] + '-' + gitCommit + '.js'

    if not os.path.exists(outputPath):
        os.makedirs(outputPath)

    # create concatenated file
    combinedFile = open(outputPath + '/' + combinedFilename, 'w+')
    combinedFile.write('"use strict";')

    for jsFile in page['jsFiles']:
        containsUseStrict = False

        for line in open(jsFile):
            if line.find('"use strict";') != -1:
                containsUseStrict = True
            else:
                combinedFile.write(line)

        if not containsUseStrict:
           print 'ERROR: no "use strict"; in ', jsFile
           sys.exit(1)

    inFile = open(phpFilepath)
    outFile = open(outputPath + '/index.php', 'w+')

    # strip existing references to the javascript source files and
    # in place of the last one write the concatenated file
    foundLines = []
    for line in inFile:
        useLine = True
        containsUseStrict = {}

        for cssFile in cssFiles:
            line = line.replace('"../css/' + cssFile + '.css"', '"../css/' + cssFile + '-' + gitCommit + '.css"')

        for jsFile in page['jsFiles']:
            beforeText = '\t<script type="text/javascript" src="'
            afterText = '"></script>'

            if line.find(beforeText + '../' + jsFile + afterText) != -1:
                useLine = False
                foundLines.append(jsFile)
                if len(foundLines) == len(page['jsFiles']):
                    outFile.write(beforeText + combinedFilename + afterText)

        if useLine:
            outFile.write(line)

    if set(foundLines) != set(page['jsFiles']):
        print "ERROR: didn't find all source file references in ", phpFilepath
        print "missing references = ", set(page['jsFiles']) - set(foundLines)
        sys.exit(1)

    inFile.close()
    outFile.close()

# copy other resources:
for dir in directoriesToCopy:
    shutil.copytree(dir, buildDir + '/' + dir, ignore=ignored_files)

for file in filesToCopy:
    shutil.copyfile(file, buildDir + '/' + file)

<|endoftext|>