"""Attendee Registration Views

A registration is processed as follows:

1. start at the register view.
  1.1. if a user has a registration or invoices, warn about that
       (but only once, and force 'continue') [Hard to do! seperate view?]
  1.2. if a 'add reg' POST, then add another person to be registered to the
       form list.
  1.3. if 'remove reg' then remove that particular form from the form list.
  1.4. if 'proceed to checkout' then
    1.4.1. create Invoice and Registration objects.
    1.4.2. mark Invoice 'CO' or allow checkout.
    1.4.3. redirect to 'checkout' view.
2. in checkout view
  2.1. if Invoice is not marked 'CO' error appropriatly.
       if it is set to 'WA' then note that if there was a problem they should
       contact PyCon staff if something went wrong.
  2.2. mark Invoice 'WA' waiting for paypal ack.
  2.3. display invoice and verisign form of hidden inputs w/ submit button
       Submit goes to a verisign URL where the user inputs the data.
       NOTE: admin can later set invoice back to 'CO' to reprocess if user
             reports something went wrong and wants to try again.
  2.4. have a checkout button for check processing
       This will post to the check_preprocess view
3. in paypal_ack view (Only PayPal should use this view)
  3.1. if not a POST, then error
  3.2. if no RESULT then error
  3.3. Create a new PayPalAck, record the IP and POST fields.
  3.4. if debug, then keep a record of all post data.
  3.5. if no INVOICE then assume it's a donation and return OK.
  3.4. Mark Invoice 'PA' paid via ack.
  3.5. Mark Registrations paid and active.
  3.6. Send e-mail to invoice owner mentioning that the payment was
       acknowledged and registrations are paid and active.
  3.7. Send e-mail to non-invoice owner registrations noting
       that the registrations are now paid and active.
       Include 'connect' links for those registrations still not connected
       to a user. Note that they can change their registration if they so
       wish.
4. in thankyou_redirect
    we get to this view when the person clicks 'Return To Site' on the paypal
    page at the end of the processing. If the user is logged in, and they
    have Invoice's, then we redirect to the invoice listing. If not, we
    redirect to the old PSF Donation ThankYou page.
5. in check_preprocess
    We only get here when someone clicks the 'pay by check' on the checkout
    view. Only 'waiting for paypal ack' status is allowed.
  5.1. change status to 'waiting for manual payment'
  5.2. mark registrations as active (but not paid)
  5.3. send out e-mails (noting status)
  5.4. redirect to check_checkout (clear post)
6. check_checkout
  only for 'waiting for manual checkout' mode
  printable page with invoice. & instructions.
  have a 'done' link or submit bar to go to invoice listing.
  could get here from Invoice for re-print.

### other connected views (that's a pun)
7. in connect view (connect user to reg)
  7.1. if the connect id does not match or if the reg has a user which is not
       the person in wuestion, then error.
  7.2. if everything looks good, then show a form for comfirmation
  7.3. if we see a connect post from the form connect the registration
       and redirect to the invoice listing
  7.4. cancel goes back to the listing.
8. invoices (list all invoices and registrations for a user)
9. invoice (view invoice w/ links to edit, request change)
10. view_reg (view a registration w/ links to edit, request change)


### make changes (Only non-cost fileds are editable, request change otherwise)
11. in change_request
  11.1. if not a valid invoice or reg, or user not assoc, error
  11.2. Create a new Request, and send e-meil to list, reg head, requestor,
       and if a reg request (vs. invoice), if not the invoice owner, then
       send to the invoice owner as well.
       At this time all followup will be CC's to the list, and mnaged via
       admin interface. May require refund or additional purchase.

12. in edit
  12.1. If canceled, give message that edits are not allowed.
  12.2. Even if not paid or inactive, allow edits of non-pay fields.

### Organizer Views

### Treasurer/RegMan Views


"""
from django.template import loader, RequestContext
from django.db.models import Q
from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.http import HttpResponseNotFound, HttpResponseNotAllowed
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth.decorators import user_passes_test
from django.contrib.sites.models import Site
from django.core.cache import cache
from django.views.generic.list_detail import object_list
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
from templatetags.attendeereg import can_view_invoice, can_view_reg
from pycon.features.models import Feature
from pycon.features.decorators import feature_required, login_required
from pycon.usermgr.newforms import AdminCreateUserForm
from decimal import Decimal
from models import *
from forms import *
from itertools import chain
import csv

