初步实现数据图表,待完善

This commit is contained in:
ngfchl
2022-11-26 11:46:49 +08:00
parent e541b2c12a
commit aaed994480
8 changed files with 581 additions and 10 deletions

View File

@@ -21,5 +21,8 @@ urlpatterns = [
path(r'control_torrent', views.control_torrent, name='control_torrent'),
path(r'torrent_info_page', views.render_torrents_page, name='torrent_info_page'),
path(r'get_torrent_info_list', views.get_torrent_info_list, name='get_torrent_info_list'),
path(r'site_status_api', views.site_status_api, name='site_status_api'),
path(r'site_status', views.site_status, name='site_status'),
path(r'downloading_status', views.downloading_status, name='downloading_status'),
path(r'do_sql', views.do_sql, name='do_sql'),
]

View File

@@ -1,6 +1,7 @@
import json
import logging
import os
import random
import subprocess
import time
from datetime import datetime, timedelta
@@ -8,9 +9,11 @@ from datetime import datetime, timedelta
import docker
import git
import qbittorrentapi
import transmission_rpc
from django.http import JsonResponse
from django.shortcuts import render
from pt_site.UtilityTool import FileSizeConvert
from pt_site.models import SiteStatus, MySite, Site, Downloader, TorrentInfo
from pt_site.views import scheduler, pt_spider
from ptools.base import CommonResponse, StatusCodeEnum, DownloaderCategory
@@ -100,14 +103,51 @@ def get_downloader(id):
"""根据id获取下载实例"""
logger.info('当前下载器id{}'.format(id))
downloader = Downloader.objects.filter(id=id).first()
qb_client = qbittorrentapi.Client(
host=downloader.host,
port=downloader.port,
username=downloader.username,
password=downloader.password,
SIMPLE_RESPONSES=True
)
return qb_client
if downloader.category == DownloaderCategory.qBittorrent:
client = qbittorrentapi.Client(
host=downloader.host,
port=downloader.port,
username=downloader.username,
password=downloader.password,
SIMPLE_RESPONSES=True
)
if downloader.category == DownloaderCategory.Transmission:
client = transmission_rpc.Client(
host=downloader.host, port=downloader.port,
username=downloader.username, password=downloader.password
)
return client
def downloading_status(request):
qb_list = Downloader.objects.filter(category=DownloaderCategory.qBittorrent)
tr_list = Downloader.objects.filter(category=DownloaderCategory.Transmission)
tr_info_list = []
for downloader in tr_list:
client = transmission_rpc.Client(
host=downloader.host, port=downloader.port,
username=downloader.username, password=downloader.password
)
session = transmission_rpc.session.Session(client=client)
print(type(session))
# print(client.get_torrents())
session_list = client.get_session()
session = {item: value for item, value in session_list.items()}
tr_info = {
# 'torrents': client.get_torrents(),
'free_space': client.free_space('/downloads'),
# 'session': session.values(),
'protocol_version': client.protocol_version,
'rpc_version': client.rpc_version,
'session_id': client.session_id,
# 'session_stats': client.session_stats(),
'arguments': client.torrent_get_arguments,
}
tr_info_list.append(tr_info)
return JsonResponse(CommonResponse.success(data={
'tr_info_list': tr_info_list
}).to_dict(), safe=False)
def get_trackers(request):
@@ -520,3 +560,65 @@ def download_tasks():
"""
downloader_list = Downloader.objects.all()
pass
def site_status_api(request):
my_site_list = MySite.objects.all()
uploaded = 0
downloaded = 0
seeding = 0
seeding_size = 0
status_list = []
now = datetime.now()
for my_site in my_site_list:
site_info = my_site.sitestatus_set.order_by('-pk').first()
downloaded += site_info.downloaded
uploaded += site_info.uploaded
seeding += my_site.seed
seeding_size += site_info.seed_vol
weeks = (now - my_site.time_join).days // 7
days = (now - my_site.time_join).days % 7
site_info = {
'name': my_site.site.name,
'class': my_site.my_level,
'invite': my_site.invitation,
'sp_hour': my_site.sp_hour,
'seeding': my_site.seed,
'time_join': f'{weeks}{days}',
'hr': my_site.my_hr,
'mail': my_site.mail,
'sp': site_info.my_sp,
'bonus': site_info.my_bonus,
# 'uploaded': FileSizeConvert.parse_2_file_size(site_info.uploaded),
# 'downloaded': FileSizeConvert.parse_2_file_size(site_info.downloaded),
# 'seeding_size': FileSizeConvert.parse_2_file_size(site_info.seed_vol),
'uploaded': site_info.uploaded,
'downloaded': site_info.downloaded,
'seeding_size': site_info.seed_vol,
}
status_list.append(site_info)
# 按上传量排序
# status_list.sort(key=lambda x: x['uploaded'], reverse=False)
# sorted(status_list, key=lambda x: x['uploaded'])
# 随机乱序
random.shuffle(status_list)
total_data = {
# 'uploaded': FileSizeConvert.parse_2_file_size(uploaded),
# 'downloaded': FileSizeConvert.parse_2_file_size(downloaded),
# 'seeding_size': FileSizeConvert.parse_2_file_size(seeding_size),
'uploaded': uploaded,
'downloaded': downloaded,
'seeding_size': seeding_size,
'seeding': seeding,
'ratio': round(uploaded / downloaded, 3),
}
# return render(request, 'auto_pt/status.html')
return JsonResponse(data=CommonResponse.success(
data={
'total_data': total_data, 'status_list': status_list
}
).to_dict(), safe=False)
def site_status(request):
return render(request, 'auto_pt/status.html')

View File

@@ -254,9 +254,13 @@ SIMPLEUI_CONFIG = {
'icon': 'fa fa-user',
'url': '/tasks/page_downloading'
}, {
'name': '查询种子',
'name': '我的站点',
'icon': 'fa fa-user',
'url': '/downloader/ptspider/index'
'url': '/tasks/site_status'
}, {
'name': '下载器展示',
'icon': 'fa fa-user',
'url': '/tasks/downloading_status'
}]
}, {
'app': 'update',

View File

@@ -0,0 +1,27 @@
/**
* 封装echarts可以在vue中使用
*/
Vue.component('charts', {
props: ['option', 'style'], data: function () {
return {}
}, mounted: function () {
this.$nextTick(function () {
var el = this.$el;
var chart = echarts.init(el, 'dark');
chart.setOption(this.option);
this.chart = chart
})
}, watch: {
obj: {
option(newValue, oldValue) {
// option发生变化时自动重新渲染
this.chart.setOption(newValue)
}, // immediate: true,
deep: true,
}
}, template: '<div :style="style">{{option}}</div>'
})

45
static/js/echarts.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,370 @@
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
{% include 'admin/includes/css-part.html' %}
<style>
body {
background-color: #2f4155;
}
.box-card {
background-color: #130f2c;
color: #ffefef;
}
.chart-button {
float: right;
margin-right: 5px;
}
</style>
</head>
<body>
<div id="status">
<el-card class="box-card" shadow="hover">
<div slot="header" class="clearfix">
<span>数据总量</span>
<span>
<el-button type="warning" size="small" class="chart-button"
@click="setTree">矩形树图</el-button>
<el-button type="success" size="small" class="chart-button"
@click="setBar">柱状图</el-button>
<el-button type="primary" size="small" class="chart-button"
@click="setPie">饼图</el-button>
</span>
</div>
<div class="body">
<charts ref="charts" style="height: 700px;" :option="option"></charts>
</div>
</el-card>
</div>
{% include 'admin/includes/js-part.html' %}
<script src="{% static 'admin/simpleui-x/js/axios.min.js' %}"></script>
<script src="{% static 'js/echarts.min.js' %}"></script>
<script src="{% static 'js/echarts-component.js' %}"></script>
<script>
function renderSize(value) {
if (null == value || value == '') {
return 0;
}
var unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
var index = 0;
var srcsize = parseFloat(value);
index = Math.floor(Math.log(srcsize) / Math.log(1024));
var size = srcsize / Math.pow(1024,
index);
size = size.toFixed(2);//保留的小数位数
return size + ' ' + unitArr[index];
}
const vm = new Vue({
el: '#status',
data() {
return {
chart: {},
ptData: {},
option: {},
}
},
beforeMount() {
},
mounted() {
this.chart = this.$refs.charts.chart
this.getData()
},
watch: {
obj: {
option(newValue, oldValue) {
// option发生变化时自动重新渲染
this.chart.setOption(newValue)
},
// immediate: true,
deep: true,
}
},
methods: {
setPie() {
let uploadedList = []
let ptData = this.ptData
ptData.status_list.forEach((site, index) => {
uploadedList.push({
'value': site.uploaded,
'path': 'uploaded/' + site.name,
'name': site.name
})
//downloadedList.push({
// 'value': site.downloaded,
// 'path': 'downloaded/' + site.name,
// 'name': site.name
//})
})
let option = {
tooltip: {
show: true,
//formatter: function (params) {
// return params.name + '\t' + renderSize(params.data.value)
//}
valueFormatter: function (value) {
return renderSize(value)
}
},
color: [
'#f66c73',
'#f68645',
'#7af6ad',
'#f4d55f',
'#488ff6',
'#0fba8d',
'#8a47dc',
'#d677f6',
],
legend: {
show: false,
{#type: 'scroll',#}
{#top: 'bottom',#}
{#right: '0',#}
{#orient: 'vertical',#}
},
toolbox: {
show: true,
feature: {
mark: {show: true},
{#dataView: {show: true, readOnly: false},#}
{#restore: {show: true},#}
saveAsImage: {show: true}
}
},
series: [
{
name: 'PT数据展示',
type: 'pie',
bottom: '5%',
radius: '55%',
{#visualDimension: 1,#}
center: ['50%', '50%'],
roseType: '',
label: {
show: true,
formatter: function (params) {
return params.name + ' \t ' + renderSize(params.data.value)
}
},
itemStyle: {
borderRadius: 7
},
data: [
{value: 40, name: 'rose 1'},
{value: 38, name: 'rose 2'},
{value: 32, name: 'rose 3'},
{value: 30, name: 'rose 4'},
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
option.series[0].data = uploadedList
this.option = option
this.$refs.charts.chart.clear()
this.$refs.charts.chart.setOption(this.option)
},
setTree() {
let ptData = this.ptData
let uploadedList = []
let downloadedList = []
ptData.status_list.forEach((site, index) => {
uploadedList.push({
'value': site.uploaded,
'path': 'uploaded/' + site.name,
'name': `${site.name}\t${renderSize(site.uploaded)}`
})
downloadedList.push({
'value': site.downloaded,
'path': 'downloaded/' + site.name,
'name': `${site.name}\t${renderSize(site.downloaded)}`
})
})
let option = {
backgroundColor: '#130f2c',
tooltip: {
show: true,
//formatter: function (params) {
// return params.name + '\t' + renderSize(params.data.value)
//}
valueFormatter: function (value) {
return renderSize(value)
}
},
color: [
'#f66c73',
'#f68645',
'#7af6ad',
'#f4d55f',
'#488ff6',
'#0fba8d',
'#8a47dc',
'#d677f6',
'#130f2c',
'#2f4155',
],
series: [
{
type: 'treemap',
name: '数据汇总',
colorMappingBy: 'index',
{#colorSaturation: [0.9, 0.9],#}
label: {
show: true,
formatter: function (params) {
return params.name + '\t' + renderSize(params.data.value)
}
},
data: [
{
name: '上传',
value: 0,
}
]
}
]
}
/**
console.log(this.treeOption.series[0].data)
this.treeOption.series[0].data.push({
name: '总下载',
value: ptData.total_data.downloaded,
})
let treeData = this.treeOption.series[0].data[0]
treeData.value = ptData.total_data.uploaded
treeData.children = dataList
**/
option.series[0].data.push({
name: `总上传\t${renderSize(ptData.total_data.uploaded)}`,
value: ptData.total_data.uploaded,
path: "uploaded",
children: uploadedList
}, {
name: `总下载\t${renderSize(ptData.total_data.downloaded)}`,
value: ptData.total_data.downloaded,
path: "downloaded",
children: downloadedList
})
{#this.treeOption.series[0].data.push()#}
{#this.$refs.charts.chart.setOption(treeOption)#}
this.option = option
this.$refs.charts.chart.clear()
this.$refs.charts.chart.setOption(this.option)
},
setBar() {
let option = {
title: {
text: 'PT数据聚合图'
},
tooltip: {
trigger: 'axis',
show: true,
axisPointer: {
type: 'shadow'
},
valueFormatter: function (value) {
return renderSize(value)
}
},
legend: {},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value',
axisLabel: {
formatter: function (value, index) {
return renderSize(value);
}
},
label: {
formatter: function (value, index) {
return renderSize(value);
}
},
boundaryGap: [0, 0.01]
},
series: []
};
let ptData = this.ptData
let uploadedList = []
let downloadedList = []
let siteList = []
{#uploadedList.push(ptData.total_data.uploaded)#}
{#downloadedList.push(ptData.total_data.downloaded)#}
{#siteList.push('总量')#}
ptData.status_list.forEach((site, index) => {
downloadedList.push(site.downloaded)
uploadedList.push(site.uploaded)
siteList.push(site.name)
})
let uploaded = {
name: '上传量',
type: 'bar',
stack: 'Ad',
data: uploadedList
}
let downloaded = {
name: '下载量',
type: 'bar',
stack: 'Ad',
data: downloadedList
}
option.series.push(downloaded, uploaded)
option.xAxis.data = siteList
this.option = option
this.$refs.charts.chart.setOption(option)
},
getData() {
axios.get(
"{% url "site_status_api" %}"
).then(res => {
console.log('获取数据列表成功', res.data)
if (res.data.code === 0) {
console.log(res.data.data)
{#this.ptData = res.data.data#}
this.ptData = res.data.data
this.setBar()
} else {
this.loading = false
this.$message({
type: 'warning',
message: '获取数据列表失败!'
});
}
}).catch(res => {
console.log('获取数据列表失败', res)
this.$message({
type: 'warning',
message: '获取数据列表失败!' + res
});
})
}
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户数据</title>
</head>
<body>
</body>
</html>