Files
4an-workorder/app/views/statistics.py
T
2025-08-08 10:38:10 +08:00

326 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():
if current_user.role not in ['装维员', '统计员']:
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
)