from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User, UserManager, Group
from django.contrib.auth import forms as auth_forms
from django.core.urlresolvers import reverse
from django.core import paginator as p
from django.db.models import Q
from django.forms.formsets import formset_factory
from django.forms.models import modelformset_factory
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404
from django.template import Context, RequestContext
from django.template.loader import get_template
from django.conf import settings
from django.core import mail
from django.utils.datastructures import MultiValueDictKeyError
from django.views.decorators.csrf import csrf_exempt

#from mess.accounting import models as a_models
from mess.utils.logging import log
from mess.membership import forms, models
from mess.events import models as e_models
from mess.accounting import models as a_models
from mess.core.permissions import has_elevated_perm
from mess.revision import models as r_models
#from mess.changelog import models as c_models

from decimal import Decimal
import copy
import datetime
import smtplib

# number of members or accounts to show per page in respective lists
PER_PAGE = 50

RECORD_STATE_ACTIVE = '1'
RECORD_STATE_INACTIVE = '0'

MEMBER_COORDINATOR_EMAIL = 'membership@mariposa.coop'
DONOTREPLY_EMAIL = 'do-not-reply@mariposa.coop'

@login_required
def members(request):
    '''
    list of all members (active by default)
    '''
    if not has_elevated_perm(request,'membership','add_member'):
        return HttpResponseRedirect(reverse('member', 
            args=[request.user.username]))
    context = RequestContext(request)
    members = models.Member.objects.all()
    if 'sort_by' in request.GET:
        form = forms.MemberListFilterForm(request.GET)
        if form.is_valid():
            search = form.cleaned_data.get('search')
            if search:
                members = members.filter(
                        Q(user__first_name__icontains=search) |
                        Q(user__last_name__icontains=search))
            sort = form.cleaned_data['sort_by']
            if sort == 'alpha':
                members = members.order_by('user__username')
            elif sort == 'oldjoin':
                members = members.order_by('date_joined')
            elif sort == 'newjoin':
                members = members.order_by('-date_joined')
            if not form.cleaned_data['active']:
                members = members.exclude(date_missing__isnull=True,
                        date_departed__isnull=True)
            if not form.cleaned_data['missing']:
                members = members.exclude(date_missing__isnull=False)
            if not form.cleaned_data['departed']:
                members = members.exclude(date_departed__isnull=False)
    else:
        form = forms.MemberListFilterForm()
        members = members.filter(date_missing__isnull=True,
                date_departed__isnull=True)
    context['form'] = form
    pager = p.Paginator(members, PER_PAGE)
    context['pager'] = pager
    page_number = request.GET.get('p')
    context['page'] = _get_current_page(pager, page_number)
    # drop any p= queries from the query string
    context['query_string'] = request.META['QUERY_STRING'].split('&p=', 1)[0]
    template = get_template('membership/members.html')
    return HttpResponse(template.render(context))

@login_required
def member(request, username):
    '''
    individual member page
    '''
    user = get_object_or_404(User, username=username)
    if (not has_elevated_perm(request,'membership','change_member') and
        (not (request.user.is_authenticated() and 
             request.user.id == user.id))):
        return HttpResponseRedirect(reverse('welcome'))
    context = RequestContext(request)
    member = user.get_profile()
    context['member'] = member
    # to get around silly {% url %} limits
    context['address_name'] = 'address'
    context['email_name'] = 'email'
    context['phone_name'] = 'phone'
    template = get_template('membership/member.html')
    return HttpResponse(template.render(context))

def get_member_availability(member_interest_form):
    '''
    Pulls the member availability bit array from the member_interest_form and returns it
    '''
    member_availability = 0

    availability_selections = member_interest_form.cleaned_data["availability_sunday"] + member_interest_form.cleaned_data["availability_monday"] + member_interest_form.cleaned_data["availability_tuesday"] + member_interest_form.cleaned_data["availability_wednesday"] + member_interest_form.cleaned_data["availability_thursday"] + member_interest_form.cleaned_data["availability_friday"] + member_interest_form.cleaned_data["availability_saturday"]

    for selection in availability_selections:
        member_availability |= int(selection) 

    return member_availability

def availability_integer_to_array(availability_bit_array, availability_tuple):
    if availability_bit_array is None:
        availability_bit_array = 0

    availability_array = []

    for value in availability_tuple:
        value = value[0]
        if availability_bit_array & value:
            availability_array.append(unicode(value))

    return availability_array

