Browse Source

migrate to pgsql

Matthew Trejo 3 days ago
parent
commit
67b9e7c0cb
7 changed files with 192 additions and 7 deletions
  1. 16 5
      README.md
  2. BIN
      __pycache__/app.cpython-313.pyc
  3. BIN
      __pycache__/routes.cpython-313.pyc
  4. 5 1
      app.py
  5. 4 0
      requirements.txt
  6. 44 0
      routes.py
  7. 123 1
      templates/configuracion.html

+ 16 - 5
README.md

@@ -1,6 +1,12 @@
 # Calculadora de Viabilidad Solar
 
 Aplicación web en Flask para calcular la viabilidad de proyectos de energía solar fotovoltaica.
+**Nota:** El sistema ha sido migrado a PostgreSQL.
+
+## Requisitos
+
+- Python 3.8+
+- PostgreSQL
 
 ## Inicio Rápido
 
@@ -12,15 +18,20 @@ Aplicación web en Flask para calcular la viabilidad de proyectos de energía so
    pip install -r requirements.txt
    ```
 
-2. **Ejecutar:**
+2. **Configuración de Base de Datos:**
+   - Asegúrate de tener un servidor PostgreSQL corriendo.
+   - Crea una base de datos llamada `solarcalc`.
+   - Verifica la cadena de conexión en `app.py` (`SQLALCHEMY_DATABASE_URI`) para asegurar que apunta a tu instancia de base de datos correcta.
+
+3. **Ejecutar:**
    ```bash
    python app.py
    ```
-   Accede a `http://127.0.0.1:5000`.0,15
+   Accede a `http://127.0.0.1:5000`.
 
 ## Estructura
 
-- `app.py`: Aplicación principal.
-- `models.py`: Modelos de datos.
-- `routes.py`: Rutas.
+- `app.py`: Aplicación principal y configuración de PostgreSQL.
+- `models.py`: Modelos de datos (SQLAlchemy).
+- `routes.py`: Rutas y lógica del controlador.
 - `templates/`: Vistas HTML.

BIN
__pycache__/app.cpython-313.pyc


BIN
__pycache__/routes.cpython-313.pyc


+ 5 - 1
app.py

@@ -1,9 +1,13 @@
+import os
 from flask import Flask
 from models import db
 
 def create_app():
     app = Flask(__name__)
-    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///solarcalc.db'
+    
+    # Configuración de la base de datos: PostgreSQL
+    # Formato PostgreSQL: postgresql://usuario:password@localhost:5432/nombre_db
+    app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:solarEn2026EsTodo@167.99.161.63:5432/solarcalc'
     app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
     app.config['SECRET_KEY'] = 'dev-secret-key-12345'
 

+ 4 - 0
requirements.txt

@@ -1,2 +1,6 @@
 Flask
 Flask-SQLAlchemy
+psycopg2-binary
+
+
+

+ 44 - 0
routes.py

@@ -17,6 +17,7 @@ def configuracion():
     config_precio = Configuracion.query.filter_by(clave='precio_kwh').first()
     ciudades = DatosSolaresCiudad.query.all()
     casas = Casa.query.all()
+    paneles = CatalogoPaneles.query.all()
     
     edit_id = request.args.get('edit_id')
     casa_editar = None
@@ -38,6 +39,7 @@ def configuracion():
                            precio_kwh=config_precio.valor if config_precio else '0.15', 
                            ciudades=ciudades,
                            casas=casas,
+                           paneles=paneles,
                            casa_editar=casa_editar)
 
 @main.route('/casa/editar/<int:id_casa>', methods=['POST'])
@@ -68,6 +70,48 @@ def eliminar_casa(id_casa):
     flash('Casa eliminada correctamente.', 'success')
     return redirect(url_for('main.configuracion'))
 
+@main.route('/panel/nuevo', methods=['POST'])
+def nuevo_panel():
+    marca = request.form.get('marca')
+    modelo = request.form.get('modelo')
+    eficiencia_r = float(request.form.get('eficiencia_r'))
+    area_m2 = float(request.form.get('area_m2'))
+    precio_unitario = float(request.form.get('precio_unitario'))
+    
+    panel = CatalogoPaneles(marca=marca, modelo=modelo, eficiencia_r=eficiencia_r, area_m2=area_m2, precio_unitario=precio_unitario)
+    db.session.add(panel)
+    db.session.commit()
+    flash('Panel agregado correctamente.', 'success')
+    return redirect(url_for('main.configuracion'))
+
+@main.route('/panel/editar/<int:id_panel>', methods=['POST'])
+def editar_panel(id_panel):
+    panel = CatalogoPaneles.query.get_or_404(id_panel)
+    panel.marca = request.form.get('marca')
+    panel.modelo = request.form.get('modelo')
+    panel.eficiencia_r = float(request.form.get('eficiencia_r'))
+    panel.area_m2 = float(request.form.get('area_m2'))
+    panel.precio_unitario = float(request.form.get('precio_unitario'))
+    
+    db.session.commit()
+    flash('Panel actualizado correctamente.', 'success')
+    return redirect(url_for('main.configuracion'))
+
+@main.route('/panel/eliminar/<int:id_panel>', methods=['POST'])
+def eliminar_panel(id_panel):
+    panel = CatalogoPaneles.query.get_or_404(id_panel)
+    
+    # Verificar si hay proyectos usando este panel
+    proyectos = ProyectosUsuario.query.filter_by(id_panel=id_panel).first()
+    if proyectos:
+        flash('No se puede eliminar este panel porque hay proyectos asociados a él.', 'danger')
+        return redirect(url_for('main.configuracion'))
+        
+    db.session.delete(panel)
+    db.session.commit()
+    flash('Panel eliminado correctamente.', 'success')
+    return redirect(url_for('main.configuracion'))
+
 @main.route('/casas')
 def casas():
     casas = Casa.query.all()