@login_required
@feature_required("RegistrationAdmin")
def on_site_reg(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    reg_search=RegSearch()
    regs = None
    user_form = UserForm()
    reg_form = NewRegEntryForm(early_reg=False, prefix='x')

    donation=DonationForm(request.user, data=request.POST )

    if request.method == 'POST':
        if "search" in request.POST:
            reg_search = RegSearch(request.POST)
            if reg_search.is_valid():
                cd = reg_search.cleaned_data
                query=cd['query']

                # search accross these fields: last, first, nic, username
                regs=Registration.objects.\
                    filter(\
                        Q(id=query)|\
                        Q(badge_name__icontains=query)|\
                        Q(name__icontains=query)|\
                        Q(badge_text1__icontains=query)|\
                        Q(badge_text2__icontains=query))\
                        .order_by('name')
        else:
            if "newreg" in request.POST:
                user_form = UserForm(request.POST)
                reg_form  = NewRegEntryForm(False, None, False, request.POST, prefix='x' )

                if reg_form.is_valid() \
                        and user_form.is_valid():
                    user_data = user_form.cleaned_data
                    reg_data = reg_form.cleaned_data

                    ## create the reg

                    # create a uniquie username
                    i=0
                    user_data['username']=user_data['first_name']
                    while User.objects.filter( \
                            username=user_data['username']).count():
                        i+=1
                        user_data['username']= \
                            '%s%s' % (user_data['first_name'], i)

                    # create user account
                    user = User.objects.create_user( user_data['username'],
                                reg_data['email']  )
                    user.set_unusable_password()
                    user.first_name=user_data['first_name']
                    user.last_name=user_data['last_name']
                    user.save()

                    total=reg_form.subtotal()
                    invoice = create_invoice(user, total, donation, [reg_form])
                    if invoice:
                        ## redirect to checkout!
                        return HttpResponseRedirect(
                            reverse('reg-checkout',None,(),
                                    {'invid':unicode(invoice)}))

    context = {}
    context['title']='On-Site Registration'
    context['reg_search']=reg_search
    context['regs']=regs
    context['user_form']=user_form
    # context['invoice_form']=invoice_form
    context['reg_form']=reg_form

    return render_to_response('attendeereg/on_site_reg.html', context,
                                context_instance=RequestContext(request))


def create_invoice(user, total, donation, regforms):
    is_valid = reduce(lambda x, y: x and y.is_valid(),
                      regforms, donation.is_valid())
    if not is_valid: return None
    invoice = Invoice()
    invoice.user   = user
    invoice.status = 'CO' ## allow checkout
    invoice.donation = donation.cleaned_data['donation']
    invoice.donation_name = donation.cleaned_data['donation_name']
    invoice.total_cost = total
    invoice.save()
    registrations = [regform.save(invoice) for regform in regforms]
    invoice.regenerate = True
    invoice.save()
    ## we now send messages only on ACK
    return invoice

def send_invoice_emails(invoice, regs=None, include_status=True,
                        check=False, refund=False, regen_invoice=True,
                        comment=u''):
    """helper function to send the invoice and registration notice
    emails.

    regs - an optimization as one of the callers
           already has this query done.

    include_status - Include the payment status in the invoice.

    This will send the invoice to the user assocuated with that invoice.
    Any regisrtarions on the invoice which are not for the invoice user
    are sent a registration e-mail letting them know about their registeration,
    including the connect url and other information.

    The invoice is CC'd to the psf-donations list. The registration e-mails
    are not CC'd, and have a reply-to of pycon@python.org.
    """
    if regs is None:
        ## due to null relation we do not do a filter
        regs = invoice.registrations.all()
    ## filter out reg for invoice user
    def hack(reg):
        if reg.user == invoice.user:
            hack.myreg = reg
            return False
        return True
    hack.myreg=None
    alt_regs = [ reg for reg in regs if hack(reg) ]
    bInvoiceUserHasReg = hack.myreg is not None

    site = Site.objects.get_current()
    context = {'site': site,
               'live': regen_invoice,
               'settings': settings,
               'invoice': invoice,
               'bReg': bInvoiceUserHasReg,
               'registration': hack.myreg,
               'bCheck': check,
               'comment': comment,
               'include_status': include_status}
    t = loader.get_template("attendeereg/invoice.txt")
    body = t.render(Context(context))
    ## Invoice e-mail
    subject = site.name+u' Registration Invoice '+unicode(invoice)
    if check: subject += u' (Payment/Change Pending)'
    if refund: subject += u' (Cancelled or Refunded)'
    emails = [ {'from_email': settings.ATTREG_INVOICE_FROM_EMAIL,
                'replyto_email': getattr(settings,
                                         'ATTREG_INVOICE_REPLYTO_EMAIL',
                                         None),
                'subject': subject,
                'body': body,
                'to': [invoice.user.email,]+getattr(settings,
                                                'ATTREG_INVOICE_CC', list()),
                },]

    ## non-invoice registrations
    t = loader.get_template("attendeereg/registration.txt")
    for reg in alt_regs:
        context['registration'] = reg
        emails.append({
            'to': [reg.email,] + getattr(settings, 'ATTREG_REG_CC', list()),
            'from_email': settings.ATTREG_REG_FROM_EMAIL,
            'replyto_email': getattr(settings,
                                     'ATTREG_REG_REPLYTO_EMAIL', None),
            'subject': site.name + u' Registration',
            'body': t.render(Context(context)),
        })

    from pycon.core.mail import send_mass_mail
    return send_mass_mail(emails)

# Top Registration Form
@login_required
@feature_required("Register")
def register(request):
    """Initial registration view. We are going to cheat a bit here.
    We will allow for having multiple registrations on the same page.
    The user must click 'add' which will call this view again, and have
    anouther registration form added to the page. They can also click
    'remove' to remove a specific registration. This is all done via post.
    The add and remove 'links' are form post buttons (or should be).

    Then when they select the checkout button, that comes here too.
    That is where the Invoice and Registration objects are actually
    created. Then we return a redirect to the real checkout fort the invoice.
    One issue is what the %$^& you do if someone clicks checkout
    and then wants to update things. Once a person has gone to checkout,
    they have an invoice. They may never go to paypal, and it will sit in
    limbo going forward. Not much we can do about that.
    We might want to have a 'cancel' link on that page, but all it would do
    is mark the invoice 'canceled' or something. We still want it incase
    we DO get a paypal ack for it.

    We could hook in some javascript on the paypal post, or allow for going
    to checkout for an existing invoice to try to re-process. I would
    like to do that, but only if time permits. And it would break things for
    people w/o javascript (risky).

    """
    early  = Feature.objects.get(name='EarlyBirdReg').is_enabled
    badge  = Feature.objects.get(name='RegBadgePreview').is_enabled
    count  = 1
    post   = None
    delreg = -1
    total  = Decimal("0.00")
    user = request.user
    at_door=False
    if request.user.is_superuser and 'user' in request.GET:
        print User.objects.get(pk=request.GET['user'])
        try: user=User.objects.get(pk=request.GET['user'])
        except: pass
        else: at_door=True
        print user
    if request.method == 'POST':
        post = request.POST
        count = int(post['reg_sentinal_count'], 10)
        for ent in post:
            if ent.startswith('__delreg'):
                delreg = int(ent[8:], 10)
                break;
    bHasInvoiceRegOrConnect = bool(user.invoice_set.count())
    if not bHasInvoiceRegOrConnect:
        bHasInvoiceRegOrConnect = bool(user.registration_set.count())
    if not bHasInvoiceRegOrConnect:
        bHasInvoiceRegOrConnect = bool(Registration.objects.filter(
            user__isnull=True, email__iexact=user.email).count())
    donation = DonationForm(user, data=post)
    regs = [NewRegEntryForm(early, sample_badge=badge,
                            user=user if i == 0 else None,
                            data=post, at_door=at_door,
                            prefix='reg%d'%i)
            for i in xrange(count)
                if (i != delreg and
                    (post is None or (('reg%d-sentinal'%i) in post)))]
    if donation.is_valid(): total=donation.cleaned_data['donation']
    for reg in regs: total+=reg.subtotal()
    if post is not None and delreg == -1:
        if '__addreg' in post:
            regs.append( NewRegEntryForm(early, sample_badge=badge,
                                         at_door=at_door,
                                         prefix='reg%d'%count))
            count+=1
        elif '__checkout' in post:
            ## validate and save data if it passes
            invoice = create_invoice(user, total, donation, regs)
            if invoice is not None:
                ## redirect to checkout!
                return HttpResponseRedirect(
                    reverse('reg-checkout',None,(),{'invid':unicode(invoice)}))

    context = {}
    context['have_invoice_or_reg'] = bHasInvoiceRegOrConnect
    context['title'] = _("Registration")
    context['donation_form'] = donation
    context['reg_forms'] = regs
    context['reg_sentinal_count'] = count
    context['total'] = total
    context['at_door'] = at_door
    return render_to_response('attendeereg/registration.html', context,
                              context_instance=RequestContext(request))

# Add Registration

# Checkout
@login_required
@feature_required("RegistrationCheckout")
def checkout(request, invid):
    """Here we present the user with the invoice, and a checkout submit
    button. There is a form which is all hidden values. The POST goes
    to a verisign URL, where the user will pay by CCard.
    """
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    ## does this user have permissions?
    if (request.user.id != invoice.user_id and
            not request.user.is_superuser):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Invoice'},
                              context_instance=RequestContext(request))
    ## add check on the invoice status (can we do another checkout?)
    if invoice.status != u'CO':
        return render_to_response('attendeereg/no_checkout.html',
                              {'title': 'Checkout',
                               'invoice': invoice},
                              context_instance=RequestContext(request))
    auto_aubmit = False #request.method == 'POST' and '__checkout' in request.POST
    invoice.status='WA' ## waiting for paypal ack
    invoice.save()
    form=PayPalCheckoutForm(invoice)
    return render_to_response('attendeereg/checkout.html',
                              {'title': 'Checkout',
                               'form': form,
                               'auto_submit': auto_aubmit},
                              context_instance=RequestContext(request))


