""" Hugging Face Contributions Graph A GitHub-style contribution calendar for Hugging Face users. Shows daily commit activity across models, datasets, and spaces. """ import gradio as gr from datetime import datetime, timedelta from hf_contributions import fetch_user_contributions, ContributionStats from typing import Optional import json # GitHub-style color palette COLORS = { "light": { 0: "#ebedf0", # No contributions 1: "#9be9a8", # Level 1 (1-3) 2: "#40c463", # Level 2 (4-6) 3: "#30a14e", # Level 3 (7-9) 4: "#216e39", # Level 4 (10+) }, "dark": { 0: "#161b22", 1: "#0e4429", 2: "#006d32", 3: "#26a641", 4: "#39d353", } } def get_contribution_level(count: int) -> int: """Determine the contribution level (0-4) based on count.""" if count == 0: return 0 elif count <= 3: return 1 elif count <= 6: return 2 elif count <= 9: return 3 else: return 4 def generate_calendar_data(contributions: dict[str, int], days: int = 365) -> list[dict]: """Generate calendar data for the past N days.""" today = datetime.now().date() start_date = today - timedelta(days=days - 1) # Adjust to start from Sunday days_since_sunday = start_date.weekday() + 1 if days_since_sunday == 7: days_since_sunday = 0 start_date = start_date - timedelta(days=days_since_sunday) calendar_data = [] current_date = start_date while current_date <= today: date_str = current_date.strftime("%Y-%m-%d") count = contributions.get(date_str, 0) level = get_contribution_level(count) calendar_data.append({ "date": date_str, "count": count, "level": level, "weekday": current_date.weekday(), "month": current_date.month, "day": current_date.day }) current_date += timedelta(days=1) return calendar_data def build_daily_activity_data(contributions_by_repo: dict) -> dict[str, list[dict]]: """Build a mapping of date -> list of contributions for that date.""" daily_activities = {} for repo_id, commits in contributions_by_repo.items(): for commit in commits: date = commit["date"] if date not in daily_activities: daily_activities[date] = [] daily_activities[date].append({ "repo_id": repo_id, "repo_type": commit["repo_type"], "message": commit.get("message", "No message")[:80] }) return daily_activities def generate_contribution_graph_html( stats: Optional[ContributionStats], theme: str = "light", username: str = "" ) -> str: """Generate the HTML/CSS for the GitHub-style contribution graph.""" if stats is None: return """

Enter a Hugging Face username to see their contribution graph

""" colors = COLORS[theme] calendar_data = generate_calendar_data(stats.contributions_by_date) # Build daily activity data for the detail view daily_activities = build_daily_activity_data(stats.contributions_by_repo) daily_activities_json = json.dumps(daily_activities) # Group by weeks weeks = [] current_week = [] for day in calendar_data: if len(current_week) == 7: weeks.append(current_week) current_week = [] current_week.append(day) if current_week: weeks.append(current_week) # Generate month labels months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] month_positions = {} for week_idx, week in enumerate(weeks): for day in week: if day["day"] <= 7: # First week of month month_positions[week_idx] = months[day["month"] - 1] break # Build HTML squares_html = "" for week_idx, week in enumerate(weeks): for day in week: color = colors[day["level"]] tooltip = f'{day["count"]} contributions on {day["date"]}' squares_html += f'''
''' # Month labels HTML months_html = "" last_month_week = -4 for week_idx, month_name in sorted(month_positions.items()): if week_idx - last_month_week >= 4: # At least 4 weeks apart left_pos = week_idx * 15 # 11px square + 4px gap months_html += f'{month_name}' last_month_week = week_idx bg_color = "#ffffff" if theme == "light" else "#0d1117" text_color = "#24292f" if theme == "light" else "#c9d1d9" border_color = "#d0d7de" if theme == "light" else "#30363d" html = f'''
{stats.total_commits} contributions in the last year
Mon Wed Fri
{months_html}
{squares_html}
Less
More
{stats.total_commits}
Total Contributions
{stats.models_count}
Models
{stats.datasets_count}
Datasets
{stats.spaces_count}
Spaces
{stats.longest_streak}
Longest Streak
{stats.current_streak}
Current Streak
''' return html # Store the current stats globally for the detail view current_stats: Optional[ContributionStats] = None def fetch_and_display(username: str, theme: str) -> tuple[str, str]: """Fetch contributions and generate the display.""" global current_stats if not username or not username.strip(): return ( generate_contribution_graph_html(None, theme), "Please enter a username" ) username = username.strip() try: # Fetch the data current_stats = fetch_user_contributions(username) # Generate the graph HTML graph_html = generate_contribution_graph_html(current_stats, theme, username) # Generate summary summary = f""" ### {username}'s Hugging Face Activity - **Total Contributions:** {current_stats.total_commits} - **Repositories:** {current_stats.total_repos} ({current_stats.models_count} models, {current_stats.datasets_count} datasets, {current_stats.spaces_count} spaces) - **Longest Streak:** {current_stats.longest_streak} days - **Current Streak:** {current_stats.current_streak} days """ if current_stats.most_active_day: summary += f"- **Most Active Day:** {current_stats.most_active_day} ({current_stats.most_active_count} contributions)" return graph_html, summary except Exception as e: return ( f'
Error: {str(e)}
', f"Error fetching data for {username}: {str(e)}" ) # Create the Gradio interface with gr.Blocks( title="HF Contributions Graph", theme=gr.themes.Soft(), css=""" .gradio-container { max-width: 1200px !important; } footer { display: none !important; } """ ) as demo: gr.Markdown(""" # ๐Ÿค— Hugging Face Contributions Graph GitHub-style contribution calendar for Hugging Face users. See your daily activity across models, datasets, and spaces! """) with gr.Row(): with gr.Column(scale=4): username_input = gr.Textbox( label="Hugging Face Username", placeholder="e.g., huggingface, Qwen, meta-llama", lines=1 ) with gr.Column(scale=1): theme_dropdown = gr.Dropdown( choices=["light", "dark"], value="light", label="Theme" ) with gr.Column(scale=1): fetch_btn = gr.Button("Fetch Contributions", variant="primary") graph_output = gr.HTML( value=generate_contribution_graph_html(None, "light"), label="Contribution Graph" ) summary_output = gr.Markdown( value="Enter a username and click 'Fetch Contributions' to see their activity." ) # Event handlers fetch_btn.click( fn=fetch_and_display, inputs=[username_input, theme_dropdown], outputs=[graph_output, summary_output] ) username_input.submit( fn=fetch_and_display, inputs=[username_input, theme_dropdown], outputs=[graph_output, summary_output] ) theme_dropdown.change( fn=lambda u, t: fetch_and_display(u, t) if u else (generate_contribution_graph_html(None, t), ""), inputs=[username_input, theme_dropdown], outputs=[graph_output, summary_output] ) gr.Markdown(""" --- **How it works:** This app fetches your public repositories (models, datasets, spaces) from the Hugging Face Hub API and counts commits to generate a contribution calendar. **Note:** Only public repositories are counted. Private repos require authentication. """) if __name__ == "__main__": demo.launch()