Diccionarios complejos (I)
Colecciones de datos
En las dos entradas previas en las que hemos trabajado con diccionarios hemos empleado de forma intencional formulaciones simples de esta colección de datos. Se trataba de comprender en qué consistía y cómo se podía trabajar con ella, así que la simplicidad se imponía como necesidad. Pero la realidad es que un diccionario "normal" no suele ser tan sencillo ni su virtud o interés requiere de que los sea.
En esta entrada idearemos una estructura compleja (y más realista, incluso) de un diccionario y trabajaremos con ella. Se trata de comprobar la realidad y la utilidad real de un diccionario. Para ello vamos a plantearlo como colección de datos potencialmente aplicable a un proyecto real de trabajo (como SEO) aunque ajustado a las necesidades didácticas del momento.
Imaginemos que sobre una estructura de diccionario queremos construir un recurso de seguimiento del alumnado. Se trata de una forma de expediente, muy parecido a lo que se podría dar soporte con una base de datos, pero también registrar y mantener como un documento JSON. Algo como esto:
"ALU-cod-n": {
"nombre": "al_nombre",
"curso": "al_curso",
"nee": true,
"medidas_apoyo": [
"mad_1"
],
"intervenciones": [
{
"fecha": "int_aa_mm_dd",
"motivo": "int_mot",
"acuerdos": "int_acuerdos"
}
]
},
Muestro aquí la estructura de un elemento del documento CSV sin contenidos ni reales ni supuestos (eso viene después) para que podemos observar en qué consiste la complejidad de la que estamos hablando cuando lo trasladamos a diccionario (ya sabemos que estructura y contenido se mantienen): ahora cada elemento del diccionario - aquí "ALU-cod-n": - es la clave de un value que es a su vez un diccionario, de ahí que el conjunto se pueda considerar como un diccionario de diccionarios.
Ese value además de ser un diccionario (de ahí que todo él se encuentre delimitado por {}, es un diccionario complejo ya que sus diferentes elementos-campos lo son de diferente tipología:
- Tenemos contenidos-campos clave:valor simples (y de distinto tipo de datos), como
"nombre": "al_nombre" - Pero también un contenido-campo cuyo value es una lista
"medidas_apoyo": ["mad_1"], - Y más aun, un contenido-campo cuyo value es una lista de diccionarios (de la que ahora sólo muestro su primer componente clave:valor por simplificar:
"intervenciones": [{"fecha": "int_aa_mm_dd",}]
Sobra decir que debermos conocer cómo tratar a cada subestructura o tipo de componente, tanto como con el conjunto. Empezaremos con ello a continuación.
import json
import os
def solicitar_booleano(mensaje):
"""
Solicita una respuesta booleana al usuario garantizando que la entrada sea s/n.
"""
while True:
respuesta = input(f"{mensaje} (s/n): ").strip().lower()
if respuesta in ['s', 'si', 'sí']:
return True
elif respuesta in ['n', 'no']:
return False
print(" [!] Entrada no válida. Por favor, introduzca 's' para Sí o 'n' para No.")
def capturar_ficha(codigo_num):
cod_alumno = f"ALU-cod-{codigo_num}"
print(f"\n==========================================")
print(f" NUEVA FICHA DE EXPEDIENTE: {cod_alumno}")
print(f"==========================================")
print("\n--- AVISO DE FORMATO DE ENTRADA ---")
print(" * Nombre del alumno: Apellidos, Nombre (ejemplo: García López, María)")
print(" * Curso: Etapa y grupo (ejemplo: 2º ESO B o 1º Bachillerato A)")
print("-------------------------------------\n")
nombre = input("Nombre completo del alumno (ej: García López, María): ").strip()
curso = input("Curso y grupo (ej: 2º ESO B): ").strip()
nee = solicitar_booleano("¿Presenta Necesidades Educativas Especiales (NEE)?")
# Se mantienen las estructuras de listas para su posterior implementación
return cod_alumno, {
"nombre": nombre,
"curso": curso,
"nee": nee,
"medidas_apoyo": [],
"intervenciones": []
}
def guardar_en_json(nuevos_expedientes, ruta_salida):
# Limpieza de comillas iniciales o finales introducidas por el usuario
ruta_salida = ruta_salida.strip('\"\'')
ruta_salida = os.path.normpath(ruta_salida)
# 1. Comprobar si el directorio existe y crearlo si no
directorio = os.path.dirname(ruta_salida)
if directorio and not os.path.exists(directorio):
os.makedirs(directorio, exist_ok=True)
print(f" [+] Directorio creado: {os.path.abspath(directorio)}")
datos_acumulados = {}
# 2. Comprobar si el archivo JSON existe para cargar su contenido previo
if os.path.exists(ruta_salida):
try:
with open(ruta_salida, mode="r", encoding="utf-8") as archivo:
contenido = archivo.read().strip()
if contenido:
datos_acumulados = json.loads(contenido)
print(f" [+] Archivo existente detectado. Se añadirán los nuevos registros a los {len(datos_acumulados)} existentes.")
except Exception as e:
print(f" [!] Advertencia: No se pudo leer el archivo existente ({e}). Se iniciará un JSON nuevo.")
datos_acumulados = {}
else:
print(" [+] El archivo no existe. Se creará un nuevo archivo JSON.")
# 3. Anexar/actualizar los registros nuevos sobre el objeto cargado
datos_acumulados.update(nuevos_expedientes)
# 4. Sobrescribir el archivo con el diccionario actualizado en formato JSON legible
with open(ruta_salida, mode="w", encoding="utf-8") as archivo:
json.dump(datos_acumulados, archivo, ensure_ascii=False, indent=4)
return os.path.abspath(ruta_salida)
def main():
expedientes_sesion = {}
contador = 1
print("----------------------------------------------------------------")
print(" SISTEMA DE REGISTRO DE EXPEDIENTES ACADÉMICOS")
print("----------------------------------------------------------------")
# Bucle de captura continua de fichas
while True:
codigo, ficha = capturar_ficha(contador)
expedientes_sesion[codigo] = ficha
contador += 1
if not solicitar_booleano("\n¿Desea incorporar la ficha de otro alumno?"):
break
# Guardado de datos en JSON
if expedientes_sesion and solicitar_booleano("\n¿Desea guardar los expedientes recopilados en un archivo JSON local?"):
print("\n=========================================================================")
print(" INSTRUCCIONES PARA LA RUTA DEL ARCHIVO JSON:")
print(" 1. NO use comillas al principio ni al final (ejemplo: NO use \"C:\\datos\\alumnos.json\")")
print(" 2. Puede usar barras inclinadas normales ( / ) o barras invertidas ( \\ ).")
print(" 3. Asegúrese de incluir la extensión .json al final del nombre.")
print(" 4. Ejemplos de rutas válidas:")
print(" - Windows (ruta absoluta): C:/MisDatos/expedientes.json ó C:\\MisDatos\\expedientes.json")
print(" - Carpeta actual (ruta relativa): ./expedientes.json ó expedientes.json")
print("=========================================================================\n")
ruta_archivo = input("Introduzca la ruta y nombre del archivo JSON: ").strip()
try:
ruta_final = guardar_en_json(expedientes_sesion, ruta_archivo)
print(f"\n[OK] Registros guardados con éxito en:\n {ruta_final}")
except Exception as e:
print(f"\n[ERROR] No se pudo guardar el archivo JSON: {e}")
print("\nCierre de sesión del sistema.")
if __name__ == "__main__":
main()
Aunque el script resultante sea mejorable, ya resulta suficientemente complejo (además de funcional) como para que nos hagamos una idea de qué estamos planteando cuando implementamos un diccionario de este nivel de complejidad. Lo que hemos pretendido es ejemplificar lo que sería la primera y básica implementación de una utilidad de este tipo: obtener del usuario los datos básicos (y simples, se podría añadir) que componen la estructura del diccionario y archivar esa información partiendo desde cero y en formato JSON. Cualquier proyecto de este tipo debe contener estas funcionalidades y ajustarse a la estructura del JSON deseado.
Pero si el contexto primero es correcto (y realista), el que planteo a continuación también lo es. Dando por supuesto el exito del anterior, creamos ahora una utilidad de acceso a los datos. Partimos de que ya tenemos algunos creados y deseamos acceder a ellos, entendiendo que ya hemos recopilado cierto volumen de información, disponible como archivo JSON. Evidentemente los datos que incluímos son inventados (sintéticos, que se dice) y no nos vamos a detener en valorar su ajuste a la realidad ni su interés pedagógico real. Para que lo que aquí te presento te sea de ayuda, proporcionaré acceso al documento "inventado" al final de esta entrada.
Dicho lo anterior, lo que ahora necesitamos es un script que nos permita acceder al documento JSON para trabajar con él en memoria como diccionario.
import json
# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"
# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
expedientes = json.load(archivo)
# 2. Obtener la clave y los datos del primer registro
primer_id = list(expedientes.keys())[0]
primer_registro = expedientes[primer_id]
# 3. Mostrar el resultado por pantalla (CMD)
print(f"--- PRIMER REGISTRO: {primer_id} ---")
print(json.dumps(primer_registro, ensure_ascii=False, indent=4))
Dado que estamos trabajando con archivos JSON, lo primero que haremos será importar la biblioteca correspondiente import json para cargar en memoria el archivo expedientes = json.load(archivo) desde su ubicación local ruta_archivo y accedemos a él with open() as archivo. Después obtenemos las claves de todos los archivos en formato lista list(expedientes.keys()) y el contenido del primer registro primer_registro = expedientes[primer_id] y lo mostramos por CMD print(json.dumps(primer_registro, ensure_ascii=False, indent=4)).
Mediante este par de script abordamos las dos maniobras básicas que deberemos realizar con diccionarios, simples o complejos. Pero como hay tarea para rato, parece conveniente finalizar ahora esta entrada.
Archivo de datos (formato JSON). Acceso para descarga desde este enlace. Deberás ajustar la dirección ruta_archivo a la que tú elijas como directorio del archivo que has descargado.
No hay comentarios:
Publicar un comentario
Comenta esta entrada