def set_member_availability(member, member_interest_form):
    '''
    Uses a member's 'availability' bit array to set the appropriate 
    availabilty checkbox values in the member interest form
    '''

    if not member.availability:
        member.availability = 0
    member_interest_form.initial['availability_sunday'] = availability_integer_to_array(member.availability, models.MEMBER_AVAILABILITY_SUNDAY)
    member_interest_form.initial['availability_monday'] = availability_integer_to_array(member.availability, models.MEMBER_AVAILABILITY_MONDAY)
    member_interest_form.initial['availability_tuesday'] = availability_integer_to_array(member.availability, models.MEMBER_AVAILABILITY_TUESDAY)
    member_interest_form.initial['availability_wednesday'] = availability_integer_to_array(member.availability, models.MEMBER_AVAILABILITY_WEDNESDAY)
    member_interest_form.initial['availability_thursday'] = availability_integer_to_array(member.availability, models.MEMBER_AVAILABILITY_THURSDAY)
    member_interest_form.initial['availability_friday'] = availability_integer_to_array(member.availability, models.MEMBER_AVAILABILITY_FRIDAY)
    member_interest_form.initial['availability_saturday'] = availability_integer_to_array(member.availability, models.MEMBER_AVAILABILITY_SATURDAY)

@login_required
def member_form(request, username=None):
    '''
    edit member info
    '''
    edit = bool(username)
    if edit:
        user = get_object_or_404(User, username=username)
        member = user.get_profile()
        user_old_values = copy.deepcopy(user.__dict__)
        member_old_values = copy.deepcopy(member.__dict__)
    else:
        user = User()
        user.set_password(User.objects.make_random_password())
        member = models.Member()

    is_errors = False
    if request.method == 'POST':
        if 'cancel' in request.POST:
            if edit:
                # FIXME this is bad if member has more than one account
                return HttpResponseRedirect(member.accounts.all()[0].get_absolute_url())
            else:
                return HttpResponseRedirect(reverse('accounts'))
        if 'delete' in request.POST:
            # We dpn't allow deleting members from MESS anymore
            #member.delete()
            #user.delete()
            return HttpResponseRedirect(reverse('accounts'))
        user_form = forms.UserForm(request.POST, prefix='user', instance=user)
        user_email_form = forms.UserEmailForm(request.POST, instance=user)
        member_form = forms.MemberForm(request.POST, prefix='member', instance=member)
        member_interest_form = forms.MemberInterestForm(request.POST, prefix='member_interest', instance=member)
        if has_elevated_perm(request, 'membership', 'add_member'):
            related_account_formset = forms.RelatedAccountFormSet(request.POST, 
                    instance=member, prefix='related_account')
            LOA_formset = forms.LeaveOfAbsenceFormSet(request.POST, 
                    instance=member, prefix='leave_of_absence')
        address_formset = forms.AddressFormSet(request.POST, instance=member,
                prefix='address')
        phone_formset = forms.PhoneFormSet(request.POST, instance=member,
                prefix='phone')
        if has_elevated_perm(request, 'membership', 'add_member'):
            if (user_form.is_valid() and member_form.is_valid() and 
                    related_account_formset.is_valid() and 
                    LOA_formset.is_valid()): 
                user = user_form.save()

                member = member_form.save(commit=False)
                member.user = user
                member.save()
                member_form.save_m2m()

                if edit:
                    log(request, user, 'edit', old_values=user_old_values)
                    log(request, member, 'edit', old_values=member_old_values)
                else:
                    log(request, user, 'add')
                    log(request, member, 'add')

                for formset in (related_account_formset, LOA_formset): 
                    _setattr_formset_save(request, formset, 'member', member)

                '''
                We perform one more member.save() after the formsets have been 
                saved. We do this because it is during the post_save of 
                member.save() that the member revision is created, and to ensure
                we get an accurate revision the formset has to have been saved
                '''
                member.save()
                
                if not edit:
                    # TODO send member an email with login information see #224
                    # see also password_reset in django.contrib.auth.views.py
                    pass


            else:
                is_errors = True


        else:
            if user_email_form.is_valid():
                user = user_email_form.save()
            else:
                is_errors = True

                # We notify the member coordinator if a non-staff user has updated
                # their own profile
                if not request.user.is_staff and request.user.id == member_instance.user.id:
                    notify_member_coordinator(member_instance)

        # must be after member.save() in case member is newly added
        if address_formset.is_valid() and phone_formset.is_valid() and not is_errors:
            for formset in (address_formset, phone_formset):
                _setattr_formset_save(request, formset, 'member', member)
        else: 
            is_errors = True



        if member_interest_form.is_valid() and not is_errors:
            member_instance = member_interest_form.save(commit=False)
            member_instance.availability = get_member_availability(member_interest_form)
            member_instance.save()
            member_interest_form.save_m2m()
        else:
            is_errors = True

        if not is_errors:
             # FIXME this is bad if member has more than one account
            try:
                redirect = member.accounts.all()[0].get_absolute_url()
            except IndexError:
                redirect = reverse('accounts')
            return HttpResponseRedirect(redirect)
    else:
        user_form = forms.UserForm(instance=user, prefix='user')
        user_email_form = forms.UserEmailForm(instance=user)
        member_form = forms.MemberForm(instance=member, prefix='member')
        member_interest_form = forms.MemberInterestForm(instance=member, prefix='member_interest')
        set_member_availability(member, member_interest_form)
        related_account_formset = forms.RelatedAccountFormSet(instance=member, 
                prefix='related_account')
        address_formset = forms.AddressFormSet(instance=member, 
                prefix='address')
        phone_formset = forms.PhoneFormSet(instance=member, prefix='phone')
        LOA_formset = forms.LeaveOfAbsenceFormSet(instance=member, 
                prefix='leave_of_absence')
    context = RequestContext(request)
    context['member'] = member
    context['user_form'] = user_form
    context['user_email_form'] = user_email_form
    context['member_form'] = member_form
    context['member_interest_form'] = member_interest_form
    if has_elevated_perm(request, 'membership', 'add_member'):
        context['formsets'] = [
            (related_account_formset, 'Accounts'), 
            (LOA_formset, 'Leaves of Absence'),
            (address_formset, 'Addresses'), 
            (phone_formset, 'Phones'),
        ]
    else:
        context['formsets'] = [
            (address_formset, 'Addresses'), 
            (phone_formset, 'Phones'),
        ]
    context['is_errors'] = is_errors
    context['edit'] = edit
    template = get_template('membership/member_form.html')
    return HttpResponse(template.render(context))

