forked from CGM_Public/pretix_original
Add daily and cumulative attendee graphs to the order statistics page (#5792)
The order statistics page previously only showed order-based graphs. This change adds attendee-based daily and cumulative graphs.
This commit is contained in:
@@ -19,6 +19,30 @@ $(function () {
|
||||
fillOpacity: 0.3,
|
||||
behaveLikeLine: true
|
||||
});
|
||||
new Morris.Area({
|
||||
element: 'abd_chart',
|
||||
data: JSON.parse($("#abd-data").html()),
|
||||
xkey: 'date',
|
||||
ykeys: ['ordered', 'paid'],
|
||||
labels: [gettext('Attendees (ordered)'), gettext('Attendees (paid)')],
|
||||
lineColors: ['#3b1c4a', '#50a167'],
|
||||
smooth: false,
|
||||
resize: true,
|
||||
fillOpacity: 0.3,
|
||||
behaveLikeLine: true
|
||||
});
|
||||
new Morris.Area({
|
||||
element: 'abt_chart',
|
||||
data: JSON.parse($("#abt-data").html()),
|
||||
xkey: 'date',
|
||||
ykeys: ['ordered', 'paid'],
|
||||
labels: [gettext('Attendees (ordered)'), gettext('Attendees (paid)')],
|
||||
lineColors: ['#3b1c4a', '#50a167'],
|
||||
smooth: false,
|
||||
resize: true,
|
||||
fillOpacity: 0.3,
|
||||
behaveLikeLine: true
|
||||
});
|
||||
new Morris.Area({
|
||||
element: 'rev_chart',
|
||||
data: JSON.parse($("#rev-data").html()),
|
||||
|
||||
@@ -31,6 +31,46 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "Attendees by day" %}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="abd_chart" class="chart"></div>
|
||||
<p class="help-block">
|
||||
<small>
|
||||
{% blocktrans trimmed %}
|
||||
Attendees in orders paid in multiple instalments are shown using the date of the
|
||||
final payment. Order dates reflect when the order was first placed; attendees added
|
||||
later via additional order positions still use the original order date. Attendees in
|
||||
placed orders include those from all order states (pending, paid, cancelled, and
|
||||
expired); attendees in paid orders include only those from paid orders and exclude
|
||||
those from cancelled orders.
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "Attendees by time" %}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="abt_chart" class="chart"></div>
|
||||
<p class="help-block">
|
||||
<small>
|
||||
{% blocktrans trimmed %}
|
||||
Attendees in orders paid in multiple instalments are shown using the date of the
|
||||
final payment. Order dates reflect when the order was first placed; attendees added
|
||||
later via additional order positions still use the original order date. Attendees in
|
||||
placed orders include those from all order states (pending, paid, cancelled, and
|
||||
expired); attendees in paid orders include only those from paid orders and exclude
|
||||
those from cancelled orders.
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "Revenue over time" %}</h3>
|
||||
@@ -177,6 +217,8 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<script type="application/json" id="obd-data">{{ obd_data|escapejson }}</script>
|
||||
<script type="application/json" id="abd-data">{{ abd_data|escapejson }}</script>
|
||||
<script type="application/json" id="abt-data">{{ abt_data|escapejson }}</script>
|
||||
<script type="application/json" id="rev-data">{{ rev_data|escapejson }}</script>
|
||||
<script type="application/json" id="obp-data">{{ obp_data|escapejson }}</script>
|
||||
<script type="application/text" id="currency">{{ request.event.currency }}</script>
|
||||
|
||||
@@ -128,6 +128,52 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
|
||||
ctx['obd_data'] = json.dumps(data)
|
||||
cache.set('statistics_obd_data' + ckey, ctx['obd_data'])
|
||||
|
||||
# Attendees by day/time
|
||||
ctx['abd_data'] = cache.get('statistics_abd_data' + ckey)
|
||||
ctx['abt_data'] = cache.get('statistics_abt_data' + ckey)
|
||||
if not ctx['abd_data'] or not ctx['abt_data']:
|
||||
opqs = OrderPosition.all.filter(order__event=self.request.event, item__admission=True).annotate(
|
||||
payment_date=Subquery(op_date, output_field=DateTimeField())
|
||||
)
|
||||
if subevent:
|
||||
opqs = opqs.filter(subevent=subevent)
|
||||
|
||||
ordered_by_day = {}
|
||||
for p in opqs.values('order__datetime'):
|
||||
day = p['order__datetime'].astimezone(tz).date()
|
||||
ordered_by_day[day] = ordered_by_day.get(day, 0) + 1
|
||||
|
||||
paid_by_day = {}
|
||||
for p in opqs.filter(payment_date__isnull=False, canceled=False, order__status=Order.STATUS_PAID).values('payment_date'):
|
||||
day = p['payment_date'].astimezone(tz).date()
|
||||
paid_by_day[day] = paid_by_day.get(day, 0) + 1
|
||||
|
||||
day_data = []
|
||||
time_data = []
|
||||
for d in dateutil.rrule.rrule(
|
||||
dateutil.rrule.DAILY,
|
||||
dtstart=min(ordered_by_day.keys()) if ordered_by_day else datetime.date.today(),
|
||||
until=max(
|
||||
max(ordered_by_day.keys() if paid_by_day else [datetime.date.today()]),
|
||||
max(paid_by_day.keys() if paid_by_day else [datetime.date(1970, 1, 1)])
|
||||
)):
|
||||
d = d.date()
|
||||
day_data.append({
|
||||
'date': d.strftime('%Y-%m-%d'),
|
||||
'ordered': ordered_by_day.get(d, 0),
|
||||
'paid': paid_by_day.get(d, 0)
|
||||
})
|
||||
time_data.append({
|
||||
'date': d.strftime('%Y-%m-%d'),
|
||||
'ordered': (time_data[-1]["ordered"] if time_data else 0) + ordered_by_day.get(d, 0),
|
||||
'paid': (time_data[-1]["paid"] if time_data else 0) + paid_by_day.get(d, 0)
|
||||
})
|
||||
|
||||
ctx['abd_data'] = json.dumps(day_data)
|
||||
ctx['abt_data'] = json.dumps(time_data)
|
||||
cache.set('statistics_abd_data' + ckey, ctx['abd_data'])
|
||||
cache.set('statistics_abt_data' + ckey, ctx['abt_data'])
|
||||
|
||||
# Orders by product
|
||||
ctx['obp_data'] = cache.get('statistics_obp_data' + ckey)
|
||||
if not ctx['obp_data']:
|
||||
|
||||
Reference in New Issue
Block a user