Skip to content

Commit f7f74d3

Browse files
apiadclaude
andcommitted
docs(reviews): add Proyecto I code reviews (batch 4: #250,#251,#255,#256,#257)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f5e8ca6 commit f7f74d3

5 files changed

Lines changed: 798 additions & 0 deletions

File tree

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Reporte de Evaluación — Proyecto I (Programación, 1er año)
2+
3+
- **Issue:** #250
4+
- **Repositorio:** https://github.com/niitse34/CM-Chess-Club
5+
- **Estudiante:** Leonardo Córdova Rosas
6+
- **Grupo:** C122
7+
- **Descripción del issue:** Planificador de eventos para un club de ajedrez, GUI en Streamlit, lógica de no solapamiento.
8+
- **Fecha de evaluación:** 2026-07-06
9+
10+
---
11+
12+
## Resumen de ejecución (lo que realmente corrí)
13+
14+
Se creó un entorno aislado con `uv venv --python 3.12` y se instaló `streamlit`
15+
(1.59.0). El proyecto **no es una app de consola**: es una app web Streamlit
16+
(`gui.py` como punto de entrada, `run.sh``streamlit run gui.py`). Se ejecutó
17+
de tres formas:
18+
19+
1. **`streamlit.testing.v1.AppTest`** recorriendo las 6 páginas del menú
20+
(`Events`, `Add Event`, `Find Slot`, `Resources`, `Save/Load`, `Settings`).
21+
**Todas renderizan sin excepción.**
22+
2. **Ejercicio directo de la lógica de dominio** (`main.ChessClub`) con 12 casos
23+
de prueba cubriendo validaciones, solapamiento, piezas de repuesto y
24+
persistencia. **Todos se comportaron correctamente** (detalle abajo).
25+
3. **Servidor Streamlit real** (`streamlit run gui.py --server.headless true`):
26+
arrancó limpio y respondió **HTTP 200** en la raíz.
27+
28+
Resultado de los 12 casos de lógica (todos correctos):
29+
30+
| # | Caso | Resultado observado |
31+
|---|------|---------------------|
32+
| 1 | friendly_match válido (board + pieces) | `(True, 'Scheduled: Amistosa')` |
33+
| 2 | friendly_match sin piezas | `(False, 'Missing required resources ...')` |
34+
| 3 | reloj en una clase (exclusión) | `(False, 'Clocks only for tournaments and team matches')` |
35+
| 4 | `end <= start` | `(False, 'Invalid time: end must be after start')` |
36+
| 5 | evento hoy (pasado) | `(False, 'Events can only be scheduled from tomorrow onwards')` |
37+
| 6 | evento en día bloqueado (Monday) | `(False, 'Events cannot be scheduled on Mondays')` |
38+
| 7 | clase de 30 min (< 1h mínimo) | `(False, 'class requires minimum 1h duration')` |
39+
| 8 | doble reserva del mismo recurso (solapamiento) | `(False, 'Unavailable or missing: Casual Board 1')` |
40+
| 9 | recurso inexistente | `(False, 'Unavailable or missing: nope_id (not found)')` |
41+
| 10 | `find_next_slot(1h, ['c_board_1'])` | devolvió `2026-07-07 09:00:00` |
42+
| 11 | pool de piezas: 6 eventos × 10 piezas > 50/día | los 5 primeros OK, el 6º `(False, 'Not enough spare pieces ... 50 available, 60 needed')` |
43+
| 12 | guardar + recargar JSON | 1 evento persistido y releído correctamente |
44+
45+
**La lógica central de no-solapamiento y validación funciona de verdad al
46+
ejecutar, no solo en el papel.**
47+
48+
---
49+
50+
## 1. Qué hace el programa
51+
52+
Es un **planificador de eventos para un club de ajedrez ("Critical Mass Chess
53+
Club")** con interfaz web construida en Streamlit. El punto de entrada es
54+
`gui.py` (`gui.py:1-48`); se lanza con `bash run.sh` (`run.sh:2`) y abre en
55+
`http://localhost:8501`. El modelo de dominio vive en `main.py` (clase
56+
`ChessClub`, `main.py:8`), las entidades en `models.py` (`Resource`, `Event`) y
57+
la persistencia en `file_processing.py`.
58+
59+
El flujo principal: el club gestiona **recursos** (salas, equipamiento —tableros,
60+
piezas, relojes, proyector— y personal —entrenadores FIDE, árbitros,
61+
comentaristas—, definidos en `resources.json`). El usuario agenda **eventos** de
62+
seis tipos (torneo, clase, enfrentamiento, partida amistosa, análisis,
63+
simultánea) desde la página "Add Event" (`gui.py:77-146`). Al agendar, el sistema
64+
valida en cascada: tiempo válido, fecha futura, día no bloqueado, duración
65+
mínima, horario del club, **disponibilidad (no solapamiento)** de cada recurso,
66+
**correquisitos y exclusiones** declarados en JSON, y el **pool diario de piezas
67+
de repuesto**. Además ofrece: búsqueda del próximo hueco libre
68+
(`find_next_slot`, `main.py:171`), monitoreo de carga horaria de entrenadores con
69+
sugerencia de alternativa (`get_coach_workload`/`suggest_alternative_coach`,
70+
`main.py:222-254`), y un panel "Settings" para editar tipos de evento, recursos y
71+
restricciones en caliente.
72+
73+
Es un proyecto **notablemente ambicioso para 1er año**: el dominio está bien
74+
pensado y las reglas de negocio están externalizadas a configuración.
75+
76+
## 2. Organización del código
77+
78+
Muy buena para el nivel. El código está **repartido en cuatro módulos** con
79+
responsabilidades claras, en vez de un `main.py` monolítico:
80+
81+
- `models.py` — entidades `Resource` y `Event` con `to_dict()` para serializar
82+
(`models.py:3-31`).
83+
- `main.py` — clase `ChessClub` con toda la lógica de dominio, bien dividida en
84+
métodos cortos y de nombre expresivo (`search_resource`, `check_available`,
85+
`validate_restrictions`, `schedule_event`, `find_next_slot`, `delete_event`…).
86+
- `file_processing.py` — funciones libres `read_json`/`write_json` y clase
87+
`FileProcessing` para guardar/cargar (`file_processing.py:5-52`).
88+
- `gui.py` — capa de presentación Streamlit, separada de la lógica.
89+
90+
Los nombres de variables y funciones son claros y en inglés consistente. Hay
91+
reutilización real: `check_available` se llama tanto desde `schedule_event`
92+
como desde `find_next_slot`; `search_resource` centraliza la búsqueda. La
93+
separación presentación/lógica es exactamente lo que se espera y rara vez se ve
94+
tan limpio en un primer proyecto. `main.py:1` empieza con una línea en blanco
95+
(cosmético, sin efecto).
96+
97+
## 3. Corrección funcional (basada en ejecución real)
98+
99+
**Arranca perfectamente** y las 6 páginas renderizan sin `Traceback` (ver
100+
"Resumen de ejecución"). El servidor real devolvió HTTP 200.
101+
102+
La lógica hace **exactamente** lo que promete el issue y el informe. Verificado
103+
al correr (`main.py:85-169` para `schedule_event`):
104+
105+
- **No solapamiento** (`check_available`, `main.py:25-34`): la condición
106+
`not (end <= event.start or start >= event.end)` es el algoritmo correcto de
107+
intersección de intervalos. El caso 8 confirmó que rechaza doble reserva del
108+
mismo tablero.
109+
- **Correquisitos y exclusiones** (`validate_restrictions`, `main.py:59-83`):
110+
casos 2 y 3 confirmaron el rechazo con mensajes descriptivos.
111+
- **Validaciones de tiempo/fecha/duración/día bloqueado**: casos 4-7 todos
112+
correctos.
113+
- **Pool de piezas de repuesto** (`main.py:151-161`): caso 11 confirmó el
114+
rechazo del 6º evento al exceder 50 piezas/día.
115+
- **Persistencia** (caso 12): round-trip de guardado/carga correcto.
116+
117+
Validación de entradas: **sólida**. Se manejan recursos inexistentes (caso 9),
118+
formatos de hora mal en config con `try/except` (`main.py:127-128`), y valores
119+
de política no numéricos (`main.py:50-57`). La GUI también valida campos vacíos
120+
antes de agendar (`gui.py:129-134`).
121+
122+
**Discrepancia menor informe↔config:** el informe (`report.md:51`) dice "los
123+
relojes solo pueden ser utilizados en torneos", pero la regla real en
124+
`resources.json:145-147` los permite en `tournament` **y** `team_match`. El
125+
mensaje de error del código ("Clocks only for tournaments and team matches") es
126+
el correcto; el informe simplificó de más en ese punto.
127+
128+
No encontré ninguna opción que lanzara excepción durante el recorrido.
129+
130+
## 4. Buenas prácticas de Python (nivel principiante)
131+
132+
Muy por encima del nivel esperado:
133+
134+
- **Legibilidad e indentación**: consistentes en todo el proyecto.
135+
- **`try/except` donde toca**: parseo de config (`main.py:50-57`, `116-128`),
136+
guardas defensivas alrededor del pool de piezas (`main.py:152-161`). El
137+
`except Exception: pass` de `main.py:160` es demasiado amplio (silencia
138+
cualquier error), pero es un desliz menor.
139+
- **f-strings** usadas idiomáticamente en mensajes y en la GUI.
140+
- **Type hints ligeros** en varias firmas (`main.py:18,25,85,214,222`) — no se
141+
exigen en 1er año, es un plus.
142+
- **Comprehensions claras** (`main.py:64,154,157`) sin abusar de anidamiento.
143+
- Sin variables globales problemáticas: el estado vive en la instancia
144+
`ChessClub` y en `st.session_state`.
145+
146+
Puntos a pulir: el `except Exception: pass` mencionado; y `type` se usa como
147+
nombre de parámetro/variable en varios sitios (`main.py:47,85,106…`), lo que
148+
sombrea la función incorporada `type()` — funciona, pero conviene evitarlo.
149+
150+
## 5. Datos y persistencia
151+
152+
Bien resuelto. **Dos archivos con responsabilidades separadas**:
153+
`resources.json` (configuración: recursos, tipos de evento, restricciones,
154+
`config`) y `CM_chess_club.json` (eventos agendados). Las estructuras son
155+
razonables: listas de objetos en memoria, serializadas vía `Event.to_dict()`
156+
(`models.py:22-31`) que guarda solo los **IDs** de los recursos y los rehidrata
157+
al cargar buscándolos por ID (`file_processing.py:48-51`) — decisión correcta
158+
que evita duplicar objetos. Las rutas se construyen con
159+
`os.path.dirname(os.path.abspath(__file__))` (`file_processing.py:6`), así que
160+
la app funciona sin importar el directorio de trabajo. El caso 12 confirmó el
161+
round-trip. Guardado automático tras cada alta/baja en la GUI
162+
(`gui.py:73,141`).
163+
164+
## 6. Informe (`report.md`)
165+
166+
El informe es **excelente y honesto**: describe con precisión lo que el código
167+
hace, explica las decisiones de diseño (por qué Streamlit, por qué JSON, por qué
168+
externalizar reglas), documenta las funcionalidades una por una y hasta incluye
169+
una sección de "Lecciones asimiladas" madura (`report.md:114-116`). No infla:
170+
casi todo lo que afirma está respaldado por el código ejecutado.
171+
172+
Dos matices:
173+
174+
- **Sobre-simplificación** en `report.md:51` (relojes "solo en torneos" cuando
175+
también valen para enfrentamientos) — ver dimensión 3.
176+
- **Testing declarado pero no incluido**: el informe describe "pruebas
177+
sistemáticas" (`report.md:110-112`) y `pyproject.toml` declara pytest como
178+
dependencia opcional (`pyproject.toml:27-31`), pero `.gitignore` excluye
179+
`test_edge_cases.py` y `test_main.py`, así que **los tests no se subieron al
180+
repo**. No penaliza en 1er año (los tests no se exigen), pero conviene saber
181+
que el informe habla de pruebas que no están versionadas. La verificación
182+
dinámica que hice yo confirma que la lógica sí es correcta.
183+
184+
También hay un desajuste menor entre la sección "Estructura" del informe
185+
(`report.md:86-94`, menciona `main.py` como "programa") y la realidad (el
186+
programa se lanza por `gui.py`); es un residuo de una versión anterior.
187+
188+
---
189+
190+
## Valoración global (orientativa, sin nota)
191+
192+
Trabajo **sobresaliente para un primer proyecto de 1er año**. La arquitectura
193+
modular, la separación presentación/lógica, la externalización de reglas a JSON
194+
y la corrección real de la lógica de no-solapamiento y validaciones —todas
195+
verificadas ejecutando— lo colocan claramente por encima del nivel esperado. La
196+
principal fortaleza es que **funciona de verdad y está bien organizado**; las
197+
áreas de mejora son cosméticas (un `except` demasiado amplio, sombrear `type`) y
198+
de honestidad documental (tests mencionados pero no subidos, y una
199+
simplificación de más en una regla). Nada de esto compromete la calidad del
200+
entregable.

0 commit comments

Comments
 (0)