REFACTOR: Split code and dynamic models
All checks were successful
Build docker container / Build image (push) Successful in 11m15s
All checks were successful
Build docker container / Build image (push) Successful in 11m15s
This commit is contained in:
242
src/app.py
242
src/app.py
@@ -1,94 +1,182 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import joblib
|
||||
import os
|
||||
from datetime import date
|
||||
|
||||
from db import fetch_data
|
||||
import threading
|
||||
from model_registry import register, load_meta
|
||||
from models_train import train_demand_model, train_nulo_model
|
||||
from datetime import datetime
|
||||
|
||||
from core.model_loader import ModelManager
|
||||
from core.predictor import PredictionHandler
|
||||
from core.model_trainer import ModelTrainer
|
||||
from config.models_config import ModelConfig
|
||||
|
||||
app = Flask(__name__)
|
||||
MODEL_DIR = "models"
|
||||
|
||||
def load_model(model_type):
|
||||
meta = load_meta()
|
||||
file = meta["current"].get(model_type)
|
||||
if not file:
|
||||
return None
|
||||
return joblib.load(os.path.join(MODEL_DIR, file))
|
||||
# Inicializar componentes
|
||||
model_config = ModelConfig()
|
||||
model_manager = ModelManager(model_config)
|
||||
prediction_handler = PredictionHandler(model_manager, model_config)
|
||||
model_trainer = ModelTrainer(model_manager, model_config)
|
||||
|
||||
def background_train():
|
||||
print("Fetching data...")
|
||||
df = fetch_data()
|
||||
print(f"Data fetched: {len(df)} rows")
|
||||
# Comprobar que las funciones estan disponibles en el código
|
||||
@app.before_first_request
|
||||
def check_functions():
|
||||
"""Verificar que todas las funciones existen"""
|
||||
from utils.dynamic_loader import execute_function
|
||||
|
||||
today = date.today().isoformat()
|
||||
os.makedirs(MODEL_DIR, exist_ok=True)
|
||||
for model_type in model_config.get_all_model_types():
|
||||
config = model_config.get_model_config(model_type)
|
||||
print(f"Verificando modelo: {model_type}")
|
||||
print(f" Módulo: {config.get('module')}")
|
||||
print(f" Función entrenamiento: {config.get('train_function')}")
|
||||
print(f" Función datos: {config.get('data_function')}")
|
||||
|
||||
# Verificar que la función de entrenamiento existe
|
||||
try:
|
||||
module = importlib.import_module(config.get('module'))
|
||||
if hasattr(module, config.get('train_function')):
|
||||
print(f" ✓ Función de entrenamiento encontrada")
|
||||
else:
|
||||
print(f" ✗ Función de entrenamiento NO encontrada")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {str(e)}")
|
||||
|
||||
print("Training demand model...")
|
||||
demand_model = train_demand_model(df)
|
||||
demand_file = f"demand_xgb_{today}.joblib"
|
||||
joblib.dump(demand_model, f"{MODEL_DIR}/{demand_file}")
|
||||
register("demand", demand_file, len(df))
|
||||
print("Demand model trained and saved.")
|
||||
|
||||
print("Training nulo model...")
|
||||
nulo_model = train_nulo_model(df)
|
||||
nulo_file = f"nulo_xgb_{today}.joblib"
|
||||
joblib.dump(nulo_model, f"{MODEL_DIR}/{nulo_file}")
|
||||
register("nulo", nulo_file, len(df))
|
||||
print("Nulo model trained and saved.")
|
||||
# Inicializar modelos al arrancar
|
||||
model_manager.init_models()
|
||||
|
||||
@app.route("/train", methods=["POST"])
|
||||
def train():
|
||||
threading.Thread(target=background_train).start()
|
||||
return jsonify({"status": "training started"})
|
||||
|
||||
@app.route("/predict_demand", methods=["GET"])
|
||||
def predict_demand():
|
||||
h3 = int(request.args["h3"])
|
||||
week = int(request.args["week"])
|
||||
dow = int(request.args["dow"])
|
||||
hour = int(request.args["hour"])
|
||||
|
||||
model = load_model("demand")
|
||||
X = [[h3, week, dow, hour]]
|
||||
pred = model.predict(X)[0]
|
||||
|
||||
return jsonify({"expected_demand": float(pred)})
|
||||
|
||||
@app.route("/predict_nulo", methods=["GET"])
|
||||
def predict_nulo():
|
||||
h3 = int(request.args["h3"])
|
||||
week = int(request.args["week"])
|
||||
dow = int(request.args["dow"])
|
||||
hour = int(request.args["hour"])
|
||||
|
||||
model = load_model("nulo")
|
||||
X = [[h3, week, dow, hour]]
|
||||
prob = model.predict_proba(X)[0][1]
|
||||
|
||||
return jsonify({"nulo_probability": float(prob)})
|
||||
|
||||
@app.route("/predict", methods=["GET"])
|
||||
def predict_legacy():
|
||||
h3 = int(request.args["h3"])
|
||||
week = int(request.args["week"])
|
||||
dow = int(request.args["dow"])
|
||||
hour = int(request.args["hour"])
|
||||
|
||||
model = load_model("nulo")
|
||||
X = [[h3, week, dow, hour]]
|
||||
prob = model.predict_proba(X)[0][1]
|
||||
|
||||
"""Entrenar modelos"""
|
||||
model_type = request.args.get('model_type')
|
||||
|
||||
# Validar que el tipo de modelo existe
|
||||
if model_type and not model_config.model_exists(model_type):
|
||||
return jsonify({
|
||||
"error": f"Model type '{model_type}' not found",
|
||||
"available_models": model_config.get_all_model_types()
|
||||
}), 400
|
||||
|
||||
threading.Thread(target=model_trainer.background_train, args=(model_type,)).start()
|
||||
|
||||
return jsonify({
|
||||
"predicted_nulo_prob": float(prob)
|
||||
"status": "training started",
|
||||
"model_type": model_type or "all",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
|
||||
@app.route("/predict", methods=["GET"])
|
||||
def predict():
|
||||
"""Endpoint único para predicciones"""
|
||||
return prediction_handler.handle_predict_request(request.args)
|
||||
|
||||
@app.route("/demand", methods=["GET"])
|
||||
def demand():
|
||||
"""Endpoint para obtener todas las predicciones"""
|
||||
return prediction_handler.handle_demand_request(request.args)
|
||||
|
||||
@app.route("/models/register", methods=["POST"])
|
||||
def register_new_model():
|
||||
"""Registrar un nuevo tipo de modelo dinámicamente"""
|
||||
data = request.get_json()
|
||||
|
||||
try:
|
||||
new_config = model_config.register_model_type(data)
|
||||
return jsonify({
|
||||
"status": "model type registered",
|
||||
"model_type": data["type"],
|
||||
"config": new_config
|
||||
})
|
||||
except ValueError as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
except Exception as e:
|
||||
return jsonify({"error": f"Registration failed: {str(e)}"}), 500
|
||||
|
||||
@app.route("/models", methods=["GET"])
|
||||
def models():
|
||||
return jsonify(load_meta())
|
||||
def list_models():
|
||||
"""Listar todos los modelos disponibles"""
|
||||
models_info = []
|
||||
|
||||
for model_type in model_config.get_all_model_types():
|
||||
model = model_manager.get_model(model_type)
|
||||
model_config_info = model_config.get_model_config(model_type)
|
||||
|
||||
model_info = {
|
||||
"type": model_type,
|
||||
"description": model_config_info.get("description", ""),
|
||||
"loaded": model is not None,
|
||||
"required_params": model_config_info.get("required_params", []),
|
||||
"module": model_config_info.get("module"),
|
||||
"train_function": model_config_info.get("train_function")
|
||||
}
|
||||
|
||||
# Añadir información del archivo si existe
|
||||
from model_registry import load_meta
|
||||
meta = load_meta()
|
||||
if model_type in meta.get("current", {}):
|
||||
model_info["file"] = meta["current"][model_type]
|
||||
|
||||
models_info.append(model_info)
|
||||
|
||||
return jsonify({
|
||||
"available_models": models_info,
|
||||
"total": len(models_info)
|
||||
})
|
||||
|
||||
@app.route("/health", methods=["GET"])
|
||||
def health():
|
||||
"""Endpoint de salud"""
|
||||
loaded_models = model_manager.get_loaded_models_status()
|
||||
all_models = model_config.get_all_model_types()
|
||||
|
||||
return jsonify({
|
||||
"status": "healthy" if all(loaded_models.values()) else "partial",
|
||||
"models_loaded": loaded_models,
|
||||
"total_configured": len(all_models),
|
||||
"loaded_count": sum(1 for v in loaded_models.values() if v)
|
||||
})
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def index():
|
||||
"""Página principal con documentación"""
|
||||
endpoints = [
|
||||
{
|
||||
"path": "/predict",
|
||||
"method": "GET",
|
||||
"description": "Realizar predicción",
|
||||
"parameters": "model_type (required), otros según modelo"
|
||||
},
|
||||
{
|
||||
"path": "/demand",
|
||||
"method": "GET",
|
||||
"description": "Obtener predicciones masivas",
|
||||
"parameters": "model_type (required), limit (opcional)"
|
||||
},
|
||||
{
|
||||
"path": "/train",
|
||||
"method": "POST",
|
||||
"description": "Entrenar modelos",
|
||||
"parameters": "model_type (opcional)"
|
||||
},
|
||||
{
|
||||
"path": "/models",
|
||||
"method": "GET",
|
||||
"description": "Listar modelos disponibles"
|
||||
},
|
||||
{
|
||||
"path": "/models/register",
|
||||
"method": "POST",
|
||||
"description": "Registrar nuevo tipo de modelo"
|
||||
},
|
||||
{
|
||||
"path": "/health",
|
||||
"method": "GET",
|
||||
"description": "Estado del sistema"
|
||||
}
|
||||
]
|
||||
|
||||
return jsonify({
|
||||
"service": "Model Prediction API",
|
||||
"version": "1.0.0",
|
||||
"endpoints": endpoints,
|
||||
"available_models": model_config.get_all_model_types()
|
||||
})
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5000)
|
||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||
Reference in New Issue
Block a user