def notify_member_coordinator(member):
    email_template = get_template('membership/emails/member_update.html')

    send_mess_email(
            "%s updated their member profile" % member.user.get_full_name(),
            MEMBER_COORDINATOR_EMAIL, 
            DONOTREPLY_EMAIL, 
            email_template.render(Context({"member": member})),
            )

@login_required
def member_interest_form(request, username=None):
    '''
    edit member workshift interests and skills
    '''
    context = RequestContext(request)
    edit = bool(username)
    if edit:
        user = get_object_or_404(User, username=username)
        member = user.get_profile()
    else:
        return HttpResponseRedirect(reverse('welcome'))

    if request.method == 'POST':
        if 'cancel' in request.POST:
            return HttpResponseRedirect(reverse('welcome'))

        member_interest_form = forms.MemberInterestForm(request.POST, prefix='member_interest', instance=member)

        if member_interest_form.is_valid():
            member_instance = member_interest_form.save(commit=False)
            member_instance.availability = get_member_availability(member_interest_form)
            member_instance.save()
            member_interest_form.save_m2m()

            # We notify the member coordinator if a non-staff user has updated
            # their own profile
            if not request.user.is_staff and request.user.id == member_instance.user.id:
                notify_member_coordinator(member_instance)

            return HttpResponseRedirect(reverse('welcome'))
        else:
            context["is_errors"] = True

    else:
        member_interest_form = forms.MemberInterestForm(instance=member, prefix='member_interest')

        set_member_availability(member, member_interest_form)

    context['member'] = member
    context['member_interest_form'] = member_interest_form
    template = get_template('membership/member_interest_form.html')
    return HttpResponse(template.render(context))

@login_required
def accounts(request):
    '''
    list of accounts
    '''
    context = RequestContext(request)
    if not has_elevated_perm(request,'membership','add_account'):
        accounts = request.user.get_profile().accounts.all()
            
        if len(accounts) == 1:
            return HttpResponseRedirect(accounts[0].get_absolute_url())
    else:
        accounts = models.Account.objects.all()
        if 'sort_by' in request.GET:
            form = forms.AccountListFilterForm(request.GET)
        else:
            form = forms.AccountListFilterForm()
        if 'sort_by' in request.GET and form.is_valid():
            if form.cleaned_data['inactive'] and form.cleaned_data['active']:
                accounts = models.Account.objects.all()
            elif form.cleaned_data['inactive']:
                accounts = models.Account.objects.inactive()
            elif form.cleaned_data['active']:
                accounts = models.Account.objects.active()
            else:
                accounts = models.Account.objects.none()
            
            if form.cleaned_data['organizations']:
                accounts = accounts.filter(account_type='o')

            search = form.cleaned_data.get('search')
            if search:
                accounts = accounts.filter(
                        Q(name__icontains=search) |
                        Q(note__icontains=search))
            sort = form.cleaned_data['sort_by']
            if sort == 'alpha':
                accounts = accounts.order_by('name')
            elif sort == 'recent':
                accounts = accounts.order_by('-id')
            elif sort == 'hours':
                accounts = accounts.order_by('-hours_balance')
            elif sort == 'balance':
                accounts = accounts.order_by('-balance')
        else:
            accounts = models.Account.objects.active()
        context['form'] = form
    pager = p.Paginator(accounts, PER_PAGE)
    context['pager'] = pager
    page_number = request.GET.get('p')
    context['page'] = _get_current_page(pager, page_number)
    context['query_string'] = request.META['QUERY_STRING'].split('&p=', 1)[0]
    template = get_template('membership/accounts.html')
    return HttpResponse(template.render(context))

