Commit 35981fe4 authored by zhengjinlei's avatar zhengjinlei

添加质检违规页面

parent ba2f79d3
...@@ -90,6 +90,32 @@ TEMPLATES = [ ...@@ -90,6 +90,32 @@ TEMPLATES = [
}, },
] ]
# 跨域增加忽略
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
'*'
)
CORS_ALLOW_METHODS = (
'GET',
'POST',
)
CORS_ALLOW_HEADERS = (
'XMLHttpRequest',
'X_FILENAME',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
'Pragma',
)
WSGI_APPLICATION = 'config.wsgi.application' WSGI_APPLICATION = 'config.wsgi.application'
# Database # Database
......
...@@ -22,6 +22,7 @@ from django.conf import settings ...@@ -22,6 +22,7 @@ from django.conf import settings
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('api/v1/', include('inspect_report.urls_api_v1')), path('api/v1/', include('inspect_report.urls_api_v1')),
path('view/page/', include('inspect_report.urls')),
path('api/auth/', include('rest_framework.urls')), path('api/auth/', include('rest_framework.urls')),
path('api/docs/', include_docs_urls(title='inspect_reportAPI', public=False)), path('api/docs/', include_docs_urls(title='inspect_reportAPI', public=False)),
path('api/auth-jwt/', obtain_jwt_token), # POST email=email&password=password path('api/auth-jwt/', obtain_jwt_token), # POST email=email&password=password
......
...@@ -8,6 +8,7 @@ from inspect_report.models import Tasks, CheckSession ...@@ -8,6 +8,7 @@ from inspect_report.models import Tasks, CheckSession
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
from config.config import TABLE_PRE from config.config import TABLE_PRE
from django.views.decorators.clickjacking import xframe_options_exempt
from inspect_report import permissions from inspect_report import permissions
import logging import logging
...@@ -38,18 +39,20 @@ class TasksApi(viewsets.ViewSet): ...@@ -38,18 +39,20 @@ class TasksApi(viewsets.ViewSet):
:return: :return:
""" """
task_id = req.GET.get('task', '') task_id = req.GET.get('task', '')
if not task_id: task_condition = {}
return Response({'code': -1, 'msg': 'not select task'}) if task_id:
try: task_condition['pk'] = task_id
task = Tasks.objects.get(pk=task_id) tasks = Tasks.objects.filter(**task_condition)
except Exception as e: return_data = []
logger.info(e) for t in tasks:
return Response({'code': -2, 'msg': 'no task'}) session_collection = t.sessionCollectionId
session_collection = task.sessionCollectionId
table_name = TABLE_PRE + session_collection table_name = TABLE_PRE + session_collection
tn = CheckSession.set_table(table_name) tn = CheckSession.set_table(table_name)
agents = tn.objects.filter(taskId=task_id).values('agentName') agents = tn.objects.filter(taskId=t.id).values('agentName')
return Response({'code': 0, 'msg': 'success', 'data': agents}) for agent in agents:
if agent not in return_data:
return_data.append(agent)
return Response({'code': 0, 'msg': 'success', 'data': return_data})
@action(['post'], detail=False) @action(['post'], detail=False)
def rule(self, req: Request): def rule(self, req: Request):
...@@ -80,13 +83,14 @@ class TasksApi(viewsets.ViewSet): ...@@ -80,13 +83,14 @@ class TasksApi(viewsets.ViewSet):
if result: if result:
data = json.loads(result) data = json.loads(result)
for d in data: for d in data:
if 'rule' in d.keys() and 'name' in d['rule'].keys(): if 'isViolation' in d.keys() and d['isViolation'] and 'rule' in d.keys() and 'name' in d['rule'].keys():
name = d['rule']['name'] name = d['rule']['name']
if name in return_data.keys(): if name in return_data.keys():
return_data[name] += 1 return_data[name] += 1
else: else:
return_data[name] = 1 return_data[name] = 1
return Response({'code': 0, 'msg': 'success', 'data': return_data}) data_sort = sorted(return_data.items(), key=lambda item: item[1], reverse=False)
return Response({'code': 0, 'msg': 'success', 'data': data_sort})
@action(['post'], detail=False) @action(['post'], detail=False)
def seat_session(self, req: Request): def seat_session(self, req: Request):
...@@ -117,8 +121,8 @@ class TasksApi(viewsets.ViewSet): ...@@ -117,8 +121,8 @@ class TasksApi(viewsets.ViewSet):
return_data[agent_name] += 1 return_data[agent_name] += 1
else: else:
return_data[agent_name] = 1 return_data[agent_name] = 1
data_max = sorted(return_data.items(), key=lambda item: item[1], reverse=True) data_max = sorted(return_data.items(), key=lambda item: item[1], reverse=False)
data_max = data_max[:int(num)] data_max = data_max[-int(num):]
data_max_return = {} data_max_return = {}
for dm in data_max: for dm in data_max:
data_max_return[dm[0]] = dm[1] data_max_return[dm[0]] = dm[1]
...@@ -159,3 +163,13 @@ class TasksApi(viewsets.ViewSet): ...@@ -159,3 +163,13 @@ class TasksApi(viewsets.ViewSet):
for dm in data_max: for dm in data_max:
data_max_return[dm[0]] = dm[1] data_max_return[dm[0]] = dm[1]
return Response({'code': 0, 'msg': 'success', 'data': data_max_return}) return Response({'code': 0, 'msg': 'success', 'data': data_max_return})
@xframe_options_exempt
def rule(request):
return render(request, 'inspect/rule.html')
@xframe_options_exempt
def seat(request):
return render(request, 'inspect/seat.html')
<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
<meta charset='utf-8' />
<title>语音坐席</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<link rel='stylesheet' type='text/css' href="{% static "inspect/css/all.css" %}">
</head>
<body style='height:100%'>
<div class='child-title'>语音坐席</div>
<div class='child-main' id='childApp'>
<div style='text-align:right;' class='childTitle'>
<div class='childTitleName'>质检违规统计</div>
<el-date-picker
v-model="dateRange"
@change='changeRange'
clearable='false'
type="daterange"
value-format='yyyy-MM-dd'
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']">
</el-date-picker>
<el-select v-model="taskvalue" placeholder="请选择" @change="changeTask">
<el-option
v-for="item in taskList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-select v-model="takevalue" placeholder="请选择" @change='changeTake'>
<el-option
v-for="item in takeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div id='main' style='width:80%;height:600px'></div>
</div>
<script type="text/javascript">
window.onload = function(){
var option = {
dataZoom : [
{
type: 'slider',
show: true,
start: 50,
// orient: 'horizontal',
orient: 'vertical',
zoomLock: true,
end: 100,
handleSize: 8
},
{
type: 'inside',
start: 50,
orient: 'vertical',
zoomLock: true,
// zoomOnMouseWheel:false,
end: 100
},
{
type: 'slider',
show: true,
xAxisIndex: 0,
filterMode: 'empty',
width: 12,
// height: '70%',
handleSize: 8,
orient: 'vertical',
zoomLock: true,
showDataShadow: false,
left: '93%'
}
],
dataset: {
source: [
[ 'amount', 'product'],
[2, 'Matcha Latte'],
[4, 'Milk Tea'],
[2, 'Cheese Cocoa'],
[5, 'Cheese Brownie'],
[5, 'Matcha Cocoa'],
[6, 'Tea'],
[2, 'Orange Juice'],
[52, 'Lemon Juice'],
[2, 'Walnut Brownie'],
[2, 'Walnut Brownie1'],
[2, 'Walnut Brownie2'],
[2, 'Walnut Brownie3'],
[2, 'Walnut Brownie4'],
[2, 'Walnut Brownie5'],
[2, 'Walnut Brownie6'],
[2, 'Walnut Brownie7'],
[2, 'Walnut Brownie8'],
[2, 'Walnut Brownie9'],
[2, 'Walnut Brownie91'],
[2, 'Walnut Brownie92'],
[2, 'Walnut Brownie93'],
[2, 'Walnut Brownie94'],
[2, 'Walnut Brownie95'],
[2, 'Walnut Brownie96'],
[2, 'Walnut Brownie97'],
]
},
grid: {containLabel: true},
xAxis: {name: 'amount'},
yAxis: {type: 'category',boundaryGap:'20%'},
series: [
{
type: 'bar',
barWidth: 20,
barCategoryGap: "20%",
label:{
normal: {
position: 'right',
show: true,
color: '#000'
}
},
itemStyle:{
normal:{
color: '#fccb04'
},
emphasis:{
color: '#f7d139'
}
},
encode: {
x: 'amount',
y: 'product'
}
}
]
};
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option)
window.onresize = function(){
myChart.resize();
}
};
</script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="{% static "inspect/js/echarts.js" %}"></script>
<script type="text/javascript" src="{% static "inspect/js/all.js" %}"></script>
</body>
</html>
<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
<meta charset='utf-8' />
<title>违规项分析</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="{% static "inspect/css/element.css" %}">
<!-- 引入组件库 -->
<link rel='stylesheet' type='text/css' href="{% static "inspect/css/all.css" %}">
</head>
<body style='height:100%'>
<div class='child-title'>语音坐席</div>
<div class='child-main' id='childApp'>
<div style='text-align:right;' class='childTitle'>
<div class='childTitleName'>违规项分析</div>
<el-date-picker
v-model="dateRange"
@change='changeRange'
clearable='false'
type="daterange"
value-format='yyyy-MM-dd'
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']">
</el-date-picker>
<el-select v-model="taskvalue" filterable placeholder="请选择" @change="changeTask">
<el-option
v-for="item in taskList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<el-select v-model="takevalue" filterable placeholder="请选择" @change='changeTake'>
<el-option
label="全部坐席"
value="">
</el-option>
<el-option
v-for="item in takeList"
:key="item.agentName"
:label="item.agentName"
:value="item.agentName">
</el-option>
</el-select>
</div>
<div id='main' style='width:80%;height:600px' v-loading='true'></div>
</div>
<script type="text/javascript">
window.onload = function(){
};
</script>
<script src="{% static "inspect/js/vue.min.js" %}"></script>
<script src="{% static "inspect/js/element.js" %}"></script>
<script src="{% static "inspect/js/echarts.js" %}"></script>
<script src="{% static "inspect/js/http.js" %}"></script>
<script type="text/javascript" src="{% static "inspect/js/all.js" %}"></script>
</body>
</html>
<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
<meta charset='utf-8' />
<title>坐席违规分析</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="{% static "inspect/css/element.css" %}">
<!-- 引入组件库 -->
<link rel='stylesheet' type='text/css' href="{% static "inspect/css/all.css" %}">
</head>
<body style='height:100%;min-width:1000px'>
<div class='child-title'>语音坐席</div>
<div class='child-main' id='childApp'>
<div style='text-align:right;margin-bottom:30px;' class='childTitle'>
<div class='childTitleName'>违规坐席分析</div>
<el-date-picker
v-model="dateRange"
@change='changeRange'
clearable='false'
type="daterange"
value-format='yyyy-MM-dd'
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']">
</el-date-picker>
<el-select filterable v-model="taskvalue" placeholder="请选择" @change="changeTask">
<el-option
v-for="item in taskList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</div>
<div class='childStatistics'>
<div>
<div id='main' v-loading='true' style='height:600px;display:inline-block;'></div>
</div>
<div>
<div id='mainTwo' v-loading='true' style='height:600px;display:inline-block;'></div>
</div>
</div>
</div>
<script src="{% static "inspect/js/vue.min.js" %}"></script>
<script src="{% static "inspect/js/element.js" %}"></script>
<script src="{% static "inspect/js/echarts.js" %}"></script>
<script type="text/javascript" src="{% static "inspect/js/http.js" %}"></script>
<script type="text/javascript" src="{% static "inspect/js/violator.js" %}"></script>
<script type="text/javascript">
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
...@@ -15,7 +15,10 @@ Including another URLconf ...@@ -15,7 +15,10 @@ Including another URLconf
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path
from .api.tasksapi import rule, seat
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), # path('admin/', admin.site.urls),
path('inspect/rule/', rule),
path('inspect/seat/', seat),
] ]
...@@ -109,3 +109,11 @@ body{ ...@@ -109,3 +109,11 @@ body{
line-height: 30px; line-height: 30px;
float: left; float: left;
} }
.childStatistics > div{
width:49%;
display: inline-block;
text-align: center;
}
.childStatistics > div > div{
width: 500px;
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
new Vue({ new Vue({
el: '#childApp', el: '#childApp',
data:function(){ data:function(){
return { return {
ss:'sas', ss:'sas',
dateRange:[new Date(), new Date()], dateRange:[new Date(), new Date()],
taskList:[{ taskList:[],
value: '选项1', taskvalue: '',
label: '黄金糕' start_date: '',
}, { end_date:'',
value: '选项2', takeList:[],
label: '双皮奶' takevalue: '',
}, { loading: true,
value: '选项3',
label: '蚵仔煎'
}, {
value: '选项4',
label: '龙须面'
}, {
value: '选项5',
label: '北京烤鸭'
}],
taskvalue: '黄金糕',
takeList:[{
value: '选项1',
label: '黄金糕'
}, {
value: '选项2',
label: '双皮奶'
}, {
value: '选项3',
label: '蚵仔煎'
}, {
value: '选项4',
label: '龙须面'
}, {
value: '选项5',
label: '北京烤鸭'
}],
takevalue: '黄金糕'
} }
}, },
methods: { methods: {
changeRange(dateRange){ changeRange(dateRange){
console.log(dateRange); console.log(dateRange);
this.start_date = dateRange[0];
this.end_date = dateRange[1];
this.getRule(this.taskvalue,this.start_date,this.end_date,this.takevalue); // 获取统计数据
}, },
changeTask(msg){ changeTask(msg){
console.log(msg); console.log(msg);
this.taskvalue = msg;
this.getTake()
this.getRule(this.taskvalue,this.start_date,this.end_date,this.takevalue); // 获取统计数据
}, },
changeTake(msg){ changeTake(msg){
this.takevalue = msg;
this.getRule(this.taskvalue,this.start_date,this.end_date,this.takevalue); // 获取统计数据
console.log(msg); console.log(msg);
},
getTask(){
let that = this;
RquestsGet('api/v1/tasks/obtain/').then(data => {
//console.log(data);
if(data.code != 0){
that.taskList = [{id:'',name:'全部任务'}];
}else{
that.taskList = [{id:'',name:'全部任务'}].concat(data.data);
that.getTake()
}
})
},
getTake(){
let that = this;
RquestsGet('api/v1/tasks/seat/?task=' + that.taskvalue).then(data => {
//console.log(data);
if(data.code != 0){
//that.takeList = [{agentName:'全部坐席'}]
}else{
that.takeList = data.data;
}
})
},
Charts(msg){
let msgLen = msg.length;
let sourceArr = [
[ 'product', 'amount'],
].concat(msg);
var option = {
dataZoom : [
{
type: 'slider',
show: true,
// orient: 'horizontal',
orient: 'vertical',
zoomLock: true,
// start: 25,
// end: 100,
startValue: msgLen,
endValue: msgLen-10,
handleSize: 8
},
{
type: 'inside',
// start: 25,
// end: 100,
orient: 'vertical',
zoomLock: true,
// zoomOnMouseWheel:false,
startValue: 17,
endValue: 7,
},
{
type: 'slider',
show: true,
xAxisIndex: 0,
filterMode: 'empty',
width: 12,
// height: '70%',
handleSize: 8,
orient: 'vertical',
zoomLock: true,
showDataShadow: false,
left: '93%'
}
],
dataset: {
source: sourceArr
},
grid: {containLabel: true},
xAxis: {name: 'amount'},
yAxis: {type: 'category',boundaryGap:'20%'},
series: [
{
type: 'bar',
barWidth: 20,
barCategoryGap: "20%",
label:{
normal: {
position: 'right',
show: true,
color: '#000'
}
},
itemStyle:{
normal:{
color: '#fccb04'
},
emphasis:{
color: '#f7d139'
}
},
encode: {
x: 'amount',
y: 'product'
} }
} }
]
};
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option)
if(msg.length == 0){
myChart.showLoading({
text: '暂无数据',
color: '#ffffff',
textColor: '#8a8e91',
maskColor: 'rgba(255, 255, 255, 0.8)',
}
);
}else{
myChart.hideLoading();
}
window.onresize = function(){
myChart.resize();
}
},
getRule(task,start_date,end_date,agentName){
let that = this;
let arr = [];
this.$loading({text:'数据加载中...'});
RquestsPost('api/v1/tasks/rule/',{task,start_date,end_date,agentName}).then(data => {
console.log(data.data);
if(data.code != 0){
that.$message(data.msg);
if(data.msg){
that.$message(data.msg);
}else{
that.$message('服务器错误')
}
}
if(!data.data){
this.$loading().close();
return false;
}
//for(let key in data.data){
// arr.unshift([data.data[key],key])
//}
that.Charts(data.data);
this.$loading().close();
})
},
dateFormat(date){
return date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate();
},
init(){
this.dateRange = [new Date(new Date()-24*60*60*1000*30), new Date()];
this.start_date = this.dateFormat(new Date(new Date()-24*60*60*1000*30));
this.end_date = this.dateFormat(new Date());
this.getTask(); // 获取任务
//this.getTake(); // 获取坐席
console.log(this.dateRange);
this.getRule(this.taskvalue,this.start_date,this.end_date,this.takevalue); // 获取统计数据
}
},
created(){
this.init()
},
}) })
This diff is collapsed.
let window_url = "http://127.0.0.1:8099/";
function RquestsGet(url){
let uri = window_url + url;
return fetch(uri,{method: 'GET'})
.then(res => {
return res.json();
})
.then(data => {
return data;
})
.catch(error => {
return error;
})
}
function RquestsPost(url,data){
let uri = window_url + url;
return fetch(uri,{
body: JSON.stringify(data),
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
method: 'POST',
mode: 'cors',
redirect: 'follow',
referrer: 'no-referrer',
})
.then(res => {
return res.json();
})
.then(data => {
return data;
})
.catch(error => {
return error
})
}
new Vue({
el: '#childApp',
data:function(){
return {
ss:'sas',
dateRange:[new Date(), new Date()],
taskList:[],
taskvalue: '',
start_date: '',
end_date:'',
loading: true,
Series:[
{
type: 'bar',
barWidth: 20,
barCategoryGap: "20%",
label:{
normal: {
position: 'right',
show: true,
color: '#000'
}
},
itemStyle:{
normal:{
color: '#fccb04'
},
emphasis:{
color: '#f7d139'
}
},
encode: {
x: 'amount',
y: 'product'
}
}
]
}
},
methods: {
changeRange(dateRange){
console.log(dateRange);
this.start_date = dateRange[0];
this.end_date = dateRange[1];
this.getStatistics(this.taskvalue,this.start_date,this.end_date); // 获取统计数据
},
changeTask(msg){
console.log(msg);
this.taskvalue = msg;
this.getStatistics(this.taskvalue,this.start_date,this.end_date); // 获取统计数据
},
getTask(){
let that = this;
RquestsGet('api/v1/tasks/obtain/').then(data => {
//console.log(data);
if(data.code != 0){
that.taskList = [{id:'',name:'全部任务'}];
}else{
that.taskList = [{id:'',name:'全部任务'}].concat(data.data);
}
})
},
ChartsOne(msg){
let sourceArr = [
[ 'amount', 'product'],
].concat(msg);
let option = {
title:{
text:'坐席得分统计',
x: '50%',
textAlign: 'center'
},
dataset: {
source: sourceArr
},
grid: {containLabel: true},
xAxis: {name: 'amount',max:'100'},
yAxis: {type: 'category',boundaryGap:'20%'},
series: this.Series
};
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option)
if(msg.length == 0){
myChart.showLoading({
text: '暂无数据',
color: '#ffffff',
textColor: '#8a8e91',
maskColor: 'rgba(255, 255, 255, 0.8)',
}
);
}else{
myChart.hideLoading();
}
window.onresize = function(){
myChart.resize();
}
},
ChartsTwo(msg){
let sourceArr = [
[ 'amount', 'product'],
].concat(msg);
var optionTwo = {
title:{
text:'违规话数统计',
x: '50%',
textAlign: 'center'
},
dataset: {
source: sourceArr
},
grid: {containLabel: true},
xAxis: {name: 'amount'},
yAxis: {type: 'category',boundaryGap:'20%'},
series: this.Series
};
var myChartTwo = echarts.init(document.getElementById('mainTwo'));
myChartTwo.setOption(optionTwo);
if(msg.length == 0){
myChartTwo.showLoading({
text: '暂无数据',
color: '#ffffff',
textColor: '#8a8e91',
maskColor: 'rgba(255, 255, 255, 0.8)',
}
);
}else{
myChartTwo.hideLoading();
}
window.onresize = function(){
myChartTwo.resize();
}
},
getStatistics(task,start_date,end_date){
let that = this;
this.$loading({text:'数据加载中...'});
RquestsPost('api/v1/tasks/seat_score/',{task,start_date,end_date}).then(data => {
console.log(data.data);
let arr = [];
if(data.code != 0){
if(data.msg){
that.$message(data.msg);
}else{
that.$message('服务器错误')
}
}
if(!data.data){
this.$loading().close();
return false;
}
for(let key in data.data){
arr.push([data.data[key],key])
}
console.log(arr);
that.ChartsOne(arr);
});
RquestsPost('api/v1/tasks/seat_session/',{task,start_date,end_date}).then(data => {
console.log(data.data);
let arr = [];
if(data.code != 0){
if(data.msg){
that.$message(data.msg);
}else{
that.$message('服务器错误')
}
}
if(!data.data){
this.$loading().close();
return false;
}
for(let key in data.data){
arr.push([data.data[key],key])
};
console.log(arr);
that.ChartsTwo(arr);
this.$loading().close();
});
},
dateFormat(date){
return date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate();
},
init(){
this.dateRange = [new Date(new Date()-24*60*60*1000*30), new Date()];
this.start_date = this.dateFormat(new Date(new Date()-24*60*60*1000*30));
this.end_date = this.dateFormat(new Date());
this.getTask(); // 获取任务
let that = this;
console.log(this.dateRange);
this.getStatistics(this.taskvalue,this.start_date,this.end_date); // 获取统计数据
}
},
created(){
this.init()
},
})
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment