program/routes.py

672 lines
21 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Flask API 路由
"""
import os
import json
from flask import Blueprint, request, jsonify, send_file
from datetime import datetime
from collector import (
get_unit_list, get_unit_by_code, get_tag_list,
collect_data, get_latest_snapshot, get_history_data
)
from ai_analyzer import (
analyze_fault, analyze_trend, generate_report as ai_generate_report,
get_available_models
)
from reports import (
create_word_report, create_data_table_report, generate_full_report
)
from database import get_connection
from tag_names import get_tag_description, enrich_data_with_names
# 创建蓝图
api = Blueprint('api', __name__, url_prefix='/api')
# ==================== 单位管理 ====================
@api.route('/units', methods=['GET'])
def list_units():
"""获取所有工作面单位列表"""
try:
units = get_unit_list()
return jsonify({
'success': True,
'data': units
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/units/<unit_code>', methods=['GET'])
def get_unit(unit_code):
"""获取指定单位信息"""
try:
unit = get_unit_by_code(unit_code)
if unit:
return jsonify({
'success': True,
'data': unit
})
return jsonify({
'success': False,
'error': '单位不存在'
}), 404
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/units/<unit_code>/tags', methods=['GET'])
def list_tags(unit_code):
"""获取标签列表"""
try:
device_type = request.args.get('device_type')
tag_category = request.args.get('tag_category')
tags = get_tag_list(unit_code, device_type=device_type, tag_category=tag_category)
return jsonify({
'success': True,
'data': tags
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
# ==================== 数据采集 ====================
@api.route('/collect', methods=['POST'])
def start_collect():
"""启动数据采集"""
try:
data = request.get_json()
unit_code = data.get('unit_code')
device_type = data.get('device_type')
tag_category = data.get('tag_category')
if not unit_code:
return jsonify({
'success': False,
'error': '请指定单位代码'
}), 400
from flask import current_app
result = collect_data(
unit_code=unit_code,
api_url=current_app.config['API_URL'],
user_handle=current_app.config['USER_HANDLE'],
batch_size=current_app.config.get('BATCH_SIZE', 100),
device_type=device_type,
tag_category=tag_category
)
return jsonify({
'success': result.get('status') == 'success',
'data': result
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/collect/all', methods=['POST'])
def collect_all():
"""采集所有单位数据"""
try:
from flask import current_app
units = get_unit_list()
results = []
for unit in units:
result = collect_data(
unit_code=unit['unit_code'],
api_url=current_app.config['API_URL'],
user_handle=current_app.config['USER_HANDLE'],
batch_size=current_app.config.get('BATCH_SIZE', 100)
)
results.append(result)
return jsonify({
'success': True,
'data': results
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
# ==================== 数据查询 ====================
@api.route('/snapshot/<unit_code>', methods=['GET'])
def get_snapshot(unit_code):
"""获取最新快照数据(含中文名称)"""
try:
snapshot = get_latest_snapshot(unit_code)
if snapshot:
# 为数据添加中文名称
if 'data' in snapshot and isinstance(snapshot['data'], list):
snapshot['data'] = enrich_data_with_names(snapshot['data'])
return jsonify({
'success': True,
'data': snapshot
})
return jsonify({
'success': False,
'error': '暂无数据'
}), 404
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/history', methods=['GET'])
def get_history():
"""获取历史数据(含中文名称)"""
try:
unit_code = request.args.get('unit_code')
start_time = request.args.get('start_time')
end_time = request.args.get('end_time')
tag_name = request.args.get('tag_name')
if not unit_code or not start_time or not end_time:
return jsonify({
'success': False,
'error': '请指定 unit_code, start_time, end_time'
}), 400
data = get_history_data(unit_code, start_time, end_time, tag_name)
# 为数据添加中文名称
if isinstance(data, list):
data = enrich_data_with_names(data)
elif isinstance(data, dict) and 'data' in data and isinstance(data['data'], list):
data['data'] = enrich_data_with_names(data['data'])
return jsonify({
'success': True,
'data': data
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/realtime/<unit_code>', methods=['GET'])
def get_realtime(unit_code):
"""获取实时数据(最新快照的数据列表,含中文名称)"""
try:
snapshot = get_latest_snapshot(unit_code)
if snapshot and 'data' in snapshot:
data = enrich_data_with_names(snapshot['data'])
return jsonify({
'success': True,
'data': data,
'snapshot_time': snapshot.get('snapshot_time')
})
return jsonify({
'success': False,
'error': '暂无数据'
}), 404
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
# ==================== AI分析 ====================
@api.route('/ai/fault', methods=['POST'])
def ai_fault_analysis():
"""AI故障诊断"""
try:
data = request.get_json()
unit_code = data.get('unit_code')
model_key = data.get('model_key')
if not unit_code:
return jsonify({
'success': False,
'error': '请指定单位代码'
}), 400
# 获取最新数据
snapshot = get_latest_snapshot(unit_code)
if not snapshot or 'data' not in snapshot:
return jsonify({
'success': False,
'error': '暂无数据,请先采集'
}), 400
# AI分析
result = analyze_fault(snapshot['data'], model_key=model_key)
return jsonify({
'success': result.get('status') == 'success',
'data': result
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/ai/trend', methods=['POST'])
def ai_trend_analysis():
"""AI趋势分析"""
try:
data = request.get_json()
unit_code = data.get('unit_code')
start_time = data.get('start_time')
end_time = data.get('end_time')
model_key = data.get('model_key')
if not unit_code or not start_time or not end_time:
return jsonify({
'success': False,
'error': '请指定 unit_code, start_time, end_time'
}), 400
# 获取历史数据
history = get_history_data(unit_code, start_time, end_time)
# AI分析
result = analyze_trend(history, model_key=model_key)
return jsonify({
'success': result.get('status') == 'success',
'data': result
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/ai/report', methods=['POST'])
def ai_generate():
"""AI生成运维报告"""
try:
data = request.get_json()
unit_code = data.get('unit_code')
model_key = data.get('model_key')
if not unit_code:
return jsonify({
'success': False,
'error': '请指定单位代码'
}), 400
# 获取单位信息
unit = get_unit_by_code(unit_code)
if not unit:
return jsonify({
'success': False,
'error': '单位不存在'
}), 404
# 获取最新数据
snapshot = get_latest_snapshot(unit_code)
if not snapshot or 'data' not in snapshot:
return jsonify({
'success': False,
'error': '暂无数据,请先采集'
}), 400
# AI生成报告
result = ai_generate_report(snapshot['data'], unit['unit_name'], model_key=model_key)
return jsonify({
'success': result.get('status') == 'success',
'data': result
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/ai/models', methods=['GET'])
def list_models():
"""获取可用AI模型列表"""
try:
models = get_available_models()
return jsonify({
'success': True,
'data': models
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/tags/names', methods=['GET'])
def get_tag_names():
"""获取所有标签中文名称映射"""
try:
from tag_names import get_all_tag_names
tag_map = get_all_tag_names()
return jsonify({
'success': True,
'data': tag_map,
'count': len(tag_map)
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/tags/lookup', methods=['POST'])
def lookup_tag_name():
"""查询单个标签的中文名称"""
try:
data = request.get_json()
tag_name = data.get('tag_name', '')
chinese_name = get_tag_description(tag_name)
return jsonify({
'success': True,
'data': {
'tag_name': tag_name,
'chinese_name': chinese_name
}
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
# ==================== 报告生成 ====================
@api.route('/report/word', methods=['POST'])
def generate_word_report():
"""生成Word报告"""
try:
data = request.get_json()
unit_code = data.get('unit_code')
report_type = data.get('type', 'data') # data, ai, full
unit = get_unit_by_code(unit_code)
if not unit:
return jsonify({
'success': False,
'error': '单位不存在'
}), 404
snapshot = get_latest_snapshot(unit_code)
if not snapshot or 'data' not in snapshot:
return jsonify({
'success': False,
'error': '暂无数据'
}), 400
if report_type == 'data':
file_path = create_data_table_report(unit['unit_name'], snapshot['data'])
elif report_type == 'ai':
ai_result = ai_generate_report(snapshot['data'], unit['unit_name'])
if ai_result.get('status') == 'success':
file_path = create_word_report(
f"{unit['unit_name']} AI分析报告",
ai_result['content']
)
else:
return jsonify({
'success': False,
'error': ai_result.get('content', 'AI分析失败')
}), 500
else:
# 完整报告
ai_result = ai_generate_report(snapshot['data'], unit['unit_name'])
if ai_result.get('status') == 'success':
model_name = ai_result.get('model_name', 'AI')
file_path = generate_full_report(
unit_code, unit['unit_name'],
snapshot['data'], ai_result['content'],
model_name
)
else:
return jsonify({
'success': False,
'error': ai_result.get('content', 'AI分析失败')
}), 500
return jsonify({
'success': True,
'data': {
'file_path': file_path,
'filename': os.path.basename(file_path)
}
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/report/download/<path:filename>', methods=['GET'])
def download_report(filename):
"""下载报告文件"""
try:
output_dir = os.path.join(os.path.dirname(__file__), 'outputs')
file_path = os.path.join(output_dir, filename)
if not os.path.exists(file_path):
return jsonify({
'success': False,
'error': '文件不存在'
}), 404
return send_file(
file_path,
as_attachment=True,
download_name=filename
)
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
# ==================== 统计信息 ====================
@api.route('/stats/<unit_code>', methods=['GET'])
def get_stats(unit_code):
"""获取单个工作面统计信息(增强版)"""
try:
conn = get_connection()
try:
with conn.cursor() as cursor:
# 获取unit_id
cursor.execute("SELECT id FROM mine_units WHERE unit_code = %s", (unit_code,))
unit = cursor.fetchone()
if not unit:
return jsonify({
'success': False,
'error': '单位不存在'
}), 404
unit_id = unit['id']
# 采集次数(总快照数)
cursor.execute("""
SELECT COUNT(*) as count
FROM data_snapshots
WHERE unit_id = %s
""", (unit_id,))
collect_count = cursor.fetchone()['count']
# 最新采集时间
cursor.execute("""
SELECT MAX(snapshot_time) as last_time
FROM data_snapshots
WHERE unit_id = %s
""", (unit_id,))
last_result = cursor.fetchone()
last_time = last_result['last_time']
# 今日采集次数
cursor.execute("""
SELECT COUNT(*) as count
FROM data_snapshots
WHERE unit_id = %s AND DATE(snapshot_time) = CURDATE()
""", (unit_id,))
today_count = cursor.fetchone()['count']
# 监测点数量(标签数)
cursor.execute("""
SELECT COUNT(DISTINCT tag_name) as tag_count
FROM tag_data
WHERE snapshot_id IN (SELECT id FROM data_snapshots WHERE unit_id = %s)
""", (unit_id,))
tag_count = cursor.fetchone()['tag_count']
# 数据总量
cursor.execute("""
SELECT COUNT(*) as total
FROM tag_data
WHERE snapshot_id IN (SELECT id FROM data_snapshots WHERE unit_id = %s)
""", (unit_id,))
data_total = cursor.fetchone()['total']
# 按设备类型统计标签数
cursor.execute("""
SELECT device_type, COUNT(DISTINCT tag_name) as cnt
FROM tag_data
WHERE snapshot_id IN (SELECT id FROM data_snapshots WHERE unit_id = %s)
GROUP BY device_type
""", (unit_id,))
device_stats = {row['device_type']: row['cnt'] for row in cursor.fetchall()}
return jsonify({
'success': True,
'data': {
'collect_count': collect_count,
'last_time': str(last_time) if last_time else None,
'today_count': today_count,
'tag_count': tag_count,
'data_total': data_total,
'device_stats': device_stats
}
})
finally:
conn.close()
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@api.route('/stats/all', methods=['GET'])
def get_all_stats():
"""获取所有工作面的总体统计信息"""
try:
conn = get_connection()
try:
with conn.cursor() as cursor:
# 工作面数量
cursor.execute("SELECT COUNT(*) as count FROM mine_units")
unit_count = cursor.fetchone()['count']
# 总采集次数
cursor.execute("SELECT COUNT(*) as count FROM data_snapshots")
total_collects = cursor.fetchone()['count']
# 今日总采集次数
cursor.execute("""
SELECT COUNT(*) as count
FROM data_snapshots
WHERE DATE(snapshot_time) = CURDATE()
""")
today_collects = cursor.fetchone()['count']
# 总数据量
cursor.execute("SELECT COUNT(*) as count FROM tag_data")
data_total = cursor.fetchone()['count']
# 总监测点数量(去重)
cursor.execute("SELECT COUNT(DISTINCT tag_name) as count FROM tag_data")
tag_count = cursor.fetchone()['count']
# 每个工作面的简要统计
cursor.execute("""
SELECT
mu.unit_code,
mu.unit_name,
COUNT(DISTINCT ds.id) as collect_count,
MAX(ds.snapshot_time) as last_time,
COUNT(DISTINCT td.tag_name) as tag_count
FROM mine_units mu
LEFT JOIN data_snapshots ds ON mu.id = ds.unit_id
LEFT JOIN tag_data td ON ds.id = td.snapshot_id
GROUP BY mu.id, mu.unit_code, mu.unit_name
ORDER BY mu.unit_name
""")
unit_stats = []
for row in cursor.fetchall():
unit_stats.append({
'unit_code': row['unit_code'],
'unit_name': row['unit_name'],
'collect_count': row['collect_count'],
'last_time': str(row['last_time']) if row['last_time'] else None,
'tag_count': row['tag_count']
})
return jsonify({
'success': True,
'data': {
'unit_count': unit_count,
'total_collects': total_collects,
'today_collects': today_collects,
'data_total': data_total,
'tag_count': tag_count,
'unit_stats': unit_stats
}
})
finally:
conn.close()
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
# ==================== 健康检查 ====================
@api.route('/health', methods=['GET'])
def health_check():
"""健康检查"""
return jsonify({
'success': True,
'data': {
'status': 'ok',
'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
})