Spaces:
Runtime error
Runtime error
import gradio as gr | |
import pandas as pd | |
import plotly.express as px | |
from datetime import datetime | |
import os | |
# --- 1. CONFIGURACIÓN DEL ARCHIVO CSV --- | |
CSV_FILE = "cronograma.csv" | |
# --- 2. LÓGICA DE CARGA Y GUARDADO DE DATOS --- | |
def load_data_from_csv(): | |
"""Carga los datos desde el archivo CSV al iniciar la aplicación.""" | |
try: | |
# Lee el CSV y convierte las columnas de fecha al formato correcto | |
df = pd.read_csv( | |
CSV_FILE, | |
parse_dates=['Fecha de Inicio', 'Fecha de Fin'] | |
) | |
return df | |
except FileNotFoundError: | |
# Si el archivo no existe, crea un DataFrame vacío con las columnas correctas | |
return create_empty_dataframe() | |
except Exception as e: | |
print(f"Error al leer el CSV: {e}") | |
return create_empty_dataframe() | |
def save_data_to_csv(df): | |
"""Guarda el DataFrame completo en el archivo CSV.""" | |
try: | |
df.to_csv(CSV_FILE, index=False) | |
print(f"Datos guardados exitosamente en {CSV_FILE}") | |
except Exception as e: | |
gr.Error(f"No se pudo guardar el archivo CSV: {e}") | |
def create_empty_dataframe(): | |
"""Crea la estructura del DataFrame si el archivo no existe o está vacío.""" | |
columns = ['ID', 'Tarea', 'Fase', 'Responsable', 'Fecha de Inicio', 'Fecha de Fin', 'Estado', 'Progreso (%)', 'Descripción'] | |
df = pd.DataFrame(columns=columns) | |
# Asegurar tipos de datos correctos para un df vacío | |
df['ID'] = df['ID'].astype(int) | |
df['Progreso (%)'] = df['Progreso (%)'].astype(int) | |
return df | |
# --- Carga inicial de datos --- | |
initial_df = load_data_from_csv() | |
# --- 3. FUNCIONES DE LA APLICACIÓN (LÓGICA DE UI) --- | |
# (Las funciones create_gantt_chart y populate_form_on_select no cambian) | |
def create_gantt_chart(df): | |
if df.empty or df['Fecha de Inicio'].isnull().all() or df['Fecha de Fin'].isnull().all(): | |
return px.timeline(title="Cronograma de Proyecto") | |
status_colors = {'No Iniciada': 'lightgrey', 'En Progreso': 'blue', 'Completada': 'green', 'Retrasada': 'orange', 'Bloqueada': 'red'} | |
df_filtered = df.dropna(subset=['Fecha de Inicio', 'Fecha de Fin']) | |
fig = px.timeline(df_filtered, x_start="Fecha de Inicio", x_end="Fecha de Fin", y="Tarea", color="Estado", color_discrete_map=status_colors, title="Diagrama de Gantt del Proyecto") | |
fig.update_yaxes(autorange="reversed") | |
return fig | |
def populate_form_on_select(df, evt: gr.SelectData): | |
if evt.index is None: return None, "", "", "", None, None, "No Iniciada", 0, "" | |
selected_row = df.iloc[evt.index[0]] | |
start_date = selected_row['Fecha de Inicio'].to_pydatetime().date() if pd.notna(selected_row['Fecha de Inicio']) else None | |
end_date = selected_row['Fecha de Fin'].to_pydatetime().date() if pd.notna(selected_row['Fecha de Fin']) else None | |
return (selected_row['ID'], selected_row['Tarea'], selected_row['Fase'], selected_row['Responsable'], start_date, end_date, selected_row['Estado'], selected_row['Progreso (%)'], selected_row['Descripción']) | |
def manage_tasks(df_state, *args): | |
"""Función central que ahora también guarda los cambios en el CSV.""" | |
action, selected_id, task_id, name, phase, responsible, start_date, end_date, status, progress, desc = args | |
df = df_state.copy() | |
# Lógica para añadir, actualizar o eliminar | |
if action == "add": | |
if not name or not start_date or not end_date: | |
gr.Warning("Nombre, Fecha de Inicio y Fecha de Fin son obligatorios.") | |
return df, create_gantt_chart(df), gr.update(), gr.update() | |
new_id = int(df['ID'].max() + 1) if not df.empty else 1 | |
new_task = pd.DataFrame([{'ID': new_id, 'Tarea': name, 'Fase': phase, 'Responsable': responsible, 'Fecha de Inicio': start_date, 'Fecha de Fin': end_date, 'Estado': status, 'Progreso (%)': progress, 'Descripción': desc}]) | |
df = pd.concat([df, new_task], ignore_index=True) | |
gr.Info(f"Tarea '{name}' añadida.") | |
elif action == "update": | |
if selected_id is None: | |
gr.Warning("Selecciona una tarea para actualizar.") | |
return df, create_gantt_chart(df), gr.update(), gr.update() | |
df.loc[df['ID'] == selected_id, ['Tarea', 'Fase', 'Responsable', 'Fecha de Inicio', 'Fecha de Fin', 'Estado', 'Progreso (%)', 'Descripción']] = [name, phase, responsible, start_date, end_date, status, progress, desc] | |
gr.Info(f"Tarea ID {selected_id} actualizada.") | |
elif action == "delete": | |
if selected_id is None: | |
gr.Warning("Selecciona una tarea para eliminar.") | |
return df, create_gantt_chart(df), gr.update(), gr.update() | |
task_name = df.loc[df['ID'] == selected_id, 'Tarea'].iloc[0] | |
df = df[df['ID'] != selected_id].reset_index(drop=True) | |
gr.Info(f"Tarea '{task_name}' eliminada.") | |
# Asegurar tipos de datos correctos antes de guardar | |
df['Fecha de Inicio'] = pd.to_datetime(df['Fecha de Inicio']) | |
df['Fecha de Fin'] = pd.to_datetime(df['Fecha de Fin']) | |
# **Paso clave: Guardar el DataFrame modificado en el archivo CSV** | |
save_data_to_csv(df) | |
all_phases = sorted(df['Fase'].dropna().unique().tolist()) | |
all_responsibles = sorted(df['Responsable'].dropna().unique().tolist()) | |
return df, create_gantt_chart(df), gr.update(choices=all_phases), gr.update(choices=all_responsibles) | |
# --- 4. INTERFAZ DE GRADIO --- | |
with gr.Blocks(theme=gr.themes.Soft(), title="Gestor de Proyectos con CSV") as demo: | |
df_state = gr.State(initial_df) | |
selected_task_id = gr.State(None) | |
initial_phases = sorted(initial_df['Fase'].dropna().unique().tolist()) | |
initial_responsibles = sorted(initial_df['Responsable'].dropna().unique().tolist()) | |
status_options = ['No Iniciada', 'En Progreso', 'Completada', 'Retrasada', 'Bloqueada'] | |
gr.Markdown("# 💾 Gestor de Proyectos (con Guardado en CSV)") | |
gr.Markdown("Los datos se guardan en el 'Space'. **Nota:** Los datos se reiniciarán si el Space está inactivo por mucho tiempo.") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
with gr.Accordion("📝 Panel de Gestión de Tareas", open=True): | |
txt_task_id = gr.Textbox(label="ID Tarea", interactive=False, visible=False) | |
txt_task_name = gr.Textbox(label="Nombre de la Tarea") | |
dd_phase = gr.Dropdown(label="Fase del Proyecto", choices=initial_phases, allow_custom_value=True) | |
dd_responsible = gr.Dropdown(label="Responsable", choices=initial_responsibles, allow_custom_value=True) | |
with gr.Row(): | |
date_start = gr.DatePicker(label="Fecha de Inicio") | |
date_end = gr.DatePicker(label="Fecha de Fin") | |
dd_status = gr.Dropdown(label="Estado", choices=status_options, value="No Iniciada") | |
slider_progress = gr.Slider(label="Progreso (%)", minimum=0, maximum=100, step=1) | |
txt_description = gr.Textbox(label="Descripción / Notas", lines=3) | |
with gr.Row(): | |
btn_add = gr.Button("✔️ Añadir Tarea", variant="primary") | |
btn_update = gr.Button("🔄 Actualizar Tarea", variant="secondary") | |
btn_delete = gr.Button("❌ Eliminar Tarea", variant="stop") | |
with gr.Column(scale=2): | |
gr.Markdown("### 🗓️ Tabla de Actividades") | |
df_table = gr.DataFrame(value=initial_df, headers=list(initial_df.columns), interactive=True, height=300) | |
gr.Markdown("### 📈 Diagrama de Gantt") | |
gantt_plot = gr.Plot(create_gantt_chart(initial_df)) | |
# --- 5. LÓGICA DE EVENTOS --- | |
task_inputs = [txt_task_id, txt_task_name, dd_phase, dd_responsible, date_start, date_end, dd_status, slider_progress, txt_description] | |
btn_add.click(fn=lambda *args: manage_tasks(*args), inputs=[gr.State("add"), df_state, selected_task_id] + task_inputs, outputs=[df_state, gantt_plot, dd_phase, dd_responsible]) | |
btn_update.click(fn=lambda *args: manage_tasks(*args), inputs=[gr.State("update"), df_state, selected_task_id] + task_inputs, outputs=[df_state, gantt_plot, dd_phase, dd_responsible]) | |
btn_delete.click(fn=lambda *args: manage_tasks(*args), inputs=[gr.State("delete"), df_state, selected_task_id] + task_inputs, outputs=[df_state, gantt_plot, dd_phase, dd_responsible]) | |
df_table.select(fn=populate_form_on_select, inputs=[df_state], outputs=[selected_task_id, txt_task_name, dd_phase, dd_responsible, date_start, date_end, dd_status, slider_progress, txt_description]) | |
df_state.change(fn=lambda df: df, inputs=[df_state], outputs=[df_table]) | |
df_state.change(fn=create_gantt_chart, inputs=[df_state], outputs=[gantt_plot]) | |
if __name__ == "__main__": | |
demo.launch(debug=True) |