{ "cells": [ { "cell_type": "markdown", "id": "title", "metadata": { "id": "title" }, "source": [ "# Buenas prácticas en Google Colab para compartir con equipos no técnicos" ] }, { "cell_type": "markdown", "id": "intro", "metadata": { "id": "intro" }, "source": [ "En equipos de trabajo donde conviven perfiles técnicos y no técnicos,\n", "es frecuente que el equipo técnico desarrolle *notebooks* en Google\n", "Colab para procesos periódicos: reportes mensuales, análisis de datos,\n", "tareas recurrentes. El problema surge cuando estos procesos requieren\n", "ejecución periódica con pequeñas variaciones —un mes diferente, otro\n", "departamento, un nuevo archivo de entrada— y la responsabilidad de\n", "ejecutarlos recae siempre sobre el equipo técnico.\n", "\n", "Esta dinámica genera una sobrecarga innecesaria: el equipo técnico\n", "se convierte en un cuello de botella para tareas que, con la\n", "estructura adecuada, cualquier persona podría ejecutar de forma\n", "autónoma.\n", "\n", "La solución consiste en estructurar los *notebooks* de manera que\n", "los equipos no técnicos puedan ejecutarlos por su cuenta, sin riesgo\n", "de alterar la lógica y sin necesidad de entender el código. Este\n", "*notebook* presenta las buenas prácticas que he aplicado en mi\n", "experiencia y, además, sirve como plantilla descargable que puedes\n", "adaptar a tus propios procesos." ] }, { "cell_type": "markdown", "id": "structure-overview", "metadata": { "id": "structure-overview" }, "source": [ "## Estructura recomendada\n", "\n", "La estructura que mejor me ha funcionado organiza el *notebook* en\n", "cinco bloques bien definidos:\n", "\n", "1. **Documentación**: celdas *Markdown* que explican qué hace el\n", " proceso, qué datos necesita, qué resultados genera y cómo\n", " utilizarlo.\n", "2. **Importaciones e inicialización de Drive**: primera celda de\n", " código, siempre visible. Solicita los permisos de Google Drive\n", " lo antes posible para que el usuario los conceda de inmediato y\n", " el resto se ejecute sin interrupciones.\n", "3. **Parámetros editables**: celda de código visible con campos de\n", " formulario de Colab. Son los únicos valores que el equipo no\n", " técnico debe modificar.\n", "4. **Variables técnicas y lógica de procesamiento**: celdas de\n", " código ocultas para evitar confusión. Contienen configuraciones\n", " internas y funciones.\n", "5. **Ejecución principal**: celda de código visible que ejecuta\n", " todo el proceso y muestra mensajes de progreso y resultados." ] }, { "cell_type": "markdown", "id": "documentation-section", "metadata": { "id": "documentation-section" }, "source": [ "## Documentación del proceso\n", "\n", "Todo *notebook* que se comparta debe comenzar con documentación\n", "clara. Es la primera impresión que tendrá el usuario y determina\n", "si podrá usarlo sin ayuda. La documentación debe incluir: qué\n", "hace el proceso, qué datos de entrada necesita (archivos y\n", "ubicación), qué datos de salida genera (archivos y ubicación) y\n", "las instrucciones paso a paso para ejecutarlo.\n", "\n", "A continuación se presenta un ejemplo de documentación para un\n", "proceso de generación de reportes." ] }, { "cell_type": "markdown", "id": "example-documentation", "metadata": { "id": "example-documentation" }, "source": [ "> **Proceso**: Reporte mensual por departamento.\n", ">\n", "> **Objetivo**: Generar un resumen mensual a partir de los datos\n", "> de operaciones diarias de un departamento.\n", ">\n", "> **Datos de entrada**: Archivo CSV en\n", "> `Mi unidad/reportes/datos/` con formato de nombre\n", "> `datos_AAAA_MM.csv` (ejemplo: `datos_2026_02.csv`).\n", ">\n", "> **Datos de salida**: Reporte CSV en\n", "> `Mi unidad/reportes/resultados/` con formato\n", "> `reporte_DEPARTAMENTO_AAAA_MM.csv` (ejemplo:\n", "> `reporte_ventas_2026_02.csv`).\n", ">\n", "> **Instrucciones**:\n", "> 1. Ejecutar la primera celda de código y autorizar el acceso\n", "> a Google Drive.\n", "> 2. Modificar los parámetros (año, mes y departamento) en la\n", "> celda de **Parámetros**.\n", "> 3. En el menú, seleccionar *Entorno de ejecución* >\n", "> *Ejecutar todas* (o `Ctrl+F9`).\n", "> 4. Esperar a que finalice. El progreso se mostrará en la\n", "> última celda." ] }, { "cell_type": "markdown", "id": "imports-section", "metadata": { "id": "imports-section" }, "source": [ "## Importaciones e inicialización de Drive\n", "\n", "Este bloque se ubica como primera celda de código por dos razones:\n", "\n", "1. Google Drive requiere autorización del usuario. Si esta celda\n", " es la primera, el usuario concede el permiso de inmediato y el\n", " resto del *notebook* se ejecuta sin interrupciones.\n", "2. Detección temprana de errores: si hay un problema con la\n", " conexión a Drive o con alguna dependencia, se detecta antes de\n", " ejecutar cualquier lógica." ] }, { "cell_type": "code", "execution_count": null, "id": "imports-code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "cellView": "form", "id": "imports-code", "outputId": "87dfde2d-4aa6-4e71-b0ed-6556f56a39c9" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Mounted at /content/drive\n", "Drive conectado exitosamente.\n" ] } ], "source": [ "# @title Texto de título predeterminado\n", "import pandas as pd\n", "from datetime import datetime\n", "from pathlib import Path\n", "\n", "from google.colab import drive\n", "\n", "drive.mount(\"/content/drive\")\n", "print(\"Drive conectado exitosamente.\")" ] }, { "cell_type": "markdown", "id": "parameters-section", "metadata": { "id": "parameters-section" }, "source": [ "## Parámetros (equipo no técnico)\n", "\n", "Esta es la única celda que el equipo no técnico debe modificar.\n", "Los parámetros se presentan como campos de formulario de Colab\n", "usando la sintaxis `#@param`, que genera controles visuales\n", "(campos de texto, *dropdowns*, etc.) que facilitan la edición\n", "sin tocar código.\n", "\n", "Un consejo importante: usa listas cerradas\n", "(`#@param [\"opción1\", ...]`) en lugar de texto libre siempre que\n", "sea posible. Esto previene errores de escritura y garantiza\n", "valores válidos.\n", "\n", "![image.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABsIAAAE+CAYAAAA3cwWkAAAgAElEQVR4XuzdB5xU1d038L+IgBURREnEEkUxYokoWKKJLcZuLLHE3js2NBYUQazYwBIFE7tJLLHGhj1qwGCiYtRgi2hABREVgojknXN9Zt9ZWNhZd2Zn2P3ez/u8LjP3nvI99/l8nvP5zTlnge4/7PG/cBEgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBBoZgILCMKa2YjqDgECBAgQIECAAAECBAgQIECAAAECBAgQIECAQCYgCPMiECBAgAABAgQIECBAgAABAgQIECBAgAABAgQINEsBQVizHFadIkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQEIR5BwgQIECAAAECBAgQIECAAAECBAgQIECAAAECBJqlgCCsWQ6rThEgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECAjCvAMECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQLNUkAQ1iyHVacIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQEYd4BAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBZikgCGuWw6pTBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECgjDvAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQLMUEIQ1y2HVKQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAUGYd4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKBZCjQ4CGvTZqFot3C7aNu2bbRu3ToWXLBVs4TRKQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgfILfPPNrJg5c2Z89dVXMf2/02PGjK9LVmnRQVi7hdvG4osvHv/73//iiy++jK9zjUh/5/5f7sr+PxcBAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBBggs8G3OlPvPgq1aR/v2i8eCrRfMZVFf5EKxrxpQTt23FhWEdeiwZFbp5MmfxTczv2l0pQogQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgUJfAArlQrEOHDtmCrJRNNeaaZxDWqlWr6NhpqZg2dWpMnTo9V4+VX43B9iwBAgQIECBAgAABAgQIECBAgAABAgQIECBAgEAxAgvkjulaMJZov2RMmvhpzJo1q5iH5rhnnkHY0p07xeeffxFfTW/80rPv1DoPESBAgAABAgQIECBAgAABAgQIECBAgAABAgQItFiBBRdsHUt1XDI++XjidzKYaxCWtkOcMeOr3Eqw/36ngj1EgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAoLECbdu2jkUWWew7bZNYZxDWbuG2sdhii8XETz7Ntc12iI0dIM8TIECAAAECBAgQIECAAAECBAgQIECAAAECBAh8V4EFYqmlloxp/50W0//bsF0M6wzC0paIn346Ob6Z+c13bZHnCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECJREYIEFIjot3anBWyTOEYS1abNQ7uCxJXKrwSaVpGEKIUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQINBYgaWW6hBffvll7mivr4suao4gbIn2i8dXX82Ir6Y3bGlZ0TW6kQABAgQIECBAgAABAgQIECBAgAABAgQIECBAgEADBRZcsHUsutjC8fmUL4p+co4gLG2LOGnipzFr1qyiC3EjAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgbIK5LZHXLqB2yPOEYR1+d6yMWH8R/G///2vrG1VOAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGGCKQca/x/JhT9yBxB2HJdvxcfjBufK0AQVrSiGwkQIECAAAECBAgQIECAAAECBAgQIECAAAECBMossEAs17VLLsf6T9H1zCUIK76AomtyIwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFGCHy7oKv4HEsQ1ghsjxIgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECDSdgCCs6azVRIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0IQCgrAmxFYVAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBA0wnMN0HYQgstFOusvXasscYPo2PHjrFUhw6Z0qeTJ8ekSZPitdf+Gf94+eX4+uuvm05PTQQIECBAgAABAgQIECBAgAABAgQIECBAgAABAlUrUPVB2LLLLhP777tv/PjHG0e7du3mCTl9+vT4y1+eixtvvjkmTPioatFL1bBu3bpF27ZtYsyY10pVpHIIECBAgAABAgQIECBAgAABAgQIECBAgAABAs1GoGqDsCWXbB9777VXbL/dtpFWgzXk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jTdqdBCWyjn6qCNj++22zSaQhfW88eabce6g8x0i3WQjriICBAgQIECAAAECzUugVEFYUklbIB6Tm7uk8Kvwmjx5clx59TXxTG5rxPyV5lBn9TsjVlxhhVr3zpgxI+666+747Q03Ni9ovSFAgAABAgQIECBA4DsJVEUQllr+2+HDcmHQcvPsxOxB2PrrrRdHHHFYpCCtmOvtd96JI448uphbm/yeHj3WiN69euV+xTglRua2+xhXhrAuhW5r9ugR3buvFu/nVp+9/MqrZamnyfFUSIAAAQIECBAgQIBAsxLIz49Sp/7xj5dj9EvfbpNY15W2U1x7rTWjc+fO2XaKI0eNalYWOkOAAAECBAgQIECAQOMEqiYIS2dVpTOr5nXlg7AUfKUALAVhDbmuyv2C8J5772vII+4lQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBCYTwWqJghr27ZN3Pi730bHjt/u8V7X9ad77o0FFlig3nPA6nr2008/jX33PzDSNhkuAgQIECBAgAABAgQIECBAgAABAgQIECBAgACB5i9QNUFYot55px2zs6zKcVkNVg5VZRIgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEqlegqoKwVq1axaBzB8R6PXuWVOzZv/wlzh10fsyaNauk5SqMAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgegWqKghLTIsuumhccvGFsfLKK5dE7Y0334yT+54SX31lS8SSgCqEAAECBAgQIECAAAECBAgQIECAAAECBAgQIDCfCFRdEJbc2rRpEycc3ye23GLzRjHed/8Dce11w5wL1ihFDxMgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIE5k+BqgzC8pQbbbhhHHrIwbHcct9vkO4HH3yYBWB/HTmyQc+5mQABAgQIECBAgAABAgQIECBAgAABAgQIECBAoPkIVHUQlpgXXHDB2GqrLWOLzTeLNXv0yP5d1/XNN9/EmNdeixGPPxGPPTYi0r9dBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECLVeg6oOwwqFJ54et8cPVY5VVVomVVlop++rdd9+Nt956K1775+sxderUljuSek6AAAECBAgQIECAAAECBAgQIECAAAECBAgQIFBLYL4KwowdAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgWIFBGHFSrmPAAECBAgQIECAAAECBAgQIECAAAECBAgQIEBgvhIQhM1Xw6WxBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECxQoIwoqVch8BAgQIECBAgAABAgQIECBAgAABAgQIECBAgMB8JSAIq5LhWnvttWKVVVaKjp06VUmLNIMAAQIECBAgQIAAgflJYNLEifHWW+/Gyy+/UpFmm9NUhF2lBAgQIECAAAECBJqFQDnnM4KwCr8i7du3jy23/GlM/mxKvP7aGzF+woQKt0j1BAgQIECAAAECBAjMjwJdll02Vl+je3RYsn2MGPFUTJkypUm6YU7TJMwqIUCAAAECBAgQINCsBco5nxGEVfjV2XXXnWLs2LfjlVfGVLglqidAgAABAgQIECBAoDkIrLVWj+jWbeW46657m6Q75jRNwqwSAgQIECBAgAABAi1CoBzzGUFYBV+dtHVIx05LxROPP1XBVqiaAAECBAgQIECAAIHmJrD5Fj+NSRM/Lfs2ieY0ze3N0R8CBAgQIECAAAEClRco9XxGEFbBMU2/nHz+uZG2Q6zgGKiaAAECBAgQIECAQHMUSNuKbLRx77KvCjOnaY5vjz4RIECAAAECBAgQqKxAqeczgrAKjudhhx8c1117fQVboGoCBAgQIECAAAECBJqrQFPMN5qijuY6PvpFgAABAgQIECBAgMDcBUo51xCEVfBNK+VAVrAbqiZAgAABAgQIECBAoAoFmmK+0RR1VCGtJhEgQIAAAQIECBAgUGaBUs41BGFlHqx5FV/KgaxgN1RNgAABAgQIECBAgEAVCjTFfKMp6qhCWk0iQIAAAQIECBAgQKDMAqWcawjCyjxYgrAKAquaAAECBAgQIECAQAsWKOXEcW6MTVFHCx5CXSdAgAABAgQIECDQYgVKOdcQhFXwNSrlQFawG6omQIAAAQIECBAgQKAKBZpivtEUdVQhrSYRIECAAAECBAgQIFBmgVLONQRhZR6seRVfyoGsYDdUTYAAAQIECBAgQIBAFQo0xXyjKeqoQlpNIkCAAAECBAgQIECgzAKlnGsIwso8WIKwCgKrmgABAgQIECBAgEALFijlxHFujE1RRwseQl0nQIAAAQIECBAg0GIFSjnXEIRV8DUq5UBWsBuqJkCAAAECBAgQIECgCgWaYr7RFHVUIa0mESBAgAABAgQIECBQZoFSzjUEYWUerHkVX8qBrGA3VE2AAAECBAgQIECAQBUKNMV8oynqqEJaTSJAgAABAgQIECBAoMwCpZxrCMLKPFiCsAoCq5oAAQIECBAgQIBACxYo5cRxboxNUUcLHkJdJ0CAAAECBAgQINBiBUo51xCEVfA1KuVAVrAbqiZAgAABAgQIECBAoAoFmmK+0RR1VCGtJhEgQIAAAQIECBAgUGaBUs41BGFlHqx5FV/KgaxgN1RNgAABAgQIECBAgEAVCjTFfKMp6qhCWk0iQIAAAQIECBAgQKDMAqWcawjCyjxYgrAKAquaAAECBAgQIECAQAsWKOXEcW6MTVFHCx5CXSdAgAABAgQIECDQYgVKOdcQhFXwNSrVQHbt2jV691o/OnfuHB9//HGMHPVijBs3bp4967nuutG7d6/snpEjR8Xol16qV2LzzTaL7t1Xi6+++ipXx6gYM+a1ep9JN6T21deeogpyEwECBAgQIECAAAECRQuUar4xrwpLVcd3mZ8UDeFGAgQIECBAgAABAgTmO4FSzTVSxwVhFRz+UgzkkUccHjvusH20bt26piczZ86Mhx95NK4YMnSO3vXsuW6c2vfk6NChQ63vJk+eHFdefU0888yzczyz6aabxDFHHTnHM2PHjo2zzxkYn3zySZ2KKQAbeM7Z0arVgrHfAQdWUFrVBAgQIECAAAECBFqeQCnmG/WpNbaONdfsESf0OS778VzhNWHChLho8CXx6qtj6muC7wkQIECAAAECBAgQaIYCjZ1rFJIIwir4gjR2IA868IDYa889IgVfzzz7bEyZ8nl0XnrpbKVXCsZuuPGmuPW222t6mCaZ5507MNq1axdj33qrZkVXjx5rRLdVVonp06fH0CuvikcfG1HzzGqrrhqDL74we+afr78eb775r2jXtm1ssEHvLBhLE9MTT+47h2Kq6+x+/aJ9+yXijTfejAHnDpprYFbBIVA1AQIECBAgQIAAgWYr0Nj5RjEwja3j9ltviU6dOmY7SPxt9Le7VGy04QaxzDLLxPjxE/ygrphBcA8BAgQIECBAgACBZijQ2LlGIYkgrIIvSGMH8qYbfhdduiwbl15+RTz00MM1Pdlv331i331+Fe+9914ceviRNZ8PH3ZtrLD88nH3n+6Ja35zba2eH3boIbH7brvOMdk8Jbd6bKstt4gXXvhrnNX/nJpnls4Fbjf8dngWuB3X54R481//qvXdkMsvyya0Dzz45zpXplWQXdUECBAgQIAAAQIEWoRAY+cbxSA1po5f7b1XHLD/fvH22+/EEUcdXau6W26+MZbJbf0+9Kqr47777i+mKe4hQIAAAQIECBAgQKAZCTRmrjE7gyCsgi9GYwfysUcemuuvJNN3n+a2O9xjz72zHu644w5x7NFHxb/ffz8OOfTwOns97NprYsUVV6w12bz6yiHRrVu3OOPMfjHqxb/Vei7/3cW5LUsKV5HlP7/jzrviumHDKyisagIECBAgQIAAAQItV6Cx841i5BpTR/+z+sXGG280x04Wqd78d+YUxYyCewgQIECAAAECBAg0P4HGzDVm1xCEVfD9KOVAzt6N2YOwYiaS+ZVko3Nbkvz69DPqlakrCMuvLCu2jHorcQMBAgQIECBAgAABAt9JoJzzjXyDylVHMfOX74TiIQIECBAgQIAAAQIE5guBUs41BGEVHPJSDmRhN9K5XlcOvaLW6q/fXH1VrLzyD+pc2ZV/tq7n5sWT32rxmGP71GyNePedf4yFFlooDjrksOxMsHT+2Jgxr1VQWdUECBAgQIAAAQIEWqZAueYbhZrlquOC8wZFz57rZlu6p63dXQQIECBAgAABAgQItCyBUs41BGEVfHdKOZCF3ehz3LGx/XbbxmMjHo+LLh6cffWH398WS3XoEFttvc08ezz7SrK53bzmmj1i8EUXxkcffVxzgPUuv9g5jjzi8Hj6mWeiffv20WONNbIzxNI1adKkGDb8+nj8iScrKK5qAgQIECBAgAABAi1HoFzzjULBctSRziO+9pqrom3btrHdDju1nAHTUwIECBAgQIAAAQIEagRKOdcQhFXwxSrlQOa7kSaNvx1+XbRq1SpOPKlvzUqtYoOwdF+68meLzY3n0sEXRwrDCvfs73fG6bHpppvEF198EQsvvHCMHftWjJ8wPrp37x7f69IlZs2aFZcPGRoPPfRwBdVVTYAAAQIECBAgQKBlCJRjvjG7XDnqOKXvybHVllvEc889H/0HDGwZg6WXBAgQIECAAAECBAjUEijlXEMQVsGXq5QDme9G/tyuBx78c1yRC53yV7FBWDErwvLngP37/ffjkEMPr6kjX/enkyfHyX1PjXHjxtV8l3/mo48/jn323b+C6qomQIAAAQIECBAg0DIEyjHfKHcQts02P4/jcztc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BAgQIECAAAECBAgQIECAAAECBAgQIECAQIkEBGElglQMAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAdQkIwqprPLSGAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgRAKCsBJBKoYAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKC6BARh1TUeWkOAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFAiAUFYiSAVQ4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgUF0CgrDqGg+tIUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQKJGAIKxEkIohQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBCoLgFBWHWNh9YQIECAAAECBAgQIECAAAECBAgQIECAAAECBAiUSEAQViJIxRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECFSXgCCsusZDawgQIECAAAECBAgQIECAAAECBAgQIECAAAECBEokIAgrEaRiCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIEqktAEFZd46E1BAgQIECAAAECBAgQIECAAAECBAgQIECAAAECJRIQhJUIUjEECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQLVJSAIq67x0BoCBAgQIECAAAECBAgQIECAAAECBAgQIECAAIESCQjCSgSpGAIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgeoSEIRV13hoDQECBAgQIECAAAECBAgQIECAAAECBAgQIECAQIkEBGElglQMAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIBAdQkIwqprPLSGAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgRAKCsBJBKoYAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKC6BARh1TUeWkOAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFAiAUFYiSDLVUzPddeN1q1bx6eTJ8fYsWPLVY1yCRAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQLNTkAQVuVD+off3xZLdegQzz33fPQfMLDKW6t5BAgQIECAAAECBAgQIECAAAECBAgQIECAAIHqERCEVc9Y1NkSQViVD5DmESBAgAABAgQIECBAgAABAgQIECBAgAABAlUrIAir2qH5tmHNKQg77NBD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IECAAAECSUAQVsH3QBBWQXxVEyBAgAABAgQIEGjmAuYbzXyAdY8AAQIECBAgQIAAgaIEBGFFMZXnJhPT8rgqlQABAgQIECBAgAABK8K8AwQIECBAgAABAgQIEEgCgrAKvgeCsAriq5oAAQIECBAgQIBAMxcw32jmA6x7BAgQIECAAAECBAgUJSAIK4qpPDeZmJbHVakECBAgQIAAAQIECFgR5h0gQIAAAQIECBAgQIBAEhCEVfA9EIRVEF/VBAgQIECAAAECBJq5gPlGMx9g3SNAgAABAgQIECBAoCgBQVhRTOW5ycS0PK5KJUCAAAECBAgQIEDAijDvAAECBAgQIECAAAECBJKAIKyC74EgrIL4qiZAgAABAgQIECDQzAXMN5r5AOseAQIECBAgQIAAAQJFCQjCimIqz00mpuVxVSoBAgQIECBAgAABAlaEeQcIECBAgAABAgQIECCQBARhFXwPdt11p3j+uZExfsKECrZC1QQIECBAgAABAgQINDeBLssuGxtt3Dvuuuve5tY1/SFAgAABAgQIECBAgECDBARhDeIq7c1rr71WdOy0VDzx+FOlLVhpBAgQIECAAAECBAi0aIHNt/hpTJr4abz88ist2kHnCRAgQIAAAQIECBAgIAir8DuQVoWNHft2vPLKmAq3RPUECBAgQIAAAQIECDQHgbXW6hHduq1sNVhzGEx9IECAAAECBAgQIECg0QKCsEYTNq6A9u3bx5Zb/jQmfzYlXn/tDdskNo7T0wQIECBAgAABAgRarEDaDnH1NbpHhyXbx4gRT8WUKVNarIWOEyBAgAABAgQIECBAIC8gCKuSdyFtk7jKKivltkrsVCUt0gwCBAgQIECAAAECBOYngUkTJ8Zbb71rO8T5adC0lQABAgQIECBAgACBsgsIwspOrAICBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFKCAjCKqGuTgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgbILCMLKTqwCAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBSggIwiqhrk4CBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGyCwjCyk6sAgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgUoICMIqoa5OAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBsgsIwspOrAICBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFKCAjCKqGuTgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgbILCMLKTqwCAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBSggIwiqhrk4CBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGyCwjCyk6sAgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgUoICMIqoa5OAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBsgsIwspOrAICBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFKCAjCKqGuTgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgbILCMLKTqwCAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBSggIwiqhrk4CBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGyCwjCyk6sAgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgUoICMIqoa5OAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBsgsIwspOrAICBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFKCAjCKqGuTgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgbILCMLKTqwCAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBSggIwiqhrk4CBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGyCwjCyk6sAgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgUoICMIqoa5OAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBsgsIwspOrAICBAgQIECAAAECBAgQIECAAAECBAgQIECAAIFKCAjCKqGuTgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgbILCMLKTqwCAgQIECBAgAABAgQIECBAgAABAgQIECBAgACBSggIwiqhrk4CBAgQIECAAAECBAgQIECAAAECBAgQIECAAIGyCwjCyk6sAgIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgUoICMIqoa7OqhRYdPEO0WHp72dtm/zJhzH1i8kNaudyP+gxx7NdVugeq6+7eYwccXuDy2tQ5W4mQIAAAQIECBAgQIAAAQIECBAgQIAAAQIE5hAQhHkpCPyfwC+Pujh+2HOLmPblZ3HFqTvEV9OnNsim//UvZfc/8aer45kHhmd/n3bVs9G23aJZmRf12bxB5bmZAAECBAgQIECAAAECBAgQIECAAAECBAgQaJyAIKxxfo1+etW1N4m9j7uippwUwEye+OFcyz3liidikcWWzL5/5a9/jruHndnoNijgW4G8bWGQ1RCbuoKws4b9LVq1ahVfz/gqBh25YUOKcy8BAgQIECBAgAABAgQIECBAgAABAgQIECDQSAFBWCMBG/v47EHYi0/eEQ/ecn6dxa6z0Q6x88Hn1HzXlEHYptsfEpv/4qis7v4Hr9vYblfd86ut85PY+aD+MXHCv+P68w74Tu2rKwjruekuseYG28Srf30oRj9z93cq10MECBAgQIAAAQIECBAgQIAAAQIECBAgQIDAdxMQhH03t5I9NXsQ9unHH8SQ03ass/x9T7o6Vv7hBjXfCcJKNgwlKaiuIKwkBSuEAAECBAgQIECAAAECBAgQIECAAAECBAgQ+E4CgrDvxFa6h/JB2KxZs7It9NJ125A+8a+Xn61VSTpn6uTLRsRCbdpG/t65BWHp3lV6bBTLdO0WU7+YHG/+/an4bNL4uTa6U5cVo/uPNos2bReO98f+Pd4a88Ic9zZkRdiii3eIHr1/npUxcsTttcpapceGsXy3H2WfvT76iRj//hv1Yqb2rbXBtrm2/SPXtufrvT/d0GX57rF6z2/P5Jpbn/IFNdQrPbfGeltlvpM/+SD++bfHs/PEvksQlvdI5aQxT+NV31XMeNVXhu8JECBAgAABAgQIECBAgAABAgQIECBAgEBLEBCEVXiUC4Owjz98K5btumr8c/Tj8cer+9ZqWT6ISmdNTfro39l9dQVhO+x/ZvzoxzvXhGr5Qt57829x+5ATssAmfy39vR/EbodfEMsst0qtuqZP+yIeuPm8GDPqkezzfMAzO1W+/sJVbY/84ZLYavcTaurPb6OYtgjcavc+0W6RxWsVM+3LKXH/jQPi9ZeerPl8l0PPzYKvcW+9HDNm/LdmFVz69/XnHzjPEVup+/qRnl98yaXn2af8lw3xSs/06LV1bL/v6bX68c3Mr2PEXUNq+l14xtjBp/0uuq6ydtaXwravvu5msdOB/essZ8Of7RtLdOg8RyDaZYXu2XlydfXtsTuusPVihf93WfUECBAgQIAAAQIECBAgQIAAAQIECBAgUH0CgrAKj0lhiPSXP/8ufrztgTHty8/ioj7frmbKX4efdVukICSFZMv9YM0sKJk9CMsHSOmZFJjN/Hp6LNBqwWi38GJZMWlV2OWnbFdT5rHn/Sk6LrNCpCBn3NuvZJ93/v4qschi7bPnr+63W0ye+GGDgrAUtC3UZuH46r9fROuF2sWgIzeM1Mc9j7ksC8e++OyTXJD3flZX15XXigVbL5SFc785e8+srnTl+5E+T6u18n35+MN34ncXHjzXEUvB3iFn3Jg9k1bNpTakq+3Ci2d1p89+f+UJNavtGuqVwqvdj7y4pqzC8lM9yTGt2KsvCEvtPPKcP9aEhanf33z9dXRY+vvZeKUrtbdwZWBh39L3/506JbuvTdtFMsN0jbhzSPzloRvm6uMLAgQIECBAgAABAgQIECBAgAABAgQIECDQ0gQEYRUe8cIg7PyjN4lThz6dhSAP3XZRjHz891nrOnT6fvS58P7s77uuOz238uj4OYKwwnLSSq47rz2tpmcbb7N/bLHLsVm5zz9yUzz6x8vj+yv1iEPPvCm75w9XnVSzIiuFSEf0/31updJi8edbL4pXRz6U3TOvrREL6/588sdx86VHxSf/eaem/r2OuzxWW3vT+PDdMTHs3P1qPi8Md1588o548Jbzs+8KA6p/vfJs3HZFn6JGKR8WpgDthosOjfH//nbbxcJ68iuzGuqVyplb+Smg3OPoS2LJjl2y+uoLwvY/+dpYafX1swDw90NPjHffeDF7Lm0peWi/W2rKKQzC8ivLZu9beuaAU4ZlfUwB6hWn7lBr1V9RcG4iQIAAAQIECBAgQIAAAQIECBAgQIAAAQLNVEAQVuGBLQxk0jaC+ZDk3ddfjBsHH561brt9Tov1N9s9Pv34gxhy2o5x4uCH5wjC9jzm0tw5Xz+tuWf2bu3d54pYda1Nsm0Vh57+i1rh2rMPXh+P333VPCWKDcLS1ogvPHrrHGWlkOjrGdPnOANr35OuzrY+fPuff42bLzkqey4fhKWVYINP2LKoYKcwLKyrDb232DNW7L5ebovCV7IwsDFedZW/zkY7xM4Hn5O1v74g7IxrXshWjv11xG3x8O2Da1mtmTtbbdfDzss+ywdh9fUthWBHD7wze6YwQK3wq616AgQIECBAgAABAgQIECBAgAABAgQIECBQcQFBWIWHYPYgLB+oFIZAx51/XyzVebnsDKj7bzy3ziAsv83h7KvB8t378TYHxJa7HZf9M39u14GnXh8rrPqj7LP82WNp1dY/nrs/O9Oq8Co2CCtcxTQ77Y9+vGOssf7Pclsmts229vvw3deybR5TgFd4hlY+CEuryy49+edFjVAKurbZ+5Ts3rSyrvAstLoKaKhXMeXnA655BWErr7Fh7Hvit6Hj3KzOGva3WlsjFlP38Rc9mK0kq+vcuKIA3USAAAECBAgQIECAAAECBAgQIECAAAECBJqhgCCswoM6exCWmnPKFU/kzulaMtKZYe+/9Y/Y+7grsvOthp62U3aOVl0rwur6rLBrddWTtkH82R4nRFqFlM6aKrxSSHXT4CNqAqXGBGFpxdIBfa+LRZdYaq7ajQ3C5tW+uiptqFcx5efLnFcQVjgOcwvCTh36VCy8yBI1QVkxdee3ThSEVfh/oVVPgAABAgQIECBAgAABAgQIECBAgAABAlUlIAir8HDUFVDlV0SlM65S8PXDnltk511dO2DvrLV1hTjpXK9lu64a9a0IS4HagEPXm6PXaTVROjdsndyqrW5rbpx9n1+Blv5uTBCWP1tr6uef5s4duzCmfDo+Kz8FZJtsd0i22q2xQVjh1oTFrAhrqFcxq7LyK7nmFYSl8PG0q57N+n/P9WfHP57/9uy3/FW4DWI+KCumb/kVYam8VK6LAAECBAgQIECAAAECBAgQIECAAAECBAgQiBCEVfgtqCsIS9sVpm0L05W2+EvhSeG5VHUFYb886uIsMJvbdoL5s8fyZ4Tlg6/2uQAsnZlVeOXPEysMpxoThOW3DHz6vuviyXt/U6uufCDV2CCsMEAqDKLylW2zV9/4fm4bxn/+bUTW34Z61XdOV89Nd4kd9j8zq66+M8Ly45e2oRx27n61PPLnwaUPC88IO/b8e7PtEuvqW+H74oywCv8vtOoJECBAgAABAgQIECBAgAABAgQIECBAoKoEBGEVHo66grDUpHxAlP4uPC8s/buuIKwwDHnj70/Fn4b3q9nWcLOdjoif7HhY1tO03eKIu4ZG4QqnO689LVtJlr8OPfOmbHXYv//19/jdhQdnHxeuSkrnlKXVYmlF1yf/eSfq2+4vv9Vf4QqzVGZhuNbYICyVl98eMIWHvx96Yrz7xotZ21fqvn7seeylWaD4z9GPxx+v7pudjZYPG4vxSuXkV7al8u+45tR4a8zzWfldVugeB5wyLCs/XfUFYT/75fGx0dbfBmDJ+NkHh8f0aV9Er9w5Z2ttsG3NOBRunZgPMmeve9HFO8Sh/W7Jzgeb9uVncVGfzWue9wcBAgQIECBAgAABAgQIECBAgAABAgQIEGjpAoKwCr8BcwvCttz12PjxtgdmrUtBze+vPLGmpXM73yq/pWK68ZuZX8eMr6bFgq0Xqjn/qzBsSvfkt9NLf6ctGKd+Pjk6f/8HNfcXri5KIc/Jl42Ihdq0rWlH/jyq+oKwfU+6Olb+4QbZcxPG/SsLfRZbomN06rJi1s7UxlIEYSmYO+SMG2sCqf9OnZLVufCi7bP/phDpN2fvmfU1XQ31Sv3c85jLspVZ6fp/7d1LaFxVGAfwL5nYpDWPJm3VSq34rlrEIiqCCFpQF4IiKLroQnciLhREdKMI6lJQfCCIigtREURERRHfiCKKWPCFUp9RkybaSZpMmof3jExNmqaZSe84k/q7FBoy53zn3N/J7s93Znb9dOVkepfks1gQluZWQrt9//ySQ++6DdHZs2ZvR1gas++7jY+NxMz0VLSv7CrvJ63//CO3xpefvtXgv2jLEyBAgAABAgQIECBAgAABAgQIECBAgACB5hEQhDX4LBYKwlLwdNuD75RDjhceuyO++Oi1vTtdKAhLA7ZeeWO52yiFS5UnhSQ/fvtpPPPAzXu7xNJnqZto2y0Px1EbT5mjkDrQ3n7p0fjg1afm/D5d/3dpdsVgJQyrNghL73L97U/EkRtOnLOn7R//806pCyqPICzVSt1ZKazq6Ttyzt6Lfw6Ug6X0XWuzn1q80rzN51wSl227IzpWde0tk4K9l5++Ny6++ubo7j2iqiCsclZnnn95dg59URorlrvVXn/2/rjp3hfnBWGVd7vi+rvnOKbf7x75K97MuvxSx52HAAECBAgQIECAAAECBAgQIECAAAECBAgQ+FdAEHYI/jWk4OmYE8+IjSdtid9/+jZ2fP1JjBaHF3zTdK3ecaeeXe5E+ubzd+Pn77cfUGX9xk3lz/t/nBsqLUaZOsA2bbkw6zzbma3z3gH3tFitxT6vrJXGffXZWzHYv2PBKbV6pUIbjt+cXQl5Qdk3XZGYus3yeNJebn/ovXKp2Vcjzq6d3u2E08/LAsmO+G77hzWfQx77VIMAAQIECBAgQIAAAQIECBAgQIAAAQIECCwHAUHYcjglezykBC6/7s449aytMfDLd/H4ff9cf1l5Kt8flrry7rnhvEPqvb0MAQIECBAgQIAAAQIECBAgQIAAAQIECBD4rwUEYf+1uPX+9wKVsCtB/PDNZ/HG8/eXTTafe2mcc9E15esw0zWJzz186//eCgABAgQIECBAgAABAgQIECBAgAABAgQIEDgYAUHYweiZS2CJAtfd9ngce/KW/c4eHvglHr3rmtyuW1ziFk0jQIAAAQIECBAgQIAAAQIECBAgQIAAAQLLXkAQtuyP0AssV4GzL7wqzt16bRze3Vt+hfGxkdj+0Wvx/itPCsGW66HaNwECBAgQIECAAAECBAgQIECAAAECBAg0lYAgrKmOw2YIECBAgAABAgQIECBAgAABAgQIECBAgAABAgTyEhCE5SWpDgECBAgQIECAAAECBAgQIECAAAECBAgQIECAQFMJCMKa6jhshgABAgQIECCYUbDiAAAFd0lEQVRAgAABAgQIECBAgAABAgQIECBAIC8BQVhekuoQIECAAAECBAgQIECAAAECBAgQIECAAAECBAg0lYAgrKmOw2YIECBAgAABAgQIECBAgAABAgQIECBAgAABAgTyEhCE5SWpDgECBAgQIECAAAECBAgQIECAAAECBAgQIECAQFMJCMKa6jhshgABAgQIECBAgAABAgQIECBAgAABAgQIECBAIC8BQVhekuoQIECAAAECBAgQIECAAAECBAgQIECAAAECBAg0lYAgrKmOw2YIECBAgAABAgQIECBAgAABAgQIECBAgAABAgTyEsgpCOvP9jOT157UIUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIHCQAi2x4Zj18fNPv1Zdp2XTaZvnJF7rjz4qfuv/PWZmBGFVKxpIgAABAgQIECBAgAABAgQIECBAgAABAgQIECBQd4GUY/X/+lvV68wLwtYdsTZ2Dg7F9PR01UUMJECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIFBXgZaIdevWxsAfg1UvMy8I6+7pilJpIkrjpaqLGEiAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgngKFQlsc3rkydv1VrHqZeUHYihWHRXdPdwwO7Ky6iIEECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIE6inQ19cbIyMjMTGxp+pl5gVhaWa6HnFoaDimJqeqLmQgAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgXoItGTXIq6t8VrEtI/9BmEdK9ujs7Mz6wobyobM1GO/ahIgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBCoQqAl+vpWx+6x3TE+VttXe+03CEsr9vauzlrLSjE6OlbFBgwhQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgkL9Ae3tbrFrVGcPDf9ZcfMEgLFVKVyTu2lWM0nht6VrNuzCBAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAwD4ChUJb9K1ZHQN/DC7J5oBBWGtra6xZ2xe7R0ezzrDxbAHXJC5J2SQCBAgQIECAAAECBAgQIECAAAECBAgQIECAAIEaBFqivb0Q3T2rY+fgUExPT9cw99+hBwzCKsPSNYmFtkK55WxqcmpJC5lEgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAYDGBlpb0FV69MTMzs6TrEGfXryoISxM6VrZHV1dXedFicST2TOwp/5z9yx6dYosdms8JECBAgAABAgQIECBAgAABAgQIECBAgAABAgT2FchSr5QzZf8VWtuip6er3JxVLBZjfOzgv7qr6iCssq0VKw7LQrGOrB2tPdra2qJQaHVmBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBJYkMDU1HZOTk1EqlbLwazwmsmasvJ6ag7C8FlaHAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAQD0FBGH11FWbAAECBAgQIECAAAECBAgQIECAAAECBAgQIECgYQKCsIbRW5gAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQKCeAoKweuqqTYAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAg0DABQVjD6C1MgAABAgQIECBAgAABAgQIECBAgAABAgQIECBQTwFBWD111SZAgAABAgQIECBAgAABAgQIECBAgAABAgQIEGiYgCCsYfQWJkCAAAECBAgQIECAAAECBAgQIECAAAECBAgQqKeAIKyeumoTIECAAAECBAgQIECAAAECBAgQIECAAAECBAg0TEAQ1jB6CxMgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECNRT4G/85h2/M8VP1wAAAABJRU5ErkJggg==)" ] }, { "cell_type": "code", "execution_count": null, "id": "parameters-code", "metadata": { "cellView": "form", "id": "parameters-code" }, "outputs": [], "source": [ "#@title Parámetros del reporte\n", "año = 2026 #@param {type:\"integer\"}\n", "mes = 2 #@param {type:\"integer\"}\n", "departamento = \"ventas\" #@param [\"ventas\", \"marketing\", \"operaciones\"]" ] }, { "cell_type": "markdown", "id": "technical-vars-section", "metadata": { "jp-MarkdownHeadingCollapsed": true, "id": "technical-vars-section" }, "source": [ "## Variables técnicas (equipo técnico)\n", "\n", "Las siguientes celdas contienen configuraciones internas que el\n", "equipo no técnico no necesita ver ni modificar. En Google Colab,\n", "las celdas se ocultan usando `#@title` como primera línea y\n", "configurando la vista de formulario (*cellView: form*). Esto las\n", "colapsa visualmente, mostrando solo el título.\n", "\n", "Para configurarlo:\n", "\n", "- Agrega `#@title Título descriptivo` como primera línea de la\n", " celda de código.\n", "- Haz clic en los tres puntos de la celda > *Ver formulario*.\n", "- O establece `\"cellView\": \"form\"` en los metadatos de la celda." ] }, { "cell_type": "code", "execution_count": null, "id": "technical-vars-code", "metadata": { "cellView": "form", "id": "technical-vars-code" }, "outputs": [], "source": [ "#@title Variables técnicas (no modificar)\n", "BASE_DIR = Path(\"/content/drive/MyDrive/reportes\")\n", "INPUT_DIR = BASE_DIR / \"datos\"\n", "OUTPUT_DIR = BASE_DIR / \"resultados\"\n", "\n", "INPUT_FILE = INPUT_DIR / f\"datos_{año}_{mes:02d}.csv\"\n", "OUTPUT_FILE = OUTPUT_DIR / f\"reporte_{departamento}_{año}_{mes:02d}.csv\"\n", "\n", "COLUMNAS_REQUERIDAS = [\"fecha\", \"departamento\", \"categoria\", \"monto\"]" ] }, { "cell_type": "markdown", "id": "logic-section", "metadata": { "id": "logic-section" }, "source": [ "## Lógica de procesamiento (equipo técnico)\n", "\n", "La lógica de procesamiento también se oculta. Toda la\n", "funcionalidad se encapsula en funciones con nombres descriptivos.\n", "Esto facilita el mantenimiento por parte del equipo técnico y\n", "evita que el equipo no técnico modifique accidentalmente la\n", "lógica." ] }, { "cell_type": "code", "execution_count": null, "id": "processing-code", "metadata": { "cellView": "form", "id": "processing-code" }, "outputs": [], "source": [ "#@title Funciones de procesamiento (no modificar)\n", "\n", "def validar_parametros(anio, mes, departamento):\n", " \"\"\"Valida que los parámetros sean correctos.\"\"\"\n", " errores = []\n", " if not (2020 <= anio <= 2030):\n", " errores.append(f\"Año fuera de rango: {anio}\")\n", " if not (1 <= mes <= 12):\n", " errores.append(f\"Mes inválido: {mes}\")\n", " if departamento not in [\"ventas\", \"marketing\", \"operaciones\"]:\n", " errores.append(f\"Departamento no reconocido: {departamento}\")\n", " return errores\n", "\n", "\n", "def cargar_datos(ruta):\n", " \"\"\"Carga el archivo CSV y valida las columnas.\"\"\"\n", " if not ruta.exists():\n", " raise FileNotFoundError(\n", " f\"No se encontró el archivo: {ruta}\\n\"\n", " f\"Verifique que el archivo exista en Google Drive.\"\n", " )\n", " df = pd.read_csv(ruta)\n", " faltantes = [c for c in COLUMNAS_REQUERIDAS if c not in df.columns]\n", " if faltantes:\n", " raise ValueError(f\"Columnas faltantes en el archivo: {faltantes}\")\n", " return df\n", "\n", "\n", "def procesar_datos(df, departamento):\n", " \"\"\"Filtra y agrega los datos por departamento.\"\"\"\n", " df_filtrado = df[df[\"departamento\"] == departamento].copy()\n", " if df_filtrado.empty:\n", " raise ValueError(\n", " f\"No hay datos para el departamento: {departamento}\"\n", " )\n", " resumen = (\n", " df_filtrado\n", " .groupby(\"categoria\")\n", " .agg(\n", " total=(\"monto\", \"sum\"),\n", " cantidad=(\"monto\", \"count\"),\n", " promedio=(\"monto\", \"mean\"),\n", " )\n", " .reset_index()\n", " .sort_values(\"total\", ascending=False)\n", " )\n", " return resumen\n", "\n", "\n", "def guardar_reporte(df, ruta):\n", " \"\"\"Guarda el reporte en la ruta especificada.\"\"\"\n", " ruta.parent.mkdir(parents=True, exist_ok=True)\n", " df.to_csv(ruta, index=False)" ] }, { "cell_type": "markdown", "id": "execution-section", "metadata": { "id": "execution-section" }, "source": [ "## Ejecución\n", "\n", "Esta celda ejecuta el proceso completo. Se mantiene visible\n", "para que el equipo no técnico pueda verificar el progreso y los\n", "resultados. Los mensajes de *log* indican cada paso." ] }, { "cell_type": "code", "execution_count": null, "id": "execution-code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "execution-code", "outputId": "da16858d-1dea-4fa7-e1f9-d0a09668bbec" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "==================================================\n", "Reporte: ventas - 2026/02\n", "==================================================\n", "\n", "▶ Validando parámetros...\n", " ✓ Parámetros válidos.\n", "▶ Cargando datos desde: datos_2026_02.csv\n", " ✓ 300 registros cargados.\n", "▶ Procesando datos para 'ventas'...\n", " ✓ 4 categorías en el resumen.\n", "▶ Guardando reporte en: reporte_ventas_2026_02.csv\n", " ✓ Reporte guardado exitosamente.\n", "\n", "==================================================\n", "Proceso finalizado.\n", "==================================================\n" ] } ], "source": [ "print(f\"{'=' * 50}\")\n", "print(f\"Reporte: {departamento} - {año}/{mes:02d}\")\n", "print(f\"{'=' * 50}\")\n", "print()\n", "\n", "# Paso 1: Validar parámetros\n", "print(\"▶ Validando parámetros...\")\n", "errores = validar_parametros(año, mes, departamento)\n", "if errores:\n", " for error in errores:\n", " print(f\" ✗ {error}\")\n", " raise SystemExit(\"Proceso detenido por errores en parámetros.\")\n", "print(\" ✓ Parámetros válidos.\")\n", "\n", "# Paso 2: Cargar datos\n", "print(f\"▶ Cargando datos desde: {INPUT_FILE.name}\")\n", "df = cargar_datos(INPUT_FILE)\n", "print(f\" ✓ {len(df)} registros cargados.\")\n", "\n", "# Paso 3: Procesar datos\n", "print(f\"▶ Procesando datos para '{departamento}'...\")\n", "resumen = procesar_datos(df, departamento)\n", "print(f\" ✓ {len(resumen)} categorías en el resumen.\")\n", "\n", "# Paso 4: Guardar reporte\n", "print(f\"▶ Guardando reporte en: {OUTPUT_FILE.name}\")\n", "guardar_reporte(resumen, OUTPUT_FILE)\n", "print(f\" ✓ Reporte guardado exitosamente.\")\n", "\n", "print()\n", "print(f\"{'=' * 50}\")\n", "print(\"Proceso finalizado.\")\n", "print(f\"{'=' * 50}\")" ] }, { "cell_type": "markdown", "id": "naming-conventions", "metadata": { "id": "naming-conventions" }, "source": [ "## Convenciones de nombres\n", "\n", "Una de las buenas prácticas más importantes es usar nombres\n", "fijos o con formato programático para archivos y carpetas. Esto\n", "previene errores humanos y facilita la automatización.\n", "\n", "### Nombres a evitar\n", "\n", "| Ejemplo | Problema |\n", "|---------|----------|\n", "| `reporte enero.csv` | Espacio en el nombre, sin año, texto libre |\n", "| `Datos_Ventas_2026.CSV` | Mayúsculas inconsistentes, extensión en mayúscula |\n", "| `mi reporte (final) v2.csv` | Caracteres especiales, versionado manual |\n", "\n", "### Formato recomendado\n", "\n", "| Tipo | Formato | Ejemplo |\n", "|------|---------|----------|\n", "| Datos de entrada | `datos_AAAA_MM.csv` | `datos_2026_02.csv` |\n", "| Reporte de salida | `reporte_DEPTO_AAAA_MM.csv` | `reporte_ventas_2026_02.csv` |\n", "| Registros | `log_DEPTO_AAAA_MM.txt` | `log_ventas_2026_02.txt` |\n", "\n", "Las variables del nombre (año, mes, departamento) se construyen\n", "programáticamente a partir de los parámetros. El equipo no\n", "técnico **nunca** escribe nombres de archivos manualmente." ] }, { "cell_type": "markdown", "id": "time-considerations", "metadata": { "id": "time-considerations" }, "source": [ "## Consideraciones de tiempo de ejecución\n", "\n", "Google Colab tiene limitaciones de tiempo que conviene conocer:\n", "\n", "- Las sesiones gratuitas se desconectan tras ~90 minutos de\n", " inactividad o 12 horas de ejecución continua.\n", "- Colab Pro ofrece sesiones más largas, pero sigue siendo\n", " limitado.\n", "\n", "Recomendaciones:\n", "\n", "1. **Guardar resultados intermedios**: si el proceso es largo,\n", " guarda resultados parciales en Drive después de cada etapa.\n", " Si la sesión se desconecta, no se pierde el progreso.\n", "2. **Indicadores de progreso**: usa `print()` o `tqdm` para\n", " que el usuario sepa que el proceso sigue en ejecución.\n", "3. **Procesamiento por lotes**: si los datos son muy grandes,\n", " procesa en *chunks* en lugar de cargar todo en memoria.\n", "4. **Manejo de errores**: envuelve la ejecución en bloques\n", " `try/except` para guardar resultados parciales en caso de\n", " fallo." ] }, { "cell_type": "markdown", "id": "summary", "metadata": { "id": "summary" }, "source": [ "## Resumen de buenas prácticas\n", "\n", "1. Documentar el proceso al inicio del *notebook*.\n", "2. Colocar importaciones y conexión a Drive como primera celda\n", " de código.\n", "3. Separar los parámetros editables en una celda visible con\n", " formularios de Colab (`#@param`).\n", "4. Ocultar las variables técnicas y la lógica de procesamiento\n", " usando `#@title` y la vista de formulario.\n", "5. Usar una celda de ejecución visible con mensajes de progreso.\n", "6. Usar nombres de archivos fijos o con formatos programáticos,\n", " nunca texto libre.\n", "7. Considerar las limitaciones de tiempo de ejecución de Colab.\n", "8. Preferir listas cerradas sobre campos de texto libre en los\n", " parámetros." ] }, { "cell_type": "markdown", "id": "references", "metadata": { "id": "references" }, "source": [ "## Referencias\n", "\n", "- [Google Colab FAQ](https://research.google.com/colaboratory/faq.html). Google.\n", "- [Adding form fields to Colab notebooks](https://colab.research.google.com/notebooks/forms.ipynb). Google Colab.\n", "- [Mounting Google Drive in Colab](https://colab.research.google.com/notebooks/io.ipynb). Google Colab." ] } ], "metadata": { "category": "tecnología, programación", "date": "2026-02-24", "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.12.3" }, "tags": "google colab, python, notebook, automatización", "colab": { "provenance": [] } }, "nbformat": 4, "nbformat_minor": 5 }