Aquí tienes un guion de prompts listo para usar con
Copilot en Codespaces (modo agente). Están ordenados
por fases. Copia y pega cada bloque, espera el resultado, ejecuta y
valida. Mantén el nombre del proyecto:
rubrica-streamlit.
Prompt 0.1 — Inicialización
Actúa como agente. Crea la estructura base de un proyecto Streamlit con SQLite para una app de rúbricas:
- Carpeta raíz: rubrica-streamlit
- Archivos: README.md, requirements.txt, .gitignore, app.py, db.py, utils.py
- Carpeta data/ para exportes CSV
- requirements.txt con: streamlit, pandas, pydantic
- .gitignore con: __pycache__/, .streamlit/, rubrica.db, data/*.csv
Explica cada archivo y deja comandos para ejecutar localmente.
Prompt 0.2 — README inicial
Genera un README.md con: descripción del proyecto, ejecución local (streamlit run app.py), variables configurables, y notas de despliegue en Streamlit Community Cloud y Render. Incluye una sección “Roadmap”.
Prompt 1.1 — Módulo db.py
Implementa db.py con una interfaz CRUD segura para SQLite:
- open_conn(path="rubrica.db"): aplica PRAGMAs WAL, synchronous=NORMAL, foreign_keys=ON
- init_db(): crea tabla evaluaciones(id, curso, evaluacion, fecha, grupo_o_estudiante, estructura, programacion, teoria, ia, reflexion, presentacion, nota_final, observaciones, created_at)
- insert_evaluacion(dict)
- list_resumen(order="DESC"): devuelve id, fecha, grupo_o_estudiante, nota_final
- list_detalle(filtro_texto=None, fecha=None, order="DESC")
- export_csv(path): exporta todo a CSV en data/
Incluye typing y manejo de errores.
Prompt 1.2 — Datos de ejemplo
Agrega en db.py una función seed_demo() que inserte 5 registros de ejemplo para pruebas. No se ejecuta por defecto, sólo cuando se llama explícitamente desde app.py con un checkbox “Cargar datos demo”.
Prompt 2.1 — Módulo utils.py
Implementa utils.py con:
- PESOS dict: estructura=15, programacion=20, teoria=15, ia=10, reflexion=15, presentacion=25
- DESCRIPCIONES dict legibles
- validate_notas(notas: dict) -> None: valida rango 1–5
- nota_final(notas: dict, pesos=PESOS) -> float: redondeo 2 decimales
- niveles_texto(): retorna “1=Deficiente · 2=Básico · 3=Aceptable · 4=Bueno · 5=Excelente”
Incluye pruebas unitarias simples dentro de un bloque `if __name__ == "__main__":` (asserts).
Prompt 3.1 — app.py: Layout y Sidebar
En app.py:
- set_page_config(title, icon, layout="wide")
- CSS simple para cabecera con logo (URL parametrizable) y card styling
- Sidebar: curso, evaluación, fecha (por defecto hoy), lista editable de grupos/estudiantes (textarea, 1 por línea), modo de almacenamiento (“SQLite” o “Sólo CSV”), botón “Cargar datos demo” que llama a seed_demo()
- Llama a init_db() al inicio
Prompt 3.2 — app.py: Calificación en vivo
En app.py agrega panel principal:
- Selectbox de grupo/estudiante a partir del roster del sidebar
- Seis sliders (1–5, step 0.5) con etiquetas y pesos desde DESCRIPCIONES/PESOS
- Cálculo en vivo de nota final con utils.nota_final()
- Text area de observaciones
- Botón “Guardar evaluación”
Lógica:
- Si modo “SQLite”: usar db.insert_evaluacion(); éxito con st.success
- Si modo “Sólo CSV”: acumular en un df de sesión (st.session_state) y ofrecer descarga CSV inmediata
Incluye validación con utils.validate_notas.
Prompt 3.3 — app.py: Reporte y exportes
Añade dos secciones:
1) “Reporte” a la derecha: tabla de resumen (id, fecha, grupo_o_estudiante, nota_final). Botón para descargar CSV de resumen.
2) “Detalle y filtros”: filtros por texto (LIKE) y fecha exacta, muestra tabla completa. Botón para descargar CSV detallado.
Si el modo es “Sólo CSV”, operar sobre el df en memoria; si es “SQLite”, consultar db.list_resumen y db.list_detalle.
Prompt 3.4 — app.py: Ayuda y accesos
Agrega un expander “Ayuda rápida” con pasos de uso en clase.
Agrega control de acceso básico opcional: text_input password comparado con st.secrets["EVAL_KEY"] (si existe). Si no coincide, st.stop() con mensaje.
Prompt 4.1 — Run y QA
Indica comandos para ejecutar localmente:
- pip install -r requirements.txt
- streamlit run app.py
Incluye lista de pruebas manuales:
1) Guardar evaluación SQLite y verla en resumen/detalle
2) Modo “Sólo CSV”: agregar filas y descargar
3) Filtros por texto y fecha
4) Carga de datos demo
5) Validación de rangos (notas fuera de 1–5)
6) Export CSV desde SQLite y desde memoria
Redacta un checklist de QA en README.
Prompt 5.1 — Streamlit Community Cloud
Agrega instrucciones en README para desplegar en Streamlit Community Cloud:
- Conectar repo
- Variables en Secrets (opcional): EVAL_KEY
- Límite de “sleep” y cómo reactivar
Crea un archivo .streamlit/config.toml con un tema claro y ancho “wide”.
Prompt 5.2 — Render (24/7 recomendado)
Agrega una sección en README: despliegue en Render como web service:
- Dockerfile mínimo o usar build command: pip install -r requirements.txt
- Start command: streamlit run app.py --server.port $PORT --server.address 0.0.0.0
Incluye nota sobre costo aproximado y persistencia del archivo rubrica.db.
Prompt 6.1 — Selector de plantilla de rúbrica
Generaliza la app para múltiples programas:
- En sidebar, selectbox “Plantilla de rúbrica”: {Agroindustrial, Civil, Estadística}
- Cambia PESOS y DESCRIPCIONES en utils según plantilla seleccionada (define tres dicts y un factory).
- Guarda el nombre de plantilla en la tabla (columna “curso” o “plantilla”).
Prompt 7.1 — Backups automáticos
Implementa en db.py una función backup_csv_timestamp() que exporte la tabla completa a data/backup_YYYYMMDD_HHMMSS.csv.
En app.py, agrega un botón “Backup CSV” en la sección de detalle que llama a esa función cuando modo=SQLite.
Prompt 8.1 — Accesibilidad y estilo
Mejora la accesibilidad:
- Etiquetas cortas y descriptivas
- Placeholders en inputs
- Mensajes de error claros
Agrega contador de registros y promedio de nota final en la parte superior del reporte.
streamlit run app.py) y
valida.¿Quieres que además te deje un set de datos de prueba (CSV) y un script de carga masiva a SQLite para simular una sesión con 10–15 grupos?
Aquí tiene un set de datos de prueba (CSV) para 15
grupos y un script de carga masiva a SQLite compatible
con su app rubrica-streamlit. Incluyo instrucciones de
uso.
Guárdelo como data/demo_evaluaciones_15.csv.
curso,evaluacion,fecha,grupo_o_estudiante,estructura,programacion,teoria,ia,reflexion,presentacion,observaciones
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 1,4.0,4.5,4.0,3.5,4.0,4.8,"Buen dominio general"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 2,3.5,4.0,3.5,3.0,3.5,4.2,"Mejorar claridad en teoría"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 3,4.5,4.8,4.2,4.0,4.3,4.9,"Excelente coordinación"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 4,3.0,3.5,3.2,3.0,3.0,3.8,"Códigos incompletos"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 5,4.2,4.0,4.0,3.5,4.1,4.6,"Buena interpretación"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 6,3.8,4.1,3.9,3.5,3.7,4.3,"Prompts adecuados"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 7,4.0,4.2,3.8,3.2,3.9,4.4,"Sólida exposición"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 8,3.6,3.9,3.5,3.3,3.6,4.0,"Mejorar estructura"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 9,4.4,4.6,4.3,3.9,4.2,4.7,"Resultados reproducibles"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 10,3.2,3.7,3.4,3.1,3.3,3.9,"Falta profundizar"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 11,4.1,4.2,4.0,3.6,4.0,4.5,"Buena coordinación del equipo"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 12,3.7,3.8,3.6,3.2,3.5,4.1,"Documentación mejorable"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 13,4.3,4.4,4.1,3.8,4.2,4.6,"Análisis sólido y claro"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 14,3.4,3.6,3.3,3.0,3.2,3.8,"Presentación correcta"
Fundamentos de Programación (Agroindustrial),Tarea Grupal — Secadores Solares,2025-11-05,Grupo 15,4.6,4.7,4.3,4.1,4.3,4.9,"Excelente dominio técnico"
Guárdelo como tools/load_csv_to_sqlite.py.
"""
Carga masiva de calificaciones desde CSV a SQLite para la app rubrica-streamlit.
Uso:
python tools/load_csv_to_sqlite.py --csv data/demo_evaluaciones_15.csv
Requisitos:
- Archivo de base de datos: rubrica.db (se crea si no existe)
- Estructura de tabla compatible con app.py:
evaluaciones(
id INTEGER PK, curso TEXT, evaluacion TEXT, fecha TEXT,
grupo_o_estudiante TEXT, estructura REAL, programacion REAL, teoria REAL,
ia REAL, reflexion REAL, presentacion REAL, nota_final REAL,
observaciones TEXT, created_at TEXT
)
"""
import argparse
import datetime as dt
import sqlite3
import pandas as pd
DB_PATH = "rubrica.db"
# Pesos coherentes con la rúbrica de la app
PESOS = {
"estructura": 15,
"programacion": 20,
"teoria": 15,
"ia": 10,
"reflexion": 15,
"presentacion": 25
}
def open_conn(path=DB_PATH):
"""Abre conexión SQLite con PRAGMAs para lecturas concurrentes razonables."""
conn = sqlite3.connect(path, check_same_thread=False, timeout=30.0)
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA synchronous=NORMAL;")
conn.execute("PRAGMA foreign_keys=ON;")
return conn
def init_db(conn):
"""Crea la tabla si no existe (esquema compatible con la app)."""
conn.execute("""
CREATE TABLE IF NOT EXISTS evaluaciones (
id INTEGER PRIMARY KEY AUTOINCREMENT,
curso TEXT,
evaluacion TEXT,
fecha TEXT,
grupo_o_estudiante TEXT,
estructura REAL,
programacion REAL,
teoria REAL,
ia REAL,
reflexion REAL,
presentacion REAL,
nota_final REAL,
observaciones TEXT,
created_at TEXT
)
""")
def validar_fila(r: pd.Series):
"""Valida rangos de notas y formato de fecha ISO."""
for k in ["estructura","programacion","teoria","ia","reflexion","presentacion"]:
try:
v = float(r[k])
except Exception as e:
raise ValueError(f"Valor no numérico en {k}: {r[k]}") from e
if not (1.0 <= v <= 5.0):
raise ValueError(f"Nota fuera de rango 1–5 en {k}: {v}")
# fecha en ISO YYYY-MM-DD
try:
dt.date.fromisoformat(str(r["fecha"]))
except Exception as e:
raise ValueError(f"Fecha no ISO (YYYY-MM-DD): {r['fecha']}") from e
def calc_nota_final(r: pd.Series) -> float:
"""Calcula la nota final ponderada con dos decimales."""
nf = (
r["estructura"] * PESOS["estructura"] +
r["programacion"] * PESOS["programacion"] +
r["teoria"] * PESOS["teoria"] +
r["ia"] * PESOS["ia"] +
r["reflexion"] * PESOS["reflexion"] +
r["presentacion"] * PESOS["presentacion"]
) / 100.0
return round(float(nf), 2)
def insert_row(conn, r: pd.Series):
"""Inserta una fila en la tabla evaluaciones."""
conn.execute("""
INSERT INTO evaluaciones (
curso, evaluacion, fecha, grupo_o_estudiante,
estructura, programacion, teoria, ia, reflexion, presentacion,
nota_final, observaciones, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
str(r["curso"]),
str(r["evaluacion"]),
str(r["fecha"]),
str(r["grupo_o_estudiante"]),
float(r["estructura"]),
float(r["programacion"]),
float(r["teoria"]),
float(r["ia"]),
float(r["reflexion"]),
float(r["presentacion"]),
float(r["nota_final"]),
str(r.get("observaciones", "")),
dt.datetime.now().isoformat(timespec="seconds")
))
def main(csv_path: str):
# Leer CSV
df = pd.read_csv(csv_path)
# Columnas requeridas
requeridas = [
"curso","evaluacion","fecha","grupo_o_estudiante",
"estructura","programacion","teoria","ia","reflexion","presentacion"
]
for c in requeridas:
if c not in df.columns:
raise ValueError(f"Falta columna requerida: {c}")
# Calcular nota_final si no existe
if "nota_final" not in df.columns:
df["nota_final"] = df.apply(calc_nota_final, axis=1)
# Validar filas
for _, row in df.iterrows():
validar_fila(row)
# DB
conn = open_conn()
init_db(conn)
with conn:
for _, row in df.iterrows():
insert_row(conn, row)
print(f"Cargadas {len(df)} filas desde {csv_path} → {DB_PATH}")
if __name__ == "__main__":
ap = argparse.ArgumentParser(description="Carga CSV de rúbricas a SQLite")
ap.add_argument("--csv", required=True, help="Ruta al CSV de evaluaciones")
args = ap.parse_args()
main(args.csv)
# Dentro del repo rubrica-streamlit
mkdir -p tools data
# Guarde el CSV en data/
# data/demo_evaluaciones_15.csv
# Instale dependencias (si no lo hizo antes)
pip install streamlit pandas
# Cargue a SQLite
python tools/load_csv_to_sqlite.py --csv data/demo_evaluaciones_15.csv
# Levante la app
streamlit run app.py