Merge branch 'chrisjrn/reports_20160919'
This commit is contained in:
		
						commit
						bcd7043862
					
				
					 7 changed files with 362 additions and 62 deletions
				
			
		|  | @ -46,9 +46,13 @@ Because every conference is different, Registrasion lets you define your own att | |||
| .. autoclass :: AttendeeProfileBase | ||||
|     :members: name_field, invoice_recipient | ||||
| 
 | ||||
| Once you've subclassed ``AttendeeProfileBase``, you'll need to implement a form that lets attendees fill out their profile. | ||||
| You specify how to find that model in your Django ``settings.py`` file:: | ||||
| 
 | ||||
| You specify how to find that form in your Django ``settings.py`` file:: | ||||
|     ATTENDEE_PROFILE_MODEL = "democon.models.AttendeeProfile" | ||||
| 
 | ||||
| When Registrasion asks the to edit their profile, a default form will be generated, showing all of the fields on the profile model. | ||||
| 
 | ||||
| If you want to customise the profile editing form, you need to specify the location of that form in your ``settings.py`` file as well. | ||||
| 
 | ||||
|     ATTENDEE_PROFILE_FORM = "democon.forms.AttendeeProfileForm" | ||||
| 
 | ||||
|  |  | |||
|  | @ -307,6 +307,10 @@ class CreditNote(PaymentBase): | |||
|             creditnoterefund=None, | ||||
|         ) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def refunded(cls): | ||||
|         return cls.objects.exclude(creditnoterefund=None) | ||||
| 
 | ||||
|     @property | ||||
|     def status(self): | ||||
|         if self.is_unclaimed: | ||||
|  |  | |||
|  | @ -1,8 +1,16 @@ | |||
| from registrasion.models import conditions | ||||
| from registrasion.models import inventory | ||||
| 
 | ||||
| from django import forms | ||||
| 
 | ||||
| # Staff-facing forms. | ||||
| # Reporting forms. | ||||
| 
 | ||||
| 
 | ||||
