使用xadmin后功能比较强大,在后台展示统计图表,这个需求真的有点烫手,最终实现效果如下图:

xadmin后台与echarts完全融合遇到以下问题:

  1.没有现成的数据model

  2.获得指定时间段的数据

  3.添加自定义菜单

  4.图表不能在当前页展示(后台点击每个model都是内嵌在当前页)

  5.echarts动态展示数据


下面解决第一个问题:

  目前现状是得从一个千万级的大表里提取近12个月,近30天,近24小时3个时间维度的数据,同事建议使用中间表,于是乎建了3个。

  model如下:

# 定义发送短信按小时统计模型类
class Count24(models.Model):
alia_day_time = models.CharField(
max_length=20,
verbose_name='年月'
)
total_nums = models.IntegerField(
default=0,
verbose_name='发送总数'
)
error_nums = models.IntegerField(
default=0,
verbose_name='失败总数'
) class Meta:
verbose_name = verbose_name_plural = '短信发送按小时统计' def __str__(self):
return '{0}: {1} {2}'.format(self.alia_day_time, self.total_nums,self.error_nums) # 定义机构发送短信统计模型类
class OrganizationCount(models.Model):
alia_month_time = models.CharField(
max_length=20,
verbose_name='年月'
)
alia_date_time = models.CharField(
max_length=20,
verbose_name='年月日'
)
total_nums = models.IntegerField(
default=0,
verbose_name='发送总数'
)
error_nums = models.IntegerField(
default=0,
verbose_name='失败总数'
)
name = models.CharField(
max_length=50,
verbose_name='机构',
) class Meta:
verbose_name = verbose_name_plural = '机构发送短信统计' def __str__(self):
return '{0}: {1} {2} {3} {4} {5}'.format(self.id, self.alia_month_time,self.alia_date_time,self.total_nums, self.error_nums, self.name) # 定义通道发送短信统计模型类
class ChannelCount(models.Model):
alia_month_time = models.CharField(
max_length=20,
verbose_name='年月'
)
alia_date_time = models.CharField(
max_length=20,
verbose_name='年月日'
)
total_nums = models.IntegerField(
default=0,
verbose_name='发送总数'
)
error_nums = models.IntegerField(
default=0,
verbose_name='失败总数'
)
name = models.CharField(
max_length=50,
verbose_name='通道',
) class Meta:
verbose_name = verbose_name_plural = '通道发送短信统计' def __str__(self):
return '{0}: {1} {2} {3} {4} {5}'.format(self.id, self.alia_month_time,self.alia_date_time, self.total_nums, self.error_nums, self.name)

  sql查询如下(当然这样sql我是憋不出来的。。。):

# 查询得到新表数据
SELECT req_time, alia_time, count(*) as total_nums, count(t.`status`=2 or null) as error_nums, name FROM
(select *, DATE_FORMAT(req_time,'%Y-%m') as alia_time, LEFT(body,LOCATE('】',body)) as name from sms_smslog
where LOCATE('】',body) >0
and LEFT(body,1)='【' ) as t GROUP BY alia_time , name; # 插入新表【机构】
INSERT into sms_organizationcount (alia_month_time, alia_date_time, total_nums, error_nums, `name`)
SELECT alia_month_time, alia_date_time, count(*) as total_nums, count(t.`status`=2 or null) as error_nums, name FROM (select *, DATE_FORMAT(req_time,'%Y-%m') as alia_month_time, DATE_FORMAT(req_time,'%Y-%m-%d') as alia_date_time,
LEFT(body,LOCATE('】',body)) as name from sms_smslog
where LOCATE('】',body) >0
and LEFT(body,1)='【' ) as t GROUP BY alia_date_time , name; # 插入新表【通道】
INSERT into sms_channelcount (alia_month_time, alia_date_time, total_nums, error_nums, `name`)
SELECT alia_month_time, alia_date_time, count(*) as total_nums, count(t.`status`=2 or null) as error_nums, name FROM (select *, DATE_FORMAT(req_time,'%Y-%m') as alia_month_time, DATE_FORMAT(req_time,'%Y-%m-%d') as alia_date_time, channel as name from sms_smslog ) as t GROUP BY alia_date_time , channel; # 插入新表【24小时】
INSERT into sms_count24 (alia_day_time, total_nums, error_nums)
SELECT alia_day_time, count(*) as total_nums, count(t.`status`=2 or null) as error_nums FROM (select *, DATE_FORMAT(req_time,'%Y-%m-%d %H') as alia_day_time from sms_smslog) as t GROUP BY alia_day_time;

  这样数据雏形就出来了,数据肯定不能这样插入,dba也不会答应的,下一篇会详细解决这个问题。


