from flask import Blueprint, render_template, request, redirect, url_for, flash from models import db, CatalogoPaneles, DatosSolaresCiudad, ProyectosUsuario, Casa, Configuracion from datetime import datetime main = Blueprint('main', __name__) @main.route('/') def index(): return render_template('index.html') @main.route('/configuracion', methods=['GET', 'POST']) def configuracion(): config_precio = Configuracion.query.filter_by(clave='precio_kwh').first() ciudades = DatosSolaresCiudad.query.all() casas = Casa.query.all() edit_id = request.args.get('edit_id') casa_editar = None if edit_id: casa_editar = Casa.query.get(edit_id) if request.method == 'POST': nuevo_precio = request.form.get('precio_kwh') if config_precio: config_precio.valor = nuevo_precio else: config_precio = Configuracion(clave='precio_kwh', valor=nuevo_precio) db.session.add(config_precio) db.session.commit() return redirect(url_for('main.configuracion')) return render_template('configuracion.html', precio_kwh=config_precio.valor if config_precio else '0.15', ciudades=ciudades, casas=casas, casa_editar=casa_editar) @main.route('/casa/editar/', methods=['POST']) def editar_casa(id_casa): casa = Casa.query.get_or_404(id_casa) casa.nombre = request.form.get('nombre') casa.area_techo = float(request.form.get('area_techo')) casa.orientacion = request.form.get('orientacion') inclinacion_str = request.form.get('inclinacion') casa.inclinacion = float(inclinacion_str) if inclinacion_str else 0.0 casa.id_ciudad = request.form.get('id_ciudad') db.session.commit() flash('Casa actualizada correctamente.', 'success') return redirect(url_for('main.configuracion')) @main.route('/casa/eliminar/', methods=['POST']) def eliminar_casa(id_casa): casa = Casa.query.get_or_404(id_casa) # Desvincular proyectos antes de eliminar proyectos = ProyectosUsuario.query.filter_by(id_casa=id_casa).all() for p in proyectos: p.id_casa = None db.session.delete(casa) db.session.commit() flash('Casa eliminada correctamente.', 'success') return redirect(url_for('main.configuracion')) @main.route('/casas') def casas(): casas = Casa.query.all() ciudades = DatosSolaresCiudad.query.all() return render_template('casas.html', casas=casas, ciudades=ciudades) @main.route('/proyectos') def proyectos(): id_casa_filter = request.args.get('id_casa', type=int) query = ProyectosUsuario.query if id_casa_filter: query = query.filter_by(id_casa=id_casa_filter) proyectos = query.order_by(ProyectosUsuario.fecha_referencia.desc()).all() casas = Casa.query.all() # Prepare chart data (reversed for chronological order in chart) chart_proyectos = proyectos[::-1] labels = [p.fecha_referencia.strftime('%Y-%m-%d') for p in chart_proyectos] costo_sin_solar = [] costo_con_solar = [] for p in chart_proyectos: costo_actual = p.costo_actual_mensual if p.costo_actual_mensual else 0 ahorro = p.ahorro_estimado if p.ahorro_estimado else 0 nuevo_costo = max(0, costo_actual - ahorro) costo_sin_solar.append(costo_actual) costo_con_solar.append(nuevo_costo) return render_template('proyectos.html', proyectos=proyectos, casas=casas, selected_casa=id_casa_filter, labels=labels, costo_sin_solar=costo_sin_solar, costo_con_solar=costo_con_solar) @main.route('/casa/nueva', methods=['POST']) def nueva_casa(): nombre = request.form.get('nombre') area_techo = float(request.form.get('area_techo')) orientacion = request.form.get('orientacion', 'Norte') inclinacion_str = request.form.get('inclinacion') inclinacion = float(inclinacion_str) if inclinacion_str else 0.0 id_ciudad = request.form.get('id_ciudad') casa = Casa(nombre=nombre, area_techo=area_techo, orientacion=orientacion, inclinacion=inclinacion, id_ciudad=id_ciudad) db.session.add(casa) db.session.commit() return redirect(url_for('main.configuracion')) @main.route('/simular/') def simular(id_casa): casa = Casa.query.get_or_404(id_casa) paneles = CatalogoPaneles.query.all() config_precio = Configuracion.query.filter_by(clave='precio_kwh').first() precio_kwh = float(config_precio.valor) if config_precio else 0.15 return render_template('simulation.html', casa=casa, paneles=paneles, precio_kwh=precio_kwh) @main.route('/calculate', methods=['POST']) def calculate(): id_casa = request.form.get('id_casa') id_panel = request.form.get('id_panel') cantidad = int(request.form.get('cantidad')) # Nuevos campos opcionales consumo_kwh = request.form.get('consumo_kwh') consumo_kwh = float(consumo_kwh) if consumo_kwh else 0 # Obtenemos el precio global configurado config_precio = Configuracion.query.filter_by(clave='precio_kwh').first() precio_kwh = float(config_precio.valor) if config_precio else 0.15 # Calculamos el costo mensual basado en el consumo y el precio global costo_mensual = consumo_kwh * precio_kwh casa = Casa.query.get(id_casa) ciudad = casa.ciudad panel = CatalogoPaneles.query.get(id_panel) # E = A * r * H * PR # Monthly E = (A_panel * quantity) * r * H * 30 * 0.75 area_total = panel.area_m2 * cantidad pr = 0.75 # Factor de corrección por orientación e inclinación # Ecuador está en latitud 0, por lo que la inclinación ideal es casi plana (0-10 grados). # Si la inclinación es mayor, la orientación empieza a importar más. factor_orientacion = 1.0 if casa.inclinacion > 10: if casa.orientacion in ['Este', 'Oeste']: factor_orientacion = 0.90 # Pérdida por menos horas de sol pico elif casa.orientacion in ['Norte', 'Sur']: factor_orientacion = 0.95 # Pérdida leve por ángulo solar elif casa.orientacion == 'Plano': factor_orientacion = 1.0 else: factor_orientacion = 0.92 # Noreste, Sureste, etc. days = 30 energia_mensual = area_total * panel.eficiencia_r * ciudad.irradiacion_h_promedio * days * pr * factor_orientacion # Bonus: ROI ahorro_mensual = energia_mensual * precio_kwh # Si el ahorro es mayor que el costo actual (si existe), lo limitamos al costo actual (no puedes ahorrar más de lo que pagas) # Aunque técnicamente podrías vender excedentes, para simplificar asumimos net metering o autoconsumo puro. if costo_mensual > 0 and ahorro_mensual > costo_mensual: ahorro_mensual = costo_mensual costo_total = panel.precio_unitario * cantidad tiempo_recuperacion = (costo_total / ahorro_mensual / 12) if ahorro_mensual > 0 else 0 # Years # Bonus: CO2 co2_evitado = energia_mensual * 0.4 # Nuevo costo estimado nuevo_costo_mensual = max(0, costo_mensual - ahorro_mensual) if costo_mensual > 0 else 0 porcentaje_cobertura = (energia_mensual / consumo_kwh * 100) if consumo_kwh > 0 else 0 return render_template('results.html', energia=round(energia_mensual, 2), ahorro=round(ahorro_mensual, 2), recuperacion=round(tiempo_recuperacion, 1), co2=round(co2_evitado, 2), ciudad=ciudad, panel=panel, cantidad=cantidad, costo_total=costo_total, casa=casa, consumo_kwh=consumo_kwh, costo_mensual=round(costo_mensual, 2), nuevo_costo_mensual=round(nuevo_costo_mensual, 2), porcentaje_cobertura=round(porcentaje_cobertura, 1), today=datetime.now().strftime('%Y-%m-%d')) @main.route('/save', methods=['POST']) def save(): nombre_cliente = request.form.get('nombre_cliente') id_casa = request.form.get('id_casa') id_panel = request.form.get('id_panel') cantidad = request.form.get('cantidad') energia = request.form.get('energia') ahorro = request.form.get('ahorro') consumo_kwh = request.form.get('consumo_kwh') costo_mensual = request.form.get('costo_mensual') fecha_str = request.form.get('fecha_referencia') casa = Casa.query.get(id_casa) if fecha_str: try: fecha_referencia = datetime.strptime(fecha_str, '%Y-%m-%d') except ValueError: fecha_referencia = datetime.now() else: fecha_referencia = datetime.now() proyecto = ProyectosUsuario( nombre_cliente=nombre_cliente, id_casa=id_casa, id_ciudad=casa.id_ciudad, id_panel=id_panel, cantidad_paneles=cantidad, energia_estimada_mensual=energia, ahorro_estimado=ahorro, consumo_actual_kwh=float(consumo_kwh) if consumo_kwh and float(consumo_kwh) > 0 else None, costo_actual_mensual=float(costo_mensual) if costo_mensual and float(costo_mensual) > 0 else None, fecha_referencia=fecha_referencia ) db.session.add(proyecto) db.session.commit() return redirect(url_for('main.proyectos'))