fffiloni commited on
Commit
cdb9fb7
Β·
verified Β·
1 Parent(s): 280f5d8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +268 -163
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import time
 
2
  from datetime import datetime
3
  from functools import lru_cache
4
  from concurrent.futures import ThreadPoolExecutor, as_completed
@@ -7,125 +8,178 @@ import gradio as gr
7
  import pandas as pd
8
  import matplotlib.pyplot as plt
9
  import seaborn as sns
10
-
11
  from huggingface_hub import HfApi
12
 
13
 
14
- api = HfApi()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
 
17
- # -----------------------------
18
- # Cached HF API helpers
19
- # -----------------------------
 
 
 
 
20
  @lru_cache(maxsize=1000)
21
- def cached_repo_info(repo_id, repo_type):
22
- return api.repo_info(repo_id=repo_id, repo_type=repo_type)
23
 
24
 
25
  @lru_cache(maxsize=1000)
26
- def cached_list_commits(repo_id, repo_type):
27
- return list(api.list_repo_commits(repo_id=repo_id, repo_type=repo_type))
28
 
29
 
30
- @lru_cache(maxsize=100)
31
- def cached_list_items(username, kind):
 
32
  if kind == "model":
33
  return list(api.list_models(author=username))
34
- elif kind == "dataset":
35
  return list(api.list_datasets(author=username))
36
- elif kind == "space":
37
  return list(api.list_spaces(author=username))
38
  return []
39
 
40
 
41
- # -----------------------------
42
- # Simple rate limiter
43
- # -----------------------------
44
- class RateLimiter:
45
- def __init__(self, calls_per_second=10):
46
- self.calls_per_second = calls_per_second
47
- self.last_call = 0
48
-
49
- def wait(self):
50
- current_time = time.time()
51
- time_since_last_call = current_time - self.last_call
52
- min_interval = 1.0 / self.calls_per_second
53
- if time_since_last_call < min_interval:
54
- time.sleep(min_interval - time_since_last_call)
55
- self.last_call = time.time()
56
-
57
-
58
- rate_limiter = RateLimiter()
59
-
60
-
61
- # -----------------------------
62
- # Data fetching
63
- # -----------------------------
64
- def fetch_commits_for_repo(repo_id, repo_type, selected_year):
 
 
 
 
 
 
 
 
 
 
65
  try:
66
  rate_limiter.wait()
67
 
68
- repo_info = cached_repo_info(repo_id, repo_type)
69
-
70
- # Ignore private / gated repos
71
- if getattr(repo_info, "private", False) or getattr(repo_info, "gated", False):
72
- return [], 0
73
 
74
  commit_dates = []
75
  commit_count = 0
76
 
77
- # Initial creation date
78
- if getattr(repo_info, "created_at", None) is not None:
79
- initial_commit_date = pd.to_datetime(repo_info.created_at).tz_localize(None).date()
80
- if initial_commit_date.year == selected_year:
81
- commit_dates.append(initial_commit_date)
82
  commit_count += 1
83
 
84
- # Repo commits
85
- commits = cached_list_commits(repo_id, repo_type)
86
  for commit in commits:
87
- if getattr(commit, "created_at", None) is None:
 
88
  continue
89
- commit_date = pd.to_datetime(commit.created_at).tz_localize(None).date()
90
- if commit_date.year == selected_year:
91
- commit_dates.append(commit_date)
 
92
  commit_count += 1
93
 
94
- return commit_dates, commit_count
95
 
96
- except Exception:
97
- return [], 0
98
 
99
 
100
- def collect_kind_data(username, kind, selected_year):
 
 
 
 
 
101
  try:
102
- items = cached_list_items(username, kind)
103
  repo_ids = [item.id for item in items]
104
 
105
  chunk_size = 5
106
  total_commits = 0
107
  all_commit_dates = []
 
108
 
109
  for i in range(0, len(repo_ids), chunk_size):
110
  chunk = repo_ids[i:i + chunk_size]
 
 