+ 123 - 1
templates/configuracion.html

@@ -75,10 +75,70 @@
                 {% endif %}
             </div>
         </div>
+
+        <div class="card mb-4">
+            <div class="card-header bg-success text-white d-flex justify-content-between align-items-center">
+                <span>Gestión de Paneles Solares</span>
+                <button type="button" class="btn btn-light btn-sm" data-bs-toggle="modal" data-bs-target="#panelModal" onclick="setupPanelModal('new')">
+                    + Nuevo Panel
+                </button>
+            </div>
+            <div class="card-body">
+                <h5 class="card-title">Catálogo de Paneles</h5>
+                {% if paneles %}
+                <div class="table-responsive mb-4">
+                    <table class="table table-hover">
+                        <thead>
+                            <tr>
+                                <th>Marca</th>
+                                <th>Modelo</th>
+                                <th>Eficiencia</th>
+                                <th>Área (m²)</th>
+                                <th>Precio ($)</th>
+                                <th>Acciones</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            {% for panel in paneles %}
+                            <tr>
+                                <td>{{ panel.marca }}</td>
+                                <td>{{ panel.modelo }}</td>
+                                <td>{{ panel.eficiencia_r }}</td>
+                                <td>{{ panel.area_m2 }}</td>
+                                <td>{{ panel.precio_unitario }}</td>
+                                <td>
+                                    <div class="btn-group" role="group">
+                                        <button type="button" class="btn btn-sm btn-warning" 
+                                            data-bs-toggle="modal" 
+                                            data-bs-target="#panelModal"
+                                            onclick="setupPanelModal('edit', {
+                                                marca: '{{ panel.marca }}',
+                                                modelo: '{{ panel.modelo }}',
+                                                eficiencia_r: '{{ panel.eficiencia_r }}',
+                                                area_m2: '{{ panel.area_m2 }}',
+                                                precio_unitario: '{{ panel.precio_unitario }}'
+                                            }, '{{ url_for('main.editar_panel', id_panel=panel.id_panel) }}')">
+                                            Editar
+                                        </button>
+                                        <form action="{{ url_for('main.eliminar_panel', id_panel=panel.id_panel) }}" method="POST" onsubmit="return confirm('¿Estás seguro de eliminar este panel?');" style="display: inline;">
+                                            <button type="submit" class="btn btn-sm btn-danger">Eliminar</button>
+                                        </form>
+                                    </div>
+                                </td>
+                            </tr>
+                            {% endfor %}
+                        </tbody>
+                    </table>
+                </div>
+                {% else %}
+                <div class="alert alert-info mb-4">No hay paneles registrados.</div>
+                {% endif %}
+            </div>
+        </div>
     </div>
 </div>
 
-<!-- Modal -->
+<!-- Modal Casa -->
 <div class="modal fade" id="casaModal" tabindex="-1" aria-labelledby="casaModalLabel" aria-hidden="true">
     <div class="modal-dialog">
         <div class="modal-content">
@@ -131,6 +191,45 @@
     </div>
 </div>
 
+<!-- Modal Panel -->
+<div class="modal fade" id="panelModal" tabindex="-1" aria-labelledby="panelModalLabel" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="panelModalLabel">Gestión de Panel Solar</h5>
+                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+            </div>
+            <div class="modal-body">
+                <form id="panelForm" method="POST" action="{{ url_for('main.nuevo_panel') }}">
+                    <div class="mb-3">
+                        <label for="marca" class="form-label">Marca</label>
+                        <input type="text" name="marca" id="marca" class="form-control" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="modelo" class="form-label">Modelo</label>
+                        <input type="text" name="modelo" id="modelo" class="form-control" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="eficiencia_r" class="form-label">Eficiencia (0.0 - 1.0)</label>
+                        <input type="number" step="0.01" name="eficiencia_r" id="eficiencia_r" class="form-control" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="area_m2" class="form-label">Área (m²)</label>
+                        <input type="number" step="0.01" name="area_m2" id="area_m2" class="form-control" required>
+                    </div>
+                    <div class="mb-3">
+                        <label for="precio_unitario" class="form-label">Precio Unitario ($)</label>
+                        <input type="number" step="0.01" name="precio_unitario" id="precio_unitario" class="form-control" required>
+                    </div>
+                    <div class="d-grid">
+                        <button type="submit" class="btn btn-primary" id="panelModalSubmitBtn">Guardar Panel</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+
 <script>
 function setupModal(mode, data, actionUrl) {
     const form = document.getElementById('casaForm');
@@ -155,5 +254,28 @@ function setupModal(mode, data, actionUrl) {
         document.getElementById('inclinacion').value = data.inclinacion;
     }
 }
+
+function setupPanelModal(mode, data, actionUrl) {
+    const form = document.getElementById('panelForm');
+    const title = document.getElementById('panelModalLabel');
+    const btn = document.getElementById('panelModalSubmitBtn');
+    
+    if (mode === 'new') {
+        title.textContent = 'Agregar Nuevo Panel';
+        btn.textContent = 'Guardar Panel';
+        form.action = "{{ url_for('main.nuevo_panel') }}";
+        form.reset();
+    } else if (mode === 'edit') {
+        title.textContent = 'Editar Panel';
+        btn.textContent = 'Actualizar Panel';
+        form.action = actionUrl;
+        
+        document.getElementById('marca').value = data.marca;
+        document.getElementById('modelo').value = data.modelo;
+        document.getElementById('eficiencia_r').value = data.eficiencia_r;
+        document.getElementById('area_m2').value = data.area_m2;
+        document.getElementById('precio_unitario').value = data.precio_unitario;
+    }
+}
 </script>
 {% endblock %}