| class DiscountForm(forms.Form): | ||||
|     discount = forms.ModelMultipleChoiceField( | ||||
|         queryset=conditions.DiscountBase.objects.all(), | ||||
|         required=False, | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| class ProductAndCategoryForm(forms.Form): | ||||
|  | @ -21,3 +29,22 @@ class UserIdForm(forms.Form): | |||
|         label="User ID", | ||||
|         required=False, | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def model_fields_form_factory(model): | ||||
|     ''' Creates a form for specifying fields from a model to display. ''' | ||||
| 
 | ||||
|     fields = model._meta.get_fields() | ||||
| 
 | ||||
|     choices = [] | ||||
|     for field in fields: | ||||
|         if hasattr(field, "verbose_name"): | ||||
|             choices.append((field.name, field.verbose_name)) | ||||
| 
 | ||||
|     class ModelFieldsForm(forms.Form): | ||||
|         fields = forms.MultipleChoiceField( | ||||
|             choices=choices, | ||||
|             required=False, | ||||
|         ) | ||||
| 
 | ||||
|     return ModelFieldsForm | ||||
|  |  | |||
|  | @ -1,16 +1,21 @@ | |||
| import forms | ||||
| 
 | ||||
| import collections | ||||
| import datetime | ||||
| 
 | ||||
| from django.conf import settings | ||||
| from django.contrib.auth.decorators import user_passes_test | ||||
| from django.core.urlresolvers import reverse | ||||
| from django.db import models | ||||
| from django.db.models import F, Q | ||||
| from django.db.models import Count, Sum | ||||
| from django.db.models import Count, Max, Sum | ||||
| from django.db.models import Case, When, Value | ||||
| from django.shortcuts import render | ||||
| 
 | ||||
| from registrasion.controllers.item import ItemController | ||||
| from registrasion.models import commerce | ||||
| from registrasion.models import people | ||||
| from registrasion import util | ||||
| from registrasion import views | ||||
| 
 | ||||
| from reports import get_all_reports | ||||
|  | @ -24,6 +29,9 @@ def CURRENCY(): | |||
|     return models.DecimalField(decimal_places=2) | ||||
| 
 | ||||
| 
 | ||||
| AttendeeProfile = util.get_object_from_name(settings.ATTENDEE_PROFILE_MODEL) | ||||
| 
 | ||||
| 
 | ||||
| @user_passes_test(views._staff_only) | ||||
| def reports_list(request): | ||||
|     ''' Lists all of the reports currently available. ''' | ||||
|  | @ -48,20 +56,27 @@ def reports_list(request): | |||
| 
 | ||||
| # Report functions | ||||
| 
 | ||||
| @report_view("Reconcilitation") | ||||
| def reconciliation(request, form): | ||||
|     ''' Shows the summary of sales, and the full history of payments and | ||||
|     refunds into the system. ''' | ||||
| 
 | ||||
| @report_view("Paid items", form_type=forms.ProductAndCategoryForm) | ||||
| def items_sold(request, form): | ||||
|     return [ | ||||
|         sales_payment_summary(), | ||||
|         items_sold(), | ||||
|         payments(), | ||||
|         credit_note_refunds(), | ||||
|     ] | ||||
| 
 | ||||
| 
 | ||||
| def items_sold(): | ||||
|     ''' Summarises the items sold and discounts granted for a given set of | ||||
|     products, or products from categories. ''' | ||||
| 
 | ||||
|     data = None | ||||
|     headings = None | ||||
| 
 | ||||
|     products = form.cleaned_data["product"] | ||||
|     categories = form.cleaned_data["category"] | ||||
| 
 | ||||
|     line_items = commerce.LineItem.objects.filter( | ||||
|         Q(product__in=products) | Q(product__category__in=categories), | ||||
|         invoice__status=commerce.Invoice.STATUS_PAID, | ||||
|     ).select_related("invoice") | ||||
| 
 | ||||
|  | @ -92,17 +107,23 @@ def items_sold(request, form): | |||
|         "(TOTAL)", "--", "--", total_income, | ||||
|     ]) | ||||
| 
 | ||||
|     return ListReport("Paid items", headings, data) | ||||
|     return ListReport("Items sold", headings, data) | ||||
| 
 | ||||
| 
 | ||||
| @report_view("Reconcilitation") | ||||
| def reconciliation(request, form): | ||||
|     ''' Reconciles all sales in the system with the payments in the | ||||
|     system. ''' | ||||
| def sales_payment_summary(): | ||||
|     ''' Summarises paid items and payments. ''' | ||||
| 
 | ||||
|     headings = ["Thing", "Total"] | ||||
|     def value_or_zero(aggregate, key): | ||||
|         return aggregate[key] or 0 | ||||
| 
 | ||||
|     def sum_amount(payment_set): | ||||
|         a = payment_set.values("amount").aggregate(total=Sum("amount")) | ||||
|         return value_or_zero(a, "total") | ||||
| 
 | ||||
|     headings = ["Category", "Total"] | ||||
|     data = [] | ||||
| 
 | ||||
|     # Summarise all sales made (= income.) | ||||
|     sales = commerce.LineItem.objects.filter( | ||||
|         invoice__status=commerce.Invoice.STATUS_PAID, | ||||
|     ).values( | ||||
|  | @ -110,42 +131,63 @@ def reconciliation(request, form): | |||
|     ).aggregate( | ||||
|         total=Sum(F("price") * F("quantity"), output_field=CURRENCY()), | ||||
|     ) | ||||
|     sales = value_or_zero(sales, "total") | ||||
| 
 | ||||
|     data.append(["Paid items", sales["total"]]) | ||||
|     all_payments = sum_amount(commerce.PaymentBase.objects.all()) | ||||
| 
 | ||||