111
 
112
  with ThreadPoolExecutor(max_workers=min(5, len(chunk))) as executor:
113
- future_to_repo = {
114
- executor.submit(fetch_commits_for_repo, repo_id, kind, selected_year): repo_id
 
 
 
 
 
 
115
  for repo_id in chunk
116
  }
117
 
118
- for future in as_completed(future_to_repo):
119
- repo_commits, repo_count = future.result()
120
  if repo_commits:
121
  all_commit_dates.extend(repo_commits)
122
  total_commits += repo_count
 
 
123
 
124
  return {
125
  "total_items": len(items),
126
  "commit_dates": all_commit_dates,
127
  "commit_count": total_commits,
128
- "error": None,
129
  }
130
 
131
  except Exception as e:
@@ -133,19 +187,28 @@ def collect_kind_data(username, kind, selected_year):
133
  "total_items": 0,
134
  "commit_dates": [],
135
  "commit_count": 0,
136
- "error": f"Error fetching {kind}s for {username}: {str(e)}",
137
  }
138
 
139
 
140
- # -----------------------------
141
  # Plot helpers
142
- # -----------------------------
143
- def make_calendar_heatmap(df, title, year):
144
- fig, ax = plt.subplots(figsize=(12, 1.3))
 
 
 
145
 
146
  if df.empty:
147
- ax.text(0.5, 0.5, f"No {title.lower()} found for {year}.",
148
- ha="center", va="center", fontsize=11)
 
 
 
 
 
 
149
  ax.axis("off")
150
  fig.tight_layout()
151
  return fig
@@ -162,7 +225,7 @@ def make_calendar_heatmap(df, title, year):
162
  heatmap_data = pd.DataFrame({"date": all_days, "count": 0})
163
  heatmap_data = heatmap_data.merge(df, on="date", how="left", suffixes=("", "_y"))
164
  heatmap_data["count"] = heatmap_data["count_y"].fillna(0)
165
- heatmap_data = heatmap_data.drop("count_y", axis=1)
166
 
167
  heatmap_data["dow"] = heatmap_data["date"].dt.dayofweek
168
  heatmap_data["week"] = (heatmap_data["date"] - start).dt.days // 7
@@ -174,8 +237,9 @@ def make_calendar_heatmap(df, title, year):
174
  month_positions = month_starts.map(lambda x: (x - start).days // 7)
175
 
176
  from matplotlib.colors import ListedColormap, BoundaryNorm
177
- colors = ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39']
178
- bounds = [0, 1, 3, 11, 31, float('inf')]
 
179
  cmap = ListedColormap(colors)
180
  norm = BoundaryNorm(bounds, cmap.N)
181
 
@@ -184,64 +248,86 @@ def make_calendar_heatmap(df, title, year):
184
  ax=ax,
185
  cmap=cmap,
186
  norm=norm,
187
- linewidths=0.5,
188
  linecolor="white",
189
  square=True,
190
  cbar=False,
191
  yticklabels=["M", "T", "W", "T", "F", "S", "S"],
192
  )
193
 
194
- ax.set_title(title, fontsize=12, pad=10)
195
  ax.set_xlabel("")
196
  ax.set_ylabel("")
197
- ax.set_xticks(month_positions)
198
- ax.set_xticklabels(month_labels, fontsize=8)
199
- ax.set_yticklabels(ax.get_yticklabels(), rotation=0, fontsize=8)
200
 
201
  fig.tight_layout()
202
  return fig
203
 
204
 
205
- def metric_md(label, value):
206
- return f"### {label}\n# {value}"
 
 
 
 
 
 
 
 
 
 
207
 
208
 
209
- # -----------------------------
210
  # Main callback
211
- # -----------------------------
212
- def run_app(selected_username, custom_username, selected_year):
 
 
 
 
 
 
 
 
 
213
  username = (custom_username or "").strip() or selected_username
 
214
 
215
  if not username:
