openfree commited on
Commit
0dbf84c
ยท
verified ยท
1 Parent(s): d38fe40

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1 -1251
app.py CHANGED
@@ -1248,1254 +1248,4 @@ else:
1248
  f'<img src="https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo.svg" style="width: 200px; margin-bottom: 30px;">'
1249
  f'<h2>Welcome to Hugging Face Contributions Dashboard</h2>'
1250
  f'<p style="font-size: 1.2rem;">Please select a contributor from the sidebar to view their activity.</p>'
1251
- f'</div>', unsafe_allow_html=True)import streamlit as st
1252
- from huggingface_hub import HfApi
1253
- import pandas as pd
1254
- import matplotlib.pyplot as plt
1255
- import seaborn as sns
1256
- from datetime import datetime
1257
- from concurrent.futures import ThreadPoolExecutor, as_completed
1258
- from functools import lru_cache
1259
- import time
1260
- import requests
1261
- from collections import Counter
1262
- import numpy as np
1263
-
1264
- st.set_page_config(page_title="HF Contributions", layout="wide", initial_sidebar_state="expanded")
1265
-
1266
- # ํ–ฅ์ƒ๋œ UI ์Šคํƒ€์ผ๋ง
1267
- st.markdown("""
1268
- <style>
1269
- /* ์‚ฌ์ด๋“œ๋ฐ” ์Šคํƒ€์ผ๋ง */
1270
- [data-testid="stSidebar"] {
1271
- min-width: 35vw !important;
1272
- max-width: 35vw !important;
1273
- background-color: #f8f9fa;
1274
- padding: 1rem;
1275
- border-right: 1px solid #e9ecef;
1276
- }
1277
-
1278
- /* ํ—ค๋” ์Šคํƒ€์ผ๋ง */
1279
- h1, h2, h3 {
1280
- color: #1e88e5;
1281
- font-weight: 700;
1282
- }
1283
- h1 {
1284
- font-size: 2.5rem;
1285
- margin-bottom: 1.5rem;
1286
- border-bottom: 2px solid #e0e0e0;
1287
- padding-bottom: 0.5rem;
1288
- }
1289
- h2 {
1290
- font-size: 1.8rem;
1291
- margin-top: 1.5rem;
1292
- }
1293
- h3 {
1294
- font-size: 1.4rem;
1295
- margin-top: 1rem;
1296
- }
1297
-
1298
- /* ์นด๋“œ ์Šคํƒ€์ผ๋ง */
1299
- div[data-testid="stMetric"] {
1300
- background-color: #f1f8fe;
1301
- border-radius: 10px;
1302
- padding: 1rem;
1303
- box-shadow: 0 2px 5px rgba(0,0,0,0.05);
1304
- margin-bottom: 1rem;
1305
- }
1306
-
1307
- /* ์ฐจํŠธ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ๋ง */
1308
- .chart-container {
1309
- background-color: white;
1310
- border-radius: 10px;
1311
- padding: 1rem;
1312
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
1313
- margin: 1rem 0;
1314
- }
1315
-
1316
- /* ํ…Œ์ด๋ธ” ์Šคํƒ€์ผ๋ง */
1317
- div[data-testid="stDataFrame"] {
1318
- background-color: white;
1319
- border-radius: 10px;
1320
- padding: 0.5rem;
1321
- box-shadow: 0 2px 5px rgba(0,0,0,0.05);
1322
- }
1323
-
1324
- /* ํƒญ ์Šคํƒ€์ผ๋ง */
1325
- button[data-baseweb="tab"] {
1326
- font-weight: 600;
1327
- }
1328
-
1329
- /* ์„œ๋ธŒํ—ค๋” ๋ฐฐ๊ฒฝ */
1330
- .subheader {
1331
- background-color: #f1f8fe;
1332
- padding: 0.5rem 1rem;
1333
- border-radius: 5px;
1334
- margin-bottom: 1rem;
1335
- }
1336
-
1337
- /* ์ •๋ณด ๋ฑƒ์ง€ */
1338
- .info-badge {
1339
- background-color: #e3f2fd;
1340
- color: #1976d2;
1341
- padding: 0.3rem 0.7rem;
1342
- border-radius: 20px;
1343
- display: inline-block;
1344
- font-weight: 500;
1345
- margin-right: 0.5rem;
1346
- }
1347
-
1348
- /* ํ”„๋กœ๊ทธ๋ ˆ์Šค ๋ฐ” */
1349
- div[data-testid="stProgress"] {
1350
- height: 0.5rem !important;
1351
- }
1352
-
1353
- /* ๋ฒ„ํŠผ ์Šคํƒ€์ผ๋ง */
1354
- .stButton button {
1355
- background-color: #1e88e5;
1356
- color: white;
1357
- border: none;
1358
- font-weight: 500;
1359
- }
1360
-
1361
- /* ๊ฒฝ๊ณ /์„ฑ๊ณต ๋ฉ”์‹œ์ง€ ๊ฐœ์„  */
1362
- div[data-testid="stAlert"] {
1363
- border-radius: 10px;
1364
- margin: 1rem 0;
1365
- }
1366
-
1367
- /* ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„์„ ์„น์…˜ */
1368
- .category-section {
1369
- background-color: white;
1370
- border-radius: 10px;
1371
- padding: 1rem;
1372
- margin-bottom: 1.5rem;
1373
- box-shadow: 0 2px 5px rgba(0,0,0,0.05);
1374
- }
1375
- </style>
1376
- """, unsafe_allow_html=True)
1377
-
1378
- api = HfApi()
1379
-
1380
- # Cache for API responses
1381
- @lru_cache(maxsize=1000)
1382
- def cached_repo_info(repo_id, repo_type):
1383
- return api.repo_info(repo_id=repo_id, repo_type=repo_type)
1384
-
1385
- @lru_cache(maxsize=1000)
1386
- def cached_list_commits(repo_id, repo_type):
1387
- return list(api.list_repo_commits(repo_id=repo_id, repo_type=repo_type))
1388
-
1389
- @lru_cache(maxsize=100)
1390
- def cached_list_items(username, kind):
1391
- if kind == "model":
1392
- return list(api.list_models(author=username))
1393
- elif kind == "dataset":
1394
- return list(api.list_datasets(author=username))
1395
- elif kind == "space":
1396
- return list(api.list_spaces(author=username))
1397
- return []
1398
-
1399
- # Rate limiting
1400
- class RateLimiter:
1401
- def __init__(self, calls_per_second=10):
1402
- self.calls_per_second = calls_per_second
1403
- self.last_call = 0
1404
-
1405
- def wait(self):
1406
- current_time = time.time()
1407
- time_since_last_call = current_time - self.last_call
1408
- if time_since_last_call < (1.0 / self.calls_per_second):
1409
- time.sleep((1.0 / self.calls_per_second) - time_since_last_call)
1410
- self.last_call = time.time()
1411
-
1412
- rate_limiter = RateLimiter()
1413
-
1414
- # Function to fetch quick commit stats for a user (optimized for ranking)
1415
- @st.cache_data(ttl=3600) # Cache for 1 hour
1416
- def get_user_commit_stats(username):
1417
- """Fetch basic commit statistics for a user"""
1418
- try:
1419
- total_commits = 0
1420
- items_count = {"model": 0, "dataset": 0, "space": 0}
1421
-
1422
- for kind in ["model", "dataset", "space"]:
1423
- try:
1424
- items = cached_list_items(username, kind)
1425
- items_count[kind] = len(items)
1426
-
1427
- # Sample a few repos to estimate commit activity
1428
- sample_size = min(5, len(items)) # Check up to 5 repos per type
1429
- if sample_size > 0:
1430
- sample_items = items[:sample_size]
1431
- for item in sample_items:
1432
- try:
1433
- rate_limiter.wait()
1434
- commits = cached_list_commits(item.id, kind)
1435
- total_commits += len(commits)
1436
- except:
1437
- pass
1438
-
1439
- # Estimate total commits based on sample
1440
- if sample_size < len(items):
1441
- total_commits = int(total_commits * len(items) / sample_size)
1442
- except:
1443
- pass
1444
-
1445
- # Calculate contribution score based on commits only
1446
- score = total_commits
1447
-
1448
- return {
1449
- "username": username,
1450
- "models": items_count["model"],
1451
- "spaces": items_count["space"],
1452
- "datasets": items_count["dataset"],
1453
- "estimated_commits": total_commits,
1454
- "score": score
1455
- }
1456
- except Exception as e:
1457
- return {
1458
- "username": username,
1459
- "models": 0,
1460
- "spaces": 0,
1461
- "datasets": 0,
1462
- "estimated_commits": 0,
1463
- "score": 0
1464
- }
1465
-
1466
- # Enhanced function to get trending accounts with commit-based ranking
1467
- @st.cache_data(ttl=3600) # Cache for 1 hour
1468
- def get_trending_accounts_with_commits(limit=100):
1469
- try:
1470
- # First, get top accounts by model/space count
1471
- spaces_response = requests.get("https://huggingface.co/api/spaces",
1472
- params={"limit": 10000},
1473
- timeout=30)
1474
- models_response = requests.get("https://huggingface.co/api/models",
1475
- params={"limit": 10000},
1476
- timeout=30)
1477
-
1478
- # Process spaces data
1479
- top_space_owners = []
1480
- if spaces_response.status_code == 200:
1481
- spaces = spaces_response.json()
1482
- owner_counts_spaces = {}
1483
- for space in spaces:
1484
- if '/' in space.get('id', ''):
1485
- owner, _ = space.get('id', '').split('/', 1)
1486
- else:
1487
- owner = space.get('owner', '')
1488
-
1489
- if owner != 'None':
1490
- owner_counts_spaces[owner] = owner_counts_spaces.get(owner, 0) + 1
1491
-
1492
- top_space_owners = sorted(owner_counts_spaces.items(), key=lambda x: x[1], reverse=True)[:limit]
1493
-
1494
- # Process models data
1495
- top_model_owners = []
1496
- if models_response.status_code == 200:
1497
- models = models_response.json()
1498
- owner_counts_models = {}
1499
- for model in models:
1500
- if '/' in model.get('id', ''):
1501
- owner, _ = model.get('id', '').split('/', 1)
1502
- else:
1503
- owner = model.get('owner', '')
1504
-
1505
- if owner != 'None':
1506
- owner_counts_models[owner] = owner_counts_models.get(owner, 0) + 1
1507
-
1508
- top_model_owners = sorted(owner_counts_models.items(), key=lambda x: x[1], reverse=True)[:limit]
1509
-
1510
- # Get unique users from top 100 of both lists
1511
- unique_users = set()
1512
- for owner, _ in top_space_owners[:100]:
1513
- unique_users.add(owner)
1514
- for owner, _ in top_model_owners[:100]:
1515
- unique_users.add(owner)
1516
-
1517
- # Create progress bar for fetching commit stats
1518
- progress_text = st.empty()
1519
- progress_bar = st.progress(0)
1520
- progress_text.text(f"Analyzing top contributors... (0/{len(unique_users)})")
1521
-
1522
- # Fetch commit stats for all unique users
1523
- user_stats = []
1524
- with ThreadPoolExecutor(max_workers=5) as executor:
1525
- future_to_user = {executor.submit(get_user_commit_stats, user): user for user in unique_users}
1526
- completed = 0
1527
- for future in as_completed(future_to_user):
1528
- stats = future.result()
1529
- if stats["score"] > 0: # Only include users with some activity
1530
- user_stats.append(stats)
1531
- completed += 1
1532
- progress = completed / len(unique_users)
1533
- progress_bar.progress(progress)
1534
- progress_text.text(f"Analyzing top contributors... ({completed}/{len(unique_users)})")
1535
-
1536
- # Clear progress indicators
1537
- progress_text.empty()
1538
- progress_bar.empty()
1539
-
1540
- # Sort by score (combination of commits and repo counts)
1541
- user_stats.sort(key=lambda x: x["score"], reverse=True)
1542
-
1543
- # Extract rankings
1544
- trending_authors = [stat["username"] for stat in user_stats[:limit]]
1545
-
1546
- # Create detailed rankings for display
1547
- spaces_rank_data = [(stat["username"], stat["spaces"]) for stat in user_stats if stat["spaces"] > 0][:limit]
1548
- models_rank_data = [(stat["username"], stat["models"]) for stat in user_stats if stat["models"] > 0][:limit]
1549
-
1550
- return trending_authors, spaces_rank_data, models_rank_data, user_stats[:limit]
1551
-
1552
- except Exception as e:
1553
- st.error(f"Error fetching trending accounts: {str(e)}")
1554
- fallback_authors = ["ritvik77", "facebook", "google", "stabilityai", "Salesforce", "tiiuae", "bigscience"]
1555
- fallback_stats = [{"username": author, "models": 0, "spaces": 0, "datasets": 0, "estimated_commits": 0, "score": 0} for author in fallback_authors]
1556
- return fallback_authors, [(author, 0) for author in fallback_authors], [(author, 0) for author in fallback_authors], fallback_stats
1557
-
1558
- # Function to fetch commits for a repository (optimized)
1559
- def fetch_commits_for_repo(repo_id, repo_type, username, selected_year):
1560
- try:
1561
- rate_limiter.wait()
1562
- # Skip private/gated repos upfront
1563
- repo_info = cached_repo_info(repo_id, repo_type)
1564
- if repo_info.private or (hasattr(repo_info, 'gated') and repo_info.gated):
1565
- return [], 0
1566
-
1567
- # Get initial commit date
1568
- initial_commit_date = pd.to_datetime(repo_info.created_at).tz_localize(None).date()
1569
- commit_dates = []
1570
- commit_count = 0
1571
-
1572
- # Add initial commit if it's from the selected year
1573
- if initial_commit_date.year == selected_year:
1574
- commit_dates.append(initial_commit_date)
1575
- commit_count += 1
1576
-
1577
- # Get all commits
1578
- commits = cached_list_commits(repo_id, repo_type)
1579
- for commit in commits:
1580
- commit_date = pd.to_datetime(commit.created_at).tz_localize(None).date()
1581
- if commit_date.year == selected_year:
1582
- commit_dates.append(commit_date)
1583
- commit_count += 1
1584
-
1585
- return commit_dates, commit_count
1586
- except Exception as e:
1587
- return [], 0
1588
-
1589
- # Function to get commit events for a user (optimized)
1590
- def get_commit_events(username, kind=None, selected_year=None):
1591
- commit_dates = []
1592
- items_with_type = []
1593
- kinds = [kind] if kind else ["model", "dataset", "space"]
1594
-
1595
- for k in kinds:
1596
- try:
1597
- items = cached_list_items(username, k)
1598
- items_with_type.extend((item, k) for item in items)
1599
- repo_ids = [item.id for item in items]
1600
-
1601
- # Optimized parallel fetch with chunking
1602
- chunk_size = 5 # Process 5 repos at a time
1603
- for i in range(0, len(repo_ids), chunk_size):
1604
- chunk = repo_ids[i:i + chunk_size]
1605
- with ThreadPoolExecutor(max_workers=min(5, len(chunk))) as executor:
1606
- future_to_repo = {
1607
- executor.submit(fetch_commits_for_repo, repo_id, k, username, selected_year): repo_id
1608
- for repo_id in chunk
1609
- }
1610
- for future in as_completed(future_to_repo):
1611
- repo_commits, repo_count = future.result()
1612
- if repo_commits: # Only extend if we got commits
1613
- commit_dates.extend(repo_commits)
1614
- except Exception as e:
1615
- st.warning(f"Error fetching {k}s for {username}: {str(e)}")
1616
-
1617
- # Create DataFrame with all commits
1618
- df = pd.DataFrame(commit_dates, columns=["date"])
1619
- if not df.empty:
1620
- df = df.drop_duplicates() # Remove any duplicate dates
1621
- return df, items_with_type
1622
-
1623
- # Calendar heatmap function (optimized)
1624
- def make_calendar_heatmap(df, title, year):
1625
- if df.empty:
1626
- st.info(f"No {title.lower()} found for {year}.")
1627
- return
1628
-
1629
- # Optimize DataFrame operations
1630
- df["count"] = 1
1631
- df = df.groupby("date", as_index=False).sum()
1632
- df["date"] = pd.to_datetime(df["date"])
1633
-
1634
- # Create date range more efficiently
1635
- start = pd.Timestamp(f"{year}-01-01")
1636
- end = pd.Timestamp(f"{year}-12-31")
1637
- all_days = pd.date_range(start=start, end=end)
1638
-
1639
- # Optimize DataFrame creation and merging
1640
- heatmap_data = pd.DataFrame({"date": all_days, "count": 0})
1641
- heatmap_data = heatmap_data.merge(df, on="date", how="left", suffixes=("", "_y"))
1642
- heatmap_data["count"] = heatmap_data["count_y"].fillna(0)
1643
- heatmap_data = heatmap_data.drop("count_y", axis=1)
1644
-
1645
- # Calculate week and day of week more efficiently
1646
- heatmap_data["dow"] = heatmap_data["date"].dt.dayofweek
1647
- heatmap_data["week"] = (heatmap_data["date"] - start).dt.days // 7
1648
-
1649
- # Create pivot table more efficiently
1650
- pivot = heatmap_data.pivot(index="dow", columns="week", values="count").fillna(0)
1651
-
1652
- # Optimize month labels calculation
1653
- month_labels = pd.date_range(start, end, freq="MS").strftime("%b")
1654
- month_positions = pd.date_range(start, end, freq="MS").map(lambda x: (x - start).days // 7)
1655
-
1656
- # Create custom colormap with specific boundaries
1657
- from matplotlib.colors import ListedColormap, BoundaryNorm
1658
- colors = ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39'] # GitHub-style green colors
1659
- bounds = [0, 1, 3, 11, 31, float('inf')] # Boundaries for color transitions
1660
- cmap = ListedColormap(colors)
1661
- norm = BoundaryNorm(bounds, cmap.N)
1662
-
1663
- # Create plot more efficiently
1664
- fig, ax = plt.subplots(figsize=(12, 1.5))
1665
-
1666
- # Convert pivot values to integers to ensure proper color mapping
1667
- pivot_int = pivot.astype(int)
1668
-
1669
- # Create heatmap with explicit vmin and vmax
1670
- sns.heatmap(pivot_int, ax=ax, cmap=cmap, norm=norm, linewidths=0.5, linecolor="white",
1671
- square=True, cbar=False, yticklabels=["M", "T", "W", "T", "F", "S", "S"])
1672
-
1673
- ax.set_title(f"{title}", fontsize=14, pad=10)
1674
- ax.set_xlabel("")
1675
- ax.set_ylabel("")
1676
- ax.set_xticks(month_positions)
1677
- ax.set_xticklabels(month_labels, fontsize=10)
1678
- ax.set_yticklabels(ax.get_yticklabels(), rotation=0, fontsize=10)
1679
-
1680
- # ์‹œ๊ฐ์  ํ–ฅ์ƒ์„ ์œ„ํ•œ figure ์Šคํƒ€์ผ๋ง
1681
- fig.tight_layout()
1682
- fig.patch.set_facecolor('#F8F9FA')
1683
-
1684
- st.pyplot(fig)
1685
-
1686
- # Function to create a fancy contribution radar chart
1687
- def create_contribution_radar(username, models_count, spaces_count, datasets_count, commits_count):
1688
- # Create radar chart for contribution metrics
1689
- categories = ['Models', 'Spaces', 'Datasets', 'Activity']
1690
- values = [models_count, spaces_count, datasets_count, commits_count]
1691
-
1692
- # Normalize values for better visualization
1693
- max_vals = [100, 100, 50, 500] # Reasonable max values for each category
1694
- normalized = [min(v/m, 1.0) for v, m in zip(values, max_vals)]
1695
-
1696
- # Create radar chart
1697
- angles = np.linspace(0, 2*np.pi, len(categories), endpoint=False).tolist()
1698
- angles += angles[:1] # Close the loop
1699
-
1700
- normalized += normalized[:1] # Close the loop
1701
-
1702
- fig, ax = plt.subplots(figsize=(6, 6), subplot_kw={'polar': True}, facecolor='#F8F9FA')
1703
-
1704
- # Add background grid with improved styling
1705
- ax.set_theta_offset(np.pi / 2)
1706
- ax.set_theta_direction(-1)
1707
- ax.set_thetagrids(np.degrees(angles[:-1]), categories, fontsize=12, fontweight='bold')
1708
-
1709
- # ๊ทธ๋ฆฌ๋“œ ์Šคํƒ€์ผ๋ง ๊ฐœ์„ 
1710
- ax.grid(color='#CCCCCC', linestyle='-', linewidth=0.5, alpha=0.7)
1711
-
1712
- # Draw the chart with improved color scheme
1713
- ax.fill(angles, normalized, color='#4CAF50', alpha=0.25)
1714
- ax.plot(angles, normalized, color='#4CAF50', linewidth=3)
1715
-
1716
- # Add value labels with improved styling
1717
- for i, val in enumerate(values):
1718
- angle = angles[i]
1719
- x = (normalized[i] + 0.1) * np.cos(angle)
1720
- y = (normalized[i] + 0.1) * np.sin(angle)
1721
- ax.text(angle, normalized[i] + 0.1, str(val),
1722
- ha='center', va='center', fontsize=12,
1723
- fontweight='bold', color='#1976D2')
1724
-
1725
- # Add highlight circles
1726
- circles = [0.25, 0.5, 0.75, 1.0]
1727
- for circle in circles:
1728
- ax.plot(angles, [circle] * len(angles), color='gray', alpha=0.3, linewidth=0.5, linestyle='--')
1729
-
1730
- ax.set_title(f"{username}'s Contribution Profile", fontsize=16, pad=20, fontweight='bold')
1731
-
1732
- # ๋ฐฐ๊ฒฝ ์› ์—†์• ๊ธฐ
1733
- ax.set_facecolor('#F8F9FA')
1734
-
1735
- return fig
1736
-
1737
- # Function to create contribution distribution pie chart
1738
- def create_contribution_pie(model_commits, dataset_commits, space_commits):
1739
- labels = ['Models', 'Datasets', 'Spaces']
1740
- sizes = [model_commits, dataset_commits, space_commits]
1741
-
1742
- # Filter out zero values
1743
- filtered_labels = [label for label, size in zip(labels, sizes) if size > 0]
1744
- filtered_sizes = [size for size in sizes if size > 0]
1745
-
1746
- if not filtered_sizes:
1747
- return None # No data to show
1748
-
1749
- # Use a more attractive color scheme
1750
- colors = ['#FF9800', '#2196F3', '#4CAF50']
1751
- filtered_colors = [color for color, size in zip(colors, sizes) if size > 0]
1752
-
1753
- fig, ax = plt.subplots(figsize=(7, 7), facecolor='#F8F9FA')
1754
-
1755
- # Create exploded pie chart with improved styling
1756
- explode = [0.1] * len(filtered_sizes) # Explode all slices for better visualization
1757
-
1758
- wedges, texts, autotexts = ax.pie(
1759
- filtered_sizes,
1760
- labels=None, # We'll add custom labels
1761
- colors=filtered_colors,
1762
- autopct='%1.1f%%',
1763
- startangle=90,
1764
- shadow=True,
1765
- explode=explode,
1766
- textprops={'fontsize': 14, 'weight': 'bold'},
1767
- wedgeprops={'edgecolor': 'white', 'linewidth': 2}
1768
- )
1769
-
1770
- # Customize the percentage text
1771
- for autotext in autotexts:
1772
- autotext.set_color('white')
1773
- autotext.set_fontsize(12)
1774
- autotext.set_weight('bold')
1775
-
1776
- # Add legend with custom styling
1777
- ax.legend(
1778
- wedges,
1779
- [f"{label} ({size})" for label, size in zip(filtered_labels, filtered_sizes)],
1780
- title="Contribution Types",
1781
- loc="center left",
1782
- bbox_to_anchor=(0.85, 0.5),
1783
- fontsize=12
1784
- )
1785
-
1786
- ax.set_title('Distribution of Contributions by Type', fontsize=16, pad=20, fontweight='bold')
1787
- ax.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle
1788
-
1789
- return fig
1790
-
1791
- # Function to create monthly activity chart
1792
- def create_monthly_activity(df, year):
1793
- if df.empty:
1794
- return None
1795
-
1796
- # Aggregate by month
1797
- df['date'] = pd.to_datetime(df['date'])
1798
- df['month'] = df['date'].dt.month
1799
- df['month_name'] = df['date'].dt.strftime('%b')
1800
-
1801
- # Count by month and ensure all months are present
1802
- month_order = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
1803
- counts_by_month = df.groupby('month_name')['date'].count()
1804
- monthly_counts = pd.Series([counts_by_month.get(m, 0) for m in month_order], index=month_order)
1805
-
1806
- # Create bar chart with improved styling
1807
- fig, ax = plt.subplots(figsize=(14, 6), facecolor='#F8F9FA')
1808
-
1809
- # Create bars with gradient colors based on activity level
1810
- norm = plt.Normalize(0, monthly_counts.max() if monthly_counts.max() > 0 else 1)
1811
- colors = plt.cm.viridis(norm(monthly_counts.values))
1812
-
1813
- bars = ax.bar(monthly_counts.index, monthly_counts.values, color=colors, width=0.7)
1814
-
1815
- # Highlight the month with most activity
1816
- if monthly_counts.max() > 0:
1817
- max_idx = monthly_counts.argmax()
1818
- bars[max_idx].set_color('#FF5722')
1819
- bars[max_idx].set_edgecolor('black')
1820
- bars[max_idx].set_linewidth(1.5)
1821
-
1822
- # Add labels and styling with enhanced design
1823
- ax.set_title(f'Monthly Activity in {year}', fontsize=18, pad=20, fontweight='bold')
1824
- ax.set_xlabel('Month', fontsize=14, labelpad=10)
1825
- ax.set_ylabel('Number of Contributions', fontsize=14, labelpad=10)
1826
-
1827
- # Add value labels on top of bars with improved styling
1828
- for i, count in enumerate(monthly_counts.values):
1829
- if count > 0:
1830
- ax.text(i, count + 0.5, str(int(count)), ha='center', fontsize=12, fontweight='bold')
1831
-
1832
- # Add grid for better readability with improved styling
1833
- ax.grid(axis='y', linestyle='--', alpha=0.7, color='#CCCCCC')
1834
- ax.set_axisbelow(True) # Grid lines behind bars
1835
-
1836
- # Style the chart borders and background
1837
- ax.spines['top'].set_visible(False)
1838
- ax.spines['right'].set_visible(False)
1839
- ax.spines['left'].set_linewidth(0.5)
1840
- ax.spines['bottom'].set_linewidth(0.5)
1841
-
1842
- # Adjust tick parameters for better look
1843
- ax.tick_params(axis='x', labelsize=12, pad=5)
1844
- ax.tick_params(axis='y', labelsize=12, pad=5)
1845
-
1846
- plt.tight_layout()
1847
-
1848
- return fig
1849
-
1850
- # Function to render follower growth simulation
1851
- def simulate_follower_data(username, spaces_count, models_count, total_commits):
1852
- # Simulate follower growth based on contribution metrics
1853
- # This is just a simulation for visual purposes
1854
- import numpy as np
1855
- from datetime import timedelta
1856
-
1857
- # Start with a base number of followers proportional to contribution metrics
1858
- base_followers = max(10, int((spaces_count * 2 + models_count * 3 + total_commits/10) / 6))
1859
-
1860
- # Generate timestamps for the past year
1861
- end_date = datetime.now()
1862
- start_date = end_date - timedelta(days=365)
1863
- dates = pd.date_range(start=start_date, end=end_date, freq='W') # Weekly data points
1864
-
1865
- # Generate follower growth with some randomness
1866
- followers = []
1867
- current = base_followers / 2 # Start from half the base
1868
-
1869
- for i in range(len(dates)):
1870
- growth_factor = 1 + (np.random.random() * 0.1) # Random growth between 0% and 10%
1871
- current = current * growth_factor
1872
- followers.append(int(current))
1873
-
1874
- # Ensure end value matches our base_followers estimate
1875
- followers[-1] = base_followers
1876
-
1877
- # Create the chart with improved styling
1878
- fig, ax = plt.subplots(figsize=(14, 6), facecolor='#F8F9FA')
1879
-
1880
- # Create gradient line for better visualization
1881
- points = np.array([dates, followers]).T.reshape(-1, 1, 2)
1882
- segments = np.concatenate([points[:-1], points[1:]], axis=1)
1883
-
1884
- from matplotlib.collections import LineCollection
1885
- norm = plt.Normalize(0, len(segments))
1886
- lc = LineCollection(segments, cmap='viridis', norm=norm, linewidth=3, alpha=0.8)
1887
- lc.set_array(np.arange(len(segments)))
1888
- line = ax.add_collection(lc)
1889
-
1890
- # Add markers
1891
- ax.scatter(dates, followers, s=50, color='#9C27B0', alpha=0.8, zorder=10)
1892
-
1893
- # Add styling with enhanced design
1894
- ax.set_title(f"Estimated Follower Growth for {username}", fontsize=18, pad=20, fontweight='bold')
1895
- ax.set_xlabel("Date", fontsize=14, labelpad=10)
1896
- ax.set_ylabel("Followers", fontsize=14, labelpad=10)
1897
-
1898
- # Format the axes limits
1899
- ax.set_xlim(dates.min(), dates.max())
1900
- ax.set_ylim(0, max(followers) * 1.1)
1901
-
1902
- # Add grid for better readability with improved styling
1903
- ax.grid(True, linestyle='--', alpha=0.7, color='#CCCCCC')
1904
- ax.set_axisbelow(True) # Grid lines behind plot
1905
-
1906
- # Style the chart borders and background
1907
- ax.spines['top'].set_visible(False)
1908
- ax.spines['right'].set_visible(False)
1909
- ax.spines['left'].set_linewidth(0.5)
1910
- ax.spines['bottom'].set_linewidth(0.5)
1911
-
1912
- # Adjust tick parameters for better look
1913
- ax.tick_params(axis='x', labelsize=12, rotation=45)
1914
- ax.tick_params(axis='y', labelsize=12)
1915
-
1916
- # Add annotations for start and end points
1917
- ax.annotate(f"Start: {followers[0]}",
1918
- xy=(dates[0], followers[0]),
1919
- xytext=(10, 10),
1920
- textcoords='offset points',
1921
- fontsize=12,
1922
- fontweight='bold',
1923
- color='#9C27B0',
1924
- bbox=dict(boxstyle="round,pad=0.3", fc="#F3E5F5", ec="#9C27B0", alpha=0.8))
1925
-
1926
- ax.annotate(f"Current: {followers[-1]}",
1927
- xy=(dates[-1], followers[-1]),
1928
- xytext=(-10, 10),
1929
- textcoords='offset points',
1930
- fontsize=12,
1931
- fontweight='bold',
1932
- color='#9C27B0',
1933
- ha='right',
1934
- bbox=dict(boxstyle="round,pad=0.3", fc="#F3E5F5", ec="#9C27B0", alpha=0.8))
1935
-
1936
- plt.tight_layout()
1937
-
1938
- return fig
1939
-
1940
- # Function to create ranking position visualization
1941
- def create_ranking_chart(username, overall_rank, spaces_rank, models_rank):
1942
- if not (overall_rank or spaces_rank or models_rank):
1943
- return None
1944
-
1945
- # Create a horizontal bar chart for rankings with improved styling
1946
- fig, ax = plt.subplots(figsize=(12, 5), facecolor='#F8F9FA')
1947
-
1948
- categories = []
1949
- positions = []
1950
- colors = []
1951
- rank_values = []
1952
-
1953
- if overall_rank:
1954
- categories.append('Overall')
1955
- positions.append(101 - overall_rank) # Invert rank for visualization (higher is better)
1956
- colors.append('#673AB7')
1957
- rank_values.append(overall_rank)
1958
-
1959
- if spaces_rank:
1960
- categories.append('Spaces')
1961
- positions.append(101 - spaces_rank)
1962
- colors.append('#2196F3')
1963
- rank_values.append(spaces_rank)
1964
-
1965
- if models_rank:
1966
- categories.append('Models')
1967
- positions.append(101 - models_rank)
1968
- colors.append('#FF9800')
1969
- rank_values.append(models_rank)
1970
-
1971
- # Create horizontal bars with enhanced styling
1972
- bars = ax.barh(categories, positions, color=colors, alpha=0.8, height=0.6,
1973
- edgecolor='white', linewidth=1.5)
1974
-
1975
- # Add rank values as text with improved styling
1976
- for i, bar in enumerate(bars):
1977
- ax.text(bar.get_width() + 2, bar.get_y() + bar.get_height()/2,
1978
- f'Rank #{rank_values[i]}', va='center', fontsize=12,
1979
- fontweight='bold', color=colors[i])
1980
-
1981
- # Set chart properties with enhanced styling
1982
- ax.set_xlim(0, 105)
1983
- ax.set_title(f"Ranking Positions for {username} (Top 100)", fontsize=18, pad=20, fontweight='bold')
1984
- ax.set_xlabel("Percentile (higher is better)", fontsize=14, labelpad=10)
1985
-
1986
- # Add explanatory text
1987
- ax.text(50, -0.6, "โ† Lower rank (higher number) | Higher rank (lower number) โ†’",
1988
- ha='center', va='center', fontsize=10, fontweight='bold', color='#666666')
1989
-
1990
- # Add a vertical line at 90th percentile to highlight top 10 with improved styling
1991
- ax.axvline(x=90, color='#FF5252', linestyle='--', alpha=0.7, linewidth=2)
1992
- ax.text(92, len(categories)/2, 'Top 10', color='#D32F2F', fontsize=12,
1993
- rotation=90, va='center', fontweight='bold')
1994
-
1995
- # Style the chart borders and background
1996
- ax.spines['top'].set_visible(False)
1997
- ax.spines['right'].set_visible(False)
1998
- ax.spines['left'].set_linewidth(0.5)
1999
- ax.spines['bottom'].set_linewidth(0.5)
2000
-
2001
- # Adjust tick parameters for better look
2002
- ax.tick_params(axis='x', labelsize=12)
2003
- ax.tick_params(axis='y', labelsize=14, pad=5)
2004
-
2005
- # Add grid for better readability
2006
- ax.grid(axis='x', linestyle='--', alpha=0.5, color='#CCCCCC')
2007
- ax.set_axisbelow(True) # Grid lines behind bars
2008
-
2009
- # Invert x-axis to show ranking position more intuitively
2010
- ax.invert_xaxis()
2011
-
2012
- plt.tight_layout()
2013
- return fig
2014
-
2015
- # Fetch trending accounts with a loading spinner (do this once at the beginning)
2016
- with st.spinner("Loading and analyzing top contributors... This may take a few moments."):
2017
- trending_accounts, top_owners_spaces, top_owners_models, user_stats = get_trending_accounts_with_commits(limit=100)
2018
-
2019
- # Sidebar
2020
- with st.sidebar:
2021
- st.markdown('<h1 style="text-align: center; color: #1E88E5;">๐Ÿ‘ค Contributor</h1>', unsafe_allow_html=True)
2022
-
2023
- # Create tabs for rankings
2024
- tab1, tab2 = st.tabs([
2025
- "Top 100 Overall",
2026
- "Top Spaces & Models"
2027
- ])
2028
-
2029
- with tab1:
2030
- # Show combined trending accounts list with commit-based ranking
2031
- st.markdown('<div class="subheader"><h3>๐Ÿ”ฅ Top 100 Contributors by Commits</h3></div>', unsafe_allow_html=True)
2032
- st.markdown('<p style="font-size: 0.9rem; color: #666; margin-bottom: 10px;">Ranked by total commit count</p>', unsafe_allow_html=True)
2033
-
2034
- # Create a data frame for the table
2035
- if user_stats:
2036
- # Create the overall ranking dataframe with trophies for top 3
2037
- overall_data = []
2038
- for idx, stat in enumerate(user_stats[:100]):
2039
- # Add trophy emojis for top 3
2040
- rank_display = ""
2041
- if idx == 0:
2042
- rank_display = "๐Ÿ† " # Gold trophy for 1st place
2043
- elif idx == 1:
2044
- rank_display = "๐Ÿ† " # Silver trophy for 2nd place
2045
- elif idx == 2:
2046
- rank_display = "๐Ÿ† " # Bronze trophy for 3rd place
2047
-
2048
- overall_data.append([
2049
- f"{rank_display}{stat['username']}",
2050
- str(stat['estimated_commits']),
2051
- str(stat['models']),
2052
- str(stat['spaces']),
2053
- str(stat['datasets'])
2054
- ])
2055
-
2056
- ranking_data_overall = pd.DataFrame(
2057
- overall_data,
2058
- columns=["Contributor", "Total Commits", "Models", "Spaces", "Datasets"]
2059
- )
2060
- ranking_data_overall.index = ranking_data_overall.index + 1 # Start index from 1 for ranking
2061
-
2062
- st.dataframe(
2063
- ranking_data_overall,
2064
- height=900, # ์•ฝ 30ํ–‰ ์ •๋„ ๋ณด์ด๋„๋ก ํ”ฝ์…€ ๋‹จ์œ„ ๋†’์ด ์„ค์ •
2065
- column_config={
2066
- "Contributor": st.column_config.TextColumn("Contributor"),
2067
- "Total Commits": st.column_config.TextColumn("Total Commits"),
2068
- "Models": st.column_config.TextColumn("Models"),
2069
- "Spaces": st.column_config.TextColumn("Spaces"),
2070
- "Datasets": st.column_config.TextColumn("Datasets")
2071
- },
2072
- use_container_width=True,
2073
- hide_index=False
2074
- )
2075
-
2076
- with tab2:
2077
- # Show trending accounts by Spaces & Models
2078
- st.markdown('<div class="subheader"><h3>๐Ÿš€ Spaces Leaders</h3></div>', unsafe_allow_html=True)
2079
-
2080
- # Create a data frame for the Spaces table with medals for top 3
2081
- if top_owners_spaces:
2082
- spaces_data = []
2083
- for idx, (owner, count) in enumerate(top_owners_spaces[:50]):
2084
- # Add medal emojis for top 3
2085
- rank_display = ""
2086
- if idx == 0:
2087
- rank_display = "๐Ÿฅ‡ " # Gold medal for 1st place
2088
- elif idx == 1:
2089
- rank_display = "๐Ÿฅˆ " # Silver medal for 2nd place
2090
- elif idx == 2:
2091
- rank_display = "๐Ÿฅ‰ " # Bronze medal for 3rd place
2092
-
2093
- spaces_data.append([f"{rank_display}{owner}", count])
2094
-
2095
- ranking_data_spaces = pd.DataFrame(spaces_data, columns=["Contributor", "Spaces Count"])
2096
- ranking_data_spaces.index = ranking_data_spaces.index + 1 # Start index from 1 for ranking
2097
-
2098
- st.dataframe(
2099
- ranking_data_spaces,
2100
- column_config={
2101
- "Contributor": st.column_config.TextColumn("Contributor"),
2102
- "Spaces Count": st.column_config.NumberColumn("Spaces Count", format="%d")
2103
- },
2104
- use_container_width=True,
2105
- hide_index=False
2106
- )
2107
-
2108
- # Display the top Models accounts list with medals for top 3
2109
- st.markdown('<div class="subheader"><h3>๐Ÿง  Models Leaders</h3></div>', unsafe_allow_html=True)
2110
-
2111
- # Create a data frame for the Models table with medals for top 3
2112
- if top_owners_models:
2113
- models_data = []
2114
- for idx, (owner, count) in enumerate(top_owners_models[:50]):
2115
- # Add medal emojis for top 3
2116
- rank_display = ""
2117
- if idx == 0:
2118
- rank_display = "๐Ÿฅ‡ " # Gold medal for 1st place
2119
- elif idx == 1:
2120
- rank_display = "๐Ÿฅˆ " # Silver medal for 2nd place
2121
- elif idx == 2:
2122
- rank_display = "๐Ÿฅ‰ " # Bronze medal for 3rd place
2123
-
2124
- models_data.append([f"{rank_display}{owner}", count])
2125
-
2126
- ranking_data_models = pd.DataFrame(models_data, columns=["Contributor", "Models Count"])
2127
- ranking_data_models.index = ranking_data_models.index + 1 # Start index from 1 for ranking
2128
-
2129
- st.dataframe(
2130
- ranking_data_models,
2131
- column_config={
2132
- "Contributor": st.column_config.TextColumn("Contributor"),
2133
- "Models Count": st.column_config.NumberColumn("Models Count", format="%d")
2134
- },
2135
- use_container_width=True,
2136
- hide_index=False
2137
- )
2138
-
2139
- # Add visual divider
2140
- st.markdown('<hr style="margin: 2rem 0; border-color: #e0e0e0;">', unsafe_allow_html=True)
2141
-
2142
- # Display contributor selection with enhanced styling
2143
- st.markdown('<div class="subheader"><h3>Select Contributor</h3></div>', unsafe_allow_html=True)
2144
- selected_trending = st.selectbox(
2145
- "Choose from trending accounts",
2146
- options=trending_accounts[:100], # Limit to top 100
2147
- index=0 if trending_accounts else None,
2148
- key="trending_selectbox"
2149
- )
2150
-
2151
- # Custom account input option with enhanced styling
2152
- st.markdown('<div style="text-align: center; margin: 15px 0; font-weight: bold;">- OR -</div>', unsafe_allow_html=True)
2153
- custom = st.text_input("Enter a username/organization:", placeholder="e.g. facebook, google...")
2154
-
2155
- # Add visual divider
2156
- st.markdown('<hr style="margin: 1.5rem 0; border-color: #e0e0e0;">', unsafe_allow_html=True)
2157
-
2158
- # Set username based on selection or custom input
2159
- if custom.strip():
2160
- username = custom.strip()
2161
- elif selected_trending:
2162
- username = selected_trending
2163
- else:
2164
- username = "facebook" # Default fallback
2165
-
2166
- # Year selection with enhanced styling
2167
- st.markdown('<div class="subheader"><h3>๐Ÿ—“๏ธ Time Period</h3></div>', unsafe_allow_html=True)
2168
- year_options = list(range(datetime.now().year, 2017, -1))
2169
- selected_year = st.selectbox("Select Year:", options=year_options)
2170
-
2171
- # Additional options for customization with enhanced styling
2172
- st.markdown('<div class="subheader"><h3>โš™๏ธ Display Options</h3></div>', unsafe_allow_html=True)
2173
- show_models = st.checkbox("Show Models", value=True)
2174
- show_datasets = st.checkbox("Show Datasets", value=True)
2175
- show_spaces = st.checkbox("Show Spaces", value=True)
2176
-
2177
- # Main Content
2178
- st.markdown(f'<h1 style="text-align: center; color: #1E88E5; margin-bottom: 2rem;">๐Ÿค— Hugging Face Contributions</h1>', unsafe_allow_html=True)
2179
-
2180
- if username:
2181
- # Find user's stats in the pre-calculated data
2182
- user_stat = next((stat for stat in user_stats if stat["username"] == username), None)
2183
-
2184
- # Create a header card with contributor info
2185
- header_col1, header_col2 = st.columns([1, 2])
2186
- with header_col1:
2187
- score_display = f"Score: {user_stat['score']:.1f}" if user_stat else "Score: N/A"
2188
- st.markdown(f'<div style="background-color: #E3F2FD; padding: 20px; border-radius: 10px; border-left: 5px solid #1E88E5;">'
2189
- f'<h2 style="color: #1E88E5;">๐Ÿ‘ค {username}</h2>'
2190
- f'<p style="font-size: 16px;">Analyzing contributions for {selected_year}</p>'
2191
- f'<p style="font-size: 14px; font-weight: bold;">{score_display}</p>'
2192
- f'<p><a href="https://huggingface.co/{username}" target="_blank" style="color: #1E88E5; font-weight: bold;">View Profile</a></p>'
2193
- f'</div>', unsafe_allow_html=True)
2194
-
2195
- with header_col2:
2196
- # Add explanation about the app
2197
- st.markdown(f'<div style="background-color: #F3E5F5; padding: 20px; border-radius: 10px; border-left: 5px solid #9C27B0;">'
2198
- f'<h3 style="color: #9C27B0;">About This Analysis</h3>'
2199
- f'<p>This dashboard analyzes {username}\'s contributions to Hugging Face in {selected_year}, including models, datasets, and spaces.</p>'
2200
- f'<p style="font-style: italic; font-size: 12px;">* Rankings are based on contribution scores combining repos and commit activity.</p>'
2201
- f'</div>', unsafe_allow_html=True)
2202
-
2203
- with st.spinner(f"Fetching detailed contribution data for {username}..."):
2204
- # Initialize variables for tracking
2205
- overall_rank = None
2206
- spaces_rank = None
2207
- models_rank = None
2208
- spaces_count = 0
2209
- models_count = 0
2210
- datasets_count = 0
2211
-
2212
- # Display contributor rank if in top 100
2213
- if username in trending_accounts[:100]:
2214
- overall_rank = trending_accounts.index(username) + 1
2215
-
2216
- # Create a prominent ranking display
2217
- st.markdown(f'<div style="background-color: #FFF8E1; padding: 20px; border-radius: 10px; border-left: 5px solid #FFC107; margin: 1rem 0;">'
2218
- f'<h2 style="color: #FFA000; text-align: center;">๐Ÿ† Ranked #{overall_rank} in Top Contributors</h2>'
2219
- f'</div>', unsafe_allow_html=True)
2220
-
2221
- # Find user in spaces ranking
2222
- for i, (owner, count) in enumerate(top_owners_spaces):
2223
- if owner == username:
2224
- spaces_rank = i+1
2225
- spaces_count = count
2226
- break
2227
-
2228
- # Find user in models ranking
2229
- for i, (owner, count) in enumerate(top_owners_models):
2230
- if owner == username:
2231
- models_rank = i+1
2232
- models_count = count
2233
- break
2234
-
2235
- # Display ranking visualization
2236
- rank_chart = create_ranking_chart(username, overall_rank, spaces_rank, models_rank)
2237
- if rank_chart:
2238
- st.pyplot(rank_chart)
2239
-
2240
- # Create a dictionary to store commits by type
2241
- commits_by_type = {}
2242
- commit_counts_by_type = {}
2243
-
2244
- # Determine which types to fetch based on checkboxes
2245
- types_to_fetch = []
2246
- if show_models:
2247
- types_to_fetch.append("model")
2248
- if show_datasets:
2249
- types_to_fetch.append("dataset")
2250
- if show_spaces:
2251
- types_to_fetch.append("space")
2252
-
2253
- if not types_to_fetch:
2254
- st.warning("Please select at least one content type to display (Models, Datasets, or Spaces)")
2255
- st.stop()
2256
-
2257
- # Create a progress container
2258
- progress_container = st.container()
2259
- progress_container.markdown('<h3 style="color: #1E88E5;">Fetching Repository Data...</h3>', unsafe_allow_html=True)
2260
- progress_bar = progress_container.progress(0)
2261
-
2262
- # Fetch commits for each selected type
2263
- for type_index, kind in enumerate(types_to_fetch):
2264
- try:
2265
- items = cached_list_items(username, kind)
2266
-
2267
- # Update counts for radar chart
2268
- if kind == "model":
2269
- models_count = len(items)
2270
- elif kind == "dataset":
2271
- datasets_count = len(items)
2272
- elif kind == "space":
2273
- spaces_count = len(items)
2274
-
2275
- repo_ids = [item.id for item in items]
2276
-
2277
- progress_container.info(f"Found {len(repo_ids)} {kind}s for {username}")
2278
-
2279
- # Process repos in chunks
2280
- chunk_size = 5
2281
- total_commits = 0
2282
- all_commit_dates = []
2283
-
2284
- for i in range(0, len(repo_ids), chunk_size):
2285
- chunk = repo_ids[i:i + chunk_size]
2286
- with ThreadPoolExecutor(max_workers=min(5, len(chunk))) as executor:
2287
- future_to_repo = {
2288
- executor.submit(fetch_commits_for_repo, repo_id, kind, username, selected_year): repo_id
2289
- for repo_id in chunk
2290
- }
2291
- for future in as_completed(future_to_repo):
2292
- repo_commits, repo_count = future.result()
2293
- if repo_commits:
2294
- all_commit_dates.extend(repo_commits)
2295
- total_commits += repo_count
2296
-
2297
- # Update progress for all types
2298
- progress_per_type = 1.0 / len(types_to_fetch)
2299
- current_type_progress = min(1.0, (i + len(chunk)) / max(1, len(repo_ids)))
2300
- overall_progress = (type_index * progress_per_type) + (current_type_progress * progress_per_type)
2301
- progress_bar.progress(overall_progress)
2302
-
2303
- commits_by_type[kind] = all_commit_dates
2304
- commit_counts_by_type[kind] = total_commits
2305
-
2306
- except Exception as e:
2307
- st.warning(f"Error fetching {kind}s for {username}: {str(e)}")
2308
- commits_by_type[kind] = []
2309
- commit_counts_by_type[kind] = 0
2310
-
2311
- # Complete progress
2312
- progress_bar.progress(1.0)
2313
- progress_container.success("Data fetching complete!")
2314
- time.sleep(0.5) # Short pause for visual feedback
2315
- progress_container.empty() # Clear the progress indicators
2316
-
2317
- # Calculate total commits across all types
2318
- total_commits = sum(commit_counts_by_type.values())
2319
-
2320
- # Main dashboard layout with improved structure
2321
- st.markdown(f'<h2 style="color: #1E88E5; border-bottom: 2px solid #E0E0E0; padding-bottom: 8px; margin-top: 2rem;">Activity Overview</h2>', unsafe_allow_html=True)
2322
-
2323
- # Profile summary
2324
- profile_col1, profile_col2 = st.columns([1, 2])
2325
-
2326
- with profile_col1:
2327
- # Create a stats card with key metrics
2328
- st.markdown(f'<div style="background-color: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">'
2329
- f'<h3 style="color: #1E88E5; text-align: center; margin-bottom: 15px;">Contribution Stats</h3>'
2330
- f'<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">'
2331
- f'<span style="font-weight: bold;">Total Commits:</span><span>{total_commits}</span></div>'
2332
- f'<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">'
2333
- f'<span style="font-weight: bold;">Models:</span><span>{models_count}</span></div>'
2334
- f'<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">'
2335
- f'<span style="font-weight: bold;">Datasets:</span><span>{datasets_count}</span></div>'
2336
- f'<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">'
2337
- f'<span style="font-weight: bold;">Spaces:</span><span>{spaces_count}</span></div>'
2338
- f'</div>', unsafe_allow_html=True)
2339
-
2340
- # Type breakdown pie chart
2341
- model_commits = commit_counts_by_type.get("model", 0)
2342
- dataset_commits = commit_counts_by_type.get("dataset", 0)
2343
- space_commits = commit_counts_by_type.get("space", 0)
2344
-
2345
- pie_chart = create_contribution_pie(model_commits, dataset_commits, space_commits)
2346
- if pie_chart:
2347
- st.pyplot(pie_chart)
2348
-
2349
- with profile_col2:
2350
- # Display contribution radar chart
2351
- radar_fig = create_contribution_radar(username, models_count, spaces_count, datasets_count, total_commits)
2352
- st.pyplot(radar_fig)
2353
-
2354
- # Create DataFrame for all commits
2355
- all_commits = []
2356
- for commits in commits_by_type.values():
2357
- all_commits.extend(commits)
2358
- all_df = pd.DataFrame(all_commits, columns=["date"])
2359
- if not all_df.empty:
2360
- all_df = all_df.drop_duplicates() # Remove any duplicate dates
2361
-
2362
- # Calendar heatmap for all commits in a separate section
2363
- st.markdown(f'<h2 style="color: #1E88E5; border-bottom: 2px solid #E0E0E0; padding-bottom: 8px; margin-top: 2rem;">Contribution Calendar</h2>', unsafe_allow_html=True)
2364
-
2365
- if not all_df.empty:
2366
- make_calendar_heatmap(all_df, "All Contributions", selected_year)
2367
- else:
2368
- st.info(f"No contributions found for {username} in {selected_year}")
2369
-
2370
- # Monthly activity chart
2371
- st.markdown(f'<h2 style="color: #1E88E5; border-bottom: 2px solid #E0E0E0; padding-bottom: 8px; margin-top: 2rem;">Monthly Activity</h2>', unsafe_allow_html=True)
2372
-
2373
- monthly_fig = create_monthly_activity(all_df, selected_year)
2374
- if monthly_fig:
2375
- st.pyplot(monthly_fig)
2376
- else:
2377
- st.info(f"No activity data available for {username} in {selected_year}")
2378
-
2379
- # Follower growth simulation
2380
- st.markdown(f'<h2 style="color: #1E88E5; border-bottom: 2px solid #E0E0E0; padding-bottom: 8px; margin-top: 2rem;">Growth Projection</h2>', unsafe_allow_html=True)
2381
- st.markdown('<div style="background-color: #EDE7F6; padding: 10px; border-radius: 5px; margin-bottom: 15px;">'
2382
- '<p style="font-style: italic; margin: 0;">๐Ÿ“Š This is a simulation based on contribution metrics - for visualization purposes only</p>'
2383
- '</div>', unsafe_allow_html=True)
2384
-
2385
- follower_chart = simulate_follower_data(username, spaces_count, models_count, total_commits)
2386
- st.pyplot(follower_chart)
2387
-
2388
- # Analytics summary section
2389
- if total_commits > 0:
2390
- st.markdown(f'<h2 style="color: #1E88E5; border-bottom: 2px solid #E0E0E0; padding-bottom: 8px; margin-top: 2rem;">๐Ÿ“Š Analytics Summary</h2>', unsafe_allow_html=True)
2391
-
2392
- # Contribution pattern analysis
2393
- monthly_df = pd.DataFrame(all_commits, columns=["date"])
2394
- monthly_df['date'] = pd.to_datetime(monthly_df['date'])
2395
- monthly_df['month'] = monthly_df['date'].dt.month
2396
-
2397
- if not monthly_df.empty:
2398
- most_active_month = monthly_df['month'].value_counts().idxmax()
2399
- month_name = datetime(2020, most_active_month, 1).strftime('%B')
2400
-
2401
- # Create a summary card
2402
- st.markdown(f'<div style="background-color: white; padding: 25px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">'
2403
- f'<h3 style="color: #1E88E5; border-bottom: 1px solid #E0E0E0; padding-bottom: 10px;">Activity Analysis for {username}</h3>'
2404
- f'<ul style="list-style-type: none; padding-left: 5px;">'
2405
- f'<li style="margin: 15px 0; font-size: 16px;">๐Ÿ“ˆ <strong>Total Activity:</strong> {total_commits} contributions in {selected_year}</li>'
2406
- f'<li style="margin: 15px 0; font-size: 16px;">๐Ÿ—“๏ธ <strong>Most Active Month:</strong> {month_name} with {monthly_df["month"].value_counts().max()} contributions</li>'
2407
- f'<li style="margin: 15px 0; font-size: 16px;">๐Ÿงฉ <strong>Repository Breakdown:</strong> {models_count} Models, {spaces_count} Spaces, {datasets_count} Datasets</li>'
2408
- f'</ul>', unsafe_allow_html=True)
2409
-
2410
- # Add ranking context if available
2411
- if overall_rank:
2412
- percentile = 100 - overall_rank
2413
- st.markdown(f'<div style="margin-top: 20px;">'
2414
- f'<h3 style="color: #1E88E5; border-bottom: 1px solid #E0E0E0; padding-bottom: 10px;">Ranking Analysis</h3>'
2415
- f'<ul style="list-style-type: none; padding-left: 5px;">'
2416
- f'<li style="margin: 15px 0; font-size: 16px;">๐Ÿ† <strong>Overall Ranking:</strong> #{overall_rank} (Top {percentile}% of contributors)</li>', unsafe_allow_html=True)
2417
-
2418
- badge_html = '<div style="margin: 20px 0;">'
2419
-
2420
- if spaces_rank and spaces_rank <= 10:
2421
- badge_html += f'<span style="background-color: #FFECB3; color: #FF6F00; padding: 8px 15px; border-radius: 20px; font-weight: bold; margin-right: 10px; display: inline-block; margin-bottom: 10px;">๐ŸŒŸ Elite Spaces Contributor (#{spaces_rank})</span>'
2422
- elif spaces_rank and spaces_rank <= 30:
2423
- badge_html += f'<span style="background-color: #E1F5FE; color: #0277BD; padding: 8px 15px; border-radius: 20px; font-weight: bold; margin-right: 10px; display: inline-block; margin-bottom: 10px;">โœจ Outstanding Spaces Contributor (#{spaces_rank})</span>'
2424
-
2425
- if models_rank and models_rank <= 10:
2426
- badge_html += f'<span style="background-color: #FFECB3; color: #FF6F00; padding: 8px 15px; border-radius: 20px; font-weight: bold; margin-right: 10px; display: inline-block; margin-bottom: 10px;">๐ŸŒŸ Elite Models Contributor (#{models_rank})</span>'
2427
- elif models_rank and models_rank <= 30:
2428
- badge_html += f'<span style="background-color: #E1F5FE; color: #0277BD; padding: 8px 15px; border-radius: 20px; font-weight: bold; margin-right: 10px; display: inline-block; margin-bottom: 10px;">โœจ Outstanding Models Contributor (#{models_rank})</span>'
2429
-
2430
- badge_html += '</div>'
2431
-
2432
- # Add achievement badges
2433
- if spaces_rank or models_rank:
2434
- st.markdown(badge_html, unsafe_allow_html=True)
2435
-
2436
- st.markdown('</ul></div></div>', unsafe_allow_html=True)
2437
-
2438
- # Detailed category analysis section
2439
- st.markdown(f'<h2 style="color: #1E88E5; border-bottom: 2px solid #E0E0E0; padding-bottom: 8px; margin-top: 2rem;">Detailed Category Analysis</h2>', unsafe_allow_html=True)
2440
-
2441
- # Create category cards in columns
2442
- cols = st.columns(len(types_to_fetch)) if types_to_fetch else st.columns(1)
2443
-
2444
- category_icons = {
2445
- "model": "๐Ÿง ",
2446
- "dataset": "๐Ÿ“ฆ",
2447
- "space": "๐Ÿš€"
2448
- }
2449
-
2450
- category_colors = {
2451
- "model": "#FF9800",
2452
- "dataset": "#2196F3",
2453
- "space": "#4CAF50"
2454
- }
2455
-
2456
- for i, kind in enumerate(types_to_fetch):
2457
- with cols[i]:
2458
- try:
2459
- emoji = category_icons.get(kind, "๐Ÿ“Š")
2460
- label = kind.capitalize() + "s"
2461
- color = category_colors.get(kind, "#1E88E5")
2462
-
2463
- total = len(cached_list_items(username, kind))
2464
- commits = commits_by_type.get(kind, [])
2465
- commit_count = commit_counts_by_type.get(kind, 0)
2466
-
2467
- # Create styled card header
2468
- st.markdown(f'<div style="background-color: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); border-top: 5px solid {color};">'
2469
- f'<h3 style="color: {color}; text-align: center;">{emoji} {label}</h3>'
2470
- f'<div style="display: flex; justify-content: space-between; margin: 15px 0;">'
2471
- f'<span style="font-weight: bold;">Total:</span><span>{total}</span></div>'
2472
- f'<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">'
2473
- f'<span style="font-weight: bold;">Commits:</span><span>{commit_count}</span></div>'
2474
- f'</div>', unsafe_allow_html=True)
2475
-
2476
- # Create calendar for this type
2477
- df_kind = pd.DataFrame(commits, columns=["date"])
2478
- if not df_kind.empty:
2479
- df_kind = df_kind.drop_duplicates() # Remove any duplicate dates
2480
- make_calendar_heatmap(df_kind, f"{label} Commits", selected_year)
2481
- else:
2482
- st.info(f"No {label.lower()} activity in {selected_year}")
2483
-
2484
- except Exception as e:
2485
- st.warning(f"Error processing {kind.capitalize()}s: {str(e)}")
2486
- # Show empty placeholder
2487
- st.markdown(f'<div style="background-color: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); border-top: 5px solid #9E9E9E; text-align: center;">'
2488
- f'<h3 style="color: #9E9E9E;">โš ๏ธ Error</h3>'
2489
- f'<p>Could not load {kind.capitalize()}s data</p>'
2490
- f'</div>', unsafe_allow_html=True)
2491
-
2492
- # Footer
2493
- st.markdown('<hr style="margin: 3rem 0 1rem 0;">', unsafe_allow_html=True)
2494
- st.markdown('<p style="text-align: center; color: #9E9E9E; font-size: 0.8rem;">Hugging Face Contributions Dashboard | Data fetched from Hugging Face API</p>', unsafe_allow_html=True)
2495
- else:
2496
- # If no username is selected, show welcome screen
2497
- st.markdown(f'<div style="text-align: center; margin: 50px 0;">'
2498
- f'<img src="https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo.svg" style="width: 200px; margin-bottom: 30px;">'
2499
- f'<h2>Welcome to Hugging Face Contributions Dashboard</h2>'
2500
- f'<p style="font-size: 1.2rem;">Please select a contributor from the sidebar to view their activity.</p>'
2501
- f'</div>', unsafe_allow_html=True)
 
1248
  f'<img src="https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo.svg" style="width: 200px; margin-bottom: 30px;">'
1249
  f'<h2>Welcome to Hugging Face Contributions Dashboard</h2>'
1250
  f'<p style="font-size: 1.2rem;">Please select a contributor from the sidebar to view their activity.</p>'
1251
+ f'</div>', unsafe_allow_html=True)