
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