|     payments = commerce.PaymentBase.objects.values( | ||||
|         "amount", | ||||
|     ).aggregate(total=Sum("amount")) | ||||
|     # Manual payments | ||||
|     # Credit notes generated (total) | ||||
|     # Payments made by credit note | ||||
|     # Claimed credit notes | ||||
| 
 | ||||
|     data.append(["Payments", payments["total"]]) | ||||
| 
 | ||||
|     ucn = commerce.CreditNote.unclaimed().values( | ||||
|         "amount" | ||||
|     ).aggregate(total=Sum("amount")) | ||||
| 
 | ||||
|     data.append(["Unclaimed credit notes", 0 - ucn["total"]]) | ||||
|     all_credit_notes = 0 - sum_amount(commerce.CreditNote.objects.all()) | ||||
|     unclaimed_credit_notes = 0 - sum_amount(commerce.CreditNote.unclaimed()) | ||||
|     claimed_credit_notes = sum_amount( | ||||
|         commerce.CreditNoteApplication.objects.all() | ||||
|     ) | ||||
|     refunded_credit_notes = 0 - sum_amount(commerce.CreditNote.refunded()) | ||||
| 
 | ||||
|     data.append(["Items on paid invoices", sales]) | ||||
|     data.append(["All payments", all_payments]) | ||||
|     data.append(["Sales - Payments ", sales - all_payments]) | ||||
|     data.append(["All credit notes", all_credit_notes]) | ||||
|     data.append(["Credit notes paid on invoices", claimed_credit_notes]) | ||||
|     data.append(["Credit notes refunded", refunded_credit_notes]) | ||||
|     data.append(["Unclaimed credit notes", unclaimed_credit_notes]) | ||||
|     data.append([ | ||||
|         "(Money not on invoices)", | ||||
|         sales["total"] - payments["total"] - ucn["total"], | ||||
|         "Credit notes - (claimed credit notes + unclaimed credit notes)", | ||||
|         all_credit_notes - claimed_credit_notes - | ||||
|             refunded_credit_notes - unclaimed_credit_notes, | ||||
|     ]) | ||||
| 
 | ||||
|     return ListReport("Sales and Payments", headings, data) | ||||
|     return ListReport("Sales and Payments Summary", headings, data) | ||||
| 
 | ||||
| 
 | ||||
| @report_view("Product status", form_type=forms.ProductAndCategoryForm) | ||||
| def product_status(request, form): | ||||
|     ''' Summarises the inventory status of the given items, grouping by | ||||
|     invoice status. ''' | ||||
| def payments(): | ||||
|     ''' Shows the history of payments into the system ''' | ||||
| 
 | ||||
|     products = form.cleaned_data["product"] | ||||
|     categories = form.cleaned_data["category"] | ||||
|     payments = commerce.PaymentBase.objects.all() | ||||
|     return QuerysetReport( | ||||
|         "Payments", | ||||
|         ["invoice__id", "id", "reference", "amount"], | ||||
|         payments, | ||||
|         link_view=views.invoice, | ||||
|     ) | ||||
| 
 | ||||
|     items = commerce.ProductItem.objects.filter( | ||||
|         Q(product__in=products) | Q(product__category__in=categories), | ||||
|     ).select_related("cart", "product") | ||||
| 
 | ||||
