672 lines
21 KiB
Python
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')
|
|
}
|
|
}) |