kolaslab commited on
Commit
d0c1a11
Β·
verified Β·
1 Parent(s): 410004f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +311 -61
app.py CHANGED
@@ -270,68 +270,223 @@ def make_calendar_heatmap(df, title, year):
270
  st.pyplot(fig)
271
 
272
 
273
- # Function to fetch follower data for a user
274
- @lru_cache(maxsize=100)
275
- def fetch_follower_data(username):
276
- try:
277
- # Generate fake data for demonstration purposes
278
- # In a real implementation, this would make an API call to get actual follower data
279
- import random
280
- from dateutil.relativedelta import relativedelta
281
-
282
- # Generate 12 months of fake data
283
- today = datetime.now()
284
- data = []
285
- followers = random.randint(10, 100)
286
-
287
- for i in range(12):
288
- date = today - relativedelta(months=11-i)
289
- followers += random.randint(0, 10)
290
- data.append({
291
- "date": date.strftime("%Y-%m-%d"),
292
- "followers": followers
293
- })
294
-
295
- return data
296
- except Exception as e:
297
- st.error(f"Error generating follower data: {str(e)}")
298
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
 
301
- # Function to render follower chart
302
- def render_follower_chart(username):
303
- follower_data = fetch_follower_data(username)
 
304
 
305
- if not follower_data:
306
- st.info(f"No follower data available for {username}")
307
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
- # Prepare data for chart
310
- dates = [item["date"] for item in follower_data]
311
- followers = [item["followers"] for item in follower_data]
312
 
313
  # Create the chart
314
  fig, ax = plt.subplots(figsize=(12, 5))
315
- ax.plot(dates, followers, marker='o', linestyle='-', color='#60A5FA')
316
 
317
- # Set plot styling
318
- ax.set_title(f"Follower Evolution for {username}", fontsize=16)
319
  ax.set_xlabel("Date", fontsize=12)
320
  ax.set_ylabel("Followers", fontsize=12)
321
 
322
- # Style improvements
323
  ax.grid(True, linestyle='--', alpha=0.7)
324
 
325
- # Rotate date labels for better readability
326
  plt.xticks(rotation=45)
327
-
328
- # Tight layout to ensure everything fits
329
  plt.tight_layout()
330
 
