Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Read by thought-leaders and decision-makers around the world. Phone Number: +1-650-246-9381 Email: [email protected]
228 Park Avenue South New York, NY 10003 United States
Website: Publisher: https://towardsai.net/#publisher Diversity Policy: https://towardsai.net/about Ethics Policy: https://towardsai.net/about Masthead: https://towardsai.net/about
Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Founders: Roberto Iriondo, , Job Title: Co-founder and Advisor Works for: Towards AI, Inc. Follow Roberto: X, LinkedIn, GitHub, Google Scholar, Towards AI Profile, Medium, ML@CMU, FreeCodeCamp, Crunchbase, Bloomberg, Roberto Iriondo, Generative AI Lab, Generative AI Lab Denis Piffaretti, Job Title: Co-founder Works for: Towards AI, Inc. Louie Peters, Job Title: Co-founder Works for: Towards AI, Inc. Louis-François Bouchard, Job Title: Co-founder Works for: Towards AI, Inc. Cover:
Towards AI Cover
Logo:
Towards AI Logo
Areas Served: Worldwide Alternate Name: Towards AI, Inc. Alternate Name: Towards AI Co. Alternate Name: towards ai Alternate Name: towardsai Alternate Name: towards.ai Alternate Name: tai Alternate Name: toward ai Alternate Name: toward.ai Alternate Name: Towards AI, Inc. Alternate Name: towardsai.net Alternate Name: pub.towardsai.net
5 stars – based on 497 reviews

Frequently Used, Contextual References

TODO: Remember to copy unique IDs whenever it needs used. i.e., URL: 304b2e42315e

Resources

Take our 85+ lesson From Beginner to Advanced LLM Developer Certification: From choosing a project to deploying a working product this is the most comprehensive and practical LLM course out there!

Publication

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

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

Last Updated on February 6, 2025 by Editorial Team

Author(s): Adnan Siddiqi

Originally published on Towards AI.

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:

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

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'
}
}

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:**
```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.

{
"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:

{
'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:

[
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:

{
'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:

**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:

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:
```json
{
"error": "true",
"message": "Unable to infer required fields for <category> analysis."
}
```

2. **If Code Is Successfully Generated:**
- Return a JSON payload with:
```json
{
"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:**
```python
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}")
```

### **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:
```json
{
"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:

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


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


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)}'
}


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:

# 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
}


# 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:

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:

```json
{DASHBOARD_JSON}
```

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:**
```json
{
"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
}
}
}
}
}
```

5. **HTML Section Example:**
```html
<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>
```

6. **Expected Output Format:**
```json
{
"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"
}
```
7. **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:

6. **Expected Output Format:**
```json
{
"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"
}
```

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:

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:

```json
{DASHBOARD_JSON}
```

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`.

2. **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:
```json
{
"results": {
"demographic_analysis": { ... },
"behavioral_analysis": { ... },
"purchase_analysis": { ... },
"response_analysis": { ... }
}
}
```
- Use `dashboardData.results` as the base for extracting and visualizing data.

3. **Output Requirements:**
- Generate an XML response with the following format:
1. `<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.
2. Avoid any prefixes, suffixes, or extra text outside the XML structure. Ensure the generated XML is valid and can be directly parsed.

4. **JSON Input Example:**
```json
{
"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
}
}
}
}
}
```

5. **HTML Summary Card Example:**
```html
<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>
```

**HTML Charts Example:**
```html
<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>
```
6. **Expected Output Format:**
```json
{
"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"
}
```
7. **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:

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`.

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

<?xml version="1.0" encoding="UTF-8"?>
<dashboard>
<html>
<![CDATA[
<div class="container-fluid">
<!-- Summary Cards -->
<div class="row mt-4">
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body">
<h5 class="card-title">Average Age</h5>
<p class="card-text" id="avg-age">0</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white">
127.0.0.1 - - [15/Jan/2025 09:50:07] "POST /upload HTTP/1.1" 200 -
<div class="card-body">
<h5 class="card-title">Total Sales</h5>
<p class="card-text" id="total-sales">$0</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white">
<div class="card-body">
<h5 class="card-title">Average Rating</h5>
<p class="card-text" id="avg-rating">0</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white">
<div class="card-body">
<h5 class="card-title">Subscription Rate</h5>
<p class="card-text" id="sub-rate">0%</p>
</div>
</div>
</div>
</div>

<!-- Charts -->
<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Gender Distribution</h5>
<canvas id="genderChart"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Size Distribution</h5>
<canvas id="sizeChart"></canvas>
</div>
</div>
</div>
</div>

<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Category Distribution</h5>
<canvas id="categoryChart"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Seasonal Preferences</h5>
<canvas id="seasonalChart"></canvas>
</div>
</div>
</div>
</div>
</div>
]]>
</html>
<javascript>
<![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'
}]
}
});
}

generateDashboard(data);
]]>
</javascript>
</dashboard>

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.

One more thing

No, no, I’m not trying to imitate 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.

A,B,C
LOL,LOL,LOL

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

{
'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:

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:

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.

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:

$("#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:

2. **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:
```javascript
const data = dashboardData.results.results;
`
``

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

2. **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:
```javascript
var data = dashboardData.results.results;
`
``

Now, it generated the JS code as follows:

// 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:

// 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();
});
}
.....

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:

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.

Looking to create something similar or even more exciting? Schedule 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 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 on January 30, 2025.

Join thousands of data leaders on the AI newsletter. Join over 80,000 subscribers and keep up to date with the latest developments in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming aΒ sponsor.

Published via Towards AI

Feedback ↓