216
- empty_fig = make_calendar_heatmap(pd.DataFrame(), "All Commits", selected_year)
 
217
  return (
218
  "## Please provide a username or org name.",
219
- metric_md("Total Commits", 0),
220
- empty_fig,
221
- metric_md("🧠 Models", 0),
222
- metric_md(f"Commits in {selected_year}", 0),
223
- make_calendar_heatmap(pd.DataFrame(), "Models Commits", selected_year),
224
- metric_md("πŸ“¦ Datasets", 0),
225
- metric_md(f"Commits in {selected_year}", 0),
226
- make_calendar_heatmap(pd.DataFrame(), "Datasets Commits", selected_year),
227
- metric_md("πŸš€ Spaces", 0),
228
- metric_md(f"Commits in {selected_year}", 0),
229
- make_calendar_heatmap(pd.DataFrame(), "Spaces Commits", selected_year),
230
- "No username provided."
231
  )
232
 
 
 
233
  commits_by_type = {}
234
  commit_counts_by_type = {}
235
  totals_by_type = {}
236
  warnings = []
237
 
238
  for kind in ["model", "dataset", "space"]:
239
- result = collect_kind_data(username, kind, int(selected_year))
240
  commits_by_type[kind] = result["commit_dates"]
241
  commit_counts_by_type[kind] = result["commit_count"]
242
  totals_by_type[kind] = result["total_items"]
243
- if result["error"]:
244
- warnings.append(result["error"])
245
 
246
  total_commits = sum(commit_counts_by_type.values())
247
 
@@ -254,109 +340,128 @@ def run_app(selected_username, custom_username, selected_year):
254
  all_df = all_df.drop_duplicates()
255
 
256
  model_df = pd.DataFrame(commits_by_type["model"], columns=["date"])
 
 
 
257
  if not model_df.empty:
258
  model_df = model_df.drop_duplicates()
259
-
260
- dataset_df = pd.DataFrame(commits_by_type["dataset"], columns=["date"])
261
  if not dataset_df.empty:
262
  dataset_df = dataset_df.drop_duplicates()
263
-
264
- space_df = pd.DataFrame(commits_by_type["space"], columns=["date"])
265
  if not space_df.empty:
266
  space_df = space_df.drop_duplicates()
267
 
268
- header = f"## {username}'s Activity in {selected_year}"
269
- warnings_text = "\n".join(warnings) if warnings else "Done."
 
 
 
270
 
271
  return (
272
- header,
273
- metric_md("Total Commits", total_commits),
274
- make_calendar_heatmap(all_df, "All Commits", int(selected_year)),
275
 
276
- metric_md("🧠 Models", totals_by_type["model"]),
277
- metric_md(f"Commits in {selected_year}", commit_counts_by_type["model"]),
278
- make_calendar_heatmap(model_df, "Models Commits", int(selected_year)),
279
 
280
- metric_md("πŸ“¦ Datasets", totals_by_type["dataset"]),
281
- metric_md(f"Commits in {selected_year}", commit_counts_by_type["dataset"]),
282
- make_calendar_heatmap(dataset_df, "Datasets Commits", int(selected_year)),
283
 
284
- metric_md("πŸš€ Spaces", totals_by_type["space"]),
285
- metric_md(f"Commits in {selected_year}", commit_counts_by_type["space"]),
286
- make_calendar_heatmap(space_df, "Spaces Commits", int(selected_year)),
287
 
288
- warnings_text,
289
  )
290
 
291
 
292
- # -----------------------------
293
- # Gradio UI
294
- # -----------------------------
295
  year_options = list(range(datetime.now().year, 2017, -1))
296
- default_users = ["ritvik77", "facebook", "google", "stabilityai", "Salesforce", "tiiuae", "bigscience"]
297
-
298
- with gr.Blocks(title="HF Contributions") as demo:
 
 
 
 
 
 
 
 
299
  gr.Markdown("# πŸ€— Hugging Face Contributions")
