Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
367ab61
integrar html
May 22, 2025
716dbbd
cambios
May 22, 2025
27dafef
cambio
May 22, 2025
2f08cd8
Enlace entre las dos paginas html
sebas112205 May 22, 2025
d0287e7
Función buscar en app.py
sebas112205 May 22, 2025
6d83f3f
Organización de tabla
Sebasval1122 May 23, 2025
35dbc87
Botón para volver al index
Sebasval1122 May 23, 2025
bcc9c75
Organización del index y nueva función buscar
Sebasval1122 May 24, 2025
8fe773f
Función buscar
Sebasval1122 May 24, 2025
e53f872
Lista de tarjetas
Sebasval1122 May 25, 2025
dea7c6a
Obtener datos del archivo
Sebasval1122 May 25, 2025
8c6597d
conección a la base de datos
sebas112205 May 27, 2025
74f8521
Se logro la coneccion a la base de datos
sebas112205 May 27, 2025
e738db1
Corrección de carpeta
Sebasval1122 May 27, 2025
023e1ae
Buscar Usuario
May 28, 2025
2aa01fb
Opción para modificar usuario
Sebasval1122 May 28, 2025
8edc395
modificación de función modificar
Sebasval1122 May 28, 2025
2372b68
crear usuario
May 28, 2025
c728b80
creación de base de datos
Sebasval1122 May 28, 2025
2008958
Menú de inicio
May 28, 2025
03beda8
Merge branch 'main' of https://github.com/Gallego09/machine_learning
May 28, 2025
6bc9f7b
correcicion
May 28, 2025
9593fc3
Merge branch 'main' of https://github.com/Gallego09/machine_learning
May 28, 2025
f668ac9
corregido
May 28, 2025
a1a934a
Actualización de la web
sebas112205 May 28, 2025
4cdaeb3
Temples
sebas112205 May 28, 2025
525f08c
Arreglos
May 29, 2025
b23acb9
Readme y Crear tabla
May 29, 2025
39b4ff4
ocultar
May 29, 2025
3205f38
Ocultar
May 29, 2025
b901739
Update requirements.txt
sebas112205 May 30, 2025
0dff823
Update requirements.txt
sebas112205 May 30, 2025
a7885a7
Actualización de cambios
Sebasval1122 May 30, 2025
ecce7ac
Merge branch 'main' of https://github.com/Gallego09/machine_learning
Sebasval1122 May 30, 2025
3dee7c1
Modificar usuario
May 30, 2025
d87ace5
requirements actualización
Sebasval1122 May 30, 2025
0a8630b
Merge branch 'main' of https://github.com/Gallego09/machine_learning
Sebasval1122 May 30, 2025
68c1dec
Update requirements.txt
Gallego09 May 30, 2025
cb39c76
Update requirements.txt
Gallego09 May 30, 2025
a73fce7
web
May 30, 2025
d62cc52
Merge branch 'main' of https://github.com/Gallego09/machine_learning
May 30, 2025
08ff8fe
Update requirements.txt
Gallego09 May 30, 2025
b8ca8b3
Update requirements.txt
Gallego09 May 30, 2025
fd9e896
Update requirements.txt
sebas112205 May 30, 2025
2d6f90d
Update requirements.txt
Gallego09 May 30, 2025
93dbe2b
Update requirements.txt
Gallego09 May 30, 2025
60ea483
Update requirements.txt
Gallego09 May 30, 2025
adee31d
SecretConfig agregado
Sebasval1122 May 30, 2025
c218ab2
Merge branch 'main' of https://github.com/Gallego09/machine_learning
Sebasval1122 May 30, 2025
09eabbc
Update requirements.txt
sebas112205 May 30, 2025
ce52dfd
Create start.sh
sebas112205 May 30, 2025
de099f5
Update requirements.txt
Gallego09 May 30, 2025
e9dd3ca
Update README.md
sebas112205 May 30, 2025
4d92284
Update .gitignore
sebas112205 May 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ __pycache__/
*.pyc
*.pyo
*.pyd

SecretConfig.py
# Environment variables
.env

.poetry/

.vscode/
.vscode/



27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
# 🧪 Instrucciones para ejecutar la aplicación localmente y con una base de datos en blanco

_Esta parte fue creada por Andrés Gallego y Sebastián Valencia._

### 🖥️ Ejecución local

Para ejecutar la aplicación desde tu máquina local y asegurarte de que la base de datos esté lista desde cero:

1. Asegúrate de tener PostgreSQL instalado o haber configurado una base de datos en Neon.

2. Crea un archivo llamado `secret_config.py` en la raíz del proyecto con esta estructura:

