routes.py 12 KB

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