300
 
301
  with gr.Sidebar():
 
 
302
  gr.Markdown("## πŸ‘€ Contributor")
303
  selected_username = gr.Dropdown(
304
  choices=default_users,
305
  value="ritvik77",
306
- label="Select a username/org"
307
  )
308
- gr.Markdown("**OR**")
309
  custom_username = gr.Textbox(
310
  label="Custom username/org",
311
- placeholder="Enter custom username/org"
312
  )
313
  selected_year = gr.Dropdown(
314
  choices=year_options,
315
  value=year_options[0],
316
- label="πŸ—“οΈ Year"
317
  )
318
  run_button = gr.Button("Fetch commit data", variant="primary")
319
 
320
  status_header = gr.Markdown("## Ready")
321
- total_commits_md = gr.Markdown("### Total Commits\n# 0")
322
- all_plot = gr.Plot(label="All Commits")
323
-
324
- with gr.Row():
325
- with gr.Column():
326
- models_total_md = gr.Markdown("### 🧠 Models\n# 0")
327
- models_commits_md = gr.Markdown(f"### Commits in {year_options[0]}\n# 0")
328
- models_plot = gr.Plot(label="Models Commits")
329
-
330
- with gr.Column():
331
- datasets_total_md = gr.Markdown("### πŸ“¦ Datasets\n# 0")
332
- datasets_commits_md = gr.Markdown(f"### Commits in {year_options[0]}\n# 0")
333
- datasets_plot = gr.Plot(label="Datasets Commits")
334
-
335
- with gr.Column():
336
- spaces_total_md = gr.Markdown("### πŸš€ Spaces\n# 0")
337
- spaces_commits_md = gr.Markdown(f"### Commits in {year_options[0]}\n# 0")
338
- spaces_plot = gr.Plot(label="Spaces Commits")
339
-
340
- logs_box = gr.Textbox(label="Logs / Warnings", interactive=False, lines=6)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
  run_button.click(
343
  fn=run_app,
344
  inputs=[selected_username, custom_username, selected_year],
345
- outputs=[
346
- status_header,
347
- total_commits_md,
348
- all_plot,
349
- models_total_md,
350
- models_commits_md,
351
- models_plot,
352
- datasets_total_md,
353
- datasets_commits_md,
354
- datasets_plot,
355
- spaces_total_md,
356
- spaces_commits_md,
357
- spaces_plot,
358
- logs_box,
359
- ],
360
  )
361
 
362
  demo.queue().launch()
 
1
  import time
2
+ import threading
3
  from datetime import datetime
4
  from functools import lru_cache
5
  from concurrent.futures import ThreadPoolExecutor, as_completed
 
8
  import pandas as pd
9
  import matplotlib.pyplot as plt
10
  import seaborn as sns
 
11
  from huggingface_hub import HfApi
12
 
13
 
14
+ # ============================================================
15
+ # API
16
+ # ============================================================
17
+ def get_api(token: str | None = None) -> HfApi:
18
+ return HfApi(token=token) if token else HfApi()
19
+
20
+
21
+ # ============================================================
22
+ # Rate limiter (thread-safe)
23
+ # ============================================================
24
+ class RateLimiter:
25
+ def __init__(self, calls_per_second=10):
26
+ self.calls_per_second = calls_per_second
27
+ self.last_call = 0.0
28
+ self.lock = threading.Lock()
29
+
30
+ def wait(self):
31
+ with self.lock:
32
+ current_time = time.time()
33
+ elapsed = current_time - self.last_call
34
+ min_interval = 1.0 / self.calls_per_second
35
+ if elapsed < min_interval:
36
+ time.sleep(min_interval - elapsed)
37
+ self.last_call = time.time()
38
+
39
+
40
+ rate_limiter = RateLimiter(calls_per_second=10)
41
 
42
 