331
- # Display the chart
332
- st.pyplot(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
 
334
 
 
 
 
335
  # Fetch trending accounts with a loading spinner (do this once at the beginning)
336
  with st.spinner("Loading trending accounts..."):
337
  trending_accounts, top_owners_spaces, top_owners_models = get_trending_accounts(limit=100)
@@ -494,26 +649,35 @@ with st.sidebar:
494
 
495
  # Main Content
496
  st.title("πŸ€— Hugging Face Contributions")
 
497
  if username:
498
  with st.spinner(f"Fetching commit data for {username}..."):
499
- # Display contributor rank if in top 30
 
 
 
 
 
 
 
 
500
  if username in trending_accounts[:100]:
501
- rank = trending_accounts.index(username) + 1
502
- st.success(f"πŸ† {username} is ranked #{rank} in the top trending contributors!")
503
 
504
  # Find user in spaces ranking
505
- spaces_rank = None
506
  for i, (owner, count) in enumerate(top_owners_spaces):
507
  if owner == username:
508
  spaces_rank = i+1
 
509
  st.info(f"πŸš€ Spaces Ranking: #{spaces_rank} with {count} spaces")
510
  break
511
 
512
  # Find user in models ranking
513
- models_rank = None
514
  for i, (owner, count) in enumerate(top_owners_models):
515
  if owner == username:
516
  models_rank = i+1
 
517
  st.info(f"🧠 Models Ranking: #{models_rank} with {count} models")
518
  break
519
 
@@ -526,6 +690,11 @@ if username:
526
 
527
  if combined_info:
528
  st.success(f"Combined Rankings (Top 100): {', '.join(combined_info)}")
 
 
 
 
 
529
 
530
  # Create a dictionary to store commits by type
531
  commits_by_type = {}
@@ -548,6 +717,23 @@ if username:
548
  for kind in types_to_fetch:
549
  try:
550
  items = cached_list_items(username, kind)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
  repo_ids = [item.id for item in items]
552
 
553
  st.info(f"Found {len(repo_ids)} {kind}s for {username}")
@@ -594,14 +780,8 @@ if username:
594
  # Profile information
595
  profile_col1, profile_col2 = st.columns([1, 3])
596
  with profile_col1:
597
- # Try to get avatar
598
- try:
599
- avatar_url = f"https://huggingface.co/avatars/{username}"
600
- st.image(avatar_url, width=150)
601
- except:
602
- st.info("No profile image available")
603
-
604
- with profile_col2:
605
  st.metric("Total Commits", total_commits)
606
 
607
  # Show contributor rank if in top owners
@@ -611,6 +791,11 @@ if username:
611
  break
612
 
613
  st.markdown(f"[View Profile on Hugging Face](https://huggingface.co/{username})")
 
 
 
 
 
614
 
615
  # Create DataFrame for all commits
616
  all_commits = []
@@ -620,13 +805,78 @@ if username:
620
  if not all_df.empty:
621
  all_df = all_df.drop_duplicates() # Remove any duplicate dates
622
 
 
 
 
 
 
 
 
 
 
 
623
  make_calendar_heatmap(all_df, "All Commits", selected_year)
624
 
625
- # Add followers chart section
626
- st.subheader(f"πŸ‘₯ Follower Evolution for {username}")
627
- render_follower_chart(username)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
628
 
629
  # Metrics and heatmaps for each selected type
 
630
  cols = st.columns(len(types_to_fetch)) if types_to_fetch else st.columns(1)
631
 
632
  for i, (kind, emoji, label) in enumerate([
@@ -652,4 +902,4 @@ if username:
652
  st.metric(f"Commits in {selected_year}", 0)
653
  make_calendar_heatmap(pd.DataFrame(), f"{label} Commits", selected_year)
654
  else:
655
- st.info("Please select an account from the sidebar to view contributions.")
 
270
  st.pyplot(fig)
271
 
272
 
273
+ # Function to create a fancy contribution radar chart
274
+ def create_contribution_radar(username, models_count, spaces_count, datasets_count, commits_count):
275
+ # Create radar chart for contribution metrics
276
+ categories = ['Models', 'Spaces', 'Datasets', 'Activity']
277
+ values = [models_count, spaces_count, datasets_count, commits_count]
278
+
279
+ # Normalize values for better visualization
280
+ max_vals = [100, 100, 50, 500] # Reasonable max values for each category
281
+ normalized = [min(v/m, 1.0) for v, m in zip(values, max_vals)]
282
+
283
+ # Create radar chart
284
+ angles = np.linspace(0, 2*np.pi, len(categories), endpoint=False).tolist()
285
+ angles += angles[:1] # Close the loop
286
+
287
+ normalized += normalized[:1] # Close the loop
288
+
289
+ fig, ax = plt.subplots(figsize=(6, 6), subplot_kw={'polar': True})
290
+
291
+ # Add background grid
292
+ ax.set_theta_offset(np.pi / 2)
293
+ ax.set_theta_direction(-1)
294
+ ax.set_thetagrids(np.degrees(angles[:-1]), categories)
295
+
296
+ # Draw the chart
297
+ ax.fill(angles, normalized, color='#4CAF50', alpha=0.25)
298
+ ax.plot(angles, normalized, color='#4CAF50', linewidth=2)
299
+
300
+ # Add value labels
301
+ for i, val in enumerate(values):
302
+ angle = angles[i]
303
+ x = normalized[i] * np.cos(angle)
304
+ y = normalized[i] * np.sin(angle)
305
+ ax.text(angle, normalized[i] + 0.05, str(val),
306
+ ha='center', va='center', fontsize=10,
307
+ fontweight='bold')
308
+
309
+ ax.set_title(f"{username}'s Contribution Profile", fontsize=15, pad=20)
310
+
311
+ return fig
312
 
313
 
314
+ # Function to create contribution distribution pie chart
315
+ def create_contribution_pie(model_commits, dataset_commits, space_commits):
316
+ labels = ['Models', 'Datasets', 'Spaces']
317
+ sizes = [model_commits, dataset_commits, space_commits]
318
 
319
+ # Filter out zero values
320
+ filtered_labels = [label for label, size in zip(labels, sizes) if size > 0]
321
+ filtered_sizes = [size for size in sizes if size > 0]
322
+
323
+ if not filtered_sizes:
324
+ return None # No data to show
325
+
326
+ fig, ax = plt.subplots(figsize=(6, 6))
327
+ colors = ['#FF9800', '#2196F3', '#4CAF50']
328
+ filtered_colors = [color for color, size in zip(colors, sizes) if size > 0]
329
+
330
+ # Create exploded pie chart
331
+ explode = [0.05] * len(filtered_sizes) # Explode all slices slightly
332
+
333
+ ax.pie(filtered_sizes, labels=filtered_labels, colors=filtered_colors,
334
+ autopct='%1.1f%%', startangle=90, shadow=True, explode=explode)
335
+ ax.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle
336
+
337
+ ax.set_title('Distribution of Contributions by Type', fontsize=15)
338
+
339
+ return fig
340
+
341
+
342
+ # Function to create monthly activity chart
343
+ def create_monthly_activity(df, year):
344
+ if df.empty:
345
+ return None
346
+
347
+ # Aggregate by month
348
+ df['date'] = pd.to_datetime(df['date'])
349
+ df['month'] = df['date'].dt.strftime('%b')
350
+ monthly_counts = df.groupby('month')['date'].count().reindex(
351
+ pd.date_range(start=f'{year}-01-01', end=f'{year}-12-31', freq='MS').strftime('%b')
352
+ ).fillna(0)
353
+
354
+ # Create bar chart
355
+ fig, ax = plt.subplots(figsize=(12, 5))
356
+ months = monthly_counts.index
357
+ counts = monthly_counts.values
358
+
359
+ bars = ax.bar(months, counts, color='#2196F3')
360
+
361
+ # Highlight the month with most activity
362
+ if counts.max() > 0:
363
+ max_idx = counts.argmax()
364
+ bars[max_idx].set_color('#FF5722')
365
+
366
+ # Add labels and styling
367
+ ax.set_title(f'Monthly Activity in {year}', fontsize=15)
368
+ ax.set_xlabel('Month', fontsize=12)
369
+ ax.set_ylabel('Number of Contributions', fontsize=12)
370
+
371
+ # Add value labels on top of bars
372
+ for i, count in enumerate(counts):
373
+ if count > 0:
374
+ ax.text(i, count + 0.5, str(int(count)), ha='center', fontsize=10)
375
+
376
+ # Add grid for better readability
377
+ ax.grid(axis='y', linestyle='--', alpha=0.7)
378
+
379
+ plt.xticks(rotation=45)
380
+ plt.tight_layout()
381
+
382
+ return fig
383
+
384
+
385
+ # Function to render follower growth simulation
386
+ def simulate_follower_data(username, spaces_count, models_count, total_commits):
387
+ # Simulate follower growth based on contribution metrics
388
+ # This is just a simulation for visual purposes
389
+ import numpy as np
390
+ from datetime import timedelta
391
+
392
+ # Start with a base number of followers proportional to contribution metrics
393
+ base_followers = max(10, int((spaces_count * 2 + models_count * 3 + total_commits/10) / 6))
394
+
395
+ # Generate timestamps for the past year
396
+ end_date = datetime.now()
397
+ start_date = end_date - timedelta(days=365)
398
+ dates = pd.date_range(start=start_date, end=end_date, freq='W') # Weekly data points
399
+
400
+ # Generate follower growth with some randomness
401
+ followers = []
402
+ current = base_followers / 2 # Start from half the base
403
+
404
+ for i in range(len(dates)):
405
+ growth_factor = 1 + (np.random.random() * 0.1) # Random growth between 0% and 10%
406
+ current = current * growth_factor
407
+ followers.append(int(current))
408
 
409
+ # Ensure end value matches our base_followers estimate
410
+ followers[-1] = base_followers
 
411
 
412
  # Create the chart
413
  fig, ax = plt.subplots(figsize=(12, 5))
414
+ ax.plot(dates, followers, marker='o', linestyle='-', color='#9C27B0', markersize=5)
415
 
416
+ # Add styling
417
+ ax.set_title(f"Estimated Follower Growth for {username}", fontsize=15)
418
  ax.set_xlabel("Date", fontsize=12)
419
  ax.set_ylabel("Followers", fontsize=12)
420
 
421
+ # Add grid for better readability
422
  ax.grid(True, linestyle='--', alpha=0.7)
423
 
424
+ # Format date axis
425
  plt.xticks(rotation=45)
 
 
426
  plt.tight_layout()
427
 
428
+ return fig
429
+
430
+
431
+ # Function to create ranking position visualization
432
+ def create_ranking_chart(username, overall_rank, spaces_rank, models_rank):
433
+ if not (overall_rank or spaces_rank or models_rank):
434
+ return None
435
+
436
+ # Create a horizontal bar chart for rankings
437
+ fig, ax = plt.subplots(figsize=(10, 4))
438
+
439
+ categories = []
440
+ positions = []
441
+ colors = []
442
+
443
+ if overall_rank:
444
+ categories.append('Overall')
445
+ positions.append(101 - overall_rank) # Invert rank for visualization (higher is better)
446
+ colors.append('#673AB7')
447
+
448
+ if spaces_rank:
449
+ categories.append('Spaces')
450
+ positions.append(101 - spaces_rank)
451
+ colors.append('#2196F3')
452
+
453
+ if models_rank:
454
+ categories.append('Models')
455
+ positions.append(101 - models_rank)
456
+ colors.append('#FF9800')
457
+
458
+ # Create horizontal bars
459
+ bars = ax.barh(categories, positions, color=colors, alpha=0.7)
460
+
461
+ # Add rank values as text
462
+ for i, bar in enumerate(bars):
463
+ rank_val = 0
464
+ if categories[i] == 'Overall': rank_val = overall_rank
465
+ elif categories[i] == 'Spaces': rank_val = spaces_rank
466
+ elif categories[i] == 'Models': rank_val = models_rank
467
+
468
+ ax.text(bar.get_width() + 1, bar.get_y() + bar.get_height()/2,
469
+ f'Rank #{rank_val}', va='center', fontsize=10, fontweight='bold')
470
+
471
+ # Set chart properties
472
+ ax.set_xlim(0, 100)
473
+ ax.set_title(f"Ranking Positions for {username} (Top 100)", fontsize=15)
474
+ ax.set_xlabel("Percentile (higher is better)", fontsize=12)
475
+
476
+ # Add a vertical line at 90th percentile to highlight top 10
477
+ ax.axvline(x=90, color='red', linestyle='--', alpha=0.5)
478
+ ax.text(91, 0.5, 'Top 10', color='red', fontsize=10, rotation=90, va='center')
479
+
480
+ # Invert x-axis to show ranking position more intuitively
481
+ ax.invert_xaxis()
482
+
483
+ plt.tight_layout()
484
+ return fig
485
 
486
 
487
+ # Import additional libraries for advanced visualizations
488
+ import numpy as np
489
+
490
  # Fetch trending accounts with a loading spinner (do this once at the beginning)
491
  with st.spinner("Loading trending accounts..."):
492
  trending_accounts, top_owners_spaces, top_owners_models = get_trending_accounts(limit=100)
 
649
 
650
  # Main Content
651
  st.title("πŸ€— Hugging Face Contributions")
652
+
653
  if username:
654
  with st.spinner(f"Fetching commit data for {username}..."):
655
+ # Initialize variables for tracking
656
+ overall_rank = None
657
+ spaces_rank = None
658
+ models_rank = None
659
+ spaces_count = 0
660
+ models_count = 0
661
+ datasets_count = 0
662
+
663
+ # Display contributor rank if in top 100
664
  if username in trending_accounts[:100]:
665
+ overall_rank = trending_accounts.index(username) + 1
666
+ st.success(f"πŸ† {username} is ranked #{overall_rank} in the top trending contributors!")
667
 
668
  # Find user in spaces ranking
 
669
  for i, (owner, count) in enumerate(top_owners_spaces):
670
  if owner == username:
671
  spaces_rank = i+1
672
+ spaces_count = count
673
  st.info(f"πŸš€ Spaces Ranking: #{spaces_rank} with {count} spaces")
674
  break
675
 
676
  # Find user in models ranking
 
677
  for i, (owner, count) in enumerate(top_owners_models):
678
  if owner == username:
679
  models_rank = i+1
680
+ models_count = count
681
  st.info(f"🧠 Models Ranking: #{models_rank} with {count} models")
682
  break
683
 
 
690
 
691
  if combined_info:
692
  st.success(f"Combined Rankings (Top 100): {', '.join(combined_info)}")
693
+
694
+ # Add ranking visualization
695
+ rank_chart = create_ranking_chart(username, overall_rank, spaces_rank, models_rank)
696
+ if rank_chart:
697
+ st.pyplot(rank_chart)
698
 
699
  # Create a dictionary to store commits by type
700
  commits_by_type = {}
 
717
  for kind in types_to_fetch:
718
  try:
719
  items = cached_list_items(username, kind)
720
+
721
+ # Update counts for radar chart
722
+ if kind == "model":
723
+ models_count = len(items)
724
+ elif kind == "dataset":
725
+
726
+
727
+ items = cached_list_items(username, kind)
728
+
729
+ # Update counts for radar chart
730
+ if kind == "model":
731
+ models_count = len(items)
732
+ elif kind == "dataset":
733
+ datasets_count = len(items)
734
+ elif kind == "space":
735
+ spaces_count = len(items)
736
+
737
  repo_ids = [item.id for item in items]
738
 
739
  st.info(f"Found {len(repo_ids)} {kind}s for {username}")
 
780
  # Profile information
781
  profile_col1, profile_col2 = st.columns([1, 3])
782
  with profile_col1:
783
+ # Skip avatar image display since it's causing problems
784
+ st.info(f"Profile: {username}")
 
 
 
 
 
 
785
  st.metric("Total Commits", total_commits)
786
 
787
  # Show contributor rank if in top owners
 
791
  break
792
 
793
  st.markdown(f"[View Profile on Hugging Face](https://huggingface.co/{username})")
794
+
795
+ with profile_col2:
796
+ # Display contribution radar chart
797
+ radar_fig = create_contribution_radar(username, models_count, spaces_count, datasets_count, total_commits)
798
+ st.pyplot(radar_fig)
799
 
800
  # Create DataFrame for all commits
801
  all_commits = []
 
805
  if not all_df.empty:
806
  all_df = all_df.drop_duplicates() # Remove any duplicate dates
807
 
808
+ # Monthly activity chart
809
+ st.subheader(f"Monthly Activity Pattern ({selected_year})")
810
+ monthly_fig = create_monthly_activity(all_df, selected_year)
811
+ if monthly_fig:
812
+ st.pyplot(monthly_fig)
813
+ else:
814
+ st.info(f"No activity data available for {username} in {selected_year}")
815
+
816
+ # Calendar heatmap for all commits
817
+ st.subheader(f"Contribution Calendar ({selected_year})")
818
  make_calendar_heatmap(all_df, "All Commits", selected_year)
819
 
820
+ # Contribution distribution pie chart
821
+ st.subheader("Contribution Distribution by Type")
822
+ model_commits = commit_counts_by_type.get("model", 0)
823
+ dataset_commits = commit_counts_by_type.get("dataset", 0)
824
+ space_commits = commit_counts_by_type.get("space", 0)
825
+
826
+ pie_chart = create_contribution_pie(model_commits, dataset_commits, space_commits)
827
+ if pie_chart:
828
+ st.pyplot(pie_chart)
829
+ else:
830
+ st.info("No contribution data available to show distribution")
831
+
832
+ # Follower growth simulation
833
+ st.subheader(f"Follower Growth Simulation")
834
+ st.caption("Based on contribution metrics - for visualization purposes only")
835
+ follower_chart = simulate_follower_data(username, spaces_count, models_count, total_commits)
836
+ st.pyplot(follower_chart)
837
+
838
+ # Add analysis message
839
+ if total_commits > 0:
840
+ st.subheader("πŸ“Š Analytics Summary")
841
+
842
+ # Contribution pattern analysis
843
+ monthly_df = pd.DataFrame(all_commits, columns=["date"])
844
+ monthly_df['date'] = pd.to_datetime(monthly_df['date'])
845
+ monthly_df['month'] = monthly_df['date'].dt.month
846
+
847
+ if not monthly_df.empty:
848
+ most_active_month = monthly_df['month'].value_counts().idxmax()
849
+ month_name = datetime(2020, most_active_month, 1).strftime('%B')
850
+
851
+ st.markdown(f"""
852
+ ### Activity Analysis for {username}
853
+
854
+ - **Total Activity**: {total_commits} contributions in {selected_year}
855
+ - **Most Active Month**: {month_name} with {monthly_df['month'].value_counts().max()} contributions
856
+ - **Repository Breakdown**: {models_count} Models, {spaces_count} Spaces, {datasets_count} Datasets
857
+ """)
858
+
859
+ # Add ranking context if available
860
+ if overall_rank:
861
+ percentile = 100 - overall_rank
862
+ st.markdown(f"""
863
+ ### Ranking Analysis
864
+
865
+ - **Overall Ranking**: #{overall_rank} (Top {percentile}% of contributors)
866
+ """)
867
+
868
+ if spaces_rank and spaces_rank <= 10:
869
+ st.markdown(f"- 🌟 **Elite Spaces Contributor**: Top 10 ({spaces_rank}) in Spaces contributions")
870
+ elif spaces_rank and spaces_rank <= 30:
871
+ st.markdown(f"- ✨ **Outstanding Spaces Contributor**: Top 30 ({spaces_rank}) in Spaces contributions")
872
+
873
+ if models_rank and models_rank <= 10:
874
+ st.markdown(f"- 🌟 **Elite Models Contributor**: Top 10 ({models_rank}) in Models contributions")
875
+ elif models_rank and models_rank <= 30:
876
+ st.markdown(f"- ✨ **Outstanding Models Contributor**: Top 30 ({models_rank}) in Models contributions")
877
 
878
  # Metrics and heatmaps for each selected type
879
+ st.subheader("Detailed Category Analysis")
880
  cols = st.columns(len(types_to_fetch)) if types_to_fetch else st.columns(1)
881
 
882
  for i, (kind, emoji, label) in enumerate([
 
902
  st.metric(f"Commits in {selected_year}", 0)
903
  make_calendar_heatmap(pd.DataFrame(), f"{label} Commits", selected_year)
904
  else:
905
+ st.info("Please select an account from the sidebar to view contributions.")