下面解决第二个问题:

  近12个月,近30天,近24小时,显然都是动态的。下面几个方法值得收藏下:

import time
from datetime import datetime, date, timedelta # 生成近期多少天的日期
def gen_dates(end_date, days):
day = timedelta(days=1)
for i in range(days):
yield (end_date - day * i) # 生成近期多少小时
def gen_hour(end_date, hours):
hour = timedelta(hours=1)
for i in range(hours):
yield (end_date - hour * i).strftime('%Y-%m-%d %H') # 解决datetime类型不能序列化问题
class CJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y/%m/%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y/%m/%d')
else:
return json.JSONEncoder.default(self, obj) # 生成近期12个月
def gen_months():
now = datetime.now()
today_year = now.year
last_year = int(now.year) - 1
today_year_months = range(1, now.month + 1)
last_year_months = range(now.month + 1, 13)
data_list_lasts = []
for last_year_month in last_year_months:
date_list = '%s-%s' % (last_year, last_year_month)
data_list_lasts.append(date_list)
data_list_todays = []
for today_year_month in today_year_months:
data_list = '%s-%s' % (today_year, today_year_month)
data_list_todays.append(data_list)
data_year_month = data_list_lasts + data_list_todays
# data_year_month.reverse()
return data_year_month

  循环得到的时间list,拼接sql就可以查询到所需要的数据了。


下面解决第三个问题:

  使用django来展示数据,主线一般是写视图,配路由,模板渲染。

  a.写视图,获取数据逻辑上面写的差不多了,最终得返回echarts什么格式的数据