43
+ # ============================================================
44
+ # Public cache helpers
45
+ # Note:
46
+ # - We cache only public calls.
47
+ # - For authenticated/private access, we bypass cache so we don't
48
+ # mix results across users/tokens.
49
+ # ============================================================
50
  @lru_cache(maxsize=1000)
51
+ def cached_repo_info_public(repo_id: str, repo_type: str):
52
+ return HfApi().repo_info(repo_id=repo_id, repo_type=repo_type)
53
 
54
 
55
  @lru_cache(maxsize=1000)
56
+ def cached_list_commits_public(repo_id: str, repo_type: str):
57
+ return list(HfApi().list_repo_commits(repo_id=repo_id, repo_type=repo_type))
58
 
59
 
60
+ @lru_cache(maxsize=200)
61
+ def cached_list_items_public(username: str, kind: str):
62
+ api = HfApi()
63
  if kind == "model":
64
  return list(api.list_models(author=username))
65
+ if kind == "dataset":
66
  return list(api.list_datasets(author=username))
67
+ if kind == "space":
68
  return list(api.list_spaces(author=username))
69
  return []
70
 
71
 
72
+ def list_items(username: str, kind: str, token: str | None = None):
73
+ if token:
74
+ api = get_api(token)
75
+ if kind == "model":
76
+ return list(api.list_models(author=username))
77
+ if kind == "dataset":
78
+ return list(api.list_datasets(author=username))
79
+ if kind == "space":
80
+ return list(api.list_spaces(author=username))
81
+ return []
82
+ return cached_list_items_public(username, kind)
83
+
84
+
85
+ def repo_info(repo_id: str, repo_type: str, token: str | None = None):
86
+ if token:
87
+ return get_api(token).repo_info(repo_id=repo_id, repo_type=repo_type)
88
+ return cached_repo_info_public(repo_id, repo_type)
89
+
90
+
91
+ def list_commits(repo_id: str, repo_type: str, token: str | None = None):
92
+ if token:
93
+ return list(get_api(token).list_repo_commits(repo_id=repo_id, repo_type=repo_type))
94
+ return cached_list_commits_public(repo_id, repo_type)
95
+
96
+
97
+ # ============================================================
98
+ # Data fetch
99
+ # ============================================================
100
+ def fetch_commits_for_repo(
101
+ repo_id: str,
102
+ repo_type: str,
103
+ selected_year: int,
104
+ token: str | None = None,
105
+ ):
106
  try:
107
  rate_limiter.wait()
108
 
109
+ info = repo_info(repo_id, repo_type, token=token)
 
 
 
 
110
 
111
  commit_dates = []
112
  commit_count = 0
113
 
114
+ created_at = getattr(info, "created_at", None)
115
+ if created_at is not None:
116
+ created = pd.to_datetime(created_at).tz_localize(None).date()
117
+ if created.year == selected_year:
118
+ commit_dates.append(created)
119
  commit_count += 1
120
 
121
+ commits = list_commits(repo_id, repo_type, token=token)
 
122
  for commit in commits:
123
+ commit_created_at = getattr(commit, "created_at", None)
124
+ if commit_created_at is None:
125
  continue
126
+
127
+ dt = pd.to_datetime(commit_created_at).tz_localize(None).date()
128
+ if dt.year == selected_year:
129
+ commit_dates.append(dt)
130
  commit_count += 1
131
 
132
+ return commit_dates, commit_count, None
133
 
134
+ except Exception as e:
135
+ return [], 0, f"{repo_type}:{repo_id} -> {str(e)}"
136
 
137
 
138
+ def collect_kind_data(
139
+ username: str,
140
+ kind: str,
141
+ selected_year: int,
142
+ token: str | None = None,
143
+ ):
144
  try:
145
+ items = list_items(username, kind, token=token)
146
  repo_ids = [item.id for item in items]
147
 
148
  chunk_size = 5
149
  total_commits = 0
150
  all_commit_dates = []
151
+ errors = []
152
 
153
  for i in range(0, len(repo_ids), chunk_size):
