Changeset - 6fb680931627
[Not reviewed]
0 4 0
Joel Addison - 5 years ago 2019-10-13 12:22:15
joel@addison.net.au
Improve registration report formatting

Show title in list instead of method name.
Add page title and head title to reports.
4 files changed with 11 insertions and 3 deletions:
0 comments (0 inline, 0 general)
pinaxcon/templates/registrasion/report.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load bootstrap %}
 
{% load registrasion_tags %}
 

	
 
{% block page_title %}Registration report{% endblock %}
 
{% block head_title %}Registration report - {{ title }}{% endblock %}
 

	
 
{% block content %}
 

	
 
  <h2>{{ title }}</h2>
 

	
 
  <p><a href="{% url 'reports_list' %}">Return to reports list</a></p>
 

	
 
  {% if form %}
 
    <form class="form-horizontal" method="GET">
 
      {{ form | bootstrap}}
 
      <br/>
 
      <input class="btn btn-primary" type="submit">
 
    </form>
 
  {% endif %}
 
<hr />
 

	
 
{% for report in reports %}
 
<h3>{{ report.title }}</h3>
 
{% if report.headings %}
 
<table class="table table-striped table-reportdata">
 
{% else %}
 
    <table class="table table-striped">
 
{% endif %}
 
    <thead>
 
        <tr>
 
            {% for heading in report.headings %}
 
            <th>{{ heading }}</th>
 
            {% endfor %}
 
        </tr>
 
    </thead>
 
    <tbody>
 
        {% for line in report.rows %}
 
        <tr>
 
            {% for item in line %}
 
            <td>
 
                {{ item|safe }}
 
            </td>
 
            {% endfor %}
 
        </tr>
 
        {% endfor %}
 
    </tbody>
 
    </table>
 

	
 
<hr />
 
{% endfor %}
 

	
 
{% endblock %}
 

	
 
{% block extra_script %}
pinaxcon/templates/registrasion/reports_list.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load registrasion_tags %}
 
{% block content %}
 

	
 
<h2>Registration reports</h2>
 
{% block page_title %}Registration reports{% endblock %}
 
{% block head_title %}Registration reports{% endblock %}
 

	
 
{% block content %}
 
<table class="table table-striped">
 
  {% for report in reports %}
 
    <tr>
 
      <td>
 
        <a href="{{ report.url }}">{{ report.name }}</a>
 
        <a href="{{ report.url }}">{{ report.title }}</a>
 
      </td>
 
      <td>
 
        {{ report.description }}
 
      </td>
 
    </tr>
 
  {% endfor %}
 
</table>
 

	
 
{% endblock %}
vendor/registrasion/registrasion/reporting/reports.py
Show inline comments
...
 