def msgsend_recent_30days_failed(request):
# 获取近30天短信失败量
con = Cache_data_to_redis().connection
cursor = con.cursor()
date_li, mon_li, data = [], [], {}
end_date = datetime.now().date()
# 获取近30天
for i in gen_dates(end_date, 30):
date_li.append(i)
date_li = date_li[::-1]
data['date_li'] = json.dumps(date_li, cls=CJsonEncoder)
for i in date_li:
sql = "SELECT error_nums as error from sms_organizationcount where alia_date_time='{alia_date_time}';".format(alia_date_time=i)
cursor.execute(sql)
num = cursor.fetchone()
if num == None:
num = {}
num['error'] = 0
mon_li.append(num['error'])
data['mon'] = mon_li
return render(request, 'data_analysis/msgsend_recent_30days_failed.html', context=data) #得到的数据如下:
{
'date_li': '["2018/07/15", "2018/07/16", "2018/07/17", "2018/07/18", "2018/07/19", "2018/07/20", "2018/07/21", "2018/07/22", "2018/07/23", "2018/07/24", "2018/07/25", "2018/07/26", "2018/07/27", "2018/07/28", "2018/07/29", "2018/07/30", "2018/07/31", "2018/08/01", "2018/08/02", "2018/08/03", "2018/08/04", "2018/08/05", "2018/08/06", "2018/08/07", "2018/08/08", "2018/08/09", "2018/08/10", "2018/08/11", "2018/08/12", "2018/08/13"]',
'mon': [0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}

  b.配路由,首先在xadmin后台添加自定义菜单

# app下的urls.py添加路由
url(r'^data_analysis/msgsend_recent_30days_failed/$', views.msgsend_recent_30days_failed, name='msgsend_recent_30days_failed'), # adminx里面添加自定义菜单和url
class GlobalSetting(object):
site_title = "短信后台管理系统"
site_footer = "http://smsweb.corp.ncfgroup.com/xadmin"
menu_style = "accordion" # 菜单
def get_site_menu(self):
return [
{
'title': '近期数据统计和分析',
'perm': self.get_model_perm(SMSLog, 'view'),
'icon': 'fa fa-bar-chart-o',
'menus': (
{
'title': '短信整体情况',
# 写死的url进行替换
'url': self.get_model_url(SMSLog,'changelist').replace('xadmin/sms/smslog/','sms/data_analysis/msgsend_recent_24hours/'),
# 'url': 'http://10.17.20.86:8004/sms/data_analysis/msgsend_recent_24hours/',
'perm': self.get_model_perm(SMSLog, 'view'),
'icon': 'fa fa-smile-o'
},
)
}
] xadmin.site.register(views.CommAdminView, GlobalSetting)

下面解决第四个问题:

  这个问题重新描述下:点数据统计图表的时候会在当前页展示出来,要是想回到主页需要点后退,这样操作就很不舒服了。找到xadmin源码里对应菜单处,添加上a标签就可以解决这个比较尴尬的问题。

xadmin/templates/xadmin/includes/sitemenu_accordion.html最后一行上面加上:

<script>
var anchors = document.getElementById("nav-panel-1").getElementsByTagName("a");
for(i=0;i<anchors.length;i++){
var anchor_item = anchors[i];
anchor_item.setAttribute("target","_blank");
}
</script>

下面解决最后一个问题:

使用echarms模板需要改动以下地方,

(1)使用sublink时,自动获取服务器host;

(2)x轴上的坐标原点可能不是紧挨着y轴,错位了一点,xAxis下面加上boundaryGap : false,

(3)title改成自己的

(4)x轴与y轴数据对应见下面完整代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>近30天通道短信情况</title>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="lineMain" style="height:400px"></div>
<!-- ECharts单文件引入 -->
<script src="http://echarts.baidu.com/build/dist/echarts.js"></script>
<script type="text/javascript">
var target = {{ target|safe }}
// 路径配置
require.config({
  paths: {
    echarts: 'http://echarts.baidu.com/build/dist'
  }
});
// 使用
require(
      [
        'echarts',
        'echarts/chart/bar',
        'echarts/chart/line'
      ],
      drawEcharts
); function drawEcharts(ec){
  drawLine(ec);
} function drawLine(ec){
  var myLineChart = ec.init(document.getElementById('lineMain'));
var date_li = {{ date_li|safe }}
var num = {{ num|safe }}
var sub = window.location.href.match('(.*)/(.*)/')[1];
var sublink = sub + '/msgsend_recent_12months_channel/';
// 动态push数据到series
var series = [];
for (var k = 0; k< target.length;k++){
var item = {
name:target[k],
type:'line',
data:num[k],
};
series.push(item);
};
  var option2 = {
    title : {
    text: '近30天渠道短信情况',
subtext: '近12个月渠道短信情况',
sublink: sublink,
  },
  tooltip : {
    trigger: 'axis'
  },
grid:{
y2: 80
},
  legend: {
orient: 'horizontal',
y: 'bottom',
data:target,
  },
  toolbox: {
    show : true,
    feature : {
      mark : {show: true},
      dataView : {show: true, readOnly: false},
      magicType : {show: true, type: ['line', 'bar']},
      restore : {show: true},
      saveAsImage : {show: true}
    }
  },
  calculable : true,
  xAxis : [
    {
      type : 'category',
      boundaryGap : false,
      data : date_li
    }
  ],
  yAxis : [
    {
      type : 'value',
    }
  ],
  series : series,
};
myLineChart.setOption(option2,true);
}
</script>
</body>
</html>

django+xadmin+echarts实现数据可视化的更多相关文章

  1. 百度地图标注及结合ECharts图谱数据可视化

    本示例中根据企业位置经纬度,在页面右侧百度地图中标注企业名称.同时页面左侧ECharts图谱饼状图用于统计企业行业与注册资本.当右侧百度地图缩放拖拽,左侧ECharts图谱根据右侧地图上出现的企业动态 ...

  2. 基于vue和echarts的数据可视化实现

    基于vue和echarts的数据可视化: https://github.com/MengFangui/awesome-vue.git

  3. 【教程】高德地图使用ECharts实现数据可视化

    关于百度地图结合ECharts实现数据可视化的资料已经很多了,毕竟是官方提供支持的,这里就不再赘述.今天我们来讲一下让高德地图与ECharts结合来实现数据可视化图表的展示. 一.ECharts 高德 ...

  4. Django+Xadmin+Echarts动态获取数据legend颜色显示灰色问题已解决

    前段时间做的使用Django的Xadmin后台和百度Echarts进行后台数据可视化,功能虽然实现,展示出来的legend图例,都是灰色的,只有鼠标放上去才会显示彩色的.百度都快被我刨穿了,看到有类似 ...

  5. django+Echarts实现数据可视化

    1.实时异步加载(从mysql读取数据) 2.scatter散点图 3.雷达图(参数选择要注意) time_1 time_2 time_3 4.面积图 我上传的源码请到github下载:https:/ ...

  6. Echarts大数据可视化物流航向省份流向迁徙动态图,开发全解+完美参数注释

    最近在研究Echarts的相关案例,毕竟现在大数据比较流行,比较了D3.js.superset等相关的图表插件,还是觉得echarts更简单上手些. 本文是以原生JS为基础,如果使用Vue.js的话, ...

  7. 微信小程序使用 ECharts 实现数据可视化

    微信小程序使用 ECharts 显示图表 首先创建微信小程序 这里就不再赘述 下载 GitHub 上的 ecomfe/echarts-for-weixin 下载后解压,打开文件夹,里面的 ec-can ...

  8. 爬取疫情数据,以django+pyecharts实现数据可视化web网页

    在家呆着也是呆着,不如做点什么消磨时间呗~ 试试用django+pyecharts实现疫情数据可视化web页面 这里要爬疫情数据 来自丁香园.搜狗及百度的疫情实时动态展示页 先看看劳动成果: 导航栏: ...

  9. 数据可视化Echarts-实例

    数据可视化 Echarts 百度 数据可视化 hightCharts 1 数据可视化 D3 老外 -----------------------------当遇到个啥玩意儿,Echarts .high ...

随机推荐

  1. 《剑指offer》第五十题(字符串中第一个只出现一次的字符)

    // 面试题50(一):字符串中第一个只出现一次的字符 // 题目:在字符串中找出第一个只出现一次的字符.如输入"abaccdeff",则输出 // 'b'. #include & ...

  2. Debug记录(1)

    今天下午在给nRF52832写程序时,莫名遇到了这个错误 错误id是一个很奇怪的数. 原代码如下: static void timers_init(void) { uint32_t timer_err ...

  3. 将本地项目上传到gitlab下

    转载自: https://blog.csdn.net/litianxiang_kaola/article/details/74075151 1.安装git    https://git-scm.com ...

  4. Codefroces 958C2 - Encryption (medium)

    C2 - Encryption (medium) 思路: 传统的dp: dp[i][j] 表示到第i个位置为止,分成j段的最大值 dp[i][j] = max(dp[l][j-1] + (sum[i] ...

  5. Asp.net core 学习笔记 (操作 url and query params)

    更新 :2018-7-25 直接添加 query string. var resetPasswordLink = QueryHelpers.AddQueryString($"{Request ...

  6. Day2-异步IO+Scrapy爬虫

    一.异步IO http://www.cnblogs.com/wupeiqi/articles/6229292.html 这篇文章写的不错,展示了多种高并发的方式,从同步执行→多线程→多进程→async ...

  7. 线程---local数据隔离

    线程之间本身是数据共享的,当多个线程同时修改一份数据的时候,数据就可能不 准确,特别是线程量特别大的时候,为了保证数据准确性: (1) 通过线程锁Lock (2)通过local数据隔离 from th ...

  8. LeetCode--405--数字转化为十六进制数

    问题描述: 给定一个整数,编写一个算法将这个数转换为十六进制数.对于负整数,我们通常使用 补码运算 方法. 注意: 十六进制中所有字母(a-f)都必须是小写. 十六进制字符串中不能包含多余的前导零.如 ...

  9. test example

    #coding=utf-8 import os import caffe import numpy as np root='/home/xxx/caffe/' #根目录 deploy=root + ' ...

  10. Git创建新项目

    1. git init 2. git remote add origin 3. git pull origin --allow-unrelated-histories 4. git push orig ...