fffiloni commited on
Commit
d810a2e
Β·
verified Β·
1 Parent(s): d545807

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +362 -0
app.py ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from datetime import datetime
3
+ from functools import lru_cache
4
+ from concurrent.futures import ThreadPoolExecutor, as_completed
5
+
6
+ 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:
132
+ return {
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
152
+
153
+ df = df.copy()
154
+ df["count"] = 1
155
+ df = df.groupby("date", as_index=False).sum()
156
+ df["date"] = pd.to_datetime(df["date"])
157
+
158
+ start = pd.Timestamp(f"{year}-01-01")
159
+ end = pd.Timestamp(f"{year}-12-31")
160
+ all_days = pd.date_range(start=start, end=end)
161
+
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
169
+
170
+ pivot = heatmap_data.pivot(index="dow", columns="week", values="count").fillna(0)
171
+
172
+ month_starts = pd.date_range(start, end, freq="MS")
173
+ month_labels = month_starts.strftime("%b")
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
+
182
+ sns.heatmap(
183
+ pivot.astype(int),
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
+
248
+ all_commits = []
249
+ for commits in commits_by_type.values():
250
+ all_commits.extend(commits)
251
+
252
+ all_df = pd.DataFrame(all_commits, columns=["date"])
253
+ if not all_df.empty:
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()