{ "cells": [ { "cell_type": "markdown", "id": "title", "metadata": { "id": "title" }, "source": [ "# Best practices in Google Colab for sharing with non-technical teams" ] }, { "cell_type": "markdown", "id": "intro", "metadata": { "id": "intro" }, "source": [ "In teams where technical and non-technical profiles coexist, it\n", "is common for the technical team to develop notebooks in Google\n", "Colab for periodic processes: monthly reports, data analysis,\n", "recurring tasks. The problem arises when these processes require\n", "periodic execution with small variations — a different month,\n", "another department, a new input file — and the responsibility\n", "of running them always falls on the technical team.\n", "\n", "This dynamic creates unnecessary overhead: the technical team\n", "becomes a bottleneck for tasks that, with the right structure,\n", "anyone could run autonomously.\n", "\n", "The solution is to structure notebooks so that non-technical\n", "teams can execute them on their own, without risk of altering\n", "the logic and without needing to understand the code. This\n", "notebook presents the best practices I have applied in my\n", "experience and also serves as a downloadable template you can\n", "adapt to your own processes." ] }, { "cell_type": "markdown", "id": "structure-overview", "metadata": { "id": "structure-overview" }, "source": [ "## Recommended structure\n", "\n", "The structure that has worked best for me organizes the notebook\n", "into five well-defined blocks:\n", "\n", "1. **Documentation**: Markdown cells explaining what the process\n", " does, what data it needs, what results it produces and how to\n", " use it.\n", "2. **Imports and Drive initialization**: First code cell, always\n", " visible. Requests Google Drive permissions as early as\n", " possible so the user grants access immediately and the rest\n", " runs uninterrupted.\n", "3. **Editable parameters**: Visible code cell with Colab form\n", " fields. The only values the non-technical team should modify.\n", "4. **Technical variables and processing logic**: Hidden code\n", " cells to avoid confusion. Contain internal configurations\n", " and functions.\n", "5. **Main execution**: Visible code cell that runs the full\n", " process and displays progress messages and results." ] }, { "cell_type": "markdown", "id": "documentation-section", "metadata": { "id": "documentation-section" }, "source": [ "## Process documentation\n", "\n", "Every shared notebook must start with clear documentation. It is\n", "the first impression the user gets and determines whether they\n", "can use it without help. The documentation should include: what\n", "the process does, what input data it needs (files and location),\n", "what output data it generates (files and location) and step-by-step\n", "instructions for running it.\n", "\n", "Below is an example of documentation for a report generation\n", "process." ] }, { "cell_type": "markdown", "id": "example-documentation", "metadata": { "id": "example-documentation" }, "source": [ "> **Process**: Monthly departmental report.\n", ">\n", "> **Objective**: Generate a monthly summary from daily operations\n", "> data for a department.\n", ">\n", "> **Input data**: CSV file in `My Drive/reports/data/` named\n", "> `data_YYYY_MM.csv` (e.g., `data_2026_02.csv`).\n", ">\n", "> **Output data**: CSV report in `My Drive/reports/results/`\n", "> named `report_DEPARTMENT_YYYY_MM.csv` (e.g.,\n", "> `report_sales_2026_02.csv`).\n", ">\n", "> **Instructions**:\n", "> 1. Run the first code cell and authorize Google Drive access.\n", "> 2. Modify the parameters (year, month and department) in the\n", "> **Parameters** cell.\n", "> 3. From the menu, select *Runtime* > *Run all* (or\n", "> `Ctrl+F9`).\n", "> 4. Wait for completion. Progress will be shown in the last\n", "> cell." ] }, { "cell_type": "markdown", "id": "imports-section", "metadata": { "id": "imports-section" }, "source": [ "## Imports and Drive initialization\n", "\n", "This block is placed as the first code cell for two reasons:\n", "\n", "1. Google Drive requires user authorization. If this cell comes\n", " first, the user grants permission immediately and the rest of\n", " the notebook runs without interruptions.\n", "2. Early error detection: if there is a problem with the Drive\n", " connection or any dependency, it is caught before executing\n", " any logic." ] }, { "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 connected successfully.\n" ] } ], "source": [ "# @title Default title text\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 connected successfully.\")" ] }, { "cell_type": "markdown", "id": "parameters-section", "metadata": { "id": "parameters-section" }, "source": [ "## Parameters (non-technical team)\n", "\n", "This is the only cell the non-technical team should modify.\n", "Parameters are presented as Colab form fields using the\n", "`#@param` syntax, which generates visual controls (text fields,\n", "dropdowns, etc.) that make editing easy without touching code.\n", "\n", "An important tip: use closed lists\n", "(`#@param [\"option1\", ...]`) instead of free text whenever\n", "possible. This prevents typos and ensures valid values.", "\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+2HOLmPblZ3HF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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 Report parameters\n", "year = 2026 #@param {type:\"integer\"}\n", "month = 2 #@param {type:\"integer\"}\n", "department = \"sales\" #@param [\"sales\", \"marketing\", \"operations\"]" ] }, { "cell_type": "markdown", "id": "technical-vars-section", "metadata": { "id": "technical-vars-section" }, "source": [ "## Technical variables (technical team)\n", "\n", "The following cells contain internal configurations that the\n", "non-technical team does not need to see or modify. In Google\n", "Colab, cells are hidden using `#@title` as the first line and\n", "setting the cell view to form (*cellView: form*). This\n", "collapses them visually, showing only the title.\n", "\n", "To configure this:\n", "\n", "- Add `#@title Descriptive title` as the first line of the\n", " code cell.\n", "- Click the three dots on the cell > *Form view*.\n", "- Or set `\"cellView\": \"form\"` in the cell metadata." ] }, { "cell_type": "code", "execution_count": null, "id": "technical-vars-code", "metadata": { "cellView": "form", "id": "technical-vars-code" }, "outputs": [], "source": [ "#@title Technical variables (do not modify)\n", "BASE_DIR = Path(\"/content/drive/MyDrive/reports\")\n", "INPUT_DIR = BASE_DIR / \"data\"\n", "OUTPUT_DIR = BASE_DIR / \"results\"\n", "\n", "INPUT_FILE = INPUT_DIR / f\"data_{year}_{month:02d}.csv\"\n", "OUTPUT_FILE = OUTPUT_DIR / f\"report_{department}_{year}_{month:02d}.csv\"\n", "\n", "REQUIRED_COLUMNS = [\"date\", \"department\", \"category\", \"amount\"]" ] }, { "cell_type": "markdown", "id": "logic-section", "metadata": { "id": "logic-section" }, "source": [ "## Processing logic (technical team)\n", "\n", "The processing logic is also hidden. All functionality is\n", "encapsulated in functions with descriptive names. This makes\n", "maintenance easier for the technical team and prevents the\n", "non-technical team from accidentally modifying the logic." ] }, { "cell_type": "code", "execution_count": null, "id": "processing-code", "metadata": { "cellView": "form", "id": "processing-code" }, "outputs": [], "source": [ "#@title Processing functions (do not modify)\n", "\n", "def validate_params(year, month, department):\n", " \"\"\"Validates that parameters are correct.\"\"\"\n", " errors = []\n", " if not (2020 <= year <= 2030):\n", " errors.append(f\"Year out of range: {year}\")\n", " if not (1 <= month <= 12):\n", " errors.append(f\"Invalid month: {month}\")\n", " if department not in [\"sales\", \"marketing\", \"operations\"]:\n", " errors.append(f\"Unrecognized department: {department}\")\n", " return errors\n", "\n", "\n", "def load_data(path):\n", " \"\"\"Loads the CSV file and validates columns.\"\"\"\n", " if not path.exists():\n", " raise FileNotFoundError(\n", " f\"File not found: {path}\\n\"\n", " f\"Please verify the file exists in Google Drive.\"\n", " )\n", " df = pd.read_csv(path)\n", " missing = [c for c in REQUIRED_COLUMNS if c not in df.columns]\n", " if missing:\n", " raise ValueError(f\"Missing columns in file: {missing}\")\n", " return df\n", "\n", "\n", "def process_data(df, department):\n", " \"\"\"Filters and aggregates data by department.\"\"\"\n", " df_filtered = df[df[\"department\"] == department].copy()\n", " if df_filtered.empty:\n", " raise ValueError(\n", " f\"No data found for department: {department}\"\n", " )\n", " summary = (\n", " df_filtered\n", " .groupby(\"category\")\n", " .agg(\n", " total=(\"amount\", \"sum\"),\n", " count=(\"amount\", \"count\"),\n", " average=(\"amount\", \"mean\"),\n", " )\n", " .reset_index()\n", " .sort_values(\"total\", ascending=False)\n", " )\n", " return summary\n", "\n", "\n", "def save_report(df, path):\n", " \"\"\"Saves the report to the specified path.\"\"\"\n", " path.parent.mkdir(parents=True, exist_ok=True)\n", " df.to_csv(path, index=False)" ] }, { "cell_type": "markdown", "id": "execution-section", "metadata": { "id": "execution-section" }, "source": [ "## Execution\n", "\n", "This cell runs the full process. It remains visible so the\n", "non-technical team can verify progress and results. Log messages\n", "indicate each step." ] }, { "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", "Report: sales - 2026/02\n", "==================================================\n", "\n", "▶ Validating parameters...\n", " ✓ Parameters valid.\n", "▶ Loading data from: data_2026_02.csv\n", " ✓ 300 records loaded.\n", "▶ Processing data for 'sales'...\n", " ✓ 4 categories in summary.\n", "▶ Saving report to: report_sales_2026_02.csv\n", " ✓ Report saved successfully.\n", "\n", "==================================================\n", "Process completed.\n", "==================================================\n" ] } ], "source": [ "print(f\"{'=' * 50}\")\n", "print(f\"Report: {department} - {year}/{month:02d}\")\n", "print(f\"{'=' * 50}\")\n", "print()\n", "\n", "# Step 1: Validate parameters\n", "print(\"▶ Validating parameters...\")\n", "errors = validate_params(year, month, department)\n", "if errors:\n", " for error in errors:\n", " print(f\" ✗ {error}\")\n", " raise SystemExit(\"Process stopped due to parameter errors.\")\n", "print(\" ✓ Parameters valid.\")\n", "\n", "# Step 2: Load data\n", "print(f\"▶ Loading data from: {INPUT_FILE.name}\")\n", "df = load_data(INPUT_FILE)\n", "print(f\" ✓ {len(df)} records loaded.\")\n", "\n", "# Step 3: Process data\n", "print(f\"▶ Processing data for '{department}'...\")\n", "summary = process_data(df, department)\n", "print(f\" ✓ {len(summary)} categories in summary.\")\n", "\n", "# Step 4: Save report\n", "print(f\"▶ Saving report to: {OUTPUT_FILE.name}\")\n", "save_report(summary, OUTPUT_FILE)\n", "print(f\" ✓ Report saved successfully.\")\n", "\n", "print()\n", "print(f\"{'=' * 50}\")\n", "print(\"Process completed.\")\n", "print(f\"{'=' * 50}\")" ] }, { "cell_type": "markdown", "id": "naming-conventions", "metadata": { "id": "naming-conventions" }, "source": [ "## Naming conventions\n", "\n", "One of the most important best practices is using fixed or\n", "programmatic format names for files and folders. This prevents\n", "human errors and makes automation easier.\n", "\n", "### Names to avoid\n", "\n", "| Example | Problem |\n", "|---------|---------|\n", "| `report january.csv` | Space in name, no year, free-form text |\n", "| `Data_Sales_2026.CSV` | Inconsistent casing, uppercase extension |\n", "| `my report (final) v2.csv` | Special characters, manual versioning |\n", "\n", "### Recommended format\n", "\n", "| Type | Format | Example |\n", "|------|--------|---------|\n", "| Input data | `data_YYYY_MM.csv` | `data_2026_02.csv` |\n", "| Output report | `report_DEPT_YYYY_MM.csv` | `report_sales_2026_02.csv` |\n", "| Logs | `log_DEPT_YYYY_MM.txt` | `log_sales_2026_02.txt` |\n", "\n", "Name variables (year, month, department) are built\n", "programmatically from the parameters. The non-technical team\n", "**never** types file names manually." ] }, { "cell_type": "markdown", "id": "time-considerations", "metadata": { "id": "time-considerations" }, "source": [ "## Time execution considerations\n", "\n", "Google Colab has time limits worth knowing about:\n", "\n", "- Free sessions disconnect after ~90 minutes of inactivity or\n", " 12 hours of continuous execution.\n", "- Colab Pro offers longer sessions but is still limited.\n", "\n", "Recommendations:\n", "\n", "1. **Save intermediate results**: If the process is long, save\n", " partial results to Drive after each stage. If the session\n", " disconnects, progress is not lost.\n", "2. **Progress indicators**: Use `print()` or `tqdm` so the user\n", " knows the process is still running.\n", "3. **Batch processing**: If data is very large, process in\n", " chunks instead of loading everything into memory.\n", "4. **Error handling**: Wrap execution in `try/except` blocks to\n", " save partial results on failure." ] }, { "cell_type": "markdown", "id": "summary", "metadata": { "id": "summary" }, "source": [ "## Best practices summary\n", "\n", "1. Document the process at the beginning of the notebook.\n", "2. Place imports and Drive connection as the first code cell.\n", "3. Separate editable parameters in a visible cell with Colab\n", " forms (`#@param`).\n", "4. Hide technical variables and processing logic using\n", " `#@title` and form view.\n", "5. Use a visible execution cell with progress messages.\n", "6. Use fixed or programmatic file names, never free-form text.\n", "7. Account for Colab time execution limits.\n", "8. Prefer closed lists over free text fields for parameters." ] }, { "cell_type": "markdown", "id": "references", "metadata": { "id": "references" }, "source": [ "## References\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": "technology, programming", "date": "2026-02-24", "language": "en", "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.7" }, "tags": "google colab, python, notebook, automation", "colab": { "provenance": [] } }, "nbformat": 4, "nbformat_minor": 5 }