@login_required
def account(request, id):
    '''
    individual account page
    '''
    account = get_object_or_404(models.Account, id=id)
    request_member = models.Member.objects.get(user=request.user)
    if (not has_elevated_perm(request,'membership','add_member') and
        not (request.user.is_authenticated() and 
             request_member in account.members.all())):
        return HttpResponseRedirect(reverse('welcome'))
    context = RequestContext(request)
    context['account'] = account
    transactions = account.transaction_set.order_by('-id')[:50]
    context['transactions'] = transactions
    template = get_template('membership/account.html')
    return HttpResponse(template.render(context))

@login_required
def account_form(request, id=None):
    '''
    edit account info
    '''

    if not has_elevated_perm(request, 'membership', 'add_account'):
        return HttpResponseRedirect(reverse('welcome'))

    context = RequestContext(request)
    if id:
        account = get_object_or_404(models.Account, id=id)
        context['edit'] = True
        old_values = copy.deepcopy(account.__dict__)
    else:
        account = models.Account()
    if request.method == 'POST':
        if 'cancel' in request.POST:
            if id:
                return HttpResponseRedirect(reverse('account', args=[id]))
            else:
                return HttpResponseRedirect(reverse('accounts'))
        if 'delete' in request.POST:
            #account.delete()
            #log(request, account, 'delete')
            return HttpResponseRedirect(reverse('accounts'))
        form = forms.AccountForm(request.POST, instance=account)
        related_member_formset = forms.RelatedMemberFormSet(request.POST, 
                instance=account, prefix='related_member')
        if form.is_valid() and related_member_formset.is_valid():


            if context.get('edit'):
                account = form.save()
                log(request, account, 'edit', old_values=old_values)
            else:
                account = form.save()
                log(request, account, 'add')

            #account_log_entry = c_models.LogEntry()
            #account_log_entry.record_entry(account, request.user)
            #account_log_entry.save()

            _setattr_formset_save(request, related_member_formset, 'account', account)

            '''
            We call account.save once last time here because this it is
            during account's post_save signal that the new account 
            revision is created, and we need that formset saved before
            getting an accurate revision
            '''
            account.save()

            return HttpResponseRedirect(reverse('account', args=[account.id]))
    else:
        form = forms.AccountForm(instance=account)
        related_member_formset = forms.RelatedMemberFormSet(instance=account, 
                prefix='related_member')
    context['account'] = account
    context['form'] = form
    context['formsets'] = [
        (related_member_formset, 'Members'), 
    ]
    template = get_template('membership/account_form.html')
    return HttpResponse(template.render(context))

@login_required
def contact_form(request, username=None, medium=None, id=0):
    '''
    sub-page of member edit page.  ?
    '''
    context = RequestContext(request)
    referer = request.META.get('HTTP_REFERER', '')
    user = get_object_or_404(User, username=username)
    # medium may be 'address', 'phone', or 'email'
    MediumForm = forms.__getattribute__(medium.capitalize() + 'Form')
    MediumModel = models.__getattribute__(medium.capitalize())
    if id:
        medium_obj = get_object_or_404(MediumModel, id=id)
    else:
        medium_obj = MediumModel()
        context['add'] = True
    if request.method == 'POST':
        if 'cancel' in request.POST:
            return HttpResponseRedirect(reverse('member', args=[username]))
        form = MediumForm(request.POST, instance=medium_obj)
        referer = request.POST.get('referer')
        if form.is_valid():
            instance = form.save(commit=False)
            instance.member = user.get_profile()
            instance.save()
            if referer:
                return HttpResponseRedirect(referer)
            else:
                return HttpResponseRedirect(reverse('member', args=[username]))
    else:
        form = MediumForm(instance=medium_obj)
    context['form'] = form
    context['medium'] = medium
    context['referer'] = referer
    # use 'this_user' because context['user'] overrides logged-in user 
    context['this_user'] = user
    return render_to_response('membership/contact_form.html', context)