# Check Preprocess
@login_required
@feature_required("RegistrationCheckout")
def check_preprocess(request, invid):
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    ## does this user have permissions?
    if (request.user.id != invoice.user_id and
        not request.user.is_superuser):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Invoice'},
                              context_instance=RequestContext(request))
    if invoice.status == u'WP':
        return HttpResponseRedirect(
            reverse('reg-check-checkout',None,(),{'invid':unicode(invoice)}))
    ## add check on the invoice status (can we do another checkout?)
    if invoice.status != u'WA':
        return render_to_response('attendeereg/no_checkout.html',
                              {'title': 'Checkout',
                               'invoice': invoice},
                              context_instance=RequestContext(request))
    invoice.status = 'WP'
    invoice.save()
    registrations = invoice.registrations.all()
    for reg in registrations:
        reg.active = True
        reg.save()
    send_invoice_emails(invoice, registrations, True, True)
    return HttpResponseRedirect(
            reverse('reg-check-checkout',None,(),{'invid':unicode(invoice)}))

# Check Checkout
@login_required
@feature_required("RegistrationCheckout")
def check_checkout(request, invid):
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    ## does this user have permissions?
    if (request.user.id != invoice.user_id and
                not request.user.is_superuser):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Invoice'},
                              context_instance=RequestContext(request))
    ## add check on the invoice status (can we do check checkout?)
    if invoice.status != u'WP':
        return render_to_response('attendeereg/no_checkout.html',
                              {'title': 'Checkout',
                               'invoice': invoice},
                              context_instance=RequestContext(request))
    return render_to_response('attendeereg/check_checkout.html',
                              {'title': 'Checkout',
                               'secure_checkout': True,
                               'invoice': invoice, },
                              context_instance=RequestContext(request))

# Thankyou
def thankyou_redirect(request):
    """helper view to redirect to either the python.org donations thankyou
    or the invoice listing page. This should be encountered when the user
    clicks 'Done' on the PayPal form. We only redirect to the pycon invoice
    listing if they are logged in, and have an invoice.
    """
    bInvRedirect = True
    if (request.user.is_anonymous() or
        request.user.invoice_set.count() == 0):
        return HttpResponseRedirect(settings.ATTREG_ALT_THANKYOU_URL)
    thankyou = getattr(settings, 'ATTREG_THANKYOU_URL', None)
    if not thankyou: thankyou = reverse('reg-invoice-listing',None,(),{})
    return HttpResponseRedirect(thankyou)

# PayPal Silent POST
def paypal_ack(request):
    if request.method != 'POST':
        return HttpResponseNotAllowed(['POST'])
    feat = Feature.objects.get(name='HandlePayPalAck')
    if feat.is_disabled: return HttpResponseForbidden()
    #if 'RESULT' not in request.POST or 'PNREF' not in request.POST:
    #    return HttpResponseForbidden()
    ack = PayPalAck()
    ack.manual_reg = False
    ack.from_ip = request.META['REMOTE_ADDR']
    for fieldname in PayPalAck.post_fields:
        if fieldname in request.POST:
            val = request.POST[fieldname]
            if fieldname == 'RESULT':
                try:
                    val = int(val, 10)
                except:
                    val = -1
            setattr(ack, fieldname, val)
    ack.recieved_ack = True
    if settings.DEBUG:
        if not ack.RESULTMSG: ack.RESULTMSG = u''
        ack.RESULTMSG += repr(request.POST)
    ack.save() ## save immediatly!!
    if 'INVOICE' in request.POST and 'RESULT' in request.POST:
        ## looks like one of ours!
        try:
            invoice = Invoice.objects.get_invoice(request.POST['INVOICE'])
            ## RED_FLAG: add code to validate the sucker!
            ack.invoice = invoice
            ack.save()
            ## mark them paid and active
            if ack.RESULT == 0:
                #if invoice.status in ['WA', 'WP']:
                #    ## only update it if we were waiting for an acc
                #    ## allow for waiting for manual payment as this is
                #    ## a state a user could put the system into
                ## always mark it paid. We got the ack. They paid.
                ## deal with the issue elsewhere
                if invoice.status != u'WA':
                    if not ack.RESULTMSG: ack.RESULTMSG= u''
                    ack.RESULTMSG+=('\n----\nRecieved ack when invoice status '
                                    'was: ' + invoice.status)
                invoice.status='PA'
                invoice.save()
                ## reguardless, mark the registrations paid.
                regs = invoice.registrations.all()
                for reg in regs:
                    reg.paid=True
                    reg.active=True
                    reg.save()
                send_invoice_emails(invoice, regs, True)
        except Invoice.DoesNotExist:
            pass

    return HttpResponse('Ok',status=200)

# Fake a PayPal Silent POST
@user_passes_test(lambda u: u.is_active and u.is_superuser)
@feature_required("FakePayPalAck")
def fake_paypal_ack(request):
    """We can not test the system without paypal being configured in test mode.
    This is not good for basic testing needs. With that in mind, this view
    will present superusers (and hopefully only superusers), the ability
    to fake a PayPal Silent POST for testing purposes.
    """
    form=FakePayPalAckForm()
    return render_to_response('attendeereg/fake_paypal_ack.html',
                              {'title': 'Fake PayPal Test Ack',
                               'form': form},
                              context_instance=RequestContext(request))

# View Reciept
@login_required
@feature_required("ViewRegInvoice")
def invoices(request):
    """List of a persons invoices and or registrations
    """
    invoices = request.user.invoice_set.all()
    registrations = request.user.registration_set.all()
    connections = Registration.objects.filter(user__isnull=True,
                                              email__iexact=request.user.email)
    bHaveReg = bool(invoices.count() + registrations.count())
    return render_to_response('attendeereg/invoices.html',
                              {'title': 'Registration Invoices',
                               'have_inv_or_reg': bHaveReg,
                               'invoices': invoices,
                               'registrations': registrations,
                               'connections': connections},
                              context_instance=RequestContext(request))


@login_required
@feature_required("ViewRegInvoice")
def invoice(request, invid):
    """View a specific invoice (with links to the reg's)
    """
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    if not can_view_invoice(request.user, invoice):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Invoice'},
                              context_instance=RequestContext(request))
    return render_to_response('attendeereg/invoice.html',
                              {'title': 'View Invoice',
                               'bTreasurer': request.user.is_superuser,
                               'invoice': invoice},
                              context_instance=RequestContext(request))

