DEV Community

Adnan Siddiqi
Adnan Siddiqi

Posted on • Originally published at towardsdatascience.com on

Create an Adaptive Customer Behavior Analytics Dashboard with Claude AI and Python

In my previous post, I introduced OpenAI’s image APIs and used them to create avatars. Today, I’m diving into another LLM service, ClaudeAI, which has gained traction for its speed and sharp analytical responses. I asked Claude to brainstorm ideas for my next blog post about itself. Among many fantastic suggestions, I chose Customer Behavior Analysis.

Finding a suitable dataset on Kaggle was easy, but the real challenge began when I started working on it. I didn’t expect it to take 4–5 hours to achieve my goal. Initially, I had no clear idea how to use Claude for this, but Claude itself guided me, step by step, on the best approach. As I got deeper, the problem turned into an exciting puzzle; it was challenging, yet fun; thanks to Claude’s capabilities.

Curious why it took so long and how it all worked? Keep reading, or jump straight to the demo video if you’re in a hurry or not interested:

https://medium.com/media/fdd787667b8a1ee0842e7fbd33d0dc6e/href

Before discussing the project, let me tell you what Customer Behaviour Analysis is.

Customer Behaviour Analytics

Customer Behavior Analysis(CBA) involves collecting and studying data to understand how customers interact with a business, including their engagement with products, services, marketing, and website features.

Simply put, CBA is a kind of exploratory data analysis in which you use shopping data to find insights about your business.

How it works

Before proceeding, let’s clarify what we’re building. The title mentions adaptive which means our system doesn’t rely on a fixed data schema. Instead, it analyzes the data and generates a dashboard dynamically. For example, websites A and B might store orders and products differently in their databases. When both CSVs are uploaded, our system uses Claude to interpret the schemas, generates Python code, executes it, and returns computed data to Claude. Claude then creates the HTML and JS for the dashboard at runtime. The steps are:

  • Upload the CSV file via the web interface.
  • Convert the CSV to JSON for Claude.
  • Writing Prompt
  • Send JSON with a prompt to Claude to generate Python code dynamically.
  • Execute the generated Python code to create a partially pre-defined JSON structure.
  • Send the resulting JSON to Claude, which generates and renders the HTML and JS for the dashboard.

I am using Flask for the web app, which is ideal for building prototypes like this. Now, let’s get into development.

Development

Web Interface for CSV Upload

It’s a basic web interface for uploading CSV files. It sends an AJAX request to the /upload endpoint, where the file is renamed and saved. The process happens entirely in the /upload endpoint, which we'll break down step by step.

Converting CSV to JSON for Claude

To inform our first prompt about the data structure, we don’t need to send the entire file. The goal is to provide Claude with the column names and their types so it can generate the relevant Python code. As of January 2025, Claude cannot execute code directly from data input. To address this, we’ll create a function called extract_sample_data. Oh, BTW, I have used this Kaggle dataset and another( randomly generated by GPT)

def extract_sample_data(file_path, num_rows=5):
    try:
        # Load the CSV file
        df = pd.read_csv(file_path)

        # Extract the first few rows
        sample_data = df.head(num_rows).to_dict(orient='records')

        # Get column data types
        column_types = {col: str(df[col].dtype) for col in df.columns}
        del df
        gc.collect()
        return {
            "sample_data": sample_data,
            "column_types": column_types
        }
    except Exception as e:
        print(f"Error: {e}")
        return None
Enter fullscreen mode Exit fullscreen mode

This function will extract the first nth rows and their data types and return a Dict object:

