I. Introduction
Bootstrap
is a popular front-end framework, and ECharts is a popular visualization library.
Bootstrap
It can be used to design user interfaces for websites and applications, while ECharts can be used to create interactive and visual charts.
chart.js
Chinese documentation: http://www.bootcss.com/p/chart.js/docs/
2. Advanced use
The basic usage has been introduced in the previous article, link: https://blog.csdn.net/qq_43030934/article/details/131540606
This article is based on an advanced use of the Django+bootstrap+echart plug-in. The basic usage will not be introduced too much.
2. Rendering
1. Daily activity statistics
2. Year and month data statistics
3. Sample code
No more nonsense, let’s go straight to the code
1. Front end
The code is written based on related projects. You can refer to it based on your own projects.
{% extends "base.html" %} {% load static %}
{% block main %}
{# <script src="../../../../static/js/echarts-v5.1.2.min.js"></script>#}
{% if 'is_superuser' in permissions.keys %}
<!-- daily active users statistics start -->
<section class="section" style="padding: 100px 0 30px 0">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 text-center">
<div class="section-title mb-2">
<h4 class="title mb-4">用户数据统计</h4>
</div>
</div><!--end col-->
</div><!--end row-->
<div class="row mt-4 pt-2 position-relative" id="userDailyData" style="z-index: 1;">
<div class="col-lg-7 col-6 mt-4 pt-2">
<div class="counter-box text-center">
<div id="userDailyIncrease" style="width:auto;height:400px;"></div>
</div><!--end counter box-->
</div>
<div class="col-lg-5 col-6 mt-4 pt-2">
<div class="counter-box text-center">
<div id="userBaseData" style="width:auto;height:400px;"></div>
</div><!--end counter box-->
</div>
</div><!--end row-->
<div class="feature-posts-placeholder"></div>
</div><!--end container-->
</section><!--end section-->
<!-- daily active users statistics End -->
<!-- monthly/year increase users statistics start -->
<section class="section" style="padding: 30px 0 100px 0">
<div class="container">
<div class="row mt-4 pt-2 position-relative" style="z-index: 1;">
<div class="col-lg-6 col-6 mt-4 pt-2">
<div class="counter-box text-center">
<div id="userMonthlyIncrease" style="width:auto;height:400px;"></div>
</div><!--end counter box-->
</div>
<div class="col-lg-6 col-6 mt-4 pt-2">
<div class="counter-box text-center">
<div id="userYearIncrease" style="width:auto;height:400px;"></div>
</div><!--end counter box-->
</div>
</div><!--end row-->
<div class="feature-posts-placeholder"></div>
</div><!--end container-->
</section><!--end section-->
<!-- monthly/year increase users statistics End -->
{% endif %}
<!-- Document change achievement Start -->
<section class="section bg-light">
<div class="container">
<div class="row mt-4 pt-2 position-relative" id="counter" style="z-index: 1;">
<div class="col-md col-6 mt-4 pt-2">
<div class="counter-box text-center">
<img src="{% static 'images/homepage/Asset260.svg' %}" class="avatar avatar-small" alt="">
<h2 class="mb-0 mt-4"><span class="counter-value"
data-count="97">{
{ quarterData.requestTotal }}</span></h2>
<h6 class="counter-head text-muted">{
{ quarterData.currentQuarter }} Change requests</h6>
</div><!--end counter box-->
</div>
<div class="col-md col-6 mt-4 pt-2">
<div class="counter-box text-center">
<img src="{% static 'images/homepage/Asset189.svg' %}" class="avatar avatar-small" alt="">
<h2 class="mb-0 mt-4"><span class="counter-value"
data-count="15">{
{ quarterData.completed }}</span></h2>
<h6 class="counter-head text-muted">Completed</h6>
</div><!--end counter box-->
</div>
<div class="col-md col-6 mt-4 pt-2">
<div class="counter-box text-center">
<img src="{% static 'images/homepage/Asset192.svg' %}" class="avatar avatar-small" alt="">
<h2 class="mb-0 mt-4"><span class="counter-value"
data-count="98">{
{ quarterData.percentageComplete }}</span>%</h2>
<h6 class="counter-head text-muted">Request Complete</h6>
</div><!--end counter box-->
</div>
</div><!--end row-->
<div class="feature-posts-placeholder"></div>
</div><!--end container-->
</section><!--end section-->
<!-- Document change achievement End -->
{% endblock %}
{% block script %}
<script>
var MyViewVar = {
userCurActiveData: {
{
userCurActiveData|safe }},
usersDataByDaily: {
{
usersDataByDaily|safe }},
usersDataByMonthly: {
{
usersDataByMonthly|safe }},
usersDataByYears: {
{
usersDataByYears|safe }},
}
console.log(MyViewVar)
var userDailyIncreaseChart = echarts.init(document.getElementById('userDailyIncrease'));
userDailyIncreaseChart.setOption({
title: {
text: '日增日活',
x: 'center'
},
// 图表图例
legend: {
orient: 'vertical',
data: ['日活用户', '日增用户',],
left: 'right',
},
xAxis: {
name: '日',
type: 'category',
data: MyViewVar.usersDataByDaily.date_list,
boundaryGap: false,
axisLabel: {
inside: false, //刻度标签是否朝内,false朝外
interval: 1, // 设置标签全部显示
rotate: 30, // 设置标签旋转角度
maxInterval: 1000, // 设置刻度间隔
},
axisLine: {
lineStyle: {
color: '#333',
type: 'solid',
},
}
},
yAxis: {
name: '数量',
type: 'value',
axisLine: {
show: true, // 是否显示坐标轴轴线
lineStyle: {
color: '#333', // 坐标轴线线的颜色
type: 'solid', // 坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
series: [
{
name: '日增用户',
data: MyViewVar.usersDataByDaily.user_increase_count_list,
type: 'line',
itemStyle: {
color: '#91CC75',
},
markPoint: {
data: [
{
type: 'max', name: '最大值'},
{
type: 'min', name: '最小值'}
]
},
lineStyle: {
color: '#91CC75', //线颜色
type: 'solid', //线的类型
opacity: 0.8, //线透明度
shadowBlur: 5, //阴影模糊度
shadowColor: '#999', //阴影颜色
shadowOffsetX: 2, //阴影X轴偏移量
shadowOffsetY: 2, //阴影Y轴偏移量
radius: 100 //曲线圆角半径
},
},
{
name: '日活用户',
type: 'line',
data: MyViewVar.usersDataByDaily.user_active_count_list,
itemStyle: {
color: '#FFC858',
},
markPoint: {
data: [
{
type: 'max', name: '最大值'},
{
type: 'min', name: '最小值'}
]
},
lineStyle: {
color: '#FFC858', //线颜色
},
},
]
});
var userBaseDataChart = echarts.init(document.getElementById('userBaseData'));
//定义饼图数据
var data = [
{
value: MyViewVar.userCurActiveData.dailyActiveCount, name: '日活用户'},
{
value: MyViewVar.userCurActiveData.monthlyActiveCount, name: '月活用户'},
];
//定义饼图配置项
var option = {
title: {
text: '用户当日及当月活跃人数\n' + '用户总人数:' + MyViewVar.userCurActiveData.totalCount,
x: 'center'
},
tooltip: {
trigger: 'item',
formatter: "{a} <br/>{b} : {c}<br/>" + "用户总人数:" + MyViewVar.userCurActiveData.totalCount
},
legend: {
orient: 'vertical',
left: 'right',
data: ['日活用户', '月活用户']
},
series: [
{
name: '用户基础数据统计',
type: 'pie',
radius: ['40%', '60%'],
center: ['50%', '60%'],
data: data,
label: {
show: true,
formatter: function (params) {
// 计算总数
var total = MyViewVar.userCurActiveData.totalCount;
// 显示数据项名称和百分比
return params.name + '\n' + params.value + '人' + '\n' + (params.value / total * 100).toFixed(2) + '%';
}
},
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
color: ['#FF8D5D', '#FFC858']
}
]
};
//渲染饼图
userBaseDataChart.setOption(option);
var userMonthlyIncreaseChart = echarts.init(document.getElementById('userMonthlyIncrease'));
userMonthlyIncreaseChart.setOption({
title: {
text: '月增月活',
x: 'center'
},
// 图表图例
legend: {
data: ['月活用户', '月增用户'],
left: 'right',
},
xAxis: {
name: '月',
type: 'category',
data: MyViewVar.usersDataByMonthly.month_list,
boundaryGap: false,
axisLabel: {
inside: false, //刻度标签是否朝内,false朝外
interval: 0, // 设置标签全部显示
rotate: 30, // 设置标签旋转角度
maxInterval: 1000, // 设置刻度间隔
},
axisLine: {
lineStyle: {
color: '#333',
type: 'solid',
},
},
},
yAxis: {
name: '数量',
type: 'value',
axisLine: {
show: true, // 是否显示坐标轴轴线
lineStyle: {
color: '#333', // 坐标轴线线的颜色
type: 'solid', // 坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
},
},
tooltip: {
trigger: 'axis',
},
series: [
{
name: '月活用户',
type: 'line',
data: MyViewVar.usersDataByMonthly.user_active_count_list,
itemStyle: {
color: '#FFC858',
},
markPoint: {
data: [
{
type: 'max', name: '最大值'},
{
type: 'min', name: '最小值'}
]
},
lineStyle: {
color: '#FFC858', //线颜色
},
},
{
name: '月增用户',
data: MyViewVar.usersDataByMonthly.user_increase_count_list,
type: 'line',
itemStyle: {
color: '#91CC75',
},
label: {
show: true,
formatter: function (params) {
return params.value;
}
},
markPoint: {
data: [
{
type: 'max', name: '最大值'},
{
type: 'min', name: '最小值'}
]
},
lineStyle: {
color: '#91CC75', //线颜色
},
},
]
});
var userYearIncreaseChart = echarts.init(document.getElementById('userYearIncrease'));
userYearIncreaseChart.setOption({
title: {
text: '用户年增长及跃人数\n' + '用户总人数:' + MyViewVar.userCurActiveData.totalCount,
x: 'center'
},
tooltip: {
},
legend: {
orient: 'vertical',
data: ['年活跃用户', '年增长用户'],
left: 'right',
},
xAxis: {
axisLine: {
show: true, // 是否显示坐标轴轴线
lineStyle: {
color: '#333', // 坐标轴线线的颜色
type: 'solid', // 坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
},
data: MyViewVar.usersDataByYears.year_list,
},
yAxis: {
name: '数量',
type: 'value',
axisLine: {
show: true, // 是否显示坐标轴轴线
lineStyle: {
color: '#333', // 坐标轴线线的颜色
type: 'solid', // 坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
},
},
series: [
{
name: '年活跃用户',
type: 'bar',
data: MyViewVar.usersDataByYears.user_active_count_list,
itemStyle: {
color: '#FFC858',
},
},
{
name: '年增长用户',
type: 'bar',
data: MyViewVar.usersDataByYears.user_increase_count_list,
},
],
label: {
show: true,
position: 'top', // 在柱形顶部显示标签
formatter: '{c}', // 数值显示格式
color: '#000', // 标签颜色
fontSize: 14, // 标签字体大小
},
})
</script>
{% endblock %}
2. Backend code
The technology stack used by the back-end code interface is. Django+ORM
The code is for reference only. You can use it as a reference according to the business of your own project.
class DataStatisticsView(LoginRequiredJSONMixin, APIView):
def get(self, request):
permissions = get_user_permissions(request.user.id)
quarterData = get_quarter_data_statistics(CP_Register)
# 获取当前日期
now_date = datetime.date.today()
userCurActiveData = self.get_cur_user_active_data(now_date)
usersDataByDaily = self.get_increase_and_active_users_by_daily(now_date)
usersDataByMonthly = self.get_increase_and_active_users_by_monthly(now_date)
usersDataByYears = self.get_increase_and_active_users_by_years(now_date)
context = {
'permissions': permissions,
'quarterData': quarterData,
'userCurActiveData': userCurActiveData,
'usersDataByDaily': usersDataByDaily,
'usersDataByMonthly': usersDataByMonthly,
'usersDataByYears': usersDataByYears,
}
return render(request, 'teams/my_dashboard/statistics/data_statistics.html', context)
def get_increase_and_active_users_by_years(self, now_date):
"""数据库中按年增长及年活跃人数"""
# 按年增长统计用户
users_per_year = User.objects.annotate(year=ExtractYear('date_joined')).values('year').annotate(
total=Count('id'))
user_increase_count_list = []
year_list = []
for user in users_per_year:
user_increase_count_list.append(user['total'])
year_list.append(user['year'])
# 按年统计活跃人数
users_per_year = User.objects.annotate(year=ExtractYear('last_login')).values('year').annotate(
total=Count('id'))
user_active_count_list = []
for user in users_per_year:
user_active_count_list.append(int(user['total']) * ActiveCoefficient.year_active)
usersDataByYears = {
'user_increase_count_list': user_increase_count_list,
'user_active_count_list': user_active_count_list,
'year_list': year_list,
}
return usersDataByYears
def get_increase_and_active_users_by_monthly(self, now_date):
"""
近一年用户月增及月活跃数据
sqlite按当月统计一年用户的数量sql:SELECT DATE('now', 'start of month') AS end_date,
DATE('now', '-12 months', 'start of month') AS start_date;
SELECT strftime('%Y-%m', date_joined) AS month,
COUNT(id) AS count
FROM auth_user
WHERE date_joined >= DATE('now', '-12 months', 'start of month')
GROUP BY month
ORDER BY month;
"""
user_increase_count_list = []
user_active_count_list = []
month_list = []
# FIXME--bug:某月用户数量为0时无数据
"""
end_date = now_date.replace(day=30)
start_date = (end_date - datetime.timedelta(days=365)).replace(day=1)
{'user_count_list': [33, 37, 30, 35, 25, 22, 11, 35, 4, 6, 1, 7],
'month_list': ['22-07', '22-08', '22-09', '22-10', '22-11', '22-12', '23-01', '23-02', '23-03', '23-04', '23-05', '23-07']
}
users_last_year = User.objects.filter(date_joined__range=(start_date, end_date)).annotate(
month=TruncMonth('date_joined')).values('month').annotate(count=Count('id'))
for user in users_last_year:
user_count_list.append(user['count'])
month_list.append(user['month'].strftime('%y-%m'))
"""
# 计算往前12个月的日期时间,计算月增月活数据
for i in range(12):
# 当月起始时间和月末时间
month_first_day = (now_date - datetime.timedelta(days=365 * i / 12)).replace(day=1)
month_last_day = (month_first_day + datetime.timedelta(days=32)).replace(day=1) - datetime.timedelta(days=1)
month_increase_count = User.objects.filter(date_joined__range=(month_first_day, month_last_day)).count()
month_list.append(month_first_day.strftime('%y-%m'))
user_increase_count_list.append(month_increase_count)
moth_active_count = int(User.objects.filter(
last_login__range=(month_first_day, month_last_day)).count() * ActiveCoefficient.monthly_active)
user_active_count_list.append(moth_active_count)
usersDataByMonthly = {
'user_increase_count_list': user_increase_count_list[::-1],
'user_active_count_list': user_active_count_list[::-1],
'month_list': month_list[::-1],
}
return usersDataByMonthly
def get_increase_and_active_users_by_daily(self, now_date):
"""近一月用户日增日活数据"""
# 获取一个月前日期
start_date = now_date - datetime.timedelta(days=30)
date_list = []
user_increase_count_list = []
user_active_count_list = []
for i in range(1, 31):
# 循环遍历获取当天日期
cur_date = start_date + datetime.timedelta(days=i)
next_date = start_date + datetime.timedelta(days=i + 1)
day_increase_count = User.objects.filter(last_login__range=(cur_date, next_date)).count()
date_list.append(cur_date.strftime('%y-%m-%d'))
user_increase_count_list.append(day_increase_count)
day_active_count = int(
User.objects.filter(last_login__range=(cur_date, next_date)).count() * ActiveCoefficient.daily_active)
user_active_count_list.append(day_active_count)
dailyIncreaseUsers = {
'date_list': date_list,
'user_increase_count_list': user_increase_count_list,
'user_active_count_list': user_active_count_list,
}
return dailyIncreaseUsers
def get_cur_user_active_data(self, now_date):
"""当日及当月活跃数据"""
# 日活用户
dailyActiveCount = User.objects.filter(last_login__gte=now_date).count()
# 月活用户
first_day_of_month = datetime.date(now_date.year, now_date.month, 1)
last_day_of_month = datetime.date(now_date.year, now_date.month + 1, 1) - datetime.timedelta(days=1)
monthlyActiveCount = User.objects.filter(last_login__range=(first_day_of_month, last_day_of_month)).count()
# 用户总数
totalCount = User.objects.all().count()
userCurActiveData = {
'dailyActiveCount': int(dailyActiveCount * ActiveCoefficient.daily_active),
'monthlyActiveCount': int(monthlyActiveCount * ActiveCoefficient.monthly_active),
'totalCount': totalCount,
'date': now_date.strftime('%Y-%m-%d'),
}
return userCurActiveData
utils.py
class ActiveCoefficient:
daily_active = 1.5
monthly_active = 1.5
year_active = 3
def get_user_permissions(user_id):
from registration.models import UserProfile
u_pro = UserProfile.objects.filter(user_id=user_id).first()
permissions = OrderedDict()
if u_pro.is_superuser:
permissions['is_superuser'] = '超级管理员'
if u_pro.is_team_leader:
permissions['is_team_leader'] = '团队负责人'
if u_pro.is_ele_feature_extension_admin:
permissions['ele_feature_extension_admin'] = '普通管理员'
if u_pro.is_es_feature_extension_admin:
permissions['es_feature_extension_admin'] = '普通管理员'
if u_pro.role:
permissions = get_role_permission(u_pro.role.id, permissions)
return permissions
def get_quarter_data_statistics(obj):
"""季度发布数据统计"""
today = datetime.date.today()
current_quarter = (today.month - 1) // 3 + 1
# 当前年份季度
current_year_quarter = str(today.year) + 'Q{}'.format(str(current_quarter))
total = obj.objects.filter(schedule__startswith=current_year_quarter).count()
completed_cnt = obj.objects.filter(schedule__startswith=current_year_quarter).filter(
Q(decision__in=['', 'Released']) | Q(decision__isnull=True)).count()
try:
percentageComplete = int(completed_cnt / total * 100)
except Exception as e:
percentageComplete = 0
logger.info('homePage error :{}'.format(e))
QuarterData = {
'requestTotal': total,
'percentageComplete': percentageComplete,
'completed': completed_cnt,
'currentQuarter': 'Q{}'.format(current_quarter),
}
return QuarterData
The above is Django + Bootstrap - [echart] advanced use of statistical charts - the basic use of statistical data such as daily user activity and monthly activity increase, etc., I hope it will be helpful to you!