# View Registration
@login_required
@feature_required("ViewRegInvoice")
def view_reg(request, regid):
    reg = get_object_or_404(Registration, pk=regid)
    if not can_view_reg(request.user, reg):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Registration'},
                              context_instance=RequestContext(request))
    return render_to_response('attendeereg/view.html',
                              {'title': 'View Registration',
                               'include_status': True,
                               'sample_badge': Feature.objects.get(
                                    name='RegBadgePreview').is_enabled,
                               'registration': reg},
                              context_instance=RequestContext(request))
# Edit Registration
@login_required
@feature_required("EditRegistration")
def edit(request, regid):
    reg = get_object_or_404(Registration, pk=regid)
    if not can_view_reg(request.user, reg):
        return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Registration'},
                              context_instance=RequestContext(request))
    post = request.POST if request.method == 'POST' else None
    form = EditRegForm(reg, data=post)
    if form.is_valid():
        form.save()
        return HttpResponseRedirect(reverse('reg-view',None,(),
                                            {'regid': str(reg.id)}))
    return render_to_response('attendeereg/edit.html',
                              {'title': 'Edit Registration',
                               'registration': reg,
                               'form': form},
                              context_instance=RequestContext(request))


# Connect Account To Registration
@login_required
@feature_required("ConnectRegistration")
def connect(request, regid, conid):
    reg = get_object_or_404(Registration, pk=regid)
    if conid.lower() != reg.connect_id.lower():
        return render_to_response('attendeereg/connect.html',
                                  {'title': 'Connect Registration',
                                   'registration': None,
                                   'connected': False,
                                   'invalid': True},
                                  context_instance=RequestContext(request))
    if reg.user is not None:
        if reg.user_id == request.user.id:
            return HttpResponseRedirect(
                reverse('reg-invoice-listing',None,(),{}))
        return render_to_response('attendeereg/connect.html',
                                  {'title': 'Connect Registration',
                                   'registration': None,
                                   'invalid': False,
                                   'connected': True},
                                  context_instance=RequestContext(request))
    if request.method == 'POST':
        if '__connect' in request.POST:
            reg.user = request.user
            reg.save()
            return HttpResponseRedirect(reg.get_absolute_url())
        return HttpResponseRedirect(reverse('reg-invoice-listing',None,(),{}))
    return render_to_response('attendeereg/connect.html',
                              {'title': 'Connect Registration',
                               'invalid': False,
                               'connected': False,
                               'registration': reg},
                              context_instance=RequestContext(request))

# Request Registration Change
@login_required
@feature_required("ChangeRegistrationRequest")
def change_request(request, regid=None, invid=None):
    if ((regid is None and invid is None) or
        (regid is not None and invid is not None)):
        raise Http404
    if invid:
        try:
            inv, reg = Invoice.objects.get_invoice(invid), None
        except Invoice.DoesNotExist:
            raise Http404
        if not can_view_invoice(request.user, inv):
            return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Invoice'},
                              context_instance=RequestContext(request))
    elif regid:
        reg, inv = get_object_or_404(Registration, pk=regid), None
        if not can_view_reg(request.user, reg):
            return render_to_response('attendeereg/no_perm.html',
                              {'title': 'Not Your Registration'},
                              context_instance=RequestContext(request))
    post = request.POST if request.method == 'POST' else None
    form = ChangeRequestForm(request.user, inv, reg, data=post)
    if post and form.is_valid():
        form.save()
        if invid: return HttpResponseRedirect(inv.get_absolute_url())
        return HttpResponseRedirect(reg.get_absolute_url())
    return render_to_response('attendeereg/change_request.html',
                              {'title': 'Registration Change Request',
                               'form': form,
                               'invoice': inv,
                               'registration': reg},
                              context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def admin_status(request, invid, status):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    if status == 'CO' and invoice.is_waiting_for_payment:
        invoice.status='CO'
        invoice.save()
    if status == 'WR' and invoice.is_paid:
        invoice.status='WR'
        invoice.save()
    if status == 'IV' and invoice.is_waiting_for_payment:
        invoice.status='IV'
        invoice.save()
        ## red_flag: switch to Q objects and a filter
        for reg in invoice.registrations.all():
            if reg.paid or reg.active:
                reg.paid=False
                reg.active=False
                reg.save()
    return HttpResponseRedirect(reverse('reg-invoice',None,(),
                                        {'invid': unicode(invoice)}))

@login_required
@feature_required("RegistrationAdmin")
def admin_regen(request, invid):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    invoice.regenerate = True
    invoice.save()
    return HttpResponseRedirect(reverse('reg-invoice',None,(),
                                        {'invid': unicode(invoice)}))


@login_required
@feature_required("RegistrationAdmin")
def admin_paid(request, invid):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    if invoice.is_paid:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'Invoice is already Paid'},
                          context_instance=RequestContext(request))
    post = None
    paypal_post=None
    if not (request.method == 'GET' or '__view' in request.POST):
        post = request.POST
    if post and '__ack' in post: paypal_post = post
    initial = {}
    if invoice.status == 'WA': initial['mail_reg'] = True
    form = OptionalCommentForm(data=post)
    paypal_form = ManualPayPalAckForm(invoice, request.META['REMOTE_ADDR'],
                                      data=paypal_post)
    if post and '__paid' in post:
        comment, email_regs = u'', False
        if form.is_valid():
            comment = form.cleaned_data['comment']
            email_regs = form.cleaned_data['mail_reg']
        if comment:
            prefix = u"\n\n----\n" if invoice.org_notes else u''
            invoice.org_notes += prefix + comment
        invoice.status = u'PM'
        invoice.save()
        regs = invoice.registrations.all()
        for reg in regs:
            reg.paid = True
            reg.active = True
            reg.save()
        if not email_regs: regs = []
        send_invoice_emails(invoice, regs=regs, regen_invoice=False,
                            comment=comment)
        return HttpResponseRedirect(reverse('reg-invoice',None,(),
                                            {'invid': unicode(invoice)}))
    if paypal_post and paypal_form.is_valid():
        paypal_form.save()
        invoice.status = u'PA'
        invoice.save()
        regs = invoice.registrations.all()
        for reg in regs:
            reg.paid = True
            reg.active = True
            reg.save()
        send_invoice_emails(invoice, regs=regs, regen_invoice=False)
        return HttpResponseRedirect(reverse('reg-invoice',None,(),
                                            {'invid': unicode(invoice)}))
    return render_to_response('attendeereg/admin_paid.html',
                              {'title': 'Manual Invoice Payment',
                               'form': form,
                               'paypal_form': paypal_form,
                               'invoice': invoice},
                              context_instance=RequestContext(request))


