Indice / Practicas / EJ-1.1
EJ-1.1 · Directorio de contactos validado
La primera práctica del curso, y la única que se resuelve entera con la biblioteca estándar. Cuarenta contactos, seis que no sirven, y un archivo que explica por qué.
Qué hay que hacer
A partir de contactos_origen.csv (40 filas), producir
dos archivos:
contactos_validos.csv- los contactos utilizables, con el correo normalizado a minúsculas.contactos_rechazados.csv- una fila por contacto descartado, diciendo cuál, dónde y por qué.
Sin pandas. csv.DictReader para leer,
csv.DictWriter para escribir, y una decisión por fila.
ELI5Explicado facil
Es la portería de un edificio. Entra quien tiene nombre, timbre y teléfono; el que no, no entra - y queda anotado en el libro, con el motivo.
Lo importante no es la lista de los que entraron: es que el libro exista. Sin él, nadie puede saber después si faltó alguien.
Archivos
- Entrada ·
contactos_origen.csv - Objetivo ·
contactos_validos.csv - Objetivo ·
contactos_rechazados.csv - Herramienta ·
comparar.py
Lo que hace interesante al ejercicio
Las cuarenta filas se leen sin un solo error. Ninguna excepción, ningún
ValueError: los seis defectos están todos en la capa 3 y
en la capa 4 de la lección 1.5, así que hay que buscarlos a
propósito.
Y uno de los seis solo se puede detectar recordando lo que ya viste: el
correo elena.escobar@example.com aparece en la fila 8 y otra vez en la 13.
Mirando una fila sola, las dos son perfectas.
Cómo saber si te salió
python comparar.py mi_validos.csv contactos_validos.csv
python comparar.py mi_rechazados.csv contactos_rechazados.csv
Si no dice nada, tu archivo es idéntico al esperado. Si difiere, te dice qué fila y qué columna. No hay nota ni entrega: es para vos.
Antes de arrancar, mirá los datos
| nombre | correo | telefono | ciudad |
|---|---|---|---|
| Guadalupe Vega | guadalupe.vega@example.com | +54 11 5555-0895 | San Juan |
| José Juárez | jose.juarez@example.com | +54 11 5555-0218 | San Juan |
| Yamila Cabrera | yamila.cabrera@example.com | +54 11 5555-0121 | Bahía Blanca |
| Nicolás Escobar | nicolas.escobar@example.com | +54 11 5555-0547 | Rosario |
| Marta Torres | vacio | +54 11 5555-0798 | Córdoba |
El contrato de salida
contactos_validos.csv · 34 filas
| Columna | Tipo | Que lleva |
|---|---|---|
nombre | texto | El nombre tal cual vino, sin espacios en los extremos |
correo | texto | El correo en minusculas y sin espacios en los extremos |
telefono | texto | El telefono tal cual vino, sin espacios en los extremos |
ciudad | texto | La ciudad tal cual vino, sin espacios en los extremos |
contactos_rechazados.csv · 6 filas
| Columna | Tipo | Que lleva |
|---|---|---|
fila | entero | Numero de registro en el archivo de entrada, empezando en 1. La cabecera NO cuenta |
columna | texto | La columna donde se detecto el defecto |
motivo | texto | Uno de: ausente, restriccion_violada, duplicado |
valor | texto | El valor que causo el rechazo, tal cual vino en el archivo y sin espacios en los extremos. Si la celda estaba vacia, el campo queda vacio |
Que hacer con cada defecto
| Defecto | Donde | Accion | Adonde va |
|---|---|---|---|
| campo obligatorio ausente | 4 filas: correo en la 5 y en la 33, telefono en la 12, nombre en la 19 | rechazar rechazar la fila entera | contactos_rechazados.csv con motivo ausente |
| el token N/A en correo | fila 33 | tratarlo tratarlo como ausente, igual que la celda vacia. La lista de tokens es NULOS = {'', '-', 'N/A', 'NaN', 'null'} | contactos_rechazados.csv con motivo ausente y valor N/A |
| telefono que no empieza con +54 | fila 27, con el texto 'no tiene' | rechazar rechazar la fila entera | contactos_rechazados.csv con motivo restriccion_violada |
| correo repetido | filas 8 y 13, las dos con elena.escobar@example.com | se se queda la primera aparicion y se rechaza la segunda. La comparacion se hace en minusculas | contactos_rechazados.csv con motivo duplicado |
| correo sin arroba | 0 filas en este archivo | la la comprobacion va igual: es la misma regla que EJ-5.2 aplica sobre clientes_crudos, donde si hay 11 casos | contactos_rechazados.csv con motivo restriccion_violada |
Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:
- ausente en nombre, correo, telefono o ciudad, en ese orden de columnas
- correo sin arroba
- telefono que no empieza con +54
- correo duplicado, comparando en minusculas contra las filas ya aceptadas
Una fila se rechaza UNA vez, con el primer defecto que aparece. Y el duplicado va ultimo a proposito: una fila que ya se rechazo por otra cosa no debe ocupar el lugar de un correo, o el duplicado se detectaria contra una fila que no llego a la salida.
Conteos clave
| filas de entrada | 40 |
|---|---|
| contactos_validos.csv | 34 |
| contactos_rechazados.csv | 6 |
| por motivo | ausente: 4, restriccion_violada: 1, duplicado: 1 |
Invariantes
Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:
len(validos) + len(rechazados) == 40
len(validos) == 34
validos['correo'].is_unique
(validos['correo'] == validos['correo'].str.lower()).all()
validos['correo'].str.contains('@').all()
validos['telefono'].str.startswith('+54').all()
not validos.isna().any().any()
rechazados['fila'].is_unique
rechazados['fila'].between(1, 40).all()
set(rechazados['motivo']) <= {'ausente', 'restriccion_violada', 'duplicado'}
validos['correo'].is_monotonic_increasing
PISTASSi te trabas, abri esto
- newline='' va en el open, al leer y al escribir. Sin eso, el CSV de salida se abre en Excel con una fila vacia entre cada dos.
- Del CSV todo llega como texto: 'fila' es el unico numero de las dos salidas, y sale de enumerate(..., start=1), no de una columna del archivo.
- El duplicado necesita memoria: un set con los correos ya aceptados. Y se guarda el correo YA normalizado, o la segunda aparicion escrita distinto no se detecta.
- El assert len(validos) + len(rechazados) == 40 antes de escribir nada. Si no da, hay una fila que no fue ni aceptada ni rechazada.
Así tienen que quedar
| nombre | correo | telefono | ciudad |
|---|---|---|---|
| Agustín Gómez | agustin.gomez@example.com | +54 11 5555-0767 | Paraná |
| Agustín Peña | agustin.pena@example.com | +54 11 5555-0474 | La Plata |
| Carla Gutiérrez | carla.gutierrez@example.com | +54 11 5555-0711 | Paraná |
| Diego Ibarra | diego.ibarra@example.com | +54 11 5555-0745 | Tucumán |
| Elena Escobar | elena.escobar@example.com | +54 11 5555-0147 | Bahía Blanca |
| fila | columna | motivo | valor |
|---|---|---|---|
| 5 | correo | ausente | vacio |
| 12 | telefono | ausente | vacio |
| 13 | correo | duplicado | elena.escobar@example.com |
| 19 | nombre | ausente | vacio |
| 27 | telefono | restriccion_violada | no tiene |
Una fila se rechaza una sola vez, con el primer defecto que aparece. Y el duplicado va último a propósito: si una fila que ya se rechazó por otra cosa ocupara el lugar de un correo, el duplicado se detectaría contra una fila que nunca llegó a la salida.
Es el tipo de decisión que no cambia si el archivo está limpio y que cambia el resultado en cuanto no lo está. Por eso está escrita.
newline=""en elopen, al leer y al escribir. Sin eso, el CSV de salida se abre en Excel con una fila vacía entre cada dos.escritor.writeheader()antes de las filas, aunque no haya ninguna. Un archivo con cabecera y cero filas dice «revisé y no encontré nada»; uno vacío no dice nada.- El
setde correos vistos guarda el correo ya normalizado. Si guardás el original, una segunda aparición escrita con otras mayúsculas no se detecta.
Antes de escribir nada
assert len(validos) + len(rechazados) == 40
Es el invariante de la lección 1.6 y atrapa la clase entera de errores: si no da, hay una fila que no fue ni aceptada ni rechazada, y desapareció sin que nadie se entere.