@login_required
def remove_contact(request, username=None, medium=None, id=None):
    '''
    delete a piece of contact info.
    '''
    context = RequestContext(request)
    MediumModel = models.__getattribute__(medium.capitalize())
    medium_obj = get_object_or_404(MediumModel, id=id)
    if request.method == 'POST':
        if 'cancel' in request.POST:
            return HttpResponseRedirect(reverse('member', args=[username]))
        medium_obj.delete()
        return HttpResponseRedirect(reverse('member', args=[username]))
    user = get_object_or_404(User, username=username)
    context['this_user'] = user
    context['contact'] = medium_obj
    context['medium'] = medium
    template = get_template('membership/remove_contact.html')
    return HttpResponse(template.render(context))

def depart_account(request, id):
    '''
    confirms departure of all members on account.  prompts today's date but editable.
    sets departure date and cancels all workshifts after said date.
    '''
    context = RequestContext(request)
    account = get_object_or_404(models.Account, id=id)
    if request.method == 'POST':       # user clicked one of the buttons
        if 'cancel' in request.POST:   # cancel button
            return HttpResponseRedirect(account.get_absolute_url())
        form = forms.DateForm(request.POST)
        if form.is_valid():            # save button
            # for each member, enter the date departed and get them off of their workshifts.
            for mem in account.members.all():
                if not mem.date_departed:
                    mem.date_departed = form.cleaned_data['day']
                    mem.save()
                mem.remove_from_shifts(form.cleaned_data['day'])
            return HttpResponseRedirect(account.get_absolute_url())
    else:                         # show prompt with default form values.
        form = forms.DateForm()
    context['account']=account
    context['form'] = form
    return render_to_response('membership/depart.html', context)

####anna bookmark.
def loa_account(request, id):
    '''
    confirms new leave of absence for all members on account.  
    start and end defaults specified in membership/forms.LoaForm
    duplicates each workshift scheduled between the two, inclusive.
    '''
    context = RequestContext(request)
    account = get_object_or_404(models.Account, id=id)
    if request.method == 'POST':       # user clicked one of the buttons
        if 'cancel' in request.POST:   # cancel button
            return HttpResponseRedirect(account.get_absolute_url())
        form = forms.LoaForm(request.POST)
        if form.is_valid():            # save button,
            for mem in account.members.active():
                s = form.cleaned_data['start']
                e = form.cleaned_data['end']
                new_loa = models.LeaveOfAbsence(member=mem, start=s, end=e)
                new_loa.save()
                if form.cleaned_data['shifts_during_LOA'] == 'long':
                    e = None   # members shd be removed from all workshifts
                mem.remove_from_shifts(s, e)
            return HttpResponseRedirect(account.get_absolute_url())
    else:                         # show prompt with default form values.
        form = forms.LoaForm()
    context['account']=account
    context['form'] = form
    return render_to_response('membership/loa_account.html', context)

def templimit(request, id):
    ' view temporary balance limit history, and set a different limit '
    account = get_object_or_404(models.Account, id=id)
    currentlimit = account.temporarybalancelimit_set.current()
    form = forms.TemporaryBalanceLimitForm()
    if len(currentlimit) > 1:
        return HttpResponse('Severe Error: Two current temporary balance limits')
    elif len(currentlimit) == 1:
        currentlimit = currentlimit[0]
    if request.method == 'POST':
        if currentlimit:  # clear out currentlimit in any event
            currentlimit.until = datetime.date.today() - datetime.timedelta(1)
            currentlimit.save()
        if request.POST.get('action') == 'set limit':
            form = forms.TemporaryBalanceLimitForm(request.POST)
            if form.is_valid():
                newlimit = form.save(commit=False)
                newlimit.account = account
                newlimit.save()
                form = forms.TemporaryBalanceLimitForm()
    currentlimit = account.temporarybalancelimit_set.current()
    if len(currentlimit) == 1:
        currentlimit = currentlimit[0]
    history = account.temporarybalancelimit_set.all()
    return render_to_response('membership/templimit.html', locals(),
            context_instance=RequestContext(request))
                        

def formset_form(request, medium):
    context = RequestContext(request)
    form_name = ''.join([x.capitalize() for x in medium.split('_')]) + 'Form'
    MediumForm = forms.__getattribute__(form_name)
    index = request.GET.get('index')
    if index:
        form = MediumForm(prefix='%s-%s' % (medium, index))
    else:
        form = MediumForm()
    context['form'] = form
    template = get_template('membership/snippets/formset_form.html')
    return HttpResponse(template.render(context))