```python
# secret_config.py
PGHOST = "tu_host"
PGDATABASE = "tu_base_de_datos"
PGUSER = "tu_usuario"
PGPASSWORD = "tu_contraseña"
PGPORT = "puerto" # normalmente 5432





# Segmentación Socioeconómica de Países usando K-Means Clustering

**Autores:**
Expand Down Expand Up @@ -202,3 +226,6 @@ Las contribuciones son bienvenidas. Para contribuir:
5. Haz commit de tus cambios (`git commit -m 'Add some amazing feature'`)
6. Haz push a la rama (`git push origin feature/amazing-feature`)
7. Abre un Pull Request

##Web:
link: https://machine-learning-czmp.onrender.com
19 changes: 6 additions & 13 deletions SecretConfig.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
# You must create your own .env file with your own connection string.
# Do not expose your Neon credentials to the browser

from dotenv import load_dotenv
import os

# Load environment variables from the .env file
load_dotenv()

class SecretConfig:
DB_URL = os.getenv("DB_URL")

# Raise an error if DB_URL is not set in the .env file
if not DB_URL:
raise ValueError("DB_URL is not set. Please check your .env file.")
PGHOST='ep-cool-brook-a8o0c39i-pooler.eastus2.azure.neon.tech'
PGDATABASE='neondb'
PGUSER='neondb_owner'
PGPASSWORD='npg_Zo28dBzaOVUh'
PGPORT=5432
264 changes: 264 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
from flask import Flask, render_template, request, jsonify, redirect, url_for
import os
import pandas as pd
import sys
import json

sys.path.append('src')
sys.path.append('src/controller')

from src.controller.results_controller import ResultsController
from src.model.result_model import ClusteringResult

app = Flask(__name__)

# Página principal (menú)
@app.route('/')
def menu():
return render_template('menu.html', menu_url=url_for('menu'))

# Ruta para crear la base de datos
@app.route('/crear_base_datos', methods=['POST'])
def crear_base_datos():
handler = ResultsController()
crear = request.form.get('crear')

if crear == 'yes':
try:
handler.create_table()
return render_template('index.html', mensaje="Base de datos creada exitosamente", menu_url=url_for('menu'))
except Exception as e:
return render_template('index.html', error=f"Error al crear la base de datos: {e}", menu_url=url_for('menu'))
return redirect(url_for('menu'))

# Nueva ruta para crear la tabla directamente desde el menú
@app.route('/crear_tabla', methods=['GET', 'POST'])
def crear_tabla():
handler = ResultsController()
mensaje = None
error = None

if request.method == 'POST':
try:
handler.create_table()
mensaje = "Tabla creada exitosamente en la base de datos de Neon.tech."
except Exception as e:
error = f"Error al crear la tabla: {e}"

return render_template('crear_tabla.html', mensaje=mensaje, error=error, menu_url=url_for('menu'))

# Ruta para subir y procesar archivo CSV
@app.route('/subir_archivo', methods=['GET', 'POST'])
def subir_archivo():
if request.method == 'POST':
if 'archivo' not in request.files:
return "No se subió ningún archivo", 400

archivo = request.files['archivo']
if archivo.filename == '':
return "Nombre de archivo vacío", 400

try:
df = pd.read_csv(archivo)
columnas = list(df.columns)
filas = df.head(5).values.tolist()
return render_template('resultados.html', columnas=columnas, resultados=filas, menu_url=url_for('menu'))
except Exception as e:
return f"Error al procesar el archivo: {e}", 500
return render_template('index.html', menu_url=url_for('menu'))

# Página con formulario para buscar
@app.route('/buscar', methods=['GET'])
def buscar():
return render_template('buscar.html', menu_url=url_for('menu'))

# Ruta para petición desde JavaScript (fetch)
@app.route('/api/buscar', methods=['GET'])
def api_buscar():
id = request.args.get('id')
if not id:
return jsonify({"error": "ID no proporcionado"}), 400

try:
id = int(id)
except ValueError:
return jsonify({"error": "ID inválido"}), 400

handler = ResultsController()
result = handler.get_result_by_id(id)

if result is None:
return jsonify({"error": "No se encontró ningún resultado"}), 404

return jsonify(result.to_dict())

# Ruta tradicional con formulario POST que muestra resultados en una nueva página
@app.route("/lista", methods=["GET", "POST"])
def lista():
handler = ResultsController()
result = None
result_id = None
results_list = []

try:
results_list = handler.list_all_titles()
except Exception as e:
return f"Error al obtener los resultados: {e}", 500

if request.method == "POST":
result_id = request.form.get("id")
if result_id:
try:
result_id = int(result_id)
result = handler.get_result_by_id(result_id)
if not result:
return "No se encontró un resultado con ese ID", 404
except ValueError:
return "ID inválido", 400
except Exception as e:
return f"Error al buscar el resultado: {e}", 500

return render_template("lista.html", result=result, result_id=result_id, results_list=results_list, menu_url=url_for('menu'))
# Procesamiento del archivo CSV
@app.route('/resultados', methods=['POST'])
def procesar_archivo():
if 'archivo' not in request.files:
return "No se subió ningún archivo", 400

archivo = request.files['archivo']

if archivo.filename == '':
return "Nombre de archivo vacío", 400

try:
df = pd.read_csv(archivo)
columnas = list(df.columns)
filas = df.head(5).values.tolist()
return render_template('resultados.html', columnas=columnas, resultados=filas, menu_url=url_for('menu'))
except Exception as e:
return f"Error al procesar el archivo: {e}", 500

# Ruta para modificar un usuario
@app.route('/modificar_usuario', methods=['GET', 'POST'])
def modificar_usuario():
handler = ResultsController()
result = None
error = None
result_id = None
formatted_coordinates = ''

if request.method == 'GET':
result_id = request.args.get('id')
if not result_id:
error = "No se proporcionó un ID. Por favor, seleccione un resultado desde la lista."
return redirect(url_for('lista'))
try:
result_id = int(result_id)
result = handler.get_result_by_id(result_id)
if not result:
error = f"No se encontró un resultado con el ID {result_id}"
return redirect(url_for('lista'))
# Format coordinates list to string (e.g., "[1.0, 2.0, 3.0, 4.0]" -> "1.0,2.0,3.0,4.0")
if result.coordinates and isinstance(result.coordinates, list):
formatted_coordinates = ','.join(map(str, result.coordinates))
except ValueError:
error = "ID inválido, debe ser un número entero"
return redirect(url_for('lista'))
except Exception as e:
error = f"Error al cargar el resultado: {e}"
return redirect(url_for('lista'))

if request.method == 'POST':
result_id = request.form.get('id')
if not result_id:
error = "No se proporcionó un ID para actualizar. Por favor, intente de nuevo desde la lista."
return redirect(url_for('lista'))
try:
result_id = int(result_id)
result = handler.get_result_by_id(result_id)
if not result:
error = f"No se encontró un resultado con el ID {result_id}"
return redirect(url_for('lista'))

# Procesar coordenadas
coordinates = request.form.get('coordinates', '')
if coordinates:
coordinates = coordinates.split(',')
coordinates = [float(coord.strip()) for coord in coordinates if coord.strip()]
coordinates = '{' + ','.join(map(str, coordinates)) + '}'
else:
coordinates = '{}'

# Crear el objeto ClusteringResult con los datos del formulario
updated_result = ClusteringResult(
id=result_id,
title=request.form.get('title'),
n_clusters=int(request.form.get('n_clusters')),
used_iterations=int(request.form.get('used_iterations')),
coordinates=coordinates,
assigned_cluster=int(request.form.get('assigned_cluster')) if request.form.get('assigned_cluster') else None,
is_centroid='is_centroid' in request.form,
centroid_label=request.form.get('centroid_label') or None
)

# Actualizar el resultado en la base de datos
success = handler.update_result(result_id, updated_result)
if success:
return redirect(url_for('lista'))
else:
error = "No se pudo actualizar el resultado en la base de datos"
except ValueError as e:
error = f"Datos inválidos proporcionados: {e}"
except Exception as e:
error = f"Error al actualizar el resultado: {e}"

return render_template('modificar.html', result=result, error=error, result_id=result_id,
formatted_coordinates=formatted_coordinates, menu_url=url_for('menu'))

# Ruta para mostrar el formulario de crear usuario
@app.route('/crear_usuario', methods=['GET'])
def show_crear_usuario():
return render_template('crear_usuario.html', menu_url=url_for('menu'))

# Ruta para procesar la creación de un usuario
@app.route('/crear_usuario', methods=['POST'])
def crear_usuario():
handler = ResultsController()
try:
title = request.form.get('title')
n_clusters = int(request.form.get('n_clusters'))
used_iterations = int(request.form.get('used_iterations'))

coordinates = request.form.get('coordinates')
if coordinates:
coordinates = coordinates.strip('[]').replace(' ', '')
coordinates = '{' + coordinates + '}'
else:
coordinates = '{}'

assigned_cluster = request.form.get('assigned_cluster')
assigned_cluster = int(assigned_cluster) if assigned_cluster else None
is_centroid = 'is_centroid' in request.form
centroid_label = request.form.get('centroid_label')

clustering_result = ClusteringResult(
id=None,
title=title,
n_clusters=n_clusters,
used_iterations=used_iterations,
coordinates=coordinates,
assigned_cluster=assigned_cluster,
is_centroid=is_centroid,
centroid_label=centroid_label
)

success = handler.create_result(clustering_result)
if success:
return redirect(url_for('menu'))
else:
return "Error al crear el usuario", 500
except Exception as e:
return f"Error al crear el usuario: {e}", 500

if __name__ == '__main__':
app.run(debug=True)
Binary file modified casos_prueba.xlsx
Binary file not shown.
Binary file modified requirements.txt
Binary file not shown.
Loading