|     items = items.annotate( | ||||
| def credit_note_refunds(): | ||||
|     ''' Shows all of the credit notes that have been generated. ''' | ||||
|     notes_refunded = commerce.CreditNote.refunded() | ||||
|     return QuerysetReport( | ||||
|         "Credit note refunds", | ||||
|         ["id", "creditnoterefund__reference", "amount"], | ||||
|         notes_refunded, | ||||
|         link_view=views.credit_note, | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def group_by_cart_status(queryset, order, values): | ||||
|     queryset = queryset.annotate( | ||||
|         is_reserved=Case( | ||||
|             When(cart__in=commerce.Cart.reserved_carts(), then=Value(True)), | ||||
|             default=Value(False), | ||||
|  | @ -153,14 +195,8 @@ def product_status(request, form): | |||
|         ), | ||||
|     ) | ||||
| 
 | ||||
|     items = items.order_by( | ||||
|         "product__category__order", | ||||
|         "product__order", | ||||
|     ).values( | ||||
|         "product", | ||||
|         "product__category__name", | ||||
|         "product__name", | ||||
|     ).annotate( | ||||
|     values = queryset.order_by(*order).values(*values) | ||||
|     values = values.annotate( | ||||
|         total_paid=Sum(Case( | ||||
|             When( | ||||
|                 cart__status=commerce.Cart.STATUS_PAID, | ||||
|  | @ -197,6 +233,27 @@ def product_status(request, form): | |||
|         )), | ||||
|     ) | ||||
| 
 | ||||
|     return values | ||||
| 
 | ||||
| 
 | ||||
| @report_view("Product status", form_type=forms.ProductAndCategoryForm) | ||||
| def product_status(request, form): | ||||
|     ''' Summarises the inventory status of the given items, grouping by | ||||
|     invoice status. ''' | ||||
| 
 | ||||
|     products = form.cleaned_data["product"] | ||||
|     categories = form.cleaned_data["category"] | ||||
| 
 | ||||
|     items = commerce.ProductItem.objects.filter( | ||||
|         Q(product__in=products) | Q(product__category__in=categories), | ||||
|     ).select_related("cart", "product") | ||||
| 
 | ||||
|     items = group_by_cart_status( | ||||
|         items, | ||||
|         ["product__category__order", "product__order"], | ||||
|         ["product", "product__category__name", "product__name"], | ||||
|     ) | ||||
| 
 | ||||
|     headings = [ | ||||
|         "Product", "Paid", "Reserved", "Unreserved", "Refunded", | ||||
|     ] | ||||
|  | @ -216,6 +273,77 @@ def product_status(request, form): | |||
|     return ListReport("Inventory", headings, data) | ||||
| 
 | ||||
| 
 | ||||
| @report_view("Product status", form_type=forms.DiscountForm) | ||||
| def discount_status(request, form): | ||||
|     ''' Summarises the usage of a given discount. ''' | ||||
| 
 | ||||
|     discounts = form.cleaned_data["discount"] | ||||
| 
 | ||||
|     items = commerce.DiscountItem.objects.filter( | ||||
|         Q(discount__in=discounts), | ||||
|     ).select_related("cart", "product", "product__category") | ||||
| 
 | ||||
|     items = group_by_cart_status( | ||||
|         items, | ||||
|         ["discount",], | ||||
|         ["discount", "discount__description",], | ||||
|     ) | ||||
| 
 | ||||
|     headings = [ | ||||
|         "Discount", "Paid", "Reserved", "Unreserved", "Refunded", | ||||
|     ] | ||||
|     data = [] | ||||
| 
 | ||||
|     for item in items: | ||||
|         data.append([ | ||||
|             item["discount__description"], | ||||
|             item["total_paid"], | ||||
|             item["total_reserved"], | ||||
|             item["total_unreserved"], | ||||
|             item["total_refunded"], | ||||
|         ]) | ||||
| 
 | ||||
|     return ListReport("Usage by item", headings, data) | ||||
| 
 | ||||
| 
 | ||||
| @report_view("Paid invoices by date", form_type=forms.ProductAndCategoryForm) | ||||
| def paid_invoices_by_date(request, form): | ||||
|     ''' Shows the number of paid invoices containing given products or | ||||
|     categories per day. ''' | ||||
| 
 | ||||
|     products = form.cleaned_data["product"] | ||||
|     categories = form.cleaned_data["category"] | ||||
| 
 | ||||
|     invoices = commerce.Invoice.objects.filter( | ||||
|         Q(lineitem__product__in=products) | Q(lineitem__product__category__in=categories), | ||||
|         status=commerce.Invoice.STATUS_PAID, | ||||
|     ) | ||||
| 
 | ||||
|     payments = commerce.PaymentBase.objects.all() | ||||
|     payments = payments.filter( | ||||
|         invoice__in=invoices, | ||||
|     ) | ||||
|     payments = payments.order_by("invoice") | ||||
|     invoice_max_time = payments.values("invoice").annotate(max_time=Max("time")) | ||||
| 
 | ||||
|     by_date = collections.defaultdict(int) | ||||
| 
 | ||||
|     for line in invoice_max_time: | ||||
|         time = line["max_time"] | ||||
|         date = datetime.datetime( | ||||
|             year=time.year, month=time.month, day=time.day | ||||
|         ) | ||||
|         by_date[date] += 1 | ||||
| 
 | ||||
|     data = [(date, count) for date, count in sorted(by_date.items())] | ||||
|     data = [(date.strftime("%Y-%m-%d"), count) for date, count in data] | ||||
| 
 | ||||
|     return ListReport( | ||||
|         "Paid Invoices By Date", | ||||
|         ["date", "count"], | ||||
|         data, | ||||
|     ) | ||||
| 
 | ||||
| @report_view("Credit notes") | ||||
| def credit_notes(request, form): | ||||
|     ''' Shows all of the credit notes in the system. ''' | ||||
|  | @ -236,6 +364,12 @@ def credit_notes(request, form): | |||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| class AttendeeListReport(ListReport): | ||||
| 
 | ||||
|     def get_link(self, argument): | ||||
|         return reverse(self._link_view) + "?user=%d" % int(argument) | ||||
| 
 | ||||
| 
 | ||||
| @report_view("Attendee", form_type=forms.UserIdForm) | ||||
| def attendee(request, form, user_id=None): | ||||
|     ''' Returns a list of all manifested attendees if no attendee is specified, | ||||
|  | @ -252,6 +386,22 @@ def attendee(request, form, user_id=None): | |||
| 
 | ||||
|     reports = [] | ||||
| 
 | ||||
|     profile_data = [] | ||||
|     profile = people.AttendeeProfileBase.objects.get_subclass( | ||||
|         attendee=attendee | ||||
|     ) | ||||
|     exclude = set(["attendeeprofilebase_ptr", "id"]) | ||||
|     for field in profile._meta.get_fields(): | ||||
|         if field.name in exclude: | ||||
|             # Not actually important | ||||
|             continue | ||||
|         if not hasattr(field, "verbose_name"): | ||||
|             continue  # Not a publicly visible field | ||||
|         value = getattr(profile, field.name) | ||||
|         profile_data.append((field.verbose_name, value)) | ||||
| 
 | ||||
|     reports.append(ListReport("Profile", ["", ""], profile_data)) | ||||
| 
 | ||||
|     links = [] | ||||
|     links.append(( | ||||
|         reverse(views.amend_registration, args=[user_id]), | ||||
|  | @ -340,9 +490,98 @@ def attendee_list(request): | |||
|     # Sort by whether they've registered, then ID. | ||||
|     data.sort(key=lambda a: (-a[3], a[0])) | ||||
| 
 | ||||
|     class Report(ListReport): | ||||
|     return AttendeeListReport("Attendees", headings, data, link_view=attendee) | ||||
| 
 | ||||
|         def get_link(self, argument): | ||||
|             return reverse(self._link_view) + "?user=%d" % int(argument) | ||||
| 
 | ||||
|     return Report("Attendees", headings, data, link_view=attendee) | ||||
| ProfileForm = forms.model_fields_form_factory(AttendeeProfile) | ||||
| class ProductCategoryProfileForm(forms.ProductAndCategoryForm, ProfileForm): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| @report_view( | ||||
|     "Attendees By Product/Category", | ||||
|     form_type=ProductCategoryProfileForm, | ||||
| ) | ||||
| def attendee_data(request, form, user_id=None): | ||||
|     ''' Lists attendees for a given product/category selection along with | ||||
|     profile data.''' | ||||
| 
 | ||||
|     status_display = { | ||||
|         commerce.Cart.STATUS_ACTIVE: "Unpaid", | ||||
|         commerce.Cart.STATUS_PAID: "Paid", | ||||
|         commerce.Cart.STATUS_RELEASED: "Refunded", | ||||
|     } | ||||
| 
 | ||||
|     output = [] | ||||
| 
 | ||||
|     products = form.cleaned_data["product"] | ||||
|     categories = form.cleaned_data["category"] | ||||
|     fields = form.cleaned_data["fields"] | ||||
|     name_field = AttendeeProfile.name_field() | ||||
| 
 | ||||
|     items = commerce.ProductItem.objects.filter( | ||||
|         Q(product__in=products) | Q(product__category__in=categories), | ||||
|     ).exclude( | ||||
|         cart__status=commerce.Cart.STATUS_RELEASED | ||||
|     ).select_related( | ||||
|         "cart", "product" | ||||
|     ).order_by("cart__status") | ||||
| 
 | ||||
|     # Get all of the relevant attendee profiles in one hit. | ||||
|     profiles = AttendeeProfile.objects.filter( | ||||
|         attendee__user__cart__productitem__in=items | ||||
|     ).select_related("attendee__user") | ||||
|     by_user = {} | ||||
|     for profile in profiles: | ||||
|         by_user[profile.attendee.user] = profile | ||||
| 
 | ||||
|     for field in fields: | ||||
|         field_verbose = AttendeeProfile._meta.get_field(field).verbose_name | ||||
| 
 | ||||
|         cart = "attendee__user__cart" | ||||
|         cart_status = cart + "__status" | ||||
|         product = cart + "__productitem__product" | ||||
|         product_name = product + "__name" | ||||
|         category_name = product + "__category__name" | ||||
| 
 | ||||
|         p = profiles.order_by(product, field).values( | ||||
|             cart_status, product, product_name, category_name, field | ||||
|         ).annotate(count=Count("id")) | ||||
|         output.append(ListReport( | ||||
|             "Grouped by %s" % field_verbose, | ||||
|             ["Product", "Status", field_verbose, "count"], | ||||
|             [ | ||||
|                 ( | ||||
|                     "%s - %s" % (i[category_name], i[product_name]), | ||||
|                     status_display[i[cart_status]], | ||||
|                     i[field], | ||||
|                     i["count"] or 0, | ||||
|                 ) | ||||
|                 for i in p | ||||
|             ], | ||||
|         )) | ||||
| 
 | ||||
|     # DO the report for individual attendees | ||||
| 
 | ||||
|     field_names = [ | ||||
|         AttendeeProfile._meta.get_field(field).verbose_name for field in fields | ||||
|     ] | ||||
| 
 | ||||
|     headings = ["User ID", "Name", "Product", "Item Status"] + field_names | ||||
|     data = [] | ||||
|     for item in items: | ||||
|         profile = by_user[item.cart.user] | ||||
|         line = [ | ||||
|             item.cart.user.id, | ||||
|             getattr(profile, name_field), | ||||
|             item.product, | ||||
|             status_display[item.cart.status], | ||||
|         ] + [ | ||||
|             getattr(profile, field) for field in fields | ||||
|         ] | ||||
|         data.append(line) | ||||
| 
 | ||||
|     output.append(AttendeeListReport( | ||||
|         "Attendees by item with profile data", headings, data, link_view=attendee | ||||
|     )) | ||||
|     return output | ||||
|  |  | |||
|  | @ -43,9 +43,15 @@ public = [ | |||
| reports = [ | ||||
|     url(r"^$", rv.reports_list, name="reports_list"), | ||||
|     url(r"^attendee/?$", rv.attendee, name="attendee"), | ||||
|     url(r"^attendee_data/?$", rv.attendee_data, name="attendee_data"), | ||||
|     url(r"^attendee/([0-9]*)$", rv.attendee, name="attendee"), | ||||
|     url(r"^credit_notes/?$", rv.credit_notes, name="credit_notes"), | ||||
|     url(r"^items_sold/?$", rv.items_sold, name="items_sold"), | ||||
|     url(r"^discount_status/?$", rv.discount_status, name="discount_status"), | ||||
|     url( | ||||
|         r"^paid_invoices_by_date/?$", | ||||
|         rv.paid_invoices_by_date, | ||||
|         name="paid_invoices_by_date" | ||||
|     ), | ||||
|     url(r"^product_status/?$", rv.product_status, name="product_status"), | ||||
|     url(r"^reconciliation/?$", rv.reconciliation, name="reconciliation"), | ||||
| ] | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import string | ||||
| import sys | ||||
| 
 | ||||
| from django.utils.crypto import get_random_string | ||||
| 
 | ||||
|  | @ -55,3 +56,19 @@ def lazy(function, *args, **kwargs): | |||
|         return retval[0] | ||||
| 
 | ||||
|     return evaluate | ||||
| 
 | ||||
| 
 | ||||
| def get_object_from_name(name): | ||||
|     ''' Returns the named object. | ||||
| 
 | ||||
|     Arguments: | ||||
|         name (str): A string of form `package.subpackage.etc.module.property`. | ||||
|             This function will import `package.subpackage.etc.module` and | ||||
|             return `property` from that module. | ||||
| 
 | ||||
|     ''' | ||||
| 
 | ||||
|     dot = name.rindex(".") | ||||
|     mod_name, property_name = name[:dot], name[dot + 1:] | ||||
|     __import__(mod_name) | ||||
|     return getattr(sys.modules[mod_name], property_name) | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import sys | ||||
| import util | ||||
| 
 | ||||
| from registrasion import forms | ||||
| from registrasion import util | ||||
|  | @ -16,6 +17,7 @@ from registrasion.exceptions import CartValidationError | |||
| 
 | ||||
| from collections import namedtuple | ||||
| 
 | ||||
| from django import forms as django_forms | ||||
| from django.conf import settings | ||||
| from django.contrib.auth.decorators import login_required | ||||
| from django.contrib.auth.decorators import user_passes_test | ||||
|  | @ -59,13 +61,6 @@ class GuidedRegistrationSection(_GuidedRegistrationSection): | |||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| def get_form(name): | ||||
|     dot = name.rindex(".") | ||||
|     mod_name, form_name = name[:dot], name[dot + 1:] | ||||
|     __import__(mod_name) | ||||
|     return getattr(sys.modules[mod_name], form_name) | ||||
| 
 | ||||
| 
 | ||||
| @login_required | ||||
| def guided_registration(request): | ||||
|     ''' Goes through the registration process in order, making sure user sees | ||||
|  | @ -274,6 +269,16 @@ def edit_profile(request): | |||
|     return render(request, "registrasion/profile_form.html", data) | ||||
| 
 | ||||
| 
 | ||||
| # Define the attendee profile form, or get a default. | ||||
| try: | ||||
|     ProfileForm = util.get_object_from_name(settings.ATTENDEE_PROFILE_FORM) | ||||
| except: | ||||
|     class ProfileForm(django_forms.ModelForm): | ||||
|         class Meta: | ||||
|             model = util.get_object_from_name(settings.ATTENDEE_PROFILE_MODEL) | ||||
|             exclude = ["attendee"] | ||||
| 
 | ||||
| 
 | ||||
| def _handle_profile(request, prefix): | ||||
|     ''' Returns a profile form instance, and a boolean which is true if the | ||||
|     form was handled. ''' | ||||
|  | @ -287,8 +292,6 @@ def _handle_profile(request, prefix): | |||
|     except ObjectDoesNotExist: | ||||
|         profile = None | ||||
| 
 | ||||
|     ProfileForm = get_form(settings.ATTENDEE_PROFILE_FORM) | ||||
| 
 | ||||
|     # Load a pre-entered name from the speaker's profile, | ||||
|     # if they have one. | ||||
|     try: | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Christopher Neugebauer
						Christopher Neugebauer