@@ -33,211 +33,214 @@ class Report(object):
 
            content_type (str): The content-type for the output format of this
 
            report.
 

	
 
        Returns:
 
            An iterator, which yields each row of the data. Each row should
 
            be an iterable containing the cells, rendered appropriately for
 
            content_type.
 
        '''
 
        raise NotImplementedError
 

	
 
    def _linked_text(self, content_type, address, text):
 
        '''
 

	
 
        Returns:
 
            an HTML linked version of text, if the content_type for this report
 
            is HTMLish, otherwise, the text.
 
        '''
 

	
 
        if content_type == "text/html":
 
            return Report._html_link(address, text)
 
        else:
 
            return text
 

	
 
    @staticmethod
 
    def _html_link(address, text):
 
        return '<a href="%s">%s</a>' % (address, text)
 

	
 

	
 
class _ReportTemplateWrapper(object):
 
    ''' Used internally to pass `Report` objects to templates. They effectively
 
    are used to specify the content_type for a report. '''
 

	
 
    def __init__(self, content_type, report):
 
        self.content_type = content_type
 
        self.report = report
 

	
 
    def title(self):
 
        return self.report.title()
 

	
 
    def headings(self):
 
        return self.report.headings()
 

	
 
    def rows(self):
 
        return self.report.rows(self.content_type)
 

	
 
    def count(self):
 
        return self.report.count()
 

	
 

	
 
class BasicReport(Report):
 

	
 
    def __init__(self, title, headings, link_view=None):
 
        super(BasicReport, self).__init__()
 
        self._title = title
 
        self._headings = headings
 
        self._link_view = link_view
 

	
 
    def title(self):
 
        ''' Returns the title for this report. '''
 
        return self._title
 

	
 
    def headings(self):
 
        ''' Returns the headings for the table. '''
 
        return self._headings
 

	
 
    def cell_text(self, content_type, index, text):
 
        if index > 0 or not self._link_view:
 
            return text
 
        else:
 
            address = self.get_link(text)
 
            return self._linked_text(content_type, address, text)
 

	
 
    def get_link(self, argument):
 
        return reverse(self._link_view, args=[argument])
 

	
 

	
 
class ListReport(BasicReport):
 

	
 
    def __init__(self, title, headings, data, link_view=None):
 
        super(ListReport, self).__init__(title, headings, link_view=link_view)
 
        self._data = data
 

	
 
    def rows(self, content_type):
 
        ''' Returns the data rows for the table. '''
 

	
 
        for row in self._data:
 
            yield [
 
                self.cell_text(content_type, i, cell)
 
                for i, cell in enumerate(row)
 
            ]
 

	
 
    def count(self):
 
        return len(self._data)
 

	
 

	
 
class QuerysetReport(BasicReport):
 

	
 
    def __init__(self, title, attributes, queryset, headings=None,
 
                 link_view=None):
 
        super(QuerysetReport, self).__init__(
 
            title, headings, link_view=link_view
 
        )
 
        self._attributes = attributes
 
        self._queryset = queryset
 

	
 
    def headings(self):
 
        if self._headings is not None:
 
            return self._headings
 

	
 
        return [
 
            " ".join(i.split("_")).capitalize() for i in self._attributes
 
        ]
 

	
 
    def rows(self, content_type):
 

	
 
        def rgetattr(item, attr):
 
            for i in attr.split("__"):
 
                item = getattr(item, i)
 

	
 
            if callable(item):
 
                try:
 
                    return item()
 
                except TypeError:
 
                    pass
 

	
 
            return item
 

	
 
        for row in self._queryset:
 
            yield [
 
                self.cell_text(content_type, i, rgetattr(row, attribute))
 
                for i, attribute in enumerate(self._attributes)
 
            ]
 

	
 

	
 
    def count(self):
 
        return self._queryset.count()
 

	
 

	
 
class Links(Report):
 

	
 
    def __init__(self, title, links):
 
        '''
 
        Arguments:
 
            links ([tuple, ...]): a list of 2-tuples:
 
                (url, link_text)
 

	
 
        '''
 
        self._title = title
 
        self._links = links
 

	
 
    def title(self):
 
        return self._title
 

	
 
    def headings(self):
 
        return []
 

	
 
    def rows(self, content_type):
 
        for url, link_text in self._links:
 
            yield [
 
                self._linked_text(content_type, url, link_text)
 
            ]
 

	
 
    def count(self):
 
        return len(self._links)
 

	
 

	
 
def report_view(title, form_type=None):
 
    ''' Decorator that converts a report view function into something that
 
    displays a Report.
 

	
 
    Arguments:
 
        title (str):
 
            The title of the report.
 
        form_type (Optional[forms.Form]):
 
            A form class that can make this report display things. If not
 
            supplied, no form will be displayed.
 

	
 
    '''
 

	
 
    # Create & return view
 
    def _report(view):
 
        report_view = ReportView(view, title, form_type)
 
        report_view = user_passes_test(views._staff_only)(report_view)
 
        report_view = wraps(view)(report_view)
 

	
 
        # Add this report to the list of reports.
 
        _all_report_views.append(report_view)
 

	
 
        return report_view
 

	
 
    return _report
 

	
 

	
 
class ReportView(object):
 
    ''' View objects that can render report data into HTML or CSV. '''
 

	
 
    def __init__(self, inner_view, title, form_type):
 
        '''
 

	
 
        Arguments:
 
            inner_view: Callable that returns either a Report or a sequence of
 
                Report objects.
 

	
 
            title: The title that appears at the top of all of the reports.
 

	
 
            form_type: A Form class that can be used to query the report.
 

	
 
        '''
 

	
 
        # Consolidate form_type so it has content type and section
 
        self.inner_view = inner_view
 
        self.title = title
 
        self.form_type = form_type
 

	
vendor/registrasion/registrasion/reporting/views.py
Show inline comments
...
 
@@ -6,96 +6,97 @@ import itertools
 

	
 
from django.conf import settings
 
from django.contrib.auth.decorators import user_passes_test
 
from django.contrib.auth.models import User
 
from django.core.urlresolvers import reverse
 
from django.db import models
 
from django.db.models import F, Q, Subquery, OuterRef
 
from django.db.models import Count, Max, Sum
 
from django.db.models import Case, When, Value
 
from django.db.models.fields.related import RelatedField
 
from django.db.models.fields import CharField
 
from django.shortcuts import render
 

	
 
from registrasion.controllers.cart import CartController
 
from registrasion.controllers.item import ItemController
 
from registrasion.models import conditions
 
from registrasion.models import commerce
 
from registrasion.models import people
 
from registrasion import util
 
from registrasion import views
 

	
 
from symposion.schedule import models as schedule_models
 

	
 
from .reports import get_all_reports
 
from .reports import Links
 
from .reports import ListReport
 
from .reports import QuerysetReport
 
from .reports import report_view
 

	
 
import bleach
 

	
 

	
 
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. '''
 

	
 
    reports = []
 

	
 
    for report in get_all_reports():
 
        reports.append({
 
            "name": report.__name__,
 
            "title": report.title,
 
            "url": reverse(report),
 
            "description": report.__doc__,
 
        })
 

	
 
    reports.sort(key=lambda report: report["name"])
 

	
 
    ctx = {
 
        "reports": reports,
 
    }
 

	
 
    return render(request, "registrasion/reports_list.html", ctx)
 

	
 

	
 
# 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. '''
 

	
 
    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
 

	
 
    line_items = commerce.LineItem.objects.filter(
 
        invoice__status=commerce.Invoice.STATUS_PAID,
 
    ).select_related("invoice")
 

	
 
    line_items = line_items.order_by(
 
        # sqlite requires an order_by for .values() to work
 
        "-price", "description",
 
    ).values(
 
        "price", "description",
 
    ).annotate(
 
        total_quantity=Sum("quantity"),
 
    )
 

	
0 comments (0 inline, 0 general)