audit-labs/tutorials
Learn how to perform data analysis, scripting, automation, and more!
clone: git clone https://gitbay.org/audit-labs/tutorials.git
85b1c325c6a36a3178bd6ff8df0d0c0f1ec6f1b0
unsigned
author: Christian Cleberg <hello@cleberg.net> · 2025-12-24T01:13:30Z
.../basic_data_analysis-checkpoint.ipynb | 720 +++++++++++++++++++++ .../racf_access_analysis-checkpoint.ipynb | 526 +++++++++++++++ notebooks/basic_data_analysis.ipynb | 720 +++++++++++++++++++++ notebooks/racf_access_analysis.ipynb | 526 +++++++++++++++ notebooks/sample_racf_data.txt | 54 ++ 5 files changed, 2546 insertions(+) new file mode 100644 @@ -0,0 +1,720 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2bc817b1", + "metadata": {}, + "source": [ + "\n", + "# Basic Data Analysis & Visualization\n", + "\n", + "This notebook demonstrates how to load a sample dataset, perform quick exploratory analysis, group and pivot the data, and create visualizations.\n", + "\n", + "We’re using a CSV file from: https://sample-files.com/downloads/data/csv/basic-data.csv\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "a92fdfd1", + "metadata": {}, + "source": [ + "\n", + "## Install Dependencies\n", + "\n", + "If you haven't installed the required libraries, run:\n", + "\n", + "```bash\n", + "pip install pandas matplotlib seaborn\n", + "```\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b5d7308a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting pandas\n", + " Downloading pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl.metadata (91 kB)\n", + "Collecting matplotlib\n", + " Downloading matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl.metadata (52 kB)\n", + "Collecting seaborn\n", + " Downloading seaborn-0.13.2-py3-none-any.whl.metadata (5.4 kB)\n", + "Collecting numpy>=1.26.0 (from pandas)\n", + " Downloading numpy-2.4.0-cp314-cp314-macosx_14_0_arm64.whl.metadata (6.6 kB)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /opt/homebrew/Cellar/jupyterlab/4.5.1/libexec/lib/python3.14/site-packages (from pandas) (2.9.0.post0)\n", + "Collecting pytz>=2020.1 (from pandas)\n", + " Using cached pytz-2025.2-py2.py3-none-any.whl.metadata (22 kB)\n", + "Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/Cellar/jupyterlab/4.5.1/libexec/lib/python3.14/site-packages (from pandas) (2025.3)\n", + "Collecting contourpy>=1.0.1 (from matplotlib)\n", + " Downloading contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl.metadata (5.5 kB)\n", + "Collecting cycler>=0.10 (from matplotlib)\n", + " Downloading cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)\n", + "Collecting fonttools>=4.22.0 (from matplotlib)\n", + " Downloading fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl.metadata (114 kB)\n", + "Collecting kiwisolver>=1.3.1 (from matplotlib)\n", + " Downloading kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl.metadata (6.3 kB)\n", + "Requirement already satisfied: packaging>=20.0 in /opt/homebrew/Cellar/jupyterlab/4.5.1/libexec/lib/python3.14/site-packages (from matplotlib) (25.0)\n", + "Collecting pillow>=8 (from matplotlib)\n", + " Downloading pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl.metadata (8.8 kB)\n", + "Collecting pyparsing>=3 (from matplotlib)\n", + " Downloading pyparsing-3.3.1-py3-none-any.whl.metadata (5.6 kB)\n", + "Requirement already satisfied: six>=1.5 in /opt/homebrew/Cellar/jupyterlab/4.5.1/libexec/lib/python3.14/site-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)\n", + "Downloading pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl (10.8 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m10.8/10.8 MB\u001b[0m \u001b[31m5.0 MB/s\u001b[0m \u001b[33m0:00:02\u001b[0m.9 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m:02\u001b[0m\n", + "\u001b[?25hDownloading matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl (8.2 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m8.2/8.2 MB\u001b[0m \u001b[31m6.5 MB/s\u001b[0m \u001b[33m0:00:01\u001b[0mm \u001b[31m6.6 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hDownloading seaborn-0.13.2-py3-none-any.whl (294 kB)\n", + "Downloading contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl (273 kB)\n", + "Downloading cycler-0.12.1-py3-none-any.whl (8.3 kB)\n", + "Downloading fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl (2.8 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.8/2.8 MB\u001b[0m \u001b[31m6.9 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m7.6 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hDownloading kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl (64 kB)\n", + "Downloading numpy-2.4.0-cp314-cp314-macosx_14_0_arm64.whl (5.2 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.2/5.2 MB\u001b[0m \u001b[31m7.6 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m7.8 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hDownloading pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl (4.7 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.7/4.7 MB\u001b[0m \u001b[31m7.9 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m7.9 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hDownloading pyparsing-3.3.1-py3-none-any.whl (121 kB)\n", + "Using cached pytz-2025.2-py2.py3-none-any.whl (509 kB)\n", + "Installing collected packages: pytz, pyparsing, pillow, numpy, kiwisolver, fonttools, cycler, pandas, contourpy, matplotlib, seaborn\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m11/11\u001b[0m [seaborn]━\u001b[0m \u001b[32m10/11\u001b[0m [seaborn]atplotlib]\n", + "\u001b[1A\u001b[2KSuccessfully installed contourpy-1.3.3 cycler-0.12.1 fonttools-4.61.1 kiwisolver-1.4.9 matplotlib-3.10.8 numpy-2.4.0 pandas-2.3.3 pillow-12.0.0 pyparsing-3.3.1 pytz-2025.2 seaborn-0.13.2\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install pandas matplotlib seaborn" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0632140a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Matplotlib is building the font cache; this may take a moment.\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# Set plot style\n", + "sns.set(style=\"whitegrid\")" + ] + }, + { + "cell_type": "markdown", + "id": "d326b5b6", + "metadata": {}, + "source": [ + "## Load Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5d5de24b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>ID</th>\n", + " <th>Name</th>\n", + " <th>Age</th>\n", + " <th>Country</th>\n", + " <th>Email</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>1</td>\n", + " <td>Name_1</td>\n", + " <td>62</td>\n", + " <td>Country_1</td>\n", + " <td>email_1@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>2</td>\n", + " <td>Name_2</td>\n", + " <td>48</td>\n", + " <td>Country_2</td>\n", + " <td>email_2@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>3</td>\n", + " <td>Name_3</td>\n", + " <td>61</td>\n", + " <td>Country_3</td>\n", + " <td>email_3@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>4</td>\n", + " <td>Name_4</td>\n", + " <td>32</td>\n", + " <td>Country_4</td>\n", + " <td>email_4@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>5</td>\n", + " <td>Name_5</td>\n", + " <td>69</td>\n", + " <td>Country_5</td>\n", + " <td>email_5@example.com</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " ID Name Age Country Email\n", + "0 1 Name_1 62 Country_1 email_1@example.com\n", + "1 2 Name_2 48 Country_2 email_2@example.com\n", + "2 3 Name_3 61 Country_3 email_3@example.com\n", + "3 4 Name_4 32 Country_4 email_4@example.com\n", + "4 5 Name_5 69 Country_5 email_5@example.com" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load the dataset from URL\n", + "url = \"https://sample-files.com/downloads/data/csv/basic-data.csv\"\n", + "df = pd.read_csv(url, skiprows=1)\n", + "df.columns = df.columns.str.strip()\n", + "\n", + "# Preview the data\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "e7f7c2e7", + "metadata": {}, + "source": [ + "## Basic Exploration" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "aca7309e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(100, 5)\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>ID</th>\n", + " <th>Name</th>\n", + " <th>Age</th>\n", + " <th>Country</th>\n", + " <th>Email</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>count</th>\n", + " <td>100.000000</td>\n", + " <td>100</td>\n", + " <td>100.000000</td>\n", + " <td>100</td>\n", + " <td>100</td>\n", + " </tr>\n", + " <tr>\n", + " <th>unique</th>\n", + " <td>NaN</td>\n", + " <td>100</td>\n", + " <td>NaN</td>\n", + " <td>10</td>\n", + " <td>100</td>\n", + " </tr>\n", + " <tr>\n", + " <th>top</th>\n", + " <td>NaN</td>\n", + " <td>Name_1</td>\n", + " <td>NaN</td>\n", + " <td>Country_1</td>\n", + " <td>email_1@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>freq</th>\n", + " <td>NaN</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>10</td>\n", + " <td>1</td>\n", + " </tr>\n", + " <tr>\n", + " <th>mean</th>\n", + " <td>50.500000</td>\n", + " <td>NaN</td>\n", + " <td>44.530000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>std</th>\n", + " <td>29.011492</td>\n", + " <td>NaN</td>\n", + " <td>15.190012</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>min</th>\n", + " <td>1.000000</td>\n", + " <td>NaN</td>\n", + " <td>18.000000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>25%</th>\n", + " <td>25.750000</td>\n", + " <td>NaN</td>\n", + " <td>32.000000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>50%</th>\n", + " <td>50.500000</td>\n", + " <td>NaN</td>\n", + " <td>43.500000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>75%</th>\n", + " <td>75.250000</td>\n", + " <td>NaN</td>\n", + " <td>59.250000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>max</th>\n", + " <td>100.000000</td>\n", + " <td>NaN</td>\n", + " <td>69.000000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " ID Name Age Country Email\n", + "count 100.000000 100 100.000000 100 100\n", + "unique NaN 100 NaN 10 100\n", + "top NaN Name_1 NaN Country_1 email_1@example.com\n", + "freq NaN 1 NaN 10 1\n", + "mean 50.500000 NaN 44.530000 NaN NaN\n", + "std 29.011492 NaN 15.190012 NaN NaN\n", + "min 1.000000 NaN 18.000000 NaN NaN\n", + "25% 25.750000 NaN 32.000000 NaN NaN\n", + "50% 50.500000 NaN 43.500000 NaN NaN\n", + "75% 75.250000 NaN 59.250000 NaN NaN\n", + "max 100.000000 NaN 69.000000 NaN NaN" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Check shape and summary stats\n", + "print(df.shape)\n", + "df.describe(include=\"all\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e775e42f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ID Name Age Country Email\n", + "0 1 Name_1 62 Country_1 email_1@example.com\n", + "1 2 Name_2 48 Country_2 email_2@example.com\n", + "2 3 Name_3 61 Country_3 email_3@example.com\n", + "3 4 Name_4 32 Country_4 email_4@example.com\n", + "4 5 Name_5 69 Country_5 email_5@example.com\n", + ".. ... ... ... ... ...\n", + "95 96 Name_96 60 Country_6 email_96@example.com\n", + "96 97 Name_97 26 Country_7 email_97@example.com\n", + "97 98 Name_98 52 Country_8 email_98@example.com\n", + "98 99 Name_99 24 Country_9 email_99@example.com\n", + "99 100 Name_100 55 Country_0 email_100@example.com\n", + "\n", + "[100 rows x 5 columns]\n" + ] + } + ], + "source": [ + "print(df)" + ] + }, + { + "cell_type": "markdown", + "id": "dc10be72", + "metadata": {}, + "source": [ + "## Grouping and Pivoting Data" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f87ea8af", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Country</th>\n", + " <th>RecordCount</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>Country_1</td>\n", + " <td>10</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>Country_2</td>\n", + " <td>10</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>Country_3</td>\n", + " <td>10</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>Country_4</td>\n", + " <td>10</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>Country_5</td>\n", + " <td>10</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Country RecordCount\n", + "0 Country_1 10\n", + "1 Country_2 10\n", + "2 Country_3 10\n", + "3 Country_4 10\n", + "4 Country_5 10" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Example: Group by 'Country' and count number of records\n", + "country_counts = df[\"Country\"].value_counts().reset_index()\n", + "country_counts.columns = [\"Country\", \"RecordCount\"]\n", + "country_counts.head()" + ] + }, + { + "cell_type": "markdown", + "id": "f156d6de", + "metadata": {}, + "source": [ + "## Visualizing Record Counts by Country" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d644fc06", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/z_/x3_6fwb174s0c7wcsd813s6r0000gn/T/ipykernel_10080/1795714619.py:3: FutureWarning: \n", + "\n", + "Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.\n", + "\n", + " sns.barplot(data=country_counts, x=\"Country\", y=\"RecordCount\", palette=\"viridis\")\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA00AAAJUCAYAAADJrc3LAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAASopJREFUeJzt3QeYVOXZP+CXKiCiiIrGimLD2AV7sGvsvcZeY8OgQbFhrKh8ig0/rNhb7DG2iEZjxxg1ihoU7NgoIqC0+V/P+f6z2cXlwC7L7uzOfV/Xsrszs7NnH94zc37nLadZoVAoJAAAAKrVvPqbAQAAEJoAAABmQ08TAABADqEJAAAgh9AEAACQQ2gCAADIITQBAADkEJoAAAByCE0AUIlrvs+a2gDlSmgCKAEHHnhgWnnllat8rLLKKmmdddZJu+++e3r44YdTqfv888+z7X7ggQdm+9ivv/46XXLJJWm77bZLa665Ztpkk03SMccck4YNG5Ya0jPPPJNOPfXU3MecdtppaYsttphn2/Dzzz+nIUOGpD322COtu+66qUePHmnfffdNDz30UIOGlv/85z9pv/32a7DfD9CQWjbobwegQrdu3VK/fv0qvp8+fXoaPXp0dgDdp0+ftNBCC6WePXs2+oq98cYb6bjjjksdO3ZMBx10UOrSpUsaN25cuueee7LweNFFF6Vdd921QbYtat2Qvvvuu3TEEUekr776KqvFGmuskWbMmJGeffbZLKxFqDzvvPNSs2bN6n3bnnjiifTmm2/W++8FKAVCE0CJaN++fVprrbV+cftvfvObtOGGG2Y9OI09NEU4Oumkk9Jyyy2Xbr755tS2bduK+7bddtt01FFHpbPPPjvreVpkkUVSuYlergjKESCjRkWbbbZZ+tWvfpUuu+yytPnmm6ctt9yyQbcToNwYngdQ4uabb77UunXrKr0L0ftw3XXXpa233jr9+te/zgLHbbfd9oufjSFdu+22WzYELg68/+d//idNmTKl4v533nknHX744Wn99dfPhgLGELkYhlX06quvZkPu7r777uxgPR7z4osvZvc99dRTaeedd856Q+J3vP/++7P9W2J7vvnmm3T66adXCUyhefPm6ZRTTkkHHHBA+vHHHytuj9+3//77Z0PVYjtPPvnkrCem6Kqrrsq2cWZxW9xXeejg448/nk488cS09tprZ8Pe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oQkAAKB29DQBAADkEJoAAAByCE0AAAA5hCYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMghNAEAAOQQmgAAAHIITQAAADmEJgAAgBxCEwAAQA6hCQAAIIfQBAAAkENoAgAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAAAgh9AEAACQQ2gCAADIITQBAADkEJoAAAByCE0AAAA5hCYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMghNAEAAOQQmgAAAHIITQAAADmEJgAAgBxCEwAAQA6hCQAAIIfQBAAAkENoAgAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAAAgh9AEAACQQ2gCAADIITQBAADkEJoAAAByCE0AAAA5hCYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMghNAEAAOQQmgAAAHIITQAAAHUdmvr27Zs+++yzau/7+OOP0zHHHFObpwUAACg5Lef0gV9++WXF1w899FDaaqutUosWLX7xuOeffz699NJLdbeFAAAAjSE0/elPf8oCUdHxxx9f7eMKhULaeOON62brAAAAGktoOvfcc7MepAhFp59+evr973+flllmmSqPad68eerQoUNaf/3158W2AgAAlG5o6ty5c9ptt92yr5s1a5Z69uyZFl544Xm5bQAAAI0nNFUW4Sl6nN577700adKk7OuZde/evS62DwAAoPGFprfffjv16tUrjR49Ovu+GJqiByq+js/Dhw+v2y0FAABoLKHpoosuSi1btsw+L7744tlcJgAAgKaoVqHp3XffTZdddlm27Hh9iWXOr7vuuuz6ULEARaze99vf/rbefj8AAFCeatVF1KlTp2qv0TSvPPzww+mMM85IBxxwQHrsscfSjjvumHr37p3efPPNetsGAACgPNUqNO2///5p8ODB2SIQ81rMkbriiivSQQcdlIWm6GWK5c432mij9Nprr83z3w8AAJS3Wg3P++STT9JHH32UXcR2xRVXTG3atKlyfywEccstt9TJBo4cOTJ98cUXaaeddqpy+4033lgnzw8AADBPQtMqq6xS8f3MS45XtwT53ISmEL1ahx9+eLbM+VJLLZX1Nm2xxRa1es7Yvln1kkXga9u2bSpXkydPrvH/n5rVvGbqpq3V1/6prWlr2lr98R5aPzULjj0cr9VVWyuu/D1PQtNtt92W6suPP/6YfT711FOzxR9OOeWU9OSTT6Zjjz023XzzzWnDDTes8XNOnTp1lkuiR2Dq1q1bKlcRUqNR1YSa1bxm6qat1df+qa1pa9pa/fEeWj81C449HK/VZVtr3br1vAlN9alVq1bZ5+hliovqhlVXXTXrcaptaIrn7Nq1a7X3zUnSbMq6dOlSq56mclabmgV109a0tfrhda1+aha8rnld09bqh9e1uqvZiBEj5ujnaxWaYljc7F4Yn3nmmVQXOnfunH1eaaWVqtweoee5556r1XPGtrdr165Otq+pKeehibWlZuqmrZU2+6iaaWuly/6pbg3d1ub0ZE+tQlOPHj1+8QsmTpyY3nnnnfTzzz+ngw8+ONWV1VZbLc0///zprbfeSuutt17F7R9++GG2kh4AAMC8VKvQ1L9//1nOFYq5RrUZmzorsTLfEUccka655pqs12mNNdbIrtX04osvpiFDhtTZ7wEAAJjnc5pirlBcT6lv377ppJNOqrPnjSAWXWqXX355+vrrr9MKK6yQrrrqqrT++uvX2e8AAACol4Ugxo8fnw3Vq2uHHnpo9gEAAFDyoemhhx76xW3Tp09Po0ePTrfffnuVuUcAAABlF5pOO+20Wd639tprp7POOmtutgkAAKBxh6bqlhOP1fTat2+fOnToUBfbBQAA0HhD05JLLlnx9UcffZQmTJiQOnbsKDABAABNTq0XgvjLX/6SLr744vTdd99V3LbIIoukk08+Oe266651tX0AAACNLzQNHTo0/fGPf0wbbLBB6t27dxaWvvnmm/TII49ky40vtNBCabPNNqv7rQUAAGgMoenaa69N2223XXbdpMr22GOP9Ic//CENHjxYaAIAAJqE5rX5oQ8//DDttttu1d4Xt7///vtzu10AAACNNzTFog9xEdvqjBs3LrVu3XputwsAAKDxhqYNN9wwXX311dnFbCv76quv0jXXXJM23njjuto+AACAxjenKRZ/iPlL22yzTXYx21gIIlbRe/PNN9OCCy6YraAHAABQtj1Niy66aHrwwQfTgQcemCZPnpz+/e9/Z5/j+7i98nWcAAAAyvI6TZ06dcqWHQcAAGjKatTTNG3atHT77benp59+usrt06dPz1bNGzJkSJoxY0ZdbyMAAEDph6apU6emY489Nl1wwQXZ3KXKxowZk4Wl/v37p+OPPz4LUQAAAGUVmu655570yiuvpAEDBqQ+ffr8Yo7Tww8/nIWm559/Pt1///3zYlsBAABKNzQ98MAD6ZBDDkk77LDDLB+z6667pr322ivdd999dbV9AAAAjSM0ffLJJ2mDDTaY7eN69uyZRo0aNbfbBQAA0LhCU8uWLbN5TXPyuGbNms3tdgEAADSu0LTiiiumV199dbaPe+2119JSSy01t9sFAADQuELTLrvsku6666709ttvz/Ix7777brrjjjvSb3/727raPgAAgMZxcds999wz/eUvf0kHHnhg9vVmm22W9SjFUuNffPFFtmrevffem1ZeeeXsMQAAAGUVmmKe0uDBg9OFF16YLT9+5513VtxXKBSyuUyxcl7v3r1TmzZt5tX2AgAAlGZoChGGzj333HTSSSell19+OY0ePTq1aNEiLbnkktnKegsssMC821IAAIBSD01FCy+8cO71mgAAAMouND300EM1euK40C0AAEDZhKbTTjutyvfFazHFfKaZbwtCEwAAUFah6Zlnnqn4evjw4emPf/xjOvbYY7PlxRdbbLE0duzYNHTo0HTVVVeliy66aF5tLwAAQGmGpljsoeiEE07IAtORRx5ZcVvnzp3Tfvvtl6ZMmZIuvfTS1LNnz7rfWgAAgFK9uG1lH330UerWrVu19y2//PLp888/n9vtAgAAaLyhabnllkuPPvpotffFNZxWWmmlud0uAACAxrvk+HHHHZd69eqVRo0alTbffPPUsWPH9N1336WnnnoqjRgxIl1//fV1v6UAAACNJTRts8026ZprrkmDBg1KAwcOzFbQa968eVp77bXTkCFD0nrrrVf3WwoAANBYQtPLL7+cNtpoo7TFFlukn3/+OY0fPz4ttNBCqXXr1nW/hQAAAI1tTlOsnhdD8cJ8882XLTkuMAEAAE1RrUJThw4dUps2bep+awAAAJrC8Lyjjz46nX/++WnkyJFplVVWSe3atfvFY7p3714X2wcAAND4QlO/fv2yz5dffnn2uVmzZhX3xaIQ8f3w4cPrahsBAAAaV2i69dZb635LAAAAmkpo6tGjR91vCQAAQFMJTSHmM1155ZXptddeSz/88EN2gdu4PlNc+HaFFVao260EAABoTKFpxIgRad99900tWrTIrtW0yCKLpG+//TY9++yz6bnnnkv33Xef4AQAAJRvaBowYEBaaqml0m233ZYWWGCBitsnTJiQDj744GyBiKuvvroutxMAAKDxXKfp9ddfT8ccc0yVwBTi+6OOOiq7HwAAoGxDU8uWLdN8881X7X2tW7dOU6ZMmdvtAgAAaLyhafXVV0933nlndk2myuL7O+64I/3617+uq+0DAABoULWa09SrV6+03377pZ133jltt912adFFF80WgnjiiSeyVfVuvvnmut9SAACAxhKaoqfphhtuSP/zP/+TLfgQPUzNmjXLepiuv/761L1797rfUgAAgMZ0naYNNtgg3X333dn8pbhOU4cOHdK0adN+sTgEAABA2c1pmjp1aurXr1/ae++9U9u2bVPnzp3Tm2++mTbccMN08cUXpxkzZtT9lgIAADSW0HTVVVelRx55JO2www4Vt3Xr1i2dcsop6d57782G7gEAAJTt8LxHH300nXrqqWnfffetuG2hhRZKhxxySLYc+a233ppdrwkAAKAse5rGjh2bll566WrvW3755dPo0aPndrsAAAAab2iKYPTkk09We9/QoUPTsssuO7fbBQAA0HiH5x100EHptNNOS+PGjUtbbbVV6tSpUxozZkx69tln0+OPP54uuuiiut9SAACAxhKadt111zRx4sQ0aNCg9NRTT1Xc3rFjx3TWWWdl9wMAAJT1dZoOOOCAtP/++6eRI0dmPU5xnaYYtte8ea1G/AEAAJSkuUo4cVHbCE0ffPBBWnjhhdOoUaNSoVCou60DAABorD1N1157bRo8eHD66aefUrNmzdIaa6yRBg4cmK2sd9NNN2U9TwAAAGXZ03T77bdnF7g99NBDs4vZFnuXfve736XPPvssXXHFFXW9nQAAAI0nNN12223ZxWt79eqVVltttYrbe/bsmU466aRs2XEAAICyDU1ffvll6tGjR7X3xWIQ33333dxuFwAAQOMNTUsssUR68803q73v3//+d3Y/AABA2S4Eseeee2Zzmtq0aZM222yz7LZJkyalJ598MlscIuY6AQAAlG1oOvLII9Pnn3+eBgwYkH2Egw46KFsQYuedd05HH310XW8nAABA4wlNscT4ueeemw477LD0yiuvZBe3XWCBBVL37t1T165d01133ZVd/BYAAKCsQtPzzz+fHnzwwSw07bLLLtlqecstt1zF/cOGDUu77757drFboQkAACir0PTII4+kPn36pFatWqXWrVunxx9/PF155ZVp6623znqazj///PTYY4+lFi1amNMEAACUX2i65ZZb0pprrpluvPHGLDT17ds3XXPNNWnFFVfMQtJXX32VNt1003T66aenLl26zNutBgAAKLXQNGrUqHTeeeel9u3bZ98ff/zxafvtt0/HHntsmjJlSrriiivStttuOy+3FQAAoHRDUywpXvn6S0suuWS2Wl7Lli2zoXudOnWaV9sIAABQ+he3jYAU85WKil//4Q9/EJgAAIAma45D06wstthidbMlAAAATTE0xfLjAAAATVWNrtN0zjnnVCwEEcP1wllnnZXmn3/+XwSpWG0PAACgbEJT9+7dq4SlWd1W3fcAAABNPjTddtttqaGNHDky7b777lnvVnwGAAAo+TlN9WXq1KnplFNOyZY+BwAAqC+NJjRdddVVFfOpAAAA6kujCE2vv/56uueee1L//v0belMAAIAyU6PV8xrCDz/8kPr06ZPOPPPMtMQSS9TJc8ZCFbMa5hcr/7Vt2zaVq8mTJ9d4IQ81q3nN1E1bq6/9U1vT1rS1+uM9tH5qFhx7OF6rq7YWt83JJZRKPjTFMudrr7122mmnnep0ftTw4cOrvS8CU7du3VK5isU2olHVhJrVvGbqpq3V1/6prWlr2lr98R5aPzULjj0cr9VlW2vdunXjDk0PPfRQGjZsWHr00Ufr9HlbtWqVunbtWu195X6x3i5dutSqp6mc1aZmQd20NW2tfnhdq5+aBa9rXte0tfrhda3uajZixIg5+vmSDk33339/+v7779Nmm21W5fZ+/fqlv/71r+mGG26o1fPGi3q7du3qaCublnIemlhbaqZu2lpps4+qmbZWuuyf6tbQbW1OT/aUdGgaMGBA+umnn6rcts0226QTTzwx7bzzzg22XQAAQPko6dDUuXPnam/v1KnTLO8DAAAouyXHAQAAGkpJ9zRV54MPPmjoTQAAAMqIniYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMghNAEAAOQQmgAAAHIITQAAADmEJgAAgBxCEwAAQA6hCQAAIIfQBAAAkENoAgAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAAAgh9AEAACQQ2gCAADIITQBAADkEJoAAAByCE0AAAA5hCYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMgh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osXBRVGEgKiTuv3SAQcckLWte++9Nzvo79evX3YiMk6mMfuz/lG32Cfj+OOss87KXtvihFCxp84++l/XXXdd9rp25513ZiNaokYxAiGGhI4dOzZ73au83/J/ol7RKxcBc7755svaW4w+iOO3r776Kh155JHzvG5OnzQh8YYYL/prrrlm9qIfwSle9Jm1OPvapUuXLDAV55nsvffe6bPPPsvO/vBLo0ePTmuttVZ2BjZeuOLNMGp11FFHZWfJYthevCFQ/dyS6J3bcssts/3ztttuS1dddVXFY+JAg/8Tb47zzz9/dvawWJf//Oc/qXfv3lmPwO67754uu+yybPhZBAFzT/4rAtO3335b0eaibnESLfbPqNvAgQPVrZITTzwxe82K/TECZQyfXXrppdP777+f3R8hnVnvp3HgH8cesR/G93HgHyEg9tMrr7xSW5tJHODHSe44yRh1K56MjF6np556Kt19993ZiUmqirZVDEvFMH7JJZekzTffPP3jH//IAnw8Zl4SmpqIOGB4++2306KLLpqdrYghLfGCLzjliwOLESNGZF8Xe0niIC1esByEVS/eECdPnpy9aMWLf7SxDTfcMG2yySZZ+7v99tuzF68YUsV/FXtCYr5cDKHdeOON09lnn52FzP79+6eDDz44Pf7440r2/0XvW5z9jzfJqNGHH35Y0Su3zz77pHXWWSd7o4yDsthfzT35r3gtK/bEfffdd9kJjdg3YxjVuuuum/7+979ngTN6CMq9btF2dt5552yYT3Eea5zYiKFm99xzT/Z91JH8k48xfDZev+LM//rrr5969uyZlltuufT0009nxyRxMFvuba3ySY0YYhbtKoYcxwnbON749a9/nbp27ZrVMV7vguOQVKUGUbvia1vxGKPY5v7yl79kQ5Hnad0KNBnvvPNOYeDAgYUZM2Zk3992222FHXfcsXDKKacURowY0dCbV1KKNXrllVcKvXv3Lnz11VcV9w0dOrSw2mqrFT755JMqPzNp0qRCOSvWbNSoUYXnnnsu+3rcuHGFu+66q/Dzzz9XPO6ss84qbLnllmVfr1mJfXHjjTcujBw5Mvv+vvvuK6yyyiqF3/zmN4XRo0fP8//HxuaKK64o7LbbbtnrWP/+/SvaYbj22msLW221VeH7779v0G0sNbE/brrppoU+ffoU3nvvvUK/fv0KU6ZMqbj/uuuuK2yxxRYVbZD/mj59evb58ccfL2y99daFYcOGZd9XbndUrdc+++xT2GSTTQrrr79+4dhjj61Snmhr8X7wxRdflH3Zim3o5ZdfLqy33nrZ61nR008/nR2vfffdd4X99tuvcPjhh5d9vWYWr/M9evQo9O3bt+K2yscehx12WGHvvfcuzEt6mpqQOEtx7LHHVpzNiUmFcUY2epxi7o6hev9VrFGcdT3nnHOyM4tF0V0ew4Iqz5E444wz0hNPPJHKWbFmyy67bHYWMcRwghjOGGfLYgJ1+P3vf58Nx4ghovxSzDOMoT/FYRkxTjvOakePQAwPMkQvVTlTGEOnovf30UcfzcarRzssDpeKnqhoa9HLzv+J2sT+GIvZvPDCC9l7QpzVjuG0xbrFZOoYjhY9nlRVfN3fYIMNsn002l3x9c9Z/6qiPUW94jUseoNj6GdxDlixFyDmCEdP1Lvvvlv2Ta34Hrrqqqtm829ibk4s1BLvoTGcMXqCYwW9mNP0+eefZ/uoNvd/og7x+v/HP/4xPfvss+nSSy/Nbi/21IWo4SeffJItTDWv6iY0NTHF1ZGKY9kjOMW44g8++CAb+xmrjfBf0c27wAILZF8XDyi+/PLL7I2gOKEwhra8+OKL2eoszPogI2pZHPIYwzJiOBDVi7AZITwOKCIQxEIasTz0DTfckB555BFl+/8HqcV9MoYv7rHHHtkqSZWHS0VgihBf+aRHuSvWJobMRjiKGsaQ2jipUbwvvo/g3rlz5wbe2tIU759xOYBTTjklGypVXIjE8LKqoj0VF3iIucGx8MM777yT3VdcOjtOBsWQsyWXXLLe/x9L+fU/XvvjJNm2226bzcmJRVviNS5EWI/jkphrrc39n2IdYj5wHNc+/PDD6aKLLqoytSKmDcT7aXFxjXnB6nlNVOUVpeJsbIzbjrk7SyyxRENvWskqHlAUJ7TG2YtYfjwWOYhx2REK4gDE+PaqYpJ5rJgUc5riRSvmOMUBhzfJXyq2n1hBL64Nts0222RhKU527LLLLlndYq4T/90n43UsAvgFF1yQ3fbyyy9nB2gxHyfmSkSv8EorraRkM4m2FAdhcfAQIw1iIYg4gRYHs7HaVLS5mBfGrE8ERX3idS0O0CKYxyJLVF+rsN5666Ubb7wxXXzxxemII47ITqDFpSji4H/llVdWukpibmb0OMVHiMWn4mRG9ND99a9/TUsttZTVaKsRoag4uiVWIYy6xXzgeK+4+eabs/00FtmYV5rFGL159uw0uMrLfBavl2Dpz+oV6xMLGcQLfRyoRXCKyYVxgBFnaou9KfxXXJcjDsiiPr/61a+ylafijTNqpq3NumZxIBZnsqu7TpO2Vr0YehErwMW1rpZZZpmsrd10003aWo4Il3H2f8CAAdkwvQhTcfIsVm0sDtlzImjWYtGM2E/jen4x9JhZi6HFsXhGvP7He2cc+McBbIR2bW3WYnGDWOQmTkDGcUcsflM8seE6V9WLhUXee++97KRjDGOM3qYITPP62ENoKgOVdzo74OzFjhhjs1dbbbVs6U+BKV8cdMWBWfRkxpCCmJ8TL1YO/PP3R4Gydm0tDvxjvlwMn419VFubM9HeYrXLOLgozg2zj855cIoeJ+Fy9qKnJA5oYw5TBCbvB3MmTgTFaqBxUiPmWkdbs3/O2etanEyLE9oxumVevx8ITY2EA6z6q1vMk4gJmnFWsRxfuGpas+qCeDm213L8m0uhrZVjT0lN61bd48utvdbm7535Z7S12fN+UDf7p7ZWmq9r5fOK2cgVG0GcKay80EOemUdeluMF+mpTt5gcffzxx1cciJXbRMya1qy6VaXKcdSvfXTe16zYQ1fualq36i7+W277aG32z3J77a+O94N5X7PiHPRy17wRvK4JTY3I0KFD029/+9uKq2vP6dme5557rizPWtSmbpV30nKum7ambqXa1oqPKef9M9hH1UxbK11e15rm65rQ1IjExOdYdSsmkef1HFVuSDEp85hjjklvvvlmKlc1qVtxJy33umlr6qatlTb7qJppa6XL/tlE6zZPL53LXF+VfGbHH398drXoWal81fK77roru+r0k08+WTb/E+qmZtpa6bJ/qpu2Vtrso2qmrc2a0FSCKgeft956qzBhwoSK7z/99NNCz549Cw8++OBsA9M666xTeOKJJwrlQt3UTFsrXfZPddPWSpt9VM20tXxCUwmf5Yngs9VWWxV22GGHwtChQ7PAFHr37l0499xzq7zIlXtgUjc109ZKl/1T3bS10mYfVTNtbfYsOV5CKi+VeOWVV6aPP/44bbrppumVV15Jzz//fOrSpUt2Rfe4unbv3r2zi5+tscYaVZ4jrit06aWXZhf82nbbbVM5UDc109ZKl/1T3bS10mYfVTNtbQ7NQbCinr366quF3XbbrTBixIiKHqQXXnihcNlllxVWX331bF7TmmuumfU2TZkypeIM0bPPPpvdXk49TJWpm5ppa6XL/qlu2lpps4+qmbaWT2hqYKNHj67SLf70008XevToUTjkkEMKEydOLEydOrXK47/44ovCFVdcUdhnn30KG2ywQeGHH36ouG/cuHGFYcOGFcqBuqmZtla67J/qpq2VNvuommlrNWfJ8QY0ZcqUNHDgwHTnnXdm37///vtZN/mKK66Y3n777fTdd9+lli1bZo8rLr34q1/9Kh133HHp1ltvTUsvvXQaNGhQdt+0adPSggsumNZdd93U1Kmbmmlrpcv+qW7aWmmzj6qZtlY7QlMDat26dRaCzj///GyuUsxF6tGjR+rVq1dacskl0+GHH55+/PHH7HERiooX7YrPcVu3bt3SuHHjstsiXJULdVMzba102T/VTVsrbfZRNdPWakdoamAnnHBCWnXVVdO//vWvtNJKK6WFFloorbPOOunMM89M8803X9pjjz2y4BShKIJT0dixY7OFIj7//PPsrFEMtSwn6qZm2lrpsn+qm7ZW2uyjaqat1ZzQ1AAqB5wxY8Zkw+z23HPPdPPNN6chQ4ZkPUkxzO7ss8/OzgjttddeacKECVV6k4o/d8YZZ2SPKV4ZuSlTNzXT1kqX/VPdtLXSZh9VM21t7nci6lHlRR+KizjECnjh8ssvL6y88sqFm2++ueIxr7/+emHTTTctnHjiib94rp9//rlQLtRNzbS10mX/VDdtrbTZR9VMW5t75TMRpkTO8hSvw3TjjTdm115q165dNp9pww03zOYyRY/RxRdfnH3ef//908SJE7PFItZcc81fPF/0MJUDdVMzba102T/VTVsrbfZRNdPW6kgdBC/mQPF6S2HgwIGFddZZp3DeeecVttlmm8IWW2xRuPfee7Oeo3hcLCkePU7du3cvHHfccRU/O23atLKrtbqpmbZWuuyf6qatlTb7qJppa3VHaKpncZ2lI488svDmm29W3Hb00UcXtt1224rgVLxe05VXXllxnabKL3zlSN3UTFsrXfZPddPWSpt9VM20tbknNNWjW265pbDZZpsVdt5558Knn35acXvMaaocnOKitpXNfIHbcqNuaqatlS77p7ppa6XNPqpm2lrdsHrePDTzMuA77bRTatOmTfrggw8qLmQbWrVqla6++uq0/PLLp0suuSS99NJLVX6unK7BFNRNzbS10mX/VDdtrbTZR9VMW5tH6ih8keO2224rPP7449nX48ePL2y11VaF7bffPlsZr/Kwu+hRuuSSS8py7lJ11E3NtLXSZf9UN22ttNlH1Uxbq1tC0zw2evTowqGHHlrYeOONC0OHDs1uGzt2bGHzzTcv7LTTTr8ITkXlHpzUTc20tdJl/1Q3ba202UfVTFure0LTPLoWQuUgFIs+9OrVq9CzZ8/CM888UxGcYtW8XXfdtfDSSy+V/UIP6qat1RdtTc20tdJl/1Q3ba20TS/j41yhaR6e5ansX//6V+GEE07IGlSxx2ncuHGFNdZYo9CnT595tRmNjrqpmbZWuuyf6qatlTb7qJppa/OO0DQPPPbYY4V11103G3pXWSTxI444ovCb3/ym8OKLL2a3TZgwoeyH4qmbtlbf7KNqpq2VLvunumlrpe2xMj3OtXrePLDQQgulNddcM51xxhlp2LBhFbevtdZaafvtt09ff/11Ouqoo9Krr76a2rdvn1q0aJGmT5+eyp26qZm2Vrrsn+qmrZU2+6iaaWvzWEOntqYytnNmb7/9duGwww7LxnNWTuJvvPFGoXfv3oX77ruvySTv2lA3NdPWSpf9U920tdJmH1Uzba3+NYt/5nUwa6riOkvNm/9fZ92jjz6aRo0alb788su08cYbp6233jp99tlnqX///unjjz9O/fr1S127dk0XXHBBWmSRRdK5556b/Vz0MEVPUzlRNzXT1kqX/VPdtLXSZh9VM22tYQhNdSAuSPvAAw+k9dZbL3300Udp8uTJacUVV0wDBw5Mn3/+ebrsssvS3//+9/SrX/0qzT///Nlj44K2kVebNWuWypW6qZm2Vrrsn+qmrZU2+6iaaWv1rAF6t5qUp59+OrtY7bvvvltx21133VXYeeeds2F4ccHaMWPGFF544YXsArfFIXlxezlTNzXT1kqX/VPdtLXSZh9VM22t/rWs75DWmH3//ffp22+/zYbTRU9S+PTTT9MSSyyRVlhhhTR16tSsB2n33XdP48ePT3fffXc2ZC+G5W2yySYVzxND8lq2LJ/Sq5uaaWuly/6pbtpaabOPqpm2VhqsnjeHTj/99NSrV6+06667Zh8333xzdnuEoghI8803XxaYpkyZklq3bp322GOPNHr06Gy43szKaQ6TuqmZtla67J/qpq2VNvuommlrpUNomgOHHnpotpjDYYcdlm699dZsrlIsHR523HHHLBgNHjw4+z4CU4ggtcoqq2TzmMqVuqmZtla67J/qpq2VNvuommlrpaV8xojV0lVXXZUt7DBo0KC08MILV1m5JhZyWGONNdKBBx6YbrrppjRx4sS0zz77pB9//DFdfvnlqU2bNmm11VZL5Ujd1ExbK132T3XT1kqbfVTNtLXSIzTNxnvvvZe22mqrisAUisuMx8p3EYzatm2bjjzyyHTjjTdmPVGdO3dOnTp1yr6Ox1ZeHrRcqJuaaWuly/6pbtpaabOPqpm2VnqEplmIoBOLPvzzn/9MBx98cMVtM4efsWPHpsceeyz17Nkzu1bTu+++mwWmbt26ZY+dNm1aWS36oG5qpq2VLvunumlrpc0+qmbaWukqr+6PGojAExehbd++fXr55Zcrbqsshud17NgxdenSJX399dfZ4yM8/frXv67oYSqnwBTUTc20tdJl/1Q3ba202UfVTFsrXULTLEQgiiXEl19++fTKK6+kkSNH/uIxxQvTxhLicdHaXxS3zIbkBXVTM22tdNk/1U1bK232UTXT1kpX+R3Vz6HifKXjjz8+G1t8ww03ZMP1ZjZmzJg0YcKEbDge6qat2UdLmdc1ddPWSpt9VM20tRLWABfUbXTuvffewmqrrVY44YQTCi+99FJ229SpUwtffPFF4eijjy7svffehWnTpjX0ZpYcdVMzba102T/VTVsrbfZRNdPWSkuz+Kehg1upixI9/fTTqV+/ftmFaWN1vDgbFPOVYgjeLbfckl3YNobpldOFa2dH3dRMWytd9k9109ZKm31UzbS10iI01cCXX36ZXnrppfT2229nC0CsvPLKadttt82CUrmtklcT6qZm2lrpsn+qm7ZW2uyjaqatlQahqQ7oYVK3+qKtqZu2Vtrso2qmrZUu+6e6zQ2hqRbd5cVV8yp/jbrVNW1N3eqLtqZu2lpps4+qmbbW8IQmAACAHJYcBwAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAGpwvRwAyk/Lht4AAJjZO++8k2699db0+uuvpzFjxqTFFlssbbjhhumoo45KSy+9dIMUbNCgQal169bpiCOOaJDfD0DD0dMEQEm544470r777pu+//77dPLJJ6frr78+C0uvvfZa2nPPPdP777/fINt1xRVXpMmTJzfI7wagYelpAqBkvPHGG+mCCy5IBxxwQDrjjDMqbl9//fXTVlttlXbdddd0+umnpwceeKBBtxOA8qKnCYCSceONN6YFFlgg9e7d+xf3Lbzwwum0005LW265ZZo0aVKaPn161iu10047pTXWWCNtttlmacCAAennn3+u+JkDDzww+6js1VdfTSuvvHL2OUQA69atW3rrrbfSPvvsk1ZfffW0+eabZ9tSFI8PV199dcXXV111Vdp6662z23r06JE22WSTdOaZZ2bbMmHChF8M7Vt33XX1VAE0UkITACWzyMI//vGPbO5S27Ztq33M9ttvn4477rjUrl27dPbZZ6eLLroo64G69tprs96p22+/PR177LE1XrBhxowZ6aSTTsqe/7rrrkvrrLNOuuSSS9ILL7yQ3X/PPfdkn2N4YPHr8OWXX6a///3v6fLLL099+/ZNhx56aBbannjiiSrP//DDD2fPPau/C4DSZngeACVh7NixWeBYaqmlZvvYESNGpD//+c/ZnKeY7xQ23njjbMGIPn36pOeffz717Nlzjn93hKwIW3vttVf2ffQKPf300+m5555Lm266aVprrbWy2xdffPGKr8O0adPSqaeemtZbb72K29Zee+0sJBWf65///GcaNWpU6t+/fw2qAUAp0dMEQElo0aJF9jmG3c1OLAoRdthhhyq3x/fxPMWhdzURYacoVsmL4YAxDHB2Vl111Srf77HHHmnYsGHpiy++yL5/8MEHU5cuXao8PwCNi9AEQElYcMEF0/zzz58NeZuVCDHjx4/PPsKiiy5a5f6WLVumjh07/mJO0Zxo06ZNle+bN28+R8P8YpsrKw7Di96m6Dl7/PHH0+67717j7QGgdAhNAJSMWEwheokqL+ZQ2b333ps22GCDiu+//fbbKvdPnTo1G+YXwalo5p6rOek9mhsRorbbbrssLMWcqPh9u+yyyzz9nQDMW0ITACXjsMMOS+PGjUsDBw78xX0RkG666abUtWvXbNW68Nhjj1V5THwfISnmJIX27dun0aNH/2JZ89qInqc5FQtGfPjhh+mWW25JG220UercuXOtficApcFCEACUjFhkoVevXllo+uijj7LrMkWv0X/+859sCfDogYr7VlhhhbTbbrulK6+8MlvGu3v37mn48OHZ8t9xTadYvCHE0uFDhw7NVtnbYostsrlGDz30UK22rUOHDtmiDq+//nqVhR+qE6Et5jHF3KtYWQ+Axk1oAqCk/P73v8+umxTXYLrwwguz+UtLLLFEdh2mY445Jvs6xEVwl1122XT//fen66+/Pls576CDDspWwSv2CsWiDJ9++mm2GMPdd9+dhasIWvvtt1+Ntyt+d1xv6cgjj0x//etfZ/v42N4xY8ZkS6ID0Lg1K9T0YhYAQK54a42V/GKO1umnn65aAI2cniYAqCM//vhjGjJkSHrnnXfSZ599lg488EC1BWgChCYAqCOxbHkMA5wxY0Y2tHDppZdWW4AmwPA8AACAHJYcBwAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAAAgh9AEAACQQ2gCAABIs/b/AF1tvKUDoPojAAAAAElFTkSuQmCC", + "text/plain": [ + "<Figure size 1000x600 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot bar chart\n", + "plt.figure(figsize=(10, 6))\n", + "sns.barplot(data=country_counts, x=\"Country\", y=\"RecordCount\", palette=\"viridis\")\n", + "plt.title(\"Record Count by Country\")\n", + "plt.xticks(rotation=45)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "887b2525", + "metadata": {}, + "source": [ + "## Pivot Table Example" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1e26e06b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Age</th>\n", + " </tr>\n", + " <tr>\n", + " <th>Country</th>\n", + " <th></th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>Country_0</th>\n", + " <td>43.7</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_1</th>\n", + " <td>43.8</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_2</th>\n", + " <td>51.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_3</th>\n", + " <td>38.2</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_4</th>\n", + " <td>44.3</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_5</th>\n", + " <td>48.3</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_6</th>\n", + " <td>47.3</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_7</th>\n", + " <td>41.1</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_8</th>\n", + " <td>40.5</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_9</th>\n", + " <td>47.1</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Age\n", + "Country \n", + "Country_0 43.7\n", + "Country_1 43.8\n", + "Country_2 51.0\n", + "Country_3 38.2\n", + "Country_4 44.3\n", + "Country_5 48.3\n", + "Country_6 47.3\n", + "Country_7 41.1\n", + "Country_8 40.5\n", + "Country_9 47.1" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# If 'Age' exists, average age by country (example only if dataset has relevant column)\n", + "if \"Age\" in df.columns:\n", + " age_pivot = df.pivot_table(index=\"Country\", values=\"Age\", aggfunc=\"mean\")\n", + " display(age_pivot)\n", + "else:\n", + " print(\"No 'Age' column in dataset to pivot on.\")" + ] + }, + { + "cell_type": "markdown", + "id": "d9793c4e", + "metadata": {}, + "source": [ + "\n", + "## Summary\n", + "\n", + "In this notebook, we:\n", + "- Loaded a sample CSV dataset\n", + "- Explored its structure\n", + "- Grouped and counted records by country\n", + "- Visualized results with a bar chart\n", + "- Created a pivot table (if applicable)\n", + "\n", + "Try modifying this notebook for your own datasets or audit use cases!\n", + " " + ] + } + ], + "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.14.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} new file mode 100644 @@ -0,0 +1,526 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4c3a8609", + "metadata": {}, + "source": [ + "\n", + "# RACF Access Report Analysis\n", + "\n", + "This notebook demonstrates how to parse a RACF-like mainframe access report stored in a fixed-width text format, extract user access details, identify unusual access configurations, and summarize the results for follow-up.\n", + "\n", + "We'll be working with the file `sample_racf_data.txt`.\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "a0b3e2b2", + "metadata": {}, + "source": [ + "\n", + "## Install Dependencies\n", + "\n", + "If you haven't already installed `pandas`, run:\n", + "\n", + "```bash\n", + "pip install pandas\n", + "```\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "ca527b49", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import re" + ] + }, + { + "cell_type": "markdown", + "id": "d1694149", + "metadata": {}, + "source": [ + "## Load RACF Report" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4891d98b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "LISTGRP *\n", + "INFORMATION FOR GROUP PAYROLLB\n", + "SUPERIOR GROUP=RESEARCH OWNER=IBMUSER CREATED=06.123\n", + "NO INSTALLATION DATA\n", + "NO MODEL DATA SET\n", + "TERMUACC\n", + "NO SUBGROUPS\n", + "USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS=\n", + "IBMUSER JOIN 000000 ALTER\n", + "CONNECT ATTRIBUTES=NONE\n", + "REVOKE DATE=NONE RESUME DATE=NONE\n", + "DAF0 CREATE 000000 READ\n", + "CONNECT ATTRIBUTES=NONE\n", + "REVOKE DATE=NONE RESUME DATE=NONE\n", + "IA0 CREATE 000000 READ\n", + "CONNECT ATTRIBUTES=ADSP SPECIAL OPERATIONS\n", + "REVOKE DATE=NONE RESUME DATE=NONE\n", + "AEH0 CREATE 000000 READ\n", + "CONNECT ATTRIBUTES=NONE\n", + "REVOKE DATE=NONE RESUME DATE=NONE\n" + ] + } + ], + "source": [ + "with open(\"sample_racf_data.txt\", \"r\") as file:\n", + " lines = file.readlines()\n", + "\n", + "# Preview first 20 lines\n", + "for line in lines[:20]:\n", + " print(line.strip())" + ] + }, + { + "cell_type": "markdown", + "id": "9d5ee64a", + "metadata": {}, + "source": [ + "## Parse User Access Records" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "18f06bc9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Group</th>\n", + " <th>User</th>\n", + " <th>Access</th>\n", + " <th>Access Count</th>\n", + " <th>Universal Access</th>\n", + " <th>Attributes</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>0</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>PAYROLLB</td>\n", + " <td>DAF0</td>\n", + " <td>CREATE</td>\n", + " <td>0</td>\n", + " <td>READ</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IA0</td>\n", + " <td>CREATE</td>\n", + " <td>0</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>PAYROLLB</td>\n", + " <td>AEH0</td>\n", + " <td>CREATE</td>\n", + " <td>0</td>\n", + " <td>READ</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>RESEARCH</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>0</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Group User Access Access Count Universal Access \\\n", + "0 PAYROLLB IBMUSER JOIN 0 ALTER \n", + "1 PAYROLLB DAF0 CREATE 0 READ \n", + "2 PAYROLLB IA0 CREATE 0 READ \n", + "3 PAYROLLB AEH0 CREATE 0 READ \n", + "4 RESEARCH IBMUSER JOIN 0 ALTER \n", + "\n", + " Attributes \n", + "0 NONE \n", + "1 NONE \n", + "2 ADSP SPECIAL OPERATIONS \n", + "3 NONE \n", + "4 NONE " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Initialize lists to hold parsed records\n", + "records = []\n", + "current_group = \"\"\n", + "\n", + "for i, line in enumerate(lines):\n", + " if \"INFORMATION FOR GROUP\" in line:\n", + " current_group = line.strip().split()[-1]\n", + "\n", + " # Identify user lines: starts with a non-empty, non-space string followed by access keywords\n", + " match = re.match(r\"^\\s*(\\S+)\\s+(JOIN|CREATE|CONNECT|USE)\\s+(\\d{6})\\s+(\\S+)\", line)\n", + " if match:\n", + " user, access, access_count, universal_access = match.groups()\n", + "\n", + " # Look ahead for CONNECT ATTRIBUTES line\n", + " attr_line = lines[i + 1].strip() if (i + 1) < len(lines) else \"\"\n", + " attr_match = re.search(r\"CONNECT ATTRIBUTES=(.*)\", attr_line)\n", + " attributes = attr_match.group(1) if attr_match else \"NONE\"\n", + "\n", + " records.append(\n", + " {\n", + " \"Group\": current_group,\n", + " \"User\": user,\n", + " \"Access\": access,\n", + " \"Access Count\": int(access_count),\n", + " \"Universal Access\": universal_access,\n", + " \"Attributes\": attributes,\n", + " }\n", + " )\n", + "\n", + "# Convert to DataFrame\n", + "df = pd.DataFrame(records)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "ab5546a6", + "metadata": {}, + "source": [ + "## Analyze Access Data" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d1b8269a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Access\n", + "CREATE 5\n", + "JOIN 4\n", + "USE 3\n", + "CONNECT 1\n", + "Name: count, dtype: int64\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Group</th>\n", + " <th>User</th>\n", + " <th>Access</th>\n", + " <th>Access Count</th>\n", + " <th>Universal Access</th>\n", + " <th>Attributes</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>0</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IA0</td>\n", + " <td>CREATE</td>\n", + " <td>0</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>RESEARCH</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>0</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>6</th>\n", + " <td>RESEARCH</td>\n", + " <td>IA0</td>\n", + " <td>CONNECT</td>\n", + " <td>4</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Group User Access Access Count Universal Access \\\n", + "0 PAYROLLB IBMUSER JOIN 0 ALTER \n", + "2 PAYROLLB IA0 CREATE 0 READ \n", + "4 RESEARCH IBMUSER JOIN 0 ALTER \n", + "6 RESEARCH IA0 CONNECT 4 READ \n", + "\n", + " Attributes \n", + "0 NONE \n", + "2 ADSP SPECIAL OPERATIONS \n", + "4 NONE \n", + "6 ADSP SPECIAL OPERATIONS " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Count users by Access type\n", + "access_summary = df[\"Access\"].value_counts()\n", + "print(access_summary)\n", + "\n", + "# Identify users with ALTER access or SPECIAL OPERATIONS attribute\n", + "anomalies = df[\n", + " (df[\"Universal Access\"] == \"ALTER\")\n", + " | (df[\"Attributes\"].str.contains(\"SPECIAL OPERATIONS\"))\n", + "]\n", + "\n", + "anomalies" + ] + }, + { + "cell_type": "markdown", + "id": "38201382", + "metadata": {}, + "source": [ + "## Prepare Follow-Up Report" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a885710c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Group</th>\n", + " <th>User</th>\n", + " <th>Access</th>\n", + " <th>Universal Access</th>\n", + " <th>Attributes</th>\n", + " <th>Notes</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " <td>Review access appropriateness with system owner</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IA0</td>\n", + " <td>CREATE</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " <td>Review access appropriateness with system owner</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>RESEARCH</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " <td>Review access appropriateness with system owner</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>RESEARCH</td>\n", + " <td>IA0</td>\n", + " <td>CONNECT</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " <td>Review access appropriateness with system owner</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Group User Access Universal Access Attributes \\\n", + "0 PAYROLLB IBMUSER JOIN ALTER NONE \n", + "1 PAYROLLB IA0 CREATE READ ADSP SPECIAL OPERATIONS \n", + "2 RESEARCH IBMUSER JOIN ALTER NONE \n", + "3 RESEARCH IA0 CONNECT READ ADSP SPECIAL OPERATIONS \n", + "\n", + " Notes \n", + "0 Review access appropriateness with system owner \n", + "1 Review access appropriateness with system owner \n", + "2 Review access appropriateness with system owner \n", + "3 Review access appropriateness with system owner " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create a concise follow-up report\n", + "follow_up = anomalies[\n", + " [\"Group\", \"User\", \"Access\", \"Universal Access\", \"Attributes\"]\n", + "].copy()\n", + "follow_up[\"Notes\"] = \"Review access appropriateness with system owner\"\n", + "\n", + "follow_up.reset_index(drop=True, inplace=True)\n", + "follow_up" + ] + }, + { + "cell_type": "markdown", + "id": "0158f787", + "metadata": {}, + "source": [ + "\n", + "## Summary\n", + "\n", + "In this notebook, we:\n", + "- Parsed a RACF-like access report from a fixed-width text file\n", + "- Extracted key fields into a structured DataFrame\n", + "- Analyzed access configurations for high-risk permissions\n", + "- Summarized anomalies requiring follow-up with system owners\n", + "\n", + "Use this as a starting point for mainframe audit automation projects!\n", + " " + ] + } + ], + "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.14.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} new file mode 100644 @@ -0,0 +1,720 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2bc817b1", + "metadata": {}, + "source": [ + "\n", + "# Basic Data Analysis & Visualization\n", + "\n", + "This notebook demonstrates how to load a sample dataset, perform quick exploratory analysis, group and pivot the data, and create visualizations.\n", + "\n", + "We’re using a CSV file from: https://sample-files.com/downloads/data/csv/basic-data.csv\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "a92fdfd1", + "metadata": {}, + "source": [ + "\n", + "## Install Dependencies\n", + "\n", + "If you haven't installed the required libraries, run:\n", + "\n", + "```bash\n", + "pip install pandas matplotlib seaborn\n", + "```\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b5d7308a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting pandas\n", + " Downloading pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl.metadata (91 kB)\n", + "Collecting matplotlib\n", + " Downloading matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl.metadata (52 kB)\n", + "Collecting seaborn\n", + " Downloading seaborn-0.13.2-py3-none-any.whl.metadata (5.4 kB)\n", + "Collecting numpy>=1.26.0 (from pandas)\n", + " Downloading numpy-2.4.0-cp314-cp314-macosx_14_0_arm64.whl.metadata (6.6 kB)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /opt/homebrew/Cellar/jupyterlab/4.5.1/libexec/lib/python3.14/site-packages (from pandas) (2.9.0.post0)\n", + "Collecting pytz>=2020.1 (from pandas)\n", + " Using cached pytz-2025.2-py2.py3-none-any.whl.metadata (22 kB)\n", + "Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/Cellar/jupyterlab/4.5.1/libexec/lib/python3.14/site-packages (from pandas) (2025.3)\n", + "Collecting contourpy>=1.0.1 (from matplotlib)\n", + " Downloading contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl.metadata (5.5 kB)\n", + "Collecting cycler>=0.10 (from matplotlib)\n", + " Downloading cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)\n", + "Collecting fonttools>=4.22.0 (from matplotlib)\n", + " Downloading fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl.metadata (114 kB)\n", + "Collecting kiwisolver>=1.3.1 (from matplotlib)\n", + " Downloading kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl.metadata (6.3 kB)\n", + "Requirement already satisfied: packaging>=20.0 in /opt/homebrew/Cellar/jupyterlab/4.5.1/libexec/lib/python3.14/site-packages (from matplotlib) (25.0)\n", + "Collecting pillow>=8 (from matplotlib)\n", + " Downloading pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl.metadata (8.8 kB)\n", + "Collecting pyparsing>=3 (from matplotlib)\n", + " Downloading pyparsing-3.3.1-py3-none-any.whl.metadata (5.6 kB)\n", + "Requirement already satisfied: six>=1.5 in /opt/homebrew/Cellar/jupyterlab/4.5.1/libexec/lib/python3.14/site-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)\n", + "Downloading pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl (10.8 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m10.8/10.8 MB\u001b[0m \u001b[31m5.0 MB/s\u001b[0m \u001b[33m0:00:02\u001b[0m.9 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m:02\u001b[0m\n", + "\u001b[?25hDownloading matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl (8.2 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m8.2/8.2 MB\u001b[0m \u001b[31m6.5 MB/s\u001b[0m \u001b[33m0:00:01\u001b[0mm \u001b[31m6.6 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hDownloading seaborn-0.13.2-py3-none-any.whl (294 kB)\n", + "Downloading contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl (273 kB)\n", + "Downloading cycler-0.12.1-py3-none-any.whl (8.3 kB)\n", + "Downloading fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl (2.8 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.8/2.8 MB\u001b[0m \u001b[31m6.9 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m7.6 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hDownloading kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl (64 kB)\n", + "Downloading numpy-2.4.0-cp314-cp314-macosx_14_0_arm64.whl (5.2 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.2/5.2 MB\u001b[0m \u001b[31m7.6 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m7.8 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hDownloading pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl (4.7 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.7/4.7 MB\u001b[0m \u001b[31m7.9 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m7.9 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hDownloading pyparsing-3.3.1-py3-none-any.whl (121 kB)\n", + "Using cached pytz-2025.2-py2.py3-none-any.whl (509 kB)\n", + "Installing collected packages: pytz, pyparsing, pillow, numpy, kiwisolver, fonttools, cycler, pandas, contourpy, matplotlib, seaborn\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m11/11\u001b[0m [seaborn]━\u001b[0m \u001b[32m10/11\u001b[0m [seaborn]atplotlib]\n", + "\u001b[1A\u001b[2KSuccessfully installed contourpy-1.3.3 cycler-0.12.1 fonttools-4.61.1 kiwisolver-1.4.9 matplotlib-3.10.8 numpy-2.4.0 pandas-2.3.3 pillow-12.0.0 pyparsing-3.3.1 pytz-2025.2 seaborn-0.13.2\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install pandas matplotlib seaborn" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0632140a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Matplotlib is building the font cache; this may take a moment.\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "# Set plot style\n", + "sns.set(style=\"whitegrid\")" + ] + }, + { + "cell_type": "markdown", + "id": "d326b5b6", + "metadata": {}, + "source": [ + "## Load Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5d5de24b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>ID</th>\n", + " <th>Name</th>\n", + " <th>Age</th>\n", + " <th>Country</th>\n", + " <th>Email</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>1</td>\n", + " <td>Name_1</td>\n", + " <td>62</td>\n", + " <td>Country_1</td>\n", + " <td>email_1@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>2</td>\n", + " <td>Name_2</td>\n", + " <td>48</td>\n", + " <td>Country_2</td>\n", + " <td>email_2@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>3</td>\n", + " <td>Name_3</td>\n", + " <td>61</td>\n", + " <td>Country_3</td>\n", + " <td>email_3@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>4</td>\n", + " <td>Name_4</td>\n", + " <td>32</td>\n", + " <td>Country_4</td>\n", + " <td>email_4@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>5</td>\n", + " <td>Name_5</td>\n", + " <td>69</td>\n", + " <td>Country_5</td>\n", + " <td>email_5@example.com</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " ID Name Age Country Email\n", + "0 1 Name_1 62 Country_1 email_1@example.com\n", + "1 2 Name_2 48 Country_2 email_2@example.com\n", + "2 3 Name_3 61 Country_3 email_3@example.com\n", + "3 4 Name_4 32 Country_4 email_4@example.com\n", + "4 5 Name_5 69 Country_5 email_5@example.com" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load the dataset from URL\n", + "url = \"https://sample-files.com/downloads/data/csv/basic-data.csv\"\n", + "df = pd.read_csv(url, skiprows=1)\n", + "df.columns = df.columns.str.strip()\n", + "\n", + "# Preview the data\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "e7f7c2e7", + "metadata": {}, + "source": [ + "## Basic Exploration" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "aca7309e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(100, 5)\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>ID</th>\n", + " <th>Name</th>\n", + " <th>Age</th>\n", + " <th>Country</th>\n", + " <th>Email</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>count</th>\n", + " <td>100.000000</td>\n", + " <td>100</td>\n", + " <td>100.000000</td>\n", + " <td>100</td>\n", + " <td>100</td>\n", + " </tr>\n", + " <tr>\n", + " <th>unique</th>\n", + " <td>NaN</td>\n", + " <td>100</td>\n", + " <td>NaN</td>\n", + " <td>10</td>\n", + " <td>100</td>\n", + " </tr>\n", + " <tr>\n", + " <th>top</th>\n", + " <td>NaN</td>\n", + " <td>Name_1</td>\n", + " <td>NaN</td>\n", + " <td>Country_1</td>\n", + " <td>email_1@example.com</td>\n", + " </tr>\n", + " <tr>\n", + " <th>freq</th>\n", + " <td>NaN</td>\n", + " <td>1</td>\n", + " <td>NaN</td>\n", + " <td>10</td>\n", + " <td>1</td>\n", + " </tr>\n", + " <tr>\n", + " <th>mean</th>\n", + " <td>50.500000</td>\n", + " <td>NaN</td>\n", + " <td>44.530000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>std</th>\n", + " <td>29.011492</td>\n", + " <td>NaN</td>\n", + " <td>15.190012</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>min</th>\n", + " <td>1.000000</td>\n", + " <td>NaN</td>\n", + " <td>18.000000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>25%</th>\n", + " <td>25.750000</td>\n", + " <td>NaN</td>\n", + " <td>32.000000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>50%</th>\n", + " <td>50.500000</td>\n", + " <td>NaN</td>\n", + " <td>43.500000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>75%</th>\n", + " <td>75.250000</td>\n", + " <td>NaN</td>\n", + " <td>59.250000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " <tr>\n", + " <th>max</th>\n", + " <td>100.000000</td>\n", + " <td>NaN</td>\n", + " <td>69.000000</td>\n", + " <td>NaN</td>\n", + " <td>NaN</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " ID Name Age Country Email\n", + "count 100.000000 100 100.000000 100 100\n", + "unique NaN 100 NaN 10 100\n", + "top NaN Name_1 NaN Country_1 email_1@example.com\n", + "freq NaN 1 NaN 10 1\n", + "mean 50.500000 NaN 44.530000 NaN NaN\n", + "std 29.011492 NaN 15.190012 NaN NaN\n", + "min 1.000000 NaN 18.000000 NaN NaN\n", + "25% 25.750000 NaN 32.000000 NaN NaN\n", + "50% 50.500000 NaN 43.500000 NaN NaN\n", + "75% 75.250000 NaN 59.250000 NaN NaN\n", + "max 100.000000 NaN 69.000000 NaN NaN" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Check shape and summary stats\n", + "print(df.shape)\n", + "df.describe(include=\"all\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e775e42f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ID Name Age Country Email\n", + "0 1 Name_1 62 Country_1 email_1@example.com\n", + "1 2 Name_2 48 Country_2 email_2@example.com\n", + "2 3 Name_3 61 Country_3 email_3@example.com\n", + "3 4 Name_4 32 Country_4 email_4@example.com\n", + "4 5 Name_5 69 Country_5 email_5@example.com\n", + ".. ... ... ... ... ...\n", + "95 96 Name_96 60 Country_6 email_96@example.com\n", + "96 97 Name_97 26 Country_7 email_97@example.com\n", + "97 98 Name_98 52 Country_8 email_98@example.com\n", + "98 99 Name_99 24 Country_9 email_99@example.com\n", + "99 100 Name_100 55 Country_0 email_100@example.com\n", + "\n", + "[100 rows x 5 columns]\n" + ] + } + ], + "source": [ + "print(df)" + ] + }, + { + "cell_type": "markdown", + "id": "dc10be72", + "metadata": {}, + "source": [ + "## Grouping and Pivoting Data" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f87ea8af", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Country</th>\n", + " <th>RecordCount</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>Country_1</td>\n", + " <td>10</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>Country_2</td>\n", + " <td>10</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>Country_3</td>\n", + " <td>10</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>Country_4</td>\n", + " <td>10</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>Country_5</td>\n", + " <td>10</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Country RecordCount\n", + "0 Country_1 10\n", + "1 Country_2 10\n", + "2 Country_3 10\n", + "3 Country_4 10\n", + "4 Country_5 10" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Example: Group by 'Country' and count number of records\n", + "country_counts = df[\"Country\"].value_counts().reset_index()\n", + "country_counts.columns = [\"Country\", \"RecordCount\"]\n", + "country_counts.head()" + ] + }, + { + "cell_type": "markdown", + "id": "f156d6de", + "metadata": {}, + "source": [ + "## Visualizing Record Counts by Country" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d644fc06", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/z_/x3_6fwb174s0c7wcsd813s6r0000gn/T/ipykernel_10080/1795714619.py:3: FutureWarning: \n", + "\n", + "Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.\n", + "\n", + " sns.barplot(data=country_counts, x=\"Country\", y=\"RecordCount\", palette=\"viridis\")\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA00AAAJUCAYAAADJrc3LAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAASopJREFUeJzt3QeYVOXZP+CXKiCiiIrGimLD2AV7sGvsvcZeY8OgQbFhrKh8ig0/rNhb7DG2iEZjxxg1ihoU7NgoIqC0+V/P+f6z2cXlwC7L7uzOfV/Xsrszs7NnH94zc37nLadZoVAoJAAAAKrVvPqbAQAAEJoAAABmQ08TAABADqEJAAAgh9AEAACQQ2gCAADIITQBAADkEJoAAAByCE0AUIlrvs+a2gDlSmgCKAEHHnhgWnnllat8rLLKKmmdddZJu+++e3r44YdTqfv888+z7X7ggQdm+9ivv/46XXLJJWm77bZLa665Ztpkk03SMccck4YNG5Ya0jPPPJNOPfXU3MecdtppaYsttphn2/Dzzz+nIUOGpD322COtu+66qUePHmnfffdNDz30UIOGlv/85z9pv/32a7DfD9CQWjbobwegQrdu3VK/fv0qvp8+fXoaPXp0dgDdp0+ftNBCC6WePXs2+oq98cYb6bjjjksdO3ZMBx10UOrSpUsaN25cuueee7LweNFFF6Vdd921QbYtat2Qvvvuu3TEEUekr776KqvFGmuskWbMmJGeffbZLKxFqDzvvPNSs2bN6n3bnnjiifTmm2/W++8FKAVCE0CJaN++fVprrbV+cftvfvObtOGGG2Y9OI09NEU4Oumkk9Jyyy2Xbr755tS2bduK+7bddtt01FFHpbPPPjvreVpkkUVSuYlergjKESCjRkWbbbZZ+tWvfpUuu+yytPnmm6ctt9yyQbcToNwYngdQ4uabb77UunXrKr0L0ftw3XXXpa233jr9+te/zgLHbbfd9oufjSFdu+22WzYELg68/+d//idNmTKl4v533nknHX744Wn99dfPhgLGELkYhlX06quvZkPu7r777uxgPR7z4osvZvc99dRTaeedd856Q+J3vP/++7P9W2J7vvnmm3T66adXCUyhefPm6ZRTTkkHHHBA+vHHHytuj9+3//77Z0PVYjtPPvnkrCem6Kqrrsq2cWZxW9xXeejg448/nk488cS09tprZ8Pe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oQkAAKB29DQBAADkEJoAAAByCE0AAAA5hCYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMghNAEAAOQQmgAAAHIITQAAADmEJgAAgBxCEwAAQA6hCQAAIIfQBAAAkENoAgAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAAAgh9AEAACQQ2gCAADIITQBAADkEJoAAAByCE0AAAA5hCYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMghNAEAAOQQmgAAAHIITQAAADmEJgAAgBxCEwAAQA6hCQAAIIfQBAAAkENoAgAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAAAgh9AEAACQQ2gCAADIITQBAADkEJoAAAByCE0AAAA5hCYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMghNAEAAOQQmgAAAHIITQAAAHUdmvr27Zs+++yzau/7+OOP0zHHHFObpwUAACg5Lef0gV9++WXF1w899FDaaqutUosWLX7xuOeffz699NJLdbeFAAAAjSE0/elPf8oCUdHxxx9f7eMKhULaeOON62brAAAAGktoOvfcc7MepAhFp59+evr973+flllmmSqPad68eerQoUNaf/3158W2AgAAlG5o6ty5c9ptt92yr5s1a5Z69uyZFl544Xm5bQAAAI0nNFUW4Sl6nN577700adKk7OuZde/evS62DwAAoPGFprfffjv16tUrjR49Ovu+GJqiByq+js/Dhw+v2y0FAABoLKHpoosuSi1btsw+L7744tlcJgAAgKaoVqHp3XffTZdddlm27Hh9iWXOr7vuuuz6ULEARaze99vf/rbefj8AAFCeatVF1KlTp2qv0TSvPPzww+mMM85IBxxwQHrsscfSjjvumHr37p3efPPNetsGAACgPNUqNO2///5p8ODB2SIQ81rMkbriiivSQQcdlIWm6GWK5c432mij9Nprr83z3w8AAJS3Wg3P++STT9JHH32UXcR2xRVXTG3atKlyfywEccstt9TJBo4cOTJ98cUXaaeddqpy+4033lgnzw8AADBPQtMqq6xS8f3MS45XtwT53ISmEL1ahx9+eLbM+VJLLZX1Nm2xxRa1es7Yvln1kkXga9u2bSpXkydPrvH/n5rVvGbqpq3V1/6prWlr2lr98R5aPzULjj0cr9VVWyuu/D1PQtNtt92W6suPP/6YfT711FOzxR9OOeWU9OSTT6Zjjz023XzzzWnDDTes8XNOnTp1lkuiR2Dq1q1bKlcRUqNR1YSa1bxm6qat1df+qa1pa9pa/fEeWj81C449HK/VZVtr3br1vAlN9alVq1bZ5+hliovqhlVXXTXrcaptaIrn7Nq1a7X3zUnSbMq6dOlSq56mclabmgV109a0tfrhda1+aha8rnld09bqh9e1uqvZiBEj5ujnaxWaYljc7F4Yn3nmmVQXOnfunH1eaaWVqtweoee5556r1XPGtrdr165Otq+pKeehibWlZuqmrZU2+6iaaWuly/6pbg3d1ub0ZE+tQlOPHj1+8QsmTpyY3nnnnfTzzz+ngw8+ONWV1VZbLc0///zprbfeSuutt17F7R9++GG2kh4AAMC8VKvQ1L9//1nOFYq5RrUZmzorsTLfEUccka655pqs12mNNdbIrtX04osvpiFDhtTZ7wEAAJjnc5pirlBcT6lv377ppJNOqrPnjSAWXWqXX355+vrrr9MKK6yQrrrqqrT++uvX2e8AAACol4Ugxo8fnw3Vq2uHHnpo9gEAAFDyoemhhx76xW3Tp09Po0ePTrfffnuVuUcAAABlF5pOO+20Wd639tprp7POOmtutgkAAKBxh6bqlhOP1fTat2+fOnToUBfbBQAA0HhD05JLLlnx9UcffZQmTJiQOnbsKDABAABNTq0XgvjLX/6SLr744vTdd99V3LbIIoukk08+Oe266651tX0AAACNLzQNHTo0/fGPf0wbbLBB6t27dxaWvvnmm/TII49ky40vtNBCabPNNqv7rQUAAGgMoenaa69N2223XXbdpMr22GOP9Ic//CENHjxYaAIAAJqE5rX5oQ8//DDttttu1d4Xt7///vtzu10AAACNNzTFog9xEdvqjBs3LrVu3XputwsAAKDxhqYNN9wwXX311dnFbCv76quv0jXXXJM23njjuto+AACAxjenKRZ/iPlL22yzTXYx21gIIlbRe/PNN9OCCy6YraAHAABQtj1Niy66aHrwwQfTgQcemCZPnpz+/e9/Z5/j+7i98nWcAAAAyvI6TZ06dcqWHQcAAGjKatTTNG3atHT77benp59+usrt06dPz1bNGzJkSJoxY0ZdbyMAAEDph6apU6emY489Nl1wwQXZ3KXKxowZk4Wl/v37p+OPPz4LUQAAAGUVmu655570yiuvpAEDBqQ+ffr8Yo7Tww8/nIWm559/Pt1///3zYlsBAABKNzQ98MAD6ZBDDkk77LDDLB+z6667pr322ivdd999dbV9AAAAjSM0ffLJJ2mDDTaY7eN69uyZRo0aNbfbBQAA0LhCU8uWLbN5TXPyuGbNms3tdgEAADSu0LTiiiumV199dbaPe+2119JSSy01t9sFAADQuELTLrvsku6666709ttvz/Ix7777brrjjjvSb3/727raPgAAgMZxcds999wz/eUvf0kHHnhg9vVmm22W9SjFUuNffPFFtmrevffem1ZeeeXsMQAAAGUVmmKe0uDBg9OFF16YLT9+5513VtxXKBSyuUyxcl7v3r1TmzZt5tX2AgAAlGZoChGGzj333HTSSSell19+OY0ePTq1aNEiLbnkktnKegsssMC821IAAIBSD01FCy+8cO71mgAAAMouND300EM1euK40C0AAEDZhKbTTjutyvfFazHFfKaZbwtCEwAAUFah6Zlnnqn4evjw4emPf/xjOvbYY7PlxRdbbLE0duzYNHTo0HTVVVeliy66aF5tLwAAQGmGpljsoeiEE07IAtORRx5ZcVvnzp3Tfvvtl6ZMmZIuvfTS1LNnz7rfWgAAgFK9uG1lH330UerWrVu19y2//PLp888/n9vtAgAAaLyhabnllkuPPvpotffFNZxWWmmlud0uAACAxrvk+HHHHZd69eqVRo0alTbffPPUsWPH9N1336WnnnoqjRgxIl1//fV1v6UAAACNJTRts8026ZprrkmDBg1KAwcOzFbQa968eVp77bXTkCFD0nrrrVf3WwoAANBYQtPLL7+cNtpoo7TFFlukn3/+OY0fPz4ttNBCqXXr1nW/hQAAAI1tTlOsnhdD8cJ8882XLTkuMAEAAE1RrUJThw4dUps2bep+awAAAJrC8Lyjjz46nX/++WnkyJFplVVWSe3atfvFY7p3714X2wcAAND4QlO/fv2yz5dffnn2uVmzZhX3xaIQ8f3w4cPrahsBAAAaV2i69dZb635LAAAAmkpo6tGjR91vCQAAQFMJTSHmM1155ZXptddeSz/88EN2gdu4PlNc+HaFFVao260EAABoTKFpxIgRad99900tWrTIrtW0yCKLpG+//TY9++yz6bnnnkv33Xef4AQAAJRvaBowYEBaaqml0m233ZYWWGCBitsnTJiQDj744GyBiKuvvroutxMAAKDxXKfp9ddfT8ccc0yVwBTi+6OOOiq7HwAAoGxDU8uWLdN8881X7X2tW7dOU6ZMmdvtAgAAaLyhafXVV0933nlndk2myuL7O+64I/3617+uq+0DAABoULWa09SrV6+03377pZ133jltt912adFFF80WgnjiiSeyVfVuvvnmut9SAACAxhKaoqfphhtuSP/zP/+TLfgQPUzNmjXLepiuv/761L1797rfUgAAgMZ0naYNNtgg3X333dn8pbhOU4cOHdK0adN+sTgEAABA2c1pmjp1aurXr1/ae++9U9u2bVPnzp3Tm2++mTbccMN08cUXpxkzZtT9lgIAADSW0HTVVVelRx55JO2www4Vt3Xr1i2dcsop6d57782G7gEAAJTt8LxHH300nXrqqWnfffetuG2hhRZKhxxySLYc+a233ppdrwkAAKAse5rGjh2bll566WrvW3755dPo0aPndrsAAAAab2iKYPTkk09We9/QoUPTsssuO7fbBQAA0HiH5x100EHptNNOS+PGjUtbbbVV6tSpUxozZkx69tln0+OPP54uuuiiut9SAACAxhKadt111zRx4sQ0aNCg9NRTT1Xc3rFjx3TWWWdl9wMAAJT1dZoOOOCAtP/++6eRI0dmPU5xnaYYtte8ea1G/AEAAJSkuUo4cVHbCE0ffPBBWnjhhdOoUaNSoVCou60DAABorD1N1157bRo8eHD66aefUrNmzdIaa6yRBg4cmK2sd9NNN2U9TwAAAGXZ03T77bdnF7g99NBDs4vZFnuXfve736XPPvssXXHFFXW9nQAAAI0nNN12223ZxWt79eqVVltttYrbe/bsmU466aRs2XEAAICyDU1ffvll6tGjR7X3xWIQ33333dxuFwAAQOMNTUsssUR68803q73v3//+d3Y/AABA2S4Eseeee2Zzmtq0aZM222yz7LZJkyalJ598MlscIuY6AQAAlG1oOvLII9Pnn3+eBgwYkH2Egw46KFsQYuedd05HH310XW8nAABA4wlNscT4ueeemw477LD0yiuvZBe3XWCBBVL37t1T165d01133ZVd/BYAAKCsQtPzzz+fHnzwwSw07bLLLtlqecstt1zF/cOGDUu77757drFboQkAACir0PTII4+kPn36pFatWqXWrVunxx9/PF155ZVp6623znqazj///PTYY4+lFi1amNMEAACUX2i65ZZb0pprrpluvPHGLDT17ds3XXPNNWnFFVfMQtJXX32VNt1003T66aenLl26zNutBgAAKLXQNGrUqHTeeeel9u3bZ98ff/zxafvtt0/HHntsmjJlSrriiivStttuOy+3FQAAoHRDUywpXvn6S0suuWS2Wl7Lli2zoXudOnWaV9sIAABQ+he3jYAU85WKil//4Q9/EJgAAIAma45D06wstthidbMlAAAATTE0xfLjAAAATVWNrtN0zjnnVCwEEcP1wllnnZXmn3/+XwSpWG0PAACgbEJT9+7dq4SlWd1W3fcAAABNPjTddtttqaGNHDky7b777lnvVnwGAAAo+TlN9WXq1KnplFNOyZY+BwAAqC+NJjRdddVVFfOpAAAA6kujCE2vv/56uueee1L//v0belMAAIAyU6PV8xrCDz/8kPr06ZPOPPPMtMQSS9TJc8ZCFbMa5hcr/7Vt2zaVq8mTJ9d4IQ81q3nN1E1bq6/9U1vT1rS1+uM9tH5qFhx7OF6rq7YWt83JJZRKPjTFMudrr7122mmnnep0ftTw4cOrvS8CU7du3VK5isU2olHVhJrVvGbqpq3V1/6prWlr2lr98R5aPzULjj0cr9VlW2vdunXjDk0PPfRQGjZsWHr00Ufr9HlbtWqVunbtWu195X6x3i5dutSqp6mc1aZmQd20NW2tfnhdq5+aBa9rXte0tfrhda3uajZixIg5+vmSDk33339/+v7779Nmm21W5fZ+/fqlv/71r+mGG26o1fPGi3q7du3qaCublnIemlhbaqZu2lpps4+qmbZWuuyf6tbQbW1OT/aUdGgaMGBA+umnn6rcts0226QTTzwx7bzzzg22XQAAQPko6dDUuXPnam/v1KnTLO8DAAAouyXHAQAAGkpJ9zRV54MPPmjoTQAAAMqIniYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMghNAEAAOQQmgAAAHIITQAAADmEJgAAgBxCEwAAQA6hCQAAIIfQBAAAkENoAgAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAAAgh9AEAACQQ2gCAADIITQBAADkEJoAAAByCE0AAAA5hCYAAIAcQhMAAEAOoQkAACCH0AQAAJBDaAIAAMgh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osXBRVGEgKiTuv3SAQcckLWte++9Nzvo79evX3YiMk6mMfuz/lG32Cfj+OOss87KXtvihFCxp84++l/XXXdd9rp25513ZiNaokYxAiGGhI4dOzZ73au83/J/ol7RKxcBc7755svaW4w+iOO3r776Kh155JHzvG5OnzQh8YYYL/prrrlm9qIfwSle9Jm1OPvapUuXLDAV55nsvffe6bPPPsvO/vBLo0ePTmuttVZ2BjZeuOLNMGp11FFHZWfJYthevCFQ/dyS6J3bcssts/3ztttuS1dddVXFY+JAg/8Tb47zzz9/dvawWJf//Oc/qXfv3lmPwO67754uu+yybPhZBAFzT/4rAtO3335b0eaibnESLfbPqNvAgQPVrZITTzwxe82K/TECZQyfXXrppdP777+f3R8hnVnvp3HgH8cesR/G93HgHyEg9tMrr7xSW5tJHODHSe44yRh1K56MjF6np556Kt19993ZiUmqirZVDEvFMH7JJZekzTffPP3jH//IAnw8Zl4SmpqIOGB4++2306KLLpqdrYghLfGCLzjliwOLESNGZF8Xe0niIC1esByEVS/eECdPnpy9aMWLf7SxDTfcMG2yySZZ+7v99tuzF68YUsV/FXtCYr5cDKHdeOON09lnn52FzP79+6eDDz44Pf7440r2/0XvW5z9jzfJqNGHH35Y0Su3zz77pHXWWSd7o4yDsthfzT35r3gtK/bEfffdd9kJjdg3YxjVuuuum/7+979ngTN6CMq9btF2dt5552yYT3Eea5zYiKFm99xzT/Z91JH8k48xfDZev+LM//rrr5969uyZlltuufT0009nxyRxMFvuba3ySY0YYhbtKoYcxwnbON749a9/nbp27ZrVMV7vguOQVKUGUbvia1vxGKPY5v7yl79kQ5Hnad0KNBnvvPNOYeDAgYUZM2Zk3992222FHXfcsXDKKacURowY0dCbV1KKNXrllVcKvXv3Lnz11VcV9w0dOrSw2mqrFT755JMqPzNp0qRCOSvWbNSoUYXnnnsu+3rcuHGFu+66q/Dzzz9XPO6ss84qbLnllmVfr1mJfXHjjTcujBw5Mvv+vvvuK6yyyiqF3/zmN4XRo0fP8//HxuaKK64o7LbbbtnrWP/+/SvaYbj22msLW221VeH7779v0G0sNbE/brrppoU+ffoU3nvvvUK/fv0KU6ZMqbj/uuuuK2yxxRYVbZD/mj59evb58ccfL2y99daFYcOGZd9XbndUrdc+++xT2GSTTQrrr79+4dhjj61Snmhr8X7wxRdflH3Zim3o5ZdfLqy33nrZ61nR008/nR2vfffdd4X99tuvcPjhh5d9vWYWr/M9evQo9O3bt+K2yscehx12WGHvvfcuzEt6mpqQOEtx7LHHVpzNiUmFcUY2epxi7o6hev9VrFGcdT3nnHOyM4tF0V0ew4Iqz5E444wz0hNPPJHKWbFmyy67bHYWMcRwghjOGGfLYgJ1+P3vf58Nx4ghovxSzDOMoT/FYRkxTjvOakePQAwPMkQvVTlTGEOnovf30UcfzcarRzssDpeKnqhoa9HLzv+J2sT+GIvZvPDCC9l7QpzVjuG0xbrFZOoYjhY9nlRVfN3fYIMNsn002l3x9c9Z/6qiPUW94jUseoNj6GdxDlixFyDmCEdP1Lvvvlv2Ta34Hrrqqqtm829ibk4s1BLvoTGcMXqCYwW9mNP0+eefZ/uoNvd/og7x+v/HP/4xPfvss+nSSy/Nbi/21IWo4SeffJItTDWv6iY0NTHF1ZGKY9kjOMW44g8++CAb+xmrjfBf0c27wAILZF8XDyi+/PLL7I2gOKEwhra8+OKL2eoszPogI2pZHPIYwzJiOBDVi7AZITwOKCIQxEIasTz0DTfckB555BFl+/8HqcV9MoYv7rHHHtkqSZWHS0VgihBf+aRHuSvWJobMRjiKGsaQ2jipUbwvvo/g3rlz5wbe2tIU759xOYBTTjklGypVXIjE8LKqoj0VF3iIucGx8MM777yT3VdcOjtOBsWQsyWXXLLe/x9L+fU/XvvjJNm2226bzcmJRVviNS5EWI/jkphrrc39n2IdYj5wHNc+/PDD6aKLLqoytSKmDcT7aXFxjXnB6nlNVOUVpeJsbIzbjrk7SyyxRENvWskqHlAUJ7TG2YtYfjwWOYhx2REK4gDE+PaqYpJ5rJgUc5riRSvmOMUBhzfJXyq2n1hBL64Nts0222RhKU527LLLLlndYq4T/90n43UsAvgFF1yQ3fbyyy9nB2gxHyfmSkSv8EorraRkM4m2FAdhcfAQIw1iIYg4gRYHs7HaVLS5mBfGrE8ERX3idS0O0CKYxyJLVF+rsN5666Ubb7wxXXzxxemII47ITqDFpSji4H/llVdWukpibmb0OMVHiMWn4mRG9ND99a9/TUsttZTVaKsRoag4uiVWIYy6xXzgeK+4+eabs/00FtmYV5rFGL159uw0uMrLfBavl2Dpz+oV6xMLGcQLfRyoRXCKyYVxgBFnaou9KfxXXJcjDsiiPr/61a+ylafijTNqpq3NumZxIBZnsqu7TpO2Vr0YehErwMW1rpZZZpmsrd10003aWo4Il3H2f8CAAdkwvQhTcfIsVm0sDtlzImjWYtGM2E/jen4x9JhZi6HFsXhGvP7He2cc+McBbIR2bW3WYnGDWOQmTkDGcUcsflM8seE6V9WLhUXee++97KRjDGOM3qYITPP62ENoKgOVdzo74OzFjhhjs1dbbbVs6U+BKV8cdMWBWfRkxpCCmJ8TL1YO/PP3R4Gydm0tDvxjvlwMn419VFubM9HeYrXLOLgozg2zj855cIoeJ+Fy9qKnJA5oYw5TBCbvB3MmTgTFaqBxUiPmWkdbs3/O2etanEyLE9oxumVevx8ITY2EA6z6q1vMk4gJmnFWsRxfuGpas+qCeDm213L8m0uhrZVjT0lN61bd48utvdbm7535Z7S12fN+UDf7p7ZWmq9r5fOK2cgVG0GcKay80EOemUdeluMF+mpTt5gcffzxx1cciJXbRMya1qy6VaXKcdSvfXTe16zYQ1fualq36i7+W277aG32z3J77a+O94N5X7PiHPRy17wRvK4JTY3I0KFD029/+9uKq2vP6dme5557rizPWtSmbpV30nKum7ambqXa1oqPKef9M9hH1UxbK11e15rm65rQ1IjExOdYdSsmkef1HFVuSDEp85hjjklvvvlmKlc1qVtxJy33umlr6qatlTb7qJppa6XL/tlE6zZPL53LXF+VfGbHH398drXoWal81fK77roru+r0k08+WTb/E+qmZtpa6bJ/qpu2Vtrso2qmrc2a0FSCKgeft956qzBhwoSK7z/99NNCz549Cw8++OBsA9M666xTeOKJJwrlQt3UTFsrXfZPddPWSpt9VM20tXxCUwmf5Yngs9VWWxV22GGHwtChQ7PAFHr37l0499xzq7zIlXtgUjc109ZKl/1T3bS10mYfVTNtbfYsOV5CKi+VeOWVV6aPP/44bbrppumVV15Jzz//fOrSpUt2Rfe4unbv3r2zi5+tscYaVZ4jrit06aWXZhf82nbbbVM5UDc109ZKl/1T3bS10mYfVTNtbQ7NQbCinr366quF3XbbrTBixIiKHqQXXnihcNlllxVWX331bF7TmmuumfU2TZkypeIM0bPPPpvdXk49TJWpm5ppa6XL/qlu2lpps4+qmbaWT2hqYKNHj67SLf70008XevToUTjkkEMKEydOLEydOrXK47/44ovCFVdcUdhnn30KG2ywQeGHH36ouG/cuHGFYcOGFcqBuqmZtla67J/qpq2VNvuommlrNWfJ8QY0ZcqUNHDgwHTnnXdm37///vtZN/mKK66Y3n777fTdd9+lli1bZo8rLr34q1/9Kh133HHp1ltvTUsvvXQaNGhQdt+0adPSggsumNZdd93U1Kmbmmlrpcv+qW7aWmmzj6qZtlY7QlMDat26dRaCzj///GyuUsxF6tGjR+rVq1dacskl0+GHH55+/PHH7HERiooX7YrPcVu3bt3SuHHjstsiXJULdVMzba102T/VTVsrbfZRNdPWakdoamAnnHBCWnXVVdO//vWvtNJKK6WFFloorbPOOunMM89M8803X9pjjz2y4BShKIJT0dixY7OFIj7//PPsrFEMtSwn6qZm2lrpsn+qm7ZW2uyjaqat1ZzQ1AAqB5wxY8Zkw+z23HPPdPPNN6chQ4ZkPUkxzO7ss8/OzgjttddeacKECVV6k4o/d8YZZ2SPKV4ZuSlTNzXT1kqX/VPdtLXSZh9VM21t7nci6lHlRR+KizjECnjh8ssvL6y88sqFm2++ueIxr7/+emHTTTctnHjiib94rp9//rlQLtRNzbS10mX/VDdtrbTZR9VMW5t75TMRpkTO8hSvw3TjjTdm115q165dNp9pww03zOYyRY/RxRdfnH3ef//908SJE7PFItZcc81fPF/0MJUDdVMzba102T/VTVsrbfZRNdPW6kgdBC/mQPF6S2HgwIGFddZZp3DeeecVttlmm8IWW2xRuPfee7Oeo3hcLCkePU7du3cvHHfccRU/O23atLKrtbqpmbZWuuyf6qatlTb7qJppa3VHaKpncZ2lI488svDmm29W3Hb00UcXtt1224rgVLxe05VXXllxnabKL3zlSN3UTFsrXfZPddPWSpt9VM20tbknNNWjW265pbDZZpsVdt5558Knn35acXvMaaocnOKitpXNfIHbcqNuaqatlS77p7ppa6XNPqpm2lrdsHrePDTzMuA77bRTatOmTfrggw8qLmQbWrVqla6++uq0/PLLp0suuSS99NJLVX6unK7BFNRNzbS10mX/VDdtrbTZR9VMW5tH6ih8keO2224rPP7449nX48ePL2y11VaF7bffPlsZr/Kwu+hRuuSSS8py7lJ11E3NtLXSZf9UN22ttNlH1Uxbq1tC0zw2evTowqGHHlrYeOONC0OHDs1uGzt2bGHzzTcv7LTTTr8ITkXlHpzUTc20tdJl/1Q3ba202UfVTFure0LTPLoWQuUgFIs+9OrVq9CzZ8/CM888UxGcYtW8XXfdtfDSSy+V/UIP6qat1RdtTc20tdJl/1Q3ba20TS/j41yhaR6e5ansX//6V+GEE07IGlSxx2ncuHGFNdZYo9CnT595tRmNjrqpmbZWuuyf6qatlTb7qJppa/OO0DQPPPbYY4V11103G3pXWSTxI444ovCb3/ym8OKLL2a3TZgwoeyH4qmbtlbf7KNqpq2VLvunumlrpe2xMj3OtXrePLDQQgulNddcM51xxhlp2LBhFbevtdZaafvtt09ff/11Ouqoo9Krr76a2rdvn1q0aJGmT5+eyp26qZm2Vrrsn+qmrZU2+6iaaWvzWEOntqYytnNmb7/9duGwww7LxnNWTuJvvPFGoXfv3oX77ruvySTv2lA3NdPWSpf9U920tdJmH1Uzba3+NYt/5nUwa6riOkvNm/9fZ92jjz6aRo0alb788su08cYbp6233jp99tlnqX///unjjz9O/fr1S127dk0XXHBBWmSRRdK5556b/Vz0MEVPUzlRNzXT1kqX/VPdtLXSZh9VM22tYQhNdSAuSPvAAw+k9dZbL3300Udp8uTJacUVV0wDBw5Mn3/+ebrsssvS3//+9/SrX/0qzT///Nlj44K2kVebNWuWypW6qZm2Vrrsn+qmrZU2+6iaaWv1rAF6t5qUp59+OrtY7bvvvltx21133VXYeeeds2F4ccHaMWPGFF544YXsArfFIXlxezlTNzXT1kqX/VPdtLXSZh9VM22t/rWs75DWmH3//ffp22+/zYbTRU9S+PTTT9MSSyyRVlhhhTR16tSsB2n33XdP48ePT3fffXc2ZC+G5W2yySYVzxND8lq2LJ/Sq5uaaWuly/6pbtpaabOPqpm2VhqsnjeHTj/99NSrV6+06667Zh8333xzdnuEoghI8803XxaYpkyZklq3bp322GOPNHr06Gy43szKaQ6TuqmZtla67J/qpq2VNvuommlrpUNomgOHHnpotpjDYYcdlm699dZsrlIsHR523HHHLBgNHjw4+z4CU4ggtcoqq2TzmMqVuqmZtla67J/qpq2VNvuommlrpaV8xojV0lVXXZUt7DBo0KC08MILV1m5JhZyWGONNdKBBx6YbrrppjRx4sS0zz77pB9//DFdfvnlqU2bNmm11VZL5Ujd1ExbK132T3XT1kqbfVTNtLXSIzTNxnvvvZe22mqrisAUisuMx8p3EYzatm2bjjzyyHTjjTdmPVGdO3dOnTp1yr6Ox1ZeHrRcqJuaaWuly/6pbtpaabOPqpm2VnqEplmIoBOLPvzzn/9MBx98cMVtM4efsWPHpsceeyz17Nkzu1bTu+++mwWmbt26ZY+dNm1aWS36oG5qpq2VLvunumlrpc0+qmbaWukqr+6PGojAExehbd++fXr55Zcrbqsshud17NgxdenSJX399dfZ4yM8/frXv67oYSqnwBTUTc20tdJl/1Q3ba202UfVTFsrXULTLEQgiiXEl19++fTKK6+kkSNH/uIxxQvTxhLicdHaXxS3zIbkBXVTM22tdNk/1U1bK232UTXT1kpX+R3Vz6HifKXjjz8+G1t8ww03ZMP1ZjZmzJg0YcKEbDge6qat2UdLmdc1ddPWSpt9VM20tRLWABfUbXTuvffewmqrrVY44YQTCi+99FJ229SpUwtffPFF4eijjy7svffehWnTpjX0ZpYcdVMzba102T/VTVsrbfZRNdPWSkuz+Kehg1upixI9/fTTqV+/ftmFaWN1vDgbFPOVYgjeLbfckl3YNobpldOFa2dH3dRMWytd9k9109ZKm31UzbS10iI01cCXX36ZXnrppfT2229nC0CsvPLKadttt82CUrmtklcT6qZm2lrpsn+qm7ZW2uyjaqatlQahqQ7oYVK3+qKtqZu2Vtrso2qmrZUu+6e6zQ2hqRbd5cVV8yp/jbrVNW1N3eqLtqZu2lpps4+qmbbW8IQmAACAHJYcBwAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAGpwvRwAyk/Lht4AAJjZO++8k2699db0+uuvpzFjxqTFFlssbbjhhumoo45KSy+9dIMUbNCgQal169bpiCOOaJDfD0DD0dMEQEm544470r777pu+//77dPLJJ6frr78+C0uvvfZa2nPPPdP777/fINt1xRVXpMmTJzfI7wagYelpAqBkvPHGG+mCCy5IBxxwQDrjjDMqbl9//fXTVlttlXbdddd0+umnpwceeKBBtxOA8qKnCYCSceONN6YFFlgg9e7d+xf3Lbzwwum0005LW265ZZo0aVKaPn161iu10047pTXWWCNtttlmacCAAennn3+u+JkDDzww+6js1VdfTSuvvHL2OUQA69atW3rrrbfSPvvsk1ZfffW0+eabZ9tSFI8PV199dcXXV111Vdp6662z23r06JE22WSTdOaZZ2bbMmHChF8M7Vt33XX1VAE0UkITACWzyMI//vGPbO5S27Ztq33M9ttvn4477rjUrl27dPbZZ6eLLroo64G69tprs96p22+/PR177LE1XrBhxowZ6aSTTsqe/7rrrkvrrLNOuuSSS9ILL7yQ3X/PPfdkn2N4YPHr8OWXX6a///3v6fLLL099+/ZNhx56aBbannjiiSrP//DDD2fPPau/C4DSZngeACVh7NixWeBYaqmlZvvYESNGpD//+c/ZnKeY7xQ23njjbMGIPn36pOeffz717Nlzjn93hKwIW3vttVf2ffQKPf300+m5555Lm266aVprrbWy2xdffPGKr8O0adPSqaeemtZbb72K29Zee+0sJBWf65///GcaNWpU6t+/fw2qAUAp0dMEQElo0aJF9jmG3c1OLAoRdthhhyq3x/fxPMWhdzURYacoVsmL4YAxDHB2Vl111Srf77HHHmnYsGHpiy++yL5/8MEHU5cuXao8PwCNi9AEQElYcMEF0/zzz58NeZuVCDHjx4/PPsKiiy5a5f6WLVumjh07/mJO0Zxo06ZNle+bN28+R8P8YpsrKw7Di96m6Dl7/PHH0+67717j7QGgdAhNAJSMWEwheokqL+ZQ2b333ps22GCDiu+//fbbKvdPnTo1G+YXwalo5p6rOek9mhsRorbbbrssLMWcqPh9u+yyyzz9nQDMW0ITACXjsMMOS+PGjUsDBw78xX0RkG666abUtWvXbNW68Nhjj1V5THwfISnmJIX27dun0aNH/2JZ89qInqc5FQtGfPjhh+mWW25JG220UercuXOtficApcFCEACUjFhkoVevXllo+uijj7LrMkWv0X/+859sCfDogYr7VlhhhbTbbrulK6+8MlvGu3v37mn48OHZ8t9xTadYvCHE0uFDhw7NVtnbYostsrlGDz30UK22rUOHDtmiDq+//nqVhR+qE6Et5jHF3KtYWQ+Axk1oAqCk/P73v8+umxTXYLrwwguz+UtLLLFEdh2mY445Jvs6xEVwl1122XT//fen66+/Pls576CDDspWwSv2CsWiDJ9++mm2GMPdd9+dhasIWvvtt1+Ntyt+d1xv6cgjj0x//etfZ/v42N4xY8ZkS6ID0Lg1K9T0YhYAQK54a42V/GKO1umnn65aAI2cniYAqCM//vhjGjJkSHrnnXfSZ599lg488EC1BWgChCYAqCOxbHkMA5wxY0Y2tHDppZdWW4AmwPA8AACAHJYcBwAAyCE0AQAA5BCaAAAAcghNAAAAOYQmAACAHEITAABADqEJAAAgh9AEAACQQ2gCAABIs/b/AF1tvKUDoPojAAAAAElFTkSuQmCC", + "text/plain": [ + "<Figure size 1000x600 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot bar chart\n", + "plt.figure(figsize=(10, 6))\n", + "sns.barplot(data=country_counts, x=\"Country\", y=\"RecordCount\", palette=\"viridis\")\n", + "plt.title(\"Record Count by Country\")\n", + "plt.xticks(rotation=45)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "887b2525", + "metadata": {}, + "source": [ + "## Pivot Table Example" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1e26e06b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Age</th>\n", + " </tr>\n", + " <tr>\n", + " <th>Country</th>\n", + " <th></th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>Country_0</th>\n", + " <td>43.7</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_1</th>\n", + " <td>43.8</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_2</th>\n", + " <td>51.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_3</th>\n", + " <td>38.2</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_4</th>\n", + " <td>44.3</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_5</th>\n", + " <td>48.3</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_6</th>\n", + " <td>47.3</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_7</th>\n", + " <td>41.1</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_8</th>\n", + " <td>40.5</td>\n", + " </tr>\n", + " <tr>\n", + " <th>Country_9</th>\n", + " <td>47.1</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Age\n", + "Country \n", + "Country_0 43.7\n", + "Country_1 43.8\n", + "Country_2 51.0\n", + "Country_3 38.2\n", + "Country_4 44.3\n", + "Country_5 48.3\n", + "Country_6 47.3\n", + "Country_7 41.1\n", + "Country_8 40.5\n", + "Country_9 47.1" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# If 'Age' exists, average age by country (example only if dataset has relevant column)\n", + "if \"Age\" in df.columns:\n", + " age_pivot = df.pivot_table(index=\"Country\", values=\"Age\", aggfunc=\"mean\")\n", + " display(age_pivot)\n", + "else:\n", + " print(\"No 'Age' column in dataset to pivot on.\")" + ] + }, + { + "cell_type": "markdown", + "id": "d9793c4e", + "metadata": {}, + "source": [ + "\n", + "## Summary\n", + "\n", + "In this notebook, we:\n", + "- Loaded a sample CSV dataset\n", + "- Explored its structure\n", + "- Grouped and counted records by country\n", + "- Visualized results with a bar chart\n", + "- Created a pivot table (if applicable)\n", + "\n", + "Try modifying this notebook for your own datasets or audit use cases!\n", + " " + ] + } + ], + "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.14.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} new file mode 100644 @@ -0,0 +1,526 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4c3a8609", + "metadata": {}, + "source": [ + "\n", + "# RACF Access Report Analysis\n", + "\n", + "This notebook demonstrates how to parse a RACF-like mainframe access report stored in a fixed-width text format, extract user access details, identify unusual access configurations, and summarize the results for follow-up.\n", + "\n", + "We'll be working with the file `sample_racf_data.txt`.\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "a0b3e2b2", + "metadata": {}, + "source": [ + "\n", + "## Install Dependencies\n", + "\n", + "If you haven't already installed `pandas`, run:\n", + "\n", + "```bash\n", + "pip install pandas\n", + "```\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "ca527b49", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import re" + ] + }, + { + "cell_type": "markdown", + "id": "d1694149", + "metadata": {}, + "source": [ + "## Load RACF Report" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4891d98b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "LISTGRP *\n", + "INFORMATION FOR GROUP PAYROLLB\n", + "SUPERIOR GROUP=RESEARCH OWNER=IBMUSER CREATED=06.123\n", + "NO INSTALLATION DATA\n", + "NO MODEL DATA SET\n", + "TERMUACC\n", + "NO SUBGROUPS\n", + "USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS=\n", + "IBMUSER JOIN 000000 ALTER\n", + "CONNECT ATTRIBUTES=NONE\n", + "REVOKE DATE=NONE RESUME DATE=NONE\n", + "DAF0 CREATE 000000 READ\n", + "CONNECT ATTRIBUTES=NONE\n", + "REVOKE DATE=NONE RESUME DATE=NONE\n", + "IA0 CREATE 000000 READ\n", + "CONNECT ATTRIBUTES=ADSP SPECIAL OPERATIONS\n", + "REVOKE DATE=NONE RESUME DATE=NONE\n", + "AEH0 CREATE 000000 READ\n", + "CONNECT ATTRIBUTES=NONE\n", + "REVOKE DATE=NONE RESUME DATE=NONE\n" + ] + } + ], + "source": [ + "with open(\"sample_racf_data.txt\", \"r\") as file:\n", + " lines = file.readlines()\n", + "\n", + "# Preview first 20 lines\n", + "for line in lines[:20]:\n", + " print(line.strip())" + ] + }, + { + "cell_type": "markdown", + "id": "9d5ee64a", + "metadata": {}, + "source": [ + "## Parse User Access Records" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "18f06bc9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Group</th>\n", + " <th>User</th>\n", + " <th>Access</th>\n", + " <th>Access Count</th>\n", + " <th>Universal Access</th>\n", + " <th>Attributes</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>0</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>PAYROLLB</td>\n", + " <td>DAF0</td>\n", + " <td>CREATE</td>\n", + " <td>0</td>\n", + " <td>READ</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IA0</td>\n", + " <td>CREATE</td>\n", + " <td>0</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>PAYROLLB</td>\n", + " <td>AEH0</td>\n", + " <td>CREATE</td>\n", + " <td>0</td>\n", + " <td>READ</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>RESEARCH</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>0</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Group User Access Access Count Universal Access \\\n", + "0 PAYROLLB IBMUSER JOIN 0 ALTER \n", + "1 PAYROLLB DAF0 CREATE 0 READ \n", + "2 PAYROLLB IA0 CREATE 0 READ \n", + "3 PAYROLLB AEH0 CREATE 0 READ \n", + "4 RESEARCH IBMUSER JOIN 0 ALTER \n", + "\n", + " Attributes \n", + "0 NONE \n", + "1 NONE \n", + "2 ADSP SPECIAL OPERATIONS \n", + "3 NONE \n", + "4 NONE " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Initialize lists to hold parsed records\n", + "records = []\n", + "current_group = \"\"\n", + "\n", + "for i, line in enumerate(lines):\n", + " if \"INFORMATION FOR GROUP\" in line:\n", + " current_group = line.strip().split()[-1]\n", + "\n", + " # Identify user lines: starts with a non-empty, non-space string followed by access keywords\n", + " match = re.match(r\"^\\s*(\\S+)\\s+(JOIN|CREATE|CONNECT|USE)\\s+(\\d{6})\\s+(\\S+)\", line)\n", + " if match:\n", + " user, access, access_count, universal_access = match.groups()\n", + "\n", + " # Look ahead for CONNECT ATTRIBUTES line\n", + " attr_line = lines[i + 1].strip() if (i + 1) < len(lines) else \"\"\n", + " attr_match = re.search(r\"CONNECT ATTRIBUTES=(.*)\", attr_line)\n", + " attributes = attr_match.group(1) if attr_match else \"NONE\"\n", + "\n", + " records.append(\n", + " {\n", + " \"Group\": current_group,\n", + " \"User\": user,\n", + " \"Access\": access,\n", + " \"Access Count\": int(access_count),\n", + " \"Universal Access\": universal_access,\n", + " \"Attributes\": attributes,\n", + " }\n", + " )\n", + "\n", + "# Convert to DataFrame\n", + "df = pd.DataFrame(records)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "ab5546a6", + "metadata": {}, + "source": [ + "## Analyze Access Data" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d1b8269a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Access\n", + "CREATE 5\n", + "JOIN 4\n", + "USE 3\n", + "CONNECT 1\n", + "Name: count, dtype: int64\n" + ] + }, + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Group</th>\n", + " <th>User</th>\n", + " <th>Access</th>\n", + " <th>Access Count</th>\n", + " <th>Universal Access</th>\n", + " <th>Attributes</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>0</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IA0</td>\n", + " <td>CREATE</td>\n", + " <td>0</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>RESEARCH</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>0</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " </tr>\n", + " <tr>\n", + " <th>6</th>\n", + " <td>RESEARCH</td>\n", + " <td>IA0</td>\n", + " <td>CONNECT</td>\n", + " <td>4</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Group User Access Access Count Universal Access \\\n", + "0 PAYROLLB IBMUSER JOIN 0 ALTER \n", + "2 PAYROLLB IA0 CREATE 0 READ \n", + "4 RESEARCH IBMUSER JOIN 0 ALTER \n", + "6 RESEARCH IA0 CONNECT 4 READ \n", + "\n", + " Attributes \n", + "0 NONE \n", + "2 ADSP SPECIAL OPERATIONS \n", + "4 NONE \n", + "6 ADSP SPECIAL OPERATIONS " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Count users by Access type\n", + "access_summary = df[\"Access\"].value_counts()\n", + "print(access_summary)\n", + "\n", + "# Identify users with ALTER access or SPECIAL OPERATIONS attribute\n", + "anomalies = df[\n", + " (df[\"Universal Access\"] == \"ALTER\")\n", + " | (df[\"Attributes\"].str.contains(\"SPECIAL OPERATIONS\"))\n", + "]\n", + "\n", + "anomalies" + ] + }, + { + "cell_type": "markdown", + "id": "38201382", + "metadata": {}, + "source": [ + "## Prepare Follow-Up Report" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a885710c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>Group</th>\n", + " <th>User</th>\n", + " <th>Access</th>\n", + " <th>Universal Access</th>\n", + " <th>Attributes</th>\n", + " <th>Notes</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " <td>Review access appropriateness with system owner</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>PAYROLLB</td>\n", + " <td>IA0</td>\n", + " <td>CREATE</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " <td>Review access appropriateness with system owner</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>RESEARCH</td>\n", + " <td>IBMUSER</td>\n", + " <td>JOIN</td>\n", + " <td>ALTER</td>\n", + " <td>NONE</td>\n", + " <td>Review access appropriateness with system owner</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>RESEARCH</td>\n", + " <td>IA0</td>\n", + " <td>CONNECT</td>\n", + " <td>READ</td>\n", + " <td>ADSP SPECIAL OPERATIONS</td>\n", + " <td>Review access appropriateness with system owner</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " Group User Access Universal Access Attributes \\\n", + "0 PAYROLLB IBMUSER JOIN ALTER NONE \n", + "1 PAYROLLB IA0 CREATE READ ADSP SPECIAL OPERATIONS \n", + "2 RESEARCH IBMUSER JOIN ALTER NONE \n", + "3 RESEARCH IA0 CONNECT READ ADSP SPECIAL OPERATIONS \n", + "\n", + " Notes \n", + "0 Review access appropriateness with system owner \n", + "1 Review access appropriateness with system owner \n", + "2 Review access appropriateness with system owner \n", + "3 Review access appropriateness with system owner " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create a concise follow-up report\n", + "follow_up = anomalies[\n", + " [\"Group\", \"User\", \"Access\", \"Universal Access\", \"Attributes\"]\n", + "].copy()\n", + "follow_up[\"Notes\"] = \"Review access appropriateness with system owner\"\n", + "\n", + "follow_up.reset_index(drop=True, inplace=True)\n", + "follow_up" + ] + }, + { + "cell_type": "markdown", + "id": "0158f787", + "metadata": {}, + "source": [ + "\n", + "## Summary\n", + "\n", + "In this notebook, we:\n", + "- Parsed a RACF-like access report from a fixed-width text file\n", + "- Extracted key fields into a structured DataFrame\n", + "- Analyzed access configurations for high-risk permissions\n", + "- Summarized anomalies requiring follow-up with system owners\n", + "\n", + "Use this as a starting point for mainframe audit automation projects!\n", + " " + ] + } + ], + "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.14.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} new file mode 100644 @@ -0,0 +1,54 @@ +LISTGRP * +INFORMATION FOR GROUP PAYROLLB + SUPERIOR GROUP=RESEARCH OWNER=IBMUSER CREATED=06.123 + NO INSTALLATION DATA + NO MODEL DATA SET + TERMUACC + NO SUBGROUPS + USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS= + IBMUSER JOIN 000000 ALTER + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + DAF0 CREATE 000000 READ + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + IA0 CREATE 000000 READ + CONNECT ATTRIBUTES=ADSP SPECIAL OPERATIONS + REVOKE DATE=NONE RESUME DATE=NONE + AEH0 CREATE 000000 READ + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE +INFORMATION FOR GROUP RESEARCH + SUPERIOR GROUP=SYS1 OWNER=IBMUSER CREATED=06.123 + NO INSTALLATION DATA + NO MODEL DATA SET + TERMUACC + SUBGROUP(S)= PAYROLLB + USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS= + IBMUSER JOIN 000000 ALTER + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + DAF0 JOIN 000002 READ + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + IA0 CONNECT 000004 READ + CONNECT ATTRIBUTES=ADSP SPECIAL OPERATIONS + REVOKE DATE=NONE RESUME DATE=NONE + ESH25 USE 000000 READ + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + PROJECTB USE 000000 READ + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + RV2 CREATE 000002 READ + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + RV3 CREATE 000000 READ + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + ADM1 JOIN 000001 READ + CONNECT ATTRIBUTES=OPERATIONS + REVOKE DATE=NONE RESUME DATE=NONE + AEH0 USE 000000 READ + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE