← back to Handbag Auth Nextjs
analytics/visualizations.py
438 lines
#!/usr/bin/env python3
"""
LUXVAULT Analytics Visualizations
Generate chart-ready data for frontend visualization libraries
"""
import json
from analytics_engine import AuctionAnalytics
import numpy as np
from datetime import datetime, timedelta
class AnalyticsVisualizations:
"""Generate visualization data for charts and graphs"""
def __init__(self):
self.analytics = AuctionAnalytics()
def price_trend_chart(self, brand=None, months=12):
"""
Generate time series data for price trends
Format: Ready for Chart.js line chart
"""
df = self.analytics.df
if brand:
df = df[df['search_term'] == brand]
# Group by month
df['month'] = df['end_date'].dt.to_period('M')
monthly = df.groupby('month').agg({
'current_price': ['mean', 'min', 'max', 'count']
}).reset_index()
monthly.columns = ['month', 'avg', 'min', 'max', 'count']
monthly = monthly.tail(months)
return {
'labels': [str(m) for m in monthly['month']],
'datasets': [
{
'label': 'Average Price',
'data': [float(x) for x in monthly['avg']],
'borderColor': 'rgb(102, 126, 234)',
'backgroundColor': 'rgba(102, 126, 234, 0.1)',
'tension': 0.4
},
{
'label': 'Max Price',
'data': [float(x) for x in monthly['max']],
'borderColor': 'rgb(118, 75, 162)',
'backgroundColor': 'rgba(118, 75, 162, 0.1)',
'borderDash': [5, 5],
'tension': 0.4
},
{
'label': 'Min Price',
'data': [float(x) for x in monthly['min']],
'borderColor': 'rgb(16, 185, 129)',
'backgroundColor': 'rgba(16, 185, 129, 0.1)',
'borderDash': [5, 5],
'tension': 0.4
}
]
}
def brand_volume_chart(self, top_n=10):
"""
Generate bar chart data for auction volume by brand
Format: Ready for Chart.js bar chart
"""
brand_counts = self.analytics.df['search_term'].value_counts().head(top_n)
return {
'labels': brand_counts.index.tolist(),
'datasets': [{
'label': 'Total Auctions',
'data': brand_counts.values.tolist(),
'backgroundColor': [
'rgba(102, 126, 234, 0.8)',
'rgba(118, 75, 162, 0.8)',
'rgba(16, 185, 129, 0.8)',
'rgba(59, 130, 246, 0.8)',
'rgba(139, 92, 246, 0.8)',
'rgba(245, 158, 11, 0.8)',
'rgba(239, 68, 68, 0.8)',
'rgba(236, 72, 153, 0.8)',
'rgba(14, 165, 233, 0.8)',
'rgba(34, 197, 94, 0.8)'
][:top_n],
'borderWidth': 0
}]
}
def price_distribution_histogram(self, brand=None, bins=20):
"""
Generate histogram data for price distribution
Format: Ready for Chart.js bar chart
"""
df = self.analytics.df
if brand:
df = df[df['search_term'] == brand]
prices = df['current_price'].values
hist, bin_edges = np.histogram(prices, bins=bins)
# Create bin labels
labels = [f"${int(bin_edges[i])}-${int(bin_edges[i+1])}" for i in range(len(bin_edges)-1)]
return {
'labels': labels,
'datasets': [{
'label': 'Number of Auctions',
'data': hist.tolist(),
'backgroundColor': 'rgba(102, 126, 234, 0.6)',
'borderColor': 'rgb(102, 126, 234)',
'borderWidth': 1
}]
}
def savings_scatter_plot(self, sample_size=200):
"""
Generate scatter plot data showing price vs savings
Format: Ready for Chart.js scatter plot
"""
df = self.analytics.df.sample(min(sample_size, len(self.analytics.df)))
data_points = []
for _, row in df.iterrows():
data_points.append({
'x': float(row['current_price']),
'y': float(row['savings_percent']),
'brand': row['search_term'],
'title': row['title']
})
return {
'datasets': [{
'label': 'Auction Items',
'data': data_points,
'backgroundColor': 'rgba(102, 126, 234, 0.6)',
'pointRadius': 5,
'pointHoverRadius': 8
}]
}
def brand_comparison_radar(self, brands=None):
"""
Generate radar chart for brand comparison
Format: Ready for Chart.js radar chart
"""
if brands is None:
# Use top 5 brands by volume
brands = self.analytics.df['search_term'].value_counts().head(5).index.tolist()
metrics = []
for brand in brands:
brand_data = self.analytics.df[self.analytics.df['search_term'] == brand]
# Normalize metrics to 0-100 scale
avg_price_norm = min((brand_data['current_price'].mean() / self.analytics.df['current_price'].max()) * 100, 100)
savings_norm = min(brand_data['savings_percent'].mean(), 100)
volume_norm = min((len(brand_data) / len(self.analytics.df)) * 1000, 100)
# Get rarity score
rarity = self.analytics.calculate_rarity_score(brand)
# Get trend
trends = self.analytics.analyze_brand_trends(days=90)
brand_trend = next((t for t in trends if t['brand'] == brand), None)
momentum_norm = min(max(brand_trend['momentum_score'] + 50, 0), 100) if brand_trend else 50
metrics.append({
'label': brand,
'data': [
round(avg_price_norm, 1),
round(savings_norm, 1),
round(volume_norm, 1),
round(rarity, 1),
round(momentum_norm, 1)
]
})
return {
'labels': ['Price', 'Savings', 'Volume', 'Rarity', 'Momentum'],
'datasets': [
{
'label': m['label'],
'data': m['data'],
'borderColor': color,
'backgroundColor': color.replace('rgb', 'rgba').replace(')', ', 0.2)'),
'pointBackgroundColor': color,
'pointBorderColor': '#fff',
'pointHoverBackgroundColor': '#fff',
'pointHoverBorderColor': color
}
for m, color in zip(metrics, [
'rgb(102, 126, 234)',
'rgb(118, 75, 162)',
'rgb(16, 185, 129)',
'rgb(59, 130, 246)',
'rgb(139, 92, 246)'
])
]
}
def auction_house_pie(self):
"""
Generate pie chart for auction house market share
Format: Ready for Chart.js pie chart
"""
house_counts = self.analytics.df['auction_house'].value_counts()
return {
'labels': house_counts.index.tolist(),
'datasets': [{
'data': house_counts.values.tolist(),
'backgroundColor': [
'rgba(102, 126, 234, 0.8)',
'rgba(118, 75, 162, 0.8)',
'rgba(16, 185, 129, 0.8)',
'rgba(59, 130, 246, 0.8)',
'rgba(139, 92, 246, 0.8)'
],
'borderWidth': 2,
'borderColor': '#fff'
}]
}
def seasonality_heatmap(self, brand):
"""
Generate heatmap data for seasonal patterns
Format: Matrix data for heatmap visualization
"""
patterns = self.analytics.detect_seasonal_patterns(brand)
if not patterns:
return None
# Create month names
month_names = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
return {
'months': month_names,
'metrics': ['Avg Price', 'Volume', 'Savings'],
'data': [
[p['avg_price'] for p in patterns],
[p['auction_count'] for p in patterns],
[p['avg_savings'] for p in patterns]
]
}
def deal_score_distribution(self):
"""
Generate histogram of deal scores
Format: Ready for Chart.js bar chart
"""
scores = []
# Sample 200 random auctions for performance
sample = self.analytics.df.sample(min(200, len(self.analytics.df)))
for auction_id in sample['auction_id']:
score_data = self.analytics.calculate_deal_score(auction_id)
scores.append(score_data['deal_score'])
# Create bins for scores
bins = [0, 30, 40, 50, 60, 70, 80, 100]
labels = ['D', 'C', 'C+', 'B', 'B+', 'A', 'A+']
hist, _ = np.histogram(scores, bins=bins)
return {
'labels': labels,
'datasets': [{
'label': 'Number of Deals',
'data': hist.tolist(),
'backgroundColor': [
'rgba(239, 68, 68, 0.6)',
'rgba(245, 158, 11, 0.6)',
'rgba(245, 158, 11, 0.8)',
'rgba(59, 130, 246, 0.6)',
'rgba(139, 92, 246, 0.6)',
'rgba(59, 130, 246, 0.8)',
'rgba(16, 185, 129, 0.8)'
],
'borderWidth': 0
}]
}
def roi_projection_chart(self, brand):
"""
Generate line chart showing ROI projections
Format: Ready for Chart.js line chart
"""
# Get sample auction for this brand
brand_auctions = self.analytics.df[self.analytics.df['search_term'] == brand]
if len(brand_auctions) == 0:
return None
sample_auction = brand_auctions.iloc[0]
roi_data = self.analytics.calculate_roi_potential(sample_auction['auction_id'])
if not roi_data:
return None
years = [0, 1, 2, 3, 4, 5]
values = [
roi_data['current_price'],
roi_data['projected_value_1yr'],
roi_data['current_price'] * ((1 + roi_data['annual_appreciation_percent']/100) ** 2),
roi_data['projected_value_3yr'],
roi_data['current_price'] * ((1 + roi_data['annual_appreciation_percent']/100) ** 4),
roi_data['projected_value_5yr']
]
return {
'labels': [f'Year {y}' for y in years],
'datasets': [{
'label': f'{brand} Value Projection',
'data': [round(v, 2) for v in values],
'borderColor': 'rgb(16, 185, 129)',
'backgroundColor': 'rgba(16, 185, 129, 0.1)',
'tension': 0.4,
'fill': True
}]
}
def trending_brands_timeline(self, days=180):
"""
Generate timeline showing brand momentum over time
Format: Multi-line chart
"""
# Track top 5 brands over time
top_brands = self.analytics.df['search_term'].value_counts().head(5).index.tolist()
# Split time into 6 periods
periods = 6
days_per_period = days // periods
datasets = []
colors = [
'rgb(102, 126, 234)',
'rgb(118, 75, 162)',
'rgb(16, 185, 129)',
'rgb(59, 130, 246)',
'rgb(139, 92, 246)'
]
for idx, brand in enumerate(top_brands):
brand_data = self.analytics.df[self.analytics.df['search_term'] == brand]
momentum_over_time = []
for period in range(periods):
period_end = datetime.now() - timedelta(days=period * days_per_period)
period_start = period_end - timedelta(days=days_per_period)
period_auctions = brand_data[
(brand_data['end_date'] >= period_start) &
(brand_data['end_date'] < period_end)
]
# Simple momentum: auction count
momentum = len(period_auctions)
momentum_over_time.insert(0, momentum)
datasets.append({
'label': brand,
'data': momentum_over_time,
'borderColor': colors[idx],
'backgroundColor': colors[idx].replace('rgb', 'rgba').replace(')', ', 0.1)'),
'tension': 0.4
})
labels = [f'{i*days_per_period}-{(i+1)*days_per_period} days ago' for i in range(periods)]
labels.reverse()
return {
'labels': labels,
'datasets': datasets
}
def generate_all_charts(self):
"""Generate all chart data at once"""
return {
'price_trends': {
'hermes_birkin': self.price_trend_chart('Hermes Birkin'),
'chanel_classic': self.price_trend_chart('Chanel Classic Flap'),
'all_brands': self.price_trend_chart()
},
'brand_volume': self.brand_volume_chart(),
'price_distribution': self.price_distribution_histogram(),
'savings_scatter': self.savings_scatter_plot(),
'brand_radar': self.brand_comparison_radar(),
'auction_house_pie': self.auction_house_pie(),
'deal_scores': self.deal_score_distribution(),
'trending_timeline': self.trending_brands_timeline()
}
def export_to_file(self, filename='chart_data.json'):
"""Export all chart data to JSON file"""
charts = self.generate_all_charts()
with open(filename, 'w') as f:
json.dump(charts, f, indent=2)
return filename
def main():
"""Generate and display sample visualizations"""
print("Generating LUXVAULT Analytics Visualizations...")
viz = AnalyticsVisualizations()
print("\n=== PRICE TREND CHART (Hermes Birkin) ===")
price_chart = viz.price_trend_chart('Hermes Birkin')
print(f"Data points: {len(price_chart['labels'])}")
print(f"Datasets: {len(price_chart['datasets'])}")
print("\n=== BRAND VOLUME CHART ===")
volume_chart = viz.brand_volume_chart()
print(f"Brands: {', '.join(volume_chart['labels'][:5])}...")
print("\n=== DEAL SCORE DISTRIBUTION ===")
score_chart = viz.deal_score_distribution()
print(f"Grade distribution: {dict(zip(score_chart['labels'], score_chart['datasets'][0]['data']))}")
print("\n=== EXPORTING ALL CHARTS ===")
filename = viz.export_to_file('/root/Projects/handbag-auth-nextjs/analytics/chart_data.json')
print(f"Exported to: {filename}")
print("\nVisualization generation complete!")
if __name__ == "__main__":
main()