{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "845cae40",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Ayudantía 5 🤓\n",
    "\n",
    "### Serialización, manejo de bytes y excepciones"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69162a03",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "## Ayudantes  👾"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2977da41",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "- [Clemente Campos](https://github.com/mskdancers)\n",
    "- [Patricio Hinostroza](https://github.com/Dvckhv)\n",
    "- [Julio Huerta](https://github.com/julius)\n",
    "- [Carlos Olguin](https://github.com/CarlangaUC)\n",
    "- [Catalina Miranda](https://github.com/catalinamirandah)\n",
    "- [Felipe Vidal](https://github.com/fvidalf)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "edb0f151",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 📖 Contenidos 📖\n",
    "\n",
    "#### En este ayudantía usaremos:\n",
    "- Serialización\n",
    "- Manejo de bytes\n",
    "- Excepciones"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87c011bc",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Serialización\n",
    "\n",
    "Es el procedimiento de transformar cualquier objeto en una secuencia o serie de bytes. Esto nos permite almacenar el estado de un objeto de forma persistente, por ejemplo en un archivo o una base de datos que podamos consultar más tarde. También nos permite enviar el objeto a otros computadores y programas."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5571ebef",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Pickle 🥒 y JSON 👻\n",
    "\n",
    "#### Métodos:\n",
    "- `dumps`: serializa un objeto, es decir, lo guarda.\n",
    "- `loads`: deserializa un objeto serializado, es decir, carga un objeto a su estado original.\n",
    "- `dump`: guarda un archivo con el objeto serializado.\n",
    "- `load`: deserializa un objeto almacenado en un archivo (lo \"trae de vuelta\")."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08c2f3fe",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Veamos un ejemplo utilizando pickle:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "733fefd8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Soy una <class 'list'> y contengo ['asado', 'terremoto', 18, True]\n",
      "\n",
      "Soy una <class 'bytes'> y contengo b'\\x80\\x04\\x95\\x1c\\x00\\x00\\x00\\x00\\x00\\x00\\x00]\\x94(\\x8c\\x05asado\\x94\\x8c\\tterremoto\\x94K\\x12\\x88e.'\n",
      "\n",
      "Soy una <class 'list'> y contengo ['asado', 'terremoto', 18, True]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import pickle\n",
    "lista = [\"asado\", \"terremoto\", 18, True]\n",
    "print(f\"Soy una {type(lista)} y contengo {lista}\\n\")\n",
    "\n",
    "\n",
    "lista_serializada = pickle.dumps(lista)\n",
    "print(f\"Soy una {type(lista_serializada)} y contengo {lista_serializada}\\n\")\n",
    "\n",
    "lista_deserializada = pickle.loads(lista_serializada)\n",
    "print(f\"Soy una {type(lista_deserializada)} y contengo {lista_deserializada}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27e85ba3",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Veamos un ejemplo utilizando json:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3d38913e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "datos en formato JSON: <class 'str'> {\"nombre\": \"Parque O'higgins\", \"n_curados\": 100000, \"nombres_detenidos\": [\"Felipe\", \"Catalina\"]}\n",
      "datos en formato Python: <class 'dict'> {'nombre': \"Parque O'higgins\", 'n_curados': 100000, 'nombres_detenidos': ['Felipe', 'Catalina']}\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "class Fonda:\n",
    "    def __init__(self, nombre, n_curados, nombres_detenidos):\n",
    "        self.nombre = nombre\n",
    "        self.n_curados = n_curados\n",
    "        self.nombres_detenidos = nombres_detenidos\n",
    "\n",
    "            \n",
    "fonda = Fonda(\"Parque O'higgins\", 100000, [\"Felipe\", \"Catalina\"])\n",
    "\n",
    "json_string = json.dumps(fonda.__dict__)\n",
    "print(\"datos en formato JSON:\", type(json_string), json_string)\n",
    "json_deserializado = json.loads(json_string)\n",
    "print(\"datos en formato Python:\", type(json_deserializado), json_deserializado)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32c7acba",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Pickle v/s JSON\n",
    "\n",
    "<img width=500 src=\"img/diferencias.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5006f982",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Manejo de bytes 🤖"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e9573dc",
   "metadata": {},
   "source": [
    "### Bits y Bytes\n",
    "\n",
    "Un byte es la estructura básica para guardar datos en computación. A su vez, un byte está compuesto por 8 bits, y cada bit es un número que puede ser 1 o 0. Usamos esta estructura (byte) para medir el tamaño de los archivos.\n",
    "\n",
    "<img width=600 src=\"img/bit_bytes.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1caa778b",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Codificación UTF-8\n",
    "\n",
    "Podríamos decir que el Byte 0 corresponde a la letra \"a\", el Byte 1 corresponde a \"b\", el Byte 2 corresponde a \"c\", etc, hasta cubrir todos los caracteres que queremos representar. Esa asociación se conoce como codificación o encoding. \n",
    "\n",
    "Una codificación muy común es la codificación UTF-8, la cual nace como una ampliación de la codificación ASCII dada la necesidad de agregar caracteres que no se encontraban presentes en un teclado tradicional estadounidense."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afd955f5",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "La codificación ASCII asocia números (Bytes) con caracteres de la siguiente manera:\n",
    "\n",
    "<img width=400 src=\"img/ascii.jpg\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4aa7e196",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Objeto Byte\n",
    "\n",
    "Un objeto de tipo bytes es una secuencia inmutable, tal como los strings. Para declarar que un objeto es un byte simplemente se pone al comienzo del objeto una \"b\". Por ejemplo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2836e167",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'Guat\\xc3\\xb3n Loyala'\n",
      "<class 'bytes'>\n",
      "Guatón Loyala\n"
     ]
    }
   ],
   "source": [
    "nombre = b\"\\x47\\x75\\x61\\x74\\xc3\\xb3\\x6e\\x20\\x4c\\x6f\\x79\\x61\\x6c\\x61\"\n",
    "\n",
    "print(nombre)\n",
    "print(type(nombre))\n",
    "print(nombre.decode())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca4f9063",
   "metadata": {},
   "source": [
    "*Nota: el binario b\"\\xc3\\xb3\" corresponde a la letra \"ó\" en UTF-8*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50b25712",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Codificar y decodificar\n",
    "\n",
    "En python existen las funciones `encode` para convertir los caracteres a su versión en bytes y `decode` para decodificar estos. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a0f3e359",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'Guat\\xc3\\xb3n Loyola'\n",
      "Guatón Loyola\n"
     ]
    }
   ],
   "source": [
    "nombre = \"Guatón Loyola\"\n",
    "nombre_codificado = nombre.encode()\n",
    "nombre_decodificado = nombre_codificado.decode()\n",
    "\n",
    "print(nombre_codificado)\n",
    "print(nombre_decodificado)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a71b8df",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### bytes() y bytearray()\n",
    "\n",
    "Podemos usar otros métodos para definir la estructura de los bytes: bytes() y bytearray(), y el método que usemos va a depender de la funcionalidad que necesitemos.\n",
    "\n",
    "- ¿Quieres usar una estructura inmutable, similar a un string? Usa bytes()\n",
    "- ¿Quieres usar una estructura mutable, similar a una lista? Usa bytearray()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "283c1abd",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "#### Ejemplo 💃\n",
    "\n",
    "Queremos que nuestro programa convierta en bytes la letra de la famosa canción \"Yo tomo vino y cerveza\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "dd887097",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'Yo tomo vino y cerveza para olvidarme de ella'\n"
     ]
    }
   ],
   "source": [
    "letra = \"Yo tomo vino y cerveza para olvidarme de ella\"\n",
    "\n",
    "# Utilizamos bytes() para pasar nuestra letra a bytes\n",
    "# Esta función recibe 2 argumentos, el string a convertir y el encoding\n",
    "# Utilizamos encoding \"utf-8\", ya que, es el que más nos conviene\n",
    "byte_letra = bytes(letra, 'utf-8')\n",
    "print(byte_letra)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fc792f5",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Pero a nuestra letra le falta algo importante 🤔, olvidamos que entre medio se debe gritar \"PISCO Y RON\" en la canción."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "0ce76f8f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bytearray(b'Yo tomo vino y cerveza PISCO Y RON para olvidarme de ella')\n"
     ]
    }
   ],
   "source": [
    "# Trabajamos con bytearray() para que nuestra estructura sea mutable\n",
    "arr_letra = bytearray(byte_letra)\n",
    "letra_faltante = \" PISCO Y RON \".encode()\n",
    "\n",
    "arr_letra[22:23] = letra_faltante\n",
    "print(arr_letra)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee995e12",
   "metadata": {},
   "source": [
    "*Nota: si hubiesemos usado `arr_letra[22]` nuestro código no funcionaría.*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6130de84",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Endianess\n",
    "\n",
    "- big-endian: En este formato, el byte más significativo se almacena en primer lugar y los demás bytes le siguen en orden de significado descendente.\n",
    "\n",
    "- little-endian: Aquí sucede al revés, en este formato el byte menos significativo se almacena en primer lugar y los demás bytes le siguen en orden de significado ascendente."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81bcfa9d",
   "metadata": {},
   "source": [
    "<img width=500 src=\"img/endianess.jpg\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a172a5a",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### from_bytes()\n",
    "\n",
    "La función `from_bytes()` nos permite convertir la información en bytes a un número entero. Esta función recibe dos parámetros, los bytes a convertir y el orden en el que vienen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4447c8a3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "273\n"
     ]
    }
   ],
   "source": [
    "print(int.from_bytes(b'\\x01\\x11', byteorder='big')) # 0111 = 273"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ecfd3a4f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4353\n"
     ]
    }
   ],
   "source": [
    "print(int.from_bytes(b'\\x01\\x11', byteorder='little')) # 0111 = 4353"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3c1cc89",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### to_bytes()\n",
    "\n",
    "La función `to_bytes()` nos permite convertir un número en bytes. Al igual que `from_bytes()`, esta función recibe dos parámetros, pero son el primero es diferente. Ahora necesitamos la cantidad de bytes que usaremos para escribir el número convertido y el orden a usar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "205969b9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'\\x00\\x00\\x08\\xb9'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "numero_a_convertir = 2233\n",
    "numero_a_convertir.to_bytes(4, byteorder=\"big\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "062366ee",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'\\xb9\\x08\\x00\\x00'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "numero_a_convertir = 2233\n",
    "numero_a_convertir.to_bytes(4, byteorder=\"little\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1496b896",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Excepciones 🤔\n",
    "\n",
    "Son situaciones anómalas o inesperadas que pueden ocurrir en un proceso de cómputo. Estos eventos surgen cuando ocurren condiciones que alteran el flujo normal o esperado de un programa, o alguna acción no pudo ser ejecutada tal como se esperaba. A las excepciones uno les suele llamar comúnmente como \"errores\"."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6e1856a",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Levantando excepciones\n",
    "\n",
    "Se puede generar una excepción en el momento que queramos creando una nueva instancia de la excepción, y utilizando la sentencia raise. La forma de hacerlo es la siguiente:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2915884d",
   "metadata": {},
   "outputs": [],
   "source": [
    "raise NombreExcepcion('Mensaje de error')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd2cf767",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "A continuación se muestra un ejemplo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "01bcd97b",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Ambos argumentos deben ser tipo int",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[11], line 10\u001b[0m\n\u001b[0;32m      6\u001b[0m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAmbos argumentos deben ser tipo int\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m      8\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m x \u001b[38;5;241m+\u001b[39m y\n\u001b[1;32m---> 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43msuma\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mHola\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m)\n",
      "Cell \u001b[1;32mIn[11], line 6\u001b[0m, in \u001b[0;36msuma\u001b[1;34m(x, y)\u001b[0m\n\u001b[0;32m      4\u001b[0m check \u001b[38;5;241m=\u001b[39m \u001b[38;5;28misinstance\u001b[39m(x, \u001b[38;5;28mint\u001b[39m) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(y, \u001b[38;5;28mint\u001b[39m)\n\u001b[0;32m      5\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m check:\n\u001b[1;32m----> 6\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAmbos argumentos deben ser tipo int\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m      8\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m x \u001b[38;5;241m+\u001b[39m y\n",
      "\u001b[1;31mTypeError\u001b[0m: Ambos argumentos deben ser tipo int"
     ]
    }
   ],
   "source": [
    "def suma(x, y):\n",
    "    \n",
    "    # Si el input no es del tipo esperado\n",
    "    check = isinstance(x, int) and isinstance(y, int)\n",
    "    if not check:\n",
    "        raise TypeError('Ambos argumentos deben ser tipo int')\n",
    "\n",
    "    return x + y\n",
    "\n",
    "print(suma(\"Hola\", 3))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "957d7cbd",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Manejo de excepciones\n",
    "\n",
    "Cada vez que se levanta una excepción, es posible atraparla mediante el uso de las sentencias `try` y `except`.\n",
    "\n",
    "Funcionamiento: si se levanta una excepción dentro del scope de `try`, entonces la excepción es capturada, y debe seguir una o más instrucciones `except`. Si no ocurre ningún problema, el programa sigue su flujo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "840736e6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "El número elegido es 0\n",
      "Error: division by zero -> no se puede dividir por cero\n",
      "El programa continúa después del try/except\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    x = 0\n",
    "    print(f\"El número elegido es {x}\")\n",
    "    print(f\"Resultado operación: {1/x}\")\n",
    "\n",
    "except (ZeroDivisionError) as error:\n",
    "    print(f\"Error: {error} -> no se puede dividir por cero\")\n",
    "\n",
    "print(\"El programa continúa después del try/except\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d10ec21",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "También existen las sentencias complementarias `else` y `finally`:\n",
    "\n",
    "- `else`: instrucciones se ejecutarán siempre y cuando no se haya lanzado ninguna excepción.\n",
    "- `finally`: instrucciones se realizan siempre, independientemente de si ocurrió una excepción o no."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "d1c724af",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "El número elegido es 1\n",
      "Resultado operación: 1.0\n",
      "¡Todo OK! La división se hizo correctamente\n",
      "Recuerde SIEMPRE usar excepciones para manejar los errores de su programa\n",
      "El programa continúa después del try/except\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    x = 1\n",
    "    print(f\"El número elegido es {x}\")\n",
    "    print(f\"Resultado operación: {1/x}\")\n",
    "\n",
    "except (ZeroDivisionError) as error:\n",
    "    print(f\"Error: {error}, no se puede dividir por cero\")\n",
    "\n",
    "else:\n",
    "    # Si no hay errores, se ejecuta este bloque\n",
    "    # Si se colocara un return después de la operación y esta es correcta, \n",
    "    # entonces nunca se ejecutará este punto.\n",
    "    print(\"¡Todo OK! La división se hizo correctamente\")\n",
    "\n",
    "finally:\n",
    "    # Este bloque siempre se ejecuta\n",
    "    print(\"Recuerde SIEMPRE usar excepciones para manejar los errores de su programa\")\n",
    "\n",
    "print(\"El programa continúa después del try/except\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "33fd0425",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Música DCCiochera!\n",
    "\n",
    "El 18 llegó con todo, y ¡estamos organizando nuestra propia fonda! Queremos celebrar en grande, por lo que nos contactamos con todo tipo de artistas de la región para que vengan a actuar en nuestro evento. El único problema, es que el formulario que les dimos para anotarse en el concierto llegó con información poco confiable, por lo que no sabemos cuanta gente debe estar en el escenario para cada acto, ni por cuanto tiempo. ¡Ni siquiera en que orden deben salir 😨!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df14a87f",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Por fortuna, existe un archivo JSON que contiene la información de cada acto, el cual además  contiene una llave, que en sí trae de forma encriptada una confirmación de los datos relacionados al número de miembros y al largo del acto. La primera parte de tu trabajo será seguir los pasos para extraer correctamente la información, y avisar de posibles errores."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e692dba2",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Por otro lado, para determinar el orden en que saldrán los actos, deberás rankear los actos de acuerdo a su nivel de interés, el cual depende del largo del acto, y del interés por el género. Para eso, contarás con un segundo archivo JSON. Pero OJO, en ese archivo NO están todos los géneros! Por lo que deberemos manejar esos casos de forma distinta."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0920cb6",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### La encriptación\n",
    "\n",
    "La llave es un string de 6 caracteres, los cuales deberás convertir en un bytearray para poder trabajar con ellos. Desde el segundo byte, la llave debe ser separada en dos partes, desde el segundo al tercer byte, y desde el cuarto al sexto byte. Estas subsecuencias, deberán ser interpretadas como números enteros, pero siguiendo el criterio de *endianness* indicado por el primer byte. Si el primer byte representa un número par, será en *big-endian*. Si es impar, en *little-endian*.\n",
    "\n",
    "Luego, en base a esos números deberás verificar si los datos son correctos o no (el criterio en específico se detalla más adelante). Si la información no calza, por precaución, el acto no será invitado al concierto 😔."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c38332be",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Instrucciones"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccbfe35f",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "### Primera parte: Lectura de JSON\n",
    "\n",
    "Lo primero que deberás hacer es leer correctamente los archivos JSON que se ubican en la carpeta `data`. `actos.json` corresponderá a una lista de diccionarios, cada uno con la información de un acto, mientras que `generos.json` es un único diccionario con la relación entre género y su factor de interés."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2e3ea97",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Segunda parte: Clase Acto\n",
    "\n",
    "Aquí deberás completar los siguiente métodos:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6dd66eda",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "`serializar_llave()`: Deberás transformar el atributo `self.llave`, recibido originalmente como un string, a un *bytearray* para su manipulación más adelante con encoding \"UTF-8\".\n",
    "\n",
    "`deserializar_llave()`: Deberás transformar una llave en formato bytearray de vuelta a un *str* en formato \"UTF-8\".\n",
    "\n",
    "`determinar_big_o_little()`: Este método determinará la lectura del resto de los bytes de la llave, utilizando el primer byte de esta.\n",
    "- Si el byte corresponde a un número par, se retornará `True`, indicando que los bytes se leerán en formato *big-endian*.\n",
    "- En caso contrario, se retornará `False`, indicando que los bytes se leerán en formato *little-endian*."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "369bc92b",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "`verificar_datos()`: Este método realiza la verificación de los valores de `self.miembros` y `self.largo_acto`. Para esto, se deben seguir los siguientes pasos:\n",
    "\n",
    "- Primero, se debe serializar la llave usando el método `serializar_llave`\n",
    "- Se debe dividir la secuencia de bytes restantes (luego de extraído el primer byte) en las dos partes correspondientes.\n",
    "- Cada una de estas partes debe ser interpretada como un *int*, siguiendo el formato de *endianness* correcto.\n",
    "- Para validar `self.miembros`, su valor debe corresponder a la división módulo 10 del valor absoluto de la resta entre los enteros obtenidos.\n",
    "- Para validar `self.largo_acto`, su valor debe corresponder a la división módulo 100 del valor absoluto de la suma entre los enteros obtenidos.\n",
    "\n",
    "En caso de que alguno de los valores no sea correcto, se deberá **levantar una excepción de tipo ValueError**, especificando como mensaje de error la(s) causas del error. En caso de que todo esté en orden, no es necesario retornar nada.\n",
    "\n",
    "Finalmente, antes de terminar el método, deberás deseralizar la llave (convertirla nuevamente en un *str*) con el método correspondiente."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ba1f3db",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "`calcular_interes(interes_por_genero)`: Este método deberá calcular el interés por el acto en función de la duración de su show, y un factor de interés determinado por su género, el cual estará disponible mediante el diccionario recibido `interes_por_genero`, en el cual las llaves corresponden a un género en *str*, y los valores son de tipo *float*. Se deberá intentar asignar al atributo `self.interes` la multiplicación entre ambas cantidades, a excepción del caso en el que no se encuentre el género como llave del diccionario, en cuyo caso, se deberá **manejar la excepción de forma acorde** y asignar el valor como 0."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "347e647f",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Tercera parte: Clase Concierto\n",
    "\n",
    "Aquí deberás completar los siguientes métodos:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59b80bc5",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "`validar_actos()`: Este método debe recorrer todos los actos almacenados en `self.actos`, donde para cada uno debe ejecutar su método `verificar_datos`. \n",
    "- En caso de que se levante una excepción del tipo `ValueError`, se deberá manejar el error, imprimiendo un mensaje avisando de su eliminación del concierto, seguido de los motivos adjuntos en el mensaje de error. Finalmente, se deberá eliminar el acto del festival con el método correspondiente.\n",
    "\n",
    "- En caso que no ocurrá una excepción, se deberá imprimir un mensaje avisando que el acto fue validado correctamente."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "253d7d49",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "\n",
    "`planificar_conciertos()`: Este método debe rankear los actos de acuerdo a su nivel de interés, para producir un itinerario. \n",
    "\n",
    "- Para cada acto del concierto, se deberá calcular su interés con el método `calcular_interes`. Luego, se ordenerá la lista de acuerdo al atributo `acto.interes`. \n",
    "\n",
    "- Se deberá generar un archivo serializado en JSON que contenga cada uno de los actos. *Hint: Recordar el método `__dict__` de un objeto, y que un JSON puede serializarse como una lista de dicts*.\n",
    "\n",
    "- Finalmente, se deberá imprimir en orden cada uno de los actos, junto a su género, y duración de presentación."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ae4178d",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Cuarta parte: Flujo de Programa"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b6eebf9",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Debes instanciar un `Concierto`, y por cada elemento de la lista proveniente de `actos.json`, instanciar un `Acto` y agregarlo a la instancia de Concierto con el método correspondiente.\n",
    "\n",
    "Finalmente, deberás ejecutar los métodos `validar_actos` y `planificar_concierto` de tu instancia de `concierto`. "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