154
  chunk = repo_ids[i:i + chunk_size]
155
+ if not chunk:
156
+ continue
157
 
158
  with ThreadPoolExecutor(max_workers=min(5, len(chunk))) as executor:
159
+ futures = {
160
+ executor.submit(
161
+ fetch_commits_for_repo,
162
+ repo_id,
163
+ kind,
164
+ selected_year,
165
+ token,
166
+ ): repo_id
167
  for repo_id in chunk
168
  }
169
 
170
+ for future in as_completed(futures):
171
+ repo_commits, repo_count, err = future.result()
172
  if repo_commits:
173
  all_commit_dates.extend(repo_commits)
174
  total_commits += repo_count
175
+ if err:
176
+ errors.append(err)
177
 
178
  return {
179
  "total_items": len(items),
180
  "commit_dates": all_commit_dates,
181
  "commit_count": total_commits,
182
+ "errors": errors,
183
  }
184
 
185
  except Exception as e:
 
187
  "total_items": 0,
188
  "commit_dates": [],
189
  "commit_count": 0,
190
+ "errors": [f"Error fetching {kind}s for {username}: {str(e)}"],
191
  }
192
 
193
 
194
+ # ============================================================
195
  # Plot helpers
196
+ # ============================================================
197
+ def make_calendar_heatmap(df: pd.DataFrame, title: str, year: int, wide: bool = False):
198
+ figsize = (18, 3.0) if wide else (10, 3.4)
199
+ dpi = 180
200
+
201
+ fig, ax = plt.subplots(figsize=figsize, dpi=dpi)
202
 
203
  if df.empty:
204
+ ax.text(
205
+ 0.5,
206
+ 0.5,
207
+ f"No {title.lower()} found for {year}.",
208
+ ha="center",
209
+ va="center",
210
+ fontsize=12,
211
+ )
212
  ax.axis("off")
213
  fig.tight_layout()
214
  return fig
 
225
  heatmap_data = pd.DataFrame({"date": all_days, "count": 0})
226
  heatmap_data = heatmap_data.merge(df, on="date", how="left", suffixes=("", "_y"))
227
  heatmap_data["count"] = heatmap_data["count_y"].fillna(0)
228
+ heatmap_data = heatmap_data.drop(columns=["count_y"])
229
 
230
  heatmap_data["dow"] = heatmap_data["date"].dt.dayofweek
231
  heatmap_data["week"] = (heatmap_data["date"] - start).dt.days // 7
 
237
  month_positions = month_starts.map(lambda x: (x - start).days // 7)
238
 
239
  from matplotlib.colors import ListedColormap, BoundaryNorm
240
+
241
+ colors = ["#ebedf0", "#9be9a8", "#40c463", "#30a14e", "#216e39"]
242
+ bounds = [0, 1, 3, 11, 31, float("inf")]
243
  cmap = ListedColormap(colors)
244
  norm = BoundaryNorm(bounds, cmap.N)
245
 
 
248
  ax=ax,
249
  cmap=cmap,
250
  norm=norm,
251
+ linewidths=0.6,
252
  linecolor="white",
253
  square=True,
254
  cbar=False,
255
  yticklabels=["M", "T", "W", "T", "F", "S", "S"],
256
  )
257
 
258
+ ax.set_title(title, fontsize=14, pad=12)
259
  ax.set_xlabel("")
260
  ax.set_ylabel("")
261
+ ax.set_xticks(month_positions + 0.5)
262
+ ax.set_xticklabels(month_labels, fontsize=10)
263
+ ax.set_yticklabels(ax.get_yticklabels(), rotation=0, fontsize=10)
264
 
265
  fig.tight_layout()
266
  return fig
267
 
268
 
269
+ def metric_card(label: str, value):
270
+ return f"""
271
+ <div style="
272
+ padding: 14px 16px;
273
+ border: 1px solid #e5e7eb;
274
+ border-radius: 14px;
275
+ background: white;
276
+ ">
277
+ <div style="font-size: 14px; opacity: 0.75;">{label}</div>
278
+ <div style="font-size: 30px; font-weight: 700; margin-top: 4px;">{value}</div>
279
+ </div>
280
+ """
281
 
