routes.py 9.7 KB

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