Fix coverage: add /api/coverage route, remove stray gather code from loadCoverage
This commit is contained in:
201
simple_api.py
Normal file
201
simple_api.py
Normal file
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python3
|
||||
from flask import Flask, jsonify, request
|
||||
from flask_cors import CORS
|
||||
import json
|
||||
import h5py
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
# Load index once at startup
|
||||
with open('/data/index.json', 'r') as f:
|
||||
INDEX = json.load(f)
|
||||
|
||||
H5_DIR = Path('/data/h5')
|
||||
|
||||
@app.route('/api/nodes', methods=['GET'])
|
||||
def get_nodes():
|
||||
nodes_list = []
|
||||
for node_id, node_info in INDEX['nodes'].items():
|
||||
file_count = len(node_info.get('files', []))
|
||||
nodes_list.append({
|
||||
'id': node_id,
|
||||
'position': node_info.get('position', {}),
|
||||
'file_count': file_count,
|
||||
'hasDates': file_count > 0
|
||||
})
|
||||
return jsonify({
|
||||
'nodes': nodes_list,
|
||||
'sampleRateHz': 500
|
||||
})
|
||||
|
||||
@app.route('/api/dates', methods=['GET'])
|
||||
def get_dates():
|
||||
return jsonify({'dates': INDEX['dates']})
|
||||
|
||||
@app.route('/api/migration-status', methods=['GET'])
|
||||
def get_migration_status():
|
||||
"""Status de la conversion RAW -> H5"""
|
||||
total_files = 345 # Connu du projet
|
||||
h5_files = list(H5_DIR.glob('*.h5'))
|
||||
converted = len(h5_files)
|
||||
|
||||
return jsonify({
|
||||
'summary': {
|
||||
'total_files': total_files,
|
||||
'converted_files': converted,
|
||||
'percentage': round(converted / total_files * 100, 1),
|
||||
'status': 'in_progress' if converted < total_files else 'complete'
|
||||
},
|
||||
'h5_files_available': converted
|
||||
})
|
||||
|
||||
@app.route('/api/rms-timeline', methods=['GET'])
|
||||
def get_rms_timeline():
|
||||
"""Timeline RMS pour un channel et une date donnés"""
|
||||
date = request.args.get('date')
|
||||
channel = request.args.get('channel', 'ch0')
|
||||
|
||||
if not date:
|
||||
return jsonify({'error': 'date parameter required'}), 400
|
||||
|
||||
# Chercher les fichiers H5 pour cette date
|
||||
timeline = []
|
||||
for node_id, node_info in INDEX['nodes'].items():
|
||||
for file_info in node_info.get('files', []):
|
||||
if file_info.get('date') == date:
|
||||
# Pour l'instant, retourner des données mock
|
||||
# TODO: Calculer le vrai RMS depuis les fichiers H5
|
||||
timeline.append({
|
||||
'node_id': node_id,
|
||||
'timestamp': 0,
|
||||
'rms': 0 # À calculer depuis H5
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
'date': date,
|
||||
'channel': channel,
|
||||
'timeline': timeline
|
||||
})
|
||||
|
||||
@app.route('/api/data', methods=['GET'])
|
||||
def get_waveform_data():
|
||||
"""Données waveform pour un node, date, channel, timestamp"""
|
||||
node_id = request.args.get('node')
|
||||
date = request.args.get('date')
|
||||
channel = request.args.get('channel', 'ch0')
|
||||
start = float(request.args.get('start', 0))
|
||||
duration = float(request.args.get('duration', 10))
|
||||
|
||||
if not node_id or not date:
|
||||
return jsonify({'error': 'node and date required'}), 400
|
||||
|
||||
# Chercher le fichier H5 correspondant
|
||||
node_info = INDEX['nodes'].get(node_id)
|
||||
if not node_info:
|
||||
return jsonify({'error': 'node not found'}), 404
|
||||
|
||||
h5_file = None
|
||||
for file_info in node_info.get('files', []):
|
||||
if file_info.get('date') == date:
|
||||
h5_file = H5_DIR / file_info.get('filename', '')
|
||||
break
|
||||
|
||||
if not h5_file or not h5_file.exists():
|
||||
return jsonify({'error': 'H5 file not found'}), 404
|
||||
|
||||
try:
|
||||
with h5py.File(h5_file, 'r') as f:
|
||||
sample_rate = f['metadata'].attrs['sample_rate_hz']
|
||||
channel_num = int(channel.replace('ch', '').replace('CH', ''))
|
||||
|
||||
# Lire les données calibrées
|
||||
dataset = f[f'calibrated_data/channel_{channel_num + 1}']
|
||||
start_sample = int(start * sample_rate)
|
||||
end_sample = int((start + duration) * sample_rate)
|
||||
|
||||
# Limiter à la taille du dataset
|
||||
end_sample = min(end_sample, dataset.shape[0])
|
||||
|
||||
if start_sample >= dataset.shape[0]:
|
||||
return jsonify({'error': 'start time out of range'}), 400
|
||||
|
||||
data = dataset[start_sample:end_sample]
|
||||
|
||||
# Calculer stats
|
||||
rms = float(np.sqrt(np.mean(data ** 2)))
|
||||
peak = float(np.max(np.abs(data)))
|
||||
|
||||
return jsonify({
|
||||
'node_id': node_id,
|
||||
'date': date,
|
||||
'channel': channel,
|
||||
'start': start,
|
||||
'duration': duration,
|
||||
'sample_rate': int(sample_rate),
|
||||
'data': data.tolist(),
|
||||
'stats': {
|
||||
'rms': rms,
|
||||
'peak': peak,
|
||||
'samples': len(data)
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/api/h5/files', methods=['GET'])
|
||||
def get_h5_files():
|
||||
"""Liste des fichiers H5 disponibles avec métadonnées"""
|
||||
files = []
|
||||
for h5_path in H5_DIR.glob('*.h5'):
|
||||
try:
|
||||
with h5py.File(h5_path, 'r') as f:
|
||||
files.append({
|
||||
'filename': h5_path.name,
|
||||
'size_mb': round(h5_path.stat().st_size / 1024 / 1024, 2),
|
||||
'duration_sec': int(f['metadata'].attrs.get('duration_sec', 0)),
|
||||
'sample_rate': int(f['metadata'].attrs.get('sample_rate_hz', 500)),
|
||||
'channels': int(f['metadata'].attrs.get('n_channels', 4))
|
||||
})
|
||||
except:
|
||||
pass
|
||||
|
||||
return jsonify({'files': files, 'count': len(files)})
|
||||
|
||||
@app.route('/api/h5/coverage', methods=['GET'])
|
||||
def get_h5_coverage():
|
||||
"""Matrice de couverture nodes x dates"""
|
||||
coverage = {}
|
||||
for node_id, node_info in INDEX['nodes'].items():
|
||||
node_dates = [f['date'] for f in node_info.get('files', []) if 'date' in f]
|
||||
coverage[node_id] = node_dates
|
||||
|
||||
return jsonify({
|
||||
'coverage': coverage,
|
||||
'total_nodes': len(coverage),
|
||||
'total_dates': len(INDEX['dates'])
|
||||
})
|
||||
|
||||
@app.route('/api/chat', methods=['POST'])
|
||||
def chat():
|
||||
"""Endpoint chat assistant (mock)"""
|
||||
data = request.json
|
||||
message = data.get('message', '')
|
||||
|
||||
return jsonify({
|
||||
'response': f"[Mock] Vous avez dit: {message}",
|
||||
'timestamp': datetime.now().isoformat()
|
||||
})
|
||||
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health():
|
||||
return jsonify({'status': 'ok', 'nodes': len(INDEX['nodes']), 'dates': len(INDEX['dates'])})
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(f"Loaded {len(INDEX['nodes'])} nodes, {len(INDEX['dates'])} dates")
|
||||
print(f"H5 directory: {H5_DIR}")
|
||||
app.run(host='0.0.0.0', port=3004)
|
||||
Reference in New Issue
Block a user