|
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 = 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> |
|
""" |
|
|
|
|
|
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') |
|
) |
|
|
|
|
|
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') |
|
) |
|
|
|
|
|
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() |