def accountmemberflags(request):
    ''' show and allow editing of accountmember flags 
        (contact aka deposit holder; shopper).  
        this is kind of a report, but allows editing of the flags... '''
    results = []
    am = models.AccountMember.objects.filter(member__in=models.Member.objects.active()).filter(Q(shopper=True,account_contact=True)|Q(shopper=False,account_contact=False)).order_by('-id')
    if request.method == 'POST':
        amformset = forms.AccountMemberFlagsFormSet(request.POST, queryset=am)
        if amformset.is_valid():
            amformset.save()
            results.append('Formset was valid and saved...')
        else:
            results.append('Formset was invalid...')
            results.append(amformset.errors)
    else:
        amformset = forms.AccountMemberFlagsFormSet(queryset=am)
    # loop to mark each new account
    curracct = None
    for form in amformset.forms[::-1]:
        form.diffacct = (form.instance.account != curracct)
        form.anomaly = (form.instance.account_contact == form.instance.shopper)
        curracct = form.instance.account
    return render_to_response('membership/accountmemberflags.html', locals(),
            context_instance=RequestContext(request))

def admin_reset_password(request, username):
    ''' Send a password reset link to the member.  This function makes
    auth_forms think a passwordresetform was submitted for the user.email '''
    if request.method != "POST":
        return HttpResponse('Error. Adminresetpassword must be called by POST.')
    user = get_object_or_404(User, username=username)
    member = user.get_profile()
    if not user.email:
        return HttpResponse('Sorry. No emails on file for %s.' % member)
    phantomform = auth_forms.PasswordResetForm({'email': user.email})
    assert phantomform.is_valid()
    # send https link, i.e. https://mess.mariposa.coop/passwordreset/...
    phantomform.save(use_https=True,
        email_template_name='membership/welcome_email.txt')    
    message = '''<meta http-equiv="refresh" content="3; url=%s">
            Password reset email was sent to %s (%s).''' % \
            (member.get_absolute_url(), member, user.email)
    return HttpResponse(message)

def send_mess_email(subject, to_email, from_email, message):
    try:
        mail.send_mail(subject, message, from_email, [to_email])
    except smtplib.SMTPRecipientsRefused, e:
        print "SMTP Error: %s" % e

@csrf_exempt
def member_signup(request):
    context = RequestContext(request)

    if request.method == "POST":
        form = forms.MemberSignUpForm(request.POST)

        if form.is_valid():
            new_member = models.MemberSignUp()
            new_member.first_name = form.cleaned_data["first_name"] 
            new_member.last_name = form.cleaned_data["last_name"] 
            new_member.email = form.cleaned_data["email"] 
            new_member.phone = form.cleaned_data["phone"] 
            new_member.address1 = form.cleaned_data["street_address"] 
            new_member.city = form.cleaned_data["city"] 
            new_member.state = form.cleaned_data["state"] 
            new_member.postal_code = form.cleaned_data["postal_code"] 
            new_member.referral_source = form.cleaned_data["referral_source"] 
            new_member.referring_member = form.cleaned_data["referring_member"]
            new_member.orientation = e_models.Orientation.objects.get(id=form.cleaned_data["orientation"]) 
            new_member.equity_paid = form.cleaned_data["equity_paid"] 
            new_member.save()

            context["new_member"] = new_member
            context["equity"] = new_member.equity_paid

            if new_member.email:
                email_template = get_template('membership/emails/member_signup_member_confirmation.html')
                send_mess_email("Mariposa Member Sign Up", new_member.email, MEMBER_COORDINATOR_EMAIL, email_template.render(context))

            email_template = get_template('membership/emails/member_signup_member_coordinator_confirmation.html')
            send_mess_email("New Member Sign Up: " + new_member.first_name + " " + new_member.last_name, MEMBER_COORDINATOR_EMAIL, MEMBER_COORDINATOR_EMAIL, email_template.render(context))

            if (new_member.equity_paid == '0'):
                template = get_template('membership/confirmations/member_signup.html')
            else:
                template = get_template('membership/confirmations/member_signup_paypal.html')
                context["name"] = unicode(new_member)
                context["equity"] = new_member.equity_paid

        else:
            template = get_template('membership/member_signup.html')
            context['form'] = form
    else:
        template = get_template('membership/member_signup.html')
        context['form'] = forms.MemberSignUpForm()

    return HttpResponse(template.render(context))

@login_required
def member_signup_edit(request, id=None):
    if not has_elevated_perm(request,'membership','edit_membersignup'):
        return HttpResponseRedirect(reverse('welcome'))

    context = RequestContext(request)
    template = get_template('membership/member_signup_edit.html')

    if request.method == "POST":
        if 'cancel' in request.POST:
            return HttpResponseRedirect(reverse('member-signup-review'))
        else:
            if id:
                form = forms.MemberSignUpEditForm(request.POST, instance=models.MemberSignUp.objects.get(pk=id))
            else:
                form = forms.MemberSignUpEditForm(request.POST)

            if form.is_valid():
                form.save()
                return HttpResponseRedirect(reverse('member-signup-review'))
    else:
        if id:
            form = forms.MemberSignUpEditForm(instance=models.MemberSignUp.objects.get(pk=id))
            context["edit"] = True
        else:
            form = forms.MemberSignUpEditForm()

    context['form'] = form
    return HttpResponse(template.render(context))