@login_required
@feature_required("RegistrationAdmin")
def admin_refunded(request, invid):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    if not invoice.is_paid:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'Invoice is not yet Paid'},
                          context_instance=RequestContext(request))
    post = None
    if not (request.method == 'GET' or '__view' in request.POST):
        post = request.POST
    form = OptionalCommentForm(data=post)
    if post and '__refunded' in post:
        comment, email_regs = u'', False
        if form.is_valid():
            comment = form.cleaned_data['comment']
            email_regs = form.cleaned_data['mail_reg']
        if comment:
            prefix = u"\n\n----\n" if invoice.org_notes else u''
            invoice.org_notes += prefix + comment
        invoice.status = u'CA'
        invoice.save()
        regs = invoice.registrations.all()
        for reg in regs:
            reg.paid = False
            reg.active = False
            reg.save()
        if not email_regs: regs = []
        send_invoice_emails(invoice, regs=regs, comment=comment,
                            regen_invoice=False, refund=True)
        return HttpResponseRedirect(reverse('reg-invoice',None,(),
                                            {'invid': unicode(invoice)}))
    return render_to_response('attendeereg/admin_refunded.html',
                              {'title': 'Invoice Refunded',
                               'form': form,
                               'invoice': invoice},
                              context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def admin_invalidate(request, invid):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    try: invoice = Invoice.objects.get_invoice(invid)
    except Invoice.DoesNotExist: raise Http404
    if invoice.is_paid:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'Invoice is Paid'},
                          context_instance=RequestContext(request))
    post = None
    if not (request.method == 'GET' or '__view' in request.POST):
        post = request.POST
    form = OptionalCommentForm(data=post)
    if post and '__invalidate' in post:
        comment, email_regs = u'', False
        if form.is_valid():
            comment = form.cleaned_data['comment']
            email_regs = form.cleaned_data['mail_reg']
        if comment:
            prefix = u"\n\n----\n" if invoice.org_notes else u''
            invoice.org_notes += prefix + comment
        invoice.status = u'IV'
        invoice.save()
        regs = invoice.registrations.all()
        for reg in regs:
            reg.paid = False
            reg.active = False
            reg.save()
        if not email_regs: regs = []
        send_invoice_emails(invoice, regs=regs, comment=comment,
                            regen_invoice=False, refund=True)
        return HttpResponseRedirect(reverse('reg-invoice',None,(),
                                            {'invid': unicode(invoice)}))
    return render_to_response('attendeereg/admin_invalidated.html',
                              {'title': 'Invoice Invalid',
                               'form': form,
                               'invoice': invoice},
                              context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def admin_issues_mark_requests_handled(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    if request.method == 'POST' and request.POST:
        MarkRequestsHandled(data=request.POST).save()
    return HttpResponseRedirect(reverse('reg-admin-issues',None,(),{}))

@login_required
@feature_required("RegistrationAdmin")
def admin_issues(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    context = {'title': 'Registration Issues'}

    #* Double Billing (invoice with multiple acks)
    double = Invoice.objects.extra(where=[
        '(SELECT COUNT(*) FROM attendeereg_paypalack '
        ' WHERE attendeereg_invoice.id = attendeereg_paypalack.invoice_id) '
        '> 1'])
    context['double'] = double

    #* Multi Invoice (user with multiple non-invalidated invoices)
    multi_inv = User.objects.extra(where=[
        '(SELECT COUNT(*) FROM attendeereg_invoice '
        ' WHERE (attendeereg_invoice.user_id = auth_user.id AND '
        "        attendeereg_invoice.status != 'IV') ) "
        '> 1'])
    context['multi_inv'] = multi_inv

    #* Multi Reg (user with multiple registrations
    multi_reg = User.objects.extra(where=[
        '(SELECT COUNT(*) FROM attendeereg_registration '
        ' WHERE attendeereg_registration.user_id = auth_user.id '
        '       AND attendeereg_registration.active = TRUE) '
        '> 1'])
    context['multi_reg'] = multi_reg

    #* Change request listing
    change_requests = ChangeRequest.objects.filter(
        invoice__changerequest__handled=False).distinct().order_by(
        'invoice', 'created')
    context['change_requests'] = change_requests
    context['change_requests_count'] = ChangeRequest.objects.count()
    context['change_requests_unhandled_count'] = ChangeRequest.objects.filter(
        handled=False).count()

    #* Lost Invoices (Ack with Invoice ID, but no Invoice with that ID)
    lost_invoices = PayPalAck.objects.exclude(INVOICE__exact="").filter(
                                              invoice__isnull=True)
    context['lost_invoices'] = lost_invoices

    #* Pending refund
    pend_refund = Invoice.objects.filter(status="WR")
    context['pend_refund'] = pend_refund

    #* Pending manual payment
    pend_manual = Invoice.objects.filter(status="WP")
    context['pend_manual'] = pend_manual

    #* Pending PayPal Ack
    pend_ack = Invoice.objects.filter(status="WA")
    context['pend_ack'] = pend_ack

    #* Refunded invoices
    refunded = Invoice.objects.filter(status="CA")
    context['refunded'] = refunded

    #* Invalidated invoices
    #invalid = Invoice.objects.filter(status="IV")
    #context['invalid'] = invalid

    #* Check-Out invoices
    checkout = Invoice.objects.filter(status="CO")
    context['checkout'] = checkout

    #* Unconnected Registrations
    unconnected = Registration.objects.filter(user__isnull=True).exclude(
        invoice__status='IV')
    context['unconnected'] = unconnected

    return render_to_response('attendeereg/admin_issues.html',
            context, context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def admin_tutorials(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    sessions = [ { 'id': sess.replace(':', '-'), 'name': name,
                   'tutorials': Tutorial.objects.filter(session=sess)}
                        for sess, name in SESSIONS ]
    return render_to_response('attendeereg/admin_tutorials.html',
            { 'title': 'Tutorial Listing',
              'total': Registration.objects.filter(active=True).count(),
              'pending': Registration.objects.filter(
                active=False, invoice__status__in=['CO', 'WA']).count(),
              'sessions': sessions},
            context_instance=RequestContext(request))

def splitname(name, status):
    """
    Return (last, first, full name, status) for accountless registrants.
    Not perfect, but better than nothing.
    """
    s = name.split()
    if len(s) == 2:
        return [s[1], s[0], name, status ]
    if len(s) < 2:
        return (name, u'', name, status)
    if u'.' in s[0] and len(s[0]) > 2:
        # "Mr.", "Ms.", etc.
        s.pop(0)
    if u'.' in s[-1] and len(s[-1]) > 2:
        # "Esq." etc?
        s.pop()
    if len(s) < 2:
        return (s[0], u'', name, status)
    return (s[-1], s[0], name, status)

def reg2entry(reg, unpaid, status):
    b = lambda val: u'1' if val else u'0'
    if reg.user:
        first = reg.user.first_name
        last = reg.user.last_name
        full = unicode(reg.user)
        badge = reg.badge_name
    else:
        last, first, full, badge = splitname(reg.name, reg.badge_name)
    tutorial_only = reg.tutorial and not reg.conference
    vendor_only = reg.vendor and not reg.conference
    am, pm, ev = u'', u'', u''
    for tut in reg.tutorials.all():
        if tut.session == '1:AM':
            am = tut.name + u': ' + tut.title[:15]
        elif tut.session == '2:PM':
            pm = tut.name + u': ' + tut.title[:15]
        elif tut.session == '3:EV':
            ev = tut.name + u': ' + tut.title[:15]
    return (u'R%d' % reg.id, badge, reg.badge_text1, reg.badge_text2, # front
            b(reg.keynote), b(reg.speaker), b(reg.session), #icons
            b(reg.sponsor), b(reg.vendor),    # icons
            b(tutorial_only), b(vendor_only), # custom badge
            first, last, b(reg.tutorial),     # ignorable
            full, am, pm, ev, reg.shirt_size, reg.shirt_type,
            b(reg.vegetarian), b(reg.vegan), b(reg.kosher), b(reg.halal),
            b(unpaid), status)
reg2entry.heading = (u'id,badge-name,badge-text1,badge-text2,'
                     u'keynote,speaker,session-chair,sponsor,vendor,'
                     u'tutorial-only,vendor-only,'
                     u'first,last,tutorial,'
                     u'full-name,tut-am,tut-pm,tut-ev,'
                     u'shirt-size,shirt-type,'
                     u'vegetarian,vegan,kosher,halal,unpaid,status').split(',')
_ind1 = reg2entry.heading.index('last')
_ind2 = reg2entry.heading.index('first')
reg2entry.key=lambda x: (x[_ind1].lower(), x[_ind2].lower())

@login_required
@feature_required("RegistrationAdmin")
def admin_export_badges_csv(request, filename='badges.csv', thursday_only=False,
                            friday_only=False):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    teachers = Registration.objects.filter(active=True, tutorial=False,
        user__groups__name='Teachers').order_by('id').distinct().select_related()
    if thursday_only:
        paid = chain(teachers, Registration.objects.filter(
            active=True, paid=True, tutorial=True).select_related())
        manual = Registration.objects.filter(
            active=True, paid=False, tutorial=True).select_related()
        ccard = Registration.objects.filter(
            active=False, paid=False, tutorial=True,
            invoice__status__in=['CO', 'WA']).order_by('id'
                ).distinct().select_related()
    elif friday_only:
        paid = Registration.objects.filter(
            active=True, paid=True, tutorial=False).exclude(
                id__in=[x['id'] for x in teachers.values('id')]).select_related()
        manual = Registration.objects.filter(
            active=True, paid=False, tutorial=False).exclude(
                id__in=[x['id'] for x in teachers.values('id')]
                ).select_related()
        ccard = Registration.objects.filter(
            active=False, paid=False, tutorial=False,
            invoice__status__in=['CO', 'WA']).order_by('id'
                ).distinct().select_related()
    else:
        paid = Registration.objects.filter(
            active=True, paid=True).select_related()
        manual = Registration.objects.filter(
            active=True, paid=False).select_related()
        ccard = Registration.objects.filter(
            active=False, paid=False,
            invoice__status__in=['CO', 'WA']).order_by(
                'id').distinct().select_related()
    data = chain((reg2entry(entry, False, u'Paid') for entry in paid),
                 (reg2entry(entry, True, u'Manual/Check') for entry in manual),
                 (reg2entry(entry, True, u'CCard Issue') for entry in ccard))
    entries = sorted(data, key=reg2entry.key)
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=' + filename
    csvdata = csv.writer(response)
    csvdata.writerow(tuple(part.encode('utf-8') for part in reg2entry.heading))
    csvdata.writerows(tuple(part.encode('utf-8') for part in record)
                      for record in entries)
    return response

@login_required
@feature_required("RegistrationAdmin")
def admin_attendees_csv(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))

    names = [(u.last_name, u.first_name, unicode(u), 'Paid') for u in
            User.objects.filter(registration__active=True,
                                registration__paid=True
                                ).distinct().select_related() ]
    names.extend((u.last_name, u.first_name, unicode(u), 'UnPaid') for u in
            User.objects.filter(registration__active=True,
                                registration__paid=False
                                ).distinct().select_related())
    names.extend((u.last_name, u.first_name, unicode(u), 'Pending') for u in
            User.objects.filter(registration__active=False,
                                registration__invoice__status__in=['WA', 'CO']
                                ).distinct().select_related())
    names.extend(splitname(ent['name'], 'Paid') for ent in
            Registration.objects.filter(active=True, paid=True,
                                        user__isnull=True).values('name') )
    names.extend(splitname(ent['name'], 'UnPaid') for ent in
            Registration.objects.filter(active=True, paid=False,
                                        user__isnull=True).values('name') )
    names.extend(splitname(ent['name'], 'Pending') for ent in
            Registration.objects.filter(active=False,
                                        invoice__status__in=['WA', 'CO'],
                                        user__isnull=True).values('name') )
    ## with apologies to Brett Cannon:
    names.sort(key=lambda (a, b, n, s): (a.lower(), b.lower()))
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=attendees.csv'
    csvdata = csv.writer(response)
    csvdata.writerows(tuple(part.encode('utf-8') for part in record)
                      for record in names)
    return response

@login_required
@feature_required("RegistrationAdmin")
def admin_update_flags(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    base = Registration.objects.filter(active=True,
        user__groups__name__in=Registration.group_flag_map.keys()).order_by(
        'id').distinct().select_related()
    for reg in base: reg.save()
    base = Registration.objects.filter(active=False,
        invoice__status__in=['WA', 'CO'],
        user__groups__name__in=Registration.group_flag_map.keys()).order_by(
        'id').distinct().select_related()
    for reg in base: reg.save()
    return HttpResponseRedirect(reverse('reg-report-missing',None,(),{}))

@login_required
@feature_required("RegistrationAdmin")
def report_missing_registrations(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    presenters = User.objects.filter(
        groups__name__in=['Presenters', 'Session Chairs', 'Teachers']).order_by(
        'last_name', 'first_name').distinct()
    missing = presenters.extra(where=[
        '(SELECT COUNT(*) FROM attendeereg_registration '
        ' WHERE attendeereg_registration.user_id = auth_user.id) '
        '< 1'])
    unconnected = Registration.objects.filter(user__isnull=True).exclude(
        invoice__status='IV')
    for user in missing:
        user.unconnected = unconnected.filter(
            Q(name__icontains=user.last_name)|Q(email__iexact=user.email)
                ).order_by('id').distinct()

    return render_to_response('attendeereg/admin_missing.html',
            { 'title': 'Unregistered Speakers',
              'missing': missing},
            context_instance=RequestContext(request))



@login_required
@feature_required("RegistrationAdmin")
def admin_finances(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    # RED_FLAG: assumes we have only one price set for tutorials
    tut_costs = reg_costs[0]['tut']
    def add_breakout(query):
        freebies = 0
        for reg in query:
            reg.tut_cost = Decimal(tut_costs[reg.num_tutorials])
            if reg.cost <= reg.tut_cost:
                reg.reg_cost = Decimal("0.00")
                if reg.conference:
                    freebies += 1
            else:
                reg.reg_cost = reg.cost - reg.tut_cost
        query.total = sum(r.cost for r in query)
        query.reg = sum(r.reg_cost for r in query)
        query.tut = sum(r.tut_cost for r in query)
        query.free = freebies
        return query

    qPaid = Q(active=True, paid=True)
    qOwed = Q(active=True, paid=False)
    qPend = Q(active=False, paid=False, invoice__status__in=['CO', 'WA'])

    # base registration information
    base = Registration.objects.extra(select={'num_tutorials':
                'SELECT COUNT(*) FROM attendeereg_registration_tutorials '
                'WHERE attendeereg_registration.id = '
                    'attendeereg_registration_tutorials.registration_id'})
    paid = add_breakout(base.filter(qPaid))
    owed = add_breakout(base.filter(qOwed))
    pend = add_breakout(base.filter(qPend))

    vendor_count = Registration.objects.filter(active=True, vendor=True).count()
    sponsor_count = Registration.objects.filter(
        active=True, sponsor=True).count()
    free_count = paid.free + owed.free + pend.free
    tut_only_count = Registration.objects.filter(
        active=True, conference=False, tutorial=True).count()
    student_count = Registration.objects.filter(
        active=True, conference=True, student=True).count()
    regular_count = Registration.objects.filter(
        active=True, conference=True, student=False, corporate=False).count()
    corporate_count = Registration.objects.filter(
        active=True, conference=True, corporate=True).count()

    # partial refunds/additional charges
    partials = Invoice.objects.extra(where=[
        '(SELECT COUNT(*) FROM attendeereg_registration '
        ' WHERE attendeereg_invoice.id = attendeereg_registration.invoice_id) '
        '= 0'])
    part_refund = partials.filter(status='CA')
    part_pend_refund = partials.filter(status='WR')
    part_paid   = partials.filter(status__in=['PM', 'PA'])
    part_owed   = partials.filter(status__in=['CO', 'WP', 'WA'])

    part_refund.total = sum((i.total_cost for i in part_refund), Decimal("0.00"))
    part_paid.total   = sum((i.total_cost for i in part_paid), Decimal("0.00"))
    part_owed.total   = sum((i.total_cost for i in part_owed), Decimal("0.00"))
    part_pend_refund.total = sum((i.total_cost for i in part_pend_refund),
                                 Decimal("0.00"))

    reg = {'paid': paid, 'owed': owed, 'pend': pend,
           'part_paid': part_paid, 'part_owed': part_owed,
           'part_refund': part_refund,
           'part_pend_refund': part_pend_refund,
           'total_count': paid.count() + owed.count(),
           'vendor_count': vendor_count,
           'sponsor_count': sponsor_count,
           'free_count': free_count,
           'tut_only_count': tut_only_count,
           'student_count': student_count,
           'regular_count': regular_count,
           'corporate_count': corporate_count,
           'total_reg': paid.reg + owed.reg,
           'total_tut': paid.tut + owed.tut,
           'total': (paid.total+part_paid.total+part_owed.total+owed.total
                     -part_refund.total)}
    reg['pend_total_count'] = reg['total_count']+pend.count()
    reg['pend_total']       = reg['total']+pend.total-part_pend_refund.total
    reg['pend_total_reg']   = reg['total_reg']+pend.reg
    reg['pend_total_tut']   = reg['total_tut']+pend.tut

    # Number taking 1 tutorial:
    tut_one = Registration.objects.extra(where=[
        '(SELECT COUNT(*) FROM attendeereg_registration_tutorials '
        'WHERE attendeereg_registration.id = '
        'attendeereg_registration_tutorials.registration_id) = 1'])
    # Number taking 2 tutorials:
    tut_two = Registration.objects.extra(where=[
        '(SELECT COUNT(*) FROM attendeereg_registration_tutorials '
        'WHERE attendeereg_registration.id = '
        'attendeereg_registration_tutorials.registration_id) = 2'])
    # Number taking 3 tutorials:
    tut_three = Registration.objects.extra(where=[
        '(SELECT COUNT(*) FROM attendeereg_registration_tutorials '
        'WHERE attendeereg_registration.id = '
        'attendeereg_registration_tutorials.registration_id) = 3'])
    # there is a for loop hiding in here...
    tut = {'one_paid': tut_one.filter(qPaid).count(),
           'one_owed': tut_one.filter(qOwed).count(),
           'one_pend': tut_one.filter(qPend).count(),
           'two_paid': tut_two.filter(qPaid).count(),
           'two_owed': tut_two.filter(qOwed).count(),
           'two_pend': tut_two.filter(qPend).count(),
           'three_paid': tut_three.filter(qPaid).count(),
           'three_owed': tut_three.filter(qOwed).count(),
           'three_pend': tut_three.filter(qPend).count(),}
    tut['two_or_three_paid']  = tut['two_paid'] + tut['three_paid']
    tut['two_or_three_owed']  = tut['two_owed'] + tut['three_owed']
    tut['two_or_three_pend']  = tut['two_pend'] + tut['three_pend']
    tut['total_paid']  = tut['one_paid'] + tut['two_paid'] + tut['three_paid']
    tut['total_owed']  = tut['one_owed'] + tut['two_owed'] + tut['three_owed']
    tut['total_pend']  = tut['one_pend'] + tut['two_pend'] + tut['three_pend']
    tut['total_one']   = tut['one_paid'] + tut['one_owed']
    tut['total_two']   = tut['two_paid'] + tut['two_owed']
    tut['total_three'] = tut['three_paid'] + tut['three_owed']
    tut['total_two_or_three'] = tut['total_two'] + tut['total_three']
    tut['total']       = tut['total_paid'] + tut['total_owed']
    tut['potential_one']   = tut['total_one'] + tut['one_pend']
    tut['potential_two']   = tut['total_two'] + tut['two_pend']
    tut['potential_three'] = tut['total_three'] + tut['three_pend']
    tut['potential_two_or_three'] = tut['total_two_or_three'] + tut['two_or_three_pend']
    tut['potential']       = tut['total'] + tut['total_pend']

    # donations
    donations = Invoice.objects.exclude(donation=Decimal("0.00"))
    ## donations are not refunded, so even CA are counted
    don_paid = donations.filter(status__in=['PM', 'PA', 'CA'])
    don_owed = donations.filter(status='WP')
    don_pend = donations.filter(status__in=['WA', 'CO'])
    don_paid.total = sum((d.donation for d in don_paid), Decimal("0.00"))
    don_owed.total = sum((d.donation for d in don_owed), Decimal("0.00"))
    don_pend.total = sum((d.donation for d in don_pend), Decimal("0.00"))

    don = {'paid': don_paid, 'owed': don_owed, 'pend': don_pend,
           'total_count': don_paid.count() + don_owed.count(),
           'total': don_paid.total + don_owed.total,
           'pend_total_count': don_paid.count()+don_owed.count()+don_pend.count(),
           'pend_total': don_paid.total + don_owed.total + don_pend.total}

    return render_to_response('attendeereg/admin_finance.html',
            { 'title': 'Finance Breakdown',
              'reg': reg, 'tut': tut, 'don': don},
            context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def report_listing(request):
    ## RED_FLAG: add permissions check!!!!
    attendees = Registration.objects.filter(listing_ok=True, active=True)
    sessions = [ { 'name': name,
                   'tutorials': Tutorial.objects.filter(session=sess)}
                        for sess, name in SESSIONS ]
    return render_to_response('attendeereg/attendee_listing.html',
            { 'title': 'Attendee Listing',
              'total': Registration.objects.filter(active=True).count(),
              'pending': Registration.objects.filter(
                active=False, invoice__status__in=['CO', 'WA']).count(),
              'sessions': sessions,
              'attendees': attendees },
            context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def report_tshirts(request):
    #RED_FLAG: need to abstract out the '1:AM' session stuff!
    attendees = Registration.objects.filter(active=True).order_by('id')
    first = attendees.filter(tutorials__session='1:AM').distinct()
    ids = [ a['id'] for a in first.values('id') ]
    second = attendees.filter(
        tutorials__session='2:PM').distinct().exclude(id__in=ids)
    ids = [ a['id'] for a in attendees.filter(
        tutorials__session__in=['1:AM', '2:PM']).distinct().values('id') ]
    third = attendees.filter(
        tutorials__session='3:PM').distinct().exclude(id__in=ids)
    any = attendees.filter(
        tutorials__session__in=['1:AM', '2:PM', '3:EV']).distinct()
    ids = [ a['id'] for a in any.values('id') ]
    none = attendees.exclude(id__in=ids)


    ## RED_FLAG: What the %##%$ was I thinking?!?!? - DougN
    sessions = dict(SESSIONS)
    queryRubric = [ (sessions['1:AM'],  first),
                    (sessions['2:PM'],  second),
                    (sessions['3:EV'],  third),
                    (u'Thursday',       any),
                    (u'Friday',         none),
                    (u'Total',          attendees), ]

    rubric = [ dict(name=name, sizes=[ dict(name=sname, types=[dict(name=stname,
                count=query.filter(shirt_size=size, shirt_type=stype).count())
                        for stype, stname in SHIRT_TYPES ])
                    for size, sname in SHIRT_SIZES ] )
                for name, query in queryRubric ]
    for entry in rubric:
        entry['sizes'].append(dict(name='Total', types=[ dict(name=n,
                count=sum(t['count'] for s in entry['sizes']
                          for t in s['types'] if t['name']==n))
                                for k, n in SHIRT_TYPES]))

    sizes = [ name for id, name in SHIRT_SIZES ] + ['Total',]
    return render_to_response('attendeereg/report_tshirts.html',
            { 'title': 'T-Shirt Breakdown',
              'total': Registration.objects.filter(active=True).count(),
              'sizes': sizes,
              'rubric': rubric },
            context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def report_food(request):
    options = Registration.food_options
    base = Registration.objects.filter(active=True).order_by('id').distinct()
    friday = [ base.filter(**dict([(opt, True)])) for opt in options ]
    thursday = [ base.filter(
        tutorials__session__in=[ k for k,n in SESSIONS ], **dict([(opt, True)]))
                for opt in options ]
    total_count = sum(x.count() for x in friday)
    thursday_count = sum(x.count() for x in thursday)
    total  = Registration.objects.filter(active=True).count()
    tut    = base.filter(tutorials__session__in=[k for k,n in SESSIONS]).count()
    fperc = lambda x: "%04.2f%%"%((x*100.0/total) if total else 0.0)
    tperc = lambda x: "%04.2f%%"%((x*100.0/tut)   if tut   else 0.0)

    thursday = zip(thursday, (tperc(c.count()) for c in thursday))
    friday   = zip(friday,   (fperc(c.count()) for c in friday))
    rubric   = zip(options, thursday, friday)
    special  = ('Special', (thursday_count, tperc(thursday_count)),
                       (total_count, fperc(total_count)))
    noopt    = ('No Option', (tut-thursday_count, tperc(tut-thursday_count)),
                           (total-total_count, fperc(total-total_count)))
    totals   = ('Total', tut, total)

    return render_to_response('attendeereg/report_food.html',
            { 'title': 'Food Breakdown',
              'total': total,
              'totals': totals,
              'special': special,
              'noopt': noopt,
              'rubric': rubric,
              },
            context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def on_site_search(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    if request.method == 'GET' and 'clear' in request.GET:
        SelectUsersOrRegistrationsForm.regen_choices()
    post = request.POST if request.method == 'POST' else None
    search_form = SelectUsersOrRegistrationsForm(data=post)
    search_form.is_valid() ## temp hack
    lookup_form = GetRegistration()
    user_form = AdminCreateUserForm()
    return render_to_response('attendeereg/find_reg.html',
                              {'title': 'Search Users and Unconnected Registrations',
                               'search_form': search_form,
                               'lookup_form': lookup_form,
                               'user_form': user_form},
                              context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def on_site_listing(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    search_form = SelectUsersOrRegistrationsForm(data=request.POST)
    if not search_form.is_valid():
        return HttpResponseRedirect(reverse('reg-on-site-search2',None,(), {}))
    print search_form.users, search_form.unconnected
    return render_to_response('attendeereg/list_users_and_reg.html',
                              {'title': 'List Users and Unconnected Registrations',
                               'users': search_form.users,
                               'unconnected': search_form.unconnected},
                              context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def on_site_new_user_reg(request):
    if not request.user.is_superuser:
        return render_to_response('attendeereg/no_perm.html',
                          {'title': 'No Permissions'},
                          context_instance=RequestContext(request))
    post = None
    if request.method =='POST':
        post = request.POST
    user_form = AdminCreateUserForm(data=post)
    if user_form.is_valid():
        new_user = user_form.save(request)
        return HttpResponseRedirect(reverse('reg-register',None,(), {}) +
                                    u'?user=' + unicode(new_user.id))
    return render_to_response('attendeereg/new_user_reg.html',
                              {'title': 'Create User and Register',
                               'form': user_form},
                              context_instance=RequestContext(request))

@login_required
@feature_required("RegistrationAdmin")
def reg_redirect(request):
    if request.method != 'POST': raise Http404
    reg = GetRegistration(data=request.POST).registration
    if reg is None: raise Http404
    if '__invoice' in request.POST:
        return HttpResponseRedirect(reverse('reg-invoice',None,(),
                                            {'invid': unicode(reg.invoice)}))
    return HttpResponseRedirect(reverse('reg-view',None,(), {'regid': reg.id}))
