327 lines
14 KiB
Python
Executable File
327 lines
14 KiB
Python
Executable File
from flask import render_template, flash, redirect, url_for, send_file
|
|
from flask_login import login_required, current_user
|
|
from scripts.models import db, WorkOrder, WorkOrderHandling, ManualWorkOrder
|
|
from datetime import datetime, timedelta
|
|
from collections import defaultdict
|
|
from openpyxl import Workbook
|
|
from openpyxl.styles import Font, Alignment
|
|
from utils.reward import get_reward_by_distance
|
|
from scripts.models import Device
|
|
import io
|
|
|
|
def init_statistics_routes(app):
|
|
@app.route('/statistics')
|
|
@login_required
|
|
def statistics():
|
|
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
|
|
if current_user.role not in allowed_roles and current_user.name != 'Admin':
|
|
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问统计页面')
|
|
flash('您没有权限访问此页面')
|
|
return redirect(url_for('login'))
|
|
|
|
app.logger.info(f'用户 {current_user.name} 访问统计页面')
|
|
# 获取当前时间
|
|
now = datetime.now()
|
|
|
|
# 计算本周开始和结束时间
|
|
week_start = now - timedelta(days=now.weekday())
|
|
week_start = week_start.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
week_end = week_start + timedelta(days=7)
|
|
|
|
# 计算本月开始和结束时间
|
|
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
|
if now.month == 12:
|
|
month_end = now.replace(year=now.year + 1, month=1, day=1)
|
|
else:
|
|
month_end = now.replace(month=now.month + 1, day=1)
|
|
|
|
# 统计普通工单
|
|
def get_work_order_stats(start_time, end_time):
|
|
total = WorkOrder.query.filter(
|
|
WorkOrder.dispatch_time >= start_time,
|
|
WorkOrder.dispatch_time < end_time
|
|
).count()
|
|
|
|
completed = WorkOrder.query.filter(
|
|
WorkOrder.dispatch_time >= start_time,
|
|
WorkOrder.dispatch_time < end_time,
|
|
WorkOrder.status == '已处理'
|
|
).count()
|
|
|
|
pending = total - completed
|
|
return {'total': total, 'completed': completed, 'pending': pending}
|
|
|
|
# 统计手工故障单
|
|
def get_manual_order_stats(start_time, end_time):
|
|
total = ManualWorkOrder.query.filter(
|
|
ManualWorkOrder.create_time >= start_time,
|
|
ManualWorkOrder.create_time < end_time
|
|
).count()
|
|
return total
|
|
|
|
# 计算奖励金额
|
|
def calculate_reward(start_time, end_time):
|
|
rewards = defaultdict(float)
|
|
|
|
# 查询所有已处理的工单
|
|
handlings = WorkOrderHandling.query.filter(
|
|
WorkOrderHandling.restore_time >= start_time,
|
|
WorkOrderHandling.restore_time < end_time
|
|
).all()
|
|
|
|
# 查询所有手工故障单
|
|
manual_orders = ManualWorkOrder.query.filter(
|
|
ManualWorkOrder.create_time >= start_time,
|
|
ManualWorkOrder.create_time < end_time
|
|
).all()
|
|
|
|
# 计算普通工单奖励
|
|
for handling in handlings:
|
|
device = Device.query.get(handling.device_id)
|
|
if device:
|
|
distance = float(handling.distance_from_branch)
|
|
reward = get_reward_by_distance(device.project_type, distance)
|
|
rewards[device.project_type] += reward
|
|
|
|
# 计算手工故障单奖励
|
|
for order in manual_orders:
|
|
distance = float(order.distance_from_branch)
|
|
reward = get_reward_by_distance(order.project_type, distance)
|
|
rewards[order.project_type] += reward
|
|
|
|
return rewards
|
|
|
|
def get_handler_stats(start_time, end_time):
|
|
handler_stats = defaultdict(lambda: {
|
|
'work_orders': 0,
|
|
'manual_orders': 0,
|
|
'rewards': defaultdict(float)
|
|
})
|
|
|
|
# 统计普通工单
|
|
handlings = WorkOrderHandling.query.filter(
|
|
WorkOrderHandling.restore_time >= start_time,
|
|
WorkOrderHandling.restore_time < end_time
|
|
).all()
|
|
|
|
for handling in handlings:
|
|
handler = handling.handler
|
|
handler_stats[handler]['work_orders'] += 1
|
|
device = Device.query.get(handling.device_id)
|
|
if device:
|
|
distance = float(handling.distance_from_branch)
|
|
reward = get_reward_by_distance(device.project_type, distance)
|
|
handler_stats[handler]['rewards'][device.project_type] += reward
|
|
|
|
# 统计手工故障单
|
|
manual_orders = ManualWorkOrder.query.filter(
|
|
ManualWorkOrder.create_time >= start_time,
|
|
ManualWorkOrder.create_time < end_time
|
|
).all()
|
|
|
|
for order in manual_orders:
|
|
handler = order.handler
|
|
handler_stats[handler]['manual_orders'] += 1
|
|
distance = float(order.distance_from_branch)
|
|
reward = get_reward_by_distance(order.project_type, distance)
|
|
handler_stats[handler]['rewards'][order.project_type] += reward
|
|
|
|
return handler_stats
|
|
|
|
# 获取统计数据
|
|
week_work_stats = get_work_order_stats(week_start, week_end)
|
|
month_work_stats = get_work_order_stats(month_start, month_end)
|
|
|
|
week_manual_stats = get_manual_order_stats(week_start, week_end)
|
|
month_manual_stats = get_manual_order_stats(month_start, month_end)
|
|
|
|
week_rewards = calculate_reward(week_start, week_end)
|
|
month_rewards = calculate_reward(month_start, month_end)
|
|
|
|
week_handler_stats = get_handler_stats(week_start, week_end)
|
|
month_handler_stats = get_handler_stats(month_start, month_end)
|
|
|
|
# 如果是装维员,只显示自己的数据
|
|
if current_user.role == '装维员':
|
|
week_work_stats = {
|
|
'completed': WorkOrderHandling.query.filter(
|
|
WorkOrderHandling.restore_time >= week_start,
|
|
WorkOrderHandling.restore_time < week_end,
|
|
WorkOrderHandling.handler == current_user.name
|
|
).count()
|
|
}
|
|
month_work_stats = {
|
|
'completed': WorkOrderHandling.query.filter(
|
|
WorkOrderHandling.restore_time >= month_start,
|
|
WorkOrderHandling.restore_time < month_end,
|
|
WorkOrderHandling.handler == current_user.name
|
|
).count()
|
|
}
|
|
week_manual_stats = ManualWorkOrder.query.filter(
|
|
ManualWorkOrder.create_time >= week_start,
|
|
ManualWorkOrder.create_time < week_end,
|
|
ManualWorkOrder.handler == current_user.name
|
|
).count()
|
|
month_manual_stats = ManualWorkOrder.query.filter(
|
|
ManualWorkOrder.create_time >= month_start,
|
|
ManualWorkOrder.create_time < month_end,
|
|
ManualWorkOrder.handler == current_user.name
|
|
).count()
|
|
# 修改这里:只获取当前装维员的奖励统计
|
|
week_rewards = calculate_reward(week_start, week_end)
|
|
month_rewards = calculate_reward(month_start, month_end)
|
|
week_handler_stats = {current_user.name: week_handler_stats.get(current_user.name, defaultdict(int))}
|
|
month_handler_stats = {current_user.name: month_handler_stats.get(current_user.name, defaultdict(int))}
|
|
|
|
# 从 handler_stats 中提取当前用户的奖励数据
|
|
week_rewards = week_handler_stats[current_user.name]['rewards']
|
|
month_rewards = month_handler_stats[current_user.name]['rewards']
|
|
|
|
return render_template('statistics.html',
|
|
week_work_stats=week_work_stats,
|
|
month_work_stats=month_work_stats,
|
|
week_manual_stats=week_manual_stats,
|
|
month_manual_stats=month_manual_stats,
|
|
week_rewards=week_rewards,
|
|
month_rewards=month_rewards,
|
|
week_handler_stats=week_handler_stats,
|
|
month_handler_stats=month_handler_stats)
|
|
@app.route('/export_monthly_stats/<int:year>/<int:month>')
|
|
@login_required
|
|
def export_monthly_stats(year, month):
|
|
if current_user.role != '统计员':
|
|
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试导出月度统计')
|
|
flash('您没有权限执行此操作')
|
|
return redirect(url_for('statistics'))
|
|
|
|
app.logger.info(f'用户 {current_user.name} 导出 {year}年{month}月 统计报表')
|
|
|
|
# 计算指定月份的开始和结束时间
|
|
month_start = datetime(year, month, 1, 0, 0, 0)
|
|
if month == 12:
|
|
month_end = datetime(year + 1, 1, 1, 0, 0, 0)
|
|
else:
|
|
month_end = datetime(year, month + 1, 1, 0, 0, 0)
|
|
|
|
# 检查是否有数据
|
|
handlings = WorkOrderHandling.query.filter(
|
|
WorkOrderHandling.restore_time >= month_start,
|
|
WorkOrderHandling.restore_time < month_end
|
|
).first()
|
|
|
|
if not handlings:
|
|
flash(f'{year}年{month}月系统无检修工单')
|
|
return redirect(url_for('statistics'))
|
|
|
|
# 创建工作簿
|
|
wb = Workbook()
|
|
ws = wb.active
|
|
ws.title = "运维计件奖励"
|
|
|
|
# 设置标题
|
|
ws.merge_cells('A1:J1')
|
|
title = ws.cell(1, 1, f"{year}年{month}月政企重点项目运维各支局检修激励汇总")
|
|
title.font = Font(size=14, bold=True)
|
|
title.alignment = Alignment(horizontal='center')
|
|
|
|
# 设置表头
|
|
headers = ['序号', '检修单位名称', '检修项目', '检修设备总数(台)',
|
|
'单位距支局维护人员所在地址(公里)', '检修设备每个单价(元)',
|
|
'检修项目合计数(路)', '本月检修金额(元)', '支局', '检修人员','本月个人检修合计','检修时间']
|
|
for col, header in enumerate(headers, 1):
|
|
cell = ws.cell(2, col, header)
|
|
cell.font = Font(bold=True)
|
|
cell.alignment = Alignment(horizontal='center', wrap_text=True)
|
|
|
|
# 获取指定月份数据
|
|
handlings = WorkOrderHandling.query.filter(
|
|
WorkOrderHandling.restore_time >= month_start,
|
|
WorkOrderHandling.restore_time < month_end
|
|
).all()
|
|
|
|
# 查询本月所有手工故障单
|
|
manual_orders = ManualWorkOrder.query.filter(
|
|
ManualWorkOrder.create_time >= month_start,
|
|
ManualWorkOrder.create_time < month_end
|
|
).order_by(ManualWorkOrder.create_time).all()
|
|
|
|
# 填充数据
|
|
row = 3
|
|
idx = 0 # 初始化序号计数器
|
|
|
|
# 处理普通工单
|
|
for handling in handlings:
|
|
device = db.session.get(Device, handling.device_id)
|
|
if not device:
|
|
continue
|
|
|
|
idx += 1 # 增加序号
|
|
ws.cell(row, 1, idx) # 序号
|
|
ws.cell(row, 2, device.device_id) # 检修单位名称
|
|
ws.cell(row, 3, device.project_type) # 检修项目
|
|
ws.cell(row, 4, 1) # 检修设备总数
|
|
ws.cell(row, 5, handling.distance_from_branch) # 距离
|
|
|
|
# 计算单价
|
|
distance = float(handling.distance_from_branch)
|
|
reward = get_reward_by_distance(device.project_type, distance)
|
|
ws.cell(row, 6, reward) # 单价
|
|
|
|
ws.cell(row, 7, 1) # 检修项目合计数
|
|
ws.cell(row, 8, reward) # 本月检修金额
|
|
ws.cell(row, 9, handling.branch) # 支局
|
|
ws.cell(row, 10, handling.handler) # 检修人员
|
|
ws.cell(row, 12, handling.restore_time.strftime('%Y-%m-%d %H:%M:%S')) # 检修时间
|
|
|
|
row += 1
|
|
|
|
# 处理手工故障单
|
|
for manual_order in manual_orders:
|
|
idx += 1 # 增加序号
|
|
ws.cell(row, 1, idx) # 序号
|
|
ws.cell(row, 2, manual_order.device_location) # 检修单位名称
|
|
ws.cell(row, 3, manual_order.project_type) # 检修项目
|
|
ws.cell(row, 4, 1) # 检修设备总数
|
|
ws.cell(row, 5, manual_order.distance_from_branch) # 距离
|
|
|
|
# 计算单价
|
|
distance = float(manual_order.distance_from_branch)
|
|
reward = get_reward_by_distance(manual_order.project_type, distance)
|
|
ws.cell(row, 6, reward) # 单价
|
|
|
|
ws.cell(row, 7, 1) # 检修项目合计数
|
|
ws.cell(row, 8, reward) # 本月检修金额
|
|
ws.cell(row, 9, manual_order.branch) # 支局
|
|
ws.cell(row, 10, manual_order.handler) # 检修人员
|
|
ws.cell(row, 12, manual_order.create_time.strftime('%Y-%m-%d %H:%M:%S')) # 检修时间
|
|
|
|
row += 1
|
|
idx += 1
|
|
|
|
# 添加合计行
|
|
ws.cell(row, 7, f"合计")
|
|
ws.cell(row, 8, f"=SUM(H3:H{row-1})")
|
|
ws.cell(row, 8).font = Font(bold=True)
|
|
|
|
# 设置列宽
|
|
ws.column_dimensions['B'].width = 20
|
|
ws.column_dimensions['C'].width = 15
|
|
ws.column_dimensions['E'].width = 15
|
|
ws.column_dimensions['F'].width = 15
|
|
ws.column_dimensions['H'].width = 15
|
|
|
|
# 保存到内存中
|
|
excel_file = io.BytesIO()
|
|
wb.save(excel_file)
|
|
excel_file.seek(0)
|
|
|
|
# 设置文件名
|
|
filename = f'{year}年{month}月政企重点项目运维各支局检修激励汇总.xlsx'
|
|
|
|
return send_file(
|
|
excel_file,
|
|
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
as_attachment=True,
|
|
download_name=filename
|
|
)
|