282
 
283
+ # ============================================================
284
  # Main callback
285
+ # Special Gradio injected params:
286
+ # - profile: gr.OAuthProfile | None
287
+ # - oauth_token: gr.OAuthToken | None
288
+ # ============================================================
289
+ def run_app(
290
+ selected_username,
291
+ custom_username,
292
+ selected_year,
293
+ profile: gr.OAuthProfile | None,
294
+ oauth_token: gr.OAuthToken | None,
295
+ ):
296
  username = (custom_username or "").strip() or selected_username
297
+ selected_year = int(selected_year)
298
 
299
  if not username:
300
+ empty_all = make_calendar_heatmap(pd.DataFrame(), "All Commits", selected_year, wide=True)
301
+ empty_kind = make_calendar_heatmap(pd.DataFrame(), "Commits", selected_year, wide=False)
302
  return (
303
  "## Please provide a username or org name.",
304
+ metric_card("Total Commits", 0),
305
+ empty_all,
306
+ metric_card("🧠 Models", 0),
307
+ metric_card(f"Commits in {selected_year}", 0),
308
+ empty_kind,
309
+ metric_card("πŸ“¦ Datasets", 0),
310
+ metric_card(f"Commits in {selected_year}", 0),
311
+ empty_kind,
312
+ metric_card("πŸš€ Spaces", 0),
313
+ metric_card(f"Commits in {selected_year}", 0),
314
+ empty_kind,
315
+ "No username provided.",
316
  )
317
 
318
+ token = oauth_token.token if oauth_token is not None else None
319
+
320
  commits_by_type = {}
321
  commit_counts_by_type = {}
322
  totals_by_type = {}
323
  warnings = []
324
 
325
  for kind in ["model", "dataset", "space"]:
326
+ result = collect_kind_data(username, kind, selected_year, token=token)
327
  commits_by_type[kind] = result["commit_dates"]
328
  commit_counts_by_type[kind] = result["commit_count"]
329
  totals_by_type[kind] = result["total_items"]
330
+ warnings.extend(result["errors"])
 
331
 
332
  total_commits = sum(commit_counts_by_type.values())
333
 
 
340
  all_df = all_df.drop_duplicates()
341
 
342
  model_df = pd.DataFrame(commits_by_type["model"], columns=["date"])
343
+ dataset_df = pd.DataFrame(commits_by_type["dataset"], columns=["date"])
344
+ space_df = pd.DataFrame(commits_by_type["space"], columns=["date"])
345
+
346
  if not model_df.empty:
347
  model_df = model_df.drop_duplicates()
 
 
348
  if not dataset_df.empty:
349
  dataset_df = dataset_df.drop_duplicates()
 
 
350
  if not space_df.empty:
351
  space_df = space_df.drop_duplicates()
352
 
353
+ auth_status = (
354
+ f"Logged in as **{profile.username}**. Private repos accessible if your token has access."
355
+ if profile is not None
356
+ else "Not logged in. Public repos only."
357
+ )
358
 
359
  return (
360
+ f"## {username}'s Activity in {selected_year}\n\n{auth_status}",
361
+ metric_card("Total Commits", total_commits),
362
+ make_calendar_heatmap(all_df, "All Commits", selected_year, wide=True),
363
 
364
+ metric_card("🧠 Models", totals_by_type["model"]),
365
+ metric_card(f"Commits in {selected_year}", commit_counts_by_type["model"]),
366
+ make_calendar_heatmap(model_df, "Models Commits", selected_year, wide=False),
367
 
368
+ metric_card("πŸ“¦ Datasets", totals_by_type["dataset"]),
369
+ metric_card(f"Commits in {selected_year}", commit_counts_by_type["dataset"]),
370
+ make_calendar_heatmap(dataset_df, "Datasets Commits", selected_year, wide=False),
371
 
372
+ metric_card("πŸš€ Spaces", totals_by_type["space"]),
373
+ metric_card(f"Commits in {selected_year}", commit_counts_by_type["space"]),
374
+ make_calendar_heatmap(space_df, "Spaces Commits", selected_year, wide=False),
375
 
376
+ "\n".join(warnings) if warnings else "Done.",
377
  )