{
  'sample_data': [
    {
      'Customer ID': 1,
      'Age': 55,
      'Gender': 'Male',
      'Item Purchased': 'Blouse',
      'Category': 'Clothing',
      'Purchase Amount (USD)': 53,
      'Location': 'Kentucky',
      'Size': 'L',
      'Color': 'Gray',
      'Season': 'Winter',
      'Review Rating': 3.1,
      'Subscription Status': 'Yes',
      'Shipping Type': 'Express',
      'Discount Applied': 'Yes',
      'Promo Code Used': 'Yes',
      'Previous Purchases': 14,
      'Payment Method': 'Venmo',
      'Frequency of Purchases': 'Fortnightly'
    },
    {
      'Customer ID': 2,
      'Age': 19,
      'Gender': 'Male',
      'Item Purchased': 'Sweater',
      'Category': 'Clothing',
      'Purchase Amount (USD)': 64,
      'Location': 'Maine',
      'Size': 'L',
      'Color': 'Maroon',
      'Season': 'Winter',
      'Review Rating': 3.1,
      'Subscription Status': 'Yes',
      'Shipping Type': 'Express',
      'Discount Applied': 'Yes',
      'Promo Code Used': 'Yes',
      'Previous Purchases': 2,
      'Payment Method': 'Cash',
      'Frequency of Purchases': 'Fortnightly'
    }
  ],
  'column_types': {
    'Customer ID': 'int64',
    'Age': 'int64',
    'Gender': 'object',
    'Item Purchased': 'object',
    'Category': 'object',
    'Purchase Amount (USD)': 'int64',
    'Location': 'object',
    'Size': 'object',
    'Color': 'object',
    'Season': 'object',
    'Review Rating': 'float64',
    'Subscription Status': 'object',
    'Shipping Type': 'object',
    'Discount Applied': 'object',
    'Promo Code Used': 'object',
    'Previous Purchases': 'int64',
    'Payment Method': 'object',
    'Frequency of Purchases': 'object'
  }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, it returned two records, which are sufficient for Claude to identify the structure and data types. Knowing the data types is essential for generating Python code tailored to the available data.

Prompt for Python Code Generation

Now we have to come up with a prompt. If you do not know what prompt engineering is, check it out!

So, the goal is to come up with a Claude prompt that will accept the schema we generated above and produce Python code. The goal is to generate Python code that can show both text and graphs. I took the help of both GPT and Claude to generate the first prompt which is:

You are a Python data analyst tasked with creating dynamic analysis code. I will provide you with a JSON structure containing field names and their data types. Your task is to generate a Python script that reads a CSV file, performs flexible analysis based on the dataset, and returns the output strictly in a JSON payload format.

{FIELDS_AND_TYPES}

### **Output Requirements**

1. **If Required Fields Are Missing:**
   - The generated script should validate the presence of required fields in the CSV file.
   - If any required fields are missing, the script should return:
     ```

json
     {
       "error": "true",
       "message": "<Explanation of missing fields or sections>"
     }


     ```

2. **If Code Is Successfully Generated:**
   - Return a JSON payload with:
     ```

json
     {
       "error": "false",
       "message": "<Entire Python code including imports, main function, and execution block>"
     }


     ```

3. **Additional Requirements:**
   - Do not include any explanatory text, comments, or preambles in the response.
   - Avoid any prefixes like "Here's the code" or suffixes explaining the output.
   - Only output the final JSON payload.

### **Code Requirements**
- **Input Handling:**
  - The generated code should accept a CSV file as input and load it into a Pandas DataFrame.
  - Include error handling for missing or malformed CSV files.

- **Field Validation:**
  - Validate the presence of required fields in the CSV file:
    - Demographic Analysis: Requires `age`, `gender`, `location`.
    - Behavioral Analysis: Requires `frequency`, `previous purchases`, `subscription status`.
    - Purchase Patterns: Requires `amount`, `date`, `category`, `discount`.
    - Product Preferences: Requires `item`, `size`, `color`, `season`.
    - Response Analysis: Requires `rating`, `reviews`.
  - If required fields are missing, the script should log the missing fields and skip the related analysis.

- **Modular Analysis:**
  - The script must perform modular analysis based on available fields:
    - Summary statistics
    - Segmentation and profiling
    - Behavioral and purchase pattern analysis
    - Product preference and response analysis
  - Each module should check for the required fields before execution and log skipped modules.

- **Visualization:**
  - The script must generate visualizations dynamically based on the dataset.
  - Save visualizations as base64-encoded strings for JSON compatibility.

- **Output:**
  - The script should return analysis results in a JSON-compatible dictionary with the following structure:
    ```

json
    {
      "error": false,
      "results": {
        "summary_statistics": {...},
        "segmentation_results": {...},
        "behavioral_analysis": {...},
        "purchase_patterns": {...},
        "product_preferences": {...},
        "visualizations": [...]
      }
    }


    ```

### **Response Examples**

#### **Error Output:**
Enter fullscreen mode Exit fullscreen mode


json
{
"error": "true",
"message": "Missing required fields: ['age', 'gender', 'location']"
}


I pass the previously generated schema, along with instructions and sample output, to the prompt. The code must produce an output similar to the example below, where the inner fields may vary, but the main structure remains consistent. If the required CSV file is missing, the prompt will generate a relevant JSON error message.

Enter fullscreen mode Exit fullscreen mode

{
"error": false,
"results": {
"summary_statistics": {
...
},
"segmentation_results": {
...
},
"behavioral_analysis": {
...
},
"purchase_patterns": {
...
},
"product_preferences": {
...
},
"visualizations": [
...
]
}
}


The code accepts a CSV file, loads it into Pandas, and generates text and graphs.

An early output of the prompt was:

Enter fullscreen mode Exit fullscreen mode

{
'error': 'false',
'message': "import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport base64\nfrom io import BytesIO\n\ndef encode_plot(plt):\n buffer = BytesIO()\n plt.savefig(buffer, format='png', bbox_inches='tight')\n buffer.seek(0)\n image_png = buffer.getvalue()\n buffer.close()\n plt.close()\n return base64.b64encode(image_png).decode()\n\ndef analyze_data(csv_file):\n try:\n df = pd.read_csv(csv_file)\n results = {\n 'error': False,\n 'results': {\n 'summary_statistics': {},\n 'segmentation_results': {},\n 'behavioral_analysis': {},\n 'purchase_patterns': {},\n 'product_preferences': {},\n 'visualizations': []\n }\n }\n \n # Validate required fields\n required_fields = {\n 'demographic': ['Age', 'Gender', 'Location'],\n 'behavioral': ['Frequency of Purchases', 'Previous Purchases', 'Subscription Status'],\n 'purchase': ['Purchase Amount (USD)', 'Category', 'Discount Applied'],\n 'product': ['Item Purchased', 'Size', 'Color', 'Season'],\n 'response': ['Review Rating']\n }\n \n for category, fields in required_fields.items():\n if not all(field in df.columns for field in fields):\n return {\n 'error': True,\n 'message': f'Missing required fields for {category} analysis: {[f for f in fields if f not in df.columns]}'\n }\n \n # Summary Statistics\n results['results']['summary_statistics'] = {\n 'total_customers': len(df),\n 'average_purchase': float(df['Purchase Amount (USD)'].mean()),\n 'average_rating': float(df['Review Rating'].mean()),\n 'total_revenue': float(df['Purchase Amount (USD)'].sum())\n }\n \n # Segmentation Results\n age_segments = df.groupby(pd.cut(df['Age'], bins=[0, 25, 50, 75, 100])).size().to_dict()\n results['results']['segmentation_results'] = {\n 'age_distribution': {str(k): int(v) for k, v in age_segments.items()},\n 'gender_distribution': df['Gender'].value_counts().to_dict()\n }\n \n # Behavioral Analysis\n results['results']['behavioral_analysis'] = {\n 'purchase_frequency': df['Frequency of Purchases'].value_counts().to_dict(),\n 'avg_previous_purchases': float(df['Previous Purchases'].mean())\n }\n \n # Purchase Patterns\n results['results']['purchase_patterns'] = {\n 'category_distribution': df['Category'].value_counts().to_dict(),\n 'avg_amount_by_category': df.groupby('Category')['Purchase Amount (USD)'].mean().to_dict()\n }\n \n # Product Preferences\n results['results']['product_preferences'] = {\n 'popular_colors': df['Color'].value_counts().to_dict(),\n 'size_distribution': df['Size'].value_counts().to_dict(),\n 'seasonal_preference': df['Season'].value_counts().to_dict()\n }\n \n # Visualizations\n plt.figure(figsize=(10, 6))\n sns.histplot(data=df, x='Age', bins=20)\n plt.title('Age Distribution')\n results['results']['visualizations'].append({\n 'title': 'age_distribution',\n 'image': encode_plot(plt)\n })\n \n plt.figure(figsize=(10, 6))\n sns.boxplot(data=df, x='Category', y='Purchase Amount (USD)')\n plt.xticks(rotation=45)\n plt.title('Purchase Amount by Category')\n results['results']['visualizations'].append({\n 'title': 'purchase_by_category',\n 'image': encode_plot(plt)\n })\n \n return results\n \n except Exception as e:\n return {\n 'error': True,\n 'message': f'Error analyzing data: {str(e)}'\n }\n\ndef main(csv_file):\n return analyze_data(csv_file)\n\nif name == ' main':\n import sys\n if len(sys.argv) > 1:\n result = main(sys.argv[1])\n print(result)\n else:\n print({'error': True, 'message': 'No CSV file provided'})"
}


When you run the above code, it produces the output like this:

Enter fullscreen mode Exit fullscreen mode

[
TextBlock(text='{\n "error": false,\n "message": "Analysis code generated successfully",\n "code": {\n "imports": """\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport json\nimport base64\nfrom io import BytesIO\nimport os\n""",\n "main_analysis": """\ndef analyze_retail_data(data_json):\n # Convert JSON to DataFrame\n df = pd.DataFrame(data_json[\'sample_data\'])\n \n # Create results dictionary\n results = {\n "error": False,\n "message": "Analysis completed successfully",\n "results": {}\n }\n \n # Create visualization directory if it doesn\'t exist\n os.makedirs(\'visualizations\', exist_ok=True)\n \n # Summary Statistics\n numerical_cols = [\'Age\', \'Purchase Amount (USD)\', \'Review Rating\', \'Previous Purchases\']\n results[\'results\'][\'summary_statistics\'] = {\n col: df[col].describe().to_dict() for col in numerical_cols\n }\n \n # Segmentation Results\n results[\'results\'][\'segmentation_results\'] = {\n \'gender_distribution\': df[\'Gender\'].value_counts().to_dict(),\n \'age_segments\': pd.qcut(df[\'Age\'], q=3, labels=[\'Young\', \'Middle\', \'Senior\']).value_counts().to_dict()\n }\n \n # Behavioral Analysis\n results[\'results\'][\'behavioral_analysis\'] = {\n \'subscription_status\': df[\'Subscription Status\'].value_counts().to_dict(),\n \'purchase_frequency\': df[\'Frequency of Purchases\'].value_counts().to_dict()\n }\n \n # Purchase Patterns\n results[\'results\'][\'purchase_patterns\'] = {\n \'seasonal_distribution\': df[\'Season\'].value_counts().to_dict(),\n \'payment_methods\': df[\'Payment Method\'].value_counts().to_dict(),\n \'shipping_preferences\': df[\'Shipping Type\'].value_counts().to_dict()\n }\n \n # Product Preferences\n results[\'results\'][\'product_preferences\'] = {\n \'popular_items\': df[\'Item Purchased\'].value_counts().to_dict(),\n \'size_distribution\': df[\'Size\'].value_counts().to_dict(),\n \'color_preferences\': df[\'Color\'].value_counts().to_dict()\n }\n \n # Visualizations\n results[\'results\'][\'visualizations\'] = []\n \n # Age vs Purchase Amount\n plt.figure(figsize=(10, 6))\n sns.scatterplot(data=df, x=\'Age\', y=\'Purchase Amount (USD)\')\n plt.title(\'Age vs Purchase Amount\')\n \n # Save plot as base64\n buffer = BytesIO()\n plt.savefig(buffer, format=\'png\')\n buffer.seek(0)\n image_png = buffer.getvalue()\n buffer.close()\n \n # Encode to base64\n graphic = base64.b64encode(image_png).decode(\'utf-8\')\n results[\'results\'][\'visualizations\'].append({\n \'type\': \'scatter_plot\',\n \'title\': \'Age vs Purchase Amount\',\n \'base64_image\': graphic\n })\n \n # Category Distribution\n plt.figure(figsize=(10, 6))\n sns.countplot(data=df, x=\'Category\')\n plt.title(\'Category Distribution\')\n plt.xticks(rotation=45)\n \n # Save plot as base64\n buffer = BytesIO()\n plt.savefig(buffer, format=\'png\')\n buffer.seek(0)\n image_png = buffer.getvalue()\n buffer.close()\n \n # Encode to base64\n graphic = base64.b64encode(image_png).decode(\'utf-8\')\n results[\'results\'][\'visualizations\'].append({\n \'type\': \'bar_plot\',\n \'title\': \'Category Distribution\',\n \'base64_image\': graphic\n })\n \n return results\n""",\n "execution": """\nif name == " main":\n # Rea',
type='text')
]


Another variation of the generated code output is shown below:

Enter fullscreen mode Exit fullscreen mode

{
'error': False,
'results': {
'summary_statistics': {
'total_customers': 3900,
'average_purchase': 59.76435897435898,
'average_rating': 3.7499487179487176,
'total_revenue': 233081.0
},
'segmentation_results': {
'age_distribution': {
'(0, 25]': 571,
'(25, 50]': 1853,
'(50, 75]': 1476,
'(75, 100]': 0
},
'gender_distribution': {
'Male': 2652,
'Female': 1248
}
},
'behavioral_analysis': {
'purchase_frequency': {
'Every 3 Months': 584,
'Annually': 572,
'Quarterly': 563,
'Monthly': 553,
'Bi-Weekly': 547,
'Fortnightly': 542,
'Weekly': 539
},
'avg_previous_purchases': 25.35153846153846
},
'purchase_patterns': {
'category_distribution': {
'Clothing': 1737,
'Accessories': 1240,
'Footwear': 599,
'Outerwear': 324
},
'avg_purchase_by_category': {
'Accessories': 59.83870967741935,
'Clothing': 60.025331030512376,
'Footwear': 60.25542570951586,
'Outerwear': 57.17283950617284
}
},
'product_preferences': {
'popular_colors': {
'Olive': 177,
'Yellow': 174,
'Silver': 173,
'Teal': 172,
'Green': 169,
'Black': 167,
'Cyan': 166,
'Violet': 166,
'Gray': 159,
'Maroon': 158,
'Orange': 154,
'Charcoal': 153,
'Pink': 153,
'Magenta': 152,
'Blue': 152,
'Purple': 151,
'Peach': 149,
'Red': 148,
'Beige': 147,
'Indigo': 147,
'Lavender': 147,
'Turquoise': 145,
'White': 142,
'Brown': 141,
'Gold': 138
},
'size_distribution': {
'M': 1755,
'L': 1053,
'S': 663,
'XL': 429
},
'seasonal_preference': {
'Spring': 999,
'Fall': 975,
'Winter': 971,
'Summer': 955
}
},
'visualizations': [
{
'title': 'age_distribution',
'image': 'iVBORw0KGgoAAAANSUhEUgAAA1IAAAIjCAYAAAAJLyrXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA43UlEQVR4nO3de7hVBb3v4e9CYIFyE7mnXLwiqelWIrZdvBBo1ImkUtPCS1YGpmJlnu19V3TTrDZJdRTsSbdpqZm7MASlY5IpbTUUSdwYJiwQCRYgIrDG+aPHeVoB6kBkTljv+zzzeZhjjDnnb8pg1WeNMcesK4qiCAAAAK9bq2oPAAAAsKMRUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAOw0nnnmmdTV1WXKlClv+mtNmTIldXV1eeaZZyrL+vfvn/e///1v+msnyX333Ze6urrcd9992+X1AGhOSAHQzPe///3U1dVlyJAh1R4ldXV1lVvr1q3TtWvXHH744Tn33HPzxBNPbLPX+f73v79d4mtr1PJsAC1ZXVEURbWHAKB2HHnkkVm0aFGeeeaZPPXUU9l3332rNktdXV3e+9735hOf+ESKosjKlSvz6KOP5tZbb82aNWvy9a9/PePHj69sXxRF1q1blzZt2mSXXXZ53a9z0EEHpVu3bqWO7mzcuDHr169PfX196urqkvz9iNRBBx2Uu+6663U/z9bO1tTUlJdffjlt27ZNq1Z+LwqwvfnJC0DFggUL8sADD+Tqq69O9+7dc+ONN1Z7pOy///459dRT8/GPfzzjxo3Lj370ozz99NMZPHhwLrjggvzqV7+qbFtXV5d27dqViqiy1qxZkyTZZZdd0q5du0pEbW+tWrVKu3btRBRAlfjpC0DFjTfemN133z0jR47Mhz/84S2G1AsvvJCPf/zj6dSpU7p06ZIxY8bk0Ucf3eznk5588sl8+MMfTteuXdOuXbscccQRufPOO9/QnHvssUduvvnmtG7dOl/5ylcqyzf3GamGhoacfvrp2XPPPVNfX5/evXvngx/8YOWzTf3798/jjz+emTNnVk4jPOqoo5L8/89BzZw5M5/97GfTo0eP7Lnnns3W/eNnpF7xm9/8JoceemjatWuXQYMG5bbbbmu2/vLLL99sgP3zc77abFv6jNStt96aww8/PO3bt0+3bt1y6qmn5rnnnmu2zWmnnZYOHTrkueeey6hRo9KhQ4d07949n//857Nx48bX+K8PQJK0rvYAANSOG2+8MSeccELatm2bk08+Oddee20eeuihDB48uLJNU1NTPvCBD+QPf/hDzj777AwcODC/+MUvMmbMmE2e7/HHH8+RRx6Zt7zlLfnSl76U3XbbLbfccktGjRqVn//85/nQhz601bP27ds373nPe3LvvfemsbExnTp12ux2o0ePzuOPP55zzjkn/fv3z9KlSzNt2rQsXLgw/fv3zzXXXJNzzjknHTp0yL/9278lSXr27NnsOT772c+me/fuufTSSytHpLbkqaeeyoknnpjPfOYzGTNmTCZPnpyPfOQjmTp1at773veWeo+vZ7Z/NGXKlJx++ukZPHhwJkyYkCVLluQ73/lOfve73+W///u/06VLl8q2GzduzIgRIzJkyJB861vfyj333JOrrroq++yzT84+++xScwK0SAUAFEXx8MMPF0mKadOmFUVRFE1NTcWee+5ZnHvuuc22+/nPf14kKa655prKso0bNxbHHHNMkaSYPHlyZfmxxx5bHHzwwcVLL71UWdbU1FT867/+a7Hffvu95kxJirFjx25x/bnnnlskKR599NGiKIpiwYIFzWb429/+ViQpvvnNb77q67z1rW8t3vOe92yyfPLkyUWS4p3vfGexYcOGza5bsGBBZVm/fv2KJMXPf/7zyrKVK1cWvXv3Lg477LDKsssuu6zY3P8Eb+45tzTbvffeWyQp7r333qIoiuLll18uevToURx00EHF2rVrK9vdddddRZLi0ksvrSwbM2ZMkaS48sormz3nYYcdVhx++OGbvBYAm3JqHwBJ/n40qmfPnjn66KOT/P3zRieeeGJuvvnmZqd7TZ06NW3atMlZZ51VWdaqVauMHTu22fMtX748M2bMyEc/+tGsWrUqy5Yty7Jly/LCCy9kxIgReeqppzY55aysDh06JElWrVq12fXt27dP27Ztc9999+Vvf/vbVr/OWWed9bo/d9WnT59mR9o6deqUT3ziE/nv//7vNDQ0bPUMr+Xhhx/O0qVL89nPfjbt2rWrLB85cmQGDhyY//qv/9rkMZ/5zGea3X/Xu96V//mf/3nTZgTYmQgpALJx48bcfPPNOfroo7NgwYLMnz8/8+fPz5AhQ7JkyZJMnz69su1f/vKX9O7dO7vuumuz5/jnq/vNnz8/RVHkkksuSffu3ZvdLrvssiTJ0qVL39Dcq1evTpJ07Nhxs+vr6+vz9a9/Pb/+9a/Ts2fPvPvd7843vvGN0kEzYMCA173tvvvuu8nnn/bff/8k2eznqbaVv/zlL0mSAw44YJN1AwcOrKx/Rbt27dK9e/dmy3bfffc3FJwALYnPSAGQGTNmZPHixbn55ptz8803b7L+xhtvzPDhw0s9Z1NTU5Lk85//fEaMGLHZbd7opdXnzJmTXXbZ5VVD57zzzssHPvCB3HHHHbn77rtzySWXZMKECZkxY0YOO+yw1/U67du3f0Nz/rMtXelve17o4c28siFASyCkAMiNN96YHj16ZOLEiZusu+2223L77bdn0qRJad++ffr165d77703L774YrOjUvPnz2/2uL333jtJ0qZNmwwbNmybz7xw4cLMnDkzQ4cO3eIRqVfss88+ueCCC3LBBRfkqaeeyqGHHpqrrroqP/nJT5JsOWy2xitH4v7xOf/85z8n+ftV+JK/H/lJkhUrVjS7AMQ/HzUqM1u/fv2SJPPmzcsxxxzTbN28efMq6wHYNpzaB9DCrV27Nrfddlve//7358Mf/vAmt3HjxmXVqlWVS5aPGDEi69evz49+9KPKczQ1NW0SYT169MhRRx2VH/zgB1m8ePEmr/v8889v9czLly/PySefnI0bN1auZrc5L774Yl566aVmy/bZZ5907Ngx69atqyzbbbfdsmLFiq2e5x8tWrQot99+e+V+Y2NjfvzjH+fQQw9Nr169KjMkyW9/+9vKdmvWrMkNN9ywyfO93tmOOOKI9OjRI5MmTWr23n79619n7ty5GTly5Na+JQA2wxEpgBbuzjvvzKpVq/K//tf/2uz6d7zjHZUv5z3xxBMzatSovP3tb88FF1yQ+fPnZ+DAgbnzzjuzfPnyJM2PoEycODHvfOc7c/DBB+ess87K3nvvnSVLlmTWrFn561//mkcfffQ15/vzn/+cn/zkJymKIo2NjXn00Udz6623ZvXq1bn66qtz3HHHvepjjz322Hz0ox/NoEGD0rp169x+++1ZsmRJTjrppMp2hx9+eK699tp8+ctfzr777psePXpsclTn9dp///1z5pln5qGHHkrPnj1z/fXXZ8mSJZk8eXJlm+HDh6dv374588wz84UvfCG77LJLrr/++nTv3j0LFy5s9nyvd7Y2bdrk61//ek4//fS85z3vycknn1y5/Hn//v1z/vnnb9X7AWALqnzVQACq7AMf+EDRrl27Ys2aNVvc5rTTTivatGlTLFu2rCiKonj++eeLj33sY0XHjh2Lzp07F6eddlrxu9/9rkhS3Hzzzc0e+/TTTxef+MQnil69ehVt2rQp3vKWtxTvf//7i5/97GevOVuSyq1Vq1ZFly5disMOO6w499xzi8cff3yT7f/58ufLli0rxo4dWwwcOLDYbbfdis6dOxdDhgwpbrnllmaPa2hoKEaOHFl07NixSFK53PgrlyN/6KGHNnmtLV3+fOTIkcXdd99dHHLIIUV9fX0xcODA4tZbb93k8bNnzy6GDBlStG3btujbt29x9dVXb/Y5tzTbP1/+/BU//elPi8MOO6yor68vunbtWpxyyinFX//612bbjBkzpthtt902mWlLl2UHYFN1RVEU1Uk4AHYmd9xxRz70oQ/l/vvvz5FHHlntcQDgTSWkACht7dq1za5kt3HjxgwfPjwPP/xwGhoatvlV7gCg1viMFAClnXPOOVm7dm2GDh2adevW5bbbbssDDzyQr371qyIKgBbBESkASrvpppty1VVXZf78+XnppZey77775uyzz864ceOqPRoAbBdCCgAAoCTfIwUAAFCSkAIAACjJxSaSNDU1ZdGiRenYsWOzL5IEAABalqIosmrVqvTp0yetWm35uJOQSrJo0aLstdde1R4DAACoEc8++2z23HPPLa4XUkk6duyY5O//sTp16lTlaQAAgGppbGzMXnvtVWmELRFSSeV0vk6dOgkpAADgNT/y42ITAAAAJQkpAACAkqoaUhMmTMjgwYPTsWPH9OjRI6NGjcq8efOabXPUUUelrq6u2e0zn/lMs20WLlyYkSNHZtddd02PHj3yhS98IRs2bNiebwUAAGhBqvoZqZkzZ2bs2LEZPHhwNmzYkP/9v/93hg8fnieeeCK77bZbZbuzzjorV155ZeX+rrvuWvnzxo0bM3LkyPTq1SsPPPBAFi9enE984hNp06ZNvvrVr27X9wMAALQMdUVRFNUe4hXPP/98evTokZkzZ+bd7353kr8fkTr00ENzzTXXbPYxv/71r/P+978/ixYtSs+ePZMkkyZNyoUXXpjnn38+bdu2fc3XbWxsTOfOnbNy5UoX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/vd5xzvekd/85jd54okncs8996Rnz5459NBD8+///u+58MILc/nll6dt27bVeGsAAMBOrKY+I7Vy5cokSdeuXZMks2fPzvr16zNs2LDKNgMHDkzfvn0za9asJMmsWbNy8MEHp2fPnpVtRowYkcbGxjz++OObfZ1169alsbGx2Q0AAOD1qpkv5G1qasp5552XI488MgcddFCSpKGhIW3btk2XLl2abduzZ880NDRUtvnHiHpl/SvrNmfChAm54oortvE7AGBHt3DhwixbtqzaY2Tu3LnVHgGA11AzITV27NjMmTMn999//5v+WhdddFHGjx9fud/Y2Ji99trrTX9dAGrXwoULM3DggVm79sVqj1Kxft3L1R4BgC2oiZAaN25c7rrrrvz2t7/NnnvuWVneq1evvPzyy1mxYkWzo1JLlixJr169Ktv84Q9/aPZ8r1zV75Vt/ll9fX3q6+u38bsAYEe2bNmyrF37YoaccVk69e5f1VkW/2lW5tz5Q1egBahhVQ2poihyzjnn5Pbbb899992XAQMGNFt/+OGHp02bNpk+fXpGjx6dJJk3b14WLlyYoUOHJkmGDh2ar3zlK1m6dGl69OiRJJk2bVo6deqUQYMGbd83BMAOr1Pv/una94CqztC4+Jmqvj4Ar62qITV27NjcdNNN+cUvfpGOHTtWPtPUuXPntG/fPp07d86ZZ56Z8ePHp2vXrunUqVPOOeecDB06NO94xzuSJMOHD8+gQYPy8Y9/PN/4xjfS0NCQiy++OGPHjnXUCQCAmuKzmDuPqobUtddemyQ56qijmi2fPHlyTjvttCTJt7/97bRq1SqjR4/OunXrMmLEiHz/+9+vbLvLLrvkrrvuytlnn52hQ4dmt912y5gxY3LllVdur7cBAACvyWcxdy5VP7XvtbRr1y4TJ07MxIkTt7hNv3798qtf/WpbjgYAANuUz2LuXGriYhMAANBS+CzmzqGmvpAXAABgRyCkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJTUutoDAMDcuXOrPUJNzADAjkNIAVA1a1e+kKQup556arVHqVi/7uVqjwDADkBIAVA1619claTIoR+7MN0HDKzqLIv/NCtz7vxhNmzYUNU5ANgxCCkAqq5Dj77p2veAqs7QuPiZqr4+ADsWF5sAAAAoSUgBAACUJKQAAABK8hkpAOB1qYVLxNfCDACJkAIAXoPL1ANsSkgBAK/KZeoBNiWkAIDXxWXqAf4/IcUOpVbOja+VOQAAqA4hxQ6hFs/PT5yjDwDQUlU1pH7729/mm9/8ZmbPnp3Fixfn9ttvz6hRoyrrTzvttNxwww3NHjNixIhMnTq1cn/58uU555xz8stf/jKtWrXK6NGj853vfCcdOnTYXm+D7aCWzs9PnKMPANDSVTWk1qxZk7e97W0544wzcsIJJ2x2m+OOOy6TJ0+u3K+vr2+2/pRTTsnixYszbdq0rF+/Pqeffno+9alP5aabbnpTZ6c6auH8/MQ5+gAALV1VQ+r444/P8ccf/6rb1NfXp1evXptdN3fu3EydOjUPPfRQjjjiiCTJ9773vbzvfe/Lt771rfTp02ebzwwAANCq2gO8lvvuuy89evTIAQcckLPPPjsvvPBCZd2sWbPSpUuXSkQlybBhw9KqVas8+OCDW3zOdevWpbGxsdkNAADg9arpkDruuOPy4x//ONOnT8/Xv/71zJw5M8cff3w2btyYJGloaEiPHj2aPaZ169bp2rVrGhoatvi8EyZMSOfOnSu3vfba6019HwAAwM6lpq/ad9JJJ1X+fPDBB+eQQw7JPvvsk/vuuy/HHnvsVj/vRRddlPHjx1fuNzY2iikAAOB1q+kjUv9s7733Trdu3TJ//vwkSa9evbJ06dJm22zYsCHLly/f4ueqkr9/7qpTp07NbgAAAK9XTR+R+md//etf88ILL6R3795JkqFDh2bFihWZPXt2Dj/88CTJjBkz0tTUlCFDhlRzVAAAasjcuXOrPUJNzMC2U9WQWr16deXoUpIsWLAgjzzySLp27ZquXbvmiiuuyOjRo9OrV688/fTT+eIXv5h99903I0aMSJIceOCBOe6443LWWWdl0qRJWb9+fcaNG5eTTjrJFfsAAMjalS8kqcupp55a7VEq1q97udojsA1UNaQefvjhHH300ZX7r3xuacyYMbn22mvz2GOP5YYbbsiKFSvSp0+fDB8+PP/+7//e7LukbrzxxowbNy7HHnts5Qt5v/vd72739wLVVgu/5aqFGWpZrfz3qZU5YGdRC/+mamGGWrX+xVVJihz6sQvTfcDAqs6y+E+zMufOH2bDhg1VnYNto6ohddRRR6Uoii2uv/vuu1/zObp27erLd2nR/Kat9tXi31Hi7wneqFr8t+3f9ZZ16NE3XfseUNUZGhc/U9XXZ9vaoT4jBWzKb9pqXy39HSX+nmBbqaV/2/5dw/YnpGAn4Tdtta8W/o4Sf0+wrdXCv23/rmH726Eufw4AAFALhBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlLRVIbX33nvnhRde2GT5ihUrsvfee7/hoQAAAGrZVoXUM888k40bN26yfN26dXnuuefe8FAAAAC1rHWZje+8887Kn+++++507ty5cn/jxo2ZPn16+vfvv82GAwAAqEWlQmrUqFFJkrq6uowZM6bZujZt2qR///656qqrttlwAAAAtahUSDU1NSVJBgwYkIceeijdunV7U4YCAACoZaVC6hULFizY1nMAAADsMLYqpJJk+vTpmT59epYuXVo5UvWK66+//g0PBgAAUKu2KqSuuOKKXHnllTniiCPSu3fv1NXVbeu5AAAAatZWhdSkSZMyZcqUfPzjH9/W8wAAANS8rfoeqZdffjn/+q//uq1nAQAA2CFsVUh98pOfzE033bStZwEAANghbNWpfS+99FJ++MMf5p577skhhxySNm3aNFt/9dVXb5PhAAAAatFWhdRjjz2WQw89NEkyZ86cZutceAIAANjZbVVI3Xvvvdt6DgAAgB3GVn1GCgAAoCXbqiNSRx999KuewjdjxoytHggAAKDWbVVIvfL5qFesX78+jzzySObMmZMxY8Zsi7kAAABq1laF1Le//e3NLr/88suzevXqNzQQAABArdumn5E69dRTc/3112/LpwQAAKg52zSkZs2alXbt2m3LpwQAAKg5W3Vq3wknnNDsflEUWbx4cR5++OFccskl22QwAACAWrVVIdW5c+dm91u1apUDDjggV155ZYYPH75NBgMAAKhVWxVSkydP3tZzAAAA7DC2KqReMXv27MydOzdJ8ta3vjWHHXbYNhkKAACglm1VSC1dujQnnXRS7rvvvnTp0iVJsmLFihx99NG5+eab07179205IwAAQE3Zqqv2nXPOOVm1alUef/zxLF++PMuXL8+cOXPS2NiYz33uc9t6RgAAgJqyVUekpk6dmnvuuScHHnhgZdmgQYMyceJEF5sAAAB2elt1RKqpqSlt2rTZZHmbNm3S1NT0hocCAACoZVsVUsccc0zOPffcLFq0qLLsueeey/nnn59jjz12mw0HAABQi7YqpP7jP/4jjY2N6d+/f/bZZ5/ss88+GTBgQBobG/O9731vW88IAABQU7bqM1J77bVX/vjHP+aee+7Jk08+mSQ58MADM2zYsG06HAAAQC0qdURqxowZGTRoUBobG1NXV5f3vve9Oeecc3LOOedk8ODBeetb35r/+3//75s1KwAAQE0oFVLXXHNNzjrrrHTq1GmTdZ07d86nP/3pXH311dtsOAAAgFpUKqQeffTRHHfccVtcP3z48MyePfsNDwUAAFDLSoXUkiVLNnvZ81e0bt06zz///BseCgAAoJaVCqm3vOUtmTNnzhbXP/bYY+ndu/cbHgoAAKCWlQqp973vfbnkkkvy0ksvbbJu7dq1ueyyy/L+979/mw0HAABQi0pd/vziiy/Obbfdlv333z/jxo3LAQcckCR58sknM3HixGzcuDH/9m//9qYMCgAAUCtKhVTPnj3zwAMP5Oyzz85FF12UoiiSJHV1dRkxYkQmTpyYnj17vimDAgAA1IrSX8jbr1+//OpXv8rf/va3zJ8/P0VRZL/99svuu+/+ZswHAABQc0qH1Ct23333DB48eFvOAgAAsEModbEJAAAAhBQAAEBpQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKqmpI/fa3v80HPvCB9OnTJ3V1dbnjjjuarS+KIpdeeml69+6d9u3bZ9iwYXnqqaeabbN8+fKccsop6dSpU7p06ZIzzzwzq1ev3o7vAgAAaGmqGlJr1qzJ2972tkycOHGz67/xjW/ku9/9biZNmpQHH3wwu+22W0aMGJGXXnqpss0pp5ySxx9/PNOmTctdd92V3/72t/nUpz61vd4CAADQArWu5osff/zxOf744ze7riiKXHPNNbn44ovzwQ9+MEny4x//OD179swdd9yRk046KXPnzs3UqVPz0EMP5YgjjkiSfO9738v73ve+fOtb30qfPn2223sBAABajpr9jNSCBQvS0NCQYcOGVZZ17tw5Q4YMyaxZs5Iks2bNSpcuXSoRlSTDhg1Lq1at8uCDD27xudetW5fGxsZmNwAAgNerZkOqoaEhSdKzZ89my3v27FlZ19DQkB49ejRb37p163Tt2rWyzeZMmDAhnTt3rtz22muvbTw9AACwM6vZkHozXXTRRVm5cmXl9uyzz1Z7JAAAYAdSsyHVq1evJMmSJUuaLV+yZEllXa9evbJ06dJm6zds2JDly5dXttmc+vr6dOrUqdkNAADg9arZkBowYEB69eqV6dOnV5Y1NjbmwQcfzNChQ5MkQ4cOzYoVKzJ79uzKNjNmzEhTU1OGDBmy3WcGAABahqpetW/16tWZP39+5f6CBQvyyCOPpGvXrunbt2/OO++8fPnLX85+++2XAQMG5JJLLkmfPn0yatSoJMmBBx6Y4447LmeddVYmTZqU9evXZ9y4cTnppJNcsQ8AAHjTVDWkHn744Rx99NGV++PHj0+SjBkzJlOmTMkXv/jFrFmzJp/61KeyYsWKvPOd78zUqVPTrl27ymNuvPHGjBs3Lscee2xatWqV0aNH57vf/e52fy8AAEDLUdWQOuqoo1IUxRbX19XV5corr8yVV165xW26du2am2666c0YDwAAYLNq9jNSAAAAtUpIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgpJoOqcsvvzx1dXXNbgMHDqysf+mllzJ27Njsscce6dChQ0aPHp0lS5ZUcWIAAKAlqOmQSpK3vvWtWbx4ceV2//33V9adf/75+eUvf5lbb701M2fOzKJFi3LCCSdUcVoAAKAlaF3tAV5L69at06tXr02Wr1y5Mtddd11uuummHHPMMUmSyZMn58ADD8zvf//7vOMd79jeowIAAC1EzR+Reuqpp9KnT5/svffeOeWUU7Jw4cIkyezZs7N+/foMGzassu3AgQPTt2/fzJo161Wfc926dWlsbGx2AwAAeL1qOqSGDBmSKVOmZOrUqbn22muzYMGCvOtd78qqVavS0NCQtm3bpkuXLs0e07NnzzQ0NLzq806YMCGdO3eu3Pbaa6838V0AAAA7m5o+te/444+v/PmQQw7JkCFD0q9fv9xyyy1p3779Vj/vRRddlPHjx1fuNzY2iikAAOB1q+kjUv+sS5cu2X///TN//vz06tUrL7/8clasWNFsmyVLlmz2M1X/qL6+Pp06dWp2AwAAeL12qJBavXp1nn766fTu3TuHH3542rRpk+nTp1fWz5s3LwsXLszQoUOrOCUAALCzq+lT+z7/+c/nAx/4QPr165dFixblsssuyy677JKTTz45nTt3zplnnpnx48ena9eu6dSpU84555wMHTrUFfsAAIA3VU2H1F//+tecfPLJeeGFF9K9e/e8853vzO9///t07949SfLtb387rVq1yujRo7Nu3bqMGDEi3//+96s8NQAAsLOr6ZC6+eabX3V9u3btMnHixEycOHE7TQQAALCDfUYKAACgFggpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABKElIAAAAlCSkAAICShBQAAEBJQgoAAKAkIQUAAFCSkAIAAChJSAEAAJQkpAAAAEoSUgAAACUJKQAAgJKEFAAAQElCCgAAoCQhBQAAUJKQAgAAKElIAQAAlCSkAAAAShJSAAAAJQkpAACAkoQUAABASUIKAACgJCEFAABQkpACAAAoSUgBAACUJKQAAABK2mlCauLEienfv3/atWuXIUOG5A9/+EO1RwIAAHZSO0VI/fSnP8348eNz2WWX5Y9//GPe9ra3ZcSIEVm6dGm1RwMAAHZCO0VIXX311TnrrLNy+umnZ9CgQZk0aVJ23XXXXH/99dUeDQAA2Am1rvYAb9TLL7+c2bNn56KLLqosa9WqVYYNG5ZZs2Zt9jHr1q3LunXrKvdXrlyZJGlsbHxzh30dVq9enSRZ/pd52bBubVVnaVz8lyTJyueeSpvWdWb5B7U0j1nMUlYtzWOW2p8lqa15zFL7syS1NY9ZdoBZGhYm+fv/D66F/z/+ygxFUbzqdnXFa21R4xYtWpS3vOUteeCBBzJ06NDK8i9+8YuZOXNmHnzwwU0ec/nll+eKK67YnmMCAAA7kGeffTZ77rnnFtfv8EektsZFF12U8ePHV+43NTVl+fLl2WOPPVJXV/3f4mwvjY2N2WuvvfLss8+mU6dO1R6HGmLf4NXYP9gS+wavxv7BltTavlEURVatWpU+ffq86nY7fEh169Ytu+yyS5YsWdJs+ZIlS9KrV6/NPqa+vj719fXNlnXp0uXNGrHmderUqSZ2WmqPfYNXY/9gS+wbvBr7B1tSS/tG586dX3ObHf5iE23bts3hhx+e6dOnV5Y1NTVl+vTpzU71AwAA2FZ2+CNSSTJ+/PiMGTMmRxxxRN7+9rfnmmuuyZo1a3L66adXezQAAGAntFOE1Iknnpjnn38+l156aRoaGnLooYdm6tSp6dmzZ7VHq2n19fW57LLLNjnNEewbvBr7B1ti3+DV2D/Ykh1139jhr9oHAACwve3wn5ECAADY3oQUAABASUIKAACgJCEFAABQkpDayU2YMCGDBw9Ox44d06NHj4waNSrz5s1rts1LL72UsWPHZo899kiHDh0yevToTb7gmJ3Ttddem0MOOaTyBXhDhw7Nr3/968p6+wav+NrXvpa6urqcd955lWX2j5br8ssvT11dXbPbwIEDK+vtGy3bc889l1NPPTV77LFH2rdvn4MPPjgPP/xwZX1RFLn00kvTu3fvtG/fPsOGDctTTz1VxYnZXvr377/Jz466urqMHTs2yY73s0NI7eRmzpyZsWPH5ve//32mTZuW9evXZ/jw4VmzZk1lm/PPPz+//OUvc+utt2bmzJlZtGhRTjjhhCpOzfay55575mtf+1pmz56dhx9+OMccc0w++MEP5vHHH09i3+DvHnroofzgBz/IIYcc0my5/aNle+tb35rFixdXbvfff39lnX2j5frb3/6WI488Mm3atMmvf/3rPPHEE7nqqquy++67V7b5xje+ke9+97uZNGlSHnzwwey2224ZMWJEXnrppSpOzvbw0EMPNfu5MW3atCTJRz7ykSQ74M+OghZl6dKlRZJi5syZRVEUxYoVK4o2bdoUt956a2WbuXPnFkmKWbNmVWtMqmj33Xcv/s//+T/2DYqiKIpVq1YV++23XzFt2rTiPe95T3HuuecWReFnR0t32WWXFW9729s2u86+0bJdeOGFxTvf+c4trm9qaip69epVfPOb36wsW7FiRVFfX1/853/+5/YYkRpy7rnnFvvss0/R1NS0Q/7scESqhVm5cmWSpGvXrkmS2bNnZ/369Rk2bFhlm4EDB6Zv376ZNWtWVWakOjZu3Jibb745a9asydChQ+0bJEnGjh2bkSNHNtsPEj87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'
},
{
'title': 'purchase_by_category',
'image': 'iVBORw0KGgoAAAANSUhEUgAAA1IAAAJYCAYAAABoytfVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB0JUlEQVR4nO3dd1iV9eP/8ddhEwo4QQwn7lGmpYjmCCU1lXKWuS0tt6WppaY5ysqZI82cWOnHkSspyZF7VOZeuVJBTQERQYT794c/zjdyxK3gOeDzcV1cF+e+73PzOsdzC6/zvu/3sRiGYQgAAAAAkG4Otg4AAAAAAFkNRQoAAAAATKJIAQAAAIBJFCkAAAAAMIkiBQAAAAAmUaQAAAAAwCSKFAAAAACYRJECAAAAAJMoUgAAAABgEkUKALKIDz/8UBaLRZcvX7Z1FDykU6dOyWKx6LPPPrN1FADAA6JIAcA9zJkzRxaLxfrl5uamkiVLqkePHoqKirJ1vCxnwIABslgsatWqla2jZIqpU6dqzpw5to5xVydOnFDXrl1VrFgxubm5ydPTU0FBQZo4caJu3Lhhen/2/FgB4FFxsnUAALB3I0aMUNGiRZWQkKDNmzdr2rRpWrNmjfbv368nnnjC1vGyBMMw9M0336hIkSJauXKlrl27ppw5c9o6VoaaOnWq8ubNqw4dOtg6ShqrV69WixYt5Orqqnbt2ql8+fK6efOmNm/erP79++vAgQOaMWOGqX3a62MFgEeJIgUA/6FBgwaqUqWKJKlLly7KkyePxo0bp++//16vvvrqQ+371q1bSklJkYuLS0ZEtVsbNmzQX3/9pZ9//lkhISFaunSp2rdvb+tY2d7JkyfVunVrFS5cWD///LMKFChgXde9e3cdP35cq1evtmHCzHX9+nV5eHjYOgaAbIpT+wDApLp160q6/UeqJNWuXVu1a9e+Y7sOHTqoSJEi1tv/vC5mwoQJKl68uFxdXXXw4EFJ0uHDh9WyZUvly5dP7u7uKlWqlN5///079hsdHa0OHTrI29tbXl5e6tixo+Lj49NsM3v2bNWtW1f58+eXq6urypYtq2nTpt2xr927dyskJER58+aVu7u7ihYtqk6dOqXZJiUlRRMmTFC5cuXk5uYmHx8fde3aVVevXk33cxYWFqayZcuqTp06Cg4OVlhY2B3bbNiwQRaLRYsWLdLw4cNVsGBB5cyZU82bN1dMTIwSExPVp08f5c+fXzly5FDHjh2VmJiYZh+3bt3SRx99ZH1uixQposGDB9+xncVi0YcffnhHhiJFiqQZZUk9vXPLli3q16+f8uXLJw8PD7388su6dOlSmvsdOHBAGzdutJ4KerfXxN2MHz9ehQsXlru7u2rVqqX9+/db182ePVsWi0W//fbbHfcbPXq0HB0dde7cuXvue+zYsYqLi9OsWbPSlKhUAQEB6t27d5qf91+vm/96rNHR0erTp4/8/f3l6uqqgIAAffLJJ0pJSUmzn7///ltt27aVp6envL291b59e+3du1cWi+WO0wZ//vln1axZUx4eHvL29lbTpk116NChNNukXkN48OBBvfbaa8qVK5dq1Kjx0M8hANwLI1IAYNKJEyckSXny5Hmg+8+ePVsJCQl688035erqqty5c+uPP/5QzZo15ezsrDfffFNFihTRiRMntHLlSo0aNSrN/Vu2bKmiRYtqzJgx+vXXX/XVV18pf/78+uSTT6zbTJs2TeXKlVOTJk3k5OSklStX6u2331ZKSoq6d+8uSbp48aLq16+vfPnyaeDAgfL29tapU6e0dOnSND+va9eumjNnjjp27KhevXrp5MmT+uKLL/Tbb79py5YtcnZ2vu/jTUxM1JIlS/TOO+9Ikl599VV17NhRkZGR8vX1vWP7MWPGyN3dXQMHDtTx48c1efJkOTs7y8HBQVevXtWHH36o7du3a86cOSpatKiGDh1qvW+XLl00d+5cNW/eXO+884527NihMWPG6NChQ1q2bJm5f6h/6Nmzp3LlyqVhw4bp1KlTmjBhgnr06KHvvvtOkjRhwgT17NlTOXLksJZfHx+f/9zvvHnzdO3aNXXv3l0JCQmaOHGi6tatq3379snHx0fNmzdX9+7dFRYWpkqVKqW5b1hYmGrXrq2CBQvec/8rV65UsWLFVL169XQ9zvS8bu73WOPj41WrVi2dO3dOXbt2VaFChbR161YNGjRIFy5c0IQJEyTdLueNGzfWzp079dZbb6l06dL6/vvv7zpKuW7dOjVo0EDFihXThx9+qBs3bmjy5MkKCgrSr7/+mubNCklq0aKFSpQoodGjR8swjId+DgHgngwAwF3Nnj3bkGSsW7fOuHTpknH27Fnj22+/NfLkyWO4u7sbf/31l2EYhlGrVi2jVq1ad9y/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U2/v3LlTO3bsUIMGDdJsV7FiRVWsWFFfffWVlixZotatW8vJ6b/fCx0wYIA8PDzUpUsXRUVF3bH+xIkTmjhxoqT0v26kez/Wli1batu2bQoPD79jXXR0tPV6tpCQECUlJWnmzJnW9SkpKZoyZUqa+xQoUEBPP/205s6dm+bn7d+/Xz/++KP13zw9HvQ5BIB74X8QAHhInTp10rhx4xQSEqLOnTvr4sWLmj59usqVK5dmAoj7mTRpkmrUqKFnnnlGb775pooWLapTp05p9erV+v33303lqV+/vlxcXNS4cWN17dpVcXFxmjlzpvLnz59mxGXu3LmaOnWqXn75ZRUvXlzXrl3TzJkz5enpaf0DtVatWuratavGjBmj33//XfXr15ezs7OOHTumxYsXa+LEiWrevPldcyxcuFCGYahJkyZ3Xd+wYUM5OTkpLCxMVatWNfUY7+app55S+/btNWPGDEVHR6tWrVrauXOn5s6dq9DQUNWpU8e6bZcuXdStWzc1a9ZM9erV0969exUeHq68efM+8M+vXLmypk2bppEjRyogIED58+e3fubYvQQEBKhGjRp66623lJiYqAkTJihPnjx3PTWuXbt2evfddyUp3aekFS9eXAsXLlSrVq1UpkwZtWvXTuXLl9fNmze1detWLV682Pq5Wel93dzvsfbv318rVqzQSy+9pA4dOqhy5cq6fv269u3bp//97386deqU8ubNq9DQUD333HN65513dPz4cZUuXVorVqzQlStXJKUd8fr000/VoEEDBQYGqnPnztbpz728vO76WWD38yDPIQDck62mCwQAe5c69fX9pgdPtWDBAqNYsWKGi4uL8fTTTxvh4eH3nP78008/ves+9u/fb7z88suGt7e34ebmZpQqVcoYMmSIdX3q9M6XLl26a86TJ09al61YscKoWLGi4ebmZhQpUsT45JNPjK+//jrNdr/++qvx6quvGoUKFTJcXV2N/PnzGy+99JKxe/fuO7LNmDHDqFy5suHu7m7kzJnTqFChgjFgwADj/Pnz93xOKlSoYBQqVOi+z1vt2rWN/PnzG0lJSdbpzxcvXnzXx/fvf4e7PR9JSUnG8OHDjaJFixrOzs6Gv7+/MWjQICMhISHNfZOTk4333nvPyJs3r/HEE08YISEhxvHjx+85/fm/f3Zq1vXr11uXRUZGGo0aNTJy5sxp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qA8//FDt2rWT9H+/A+Li4rRw4ULNmjVLx48f1+HDh5UvXz4bpwYy3unTp1W3bl317dtXPXr0sI5Q7dq1S88++6yio6O1fPly7dixQ76+vmrVqpVKly5t69iPPYoUMs27776rb7/91vphio6OjgoLC1NQUJC2bt2qiRMnatu2bfL09FRCQoIOHTokZ2dnW8cGMsWVK1es13Q88cQT2rhxo3x9fa0zlKX+0jx//ry++eYbvfTSSypVqpSNUwOZ45+zVvbp00eTJk3S8OHD9f7771vfPEgtU4ZhKCYmRvHx8Zziimxr3759evXVV7VixQp5enpq3rx5WrlypbZv3646depYPwpASnv8wLYoUsgU8+fPV9++fbVu3Tr5+fnJYrGoY8eO2r17t9atW6fy5ctr3759OnLkiE6fPq3evXvLycmJ6Z2RrV28eFHJyclq2rSprl69ql9++UW+vr7WUdzY2Fh5enryuTjItu71B2CPHj301Vdf6dtvv1VoaOh/bg9kdamv7ejoaHl7eysmJkb+/v569tlndezYMVWpUkVVqlRR1apV1bZtWw0fPlxvvPGGrWPjXyhSeGhLly5V3bp15e3tbV320UcfWT807p9/FNapU0c3btzQ9u3b79gPp/MhO0n9JXns2DFFR0fLYrGoYsWKcnFx0alTp9SyZUvFxMRYR6YmTJigM2fOaOzYsXJ0dOSPR2Q7qcfEli1btHnzZsXExKhcuXJq06aNJOmtt97S3Llz9e2336pJkyY2TgtkntRjYfXq1frss880atQoVa9eXceOHdPkyZPl7++vNm3ayNfXVw4ODgoJCVGTJk3UvXt3W0fHv/CWJx7K6tWr1bx5c02fPl2xsbHW5RcvXtSRI0ck3Z6ZLzExUZLUv39/RUVF6c8//7xjX5QoZBepvySXLFmiWrVqqW3btnruuefUokULLV26VEWKFNHixYuVN29elSxZUs2aNVP//v3Vvn17OTk5UaKQLVksFi1dulQNGzbUgQMHdPjwYY0cOVLNmzeXJE2bNk0dO3ZU27ZttXjxYhunBTJP6rHw6quvqlatWnJzc5MklShRQhMnTlT//v3l5+cnwzA0ePBg7d27Vw0aNLBxatwNRQoPpVGjRpo2bZoGDx6sL774QtHR0ZKkDh06KCkpScOGDZMk64fFubi4yNXVldKEbM1isVg/F23YsGH6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kerevbt1OSUK2cEff/yh6tWrq2fPnho/frx1eUhIiIoUKaKxY8cqKSnJOpFKnjx5lJKSolu3btkqMvBIOTg46JdfflG+fPn07rvvauPGjUpOTlbPnj31xRdfaNCgQXrxxRf12WefadasWfLy8uL3A7KN1DeNV65cqTfffFP/+9//lJycrPLlyys+Pl4+Pj56++235eDgoMTERA0dOlQbNmxQt27dKFFZGKf2wbTk5GTNnj1bb7/9tnx8fFS+fHmlpKQoJiZGKSkp2rJli5ydna0X3gNZ3dmzZ/XMM8+obt26+u6776zLJ0+erH379skwDJ04cUL169fXwIEDFRcXp48//lhLly7Vhg0blD9/fhumBzJH6h+O169fTzOhUHx8vGrXrq1bt27p888/1/PPPy9HR0ft2LFDp0+f1nPPPcd1gsiWVqxYoZYtW2rkyJFq2rSpSpQoIen29P5Tp05VTEyMihUrppSUFP32229au3atKlWqZOPUeBgUKTyw33//XV9//bWOHDkif39/VapUSd26deNzopDtnDp1Si1btlSBAgU0YMAABQUFacyYMRo1apQ2b96sIkWK6IMPPlBERITOnz+vsmXL6vjx4/rxxx/5JYlsbePGjRo4cKBmzJiRZta9GzduqFq1akpJSdGECRNUs2ZNubi42DApkLkiIyPVtGlTtWrVSv369btj/S+//KLffvtNv/32m5566im99NJLCggIsEFSZCSKFDIcI1HIjo4dO2ad2t/Hx0fff/+95s+fb510JS4uTpGRkfrhhx/k6+urKlWqMIUtsp0bN27IwcFBUVFR8vf3V2xsrEqWLKnSpUtr6tSpKleunHVmvgMHDqhy5coqU6aMJkyYoFq1atk6PpBpIiMjVa1aNU2aNElNmjS5Y/3Nmzd5MyEb4hopPJS79XBKFLKjEiVKaOLEibpx44YWLFigAQMGWEtUcnKycuTIoYCAAPXs2VMtWrSgRCHbOXTokF5//XVVqVJFxYsXV4UKFTR37lwdOXJEZ8+eVdeuXXXgwAE5ONz+0yIuLk6NGjVSzpw5VahQIRunBzLWv//+uXTpkq5fv2693ikxMdG67o8//tB3332XZhmyB4oUHgoXCuNxUrJkSU2bNk01a9ZURESENm/eLOn2mwcM7iM727dvnwIDA1WgQAH16dNHixYtUkBAgPr06aP+/ftr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'
}
]
}
}


If you ask me, it almost achieved what I wanted; it generated stats and charts in base64 format. However, I wasn’t completely satisfied. I thought, why not have the code produce just the data and let JavaScript handle the chart generation?

Another crucial issue I faced was that the generated code didn’t accept CSV files with different names due to the following clause:

Enter fullscreen mode Exit fullscreen mode

Field Validation:

  • Validate the presence of required fields in the CSV file:
    • Demographic Analysis: Requires age, gender, location.

This caused the Python code to explicitly look for specific fields, defeating the purpose of the project. I wanted Claude to infer field names and adapt accordingly. After many iterations, the following prompt worked as intended:

Enter fullscreen mode Exit fullscreen mode

You are a Python data analyst tasked with creating dynamic analysis code. I will provide you with a dataset in CSV format. Your task is to generate a Python script that:

  1. Dynamically identifies and categorizes fields based on their context and meaning in the dataset.
  2. Performs modular analysis for each category based on the available fields.
  3. Returns the output strictly in a JSON payload format.

{FIELDS_AND_TYPES}

Output Requirements

  1. If Required Fields Are Missing:

    • If no fields can be inferred for certain categories, return:
     {
       "error": "true",
       "message": "Unable to infer required fields for <category> analysis."
     }
    
  2. If Code Is Successfully Generated:

    • Return a JSON payload with:
     {
       "error": "false",
       "message": "<Entire Python code, escaped for valid JSON format>"
     }
    
  3. Code Escaping:

    • Ensure the message field is a valid JSON string:
      • Escape all special characters, including newlines (\n) and quotes (\").
      • Use json.dumps() in Python or equivalent methods to serialize the code as a JSON-compatible string.
      • Avoid using multi-line string blocks (""") in the JSON output.
  4. Additional Requirements:

    • Do not include any explanatory text, comments, or preambles in the response.
    • Avoid any prefixes like "Here's the code" or suffixes explaining the output.
    • Only output the final JSON payload.

Code Requirements

  1. Input Handling:

    • Accept a CSV file as a parameter in the main() function. The parameter must be named {CSV_FILE_PATH} and dynamically passed when calling the function.
    • Validate the csv_file parameter:
      • Check if csv_file is a non-empty string.
      • Check if the file exists and is readable before proceeding with analysis.
      • Raise a clear error (e.g., FileNotFoundError) if the file does not exist.
  2. No if __name__ == ' __main__': Block:

    • Do not include the if __name__ == ' __main__': block in the generated code.
  3. Output Restrictions:

    • Do not include any print() statements in the generated code.
    • Return results exclusively via the main() function, as a Python dictionary.
    • Avoid any direct output, logging, or side effects.
  4. Invoke main at the End of the Script:

    • Ensure the script includes a call to main("{CSV_FILE_PATH}") at the end, passing the {CSV_FILE_PATH} placeholder dynamically.
    • Replace {CSV_FILE_PATH} with the actual file path during runtime.
    • Do not print or log the results in the script.
  5. Output Example for the Generated Code:

   def main(csv_file: str) -> Dict[str, Any]:
       try:
           if not csv_file:
               return {'error': True, 'message': 'No file provided'}
           if not os.path.exists(csv_file):
               return {'error': True, 'message': f'File not found: {csv_file}'}
           # Load and analyze the file
           df = pd.read_csv(csv_file)
           if df.empty:
               return {'error': True, 'message': 'Empty CSV file'}
           # Perform analysis
           results = {'message': 'File processed successfully'}
           return {'error': False, 'results': results}
       except Exception as e:
           return {'error': True, 'message': str(e)}

   results = main("{CSV_FILE_PATH}")
Enter fullscreen mode Exit fullscreen mode

Dynamic Analysis

  • Dynamically analyze the column names and sample data to:
  • Infer field types (e.g., numeric, categorical, boolean)
  • Determine the relevance of fields for analysis

Field Validation

  • Dynamically categorize fields into:
    • Demographic Analysis: Fields describing age, gender, or geographic distribution.
    • Behavioral Analysis: Fields related to customer behavior, such as frequency, purchase history, or subscriptions.
    • Purchase Patterns: Fields related to purchase amounts, categories, or discounts.
    • Product Preferences: Fields describing product details, such as size, color, and seasonality.
    • Response Analysis: Fields indicating customer feedback, such as ratings or reviews.
  1. Dynamic Field Identification:

    • Implement logic to identify and classify fields dynamically based on:
      • Substring matches (e.g., AgeGroup → Age, Region → Location).
      • Value patterns (e.g., numerical, categorical, boolean).
  2. Modular Analysis:

    • Perform analysis for each identified category.
    • Skip categories where no relevant fields can be inferred.
  3. Output:

    • Return results in JSON-compatible format, structured as:
     {
       "error": false,
       "results": {
         "summary_statistics": {...},
         "segmentation_results": {...},
         "behavioral_analysis": {...},
         "purchase_patterns": {...},
         "product_preferences": {...},
         "visualizations": [...]
       }
     }
    
  4. Escape Code in JSON Output:

    • The Python script should be included in the message field as a single JSON-compatible string, with all newlines (\n) and special characters escaped.

Below is the generated code from one of the recent prompts:

Enter fullscreen mode Exit fullscreen mode

import pandas as pd
import json
from typing import Dict, Any
import numpy as np

def categorize_fields(df):
categories = {
'demographic': [],
'behavioral': [],
'purchase': [],
'response': []
}

for column in df.columns:
    col_lower = column.lower()
    if any(term in col_lower for term in ['age', 'gender', 'region']):
        categories['demographic'].append(column)
    elif any(term in col_lower for term in ['frequency', 'membership', 'preferred']):
        categories['behavioral'].append(column)
    elif any(term in col_lower for term in ['purchase', 'promo', 'value']):
        categories['purchase'].append(column)
    elif any(term in col_lower for term in ['review', 'score', 'rating']):
        categories['response'].append(column)

return categories
Enter fullscreen mode Exit fullscreen mode

def analyze_demographics(df, demographic_fields):
results = {}
for field in demographic_fields:
results[field] = df[field].value_counts().to_dict()
return results

def analyze_behavioral(df, behavioral_fields):
results = {}
for field in behavioral_fields:
results[field] = df[field].value_counts().to_dict()
return results

def analyze_purchases(df, purchase_fields):
results = {}
numeric_fields = df[purchase_fields].select_dtypes(include=[np.number]).columns

for field in numeric_fields:
    results[field] = {
        'mean': float(df[field].mean()),
        'median': float(df[field].median()),
        'std': float(df[field].std())
    }

categorical_fields = list(set(purchase_fields) - set(numeric_fields))
for field in categorical_fields:
    results[field] = df[field].value_counts().to_dict()

return results
Enter fullscreen mode Exit fullscreen mode

def analyze_response(df, response_fields):
results = {}
for field in response_fields:
if df[field].dtype in [np.float64, np.int64]:
results[field] = {
'mean': float(df[field].mean()),
'median': float(df[field].median())
}
else:
results[field] = df[field].value_counts().to_dict()
return results

def main(csv_file: str) -> Dict[str, Any]:
try:
df = pd.read_csv(csv_file)
categories = categorize_fields(df)

    results = {
        'error': False,
        'results': {
            'demographic_analysis': analyze_demographics(df, categories['demographic']),
            'behavioral_analysis': analyze_behavioral(df, categories['behavioral']),
            'purchase_analysis': analyze_purchases(df, categories['purchase']),
            'response_analysis': analyze_response(df, categories['response'])
        }
    }

    return results

except Exception as e:
    return {
        'error': True,
        'message': f'Analysis failed: {str(e)}'
    }
Enter fullscreen mode Exit fullscreen mode

results = main('shopping_behavior_test.csv')
print(json.dumps(results, indent=2))


#### Send JSON to Claude for Python code generation

Below is the code that executes the generated Python code and then makes things compatible with Python data types:

Enter fullscreen mode Exit fullscreen mode

To execute generated Python code

def execute_runtime_code(generated_code):
try:
sandbox_namespace = {}
# Execute the generated code in the sandboxed namespace
exec(generated_code, sandbox_namespace)
if "main" not in sandbox_namespace:
return {
"error": True,
"message": "main function not found",
"results": None
}

    if "results" in sandbox_namespace:
        return {
            "error": False,
            "message": "Execution completed successfully.",
            "results": sandbox_namespace["results"]
        }
    else:
        return {
            "error": True,
            "message": "No 'results' variable found in the generated script.",
            "results": None
        }
except Exception as e:
    return {
        "error": True,
        "message": f"An error occurred during execution: {str(e)}",
        "results": None
    }
Enter fullscreen mode Exit fullscreen mode

Casting numpy datatypes to Python built-in types

def convert_to_serializable(obj):
if isinstance(obj, dict):
return {k: convert_to_serializable(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [convert_to_serializable(item) for item in obj]
elif isinstance(obj, np.integer):
return int(obj)
elif isinstance(obj, np.floating):
return float(obj)
elif isinstance(obj, np.ndarray):
return obj.tolist()
return obj


Alright. The next step was to access this generated data in JavaScript and show both charts and stats on the webpage.

#### Execute Python to create JSON structure.

The plan was to send the Python-generated JSON output via an AJAX request and access it in the JavaScript code. However, this approach failed because, while the parent JSON fields remained consistent, the inner fields varied. Hard-coding the JavaScript wasn’t feasible, as files with different schemas caused errors for undefined fields. The solution? Create a new prompt that generates both HTML and JavaScript by inferring the JSON structure produced by the Python code.

#### For Dashboard’s JavaScript and HTML Generation

The very first prompt was:

Enter fullscreen mode Exit fullscreen mode

You are a front-end developer tasked with creating dynamic dashboards based on JSON data. The JSON data includes fixed top-level categories but variable inner keys. The JSON Data is given below:

{DASHBOARD_JSON}
Enter fullscreen mode Exit fullscreen mode

Generate:

  1. HTML Structure:

    • Only the relevant sections of the HTML (not the entire <html> page).
    • Use Bootstrap for layout and styling.
    • Include summary cards for key metrics.
    • Each card must have a dynamic id for future updates.
    • Dynamically generate chart containers for data visualization.
  2. JavaScript Code:

    • Write JavaScript using jQuery to:
      • Dynamically populate the summary cards.
      • Generate charts based on the JSON data.
    • Assume that the JSON data will be provided as a variable named {DASHBOARD_JSON}.
    • Focus on generating dynamic JS for demographic_analysis, behavioral_analysis, purchase_analysis, and response_analysis.
  3. Output Requirements:

    • Output a JSON object with two fields:
      • html: The Bootstrap-compatible HTML structure as a JSON string.
      • javascript: The jQuery code to dynamically update the dashboard.
  4. JSON Input Example:

{
  "error": false,
  "message": "Execution completed successfully.",
  "results": {
    "error": false,
    "results": {
      "demographic_analysis": {
        "age_stats": {
          "mean": 44.06,
          "median": 44.0,
          "std": 15.2
        },
        "gender_distribution": {
          "Male": 2652,
          "Female": 1248
        }
      },
      "behavioral_analysis": {
        "subscription_rate": 0.27
      },
      "purchase_analysis": {
        "amount_stats": {
          "mean": 59.76,
          "total": 233081
        }
      },
      "response_analysis": {
        "rating_stats": {
          "mean": 3.75
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. HTML Section Example:
<section class="row mb-5" id="demographic-analysis">
  <div class="col-md-4">
    <div class="card text-white bg-primary mb-3">
      <div class="card-body">
        <h5 class="card-title">Average Age</h5>
        <p class="card-text" id="avg-age">0.00</p>
      </div>
    </div>
  </div>
</section>
Enter fullscreen mode Exit fullscreen mode
  1. Expected Output Format:
{
  "html": "<section class='row mb-5' id='demographic-analysis'> ... </section>",
  "javascript": "$('#avg-age').text({DASHBOARD_JSON}.results.results.demographic_analysis.age_stats.mean); ... // Full JS code"
}
Enter fullscreen mode Exit fullscreen mode
  1. Output Strictness: Do not include any preamble or suffix like "Here's the solution" or "I'll help you with this." The response must contain only the final JSON payload, nothing else.

This prompt accepts the JSON structure generated by the generated code( _yeah kind of “inception” you know_).

Another thing it was doing to produce the generated output of both HTML and JS in JSON format:

Enter fullscreen mode Exit fullscreen mode
  1. Expected Output Format:
{
  "html": "<section class='row mb-5' id='demographic-analysis'> ... </section>",
  "javascript": "$('#avg-age').text({DASHBOARD_JSON}.results.results.demographic_analysis.age_stats.mean); ... // Full JS code"
}
Enter fullscreen mode Exit fullscreen mode

It didn’t work due to encoding and escaping issues with the JSON. I decided to return the data in XML format instead. It was cleaner too. Below is one version of the dashboard prompt:

Enter fullscreen mode Exit fullscreen mode

You are a front-end developer tasked with creating dynamic dashboards based on JSON data. The JSON data includes fixed top-level categories but variable inner keys. The JSON Data is given below:

{DASHBOARD_JSON}
Enter fullscreen mode Exit fullscreen mode

Generate:

  1. HTML Structure:

    • Only the relevant sections of the HTML (not the entire <html> page).
    • Use Bootstrap for layout and styling.
    • Include summary cards for key metrics.
    • Each card must have a dynamic id for future updates.
    • Dynamically generate chart containers for data visualization.
  2. JavaScript Code:

    • Write JavaScript using jQuery to: Variable Declaration: - Define a variable rawData containing the JSON string from {DASHBOARD_JSON}. - Ensure rawData is a properly escaped and valid JSON string for use in JavaScript. Parsing Logic - Parse rawData into a JavaScript object using JSON.parse. Function Definition:
    • Create a function named generateDashboard that accepts a single parameter dashboardData.
      • The function must:
        • Dynamically populate summary cards.
        • Dynamically generate charts based on the data in dashboardData.
  3. Function Invocation:

    • Define a variable rawData containing the JSON string from {DASHBOARD_JSON}.
  4. Code Structure:

    • Do not include $(document).ready in the generated code.
    • Only provide:
      1. The function definition for generateDashboard.
      2. The variable rawData with {DASHBOARD_JSON} as the placeholder.
      3. The JSON.parse logic.
      4. The generateDashboard function call.
  5. Dynamic Logic:

    • Assume the input JSON follows this structure:
     {
       "results": {
         "demographic_analysis": { ... },
         "behavioral_analysis": { ... },
         "purchase_analysis": { ... },
         "response_analysis": { ... }
       }
     }
    
  • Use dashboardData.results as the base for extracting and visualizing data.
  1. Output Requirements:

    • Generate an XML response with the following format:
    • <dashboard>: Root element containing:
    • <html>: Encapsulates the HTML structure needed to display the dashboard.
      • Use a <![CDATA[]]> section for HTML content.
    • <javascript>: Encapsulates the JavaScript code required to populate and render the dashboard.
      • Use a <![CDATA[]]> section for JavaScript content.
    • Avoid any prefixes, suffixes, or extra text outside the XML structure. Ensure the generated XML is valid and can be directly parsed.
  2. JSON Input Example:

{
  "error": false,
  "message": "Execution completed successfully.",
  "results": {
    "error": false,
    "results": {
      "demographic_analysis": {
        "age_stats": {
          "mean": 44.06,
          "median": 44.0,
          "std": 15.2
        },
        "gender_distribution": {
          "Male": 2652,
          "Female": 1248
        }
      },
      "behavioral_analysis": {
        "subscription_rate": 0.27
      },
      "purchase_analysis": {
        "amount_stats": {
          "mean": 59.76,
          "total": 233081
        }
      },
      "response_analysis": {
        "rating_stats": {
          "mean": 3.75
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. HTML Summary Card Example:
<div style="margin-top:20px;" class="row">
        <div class="col-md-12">
            <!-- Summary Section -->
            <section class="row mb-5">
                <div class="col-md-4">
                    <div class="card text-white bg-primary mb-3">
                        <div class="card-body">
                            <h5 class="card-title">Average Purchase Amount</h5>
                            <p class="card-text" id="average-purchase-amount">$0.00</p>
                        </div>
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="card text-white bg-success mb-3">
                        <div class="card-body">
                            <h5 class="card-title">Total Revenue</h5>
                            <p class="card-text" id="total-revenue">$0.00</p>
                        </div>
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="card text-white bg-warning mb-3">
                        <div class="card-body">
                            <h5 class="card-title">Subscription Rate</h5>
                           <p class="card-text" id="subscription-rate">0%</p>
                        </div>
                    </div>
                </div>
            </section>
        </div>
    </div>
Enter fullscreen mode Exit fullscreen mode

HTML Charts Example:

<section>
                <style>
                .chart-container {
                    display: flex;
                    justify-content: center; /* Center horizontally */
                    align-items: center; /* Center vertically (if needed) */
                }
                .charthing {
                width: 400px !important;
                height: 300px !important;
                }
                </style>
                <!-- Age Distribution Chart -->
                <div class="chart-container">
                    <div>
                        <h5 class="text-center">Age Distribution</h5>
                        <canvas class="charthing" id="ageDistributionChart" width="800" height="400" style="display: block; box-sizing: border-box; height: 200px; width: 400px;"></canvas>
                    </div>
                </div>

                <!-- Gender Distribution Chart -->
                <div class="chart-container">
                    <h5>Gender Distribution</h5>
                    <canvas class="charthing" id="genderDistributionChart" width="600" height="600" style="display: block; box-sizing: border-box; height: 300px; width: 300px;"></canvas>
                </div>

                <!-- Color Preferences Chart -->
                <div class="chart-container">
                    <h5>Color Preferences</h5>
                    <canvas class="charthing" id="colorPreferencesChart" width="1200" height="600" style="display: block; box-sizing: border-box; height: 300px; width: 600px;"></canvas>
                </div>
            </section>
Enter fullscreen mode Exit fullscreen mode
  1. Expected Output Format:
{
  "html": "<section class='row mb-5' id='demographic-analysis'> ... </section>",
  "javascript": "$('#avg-age').text({DASHBOARD_JSON}.results.results.demographic_analysis.age_stats.mean); ... // Full JS code"
}
Enter fullscreen mode Exit fullscreen mode
  1. Output Strictness: Do not include any preamble or suffix like "Here's the solution" or "I'll help you with this." The response must contain only the final JSON payload, nothing else.

It almost worked but there was a glitch. In the section that was responsible for generating JS code, somehow it was not assigning the returned JSON for the dashboard itself in a variable:

Enter fullscreen mode Exit fullscreen mode
  1. JavaScript Code:
    • Write JavaScript using jQuery to: Variable Declaration: - Define a variable rawData containing the JSON string from {DASHBOARD_JSON}. - Ensure rawData is a properly escaped and valid JSON string for use in JavaScript. Parsing Logic - Parse rawData into a JavaScript object using JSON.parse. Function Definition:
    • Create a function named generateDashboard that accepts a single parameter dashboardData.
      • The function must:
        • Dynamically populate summary cards.
        • Dynamically generate charts based on the data in dashboardData.

It took a few iterations to come up with an output like the one below:

Enter fullscreen mode Exit fullscreen mode

<?xml version="1.0" encoding="UTF-8"?>


<![CDATA[

Average Age

0

127.0.0.1 - - [15/Jan/2025 09:50:07] "POST /upload HTTP/1.1" 200 -
Total Sales

$0

Average Rating

0

Subscription Rate

0%

Gender Distribution
Size Distribution
Category Distribution
Seasonal Preferences

]]>


<![CDATA[
const rawData = {"error":false,"message":"Execution completed successfully.","results":{"error":false,"results":{"demographic_analysis":{"age_stats":{"mean":44.06846153846154,"median":44.0,"std":15.207589127162382},"gender_distribution":{"Male":2652,"Female":1248},"location_distribution":{"Montana":96,"California":95,"Idaho":93,"Illinois":92,"Alabama":89,"Minnesota":88,"Nebraska":87,"New York":87,"Nevada":87,"Maryland":86,"Delaware":86,"Vermont":85,"Louisiana":84,"North Dakota":83,"Missouri":81,"West Virginia":81,"New Mexico":81,"Mississippi":80,"Indiana":79,"Georgia":79,"Kentucky":79,"Arkansas":79,"North Carolina":78,"Connecticut":78,"Virginia":77,"Ohio":77,"Tennessee":77,"Texas":77,"Maine":77,"South Carolina":76,"Colorado":75,"Oklahoma":75,"Wisconsin":75,"Oregon":74,"Pennsylvania":74,"Washington":73,"Michigan":73,"Alaska":72,"Massachusetts":72,"Wyoming":71,"Utah":71,"New Hampshire":71,"South Dakota":70,"Iowa":69,"Florida":68,"New Jersey":67,"Hawaii":65,"Arizona":65,"Kansas":63,"Rhode Island":63}},"purchase_patterns":{"amount_stats":{"mean":59.76435897435898,"median":60.0,"total":233081},"category_distribution":{"Clothing":1737,"Accessories":1240,"Footwear":599,"Outerwear":324},"discount_usage":0.43,"promo_usage":0.43},"product_preferences":{"size_distribution":{"M":1755,"L":1053,"S":663,"XL":429},"color_preferences":{"Olive":177,"Yellow":174,"Silver":173,"Teal":172,"Green":169,"Black":167,"Cyan":166,"Violet":166,"Gray":159,"Maroon":158,"Orange":154,"Charcoal":153,"Pink":153,"Magenta":152,"Blue":152,"Purple":151,"Peach":149,"Red":148,"Beige":147,"Indigo":147,"Lavender":147,"Turquoise":145,"White":142,"Brown":141,"Gold":138},"seasonal_preferences":{"Spring":999,"Fall":975,"Winter":971,"Summer":955},"item_popularity":{"Blouse":171,"Jewelry":171,"Pants":171,"Shirt":169,"Dress":166,"Sweater":164,"Jacket":163,"Belt":161,"Sunglasses":161,"Coat":161,"Sandals":160,"Socks":159,"Skirt":158,"Shorts":157,"Scarf":157,"Hat":154,"Handbag":153,"Hoodie":151,"Shoes":150,"T-shirt":147,"Sneakers":145,"Boots":144,"Backpack":143,"Gloves":140,"Jeans":124}},"customer_behavior":{"avg_review_rating":3.7499487179487176,"subscription_rate":0.27,"shipping_preferences":{"Free Shipping":675,"Standard":654,"Store Pickup":650,"Next Day Air":648,"Express":646,"2-Day Shipping":627},"payment_methods":{"PayPal":677,"Credit Card":671,"Cash":670,"Debit Card":636,"Venmo":634,"Bank Transfer":612},"purchase_frequency":{"Every 3 Months":584,"Annually":572,"Quarterly":563,"Monthly":553,"Bi-Weekly":547,"Fortnightly":542,"Weekly":539},"avg_previous_purchases":25.35153846153846}}}};

const dashboardData = JSON.parse(rawData);
const data = dashboardData.results.results;

function generateDashboard(data) {
// Update Summary Cards
$('#avg-age').text(data.demographic_analysis.age_stats.mean.toFixed(2));
$('#total-sales').text($${data.purchase_patterns.amount_stats.total.toLocaleString()});
$('#avg-rating').text(data.customer_behavior.avg_review_rating.toFixed(2));
$('#sub-rate').text(${(data.customer_behavior.subscription_rate * 100).toFixed(1)}%);

// Gender Distribution Chart
new Chart(document.getElementById('genderChart'), {
    type: 'pie',
    data: {
        labels: Object.keys(data.demographic_analysis.gender_distribution),
        datasets: [{
            data: Object.values(data.demographic_analysis.gender_distribution),
            backgroundColor: ['#36A2EB', '#FF6384']
        }]
    }
});

// Size Distribution Chart
new Chart(document.getElementById('sizeChart'), {
    type: 'bar',
    data: {
        labels: Object.keys(data.product_preferences.size_distribution),
        datasets: [{
            label: 'Size Distribution',
            data: Object.values(data.product_preferences.size_distribution),
            backgroundColor: '#4BC0C0'
        }]
    }
});

// Category Distribution Chart
new Chart(document.getElementById('categoryChart'), {
    type: 'bar',
    data: {
        labels: Object.keys(data.purchase_patterns.category_distribution),
        datasets: [{
            label: 'Category Distribution',
            data: Object.values(data.purchase_patterns.category_distribution),
            backgroundColor: '#FFCE56'
        }]
    }
});

// Seasonal Preferences Chart
new Chart(document.getElementById('seasonalChart'), {
    type: 'bar',
    data: {
        labels: Object.keys(data.product_preferences.seasonal_preferences),
        datasets: [{
            label: 'Seasonal Preferences',
            data: Object.values(data.product_preferences.seasonal_preferences),
            backgroundColor: '#FF9F40'
        }]
    }
});
Enter fullscreen mode Exit fullscreen mode

}

generateDashboard(data);
]]>



As you can see, the rawData variable holds the JSON output generated by the Python code. It produces summary cards and charts by inferring the structure of the incoming JSON. The entire HTML and JS code is generated based on the inference of the JSON output from the Python code.

![](https://cdn-images-1.medium.com/max/774/0*bCxFroiqPAAB_Uua.png)

#### One more thing

No, no, I’m not trying to [imitate](https://www.youtube.com/watch?v=in9SX3enCHU) Steve Jobs. When I was preparing the demo for this project, I initially planned to create a CSV file unrelated to behavior analysis just for testing. I uploaded the file, and to my surprise, Claude accepted it and generated the damn code.

Enter fullscreen mode Exit fullscreen mode

A,B,C
LOL,LOL,LOL


Obviously, the generated JS code didn’t work at all. The code produced a dictionary like this:

Enter fullscreen mode Exit fullscreen mode

{
'error': False,
'message': 'Execution completed successfully.',
'results': {
'error': True,
'results': {
.......


So, while the error inside the results was correctly returning True with the relevant text in the message field, the outer error was still False. This was happening due to the following code:

Enter fullscreen mode Exit fullscreen mode

results = sandbox_namespace['results']
if results['error']:
return {
"error": True,
"message": "Execution failed",
"results": sandbox_namespace["results"]
}
else:
return {
"error": False,
"message": "Execution completed successfully.",
"results": sandbox_namespace["results"]
}


As you can see I was only testing the existence of results key only. I fixed it by doing the following:

Enter fullscreen mode Exit fullscreen mode

results = sandbox_namespace['results']
if results['error']:
return {
"error": True,
"message": "Execution failed",
"results": sandbox_namespace["results"]
}
else:
return {
"error": False,
"message": "Execution completed successfully.",
"results": sandbox_namespace["results"]
}


OK, this was fixed, but I also need to make changes on the JS side in the index.html file.

Enter fullscreen mode Exit fullscreen mode

if(response.error) {
console.log(response.message)
$("#errorMessage").show()
$("#errorMessage").html(<div class="alert alert-danger">${response.message}</div>)
$("#wait").hide()
return true
}


OK everything was fine but then I started getting the error:

Uncaught SyntaxError: Identifier 'rawData' has already been declared

Somehow, the rawData variable reference was being retained. I tried the following, but it still didn't work:

Enter fullscreen mode Exit fullscreen mode

$("#runtimeJs").remove()
// Dynamically execute the JavaScript
const script = document.createElement('script')
script.id = "runtimeJs"
script.type = 'text/javascript'
script.text = jsContent
document.body.appendChild(script);


The relevant prompt section was:

Enter fullscreen mode Exit fullscreen mode
  1. JavaScript Code:

    • Write JavaScript using jQuery to:
      Variable Declaration:
      - Define a variable rawData containing the JSON string from {DASHBOARD_JSON}.
      Example:

      ```javascript
      const rawData = `{DASHBOARD_JSON}`;
      ```
      
      - Ensure `rawData` is a properly escaped and valid JSON string for use in JavaScript.
      

      Parsing Logic
      - Parse the JSON string into a JavaScript object using JSON.parse
      Example:

      ```javascript
          const dashboardData = JSON.parse(rawData);
      ```
      
      • Extract the main data object for rendering charts and cards:
          const data = dashboardData.results.results;
      

First, it was declared as const, preventing the removal of existing references. The final changes are:

Enter fullscreen mode Exit fullscreen mode
  1. JavaScript Code:

    • Write JavaScript using jQuery to:
      Variable Cleanup (Before Declaration)
      - Ensure rawData is removed before defining it again.
      - Ensure dashboardData is removed before defining it again.
      Example:

      ```javascript
       delete window.rawData
       delete window.dashboardData
      ```
      

      Variable Declaration:
      - Define a variable rawData using var (instead of const) to avoid redeclaration errors.
      Example:

      ```javascript
      var rawData = `{DASHBOARD_JSON}`;
      ```
      
      - Ensure `rawData` is a properly escaped and valid JSON string for use in JavaScript.
      

      Parsing Logic
      - Parse the JSON string into a JavaScript object using JSON.parse
      Example:

      ```javascript
          var dashboardData = JSON.parse(rawData);
      ```
      
      • Extract the main data object for rendering charts and cards:
          var data = dashboardData.results.results;
      

Now, it generated the JS code as follows:

Enter fullscreen mode Exit fullscreen mode

// Clean up existing variables
delete window.rawData;
delete window.dashboardData;

// Define raw data
var rawData = ....


As you can see, the generated code now clears all context related to both rawData and dashboardData from the existing browser window. To be on the safe side, I added the following before injecting and executing the runtime JS code in the current window:

Enter fullscreen mode Exit fullscreen mode

// Cleaning previous JS execution
// Remove existing one
$("#runtimeJs").remove()
window.rawData = undefined
delete window.rawData
window.dashboardData = undefined
delete window.dashboardData

                    if (window.Chart) {
                        Chart.helpers.each(Chart.instances, function(instance) {
                            instance.destroy();
                        });
                    }
Enter fullscreen mode Exit fullscreen mode

.....




Now it was removing all existing traces of both variables before injecting and executing the incoming JS code.

And while I was running the final tests, the app suddenly stopped working. I then received the following email:

![](https://cdn-images-1.medium.com/max/1024/0*WkAS2aTF2fZtX_W_.png)

Ouch! My entire $5 free credit evaporated. I had to purchase credits just to run the demo.

### Conclusion

Alright, so you saw how amazing and powerful Claude is to perform such kinds of tasks. Claude is not only good at code generation but also at performing data analysis. I don’t know whether you got excited similar to how I was while working on this project, but I’d definitely tell you that it took hours and multiple sessions over days to make it happen. I have not covered all the “toy scripts” I had written for different components of this project, as it would make this post unnecessarily lengthy, and you could have gotten bored. However, I will be putting all prompt iterations in a separate folder for your learning. Hope you’ll enjoy it. Like always, the code is available on [GitHub](https://github.com/kadnan/Adaptive-Customer-Behavior-Analysis-Dashboard).

_Looking to create something similar or even more exciting?_ [_Schedule_](https://calendly.com/kadnan/one_one_15min) _a meeting or email me at_ **_kadnan @ gmail.com._**

**Love What You’re Learning Here?**  
_If my posts have sparked ideas or saved you time, consider_ [_supporting_](https://adnansiddiqi.me/donate.html) _my journey of learning and sharing. Even a small contribution helps me keep this blog alive and thriving._

_Originally published at_ [_https://blog.adnansiddiqi.me_](https://blog.adnansiddiqi.me/create-an-adaptive-customer-behavior-analytics-dashboard-with-claude-ai-and-python/) _on January 30, 2025._

* * *
Enter fullscreen mode Exit fullscreen mode

Top comments (0)