chart.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import calendar
  2. import datetime
  3. import decimal
  4. from django.core.serializers.json import DjangoJSONEncoder
  5. from django.db import models
  6. from django.http import HttpResponse, HttpResponseNotFound
  7. from django.template import loader
  8. from django.utils.http import urlencode
  9. from django.utils.encoding import force_text, smart_text
  10. from django.utils.translation import ugettext_lazy as _, ugettext
  11. from xadmin.plugins.utils import get_context_dict
  12. from xadmin.sites import site
  13. from xadmin.views import BaseAdminPlugin, ListAdminView
  14. from xadmin.views.dashboard import ModelBaseWidget, widget_manager
  15. from xadmin.util import lookup_field, label_for_field, json
  16. @widget_manager.register
  17. class ChartWidget(ModelBaseWidget):
  18. widget_type = 'chart'
  19. description = _('Show models simple chart.')
  20. template = 'xadmin/widgets/chart.html'
  21. widget_icon = 'fa fa-bar-chart-o'
  22. def convert(self, data):
  23. self.list_params = data.pop('params', {})
  24. self.chart = data.pop('chart', None)
  25. def setup(self):
  26. super(ChartWidget, self).setup()
  27. self.charts = {}
  28. self.one_chart = False
  29. model_admin = self.admin_site._registry[self.model]
  30. chart = self.chart
  31. if hasattr(model_admin, 'data_charts'):
  32. if chart and chart in model_admin.data_charts:
  33. self.charts = {chart: model_admin.data_charts[chart]}
  34. self.one_chart = True
  35. if self.title is None:
  36. self.title = model_admin.data_charts[chart].get('title')
  37. else:
  38. self.charts = model_admin.data_charts
  39. if self.title is None:
  40. self.title = ugettext(
  41. "%s Charts") % self.model._meta.verbose_name_plural
  42. def filte_choices_model(self, model, modeladmin):
  43. return bool(getattr(modeladmin, 'data_charts', None)) and \
  44. super(ChartWidget, self).filte_choices_model(model, modeladmin)
  45. def get_chart_url(self, name, v):
  46. return self.model_admin_url('chart', name) + "?" + urlencode(self.list_params)
  47. def context(self, context):
  48. context.update({
  49. 'charts': [{"name": name, "title": v['title'], 'url': self.get_chart_url(name, v)} for name, v in self.charts.items()],
  50. })
  51. # Media
  52. def media(self):
  53. return self.vendor('flot.js', 'xadmin.plugin.charts.js')
  54. class JSONEncoder(DjangoJSONEncoder):
  55. def default(self, o):
  56. if isinstance(o, (datetime.date, datetime.datetime)):
  57. return calendar.timegm(o.timetuple()) * 1000
  58. elif isinstance(o, decimal.Decimal):
  59. return str(o)
  60. else:
  61. try:
  62. return super(JSONEncoder, self).default(o)
  63. except Exception:
  64. return smart_text(o)
  65. class ChartsPlugin(BaseAdminPlugin):
  66. data_charts = {}
  67. def init_request(self, *args, **kwargs):
  68. return bool(self.data_charts)
  69. def get_chart_url(self, name, v):
  70. return self.admin_view.model_admin_url('chart', name) + self.admin_view.get_query_string()
  71. # Media
  72. def get_media(self, media):
  73. return media + self.vendor('flot.js', 'xadmin.plugin.charts.js')
  74. # Block Views
  75. def block_results_top(self, context, nodes):
  76. context.update({
  77. 'charts': [{"name": name, "title": v['title'], 'url': self.get_chart_url(name, v)} for name, v in self.data_charts.items()],
  78. })
  79. nodes.append(loader.render_to_string('xadmin/blocks/model_list.results_top.charts.html',
  80. context=get_context_dict(context)))
  81. class ChartsView(ListAdminView):
  82. data_charts = {}
  83. def get_ordering(self):
  84. if 'order' in self.chart:
  85. return self.chart['order']
  86. else:
  87. return super(ChartsView, self).get_ordering()
  88. def get(self, request, name):
  89. if name not in self.data_charts:
  90. return HttpResponseNotFound()
  91. self.chart = self.data_charts[name]
  92. self.x_field = self.chart['x-field']
  93. y_fields = self.chart['y-field']
  94. self.y_fields = (
  95. y_fields,) if type(y_fields) not in (list, tuple) else y_fields
  96. datas = [{"data":[], "label": force_text(label_for_field(
  97. i, self.model, model_admin=self))} for i in self.y_fields]
  98. self.make_result_list()
  99. for obj in self.result_list:
  100. xf, attrs, value = lookup_field(self.x_field, obj, self)
  101. for i, yfname in enumerate(self.y_fields):
  102. yf, yattrs, yv = lookup_field(yfname, obj, self)
  103. datas[i]["data"].append((value, yv))
  104. option = {'series': {'lines': {'show': True}, 'points': {'show': False}},
  105. 'grid': {'hoverable': True, 'clickable': True}}
  106. try:
  107. xfield = self.opts.get_field(self.x_field)
  108. if type(xfield) in (models.DateTimeField, models.DateField, models.TimeField):
  109. option['xaxis'] = {'mode': "time", 'tickLength': 5}
  110. if type(xfield) is models.DateField:
  111. option['xaxis']['timeformat'] = "%y/%m/%d"
  112. elif type(xfield) is models.TimeField:
  113. option['xaxis']['timeformat'] = "%H:%M:%S"
  114. else:
  115. option['xaxis']['timeformat'] = "%y/%m/%d %H:%M:%S"
  116. except Exception:
  117. pass
  118. option.update(self.chart.get('option', {}))
  119. content = {'data': datas, 'option': option}
  120. result = json.dumps(content, cls=JSONEncoder, ensure_ascii=False)
  121. return HttpResponse(result)
  122. site.register_plugin(ChartsPlugin, ListAdminView)
  123. site.register_modelview(r'^chart/(.+)/$', ChartsView, name='%s_%s_chart')