378
 
379
 
380
+ # ============================================================
381
+ # UI
382
+ # ============================================================
383
  year_options = list(range(datetime.now().year, 2017, -1))
384
+ default_users = [
385
+ "ritvik77",
386
+ "facebook",
387
+ "google",
388
+ "stabilityai",
389
+ "Salesforce",
390
+ "tiiuae",
391
+ "bigscience",
392
+ ]
393
+
394
+ with gr.Blocks(title="HF Contributions", fill_width=True) as demo:
395
  gr.Markdown("# πŸ€— Hugging Face Contributions")
396
 
397
  with gr.Sidebar():
398
+ gr.LoginButton("Sign in with Hugging Face")
399
+
400
  gr.Markdown("## πŸ‘€ Contributor")
401
  selected_username = gr.Dropdown(
402
  choices=default_users,
403
  value="ritvik77",
404
+ label="Select a username/org",
405
  )
 
406
  custom_username = gr.Textbox(
407
  label="Custom username/org",
408
+ placeholder="Enter custom username/org",
409
  )
410
  selected_year = gr.Dropdown(
411
  choices=year_options,
412
  value=year_options[0],
413
+ label="πŸ—“οΈ Year",
414
  )
415
  run_button = gr.Button("Fetch commit data", variant="primary")
416
 
417
  status_header = gr.Markdown("## Ready")
418
+ total_commits_html = gr.HTML(metric_card("Total Commits", 0))
419
+ all_plot = gr.Plot(format="png")
420
+
421
+ with gr.Row(equal_height=False):
422
+ with gr.Column(scale=1, min_width=420):
423
+ models_total_html = gr.HTML(metric_card("🧠 Models", 0))
424
+ models_commits_html = gr.HTML(metric_card(f"Commits in {year_options[0]}", 0))
425
+ models_plot = gr.Plot(format="png")
426
+
427
+ with gr.Column(scale=1, min_width=420):
428
+ datasets_total_html = gr.HTML(metric_card("πŸ“¦ Datasets", 0))
429
+ datasets_commits_html = gr.HTML(metric_card(f"Commits in {year_options[0]}", 0))
430
+ datasets_plot = gr.Plot(format="png")
431
+
432
+ with gr.Column(scale=1, min_width=420):
433
+ spaces_total_html = gr.HTML(metric_card("πŸš€ Spaces", 0))
434
+ spaces_commits_html = gr.HTML(metric_card(f"Commits in {year_options[0]}", 0))
435
+ spaces_plot = gr.Plot(format="png")
436
+
437
+ logs_box = gr.Textbox(label="Logs / Warnings", interactive=False, lines=8)
438
+
439
+ outputs = [
440
+ status_header,
441
+ total_commits_html,
442
+ all_plot,
443
+ models_total_html,
444
+ models_commits_html,
445
+ models_plot,
446
+ datasets_total_html,
447
+ datasets_commits_html,
448
+ datasets_plot,
449
+ spaces_total_html,
450
+ spaces_commits_html,
451
+ spaces_plot,
452
+ logs_box,
453
+ ]
454
 
455
  run_button.click(
456
  fn=run_app,
457
  inputs=[selected_username, custom_username, selected_year],
458
+ outputs=outputs,
459
+ )
460
+
461
+ demo.load(
462
+ fn=run_app,
463
+ inputs=[selected_username, custom_username, selected_year],
464
+ outputs=outputs,
 
 
 
 
 
 
 
 
465
  )
466
 
467
  demo.queue().launch()