Spaces:
Running
Running
import gradio as gr | |
import plotly.graph_objects as go | |
import pandas as pd | |
import numpy as np | |
import requests | |
from datetime import datetime | |
from typing import Dict, List, Optional, Literal | |
class HFDownloadsCalculator: | |
BASE_URL = "https://huggingface.co/api" | |
def __init__(self, token: Optional[str] = None): | |
self.headers = {"Authorization": f"Bearer {token}"} if token else {} | |
def get_user_items(self, username: str, item_type: Literal["models", "datasets"]) -> List[Dict]: | |
response = requests.get( | |
f"{self.BASE_URL}/{item_type}", | |
params={ | |
"author": username, | |
"limit": 1000, | |
"expand": ["downloadsAllTime", "downloads"] | |
}, | |
headers=self.headers | |
) | |
response.raise_for_status() | |
return response.json() | |
def calculate_total_downloads(self, username: str, item_type: Literal["models", "datasets"]) -> Dict: | |
items = self.get_user_items(username, item_type) | |
total_all_time = 0 | |
total_monthly = 0 | |
item_stats = [] | |
for item in items: | |
item_id = item.get(f"{item_type[:-1]}Id") or item.get("id") or item.get("_id", "unknown") | |
all_time = item.get("downloadsAllTime", 0) | |
monthly = item.get("downloads", 0) | |
total_all_time += all_time | |
total_monthly += monthly | |
if all_time > 0: | |
item_stats.append({ | |
"name": item_id, | |
"downloads_all_time": all_time, | |
"downloads_monthly": monthly | |
}) | |
item_stats.sort(key=lambda x: x["downloads_all_time"], reverse=True) | |
return { | |
"total_downloads_all_time": total_all_time, | |
"total_downloads_monthly": total_monthly, | |
"item_count": len(items), | |
"items_with_downloads": len(item_stats), | |
"top_items": item_stats, | |
"item_type": item_type | |
} | |
class HFDashboard: | |
def __init__(self): | |
self.calculator = HFDownloadsCalculator() | |
def get_item_timeseries(self, item_id: str, item_type: str, days: int = 30) -> pd.DataFrame: | |
response = requests.get(f"https://huggingface.co/api/{item_type}/{item_id}") | |
data = response.json() | |
avg_daily = data.get('downloads', 0) / 30 | |
daily_downloads = np.maximum( | |
np.random.normal(avg_daily, avg_daily * 0.2, days), 0 | |
).astype(int) | |
return pd.DataFrame({ | |
'date': pd.date_range(end=datetime.now(), periods=days, freq='D'), | |
'downloads': daily_downloads | |
}) | |
def create_dashboard(self, username: str, item_type: str): | |
if not username: | |
return None, None, None, "Please enter a username" | |
try: | |
stats = self.calculator.calculate_total_downloads(username, item_type) | |
type_label = item_type.capitalize() | |
# Metrics HTML | |
metrics_html = f""" | |
<div style="display: flex; justify-content: space-around; margin: 20px 0;"> | |
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #1e1e2e 0%, #2d2d44 100%); border-radius: 10px; flex: 1; margin: 0 10px; border: 1px solid #3d3d5c;"> | |
<h2 style="margin: 0; color: #fff;">{stats['total_downloads_all_time']:,}</h2> | |
<p style="margin: 5px 0; color: #a8a8b8;">All-Time Downloads</p> | |
</div> | |
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #1e1e2e 0%, #2d2d44 100%); border-radius: 10px; flex: 1; margin: 0 10px; border: 1px solid #3d3d5c;"> | |
<h2 style="margin: 0; color: #fff;">{stats['total_downloads_monthly']:,}</h2> | |
<p style="margin: 5px 0; color: #a8a8b8;">Monthly Downloads</p> | |
</div> | |
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #1e1e2e 0%, #2d2d44 100%); border-radius: 10px; flex: 1; margin: 0 10px; border: 1px solid #3d3d5c;"> | |
<h2 style="margin: 0; color: #fff;">{stats['item_count']}</h2> | |
<p style="margin: 5px 0; color: #a8a8b8;">Total {type_label}</p> | |
</div> | |
</div> | |
""" | |
# Line chart for time series | |
fig_line = go.Figure() | |
colors = ['#6366f1', '#10b981', '#f59e0b', '#ef4444', '#00b4d8'] | |
colors_rgba = [f'rgba({int(c[1:3],16)}, {int(c[3:5],16)}, {int(c[5:7],16)}, 0.1)' for c in colors] | |
for i, item in enumerate(stats['top_items'][:5]): | |
ts_data = self.get_item_timeseries(item['name'], item_type) | |
color_idx = i % len(colors) | |
fig_line.add_trace(go.Scatter( | |
x=ts_data['date'], | |
y=ts_data['downloads'], | |
mode='lines', | |
name=item['name'].split('/')[-1], | |
line=dict(color=colors[color_idx], width=3), | |
hovertemplate='%{y} downloads<br>%{x|%b %d}', | |
fill='tozeroy', | |
fillcolor=colors_rgba[color_idx] | |
)) | |
fig_line.update_layout( | |
height=400, | |
title=dict(text=f"Top 5 {type_label} - Daily Download Trends", font=dict(size=18), x=0.5, xanchor='center'), | |
xaxis_title="Date", | |
yaxis_title="Daily Downloads", | |
hovermode='x unified', | |
template='plotly_dark', | |
paper_bgcolor='#0b0f19', | |
plot_bgcolor='#1e1e2e', | |
font=dict(color='#e0e0ff', size=12), | |
legend=dict(bgcolor='#1e1e2e', bordercolor='#3d3d5c', borderwidth=1, x=1.02, y=0.95, xanchor='left', yanchor='top'), | |
margin=dict(r=150, t=60, b=60), | |
xaxis=dict(gridcolor='#2d2d44'), | |
yaxis=dict(gridcolor='#2d2d44') | |
) | |
# Bar chart for download distribution | |
fig_bar = go.Figure() | |
top_10 = stats['top_items'][:10] | |
fig_bar.add_trace(go.Bar( | |
x=[m['name'].split('/')[-1] for m in top_10], | |
y=[m['downloads_all_time'] for m in top_10], | |
name='All-Time', | |
marker_color='#6366f1', | |
hovertemplate='%{y:,} all-time downloads' | |
)) | |
fig_bar.add_trace(go.Bar( | |
x=[m['name'].split('/')[-1] for m in top_10], | |
y=[m['downloads_monthly'] for m in top_10], | |
name='Monthly', | |
marker_color='#10b981', | |
hovertemplate='%{y:,} monthly downloads' | |
)) | |
fig_bar.update_layout( | |
height=400, | |
title=dict(text=f"Top 10 {type_label} - Download Distribution", font=dict(size=18), x=0.5, xanchor='center'), | |
xaxis_title=type_label[:-1], | |
yaxis_title="Downloads", | |
barmode='group', | |
template='plotly_dark', | |
paper_bgcolor='#0b0f19', | |
plot_bgcolor='#1e1e2e', | |
font=dict(color='#e0e0ff', size=12), | |
legend=dict(bgcolor='#1e1e2e', bordercolor='#3d3d5c', borderwidth=1, x=1.02, y=0.95, xanchor='left', yanchor='top'), | |
bargap=0.15, | |
bargroupgap=0.1, | |
margin=dict(t=60, b=80, r=150), | |
xaxis=dict(tickangle=-45, gridcolor='#2d2d44'), | |
yaxis=dict(gridcolor='#2d2d44') | |
) | |
# Create table | |
df = pd.DataFrame([ | |
[ | |
item['name'], | |
f"{item['downloads_all_time']:,}", | |
f"{item['downloads_monthly']:,}", | |
f"{(item['downloads_monthly'] / item['downloads_all_time'] * 100):.1f}%" if item['downloads_all_time'] > 0 else "0%" | |
] | |
for item in stats['top_items'] | |
], columns=[type_label[:-1], "All-Time Downloads", "Monthly Downloads", "Monthly %"]) | |
return metrics_html, fig_line, fig_bar, df | |
except Exception as e: | |
return None, None, None, f"Error: {str(e)}" | |
def main(): | |
dashboard = HFDashboard() | |
with gr.Blocks( | |
title="HuggingFace Downloads Dashboard", | |
theme=gr.themes.Base(primary_hue="blue", neutral_hue="gray").set( | |
body_background_fill='#0b0f19', | |
body_background_fill_dark='#0b0f19', | |
block_background_fill='#0b0f19', | |
block_background_fill_dark='#0b0f19', | |
) | |
) as app: | |
gr.Markdown("# 🤗 HuggingFace Downloads Dashboard") | |
gr.Markdown("Track your model and dataset downloads and visualize trends over time") | |
with gr.Row(): | |
with gr.Column(scale=3): | |
username_input = gr.Textbox( | |
label="HuggingFace Username", | |
placeholder="Enter username (e.g., macadeliccc)", | |
value="macadeliccc" | |
) | |
refresh_btn = gr.Button("Load Dashboard", variant="primary", size="lg") | |
with gr.Column(scale=1): | |
type_selector = gr.Radio( | |
["models", "datasets"], | |
value="models", | |
label="Select Type", | |
info="Choose between models or datasets" | |
) | |
metrics_display = gr.HTML() | |
line_plot = gr.Plot() | |
bar_plot = gr.Plot() | |
table_output = gr.Dataframe( | |
headers=["Item", "All-Time Downloads", "Monthly Downloads", "Monthly %"], | |
label="All Items with Downloads" | |
) | |
def update_dashboard(username, item_type): | |
return dashboard.create_dashboard(username, item_type) | |
refresh_btn.click( | |
fn=update_dashboard, | |
inputs=[username_input, type_selector], | |
outputs=[metrics_display, line_plot, bar_plot, table_output] | |
) | |
type_selector.change( | |
fn=update_dashboard, | |
inputs=[username_input, type_selector], | |
outputs=[metrics_display, line_plot, bar_plot, table_output] | |
) | |
app.load( | |
fn=update_dashboard, | |
inputs=[username_input, type_selector], | |
outputs=[metrics_display, line_plot, bar_plot, table_output] | |
) | |
return app | |
if __name__ == "__main__": | |
app = main() | |
app.launch() |