routes.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. from flask import Blueprint, render_template, request, redirect, url_for, flash
  2. from models import db, CatalogoPaneles, DatosSolaresCiudad, ProyectosUsuario, Casa, Configuracion
  3. from datetime import datetime
  4. main = Blueprint('main', __name__)
  5. @main.route('/')
  6. def index():
  7. return render_template('index.html')
  8. @main.route('/configuracion', methods=['GET', 'POST'])
  9. def configuracion():
  10. config_precio = Configuracion.query.filter_by(clave='precio_kwh').first()
  11. ciudades = DatosSolaresCiudad.query.all()
  12. casas = Casa.query.all()
  13. edit_id = request.args.get('edit_id')
  14. casa_editar = None
  15. if edit_id:
  16. casa_editar = Casa.query.get(edit_id)
  17. if request.method == 'POST':
  18. nuevo_precio = request.form.get('precio_kwh')
  19. if config_precio:
  20. config_precio.valor = nuevo_precio
  21. else:
  22. config_precio = Configuracion(clave='precio_kwh', valor=nuevo_precio)
  23. db.session.add(config_precio)
  24. db.session.commit()
  25. return redirect(url_for('main.configuracion'))
  26. return render_template('configuracion.html',
  27. precio_kwh=config_precio.valor if config_precio else '0.15',
  28. ciudades=ciudades,
  29. casas=casas,
  30. casa_editar=casa_editar)
  31. @main.route('/casa/editar/<int:id_casa>', methods=['POST'])
  32. def editar_casa(id_casa):
  33. casa = Casa.query.get_or_404(id_casa)
  34. casa.nombre = request.form.get('nombre')
  35. casa.area_techo = float(request.form.get('area_techo'))
  36. casa.orientacion = request.form.get('orientacion')
  37. inclinacion_str = request.form.get('inclinacion')
  38. casa.inclinacion = float(inclinacion_str) if inclinacion_str else 0.0
  39. casa.id_ciudad = request.form.get('id_ciudad')
  40. db.session.commit()
  41. flash('Casa actualizada correctamente.', 'success')
  42. return redirect(url_for('main.configuracion'))
  43. @main.route('/casa/eliminar/<int:id_casa>', methods=['POST'])
  44. def eliminar_casa(id_casa):
  45. casa = Casa.query.get_or_404(id_casa)
  46. # Desvincular proyectos antes de eliminar
  47. proyectos = ProyectosUsuario.query.filter_by(id_casa=id_casa).all()
  48. for p in proyectos:
  49. p.id_casa = None
  50. db.session.delete(casa)
  51. db.session.commit()
  52. flash('Casa eliminada correctamente.', 'success')
  53. return redirect(url_for('main.configuracion'))
  54. @main.route('/casas')
  55. def casas():
  56. casas = Casa.query.all()
  57. ciudades = DatosSolaresCiudad.query.all()
  58. return render_template('casas.html', casas=casas, ciudades=ciudades)
  59. @main.route('/proyectos')
  60. def proyectos():
  61. id_casa_filter = request.args.get('id_casa', type=int)
  62. query = ProyectosUsuario.query
  63. if id_casa_filter:
  64. query = query.filter_by(id_casa=id_casa_filter)
  65. proyectos = query.order_by(ProyectosUsuario.fecha_referencia.desc()).all()
  66. casas = Casa.query.all()
  67. # Prepare chart data (reversed for chronological order in chart)
  68. chart_proyectos = proyectos[::-1]
  69. labels = [p.fecha_referencia.strftime('%Y-%m-%d') for p in chart_proyectos]
  70. costo_sin_solar = []
  71. costo_con_solar = []
  72. for p in chart_proyectos:
  73. costo_actual = p.costo_actual_mensual if p.costo_actual_mensual else 0
  74. ahorro = p.ahorro_estimado if p.ahorro_estimado else 0
  75. nuevo_costo = max(0, costo_actual - ahorro)
  76. costo_sin_solar.append(costo_actual)
  77. costo_con_solar.append(nuevo_costo)
  78. return render_template('proyectos.html',
  79. proyectos=proyectos,
  80. casas=casas,
  81. selected_casa=id_casa_filter,
  82. labels=labels,
  83. costo_sin_solar=costo_sin_solar,
  84. costo_con_solar=costo_con_solar)
  85. @main.route('/casa/nueva', methods=['POST'])
  86. def nueva_casa():
  87. nombre = request.form.get('nombre')
  88. area_techo = float(request.form.get('area_techo'))
  89. orientacion = request.form.get('orientacion', 'Norte')
  90. inclinacion_str = request.form.get('inclinacion')
  91. inclinacion = float(inclinacion_str) if inclinacion_str else 0.0
  92. id_ciudad = request.form.get('id_ciudad')
  93. casa = Casa(nombre=nombre, area_techo=area_techo, orientacion=orientacion, inclinacion=inclinacion, id_ciudad=id_ciudad)
  94. db.session.add(casa)
  95. db.session.commit()
  96. return redirect(url_for('main.configuracion'))
  97. @main.route('/simular/<int:id_casa>')
  98. def simular(id_casa):
  99. casa = Casa.query.get_or_404(id_casa)
  100. paneles = CatalogoPaneles.query.all()
  101. config_precio = Configuracion.query.filter_by(clave='precio_kwh').first()
  102. precio_kwh = float(config_precio.valor) if config_precio else 0.15
  103. return render_template('simulation.html', casa=casa, paneles=paneles, precio_kwh=precio_kwh)
  104. @main.route('/calculate', methods=['POST'])
  105. def calculate():
  106. id_casa = request.form.get('id_casa')
  107. id_panel = request.form.get('id_panel')
  108. cantidad = int(request.form.get('cantidad'))
  109. # Nuevos campos opcionales
  110. consumo_kwh = request.form.get('consumo_kwh')
  111. consumo_kwh = float(consumo_kwh) if consumo_kwh else 0
  112. # Obtenemos el precio global configurado
  113. config_precio = Configuracion.query.filter_by(clave='precio_kwh').first()
  114. precio_kwh = float(config_precio.valor) if config_precio else 0.15
  115. # Calculamos el costo mensual basado en el consumo y el precio global
  116. costo_mensual = consumo_kwh * precio_kwh
  117. casa = Casa.query.get(id_casa)
  118. ciudad = casa.ciudad
  119. panel = CatalogoPaneles.query.get(id_panel)
  120. # E = A * r * H * PR
  121. # Monthly E = (A_panel * quantity) * r * H * 30 * 0.75
  122. area_total = panel.area_m2 * cantidad
  123. pr = 0.75
  124. # Factor de corrección por orientación e inclinación
  125. # Ecuador está en latitud 0, por lo que la inclinación ideal es casi plana (0-10 grados).
  126. # Si la inclinación es mayor, la orientación empieza a importar más.
  127. factor_orientacion = 1.0
  128. if casa.inclinacion > 10:
  129. if casa.orientacion in ['Este', 'Oeste']:
  130. factor_orientacion = 0.90 # Pérdida por menos horas de sol pico
  131. elif casa.orientacion in ['Norte', 'Sur']:
  132. factor_orientacion = 0.95 # Pérdida leve por ángulo solar
  133. elif casa.orientacion == 'Plano':
  134. factor_orientacion = 1.0
  135. else:
  136. factor_orientacion = 0.92 # Noreste, Sureste, etc.
  137. days = 30
  138. energia_mensual = area_total * panel.eficiencia_r * ciudad.irradiacion_h_promedio * days * pr * factor_orientacion
  139. # Bonus: ROI
  140. ahorro_mensual = energia_mensual * precio_kwh
  141. # 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)
  142. # Aunque técnicamente podrías vender excedentes, para simplificar asumimos net metering o autoconsumo puro.
  143. if costo_mensual > 0 and ahorro_mensual > costo_mensual:
  144. ahorro_mensual = costo_mensual
  145. costo_total = panel.precio_unitario * cantidad
  146. tiempo_recuperacion = (costo_total / ahorro_mensual / 12) if ahorro_mensual > 0 else 0 # Years
  147. # Bonus: CO2
  148. co2_evitado = energia_mensual * 0.4
  149. # Nuevo costo estimado
  150. nuevo_costo_mensual = max(0, costo_mensual - ahorro_mensual) if costo_mensual > 0 else 0
  151. porcentaje_cobertura = (energia_mensual / consumo_kwh * 100) if consumo_kwh > 0 else 0
  152. return render_template('results.html',
  153. energia=round(energia_mensual, 2),
  154. ahorro=round(ahorro_mensual, 2),
  155. recuperacion=round(tiempo_recuperacion, 1),
  156. co2=round(co2_evitado, 2),
  157. ciudad=ciudad,
  158. panel=panel,
  159. cantidad=cantidad,
  160. costo_total=costo_total,
  161. casa=casa,
  162. consumo_kwh=consumo_kwh,
  163. costo_mensual=round(costo_mensual, 2),
  164. nuevo_costo_mensual=round(nuevo_costo_mensual, 2),
  165. porcentaje_cobertura=round(porcentaje_cobertura, 1),
  166. today=datetime.now().strftime('%Y-%m-%d'))
  167. @main.route('/save', methods=['POST'])
  168. def save():
  169. nombre_cliente = request.form.get('nombre_cliente')
  170. id_casa = request.form.get('id_casa')
  171. id_panel = request.form.get('id_panel')
  172. cantidad = request.form.get('cantidad')
  173. energia = request.form.get('energia')
  174. ahorro = request.form.get('ahorro')
  175. consumo_kwh = request.form.get('consumo_kwh')
  176. costo_mensual = request.form.get('costo_mensual')
  177. fecha_str = request.form.get('fecha_referencia')
  178. casa = Casa.query.get(id_casa)
  179. if fecha_str:
  180. try:
  181. fecha_referencia = datetime.strptime(fecha_str, '%Y-%m-%d')
  182. except ValueError:
  183. fecha_referencia = datetime.now()
  184. else:
  185. fecha_referencia = datetime.now()
  186. proyecto = ProyectosUsuario(
  187. nombre_cliente=nombre_cliente,
  188. id_casa=id_casa,
  189. id_ciudad=casa.id_ciudad,
  190. id_panel=id_panel,
  191. cantidad_paneles=cantidad,
  192. energia_estimada_mensual=energia,
  193. ahorro_estimado=ahorro,
  194. consumo_actual_kwh=float(consumo_kwh) if consumo_kwh and float(consumo_kwh) > 0 else None,
  195. costo_actual_mensual=float(costo_mensual) if costo_mensual and float(costo_mensual) > 0 else None,
  196. fecha_referencia=fecha_referencia
  197. )
  198. db.session.add(proyecto)
  199. db.session.commit()
  200. return redirect(url_for('main.proyectos'))