You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
5.6 KiB
161 lines
5.6 KiB
|
|
import calendar
|
|
import datetime
|
|
import decimal
|
|
|
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
from django.db import models
|
|
from django.http import HttpResponse, HttpResponseNotFound
|
|
from django.template import loader
|
|
from django.utils.http import urlencode
|
|
from django.utils.encoding import force_text, smart_text
|
|
from django.utils.translation import ugettext_lazy as _, ugettext
|
|
|
|
from xadmin.plugins.utils import get_context_dict
|
|
from xadmin.sites import site
|
|
from xadmin.views import BaseAdminPlugin, ListAdminView
|
|
from xadmin.views.dashboard import ModelBaseWidget, widget_manager
|
|
from xadmin.util import lookup_field, label_for_field, json
|
|
|
|
|
|
@widget_manager.register
|
|
class ChartWidget(ModelBaseWidget):
|
|
widget_type = 'chart'
|
|
description = _('Show models simple chart.')
|
|
template = 'xadmin/widgets/chart.html'
|
|
widget_icon = 'fa fa-bar-chart-o'
|
|
|
|
def convert(self, data):
|
|
self.list_params = data.pop('params', {})
|
|
self.chart = data.pop('chart', None)
|
|
|
|
def setup(self):
|
|
super(ChartWidget, self).setup()
|
|
|
|
self.charts = {}
|
|
self.one_chart = False
|
|
model_admin = self.admin_site._registry[self.model]
|
|
chart = self.chart
|
|
|
|
if hasattr(model_admin, 'data_charts'):
|
|
if chart and chart in model_admin.data_charts:
|
|
self.charts = {chart: model_admin.data_charts[chart]}
|
|
self.one_chart = True
|
|
if self.title is None:
|
|
self.title = model_admin.data_charts[chart].get('title')
|
|
else:
|
|
self.charts = model_admin.data_charts
|
|
if self.title is None:
|
|
self.title = ugettext(
|
|
"%s Charts") % self.model._meta.verbose_name_plural
|
|
|
|
def filte_choices_model(self, model, modeladmin):
|
|
return bool(getattr(modeladmin, 'data_charts', None)) and \
|
|
super(ChartWidget, self).filte_choices_model(model, modeladmin)
|
|
|
|
def get_chart_url(self, name, v):
|
|
return self.model_admin_url('chart', name) + "?" + urlencode(self.list_params)
|
|
|
|
def context(self, context):
|
|
context.update({
|
|
'charts': [{"name": name, "title": v['title'], 'url': self.get_chart_url(name, v)} for name, v in self.charts.items()],
|
|
})
|
|
|
|
# Media
|
|
def media(self):
|
|
return self.vendor('flot.js', 'xadmin.plugin.charts.js')
|
|
|
|
|
|
class JSONEncoder(DjangoJSONEncoder):
|
|
def default(self, o):
|
|
if isinstance(o, (datetime.date, datetime.datetime)):
|
|
return calendar.timegm(o.timetuple()) * 1000
|
|
elif isinstance(o, decimal.Decimal):
|
|
return str(o)
|
|
else:
|
|
try:
|
|
return super(JSONEncoder, self).default(o)
|
|
except Exception:
|
|
return smart_text(o)
|
|
|
|
|
|
class ChartsPlugin(BaseAdminPlugin):
|
|
|
|
data_charts = {}
|
|
|
|
def init_request(self, *args, **kwargs):
|
|
return bool(self.data_charts)
|
|
|
|
def get_chart_url(self, name, v):
|
|
return self.admin_view.model_admin_url('chart', name) + self.admin_view.get_query_string()
|
|
|
|
# Media
|
|
def get_media(self, media):
|
|
return media + self.vendor('flot.js', 'xadmin.plugin.charts.js')
|
|
|
|
# Block Views
|
|
def block_results_top(self, context, nodes):
|
|
context.update({
|
|
'charts': [{"name": name, "title": v['title'], 'url': self.get_chart_url(name, v)} for name, v in self.data_charts.items()],
|
|
})
|
|
nodes.append(loader.render_to_string('xadmin/blocks/model_list.results_top.charts.html',
|
|
context=get_context_dict(context)))
|
|
|
|
|
|
class ChartsView(ListAdminView):
|
|
|
|
data_charts = {}
|
|
|
|
def get_ordering(self):
|
|
if 'order' in self.chart:
|
|
return self.chart['order']
|
|
else:
|
|
return super(ChartsView, self).get_ordering()
|
|
|
|
def get(self, request, name):
|
|
if name not in self.data_charts:
|
|
return HttpResponseNotFound()
|
|
|
|
self.chart = self.data_charts[name]
|
|
|
|
self.x_field = self.chart['x-field']
|
|
y_fields = self.chart['y-field']
|
|
self.y_fields = (
|
|
y_fields,) if type(y_fields) not in (list, tuple) else y_fields
|
|
|
|
datas = [{"data":[], "label": force_text(label_for_field(
|
|
i, self.model, model_admin=self))} for i in self.y_fields]
|
|
|
|
self.make_result_list()
|
|
|
|
for obj in self.result_list:
|
|
xf, attrs, value = lookup_field(self.x_field, obj, self)
|
|
for i, yfname in enumerate(self.y_fields):
|
|
yf, yattrs, yv = lookup_field(yfname, obj, self)
|
|
datas[i]["data"].append((value, yv))
|
|
|
|
option = {'series': {'lines': {'show': True}, 'points': {'show': False}},
|
|
'grid': {'hoverable': True, 'clickable': True}}
|
|
try:
|
|
xfield = self.opts.get_field(self.x_field)
|
|
if type(xfield) in (models.DateTimeField, models.DateField, models.TimeField):
|
|
option['xaxis'] = {'mode': "time", 'tickLength': 5}
|
|
if type(xfield) is models.DateField:
|
|
option['xaxis']['timeformat'] = "%y/%m/%d"
|
|
elif type(xfield) is models.TimeField:
|
|
option['xaxis']['timeformat'] = "%H:%M:%S"
|
|
else:
|
|
option['xaxis']['timeformat'] = "%y/%m/%d %H:%M:%S"
|
|
except Exception:
|
|
pass
|
|
|
|
option.update(self.chart.get('option', {}))
|
|
|
|
content = {'data': datas, 'option': option}
|
|
result = json.dumps(content, cls=JSONEncoder, ensure_ascii=False)
|
|
|
|
return HttpResponse(result)
|
|
|
|
site.register_plugin(ChartsPlugin, ListAdminView)
|
|
site.register_modelview(r'^chart/(.+)/$', ChartsView, name='%s_%s_chart')
|