pull/27/head
JamesRamm 2017-02-26 22:37:44 +00:00
rodzic 8fb63a71cb
commit bf643d5606
8 zmienionych plików z 196 dodań i 21 usunięć

BIN
docs/_static/images/dashboard.png vendored 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 113 KiB

Wyświetl plik

@ -0,0 +1,8 @@
from longclaw.longclawsettings.models import LongclawSettings
def currency(request):
settings = LongclawSettings.for_site(request.site)
return {
'currency_html_code': settings.currency_html_code,
'currency': settings.currency
}

Wyświetl plik

@ -1,10 +1,11 @@
''' '''
Various stats/analysis calculations Various stats/analysis calculations
''' '''
import itertools
import calendar import calendar
from datetime import datetime from datetime import datetime
from django.db.models import Q from django.db.models import Q, Sum, F
from longclaw.longclaworders.models import Order from longclaw.longclaworders.models import Order, OrderItem
def current_month(): def current_month():
@ -24,3 +25,24 @@ def sales_for_time_period(from_date, to_date):
).exclude(status=Order.CANCELLED) ).exclude(status=Order.CANCELLED)
return sales return sales
def daily_sales(from_date, to_date):
sales = sales_for_time_period(from_date, to_date)
grouped = itertools.groupby(sales, lambda order: order.payment_date.strftime("%Y-%m-%d"))
return grouped
def sales_by_product(from_date, to_date):
sales = OrderItem.objects.filter(
Q(order__payment_date__lte=to_date) & Q(order__payment_date__gte=from_date)
).exclude(
order__status=Order.CANCELLED
).annotate(
title=F('product__product__title')
).values(
'title'
).annotate(
quantity=Sum('quantity')
).order_by('-quantity')
return sales

Wyświetl plik

@ -1,5 +0,0 @@
<li class="icon icon-doc-empty-inverse">
<a href="{{ url }}">
<span>{{ total }}</span> {{ text }}
</a>
</li>

Wyświetl plik

@ -0,0 +1,97 @@
{% load i18n wagtailadmin_tags %}
<section class="panel nice-padding">
<div class="col6">
<canvas id="daily-sales" width="100" height="75"></canvas>
<br>
</div>
<div class="col6">
<canvas id="popular-products" width="100" height="75"></canvas>
<br>
</div>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.bundle.min.js"></script>
<script>
var ctx = document.getElementById("daily-sales");
var data = {
labels: {{ labels|safe }},
datasets: [
{
data: {{ daily_income }}
}
]
};
var dailySalesChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
title: {
display: true,
text: 'Revenue this month'
},
legend: {
display: false
},
scales: {
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Date'
}
}],
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Revenue ({{currency}})'
}
}]
}
}
});
var ctx_products = document.getElementById("popular-products");
var data = {
labels: {{ product_labels|safe }},
datasets: [
{
data: {{ sales_volume }}
}
]
};
var popProductsChart = new Chart(ctx_products, {
type: 'bar',
data: data,
options: {
title: {
display: true,
text: 'Popular products this month'
},
scales: {
yAxes: [{
ticks: {
beginAtZero:true
},
scaleLabel: {
display: true,
labelString: 'No. Sales'
}
}],
xAxes: [{
scaleLabel: {
display: true,
labelString: 'Product Title'
}
}]
},
legend: {
display: false
}
}
})
</script>

Wyświetl plik

@ -0,0 +1,5 @@
<li class="icon {{ icon }}">
<a href="{{ url }}">
<span>{{ total|safe }}</span> {{ text }}
</a>
</li>

Wyświetl plik

@ -1,47 +1,94 @@
import datetime
from wagtail.wagtailcore import hooks from wagtail.wagtailcore import hooks
from wagtail.wagtailadmin.site_summary import SummaryItem from wagtail.wagtailadmin.site_summary import SummaryItem
from longclaw.longclaworders.models import Order from longclaw.longclaworders.models import Order
from longclaw.longclawproducts.models import Product from longclaw.longclawproducts.models import Product
from longclaw.longclawstats import stats from longclaw.longclawstats import stats
from longclaw.longclawsettings.models import LongclawSettings
class OutstandingOrdersSummary(SummaryItem):
class LongclawSummaryItem(SummaryItem):
order = 10
template = 'longclawstats/summary_item.html'
def get_context(self):
return {
'total': 0,
'text': '',
'url': '',
'icon': 'icon-doc-empty-inverse'
}
class OutstandingOrders(LongclawSummaryItem):
order = 10 order = 10
template = 'longclawhome/summary_item.html'
def get_context(self): def get_context(self):
orders = Order.objects.filter(status=Order.SUBMITTED) orders = Order.objects.filter(status=Order.SUBMITTED)
return { return {
'total': orders.count(), 'total': orders.count(),
'text': 'Outstanding Orders', 'text': 'Outstanding Orders',
'url': '/admin/longclaworders/order/' 'url': '/admin/longclaworders/order/',
'icon': 'icon-warning'
} }
class ProductCount(SummaryItem): class ProductCount(LongclawSummaryItem):
order = 20 order = 20
template = 'longclawhome/summary_item.html'
def get_context(self): def get_context(self):
return { return {
'total': Product.objects.all().count(), 'total': Product.objects.all().count(),
'text': 'Products', 'text': 'Products',
'url': '' 'url': '',
'icon': 'icon-list-ul'
} }
class MonthlySales(SummaryItem): class MonthlySales(LongclawSummaryItem):
order = 30 order = 30
template = 'longclawhome/summary_item.html'
def get_context(self): def get_context(self):
settings = LongclawSettings.for_site(self.request.site)
sales = stats.sales_for_time_period(*stats.current_month()) sales = stats.sales_for_time_period(*stats.current_month())
return { return {
'total': sum(order.total for order in sales), 'total': "{}{}".format(settings.currency_html_code,
'text': 'Sales this month', sum(order.total for order in sales)),
'url': '/admin/longclaworders/order/' 'text': 'In sales this month',
'url': '/admin/longclaworders/order/',
'icon': 'icon-tick'
} }
class LongclawStatsPanel(SummaryItem):
order = 110
template = 'longclawstats/stats_panel.html'
def get_context(self):
month_start, month_end = stats.current_month()
daily_sales = stats.daily_sales(month_start, month_end)
labels = [(month_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')
for x in range(0, datetime.datetime.now().day)]
daily_income = [0] * len(labels)
for k, order_group in daily_sales:
i = labels.index(k)
daily_income[i] = float(sum(order.total for order in order_group))
popular_products = stats.sales_by_product(month_start, month_end)[:5]
print(popular_products)
return {
"daily_income": daily_income,
"labels": labels,
"product_labels": list(popular_products.values_list('title', flat=True)),
"sales_volume": list(popular_products.values_list('quantity', flat=True))
}
@hooks.register('construct_homepage_summary_items') @hooks.register('construct_homepage_summary_items')
def add_longclaw_summary_items(request, items): def add_longclaw_summary_items(request, items):
for item in items:
items.remove(item) # We are going to replace everything with our own items
items[:] = []
items.extend([ items.extend([
OutstandingOrdersSummary(request), OutstandingOrders(request),
ProductCount(request), ProductCount(request),
MonthlySales(request) MonthlySales(request)
]) ])
@hooks.register('construct_homepage_panels')
def add_stats_panel(request, panels):
return panels.append(LongclawStatsPanel(request))

Wyświetl plik

@ -93,6 +93,7 @@ TEMPLATES = [
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'longclaw.longclawsettings.context_processors.currency',
], ],
}, },
}, },