# Laboratorio de la semana 2: leer un programa ajeno y extenderlo

`grades.c` es un programa de unas 200 líneas que lee un archivo de notas y escribe un
reporte y un resumen. No lo escribieron ustedes. Primero lo van a leer, después lo van a
corregir y al final lo van a extender, sin romper lo que ya funciona.

| Archivo | Qué es |
|---|---|
| `grades.c` | El programa |
| `notas.txt` | Datos de ejemplo. Los nombres y las notas son inventados |
| `test_grades.c` | Sus pruebas en C. Se corren con `make test-grades` |
| `casos.txt` | Un archivo pequeño que usa `test_grades.c` |

Todos los comandos se corren desde `code/week-02`, salvo que se indique otra cosa:

```sh
make lab                                            # compila
cd lab && printf 'notas.txt\n' | ../build/lab/grades; cd ..   # corre el programa
make test-grades                                    # corre las pruebas en C
make test-lab                                       # corre todas las pruebas del laboratorio
```

`make test-lab` comprueba las dos sesiones, así que falla hasta que terminen la sesión 2.
Sus grupos son `Session1`, `WhatAlreadyWorked` y `Session2`. Para ver uno solo:

```sh
LAB_DIR=lab python3 -m unittest -v test_lab.Session1
```

## Sesión 1: leer y corregir

### Parte 1: leer sin cambiar nada

No cambien nada todavía. Sigan el método de los apuntes: `main` y las firmas, ejecutar con
un ejemplo, seguir un dato de principio a fin, y escribir lo que entendieron. Respondan por
escrito, en un archivo `lab/RESPUESTAS.md`, con sus palabras y sin copiar los comentarios del
código.

1. **La forma de los datos.** ¿Qué tiene cada línea de `notas.txt`? Dibujen un `struct student`
   con sus campos y los valores de la línea `A Ana 14 9 17 12`.
2. **Capacidad y longitud.** En `main`, ¿cuántos estudiantes caben en `list` y cuántos tiene
   de verdad? ¿Qué constante y qué variable lo dicen?
3. **Qué hace cada función.** Hagan una tabla con una fila por función: su nombre y una sola
   frase con lo que hace. Si necesitan la palabra "y", revisen si entendieron bien.
4. **Quién llama a quién.** Dibujen el árbol de llamadas desde `main`, como el de los apuntes.
   ¿Qué función no llama nadie todavía?
5. **Un dato de principio a fin.** Sigan la línea `A Ana 14 9 17 12` desde el archivo hasta la
   fila del reporte. Digan por qué funciones pasa y en qué forma está en cada una: un arreglo de
   `char`, un `struct`, un elemento de `list`, texto en la pantalla.
6. **Copia o arreglo.** ¿Qué funciones reciben un `struct student` y cuáles un arreglo? ¿Cuáles
   podrían cambiar los datos de quien las llama? ¿Qué dice `const` al respecto?
7. **Dos preguntas de diseño.** ¿Por qué `parse_student` llena una variable local antes de
   copiarla a `list[pos]`? ¿Por qué `is_passed` compara enteros en vez de usar `average`?
8. **Lo que dicen las pruebas.** Lean `test_parse_student` en `test_grades.c`. Sin mirar `grades.c`,
   digan qué líneas acepta el programa y cuáles no. ¿Qué dice esa prueba que un comentario no
   podría garantizar?
9. **La salida.** Ejecuten el programa y miren el reporte con cuidado, comparándolo con
   `notas.txt`. Anoten todo lo que les parezca raro, antes de correr las pruebas.

### Parte 2: dos bugs

El programa tiene exactamente dos bugs. Corran `make test-grades`.

1. Antes de abrir `grades.c`, ordenen lo que ven: ¿cuántas comprobaciones fallan? ¿Cuáles podrían
   tener la misma causa? Hay más comprobaciones rojas que bugs.
2. Tomen una comprobación que falle, la más pequeña. Léanla: dice qué entrada se dio y qué se
   esperaba. Lleguen hasta la función responsable y escriban en `RESPUESTAS.md` su hipótesis
   antes de cambiar nada.
3. Corrijan **una sola cosa** y vuelvan a correr las pruebas. Miren qué se puso verde y qué no.
4. Repitan con el otro bug. Cada corrección cabe en una línea: si están cambiando mucho más,
   paren y vuelvan a leer.
5. Revisen lo que anotaron en la pregunta 9: ¿qué bug explica cada cosa rara?

Al terminar, `make test-grades` dice `Todas las pruebas pasan`, y también pasa:

```sh
LAB_DIR=lab python3 -m unittest -v test_lab.Session1 test_lab.WhatAlreadyWorked
```

Antes de seguir, guarden una copia del programa corregido, para poder compararla después:

```sh
cp lab/grades.c lab/grades_session1.txt
```

## Sesión 2: extender sin romper

Corran las pruebas antes de empezar. Después de cada cambio, vuelvan a correrlas.

### Parte 3: el promedio sin la nota más baja

En este mismo curso, el promedio de prácticas no cuenta la nota más baja. Agreguen eso al
programa. Escriban el cuerpo de `average_without_lowest`, que ya tiene su firma en `grades.c`:

```c
double average_without_lowest(struct student s);
```

Devuelve el promedio de las notas de `s` sin contar la más baja. Si la nota más baja se repite,
se quita **una sola vez**: las notas `5 5 11 11` dan `(5 + 11 + 11) / 3`, que es 9. El
resultado lleva decimales.

1. **Primero las pruebas.** En `test_grades.c`, escriban `test_average_without_lowest`, llámenla
   desde `main` con `failed += test_average_without_lowest();`, y corran `make test-grades`: debe
   fallar. Escriban por lo menos cuatro casos, y piensen dónde está la trampa: la nota más baja al
   inicio, al final, repetida, y con todas las notas iguales.
2. Escriban la función hasta que sus pruebas pasen.
3. Muéstrenla en el reporte: una columna más al final de cada fila, con dos decimales, y el título
   `SinBaja` en el encabezado. Las columnas que ya estaban no cambian:

```text
Sec Nombre       N1 N2 N3 N4   Prom  Estado       SinBaja
A   Ana          14  9 17 12  13.00  aprobado     14.33
```

### Parte 4: filtrar por sección

Agreguen una opción: el programa puede leer de la entrada una **segunda línea**, con la letra de
una sección. Si viene, el reporte y el resumen son solo de esa sección. Si no viene, o si la línea
está vacía, salen todos, como hasta ahora. Con una sección que no tiene estudiantes, el programa
escribe `No hay estudiantes`, como con un archivo vacío.

```sh
cd lab && printf 'notas.txt\nB\n' | ../build/lab/grades; cd ..
```

El reto es tocar **la menor cantidad posible de código**. Antes de escribir, respondan en
`RESPUESTAS.md`:

1. Si cada función de reporte y de resumen recibiera la sección como parámetro, ¿cuántas funciones
   habría que cambiar? ¿Y cuántas pruebas?
2. ¿Hay un punto del programa por donde pasan todos los datos, antes de llegar a esas funciones?

Las firmas de las funciones que ya existen no deben cambiar: las pruebas las llaman. Agreguen las
funciones que necesiten, con sus pruebas en `test_grades.c`.

Al terminar, comparen con lo que guardaron:

```sh
diff lab/grades_session1.txt lab/grades.c
```

Escriban en `RESPUESTAS.md` cuántas funciones que ya existían tuvieron que cambiar, y por qué. Y
borren `lab/grades_session1.txt`.

`make test-lab` debe pasar completo.
