#!/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)