@login_required
def member_signup_review(request):
    if not has_elevated_perm(request,'membership','add_member'):
        return HttpResponseRedirect(reverse('welcome'))

    context = RequestContext(request)

    MemberSignUpReviewFormSet = formset_factory(forms.MemberSignUpReviewForm)

    '''
    We have to populate the choices for the referring member form item. We could have 
    done this in the form itself, but that would have caused each and every form to 
    hit the database for the list of members, so instead we do one query on the 
    database here and set it for each form we are about to show to the user.
    
    Here we just create the list, and we set it in each form later. The key here is 
    that the choices have to be set before each form is validated.
    '''
    #members = models.Member.objects.filter(date_departed__isnull=True)
    #member_choices = [('','')]
        
    #for member in members:
    #    member_choices.append((member.id, str(member)))


    if request.method == "POST":

        formset = MemberSignUpReviewFormSet(request.POST)
        
        review_action = request.POST["review_action"]
        context["review_action"] = review_action

        record_ids = request.POST["record_ids"].split(',')
        record_states = request.POST["record_states"].split(',')

        new_members = []

        formset_errors = False
        
        for n in range(0, formset.total_form_count()):

            new_member = models.MemberSignUp.objects.get(id=record_ids[n])
            new_members.append(new_member)

            if record_states[n] == RECORD_STATE_INACTIVE:
                continue

            form = formset.forms[n]
            #form.set_referring_member_choices(member_choices)

            # We need to check if a given form has been selected, but the selected
            # value only appears in POST if the user has actually selected... I am sure there 
            # is a better way to handle this, but I don't have time to figure it out now - TM
            try:
                if not request.POST['form-' + str(n) + '-selected']:
                    # We clear any validation errors on forms that haven't been selected
                    form.errors.clear()
                    continue
            except MultiValueDictKeyError:
                # if the form wasn't selected, then we can just continue on to the next one
                #formset.data['form-' + str(n) + '-record_id'] = unicode(new_member.id)
                form.errors.clear()
                continue

            if review_action == "spam":
                new_member.spam = True
                new_member.active = False
                new_member.save()
                record_states[n] = RECORD_STATE_INACTIVE
            elif review_action == "delete":
                new_member.active = False
                new_member.save()
                record_states[n] = RECORD_STATE_INACTIVE
            elif review_action == "save":
                if form.is_valid():
                    # Create user
                    user = User.objects.create_user(form.cleaned_data['user_name'].strip(), password=User.objects.make_random_password())
                    user.email = new_member.email
                    user.first_name = new_member.first_name
                    user.last_name = new_member.last_name

                    '''
                    We save this here because the user needs a primary key value before
                    we can add a group
                    '''
                    user.save()

                    user.groups.add(Group.objects.get(name="member"))
                    user.save()

                    log(request, user, 'add')

                    # Create member
                    member = models.Member()
                    member.user = user
                    member.status = 'd' # departed
                    member.work_status = 'n' # no workshift

                    # Per Dana: the departed date is set to a very specific value for new members
                    # depending upon whether or not they have paid for the equity at sign up
                    if form.cleaned_data["payment_verified"] and new_member.equity_paid > 0:
                        member.date_departed = datetime.date(1904, 01, 01)

                    else:
                        member.date_departed = datetime.date(1904, 02, 02)
                        
                    member.referral_source = new_member.referral_source
                    member.orientation = new_member.orientation
                    member.referring_member = form.cleaned_data["referring_member"];

                    #if form.cleaned_data["referring_member"] and member.referral_source == "Current Member":
                    #    member.referring_member = models.Member.objects.get(id=form.cleaned_data["referring_member"])

                    member.save()

                    log(request, member, 'add')

                    address = models.Address()
                    address.type = 'h'
                    address.address1 = new_member.address1
                    address.address2 = new_member.address2
                    address.city = new_member.city
                    address.state = new_member.state
                    address.postal_code = new_member.postal_code
                    address.country = new_member.country
                    address.member = member
                    address.save()

                    phone = models.Phone()
                    phone.type = 'h'
                    phone.number = new_member.phone
                    phone.member = member
                    phone.save()
                   
                    # Create account
                    current_date = unicode(datetime.date.today())
                    account = models.Account() 
                    account.name = unicode(new_member) + " " + current_date
                    account.note = u'Joined Online %s' % current_date
                    account.save()

                    log(request, account, 'add')

                    # Create account member
                    account_member = models.AccountMember.objects.create(account=account, member=member)

                    log(request, account_member, 'add')

                    # Perform equity transaction if there was one
                    if form.cleaned_data["payment_verified"] and new_member.equity_paid > 0:
                        equity_transaction = a_models.Transaction.objects.create(account=account, member=member, purchase_type='O', purchase_amount=Decimal(new_member.equity_paid), note=u'Joined Online %s' % current_date)
                        equity_transaction = a_models.Transaction.objects.create(account=account, member=member, purchase_type='S', purchase_amount=Decimal(0) - Decimal(new_member.equity_paid), note=u'Joined Online %s' % current_date)

                    new_member.saved = True
                    new_member.save()

                    record_states[n] = RECORD_STATE_INACTIVE
                else:
                    formset_errors = True

        # If there are no errors, just return a fresh GET request
        if not formset_errors:
            return HttpResponseRedirect(reverse('member-signup-review'))
    else:

        new_members = models.MemberSignUp.objects.filter(saved=False).filter(spam=False).filter(active=True)
        new_members_count = new_members.count()

        record_ids = []
        record_states = []

        for n in range(0, new_members_count):
            record_ids.append(str(new_members[n].id))
            record_states.append(RECORD_STATE_ACTIVE)

        data = {
            'form-TOTAL_FORMS': unicode(new_members_count),
            'form-INITIAL_FORMS': unicode(new_members_count),
            'form-MAX_NUM_FORMS': u'',
            }
    
        formset = MemberSignUpReviewFormSet(data)

        '''
        We have to clear the errors on a GET request because for some reason the forms are trying to 
        validate, even on GETs. We also take the opportunity to set the referring member choices 
        here
        '''
        for form in formset.forms:
            form.errors.clear()
            #form.set_referring_member_choices(member_choices)

    context["formset"] = formset
    context["record_ids"] = ','.join(record_ids)
    context["record_states"] = ','.join(record_states)
    context["formset_members_recordstates"] = zip(formset.forms, new_members, record_states)

    template = get_template('membership/member_signup_review.html')
    return HttpResponse(template.render(context))

