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//') @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 )