Source

django-members / members / views.py

Full commit
import csv
import random
import difflib

from pygments import highlight
from pygments.lexers import DiffLexer
from pygments.formatters import HtmlFormatter

from django.shortcuts import get_object_or_404
from django.db import transaction
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.contrib.auth.decorators import login_required, permission_required
from django.core.cache import cache

from caktus.django.decorators import render_with
from caktus.iter import previous_and_next, collapse

from members import models as members
from members import forms as member_forms
from crm import models as crm
from contactinfo import models as contactinfo


@login_required
@render_with('members/membership/list.html')
def list_memberships(request):
    letter = ''
    letters = cache.get('membership-letter-nav')
    if not letters:
        letters = members.Membership.objects.distinct().exclude(
            contact__last_name=''
        ).extra(
            select={
                'letter': 'SUBSTR(crm_contact.last_name, 1, 1)'
            }
        ).order_by('letter').values_list('letter', flat=True)
        cache.set('membership-letter-nav', letters)
    
    memberships = members.Membership.objects.filter(
        status__is_current_member=True
    )
    total_current_members = cache.get('membership-current-count')
    if not total_current_members:
        total_current_members = memberships.count()
        cache.set('membership-current-count', total_current_members)
    filtering = False
    form = member_forms.SearchForm(request.GET)
    if form.is_valid():
        if 'search' in form.cleaned_data and form.cleaned_data['search']:
            search = form.cleaned_data['search']
            memberships = memberships.filter(
                Q(contact__first_name__icontains=search)
                | Q(contact__last_name__icontains=search)
                | Q(contact__email__icontains=search)
                | Q(contact__name=search)
            )
            filtering = True
        if 'letter' in form.cleaned_data and form.cleaned_data['letter']:
            letter = form.cleaned_data['letter']
            memberships = memberships.filter(
                contact__last_name__istartswith=letter,
            )
            filtering = True
    
    memberships = memberships.select_related(
        'contact',
        'type',
    ).order_by('contact__sort_name')
    
    return {
        'form': form,
        'filtering': filtering,
        'letters': letters,
        'selected_letter': letter,
        'memberships': memberships,
        'total_current_members': total_current_members,
    }


def get_memberships_with_relationships():
    memberships = members.Membership.objects.filter(
        status__is_current_member=True,
    ).select_related(
        'contact',
        'type',
    ).extra(
        select={
            'street': 'contactinfo_address.street',
            'city': 'contactinfo_address.city',
            'state_province': 'contactinfo_address.state_province',
            'postal_code': 'contactinfo_address.postal_code',
            'phone_number': 'contactinfo_phone.number',
            'phone_type': 'contactinfo_phone.type',
            'location_type': 'contactinfo_locationtype.name',
        },
    ).order_by('contact__sort_name')
    connection = (
        members.Membership._meta.db_table,
        crm.Contact._meta.db_table,
        members.Membership.contact.field.column,
        crm.Contact._meta.pk.name,
    )
    memberships.query.join(connection)
    connection = (
        crm.Contact._meta.db_table,
        crm.Contact.locations.field.m2m_db_table(),
        crm.Contact._meta.pk.column,
        crm.Contact.locations.field.m2m_column_name(),
    )
    memberships.query.join(connection, promote=True)
    connection = (
        crm.Contact.locations.field.m2m_db_table(),
        contactinfo.Location._meta.db_table,
        crm.Contact.locations.field.m2m_reverse_name(),
        # or crm.Location.contact_set.related.field.m2m_reverse_name()?
        contactinfo.Location._meta.pk.column,
    )
    memberships.query.join(connection, promote=True)
    connection = (
        contactinfo.Location._meta.db_table,
        contactinfo.LocationType._meta.db_table,
        contactinfo.Location.type.field.column,
        contactinfo.LocationType._meta.pk.column,
    )
    memberships.query.join(connection, promote=True)
    connection = (
        contactinfo.Location._meta.db_table,
        contactinfo.Address._meta.db_table,
        contactinfo.Location._meta.pk.column,
        contactinfo.Address.location.field.column,
    )
    memberships.query.join(connection, promote=True)
    connection = (
        contactinfo.Location._meta.db_table,
        contactinfo.Phone._meta.db_table,
        contactinfo.Location._meta.pk.column,
        contactinfo.Phone.location.field.column,
    )
    memberships.query.join(connection, promote=True)
    memberships = collapse(
        memberships,
        ['id'],
        ['phone_number', 'phone_type'],
        sort=False,
    )
    return memberships


@permission_required('members.can_print_membership_list')
@render_with('members/membership/print.html')
def print_membership_list(request):
    return {
        'memberships': get_memberships_with_relationships(),
    }


@permission_required('members.can_export_membership_list')
def export_membership_list(request):
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=memberships.csv'
    writer = csv.writer(response)
    writer.writerow((
        'Name',
        'Contact Type',
        'Membership Type',
        'Membership Status',
        'Email',
        'Location Type',
        'Street',
        'City',
        'State/Province',
        'Postal Code',
        'Phone Number(s)',
    ))
    memberships = get_memberships_with_relationships()
    for membership in memberships:
        data = [
            membership.contact,
            membership.contact.type,
            membership.type,
            membership.status,
            membership.contact.email,
            membership.location_type,
        ]
        street = membership.street or ''
        if street:
            street = street.replace('\n', ' ').replace('\r', ' ')
        data.extend((
            street,
            membership.city,
            membership.state_province,
            membership.postal_code,
            ', '.join([unicode(n) for n in membership.phone_number if n])
        ))
        writer.writerow((data))
    return response


@permission_required('members.can_compare_membership_list')
@render_with('members/membership/compare/prepare.html')
def compare_membership_list(request, compare_id):
    compare = get_object_or_404(members.MembershipComparison, pk=compare_id)
    fh = open(compare.file.path)
    try:
        dialect = csv.Sniffer().sniff(fh.read(1024))
    except csv.Error:
        dialect = csv.excel
    fh.seek(0)
    csv_reader = csv.reader(fh)
    lines = []
    for line in csv_reader:
        lines.append(line)
        if len(lines) == 7:
            break
    output = ''
    column_count = len(lines[0])
    if request.POST:
        form = member_forms.ColumnMatchForm(
            request.POST,
            column_count=column_count,
        )
        if form.is_valid():
            columns = form.save()
            
            memberships = members.Membership.objects.filter(
                status__is_current_member=True,
            ).select_related(
                'contact',
                'type',
            ).order_by('contact__last_name', 'contact__first_name')
            fh.seek(0)
            first = []
            for membership in memberships:
                row = []
                for (type_, attr), index in columns:
                    if type_ == 'membership':
                        col = getattr(membership, attr)
                    elif type_ == 'contact':
                        col = getattr(membership.contact, attr)
                    row.append(unicode(col))
                first.append(', '.join(row))
            
            fh.seek(0)
            second = []
            for line in csv_reader:
                row = []
                for attr, index in columns:
                    row.append(line[index])
                second.append(', '.join(row))
            
            diff = '\n'.join(difflib.ndiff(first, second))
            output = highlight(diff, DiffLexer(), HtmlFormatter())
            
    else:
        form = member_forms.ColumnMatchForm(column_count=column_count)
    
    fh.close()
    
    return {
        'output': output,
        'lines': lines,
        'has_header': dialect,
        'form': form,
        'compare': compare,
    }