@csrf_exempt
def orientation_signup(request):
    context = RequestContext(request)

    if request.method == "POST":
        form = forms.OrientationSignUpForm(request.POST)

        if form.is_valid():
            template = get_template('membership/confirmations/orientation_signup.html')
            context["first_name"] = form.cleaned_data["first_name"]
            context["last_name"] = form.cleaned_data["last_name"]
            context["email"] = form.cleaned_data["email"]
            context["phone"] = form.cleaned_data["phone"]
            context["orientation"] = e_models.Orientation.objects.get(id=form.cleaned_data["orientation"]).name 

            email_template = get_template('membership/emails/orientation_signup_member_confirmation.html')
            send_mess_email("Mariposa Orientation Sign Up", form.cleaned_data["email"], MEMBER_COORDINATOR_EMAIL, email_template.render(context))

            email_template = get_template('membership/emails/orientation_signup_member_coordinator_confirmation.html')
            send_mess_email("New Orientation Sign Up: " + form.cleaned_data["first_name"] + " " + form.cleaned_data["last_name"], MEMBER_COORDINATOR_EMAIL, MEMBER_COORDINATOR_EMAIL, email_template.render(context))
        else:
            template = get_template('membership/orientation_signup.html')
            context['form'] = form
    else:
        template = get_template('membership/orientation_signup.html')
        context['form'] = forms.OrientationSignUpForm()

    return HttpResponse(template.render(context))

# helper functions below

def _setattr_formset_save(request, formset, name, value):
    instances = formset.save(commit=False)
    for instance in instances:
        setattr(instance, name, value)
        instance.save()
        log(request, instance, 'add')

def _get_current_page(pager, page_number):
    try:
        current_page = pager.page(page_number)
    except (p.PageNotAnInteger, TypeError):
        current_page = pager.page(1)
    return current_page

def account_history(request, account_id):
  context = RequestContext(request)
  account = models.Account.objects.get(id=account_id)
  revisions = r_models.AccountRevision.objects.filter(account=account).order_by('-timestamp')

  return render_to_response('membership/account_history.html', locals(),
    context_instance=RequestContext(request))

def member_history(request, member_id):
  context = RequestContext(request)
  member = models.Member.objects.get(id=member_id)
  revisions = r_models.MemberRevision.objects.filter(member=member).order_by('-timestamp')

  return render_to_response('membership/member_history.html', locals(),
    context_instance=RequestContext(request))
