Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -363,57 +363,165 @@ def empty_activity_bucket(title: str):
|
|
| 363 |
}
|
| 364 |
|
| 365 |
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
|
|
|
| 371 |
"username": "",
|
| 372 |
"selected_year": int(selected_year),
|
| 373 |
"fetched": False,
|
| 374 |
-
"
|
| 375 |
-
"pr_activity": empty_activity_bucket("PR Activity"),
|
| 376 |
-
"thread_opened": empty_activity_bucket("Community Threads"),
|
| 377 |
-
"discussion_activity": empty_activity_bucket("Community Activity"),
|
| 378 |
-
"logs": "Community activity not fetched yet.",
|
| 379 |
}
|
| 380 |
|
|
|
|
|
|
|
| 381 |
|
| 382 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
username: str,
|
| 384 |
selected_year: int,
|
| 385 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 386 |
):
|
| 387 |
username = (username or "").strip()
|
| 388 |
selected_year = int(selected_year)
|
| 389 |
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
"discussion_activity": "Community Activity",
|
| 395 |
-
}
|
| 396 |
-
title = title_map.get(mode, "Community")
|
| 397 |
|
| 398 |
if not username:
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
"logs": "No username provided.",
|
| 405 |
-
}
|
| 406 |
|
| 407 |
-
|
| 408 |
-
|
|
|
|
|
|
|
| 409 |
skip = 0
|
| 410 |
limit = 20
|
| 411 |
-
cursor = None
|
| 412 |
pages_fetched = 0
|
| 413 |
|
| 414 |
MAX_PAGES = 40
|
| 415 |
-
MAX_DURATION =
|
| 416 |
-
OPEN_EVENT_EPSILON_SECONDS = 90
|
| 417 |
start = time.time()
|
| 418 |
|
| 419 |
logs = []
|
|
@@ -427,94 +535,6 @@ def fetch_user_discussion_activity(
|
|
| 427 |
}
|
| 428 |
)
|
| 429 |
|
| 430 |
-
def normalize_payload(payload):
|
| 431 |
-
if isinstance(payload, list):
|
| 432 |
-
return payload, None
|
| 433 |
-
|
| 434 |
-
if isinstance(payload, dict):
|
| 435 |
-
events = payload.get("recentActivity")
|
| 436 |
-
if isinstance(events, list):
|
| 437 |
-
return events, payload.get("cursor")
|
| 438 |
-
|
| 439 |
-
for key in ["items", "events", "activity"]:
|
| 440 |
-
value = payload.get(key)
|
| 441 |
-
if isinstance(value, list):
|
| 442 |
-
return value, payload.get("cursor")
|
| 443 |
-
|
| 444 |
-
return [], None
|
| 445 |
-
|
| 446 |
-
def extract_author_name(discussion: dict) -> str | None:
|
| 447 |
-
author = discussion.get("author")
|
| 448 |
-
if isinstance(author, str):
|
| 449 |
-
return author
|
| 450 |
-
if isinstance(author, dict):
|
| 451 |
-
for key in ["name", "username", "login"]:
|
| 452 |
-
value = author.get(key)
|
| 453 |
-
if isinstance(value, str):
|
| 454 |
-
return value
|
| 455 |
-
return None
|
| 456 |
-
|
| 457 |
-
def parse_ts(raw_value):
|
| 458 |
-
if not raw_value:
|
| 459 |
-
return None
|
| 460 |
-
try:
|
| 461 |
-
return pd.to_datetime(raw_value, utc=True)
|
| 462 |
-
except Exception:
|
| 463 |
-
return None
|
| 464 |
-
|
| 465 |
-
def event_matches_mode(event: dict, discussion: dict) -> tuple[bool, str | None]:
|
| 466 |
-
is_pr = bool(discussion.get("isPullRequest", False))
|
| 467 |
-
author_name = extract_author_name(discussion)
|
| 468 |
-
repo_id = event.get("repoId", "unknown-repo")
|
| 469 |
-
discussion_num = discussion.get("num", "unknown-num")
|
| 470 |
-
event_id = event.get("eventId", "unknown-event")
|
| 471 |
-
|
| 472 |
-
if mode == "pr_opened":
|
| 473 |
-
if not is_pr:
|
| 474 |
-
return False, None
|
| 475 |
-
if not author_name or author_name.lower() != username.lower():
|
| 476 |
-
return False, None
|
| 477 |
-
return True, f"{repo_id}::pr::{discussion_num}"
|
| 478 |
-
|
| 479 |
-
if mode == "pr_activity":
|
| 480 |
-
if not is_pr:
|
| 481 |
-
return False, None
|
| 482 |
-
return True, f"event::{event_id}"
|
| 483 |
-
|
| 484 |
-
if mode == "thread_opened":
|
| 485 |
-
if is_pr:
|
| 486 |
-
return False, None
|
| 487 |
-
if not author_name or author_name.lower() != username.lower():
|
| 488 |
-
return False, None
|
| 489 |
-
return True, f"{repo_id}::thread::{discussion_num}"
|
| 490 |
-
|
| 491 |
-
if mode == "discussion_activity":
|
| 492 |
-
if is_pr:
|
| 493 |
-
return False, None
|
| 494 |
-
return True, f"event::{event_id}"
|
| 495 |
-
|
| 496 |
-
return False, None
|
| 497 |
-
|
| 498 |
-
def extract_date_for_mode(event: dict, discussion: dict):
|
| 499 |
-
event_ts = parse_ts(event.get("time"))
|
| 500 |
-
created_ts = parse_ts(discussion.get("createdAt"))
|
| 501 |
-
|
| 502 |
-
if mode in {"pr_opened", "thread_opened"}:
|
| 503 |
-
if created_ts is None:
|
| 504 |
-
return None
|
| 505 |
-
return created_ts.tz_localize(None).date()
|
| 506 |
-
|
| 507 |
-
if mode in {"pr_activity", "discussion_activity"}:
|
| 508 |
-
if event_ts is None:
|
| 509 |
-
return None
|
| 510 |
-
if created_ts is not None:
|
| 511 |
-
delta_seconds = abs((event_ts - created_ts).total_seconds())
|
| 512 |
-
if delta_seconds <= OPEN_EVENT_EPSILON_SECONDS:
|
| 513 |
-
return None
|
| 514 |
-
return event_ts.tz_localize(None).date()
|
| 515 |
-
|
| 516 |
-
return None
|
| 517 |
-
|
| 518 |
try:
|
| 519 |
while True:
|
| 520 |
if time.time() - start > MAX_DURATION:
|
|
@@ -529,7 +549,7 @@ def fetch_user_discussion_activity(
|
|
| 529 |
|
| 530 |
params = {
|
| 531 |
"limit": limit,
|
| 532 |
-
"activityType":
|
| 533 |
"feedType": "user",
|
| 534 |
"entity": username,
|
| 535 |
}
|
|
@@ -548,7 +568,8 @@ def fetch_user_discussion_activity(
|
|
| 548 |
payload = response.json()
|
| 549 |
pages_fetched += 1
|
| 550 |
|
| 551 |
-
events
|
|
|
|
| 552 |
|
| 553 |
if not events:
|
| 554 |
logs.append("No more events.")
|
|
@@ -560,28 +581,35 @@ def fetch_user_discussion_activity(
|
|
| 560 |
if not isinstance(event, dict):
|
| 561 |
continue
|
| 562 |
|
| 563 |
-
|
| 564 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 565 |
stop_early = True
|
| 566 |
break
|
| 567 |
|
| 568 |
-
|
| 569 |
-
if not isinstance(discussion, dict):
|
| 570 |
continue
|
| 571 |
|
| 572 |
-
|
| 573 |
-
|
|
|
|
| 574 |
continue
|
| 575 |
|
| 576 |
-
|
| 577 |
-
if dt is None or dt.year != selected_year:
|
| 578 |
continue
|
| 579 |
|
| 580 |
-
if unique_id in
|
| 581 |
continue
|
| 582 |
|
| 583 |
-
|
| 584 |
-
|
| 585 |
|
| 586 |
if stop_early:
|
| 587 |
logs.append("Stopped early after reaching feed events older than selected year.")
|
|
@@ -592,251 +620,11 @@ def fetch_user_discussion_activity(
|
|
| 592 |
else:
|
| 593 |
skip += limit
|
| 594 |
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
return {
|
| 599 |
-
"title": title,
|
| 600 |
"fetched": True,
|
| 601 |
-
"
|
| 602 |
-
"count": len(dates),
|
| 603 |
-
"logs": "\n".join(logs),
|
| 604 |
-
}
|
| 605 |
-
|
| 606 |
-
except Exception as e:
|
| 607 |
-
return {
|
| 608 |
-
"title": title,
|
| 609 |
-
"fetched": True,
|
| 610 |
-
"dates": [],
|
| 611 |
-
"count": 0,
|
| 612 |
-
"logs": f"{title} fetch failed: {str(e)}",
|
| 613 |
-
}
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
def fetch_all_community_activity(username: str, selected_year: int):
|
| 617 |
-
username = (username or "").strip()
|
| 618 |
-
selected_year = int(selected_year)
|
| 619 |
-
|
| 620 |
-
cache_key = (username.lower(), selected_year, "community")
|
| 621 |
-
cached = get_cache(cache_key)
|
| 622 |
-
if cached:
|
| 623 |
-
return cached
|
| 624 |
-
|
| 625 |
-
if not username:
|
| 626 |
-
state = empty_community_state(selected_year)
|
| 627 |
-
state["fetched"] = True
|
| 628 |
-
state["logs"] = "No username provided."
|
| 629 |
-
set_cache(cache_key, state)
|
| 630 |
-
return state
|
| 631 |
-
|
| 632 |
-
pr_opened = fetch_user_discussion_activity(username, selected_year, "pr_opened")
|
| 633 |
-
pr_activity = fetch_user_discussion_activity(username, selected_year, "pr_activity")
|
| 634 |
-
thread_opened = fetch_user_discussion_activity(username, selected_year, "thread_opened")
|
| 635 |
-
discussion_activity = fetch_user_discussion_activity(username, selected_year, "discussion_activity")
|
| 636 |
-
|
| 637 |
-
combined_logs = "\n\n".join(
|
| 638 |
-
[
|
| 639 |
-
"[Pull Requests]\n" + pr_opened["logs"],
|
| 640 |
-
"[PR Activity]\n" + pr_activity["logs"],
|
| 641 |
-
"[Community Threads]\n" + thread_opened["logs"],
|
| 642 |
-
"[Community Activity]\n" + discussion_activity["logs"],
|
| 643 |
-
]
|
| 644 |
-
)
|
| 645 |
-
|
| 646 |
-
result = {
|
| 647 |
-
"username": username,
|
| 648 |
-
"selected_year": selected_year,
|
| 649 |
-
"fetched": True,
|
| 650 |
-
"pr_opened": pr_opened,
|
| 651 |
-
"pr_activity": pr_activity,
|
| 652 |
-
"thread_opened": thread_opened,
|
| 653 |
-
"discussion_activity": discussion_activity,
|
| 654 |
-
"logs": combined_logs,
|
| 655 |
-
}
|
| 656 |
-
|
| 657 |
-
set_cache(cache_key, result)
|
| 658 |
-
return result
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
# ============================================================
|
| 662 |
-
# Generic bucketed recent-activity fetch helpers
|
| 663 |
-
# ============================================================
|
| 664 |
-
def empty_bucketed_activity_state(
|
| 665 |
-
selected_year: int,
|
| 666 |
-
logs_text: str,
|
| 667 |
-
bucket_titles: dict[str, str],
|
| 668 |
-
):
|
| 669 |
-
state = {
|
| 670 |
-
"username": "",
|
| 671 |
-
"selected_year": int(selected_year),
|
| 672 |
-
"fetched": False,
|
| 673 |
-
"logs": logs_text,
|
| 674 |
-
}
|
| 675 |
-
|
| 676 |
-
for key, title in bucket_titles.items():
|
| 677 |
-
state[key] = empty_activity_bucket(title)
|
| 678 |
-
|
| 679 |
-
return state
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
def build_fetched_bucket(title: str, dates: list, selected_year: int, label: str):
|
| 683 |
-
return {
|
| 684 |
-
"title": title,
|
| 685 |
-
"fetched": True,
|
| 686 |
-
"dates": dates,
|
| 687 |
-
"count": len(dates),
|
| 688 |
-
"logs": f"Found {len(dates)} {label} in {selected_year}.",
|
| 689 |
-
}
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
def build_failed_bucket(title: str):
|
| 693 |
-
return {
|
| 694 |
-
"title": title,
|
| 695 |
-
"fetched": True,
|
| 696 |
-
"dates": [],
|
| 697 |
-
"count": 0,
|
| 698 |
-
"logs": "",
|
| 699 |
-
}
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
def fetch_bucketed_recent_activity(
|
| 703 |
-
username: str,
|
| 704 |
-
selected_year: int,
|
| 705 |
-
cache_scope: str,
|
| 706 |
-
activity_type: str,
|
| 707 |
-
bucket_titles: dict[str, str],
|
| 708 |
-
bucket_log_labels: dict[str, str],
|
| 709 |
-
empty_logs_text: str,
|
| 710 |
-
classifier,
|
| 711 |
-
):
|
| 712 |
-
username = (username or "").strip()
|
| 713 |
-
selected_year = int(selected_year)
|
| 714 |
-
|
| 715 |
-
cache_key = (username.lower(), selected_year, cache_scope)
|
| 716 |
-
cached = get_cache(cache_key)
|
| 717 |
-
if cached:
|
| 718 |
-
return cached
|
| 719 |
-
|
| 720 |
-
if not username:
|
| 721 |
-
state = empty_bucketed_activity_state(selected_year, empty_logs_text, bucket_titles)
|
| 722 |
-
state["fetched"] = True
|
| 723 |
-
state["logs"] = "No username provided."
|
| 724 |
-
set_cache(cache_key, state)
|
| 725 |
-
return state
|
| 726 |
-
|
| 727 |
-
bucket_dates = {key: [] for key in bucket_titles}
|
| 728 |
-
bucket_seen_ids = {key: set() for key in bucket_titles}
|
| 729 |
-
|
| 730 |
-
cursor = None
|
| 731 |
-
skip = 0
|
| 732 |
-
limit = 20
|
| 733 |
-
pages_fetched = 0
|
| 734 |
-
|
| 735 |
-
MAX_PAGES = 40
|
| 736 |
-
MAX_DURATION = 20
|
| 737 |
-
start = time.time()
|
| 738 |
-
|
| 739 |
-
logs = []
|
| 740 |
-
|
| 741 |
-
session = requests.Session()
|
| 742 |
-
session.headers.update(
|
| 743 |
-
{
|
| 744 |
-
"User-Agent": "hf-contributions-app/1.0",
|
| 745 |
-
"Accept": "application/json",
|
| 746 |
-
"Cache-Control": "no-cache",
|
| 747 |
-
}
|
| 748 |
-
)
|
| 749 |
-
|
| 750 |
-
try:
|
| 751 |
-
while True:
|
| 752 |
-
if time.time() - start > MAX_DURATION:
|
| 753 |
-
logs.append("Stopped due to timeout.")
|
| 754 |
-
break
|
| 755 |
-
|
| 756 |
-
if pages_fetched >= MAX_PAGES:
|
| 757 |
-
logs.append("Stopped due to max pages.")
|
| 758 |
-
break
|
| 759 |
-
|
| 760 |
-
recent_activity_rate_limiter.wait()
|
| 761 |
-
|
| 762 |
-
params = {
|
| 763 |
-
"limit": limit,
|
| 764 |
-
"activityType": activity_type,
|
| 765 |
-
"feedType": "user",
|
| 766 |
-
"entity": username,
|
| 767 |
-
}
|
| 768 |
-
if cursor:
|
| 769 |
-
params["cursor"] = cursor
|
| 770 |
-
else:
|
| 771 |
-
params["skip"] = skip
|
| 772 |
-
|
| 773 |
-
response = session.get(
|
| 774 |
-
"https://huggingface.co/api/recent-activity",
|
| 775 |
-
params=params,
|
| 776 |
-
timeout=20,
|
| 777 |
-
)
|
| 778 |
-
response.raise_for_status()
|
| 779 |
-
|
| 780 |
-
payload = response.json()
|
| 781 |
-
pages_fetched += 1
|
| 782 |
-
|
| 783 |
-
events = payload.get("recentActivity", [])
|
| 784 |
-
next_cursor = payload.get("cursor")
|
| 785 |
-
|
| 786 |
-
if not events:
|
| 787 |
-
logs.append("No more events.")
|
| 788 |
-
break
|
| 789 |
-
|
| 790 |
-
stop_early = False
|
| 791 |
-
|
| 792 |
-
for event in events:
|
| 793 |
-
if not isinstance(event, dict):
|
| 794 |
-
continue
|
| 795 |
-
|
| 796 |
-
raw_time = event.get("time")
|
| 797 |
-
if not raw_time:
|
| 798 |
-
continue
|
| 799 |
-
|
| 800 |
-
try:
|
| 801 |
-
event_ts = pd.to_datetime(raw_time, utc=True)
|
| 802 |
-
except Exception:
|
| 803 |
-
continue
|
| 804 |
-
|
| 805 |
-
if event_ts.year < selected_year:
|
| 806 |
-
stop_early = True
|
| 807 |
-
break
|
| 808 |
-
|
| 809 |
-
if event_ts.year != selected_year:
|
| 810 |
-
continue
|
| 811 |
-
|
| 812 |
-
bucket_key, unique_id = classifier(event)
|
| 813 |
-
|
| 814 |
-
if not bucket_key or not unique_id:
|
| 815 |
-
continue
|
| 816 |
-
|
| 817 |
-
if bucket_key not in bucket_dates:
|
| 818 |
-
continue
|
| 819 |
-
|
| 820 |
-
if unique_id in bucket_seen_ids[bucket_key]:
|
| 821 |
-
continue
|
| 822 |
-
|
| 823 |
-
bucket_seen_ids[bucket_key].add(unique_id)
|
| 824 |
-
bucket_dates[bucket_key].append(event_ts.tz_localize(None).date())
|
| 825 |
-
|
| 826 |
-
if stop_early:
|
| 827 |
-
logs.append("Stopped early after reaching feed events older than selected year.")
|
| 828 |
-
break
|
| 829 |
-
|
| 830 |
-
if next_cursor:
|
| 831 |
-
cursor = next_cursor
|
| 832 |
-
else:
|
| 833 |
-
skip += limit
|
| 834 |
-
|
| 835 |
-
result = {
|
| 836 |
-
"username": username,
|
| 837 |
-
"selected_year": selected_year,
|
| 838 |
-
"fetched": True,
|
| 839 |
-
"logs": "",
|
| 840 |
}
|
| 841 |
|
| 842 |
for key, title in bucket_titles.items():
|
|
@@ -936,173 +724,339 @@ def classify_paper_event(event: dict):
|
|
| 936 |
return bucket_key, unique_id
|
| 937 |
|
| 938 |
|
| 939 |
-
|
| 940 |
-
|
| 941 |
-
|
| 942 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 943 |
return empty_bucketed_activity_state(
|
| 944 |
selected_year=selected_year,
|
| 945 |
-
logs_text="
|
| 946 |
-
bucket_titles=
|
| 947 |
-
"papers": "Paper Upvotes",
|
| 948 |
-
"blog": "Blog Upvotes",
|
| 949 |
-
"changelog": "Changelog Upvotes",
|
| 950 |
-
},
|
| 951 |
)
|
| 952 |
|
| 953 |
|
| 954 |
-
def
|
|
|
|
|
|
|
| 955 |
return fetch_bucketed_recent_activity(
|
| 956 |
username=username,
|
| 957 |
selected_year=selected_year,
|
| 958 |
-
cache_scope=
|
| 959 |
-
activity_type="
|
| 960 |
-
bucket_titles=
|
| 961 |
-
|
| 962 |
-
|
| 963 |
-
|
| 964 |
-
},
|
| 965 |
-
bucket_log_labels={
|
| 966 |
-
"papers": "paper upvote(s)",
|
| 967 |
-
"blog": "blog upvote(s)",
|
| 968 |
-
"changelog": "changelog upvote(s)",
|
| 969 |
-
},
|
| 970 |
-
empty_logs_text="Upvotes activity not fetched yet.",
|
| 971 |
-
classifier=classify_upvote_event,
|
| 972 |
)
|
| 973 |
|
| 974 |
|
| 975 |
# ============================================================
|
| 976 |
-
#
|
| 977 |
# ============================================================
|
| 978 |
-
def
|
| 979 |
-
return
|
| 980 |
-
|
| 981 |
-
|
| 982 |
-
|
| 983 |
-
|
| 984 |
-
|
| 985 |
-
|
| 986 |
-
|
| 987 |
-
|
|
|
|
| 988 |
|
| 989 |
|
| 990 |
-
def
|
| 991 |
-
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
"models": "Model Likes",
|
| 998 |
-
"datasets": "Dataset Likes",
|
| 999 |
-
"spaces": "Space Likes",
|
| 1000 |
-
},
|
| 1001 |
-
bucket_log_labels={
|
| 1002 |
-
"models": "model like(s)",
|
| 1003 |
-
"datasets": "dataset like(s)",
|
| 1004 |
-
"spaces": "space like(s)",
|
| 1005 |
-
},
|
| 1006 |
-
empty_logs_text="Likes activity not fetched yet.",
|
| 1007 |
-
classifier=classify_like_event,
|
| 1008 |
-
)
|
| 1009 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1010 |
|
| 1011 |
-
|
| 1012 |
-
|
| 1013 |
-
|
| 1014 |
-
|
| 1015 |
-
|
| 1016 |
-
|
| 1017 |
-
|
| 1018 |
-
|
| 1019 |
-
|
| 1020 |
-
|
| 1021 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1022 |
)
|
| 1023 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1024 |
|
| 1025 |
-
|
| 1026 |
-
|
| 1027 |
-
username=username,
|
| 1028 |
-
selected_year=selected_year,
|
| 1029 |
-
cache_scope="papers",
|
| 1030 |
-
activity_type="update-paper",
|
| 1031 |
-
bucket_titles={
|
| 1032 |
-
"daily": "Daily Papers",
|
| 1033 |
-
"authored": "Authored Papers",
|
| 1034 |
-
},
|
| 1035 |
-
bucket_log_labels={
|
| 1036 |
-
"daily": "daily paper submission(s)",
|
| 1037 |
-
"authored": "authored paper(s)",
|
| 1038 |
-
},
|
| 1039 |
-
empty_logs_text="Papers activity not fetched yet.",
|
| 1040 |
-
classifier=classify_paper_event,
|
| 1041 |
-
)
|
| 1042 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1043 |
|
| 1044 |
-
|
| 1045 |
-
|
| 1046 |
-
|
| 1047 |
-
|
| 1048 |
-
|
| 1049 |
-
|
| 1050 |
-
|
| 1051 |
-
|
| 1052 |
-
|
| 1053 |
-
|
| 1054 |
-
|
| 1055 |
-
|
| 1056 |
-
|
| 1057 |
-
|
| 1058 |
-
|
| 1059 |
-
def level_from_count(count: int) -> int:
|
| 1060 |
-
if count <= 0:
|
| 1061 |
-
return 0
|
| 1062 |
-
if count == 1:
|
| 1063 |
-
return 1
|
| 1064 |
-
if count <= 3:
|
| 1065 |
-
return 2
|
| 1066 |
-
if count <= 7:
|
| 1067 |
-
return 3
|
| 1068 |
-
return 4
|
| 1069 |
-
|
| 1070 |
-
|
| 1071 |
-
def level_color(palette_name: str, level: int) -> str:
|
| 1072 |
-
if level <= 0:
|
| 1073 |
-
return "var(--gh-empty-cell)"
|
| 1074 |
-
palette = PALETTES[palette_name]
|
| 1075 |
-
return palette[min(level - 1, len(palette) - 1)]
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
-
def build_daily_count_map(df: pd.DataFrame):
|
| 1079 |
-
if df.empty:
|
| 1080 |
-
return {}
|
| 1081 |
-
|
| 1082 |
-
counts = (
|
| 1083 |
-
df.copy()
|
| 1084 |
-
.assign(date=pd.to_datetime(df["date"]).dt.date, count=1)
|
| 1085 |
-
.groupby("date", as_index=False)["count"]
|
| 1086 |
-
.sum()
|
| 1087 |
-
)
|
| 1088 |
-
return {row["date"]: int(row["count"]) for _, row in counts.iterrows()}
|
| 1089 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1090 |
|
| 1091 |
-
|
| 1092 |
-
|
| 1093 |
-
|
|
|
|
|
|
|
|
|
|
| 1094 |
|
| 1095 |
-
|
| 1096 |
-
|
|
|
|
|
|
|
| 1097 |
|
| 1098 |
-
|
| 1099 |
-
|
| 1100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1102 |
|
| 1103 |
-
|
| 1104 |
-
|
| 1105 |
-
return [(d.strftime("%b"), int((d - grid_start).days // 7)) for d in month_starts]
|
| 1106 |
|
| 1107 |
|
| 1108 |
# ============================================================
|
|
@@ -1365,189 +1319,13 @@ def render_community_loaded_banner(
|
|
| 1365 |
return render_status_badge(" · ".join(parts))
|
| 1366 |
|
| 1367 |
|
| 1368 |
-
def
|
| 1369 |
-
|
| 1370 |
-
selected_year,
|
| 1371 |
-
profile: gr.OAuthProfile | None,
|
| 1372 |
-
):
|
| 1373 |
-
username = (username or "").strip()
|
| 1374 |
-
selected_year = int(selected_year)
|
| 1375 |
-
viewer = getattr(profile, "username", None) if profile else None
|
| 1376 |
-
|
| 1377 |
-
parts = []
|
| 1378 |
-
if viewer:
|
| 1379 |
-
parts.append(f"Logged in as <b>{html.escape(viewer)}</b>")
|
| 1380 |
-
else:
|
| 1381 |
-
parts.append("Not logged in")
|
| 1382 |
-
|
| 1383 |
-
if not username:
|
| 1384 |
-
parts.append("Enter a username to fetch upvotes activity")
|
| 1385 |
-
return render_status_badge(" · ".join(parts))
|
| 1386 |
-
|
| 1387 |
-
if viewer and username.lower() == viewer.lower():
|
| 1388 |
-
parts.append("Fetch your upvotes activity or enter another username")
|
| 1389 |
-
parts.append(str(selected_year))
|
| 1390 |
-
else:
|
| 1391 |
-
parts.append(f"Ready to fetch upvotes activity for <b>{html.escape(username)}</b>")
|
| 1392 |
-
parts.append(str(selected_year))
|
| 1393 |
-
|
| 1394 |
-
return render_status_badge(" · ".join(parts))
|
| 1395 |
-
|
| 1396 |
-
|
| 1397 |
-
def render_upvotes_invalid_banner(username: str, error_text: str | None = None):
|
| 1398 |
-
username = (username or "").strip()
|
| 1399 |
-
message = error_text or f"Username or org '{username}' was not found."
|
| 1400 |
-
return render_status_badge(html.escape(message))
|
| 1401 |
-
|
| 1402 |
-
|
| 1403 |
-
def render_upvotes_fetching_banner(
|
| 1404 |
-
username,
|
| 1405 |
-
selected_year,
|
| 1406 |
-
profile: gr.OAuthProfile | None,
|
| 1407 |
-
):
|
| 1408 |
-
username = (username or "").strip()
|
| 1409 |
-
selected_year = int(selected_year)
|
| 1410 |
-
viewer = getattr(profile, "username", None) if profile else None
|
| 1411 |
-
|
| 1412 |
-
if not username:
|
| 1413 |
-
return render_upvotes_idle_banner("", selected_year, profile)
|
| 1414 |
-
|
| 1415 |
-
parts = []
|
| 1416 |
-
if viewer:
|
| 1417 |
-
parts.append(f"Logged in as <b>{html.escape(viewer)}</b>")
|
| 1418 |
-
else:
|
| 1419 |
-
parts.append("Not logged in")
|
| 1420 |
-
|
| 1421 |
-
if viewer and username.lower() == viewer.lower():
|
| 1422 |
-
parts.append("Fetching <b>your upvotes activity</b>")
|
| 1423 |
-
else:
|
| 1424 |
-
parts.append(f"Fetching upvotes activity for <b>{html.escape(username)}</b>")
|
| 1425 |
-
|
| 1426 |
-
parts.append(str(selected_year))
|
| 1427 |
-
return render_status_badge(" · ".join(parts))
|
| 1428 |
-
|
| 1429 |
-
|
| 1430 |
-
def render_upvotes_loaded_banner(
|
| 1431 |
-
upvotes_state: dict | None,
|
| 1432 |
-
profile: gr.OAuthProfile | None,
|
| 1433 |
-
):
|
| 1434 |
-
if not upvotes_state or not upvotes_state.get("username"):
|
| 1435 |
-
year = upvotes_state.get("selected_year", datetime.now().year) if upvotes_state else datetime.now().year
|
| 1436 |
-
return render_upvotes_idle_banner("", year, profile)
|
| 1437 |
-
|
| 1438 |
-
username = upvotes_state["username"]
|
| 1439 |
-
selected_year = int(upvotes_state["selected_year"])
|
| 1440 |
-
viewer = getattr(profile, "username", None) if profile else None
|
| 1441 |
-
|
| 1442 |
-
parts = []
|
| 1443 |
-
if viewer:
|
| 1444 |
-
parts.append(f"Logged in as <b>{html.escape(viewer)}</b>")
|
| 1445 |
-
else:
|
| 1446 |
-
parts.append("Not logged in")
|
| 1447 |
-
|
| 1448 |
-
if viewer and username.lower() == viewer.lower():
|
| 1449 |
-
parts.append("Viewing <b>your upvotes activity</b>")
|
| 1450 |
-
else:
|
| 1451 |
-
parts.append(f"Viewing upvotes activity for <b>{html.escape(username)}</b>")
|
| 1452 |
-
|
| 1453 |
-
parts.append(str(selected_year))
|
| 1454 |
-
return render_status_badge(" · ".join(parts))
|
| 1455 |
-
|
| 1456 |
-
|
| 1457 |
-
def render_likes_idle_banner(
|
| 1458 |
-
username,
|
| 1459 |
-
selected_year,
|
| 1460 |
-
profile: gr.OAuthProfile | None,
|
| 1461 |
-
):
|
| 1462 |
-
username = (username or "").strip()
|
| 1463 |
-
selected_year = int(selected_year)
|
| 1464 |
-
viewer = getattr(profile, "username", None) if profile else None
|
| 1465 |
-
|
| 1466 |
-
parts = []
|
| 1467 |
-
if viewer:
|
| 1468 |
-
parts.append(f"Logged in as <b>{html.escape(viewer)}</b>")
|
| 1469 |
-
else:
|
| 1470 |
-
parts.append("Not logged in")
|
| 1471 |
-
|
| 1472 |
-
if not username:
|
| 1473 |
-
parts.append("Enter a username to fetch likes activity")
|
| 1474 |
-
return render_status_badge(" · ".join(parts))
|
| 1475 |
-
|
| 1476 |
-
if viewer and username.lower() == viewer.lower():
|
| 1477 |
-
parts.append("Fetch your likes activity or enter another username")
|
| 1478 |
-
parts.append(str(selected_year))
|
| 1479 |
-
else:
|
| 1480 |
-
parts.append(f"Ready to fetch likes activity for <b>{html.escape(username)}</b>")
|
| 1481 |
-
parts.append(str(selected_year))
|
| 1482 |
-
|
| 1483 |
-
return render_status_badge(" · ".join(parts))
|
| 1484 |
-
|
| 1485 |
-
|
| 1486 |
-
def render_likes_invalid_banner(username: str, error_text: str | None = None):
|
| 1487 |
-
username = (username or "").strip()
|
| 1488 |
-
message = error_text or f"Username or org '{username}' was not found."
|
| 1489 |
-
return render_status_badge(html.escape(message))
|
| 1490 |
-
|
| 1491 |
-
|
| 1492 |
-
def render_likes_fetching_banner(
|
| 1493 |
-
username,
|
| 1494 |
-
selected_year,
|
| 1495 |
-
profile: gr.OAuthProfile | None,
|
| 1496 |
-
):
|
| 1497 |
-
username = (username or "").strip()
|
| 1498 |
-
selected_year = int(selected_year)
|
| 1499 |
-
viewer = getattr(profile, "username", None) if profile else None
|
| 1500 |
-
|
| 1501 |
-
if not username:
|
| 1502 |
-
return render_likes_idle_banner("", selected_year, profile)
|
| 1503 |
-
|
| 1504 |
-
parts = []
|
| 1505 |
-
if viewer:
|
| 1506 |
-
parts.append(f"Logged in as <b>{html.escape(viewer)}</b>")
|
| 1507 |
-
else:
|
| 1508 |
-
parts.append("Not logged in")
|
| 1509 |
-
|
| 1510 |
-
if viewer and username.lower() == viewer.lower():
|
| 1511 |
-
parts.append("Fetching <b>your likes activity</b>")
|
| 1512 |
-
else:
|
| 1513 |
-
parts.append(f"Fetching likes activity for <b>{html.escape(username)}</b>")
|
| 1514 |
-
|
| 1515 |
-
parts.append(str(selected_year))
|
| 1516 |
-
return render_status_badge(" · ".join(parts))
|
| 1517 |
-
|
| 1518 |
-
|
| 1519 |
-
def render_likes_loaded_banner(
|
| 1520 |
-
likes_state: dict | None,
|
| 1521 |
-
profile: gr.OAuthProfile | None,
|
| 1522 |
-
):
|
| 1523 |
-
if not likes_state or not likes_state.get("username"):
|
| 1524 |
-
year = likes_state.get("selected_year", datetime.now().year) if likes_state else datetime.now().year
|
| 1525 |
-
return render_likes_idle_banner("", year, profile)
|
| 1526 |
-
|
| 1527 |
-
username = likes_state["username"]
|
| 1528 |
-
selected_year = int(likes_state["selected_year"])
|
| 1529 |
-
viewer = getattr(profile, "username", None) if profile else None
|
| 1530 |
-
|
| 1531 |
-
parts = []
|
| 1532 |
-
if viewer:
|
| 1533 |
-
parts.append(f"Logged in as <b>{html.escape(viewer)}</b>")
|
| 1534 |
-
else:
|
| 1535 |
-
parts.append("Not logged in")
|
| 1536 |
-
|
| 1537 |
-
if viewer and username.lower() == viewer.lower():
|
| 1538 |
-
parts.append("Viewing <b>your likes activity</b>")
|
| 1539 |
-
else:
|
| 1540 |
-
parts.append(f"Viewing likes activity for <b>{html.escape(username)}</b>")
|
| 1541 |
-
|
| 1542 |
-
parts.append(str(selected_year))
|
| 1543 |
-
return render_status_badge(" · ".join(parts))
|
| 1544 |
-
|
| 1545 |
-
|
| 1546 |
-
def render_papers_idle_banner(
|
| 1547 |
username,
|
| 1548 |
selected_year,
|
| 1549 |
profile: gr.OAuthProfile | None,
|
| 1550 |
):
|
|
|
|
| 1551 |
username = (username or "").strip()
|
| 1552 |
selected_year = int(selected_year)
|
| 1553 |
viewer = getattr(profile, "username", None) if profile else None
|
|
@@ -1559,36 +1337,39 @@ def render_papers_idle_banner(
|
|
| 1559 |
parts.append("Not logged in")
|
| 1560 |
|
| 1561 |
if not username:
|
| 1562 |
-
parts.append("Enter a username to fetch
|
| 1563 |
return render_status_badge(" · ".join(parts))
|
| 1564 |
|
| 1565 |
if viewer and username.lower() == viewer.lower():
|
| 1566 |
-
parts.append("Fetch your
|
| 1567 |
parts.append(str(selected_year))
|
| 1568 |
else:
|
| 1569 |
-
parts.append(f"Ready to fetch
|
| 1570 |
parts.append(str(selected_year))
|
| 1571 |
|
| 1572 |
return render_status_badge(" · ".join(parts))
|
| 1573 |
|
| 1574 |
|
| 1575 |
-
def
|
|
|
|
| 1576 |
username = (username or "").strip()
|
| 1577 |
message = error_text or f"Username or org '{username}' was not found."
|
| 1578 |
return render_status_badge(html.escape(message))
|
| 1579 |
|
| 1580 |
|
| 1581 |
-
def
|
|
|
|
| 1582 |
username,
|
| 1583 |
selected_year,
|
| 1584 |
profile: gr.OAuthProfile | None,
|
| 1585 |
):
|
|
|
|
| 1586 |
username = (username or "").strip()
|
| 1587 |
selected_year = int(selected_year)
|
| 1588 |
viewer = getattr(profile, "username", None) if profile else None
|
| 1589 |
|
| 1590 |
if not username:
|
| 1591 |
-
return
|
| 1592 |
|
| 1593 |
parts = []
|
| 1594 |
if viewer:
|
|
@@ -1597,24 +1378,26 @@ def render_papers_fetching_banner(
|
|
| 1597 |
parts.append("Not logged in")
|
| 1598 |
|
| 1599 |
if viewer and username.lower() == viewer.lower():
|
| 1600 |
-
parts.append("Fetching <b>your
|
| 1601 |
else:
|
| 1602 |
-
parts.append(f"Fetching
|
| 1603 |
|
| 1604 |
parts.append(str(selected_year))
|
| 1605 |
return render_status_badge(" · ".join(parts))
|
| 1606 |
|
| 1607 |
|
| 1608 |
-
def
|
| 1609 |
-
|
|
|
|
| 1610 |
profile: gr.OAuthProfile | None,
|
| 1611 |
):
|
| 1612 |
-
|
| 1613 |
-
|
| 1614 |
-
|
|
|
|
| 1615 |
|
| 1616 |
-
username =
|
| 1617 |
-
selected_year = int(
|
| 1618 |
viewer = getattr(profile, "username", None) if profile else None
|
| 1619 |
|
| 1620 |
parts = []
|
|
@@ -1624,9 +1407,9 @@ def render_papers_loaded_banner(
|
|
| 1624 |
parts.append("Not logged in")
|
| 1625 |
|
| 1626 |
if viewer and username.lower() == viewer.lower():
|
| 1627 |
-
parts.append("Viewing <b>your
|
| 1628 |
else:
|
| 1629 |
-
parts.append(f"Viewing
|
| 1630 |
|
| 1631 |
parts.append(str(selected_year))
|
| 1632 |
return render_status_badge(" · ".join(parts))
|
|
@@ -1784,129 +1567,68 @@ def render_pr_combined_chart(community_state: dict | None, mode: str, username:
|
|
| 1784 |
"opened": f"{len(opened)} PRs opened by {username} in {year}",
|
| 1785 |
"activity": f"{len(activity)} PR follow-up events for {username} in {year}",
|
| 1786 |
"both": f"{len(opened) + len(activity)} total PR events for {username} in {year}",
|
| 1787 |
-
}[mode]
|
| 1788 |
-
|
| 1789 |
-
return render_contrib_chart(
|
| 1790 |
-
df,
|
| 1791 |
-
year,
|
| 1792 |
-
"Pull Requests",
|
| 1793 |
-
palette_name="purple",
|
| 1794 |
-
subtitle=subtitle,
|
| 1795 |
-
)
|
| 1796 |
-
|
| 1797 |
-
|
| 1798 |
-
def render_discussion_combined_chart(community_state: dict | None, mode: str, username: str, year: int):
|
| 1799 |
-
if not community_state or not community_state.get("fetched"):
|
| 1800 |
-
return render_empty_chart("Community Discussions")
|
| 1801 |
-
|
| 1802 |
-
opened = community_state["thread_opened"]["dates"]
|
| 1803 |
-
activity = community_state["discussion_activity"]["dates"]
|
| 1804 |
-
merged = merge_dates(opened, activity, mode)
|
| 1805 |
-
df = pd.DataFrame(merged, columns=["date"])
|
| 1806 |
-
|
| 1807 |
-
subtitle = {
|
| 1808 |
-
"opened": f"{len(opened)} threads opened by {username} in {year}",
|
| 1809 |
-
"activity": f"{len(activity)} discussion follow-up events for {username} in {year}",
|
| 1810 |
-
"both": f"{len(opened) + len(activity)} total discussion events for {username} in {year}",
|
| 1811 |
-
}[mode]
|
| 1812 |
-
|
| 1813 |
-
return render_contrib_chart(
|
| 1814 |
-
df,
|
| 1815 |
-
year,
|
| 1816 |
-
"Community Discussions",
|
| 1817 |
-
palette_name="teal",
|
| 1818 |
-
subtitle=subtitle,
|
| 1819 |
-
)
|
| 1820 |
-
|
| 1821 |
-
|
| 1822 |
-
def render_upvotes_chart(upvotes_state: dict | None, mode: str, username: str, year: int):
|
| 1823 |
-
if not upvotes_state or not upvotes_state.get("fetched"):
|
| 1824 |
-
return render_empty_chart("Upvotes")
|
| 1825 |
-
|
| 1826 |
-
papers = upvotes_state["papers"]["dates"]
|
| 1827 |
-
blog = upvotes_state["blog"]["dates"]
|
| 1828 |
-
changelog = upvotes_state["changelog"]["dates"]
|
| 1829 |
-
|
| 1830 |
-
if mode == "papers":
|
| 1831 |
-
merged = list(papers)
|
| 1832 |
-
subtitle = f"{len(papers)} paper upvote(s) by {username} in {year}"
|
| 1833 |
-
elif mode == "blog":
|
| 1834 |
-
merged = list(blog)
|
| 1835 |
-
subtitle = f"{len(blog)} blog upvote(s) by {username} in {year}"
|
| 1836 |
-
elif mode == "changelog":
|
| 1837 |
-
merged = list(changelog)
|
| 1838 |
-
subtitle = f"{len(changelog)} changelog upvote(s) by {username} in {year}"
|
| 1839 |
-
else:
|
| 1840 |
-
merged = list(papers) + list(blog) + list(changelog)
|
| 1841 |
-
subtitle = f"{len(merged)} total upvote event(s) by {username} in {year}"
|
| 1842 |
-
|
| 1843 |
-
df = pd.DataFrame(merged, columns=["date"])
|
| 1844 |
|
| 1845 |
return render_contrib_chart(
|
| 1846 |
df,
|
| 1847 |
year,
|
| 1848 |
-
"
|
| 1849 |
palette_name="purple",
|
| 1850 |
subtitle=subtitle,
|
| 1851 |
)
|
| 1852 |
|
| 1853 |
|
| 1854 |
-
def
|
| 1855 |
-
if not
|
| 1856 |
-
return render_empty_chart("
|
| 1857 |
-
|
| 1858 |
-
models = likes_state["models"]["dates"]
|
| 1859 |
-
datasets = likes_state["datasets"]["dates"]
|
| 1860 |
-
spaces = likes_state["spaces"]["dates"]
|
| 1861 |
-
|
| 1862 |
-
if mode == "models":
|
| 1863 |
-
merged = list(models)
|
| 1864 |
-
subtitle = f"{len(models)} model like(s) by {username} in {year}"
|
| 1865 |
-
elif mode == "datasets":
|
| 1866 |
-
merged = list(datasets)
|
| 1867 |
-
subtitle = f"{len(datasets)} dataset like(s) by {username} in {year}"
|
| 1868 |
-
elif mode == "spaces":
|
| 1869 |
-
merged = list(spaces)
|
| 1870 |
-
subtitle = f"{len(spaces)} space like(s) by {username} in {year}"
|
| 1871 |
-
else:
|
| 1872 |
-
merged = list(models) + list(datasets) + list(spaces)
|
| 1873 |
-
subtitle = f"{len(merged)} total like event(s) by {username} in {year}"
|
| 1874 |
|
|
|
|
|
|
|
|
|
|
| 1875 |
df = pd.DataFrame(merged, columns=["date"])
|
| 1876 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1877 |
return render_contrib_chart(
|
| 1878 |
df,
|
| 1879 |
year,
|
| 1880 |
-
"
|
| 1881 |
-
palette_name="
|
| 1882 |
subtitle=subtitle,
|
| 1883 |
)
|
| 1884 |
|
| 1885 |
|
| 1886 |
-
def
|
| 1887 |
-
|
| 1888 |
-
return render_empty_chart("Papers")
|
| 1889 |
|
| 1890 |
-
|
| 1891 |
-
|
| 1892 |
|
| 1893 |
-
|
| 1894 |
-
|
| 1895 |
-
|
| 1896 |
-
|
| 1897 |
-
|
| 1898 |
-
|
|
|
|
|
|
|
|
|
|
| 1899 |
else:
|
| 1900 |
-
merged = list(
|
| 1901 |
-
|
|
|
|
| 1902 |
|
| 1903 |
df = pd.DataFrame(merged, columns=["date"])
|
| 1904 |
|
| 1905 |
return render_contrib_chart(
|
| 1906 |
df,
|
| 1907 |
year,
|
| 1908 |
-
"
|
| 1909 |
-
palette_name="
|
| 1910 |
subtitle=subtitle,
|
| 1911 |
)
|
| 1912 |
|
|
@@ -2340,477 +2062,141 @@ def maybe_reset_community_section(
|
|
| 2340 |
)
|
| 2341 |
|
| 2342 |
|
| 2343 |
-
def
|
| 2344 |
-
current_upvotes_state,
|
| 2345 |
-
username,
|
| 2346 |
-
selected_year,
|
| 2347 |
-
upvotes_mode,
|
| 2348 |
-
profile: gr.OAuthProfile | None,
|
| 2349 |
-
):
|
| 2350 |
-
username = (username or "").strip()
|
| 2351 |
-
selected_year = int(selected_year)
|
| 2352 |
-
upvotes_mode = upvotes_mode or "all"
|
| 2353 |
-
|
| 2354 |
-
identity = validate_username(username, profile) if username else empty_identity("", "No username provided.")
|
| 2355 |
-
if username and not identity.get("exists"):
|
| 2356 |
-
state = empty_upvotes_state(selected_year)
|
| 2357 |
-
state["username"] = username
|
| 2358 |
-
state["selected_year"] = selected_year
|
| 2359 |
-
state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2360 |
-
return (
|
| 2361 |
-
state,
|
| 2362 |
-
render_upvotes_invalid_banner(username, identity.get("error")),
|
| 2363 |
-
gr.update(value="all", visible="hidden"),
|
| 2364 |
-
render_empty_chart("Upvotes"),
|
| 2365 |
-
state["logs"],
|
| 2366 |
-
)
|
| 2367 |
-
|
| 2368 |
-
if not current_upvotes_state:
|
| 2369 |
-
state = empty_upvotes_state(selected_year)
|
| 2370 |
-
return (
|
| 2371 |
-
state,
|
| 2372 |
-
render_upvotes_idle_banner(username, selected_year, profile),
|
| 2373 |
-
gr.update(value="all", visible="hidden"),
|
| 2374 |
-
render_empty_chart("Upvotes"),
|
| 2375 |
-
"Upvotes activity not fetched yet.",
|
| 2376 |
-
)
|
| 2377 |
-
|
| 2378 |
-
previous_username = (current_upvotes_state.get("username") or "").strip()
|
| 2379 |
-
previous_year = int(current_upvotes_state.get("selected_year") or selected_year)
|
| 2380 |
-
|
| 2381 |
-
same_username = previous_username.lower() == username.lower()
|
| 2382 |
-
same_year = previous_year == selected_year
|
| 2383 |
-
|
| 2384 |
-
if same_username and same_year and current_upvotes_state.get("fetched"):
|
| 2385 |
-
chart = render_upvotes_chart(
|
| 2386 |
-
current_upvotes_state,
|
| 2387 |
-
upvotes_mode,
|
| 2388 |
-
username=username,
|
| 2389 |
-
year=selected_year,
|
| 2390 |
-
)
|
| 2391 |
-
return (
|
| 2392 |
-
current_upvotes_state,
|
| 2393 |
-
render_upvotes_loaded_banner(current_upvotes_state, profile),
|
| 2394 |
-
gr.update(value=upvotes_mode, visible=True),
|
| 2395 |
-
chart,
|
| 2396 |
-
current_upvotes_state.get("logs", "Upvotes activity already fetched."),
|
| 2397 |
-
)
|
| 2398 |
-
|
| 2399 |
-
state = empty_upvotes_state(selected_year)
|
| 2400 |
-
return (
|
| 2401 |
-
state,
|
| 2402 |
-
render_upvotes_idle_banner(username, selected_year, profile),
|
| 2403 |
-
gr.update(value="all", visible="hidden"),
|
| 2404 |
-
render_empty_chart("Upvotes"),
|
| 2405 |
-
"Upvotes activity needs to be fetched for the current username/year.",
|
| 2406 |
-
)
|
| 2407 |
-
|
| 2408 |
-
|
| 2409 |
-
def maybe_reset_likes_section(
|
| 2410 |
-
current_likes_state,
|
| 2411 |
-
username,
|
| 2412 |
-
selected_year,
|
| 2413 |
-
likes_mode,
|
| 2414 |
-
profile: gr.OAuthProfile | None,
|
| 2415 |
-
):
|
| 2416 |
username = (username or "").strip()
|
| 2417 |
-
|
| 2418 |
-
|
| 2419 |
-
|
| 2420 |
-
|
| 2421 |
-
|
| 2422 |
-
state = empty_likes_state(selected_year)
|
| 2423 |
-
state["username"] = username
|
| 2424 |
-
state["selected_year"] = selected_year
|
| 2425 |
-
state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2426 |
-
return (
|
| 2427 |
-
state,
|
| 2428 |
-
render_likes_invalid_banner(username, identity.get("error")),
|
| 2429 |
-
gr.update(value="all", visible="hidden"),
|
| 2430 |
-
render_empty_chart("Likes"),
|
| 2431 |
-
state["logs"],
|
| 2432 |
-
)
|
| 2433 |
-
|
| 2434 |
-
if not current_likes_state:
|
| 2435 |
-
state = empty_likes_state(selected_year)
|
| 2436 |
-
return (
|
| 2437 |
-
state,
|
| 2438 |
-
render_likes_idle_banner(username, selected_year, profile),
|
| 2439 |
-
gr.update(value="all", visible="hidden"),
|
| 2440 |
-
render_empty_chart("Likes"),
|
| 2441 |
-
"Likes activity not fetched yet.",
|
| 2442 |
-
)
|
| 2443 |
-
|
| 2444 |
-
previous_username = (current_likes_state.get("username") or "").strip()
|
| 2445 |
-
previous_year = int(current_likes_state.get("selected_year") or selected_year)
|
| 2446 |
-
|
| 2447 |
-
same_username = previous_username.lower() == username.lower()
|
| 2448 |
-
same_year = previous_year == selected_year
|
| 2449 |
-
|
| 2450 |
-
if same_username and same_year and current_likes_state.get("fetched"):
|
| 2451 |
-
chart = render_likes_chart(
|
| 2452 |
-
current_likes_state,
|
| 2453 |
-
likes_mode,
|
| 2454 |
-
username=username,
|
| 2455 |
-
year=selected_year,
|
| 2456 |
-
)
|
| 2457 |
-
return (
|
| 2458 |
-
current_likes_state,
|
| 2459 |
-
render_likes_loaded_banner(current_likes_state, profile),
|
| 2460 |
-
gr.update(value=likes_mode, visible=True),
|
| 2461 |
-
chart,
|
| 2462 |
-
current_likes_state.get("logs", "Likes activity already fetched."),
|
| 2463 |
-
)
|
| 2464 |
-
|
| 2465 |
-
state = empty_likes_state(selected_year)
|
| 2466 |
-
return (
|
| 2467 |
-
state,
|
| 2468 |
-
render_likes_idle_banner(username, selected_year, profile),
|
| 2469 |
-
gr.update(value="all", visible="hidden"),
|
| 2470 |
-
render_empty_chart("Likes"),
|
| 2471 |
-
"Likes activity needs to be fetched for the current username/year.",
|
| 2472 |
-
)
|
| 2473 |
|
| 2474 |
|
| 2475 |
-
def
|
| 2476 |
-
|
|
|
|
| 2477 |
username,
|
| 2478 |
selected_year,
|
| 2479 |
-
|
| 2480 |
profile: gr.OAuthProfile | None,
|
| 2481 |
):
|
|
|
|
| 2482 |
username = (username or "").strip()
|
| 2483 |
selected_year = int(selected_year)
|
| 2484 |
-
|
| 2485 |
|
| 2486 |
identity = validate_username(username, profile) if username else empty_identity("", "No username provided.")
|
| 2487 |
if username and not identity.get("exists"):
|
| 2488 |
-
state =
|
| 2489 |
state["username"] = username
|
| 2490 |
state["selected_year"] = selected_year
|
| 2491 |
state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2492 |
return (
|
| 2493 |
state,
|
| 2494 |
-
|
| 2495 |
-
gr.update(value="
|
| 2496 |
-
render_empty_chart("
|
| 2497 |
state["logs"],
|
| 2498 |
)
|
| 2499 |
|
| 2500 |
-
if not
|
| 2501 |
-
state =
|
| 2502 |
return (
|
| 2503 |
state,
|
| 2504 |
-
|
| 2505 |
-
gr.update(value="
|
| 2506 |
-
render_empty_chart("
|
| 2507 |
-
"
|
| 2508 |
)
|
| 2509 |
|
| 2510 |
-
previous_username = (
|
| 2511 |
-
previous_year = int(
|
| 2512 |
|
| 2513 |
same_username = previous_username.lower() == username.lower()
|
| 2514 |
same_year = previous_year == selected_year
|
| 2515 |
|
| 2516 |
-
if same_username and same_year and
|
| 2517 |
-
chart =
|
| 2518 |
-
|
| 2519 |
-
|
|
|
|
| 2520 |
username=username,
|
| 2521 |
year=selected_year,
|
| 2522 |
)
|
| 2523 |
return (
|
| 2524 |
-
|
| 2525 |
-
|
| 2526 |
-
gr.update(value=
|
| 2527 |
chart,
|
| 2528 |
-
|
| 2529 |
-
)
|
| 2530 |
-
|
| 2531 |
-
state = empty_papers_state(selected_year)
|
| 2532 |
-
return (
|
| 2533 |
-
state,
|
| 2534 |
-
render_papers_idle_banner(username, selected_year, profile),
|
| 2535 |
-
gr.update(value="all", visible="hidden"),
|
| 2536 |
-
render_empty_chart("Papers"),
|
| 2537 |
-
"Papers activity needs to be fetched for the current username/year.",
|
| 2538 |
-
)
|
| 2539 |
-
|
| 2540 |
-
|
| 2541 |
-
def prepare_community_fetch_ui(username, selected_year, profile: gr.OAuthProfile | None):
|
| 2542 |
-
username = (username or "").strip()
|
| 2543 |
-
if username:
|
| 2544 |
-
identity = validate_username(username, profile)
|
| 2545 |
-
if not identity.get("exists"):
|
| 2546 |
-
return render_community_invalid_banner(username, identity.get("error"))
|
| 2547 |
-
return render_community_fetching_banner(username, selected_year, profile)
|
| 2548 |
-
|
| 2549 |
-
|
| 2550 |
-
def prepare_upvotes_fetch_ui(username, selected_year, profile: gr.OAuthProfile | None):
|
| 2551 |
-
username = (username or "").strip()
|
| 2552 |
-
if username:
|
| 2553 |
-
identity = validate_username(username, profile)
|
| 2554 |
-
if not identity.get("exists"):
|
| 2555 |
-
return render_upvotes_invalid_banner(username, identity.get("error"))
|
| 2556 |
-
return render_upvotes_fetching_banner(username, selected_year, profile)
|
| 2557 |
-
|
| 2558 |
-
|
| 2559 |
-
def prepare_likes_fetch_ui(username, selected_year, profile: gr.OAuthProfile | None):
|
| 2560 |
-
username = (username or "").strip()
|
| 2561 |
-
if username:
|
| 2562 |
-
identity = validate_username(username, profile)
|
| 2563 |
-
if not identity.get("exists"):
|
| 2564 |
-
return render_likes_invalid_banner(username, identity.get("error"))
|
| 2565 |
-
return render_likes_fetching_banner(username, selected_year, profile)
|
| 2566 |
-
|
| 2567 |
-
|
| 2568 |
-
def prepare_papers_fetch_ui(username, selected_year, profile: gr.OAuthProfile | None):
|
| 2569 |
-
username = (username or "").strip()
|
| 2570 |
-
if username:
|
| 2571 |
-
identity = validate_username(username, profile)
|
| 2572 |
-
if not identity.get("exists"):
|
| 2573 |
-
return render_papers_invalid_banner(username, identity.get("error"))
|
| 2574 |
-
return render_papers_fetching_banner(username, selected_year, profile)
|
| 2575 |
-
|
| 2576 |
-
|
| 2577 |
-
def fetch_and_render_community(
|
| 2578 |
-
current_state,
|
| 2579 |
-
username,
|
| 2580 |
-
selected_year,
|
| 2581 |
-
pr_mode,
|
| 2582 |
-
discussion_mode,
|
| 2583 |
-
profile: gr.OAuthProfile | None,
|
| 2584 |
-
):
|
| 2585 |
-
username = (username or "").strip()
|
| 2586 |
-
selected_year = int(selected_year)
|
| 2587 |
-
pr_mode = pr_mode or "both"
|
| 2588 |
-
discussion_mode = discussion_mode or "both"
|
| 2589 |
-
|
| 2590 |
-
identity = validate_username(username, profile)
|
| 2591 |
-
if username and not identity.get("exists"):
|
| 2592 |
-
empty_state = empty_community_state(selected_year)
|
| 2593 |
-
empty_state["username"] = username
|
| 2594 |
-
empty_state["selected_year"] = selected_year
|
| 2595 |
-
empty_state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2596 |
-
return (
|
| 2597 |
-
empty_state,
|
| 2598 |
-
render_community_invalid_banner(username, identity.get("error")),
|
| 2599 |
-
render_empty_chart("Pull Requests"),
|
| 2600 |
-
render_empty_chart("Community Discussions"),
|
| 2601 |
-
empty_state["logs"],
|
| 2602 |
-
gr.update(value="both", visible="hidden"),
|
| 2603 |
-
gr.update(value="both", visible="hidden"),
|
| 2604 |
-
)
|
| 2605 |
-
|
| 2606 |
-
state = fetch_all_community_activity(username, selected_year)
|
| 2607 |
-
|
| 2608 |
-
pr_chart = render_pr_combined_chart(
|
| 2609 |
-
state,
|
| 2610 |
-
pr_mode,
|
| 2611 |
-
username=username,
|
| 2612 |
-
year=selected_year,
|
| 2613 |
-
)
|
| 2614 |
-
|
| 2615 |
-
discussion_chart = render_discussion_combined_chart(
|
| 2616 |
-
state,
|
| 2617 |
-
discussion_mode,
|
| 2618 |
-
username=username,
|
| 2619 |
-
year=selected_year,
|
| 2620 |
-
)
|
| 2621 |
-
|
| 2622 |
-
return (
|
| 2623 |
-
state,
|
| 2624 |
-
render_community_loaded_banner(state, profile),
|
| 2625 |
-
pr_chart,
|
| 2626 |
-
discussion_chart,
|
| 2627 |
-
state["logs"],
|
| 2628 |
-
gr.update(value=pr_mode, visible=True),
|
| 2629 |
-
gr.update(value=discussion_mode, visible=True),
|
| 2630 |
-
)
|
| 2631 |
-
|
| 2632 |
-
|
| 2633 |
-
def fetch_and_render_upvotes(
|
| 2634 |
-
current_state,
|
| 2635 |
-
username,
|
| 2636 |
-
selected_year,
|
| 2637 |
-
upvotes_mode,
|
| 2638 |
-
profile: gr.OAuthProfile | None,
|
| 2639 |
-
):
|
| 2640 |
-
username = (username or "").strip()
|
| 2641 |
-
selected_year = int(selected_year)
|
| 2642 |
-
upvotes_mode = upvotes_mode or "all"
|
| 2643 |
-
|
| 2644 |
-
identity = validate_username(username, profile)
|
| 2645 |
-
if username and not identity.get("exists"):
|
| 2646 |
-
empty_state = empty_upvotes_state(selected_year)
|
| 2647 |
-
empty_state["username"] = username
|
| 2648 |
-
empty_state["selected_year"] = selected_year
|
| 2649 |
-
empty_state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2650 |
-
return (
|
| 2651 |
-
empty_state,
|
| 2652 |
-
render_upvotes_invalid_banner(username, identity.get("error")),
|
| 2653 |
-
render_empty_chart("Upvotes"),
|
| 2654 |
-
empty_state["logs"],
|
| 2655 |
-
gr.update(value="all", visible="hidden"),
|
| 2656 |
-
)
|
| 2657 |
-
|
| 2658 |
-
state = fetch_all_upvotes_activity(username, selected_year)
|
| 2659 |
-
|
| 2660 |
-
chart = render_upvotes_chart(
|
| 2661 |
-
state,
|
| 2662 |
-
upvotes_mode,
|
| 2663 |
-
username=username,
|
| 2664 |
-
year=selected_year,
|
| 2665 |
-
)
|
| 2666 |
-
|
| 2667 |
-
return (
|
| 2668 |
-
state,
|
| 2669 |
-
render_upvotes_loaded_banner(state, profile),
|
| 2670 |
-
chart,
|
| 2671 |
-
state["logs"],
|
| 2672 |
-
gr.update(value=upvotes_mode, visible=True),
|
| 2673 |
-
)
|
| 2674 |
-
|
| 2675 |
-
|
| 2676 |
-
def fetch_and_render_likes(
|
| 2677 |
-
current_state,
|
| 2678 |
-
username,
|
| 2679 |
-
selected_year,
|
| 2680 |
-
likes_mode,
|
| 2681 |
-
profile: gr.OAuthProfile | None,
|
| 2682 |
-
):
|
| 2683 |
-
username = (username or "").strip()
|
| 2684 |
-
selected_year = int(selected_year)
|
| 2685 |
-
likes_mode = likes_mode or "all"
|
| 2686 |
-
|
| 2687 |
-
identity = validate_username(username, profile)
|
| 2688 |
-
if username and not identity.get("exists"):
|
| 2689 |
-
empty_state = empty_likes_state(selected_year)
|
| 2690 |
-
empty_state["username"] = username
|
| 2691 |
-
empty_state["selected_year"] = selected_year
|
| 2692 |
-
empty_state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2693 |
-
return (
|
| 2694 |
-
empty_state,
|
| 2695 |
-
render_likes_invalid_banner(username, identity.get("error")),
|
| 2696 |
-
render_empty_chart("Likes"),
|
| 2697 |
-
empty_state["logs"],
|
| 2698 |
-
gr.update(value="all", visible="hidden"),
|
| 2699 |
)
|
| 2700 |
|
| 2701 |
-
state =
|
| 2702 |
-
|
| 2703 |
-
chart = render_likes_chart(
|
| 2704 |
-
state,
|
| 2705 |
-
likes_mode,
|
| 2706 |
-
username=username,
|
| 2707 |
-
year=selected_year,
|
| 2708 |
-
)
|
| 2709 |
-
|
| 2710 |
return (
|
| 2711 |
state,
|
| 2712 |
-
|
| 2713 |
-
|
| 2714 |
-
|
| 2715 |
-
|
| 2716 |
)
|
| 2717 |
|
| 2718 |
|
| 2719 |
-
def
|
|
|
|
| 2720 |
current_state,
|
| 2721 |
username,
|
| 2722 |
selected_year,
|
| 2723 |
-
|
| 2724 |
profile: gr.OAuthProfile | None,
|
| 2725 |
):
|
|
|
|
|
|
|
| 2726 |
username = (username or "").strip()
|
| 2727 |
selected_year = int(selected_year)
|
| 2728 |
-
|
| 2729 |
|
| 2730 |
identity = validate_username(username, profile)
|
| 2731 |
if username and not identity.get("exists"):
|
| 2732 |
-
empty_state =
|
| 2733 |
empty_state["username"] = username
|
| 2734 |
empty_state["selected_year"] = selected_year
|
| 2735 |
empty_state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2736 |
return (
|
| 2737 |
empty_state,
|
| 2738 |
-
|
| 2739 |
-
render_empty_chart("
|
| 2740 |
empty_state["logs"],
|
| 2741 |
-
gr.update(value="
|
| 2742 |
)
|
| 2743 |
|
| 2744 |
-
state =
|
| 2745 |
|
| 2746 |
-
chart =
|
|
|
|
| 2747 |
state,
|
| 2748 |
-
|
| 2749 |
username=username,
|
| 2750 |
year=selected_year,
|
| 2751 |
)
|
| 2752 |
|
| 2753 |
return (
|
| 2754 |
state,
|
| 2755 |
-
|
| 2756 |
chart,
|
| 2757 |
state["logs"],
|
| 2758 |
-
gr.update(value=
|
| 2759 |
-
)
|
| 2760 |
-
|
| 2761 |
-
|
| 2762 |
-
def rerender_pr_from_state(community_state, pr_mode):
|
| 2763 |
-
if not community_state:
|
| 2764 |
-
return render_empty_chart("Pull Requests")
|
| 2765 |
-
return render_pr_combined_chart(
|
| 2766 |
-
community_state,
|
| 2767 |
-
pr_mode or "both",
|
| 2768 |
-
username=(community_state.get("username") or ""),
|
| 2769 |
-
year=int(community_state.get("selected_year") or datetime.now().year),
|
| 2770 |
-
)
|
| 2771 |
-
|
| 2772 |
-
|
| 2773 |
-
def rerender_discussions_from_state(community_state, discussion_mode):
|
| 2774 |
-
if not community_state:
|
| 2775 |
-
return render_empty_chart("Community Discussions")
|
| 2776 |
-
return render_discussion_combined_chart(
|
| 2777 |
-
community_state,
|
| 2778 |
-
discussion_mode or "both",
|
| 2779 |
-
username=(community_state.get("username") or ""),
|
| 2780 |
-
year=int(community_state.get("selected_year") or datetime.now().year),
|
| 2781 |
-
)
|
| 2782 |
-
|
| 2783 |
-
|
| 2784 |
-
def rerender_upvotes_from_state(upvotes_state, upvotes_mode):
|
| 2785 |
-
if not upvotes_state:
|
| 2786 |
-
return render_empty_chart("Upvotes")
|
| 2787 |
-
return render_upvotes_chart(
|
| 2788 |
-
upvotes_state,
|
| 2789 |
-
upvotes_mode or "all",
|
| 2790 |
-
username=(upvotes_state.get("username") or ""),
|
| 2791 |
-
year=int(upvotes_state.get("selected_year") or datetime.now().year),
|
| 2792 |
-
)
|
| 2793 |
-
|
| 2794 |
-
|
| 2795 |
-
def rerender_likes_from_state(likes_state, likes_mode):
|
| 2796 |
-
if not likes_state:
|
| 2797 |
-
return render_empty_chart("Likes")
|
| 2798 |
-
return render_likes_chart(
|
| 2799 |
-
likes_state,
|
| 2800 |
-
likes_mode or "all",
|
| 2801 |
-
username=(likes_state.get("username") or ""),
|
| 2802 |
-
year=int(likes_state.get("selected_year") or datetime.now().year),
|
| 2803 |
)
|
| 2804 |
|
| 2805 |
|
| 2806 |
-
def
|
| 2807 |
-
|
| 2808 |
-
|
| 2809 |
-
|
| 2810 |
-
|
| 2811 |
-
|
| 2812 |
-
|
| 2813 |
-
|
|
|
|
|
|
|
| 2814 |
)
|
| 2815 |
|
| 2816 |
|
|
@@ -2832,9 +2218,9 @@ def hydrate_header_on_load(
|
|
| 2832 |
|
| 2833 |
repo_context_html = render_idle_context_banner(effective_username, profile)
|
| 2834 |
community_context_html = render_community_idle_banner(effective_username, selected_year, profile)
|
| 2835 |
-
upvotes_context_html =
|
| 2836 |
-
likes_context_html =
|
| 2837 |
-
papers_context_html =
|
| 2838 |
|
| 2839 |
return (
|
| 2840 |
username_update,
|
|
@@ -3499,26 +2885,21 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3499 |
|
| 3500 |
with gr.Column(elem_classes="toggles-container"):
|
| 3501 |
upvotes_view_mode = gr.Radio(
|
| 3502 |
-
choices=[
|
| 3503 |
-
|
| 3504 |
-
|
| 3505 |
-
("Blog", "blog"),
|
| 3506 |
-
("Changelog", "changelog"),
|
| 3507 |
-
],
|
| 3508 |
-
value="all",
|
| 3509 |
-
label="Upvotes view",
|
| 3510 |
visible="hidden",
|
| 3511 |
elem_classes=["gh-toggle-radio"],
|
| 3512 |
elem_id="upvotes-view-toggle",
|
| 3513 |
)
|
| 3514 |
|
| 3515 |
-
upvotes_chart = gr.HTML(render_empty_chart("
|
| 3516 |
|
| 3517 |
upvotes_logs_box = gr.Textbox(
|
| 3518 |
label="Upvotes Fetch Logs",
|
| 3519 |
interactive=False,
|
| 3520 |
lines=10,
|
| 3521 |
-
value="
|
| 3522 |
)
|
| 3523 |
|
| 3524 |
gr.Markdown("## Likes activity")
|
|
@@ -3530,26 +2911,21 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3530 |
|
| 3531 |
with gr.Column(elem_classes="toggles-container"):
|
| 3532 |
likes_view_mode = gr.Radio(
|
| 3533 |
-
choices=[
|
| 3534 |
-
|
| 3535 |
-
|
| 3536 |
-
("Datasets", "datasets"),
|
| 3537 |
-
("Spaces", "spaces"),
|
| 3538 |
-
],
|
| 3539 |
-
value="all",
|
| 3540 |
-
label="Likes view",
|
| 3541 |
visible="hidden",
|
| 3542 |
elem_classes=["gh-toggle-radio"],
|
| 3543 |
elem_id="likes-view-toggle",
|
| 3544 |
)
|
| 3545 |
|
| 3546 |
-
likes_chart = gr.HTML(render_empty_chart("
|
| 3547 |
|
| 3548 |
likes_logs_box = gr.Textbox(
|
| 3549 |
label="Likes Fetch Logs",
|
| 3550 |
interactive=False,
|
| 3551 |
lines=10,
|
| 3552 |
-
value="
|
| 3553 |
)
|
| 3554 |
|
| 3555 |
gr.Markdown("## Papers activity")
|
|
@@ -3561,25 +2937,21 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3561 |
|
| 3562 |
with gr.Column(elem_classes="toggles-container"):
|
| 3563 |
papers_view_mode = gr.Radio(
|
| 3564 |
-
choices=[
|
| 3565 |
-
|
| 3566 |
-
|
| 3567 |
-
("Authored", "authored"),
|
| 3568 |
-
],
|
| 3569 |
-
value="all",
|
| 3570 |
-
label="Papers view",
|
| 3571 |
visible="hidden",
|
| 3572 |
elem_classes=["gh-toggle-radio"],
|
| 3573 |
elem_id="papers-view-toggle",
|
| 3574 |
)
|
| 3575 |
|
| 3576 |
-
papers_chart = gr.HTML(render_empty_chart("
|
| 3577 |
|
| 3578 |
papers_logs_box = gr.Textbox(
|
| 3579 |
label="Papers Fetch Logs",
|
| 3580 |
interactive=False,
|
| 3581 |
lines=10,
|
| 3582 |
-
value="
|
| 3583 |
)
|
| 3584 |
|
| 3585 |
repo_fetch_outputs = [
|
|
@@ -3677,7 +3049,14 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3677 |
demo.load(
|
| 3678 |
fn=hydrate_header_on_load,
|
| 3679 |
inputs=[username, selected_year],
|
| 3680 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3681 |
show_progress="hidden",
|
| 3682 |
)
|
| 3683 |
|
|
@@ -3705,17 +3084,17 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3705 |
outputs=community_reset_outputs,
|
| 3706 |
show_progress="hidden",
|
| 3707 |
).then(
|
| 3708 |
-
fn=
|
| 3709 |
inputs=[upvotes_state, username, selected_year, upvotes_view_mode],
|
| 3710 |
outputs=upvotes_reset_outputs,
|
| 3711 |
show_progress="hidden",
|
| 3712 |
).then(
|
| 3713 |
-
fn=
|
| 3714 |
inputs=[likes_state, username, selected_year, likes_view_mode],
|
| 3715 |
outputs=likes_reset_outputs,
|
| 3716 |
show_progress="hidden",
|
| 3717 |
).then(
|
| 3718 |
-
fn=
|
| 3719 |
inputs=[papers_state, username, selected_year, papers_view_mode],
|
| 3720 |
outputs=papers_reset_outputs,
|
| 3721 |
show_progress="hidden",
|
|
@@ -3760,12 +3139,12 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3760 |
)
|
| 3761 |
|
| 3762 |
fetch_upvotes_button.click(
|
| 3763 |
-
fn=
|
| 3764 |
inputs=[username, selected_year],
|
| 3765 |
outputs=upvotes_context_box,
|
| 3766 |
show_progress="hidden",
|
| 3767 |
).then(
|
| 3768 |
-
fn=
|
| 3769 |
inputs=[upvotes_state, username, selected_year, upvotes_view_mode],
|
| 3770 |
outputs=upvotes_fetch_outputs,
|
| 3771 |
show_progress="full",
|
|
@@ -3776,19 +3155,19 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3776 |
)
|
| 3777 |
|
| 3778 |
upvotes_view_mode.change(
|
| 3779 |
-
fn=
|
| 3780 |
inputs=[upvotes_state, upvotes_view_mode],
|
| 3781 |
outputs=upvotes_chart,
|
| 3782 |
show_progress="hidden",
|
| 3783 |
)
|
| 3784 |
|
| 3785 |
fetch_likes_button.click(
|
| 3786 |
-
fn=
|
| 3787 |
inputs=[username, selected_year],
|
| 3788 |
outputs=likes_context_box,
|
| 3789 |
show_progress="hidden",
|
| 3790 |
).then(
|
| 3791 |
-
fn=
|
| 3792 |
inputs=[likes_state, username, selected_year, likes_view_mode],
|
| 3793 |
outputs=likes_fetch_outputs,
|
| 3794 |
show_progress="full",
|
|
@@ -3799,19 +3178,19 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3799 |
)
|
| 3800 |
|
| 3801 |
likes_view_mode.change(
|
| 3802 |
-
fn=
|
| 3803 |
inputs=[likes_state, likes_view_mode],
|
| 3804 |
outputs=likes_chart,
|
| 3805 |
show_progress="hidden",
|
| 3806 |
)
|
| 3807 |
|
| 3808 |
fetch_papers_button.click(
|
| 3809 |
-
fn=
|
| 3810 |
inputs=[username, selected_year],
|
| 3811 |
outputs=papers_context_box,
|
| 3812 |
show_progress="hidden",
|
| 3813 |
).then(
|
| 3814 |
-
fn=
|
| 3815 |
inputs=[papers_state, username, selected_year, papers_view_mode],
|
| 3816 |
outputs=papers_fetch_outputs,
|
| 3817 |
show_progress="full",
|
|
@@ -3822,7 +3201,7 @@ with gr.Blocks(title="HF Contributions") as demo:
|
|
| 3822 |
)
|
| 3823 |
|
| 3824 |
papers_view_mode.change(
|
| 3825 |
-
fn=
|
| 3826 |
inputs=[papers_state, papers_view_mode],
|
| 3827 |
outputs=papers_chart,
|
| 3828 |
show_progress="hidden",
|
|
|
|
| 363 |
}
|
| 364 |
|
| 365 |
|
| 366 |
+
def empty_bucketed_activity_state(
|
| 367 |
+
selected_year: int,
|
| 368 |
+
logs_text: str,
|
| 369 |
+
bucket_titles: dict[str, str],
|
| 370 |
+
):
|
| 371 |
+
state = {
|
| 372 |
"username": "",
|
| 373 |
"selected_year": int(selected_year),
|
| 374 |
"fetched": False,
|
| 375 |
+
"logs": logs_text,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
}
|
| 377 |
|
| 378 |
+
for key, title in bucket_titles.items():
|
| 379 |
+
state[key] = empty_activity_bucket(title)
|
| 380 |
|
| 381 |
+
return state
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def build_fetched_bucket(title: str, dates: list, selected_year: int, label: str):
|
| 385 |
+
return {
|
| 386 |
+
"title": title,
|
| 387 |
+
"fetched": True,
|
| 388 |
+
"dates": dates,
|
| 389 |
+
"count": len(dates),
|
| 390 |
+
"logs": f"Found {len(dates)} {label} in {selected_year}.",
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
def build_failed_bucket(title: str):
|
| 395 |
+
return {
|
| 396 |
+
"title": title,
|
| 397 |
+
"fetched": True,
|
| 398 |
+
"dates": [],
|
| 399 |
+
"count": 0,
|
| 400 |
+
"logs": "",
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
# ============================================================
|
| 405 |
+
# Bucketed sections config
|
| 406 |
+
# ============================================================
|
| 407 |
+
BUCKETED_SECTION_CONFIGS = {
|
| 408 |
+
"upvotes": {
|
| 409 |
+
"section_title": "Upvotes activity",
|
| 410 |
+
"chart_title": "Upvotes",
|
| 411 |
+
"palette_name": "purple",
|
| 412 |
+
"context_subject_label": "upvotes activity",
|
| 413 |
+
"radio_label": "Upvotes view",
|
| 414 |
+
"default_mode": "all",
|
| 415 |
+
"mode_labels": {
|
| 416 |
+
"all": "All",
|
| 417 |
+
"papers": "Papers",
|
| 418 |
+
"blog": "Blog",
|
| 419 |
+
"changelog": "Changelog",
|
| 420 |
+
},
|
| 421 |
+
"bucket_titles": {
|
| 422 |
+
"papers": "Paper Upvotes",
|
| 423 |
+
"blog": "Blog Upvotes",
|
| 424 |
+
"changelog": "Changelog Upvotes",
|
| 425 |
+
},
|
| 426 |
+
"bucket_log_labels": {
|
| 427 |
+
"papers": "paper upvote(s)",
|
| 428 |
+
"blog": "blog upvote(s)",
|
| 429 |
+
"changelog": "changelog upvote(s)",
|
| 430 |
+
},
|
| 431 |
+
"empty_logs_text": "Upvotes activity not fetched yet.",
|
| 432 |
+
},
|
| 433 |
+
"likes": {
|
| 434 |
+
"section_title": "Likes activity",
|
| 435 |
+
"chart_title": "Likes",
|
| 436 |
+
"palette_name": "blue",
|
| 437 |
+
"context_subject_label": "likes activity",
|
| 438 |
+
"radio_label": "Likes view",
|
| 439 |
+
"default_mode": "all",
|
| 440 |
+
"mode_labels": {
|
| 441 |
+
"all": "All",
|
| 442 |
+
"models": "Models",
|
| 443 |
+
"datasets": "Datasets",
|
| 444 |
+
"spaces": "Spaces",
|
| 445 |
+
},
|
| 446 |
+
"bucket_titles": {
|
| 447 |
+
"models": "Model Likes",
|
| 448 |
+
"datasets": "Dataset Likes",
|
| 449 |
+
"spaces": "Space Likes",
|
| 450 |
+
},
|
| 451 |
+
"bucket_log_labels": {
|
| 452 |
+
"models": "model like(s)",
|
| 453 |
+
"datasets": "dataset like(s)",
|
| 454 |
+
"spaces": "space like(s)",
|
| 455 |
+
},
|
| 456 |
+
"empty_logs_text": "Likes activity not fetched yet.",
|
| 457 |
+
},
|
| 458 |
+
"papers": {
|
| 459 |
+
"section_title": "Papers activity",
|
| 460 |
+
"chart_title": "Papers",
|
| 461 |
+
"palette_name": "teal",
|
| 462 |
+
"context_subject_label": "papers activity",
|
| 463 |
+
"radio_label": "Papers view",
|
| 464 |
+
"default_mode": "all",
|
| 465 |
+
"mode_labels": {
|
| 466 |
+
"all": "All",
|
| 467 |
+
"daily": "Daily Papers",
|
| 468 |
+
"authored": "Authored",
|
| 469 |
+
},
|
| 470 |
+
"bucket_titles": {
|
| 471 |
+
"daily": "Daily Papers",
|
| 472 |
+
"authored": "Authored Papers",
|
| 473 |
+
},
|
| 474 |
+
"bucket_log_labels": {
|
| 475 |
+
"daily": "daily paper submission(s)",
|
| 476 |
+
"authored": "authored paper(s)",
|
| 477 |
+
},
|
| 478 |
+
"empty_logs_text": "Papers activity not fetched yet.",
|
| 479 |
+
},
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def get_bucketed_cfg(section_key: str) -> dict:
|
| 484 |
+
return BUCKETED_SECTION_CONFIGS[section_key]
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
# ============================================================
|
| 488 |
+
# Generic bucketed recent-activity fetch helpers
|
| 489 |
+
# ============================================================
|
| 490 |
+
def fetch_bucketed_recent_activity(
|
| 491 |
username: str,
|
| 492 |
selected_year: int,
|
| 493 |
+
cache_scope: str,
|
| 494 |
+
activity_type: str,
|
| 495 |
+
bucket_titles: dict[str, str],
|
| 496 |
+
bucket_log_labels: dict[str, str],
|
| 497 |
+
empty_logs_text: str,
|
| 498 |
+
classifier,
|
| 499 |
):
|
| 500 |
username = (username or "").strip()
|
| 501 |
selected_year = int(selected_year)
|
| 502 |
|
| 503 |
+
cache_key = (username.lower(), selected_year, cache_scope)
|
| 504 |
+
cached = get_cache(cache_key)
|
| 505 |
+
if cached:
|
| 506 |
+
return cached
|
|
|
|
|
|
|
|
|
|
| 507 |
|
| 508 |
if not username:
|
| 509 |
+
state = empty_bucketed_activity_state(selected_year, empty_logs_text, bucket_titles)
|
| 510 |
+
state["fetched"] = True
|
| 511 |
+
state["logs"] = "No username provided."
|
| 512 |
+
set_cache(cache_key, state)
|
| 513 |
+
return state
|
|
|
|
|
|
|
| 514 |
|
| 515 |
+
bucket_dates = {key: [] for key in bucket_titles}
|
| 516 |
+
bucket_seen_ids = {key: set() for key in bucket_titles}
|
| 517 |
+
|
| 518 |
+
cursor = None
|
| 519 |
skip = 0
|
| 520 |
limit = 20
|
|
|
|
| 521 |
pages_fetched = 0
|
| 522 |
|
| 523 |
MAX_PAGES = 40
|
| 524 |
+
MAX_DURATION = 20
|
|
|
|
| 525 |
start = time.time()
|
| 526 |
|
| 527 |
logs = []
|
|
|
|
| 535 |
}
|
| 536 |
)
|
| 537 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
try:
|
| 539 |
while True:
|
| 540 |
if time.time() - start > MAX_DURATION:
|
|
|
|
| 549 |
|
| 550 |
params = {
|
| 551 |
"limit": limit,
|
| 552 |
+
"activityType": activity_type,
|
| 553 |
"feedType": "user",
|
| 554 |
"entity": username,
|
| 555 |
}
|
|
|
|
| 568 |
payload = response.json()
|
| 569 |
pages_fetched += 1
|
| 570 |
|
| 571 |
+
events = payload.get("recentActivity", [])
|
| 572 |
+
next_cursor = payload.get("cursor")
|
| 573 |
|
| 574 |
if not events:
|
| 575 |
logs.append("No more events.")
|
|
|
|
| 581 |
if not isinstance(event, dict):
|
| 582 |
continue
|
| 583 |
|
| 584 |
+
raw_time = event.get("time")
|
| 585 |
+
if not raw_time:
|
| 586 |
+
continue
|
| 587 |
+
|
| 588 |
+
try:
|
| 589 |
+
event_ts = pd.to_datetime(raw_time, utc=True)
|
| 590 |
+
except Exception:
|
| 591 |
+
continue
|
| 592 |
+
|
| 593 |
+
if event_ts.year < selected_year:
|
| 594 |
stop_early = True
|
| 595 |
break
|
| 596 |
|
| 597 |
+
if event_ts.year != selected_year:
|
|
|
|
| 598 |
continue
|
| 599 |
|
| 600 |
+
bucket_key, unique_id = classifier(event)
|
| 601 |
+
|
| 602 |
+
if not bucket_key or not unique_id:
|
| 603 |
continue
|
| 604 |
|
| 605 |
+
if bucket_key not in bucket_dates:
|
|
|
|
| 606 |
continue
|
| 607 |
|
| 608 |
+
if unique_id in bucket_seen_ids[bucket_key]:
|
| 609 |
continue
|
| 610 |
|
| 611 |
+
bucket_seen_ids[bucket_key].add(unique_id)
|
| 612 |
+
bucket_dates[bucket_key].append(event_ts.tz_localize(None).date())
|
| 613 |
|
| 614 |
if stop_early:
|
| 615 |
logs.append("Stopped early after reaching feed events older than selected year.")
|
|
|
|
| 620 |
else:
|
| 621 |
skip += limit
|
| 622 |
|
| 623 |
+
result = {
|
| 624 |
+
"username": username,
|
| 625 |
+
"selected_year": selected_year,
|
|
|
|
|
|
|
| 626 |
"fetched": True,
|
| 627 |
+
"logs": "",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 628 |
}
|
| 629 |
|
| 630 |
for key, title in bucket_titles.items():
|
|
|
|
| 724 |
return bucket_key, unique_id
|
| 725 |
|
| 726 |
|
| 727 |
+
BUCKETED_FETCH_HANDLERS = {
|
| 728 |
+
"upvotes": {
|
| 729 |
+
"activity_type": "upvote",
|
| 730 |
+
"classifier": classify_upvote_event,
|
| 731 |
+
},
|
| 732 |
+
"likes": {
|
| 733 |
+
"activity_type": "like",
|
| 734 |
+
"classifier": classify_like_event,
|
| 735 |
+
},
|
| 736 |
+
"papers": {
|
| 737 |
+
"activity_type": "update-paper",
|
| 738 |
+
"classifier": classify_paper_event,
|
| 739 |
+
},
|
| 740 |
+
}
|
| 741 |
+
|
| 742 |
+
|
| 743 |
+
def empty_generic_section_state(section_key: str, selected_year: int):
|
| 744 |
+
cfg = get_bucketed_cfg(section_key)
|
| 745 |
return empty_bucketed_activity_state(
|
| 746 |
selected_year=selected_year,
|
| 747 |
+
logs_text=cfg["empty_logs_text"],
|
| 748 |
+
bucket_titles=cfg["bucket_titles"],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 749 |
)
|
| 750 |
|
| 751 |
|
| 752 |
+
def fetch_generic_bucketed_section(section_key: str, username: str, selected_year: int):
|
| 753 |
+
cfg = get_bucketed_cfg(section_key)
|
| 754 |
+
handler = BUCKETED_FETCH_HANDLERS[section_key]
|
| 755 |
return fetch_bucketed_recent_activity(
|
| 756 |
username=username,
|
| 757 |
selected_year=selected_year,
|
| 758 |
+
cache_scope=section_key,
|
| 759 |
+
activity_type=handler["activity_type"],
|
| 760 |
+
bucket_titles=cfg["bucket_titles"],
|
| 761 |
+
bucket_log_labels=cfg["bucket_log_labels"],
|
| 762 |
+
empty_logs_text=cfg["empty_logs_text"],
|
| 763 |
+
classifier=handler["classifier"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
)
|
| 765 |
|
| 766 |
|
| 767 |
# ============================================================
|
| 768 |
+
# Community activity fetch
|
| 769 |
# ============================================================
|
| 770 |
+
def empty_community_state(selected_year: int):
|
| 771 |
+
return {
|
| 772 |
+
"username": "",
|
| 773 |
+
"selected_year": int(selected_year),
|
| 774 |
+
"fetched": False,
|
| 775 |
+
"pr_opened": empty_activity_bucket("Pull Requests"),
|
| 776 |
+
"pr_activity": empty_activity_bucket("PR Activity"),
|
| 777 |
+
"thread_opened": empty_activity_bucket("Community Threads"),
|
| 778 |
+
"discussion_activity": empty_activity_bucket("Community Activity"),
|
| 779 |
+
"logs": "Community activity not fetched yet.",
|
| 780 |
+
}
|
| 781 |
|
| 782 |
|
| 783 |
+
def fetch_user_discussion_activity(
|
| 784 |
+
username: str,
|
| 785 |
+
selected_year: int,
|
| 786 |
+
mode: str,
|
| 787 |
+
):
|
| 788 |
+
username = (username or "").strip()
|
| 789 |
+
selected_year = int(selected_year)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 790 |
|
| 791 |
+
title_map = {
|
| 792 |
+
"pr_opened": "Pull Requests",
|
| 793 |
+
"pr_activity": "PR Activity",
|
| 794 |
+
"thread_opened": "Community Threads",
|
| 795 |
+
"discussion_activity": "Community Activity",
|
| 796 |
+
}
|
| 797 |
+
title = title_map.get(mode, "Community")
|
| 798 |
|
| 799 |
+
if not username:
|
| 800 |
+
return {
|
| 801 |
+
"title": title,
|
| 802 |
+
"fetched": True,
|
| 803 |
+
"dates": [],
|
| 804 |
+
"count": 0,
|
| 805 |
+
"logs": "No username provided.",
|
| 806 |
+
}
|
| 807 |
+
|
| 808 |
+
dates = []
|
| 809 |
+
seen_ids = set()
|
| 810 |
+
skip = 0
|
| 811 |
+
limit = 20
|
| 812 |
+
cursor = None
|
| 813 |
+
pages_fetched = 0
|
| 814 |
+
|
| 815 |
+
MAX_PAGES = 40
|
| 816 |
+
MAX_DURATION = 25
|
| 817 |
+
OPEN_EVENT_EPSILON_SECONDS = 90
|
| 818 |
+
start = time.time()
|
| 819 |
+
|
| 820 |
+
logs = []
|
| 821 |
+
|
| 822 |
+
session = requests.Session()
|
| 823 |
+
session.headers.update(
|
| 824 |
+
{
|
| 825 |
+
"User-Agent": "hf-contributions-app/1.0",
|
| 826 |
+
"Accept": "application/json",
|
| 827 |
+
"Cache-Control": "no-cache",
|
| 828 |
+
}
|
| 829 |
)
|
| 830 |
|
| 831 |
+
def normalize_payload(payload):
|
| 832 |
+
if isinstance(payload, list):
|
| 833 |
+
return payload, None
|
| 834 |
+
|
| 835 |
+
if isinstance(payload, dict):
|
| 836 |
+
events = payload.get("recentActivity")
|
| 837 |
+
if isinstance(events, list):
|
| 838 |
+
return events, payload.get("cursor")
|
| 839 |
+
|
| 840 |
+
for key in ["items", "events", "activity"]:
|
| 841 |
+
value = payload.get(key)
|
| 842 |
+
if isinstance(value, list):
|
| 843 |
+
return value, payload.get("cursor")
|
| 844 |
+
|
| 845 |
+
return [], None
|
| 846 |
+
|
| 847 |
+
def extract_author_name(discussion: dict) -> str | None:
|
| 848 |
+
author = discussion.get("author")
|
| 849 |
+
if isinstance(author, str):
|
| 850 |
+
return author
|
| 851 |
+
if isinstance(author, dict):
|
| 852 |
+
for key in ["name", "username", "login"]:
|
| 853 |
+
value = author.get(key)
|
| 854 |
+
if isinstance(value, str):
|
| 855 |
+
return value
|
| 856 |
+
return None
|
| 857 |
+
|
| 858 |
+
def parse_ts(raw_value):
|
| 859 |
+
if not raw_value:
|
| 860 |
+
return None
|
| 861 |
+
try:
|
| 862 |
+
return pd.to_datetime(raw_value, utc=True)
|
| 863 |
+
except Exception:
|
| 864 |
+
return None
|
| 865 |
+
|
| 866 |
+
def event_matches_mode(event: dict, discussion: dict) -> tuple[bool, str | None]:
|
| 867 |
+
is_pr = bool(discussion.get("isPullRequest", False))
|
| 868 |
+
author_name = extract_author_name(discussion)
|
| 869 |
+
repo_id = event.get("repoId", "unknown-repo")
|
| 870 |
+
discussion_num = discussion.get("num", "unknown-num")
|
| 871 |
+
event_id = event.get("eventId", "unknown-event")
|
| 872 |
+
|
| 873 |
+
if mode == "pr_opened":
|
| 874 |
+
if not is_pr:
|
| 875 |
+
return False, None
|
| 876 |
+
if not author_name or author_name.lower() != username.lower():
|
| 877 |
+
return False, None
|
| 878 |
+
return True, f"{repo_id}::pr::{discussion_num}"
|
| 879 |
+
|
| 880 |
+
if mode == "pr_activity":
|
| 881 |
+
if not is_pr:
|
| 882 |
+
return False, None
|
| 883 |
+
return True, f"event::{event_id}"
|
| 884 |
+
|
| 885 |
+
if mode == "thread_opened":
|
| 886 |
+
if is_pr:
|
| 887 |
+
return False, None
|
| 888 |
+
if not author_name or author_name.lower() != username.lower():
|
| 889 |
+
return False, None
|
| 890 |
+
return True, f"{repo_id}::thread::{discussion_num}"
|
| 891 |
+
|
| 892 |
+
if mode == "discussion_activity":
|
| 893 |
+
if is_pr:
|
| 894 |
+
return False, None
|
| 895 |
+
return True, f"event::{event_id}"
|
| 896 |
+
|
| 897 |
+
return False, None
|
| 898 |
+
|
| 899 |
+
def extract_date_for_mode(event: dict, discussion: dict):
|
| 900 |
+
event_ts = parse_ts(event.get("time"))
|
| 901 |
+
created_ts = parse_ts(discussion.get("createdAt"))
|
| 902 |
+
|
| 903 |
+
if mode in {"pr_opened", "thread_opened"}:
|
| 904 |
+
if created_ts is None:
|
| 905 |
+
return None
|
| 906 |
+
return created_ts.tz_localize(None).date()
|
| 907 |
+
|
| 908 |
+
if mode in {"pr_activity", "discussion_activity"}:
|
| 909 |
+
if event_ts is None:
|
| 910 |
+
return None
|
| 911 |
+
if created_ts is not None:
|
| 912 |
+
delta_seconds = abs((event_ts - created_ts).total_seconds())
|
| 913 |
+
if delta_seconds <= OPEN_EVENT_EPSILON_SECONDS:
|
| 914 |
+
return None
|
| 915 |
+
return event_ts.tz_localize(None).date()
|
| 916 |
+
|
| 917 |
+
return None
|
| 918 |
+
|
| 919 |
+
try:
|
| 920 |
+
while True:
|
| 921 |
+
if time.time() - start > MAX_DURATION:
|
| 922 |
+
logs.append("Stopped due to timeout.")
|
| 923 |
+
break
|
| 924 |
+
|
| 925 |
+
if pages_fetched >= MAX_PAGES:
|
| 926 |
+
logs.append("Stopped due to max pages.")
|
| 927 |
+
break
|
| 928 |
+
|
| 929 |
+
recent_activity_rate_limiter.wait()
|
| 930 |
+
|
| 931 |
+
params = {
|
| 932 |
+
"limit": limit,
|
| 933 |
+
"activityType": "discussion",
|
| 934 |
+
"feedType": "user",
|
| 935 |
+
"entity": username,
|
| 936 |
+
}
|
| 937 |
+
if cursor:
|
| 938 |
+
params["cursor"] = cursor
|
| 939 |
+
else:
|
| 940 |
+
params["skip"] = skip
|
| 941 |
+
|
| 942 |
+
response = session.get(
|
| 943 |
+
"https://huggingface.co/api/recent-activity",
|
| 944 |
+
params=params,
|
| 945 |
+
timeout=20,
|
| 946 |
+
)
|
| 947 |
+
response.raise_for_status()
|
| 948 |
+
|
| 949 |
+
payload = response.json()
|
| 950 |
+
pages_fetched += 1
|
| 951 |
+
|
| 952 |
+
events, next_cursor = normalize_payload(payload)
|
| 953 |
+
|
| 954 |
+
if not events:
|
| 955 |
+
logs.append("No more events.")
|
| 956 |
+
break
|
| 957 |
+
|
| 958 |
+
stop_early = False
|
| 959 |
+
|
| 960 |
+
for event in events:
|
| 961 |
+
if not isinstance(event, dict):
|
| 962 |
+
continue
|
| 963 |
+
|
| 964 |
+
event_ts = parse_ts(event.get("time"))
|
| 965 |
+
if event_ts is not None and event_ts.year < selected_year:
|
| 966 |
+
stop_early = True
|
| 967 |
+
break
|
| 968 |
+
|
| 969 |
+
discussion = event.get("discussionData") or {}
|
| 970 |
+
if not isinstance(discussion, dict):
|
| 971 |
+
continue
|
| 972 |
+
|
| 973 |
+
keep, unique_id = event_matches_mode(event, discussion)
|
| 974 |
+
if not keep or not unique_id:
|
| 975 |
+
continue
|
| 976 |
+
|
| 977 |
+
dt = extract_date_for_mode(event, discussion)
|
| 978 |
+
if dt is None or dt.year != selected_year:
|
| 979 |
+
continue
|
| 980 |
+
|
| 981 |
+
if unique_id in seen_ids:
|
| 982 |
+
continue
|
| 983 |
+
|
| 984 |
+
seen_ids.add(unique_id)
|
| 985 |
+
dates.append(dt)
|
| 986 |
+
|
| 987 |
+
if stop_early:
|
| 988 |
+
logs.append("Stopped early after reaching feed events older than selected year.")
|
| 989 |
+
break
|
| 990 |
+
|
| 991 |
+
if next_cursor:
|
| 992 |
+
cursor = next_cursor
|
| 993 |
+
else:
|
| 994 |
+
skip += limit
|
| 995 |
|
| 996 |
+
logs.append(f"Fetched {pages_fetched} page(s) from recent-activity.")
|
| 997 |
+
logs.append(f"Found {len(dates)} unique {mode} event(s) in {selected_year}.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 998 |
|
| 999 |
+
return {
|
| 1000 |
+
"title": title,
|
| 1001 |
+
"fetched": True,
|
| 1002 |
+
"dates": dates,
|
| 1003 |
+
"count": len(dates),
|
| 1004 |
+
"logs": "\n".join(logs),
|
| 1005 |
+
}
|
| 1006 |
|
| 1007 |
+
except Exception as e:
|
| 1008 |
+
return {
|
| 1009 |
+
"title": title,
|
| 1010 |
+
"fetched": True,
|
| 1011 |
+
"dates": [],
|
| 1012 |
+
"count": 0,
|
| 1013 |
+
"logs": f"{title} fetch failed: {str(e)}",
|
| 1014 |
+
}
|
| 1015 |
+
|
| 1016 |
+
|
| 1017 |
+
def fetch_all_community_activity(username: str, selected_year: int):
|
| 1018 |
+
username = (username or "").strip()
|
| 1019 |
+
selected_year = int(selected_year)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1020 |
|
| 1021 |
+
cache_key = (username.lower(), selected_year, "community")
|
| 1022 |
+
cached = get_cache(cache_key)
|
| 1023 |
+
if cached:
|
| 1024 |
+
return cached
|
| 1025 |
|
| 1026 |
+
if not username:
|
| 1027 |
+
state = empty_community_state(selected_year)
|
| 1028 |
+
state["fetched"] = True
|
| 1029 |
+
state["logs"] = "No username provided."
|
| 1030 |
+
set_cache(cache_key, state)
|
| 1031 |
+
return state
|
| 1032 |
|
| 1033 |
+
pr_opened = fetch_user_discussion_activity(username, selected_year, "pr_opened")
|
| 1034 |
+
pr_activity = fetch_user_discussion_activity(username, selected_year, "pr_activity")
|
| 1035 |
+
thread_opened = fetch_user_discussion_activity(username, selected_year, "thread_opened")
|
| 1036 |
+
discussion_activity = fetch_user_discussion_activity(username, selected_year, "discussion_activity")
|
| 1037 |
|
| 1038 |
+
combined_logs = "\n\n".join(
|
| 1039 |
+
[
|
| 1040 |
+
"[Pull Requests]\n" + pr_opened["logs"],
|
| 1041 |
+
"[PR Activity]\n" + pr_activity["logs"],
|
| 1042 |
+
"[Community Threads]\n" + thread_opened["logs"],
|
| 1043 |
+
"[Community Activity]\n" + discussion_activity["logs"],
|
| 1044 |
+
]
|
| 1045 |
+
)
|
| 1046 |
|
| 1047 |
+
result = {
|
| 1048 |
+
"username": username,
|
| 1049 |
+
"selected_year": selected_year,
|
| 1050 |
+
"fetched": True,
|
| 1051 |
+
"pr_opened": pr_opened,
|
| 1052 |
+
"pr_activity": pr_activity,
|
| 1053 |
+
"thread_opened": thread_opened,
|
| 1054 |
+
"discussion_activity": discussion_activity,
|
| 1055 |
+
"logs": combined_logs,
|
| 1056 |
+
}
|
| 1057 |
|
| 1058 |
+
set_cache(cache_key, result)
|
| 1059 |
+
return result
|
|
|
|
| 1060 |
|
| 1061 |
|
| 1062 |
# ============================================================
|
|
|
|
| 1319 |
return render_status_badge(" · ".join(parts))
|
| 1320 |
|
| 1321 |
|
| 1322 |
+
def render_bucketed_idle_banner(
|
| 1323 |
+
section_key: str,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1324 |
username,
|
| 1325 |
selected_year,
|
| 1326 |
profile: gr.OAuthProfile | None,
|
| 1327 |
):
|
| 1328 |
+
cfg = get_bucketed_cfg(section_key)
|
| 1329 |
username = (username or "").strip()
|
| 1330 |
selected_year = int(selected_year)
|
| 1331 |
viewer = getattr(profile, "username", None) if profile else None
|
|
|
|
| 1337 |
parts.append("Not logged in")
|
| 1338 |
|
| 1339 |
if not username:
|
| 1340 |
+
parts.append(f"Enter a username to fetch {cfg['context_subject_label']}")
|
| 1341 |
return render_status_badge(" · ".join(parts))
|
| 1342 |
|
| 1343 |
if viewer and username.lower() == viewer.lower():
|
| 1344 |
+
parts.append(f"Fetch your {cfg['context_subject_label']} or enter another username")
|
| 1345 |
parts.append(str(selected_year))
|
| 1346 |
else:
|
| 1347 |
+
parts.append(f"Ready to fetch {cfg['context_subject_label']} for <b>{html.escape(username)}</b>")
|
| 1348 |
parts.append(str(selected_year))
|
| 1349 |
|
| 1350 |
return render_status_badge(" · ".join(parts))
|
| 1351 |
|
| 1352 |
|
| 1353 |
+
def render_bucketed_invalid_banner(section_key: str, username: str, error_text: str | None = None):
|
| 1354 |
+
_ = section_key
|
| 1355 |
username = (username or "").strip()
|
| 1356 |
message = error_text or f"Username or org '{username}' was not found."
|
| 1357 |
return render_status_badge(html.escape(message))
|
| 1358 |
|
| 1359 |
|
| 1360 |
+
def render_bucketed_fetching_banner(
|
| 1361 |
+
section_key: str,
|
| 1362 |
username,
|
| 1363 |
selected_year,
|
| 1364 |
profile: gr.OAuthProfile | None,
|
| 1365 |
):
|
| 1366 |
+
cfg = get_bucketed_cfg(section_key)
|
| 1367 |
username = (username or "").strip()
|
| 1368 |
selected_year = int(selected_year)
|
| 1369 |
viewer = getattr(profile, "username", None) if profile else None
|
| 1370 |
|
| 1371 |
if not username:
|
| 1372 |
+
return render_bucketed_idle_banner(section_key, "", selected_year, profile)
|
| 1373 |
|
| 1374 |
parts = []
|
| 1375 |
if viewer:
|
|
|
|
| 1378 |
parts.append("Not logged in")
|
| 1379 |
|
| 1380 |
if viewer and username.lower() == viewer.lower():
|
| 1381 |
+
parts.append(f"Fetching <b>your {cfg['context_subject_label']}</b>")
|
| 1382 |
else:
|
| 1383 |
+
parts.append(f"Fetching {cfg['context_subject_label']} for <b>{html.escape(username)}</b>")
|
| 1384 |
|
| 1385 |
parts.append(str(selected_year))
|
| 1386 |
return render_status_badge(" · ".join(parts))
|
| 1387 |
|
| 1388 |
|
| 1389 |
+
def render_bucketed_loaded_banner(
|
| 1390 |
+
section_key: str,
|
| 1391 |
+
section_state: dict | None,
|
| 1392 |
profile: gr.OAuthProfile | None,
|
| 1393 |
):
|
| 1394 |
+
cfg = get_bucketed_cfg(section_key)
|
| 1395 |
+
if not section_state or not section_state.get("username"):
|
| 1396 |
+
year = section_state.get("selected_year", datetime.now().year) if section_state else datetime.now().year
|
| 1397 |
+
return render_bucketed_idle_banner(section_key, "", year, profile)
|
| 1398 |
|
| 1399 |
+
username = section_state["username"]
|
| 1400 |
+
selected_year = int(section_state["selected_year"])
|
| 1401 |
viewer = getattr(profile, "username", None) if profile else None
|
| 1402 |
|
| 1403 |
parts = []
|
|
|
|
| 1407 |
parts.append("Not logged in")
|
| 1408 |
|
| 1409 |
if viewer and username.lower() == viewer.lower():
|
| 1410 |
+
parts.append(f"Viewing <b>your {cfg['context_subject_label']}</b>")
|
| 1411 |
else:
|
| 1412 |
+
parts.append(f"Viewing {cfg['context_subject_label']} for <b>{html.escape(username)}</b>")
|
| 1413 |
|
| 1414 |
parts.append(str(selected_year))
|
| 1415 |
return render_status_badge(" · ".join(parts))
|
|
|
|
| 1567 |
"opened": f"{len(opened)} PRs opened by {username} in {year}",
|
| 1568 |
"activity": f"{len(activity)} PR follow-up events for {username} in {year}",
|
| 1569 |
"both": f"{len(opened) + len(activity)} total PR events for {username} in {year}",
|
| 1570 |
+
}[mode]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1571 |
|
| 1572 |
return render_contrib_chart(
|
| 1573 |
df,
|
| 1574 |
year,
|
| 1575 |
+
"Pull Requests",
|
| 1576 |
palette_name="purple",
|
| 1577 |
subtitle=subtitle,
|
| 1578 |
)
|
| 1579 |
|
| 1580 |
|
| 1581 |
+
def render_discussion_combined_chart(community_state: dict | None, mode: str, username: str, year: int):
|
| 1582 |
+
if not community_state or not community_state.get("fetched"):
|
| 1583 |
+
return render_empty_chart("Community Discussions")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1584 |
|
| 1585 |
+
opened = community_state["thread_opened"]["dates"]
|
| 1586 |
+
activity = community_state["discussion_activity"]["dates"]
|
| 1587 |
+
merged = merge_dates(opened, activity, mode)
|
| 1588 |
df = pd.DataFrame(merged, columns=["date"])
|
| 1589 |
|
| 1590 |
+
subtitle = {
|
| 1591 |
+
"opened": f"{len(opened)} threads opened by {username} in {year}",
|
| 1592 |
+
"activity": f"{len(activity)} discussion follow-up events for {username} in {year}",
|
| 1593 |
+
"both": f"{len(opened) + len(activity)} total discussion events for {username} in {year}",
|
| 1594 |
+
}[mode]
|
| 1595 |
+
|
| 1596 |
return render_contrib_chart(
|
| 1597 |
df,
|
| 1598 |
year,
|
| 1599 |
+
"Community Discussions",
|
| 1600 |
+
palette_name="teal",
|
| 1601 |
subtitle=subtitle,
|
| 1602 |
)
|
| 1603 |
|
| 1604 |
|
| 1605 |
+
def render_bucketed_chart(section_key: str, section_state: dict | None, mode: str, username: str, year: int):
|
| 1606 |
+
cfg = get_bucketed_cfg(section_key)
|
|
|
|
| 1607 |
|
| 1608 |
+
if not section_state or not section_state.get("fetched"):
|
| 1609 |
+
return render_empty_chart(cfg["chart_title"])
|
| 1610 |
|
| 1611 |
+
bucket_titles = cfg["bucket_titles"]
|
| 1612 |
+
default_mode = cfg["default_mode"]
|
| 1613 |
+
mode = mode or default_mode
|
| 1614 |
+
|
| 1615 |
+
if mode == "all":
|
| 1616 |
+
merged = []
|
| 1617 |
+
for bucket_key in bucket_titles:
|
| 1618 |
+
merged.extend(section_state[bucket_key]["dates"])
|
| 1619 |
+
subtitle = f"{len(merged)} total {cfg['chart_title'].lower()} event(s) by {username} in {year}"
|
| 1620 |
else:
|
| 1621 |
+
merged = list(section_state.get(mode, {}).get("dates", []))
|
| 1622 |
+
bucket_title = bucket_titles.get(mode, mode)
|
| 1623 |
+
subtitle = f"{len(merged)} {bucket_title.lower()} by {username} in {year}"
|
| 1624 |
|
| 1625 |
df = pd.DataFrame(merged, columns=["date"])
|
| 1626 |
|
| 1627 |
return render_contrib_chart(
|
| 1628 |
df,
|
| 1629 |
year,
|
| 1630 |
+
cfg["chart_title"],
|
| 1631 |
+
palette_name=cfg["palette_name"],
|
| 1632 |
subtitle=subtitle,
|
| 1633 |
)
|
| 1634 |
|
|
|
|
| 2062 |
)
|
| 2063 |
|
| 2064 |
|
| 2065 |
+
def prepare_bucketed_fetch_ui(section_key: str, username, selected_year, profile: gr.OAuthProfile | None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2066 |
username = (username or "").strip()
|
| 2067 |
+
if username:
|
| 2068 |
+
identity = validate_username(username, profile)
|
| 2069 |
+
if not identity.get("exists"):
|
| 2070 |
+
return render_bucketed_invalid_banner(section_key, username, identity.get("error"))
|
| 2071 |
+
return render_bucketed_fetching_banner(section_key, username, selected_year, profile)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2072 |
|
| 2073 |
|
| 2074 |
+
def maybe_reset_bucketed_section(
|
| 2075 |
+
section_key: str,
|
| 2076 |
+
current_state,
|
| 2077 |
username,
|
| 2078 |
selected_year,
|
| 2079 |
+
mode,
|
| 2080 |
profile: gr.OAuthProfile | None,
|
| 2081 |
):
|
| 2082 |
+
cfg = get_bucketed_cfg(section_key)
|
| 2083 |
username = (username or "").strip()
|
| 2084 |
selected_year = int(selected_year)
|
| 2085 |
+
mode = mode or cfg["default_mode"]
|
| 2086 |
|
| 2087 |
identity = validate_username(username, profile) if username else empty_identity("", "No username provided.")
|
| 2088 |
if username and not identity.get("exists"):
|
| 2089 |
+
state = empty_generic_section_state(section_key, selected_year)
|
| 2090 |
state["username"] = username
|
| 2091 |
state["selected_year"] = selected_year
|
| 2092 |
state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2093 |
return (
|
| 2094 |
state,
|
| 2095 |
+
render_bucketed_invalid_banner(section_key, username, identity.get("error")),
|
| 2096 |
+
gr.update(value=cfg["default_mode"], visible="hidden"),
|
| 2097 |
+
render_empty_chart(cfg["chart_title"]),
|
| 2098 |
state["logs"],
|
| 2099 |
)
|
| 2100 |
|
| 2101 |
+
if not current_state:
|
| 2102 |
+
state = empty_generic_section_state(section_key, selected_year)
|
| 2103 |
return (
|
| 2104 |
state,
|
| 2105 |
+
render_bucketed_idle_banner(section_key, username, selected_year, profile),
|
| 2106 |
+
gr.update(value=cfg["default_mode"], visible="hidden"),
|
| 2107 |
+
render_empty_chart(cfg["chart_title"]),
|
| 2108 |
+
cfg["empty_logs_text"],
|
| 2109 |
)
|
| 2110 |
|
| 2111 |
+
previous_username = (current_state.get("username") or "").strip()
|
| 2112 |
+
previous_year = int(current_state.get("selected_year") or selected_year)
|
| 2113 |
|
| 2114 |
same_username = previous_username.lower() == username.lower()
|
| 2115 |
same_year = previous_year == selected_year
|
| 2116 |
|
| 2117 |
+
if same_username and same_year and current_state.get("fetched"):
|
| 2118 |
+
chart = render_bucketed_chart(
|
| 2119 |
+
section_key,
|
| 2120 |
+
current_state,
|
| 2121 |
+
mode,
|
| 2122 |
username=username,
|
| 2123 |
year=selected_year,
|
| 2124 |
)
|
| 2125 |
return (
|
| 2126 |
+
current_state,
|
| 2127 |
+
render_bucketed_loaded_banner(section_key, current_state, profile),
|
| 2128 |
+
gr.update(value=mode, visible=True),
|
| 2129 |
chart,
|
| 2130 |
+
current_state.get("logs", f"{cfg['chart_title']} activity already fetched."),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2131 |
)
|
| 2132 |
|
| 2133 |
+
state = empty_generic_section_state(section_key, selected_year)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2134 |
return (
|
| 2135 |
state,
|
| 2136 |
+
render_bucketed_idle_banner(section_key, username, selected_year, profile),
|
| 2137 |
+
gr.update(value=cfg["default_mode"], visible="hidden"),
|
| 2138 |
+
render_empty_chart(cfg["chart_title"]),
|
| 2139 |
+
f"{cfg['chart_title']} activity needs to be fetched for the current username/year.",
|
| 2140 |
)
|
| 2141 |
|
| 2142 |
|
| 2143 |
+
def fetch_and_render_bucketed_section(
|
| 2144 |
+
section_key: str,
|
| 2145 |
current_state,
|
| 2146 |
username,
|
| 2147 |
selected_year,
|
| 2148 |
+
mode,
|
| 2149 |
profile: gr.OAuthProfile | None,
|
| 2150 |
):
|
| 2151 |
+
_ = current_state
|
| 2152 |
+
cfg = get_bucketed_cfg(section_key)
|
| 2153 |
username = (username or "").strip()
|
| 2154 |
selected_year = int(selected_year)
|
| 2155 |
+
mode = mode or cfg["default_mode"]
|
| 2156 |
|
| 2157 |
identity = validate_username(username, profile)
|
| 2158 |
if username and not identity.get("exists"):
|
| 2159 |
+
empty_state = empty_generic_section_state(section_key, selected_year)
|
| 2160 |
empty_state["username"] = username
|
| 2161 |
empty_state["selected_year"] = selected_year
|
| 2162 |
empty_state["logs"] = identity.get("error") or f"Username or org '{username}' was not found."
|
| 2163 |
return (
|
| 2164 |
empty_state,
|
| 2165 |
+
render_bucketed_invalid_banner(section_key, username, identity.get("error")),
|
| 2166 |
+
render_empty_chart(cfg["chart_title"]),
|
| 2167 |
empty_state["logs"],
|
| 2168 |
+
gr.update(value=cfg["default_mode"], visible="hidden"),
|
| 2169 |
)
|
| 2170 |
|
| 2171 |
+
state = fetch_generic_bucketed_section(section_key, username, selected_year)
|
| 2172 |
|
| 2173 |
+
chart = render_bucketed_chart(
|
| 2174 |
+
section_key,
|
| 2175 |
state,
|
| 2176 |
+
mode,
|
| 2177 |
username=username,
|
| 2178 |
year=selected_year,
|
| 2179 |
)
|
| 2180 |
|
| 2181 |
return (
|
| 2182 |
state,
|
| 2183 |
+
render_bucketed_loaded_banner(section_key, state, profile),
|
| 2184 |
chart,
|
| 2185 |
state["logs"],
|
| 2186 |
+
gr.update(value=mode, visible=True),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2187 |
)
|
| 2188 |
|
| 2189 |
|
| 2190 |
+
def rerender_bucketed_from_state(section_key: str, section_state, mode):
|
| 2191 |
+
cfg = get_bucketed_cfg(section_key)
|
| 2192 |
+
if not section_state:
|
| 2193 |
+
return render_empty_chart(cfg["chart_title"])
|
| 2194 |
+
return render_bucketed_chart(
|
| 2195 |
+
section_key,
|
| 2196 |
+
section_state,
|
| 2197 |
+
mode or cfg["default_mode"],
|
| 2198 |
+
username=(section_state.get("username") or ""),
|
| 2199 |
+
year=int(section_state.get("selected_year") or datetime.now().year),
|
| 2200 |
)
|
| 2201 |
|
| 2202 |
|
|
|
|
| 2218 |
|
| 2219 |
repo_context_html = render_idle_context_banner(effective_username, profile)
|
| 2220 |
community_context_html = render_community_idle_banner(effective_username, selected_year, profile)
|
| 2221 |
+
upvotes_context_html = render_bucketed_idle_banner("upvotes", effective_username, selected_year, profile)
|
| 2222 |
+
likes_context_html = render_bucketed_idle_banner("likes", effective_username, selected_year, profile)
|
| 2223 |
+
papers_context_html = render_bucketed_idle_banner("papers", effective_username, selected_year, profile)
|
| 2224 |
|
| 2225 |
return (
|
| 2226 |
username_update,
|
|
|
|
| 2885 |
|
| 2886 |
with gr.Column(elem_classes="toggles-container"):
|
| 2887 |
upvotes_view_mode = gr.Radio(
|
| 2888 |
+
choices=[(label, key) for key, label in BUCKETED_SECTION_CONFIGS["upvotes"]["mode_labels"].items()],
|
| 2889 |
+
value=BUCKETED_SECTION_CONFIGS["upvotes"]["default_mode"],
|
| 2890 |
+
label=BUCKETED_SECTION_CONFIGS["upvotes"]["radio_label"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2891 |
visible="hidden",
|
| 2892 |
elem_classes=["gh-toggle-radio"],
|
| 2893 |
elem_id="upvotes-view-toggle",
|
| 2894 |
)
|
| 2895 |
|
| 2896 |
+
upvotes_chart = gr.HTML(render_empty_chart(BUCKETED_SECTION_CONFIGS["upvotes"]["chart_title"]))
|
| 2897 |
|
| 2898 |
upvotes_logs_box = gr.Textbox(
|
| 2899 |
label="Upvotes Fetch Logs",
|
| 2900 |
interactive=False,
|
| 2901 |
lines=10,
|
| 2902 |
+
value=BUCKETED_SECTION_CONFIGS["upvotes"]["empty_logs_text"],
|
| 2903 |
)
|
| 2904 |
|
| 2905 |
gr.Markdown("## Likes activity")
|
|
|
|
| 2911 |
|
| 2912 |
with gr.Column(elem_classes="toggles-container"):
|
| 2913 |
likes_view_mode = gr.Radio(
|
| 2914 |
+
choices=[(label, key) for key, label in BUCKETED_SECTION_CONFIGS["likes"]["mode_labels"].items()],
|
| 2915 |
+
value=BUCKETED_SECTION_CONFIGS["likes"]["default_mode"],
|
| 2916 |
+
label=BUCKETED_SECTION_CONFIGS["likes"]["radio_label"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2917 |
visible="hidden",
|
| 2918 |
elem_classes=["gh-toggle-radio"],
|
| 2919 |
elem_id="likes-view-toggle",
|
| 2920 |
)
|
| 2921 |
|
| 2922 |
+
likes_chart = gr.HTML(render_empty_chart(BUCKETED_SECTION_CONFIGS["likes"]["chart_title"]))
|
| 2923 |
|
| 2924 |
likes_logs_box = gr.Textbox(
|
| 2925 |
label="Likes Fetch Logs",
|
| 2926 |
interactive=False,
|
| 2927 |
lines=10,
|
| 2928 |
+
value=BUCKETED_SECTION_CONFIGS["likes"]["empty_logs_text"],
|
| 2929 |
)
|
| 2930 |
|
| 2931 |
gr.Markdown("## Papers activity")
|
|
|
|
| 2937 |
|
| 2938 |
with gr.Column(elem_classes="toggles-container"):
|
| 2939 |
papers_view_mode = gr.Radio(
|
| 2940 |
+
choices=[(label, key) for key, label in BUCKETED_SECTION_CONFIGS["papers"]["mode_labels"].items()],
|
| 2941 |
+
value=BUCKETED_SECTION_CONFIGS["papers"]["default_mode"],
|
| 2942 |
+
label=BUCKETED_SECTION_CONFIGS["papers"]["radio_label"],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2943 |
visible="hidden",
|
| 2944 |
elem_classes=["gh-toggle-radio"],
|
| 2945 |
elem_id="papers-view-toggle",
|
| 2946 |
)
|
| 2947 |
|
| 2948 |
+
papers_chart = gr.HTML(render_empty_chart(BUCKETED_SECTION_CONFIGS["papers"]["chart_title"]))
|
| 2949 |
|
| 2950 |
papers_logs_box = gr.Textbox(
|
| 2951 |
label="Papers Fetch Logs",
|
| 2952 |
interactive=False,
|
| 2953 |
lines=10,
|
| 2954 |
+
value=BUCKETED_SECTION_CONFIGS["papers"]["empty_logs_text"],
|
| 2955 |
)
|
| 2956 |
|
| 2957 |
repo_fetch_outputs = [
|
|
|
|
| 3049 |
demo.load(
|
| 3050 |
fn=hydrate_header_on_load,
|
| 3051 |
inputs=[username, selected_year],
|
| 3052 |
+
outputs=[
|
| 3053 |
+
username,
|
| 3054 |
+
context_box,
|
| 3055 |
+
community_context_box,
|
| 3056 |
+
upvotes_context_box,
|
| 3057 |
+
likes_context_box,
|
| 3058 |
+
papers_context_box,
|
| 3059 |
+
],
|
| 3060 |
show_progress="hidden",
|
| 3061 |
)
|
| 3062 |
|
|
|
|
| 3084 |
outputs=community_reset_outputs,
|
| 3085 |
show_progress="hidden",
|
| 3086 |
).then(
|
| 3087 |
+
fn=lambda state, uname, year, mode, profile: maybe_reset_bucketed_section("upvotes", state, uname, year, mode, profile),
|
| 3088 |
inputs=[upvotes_state, username, selected_year, upvotes_view_mode],
|
| 3089 |
outputs=upvotes_reset_outputs,
|
| 3090 |
show_progress="hidden",
|
| 3091 |
).then(
|
| 3092 |
+
fn=lambda state, uname, year, mode, profile: maybe_reset_bucketed_section("likes", state, uname, year, mode, profile),
|
| 3093 |
inputs=[likes_state, username, selected_year, likes_view_mode],
|
| 3094 |
outputs=likes_reset_outputs,
|
| 3095 |
show_progress="hidden",
|
| 3096 |
).then(
|
| 3097 |
+
fn=lambda state, uname, year, mode, profile: maybe_reset_bucketed_section("papers", state, uname, year, mode, profile),
|
| 3098 |
inputs=[papers_state, username, selected_year, papers_view_mode],
|
| 3099 |
outputs=papers_reset_outputs,
|
| 3100 |
show_progress="hidden",
|
|
|
|
| 3139 |
)
|
| 3140 |
|
| 3141 |
fetch_upvotes_button.click(
|
| 3142 |
+
fn=lambda uname, year, profile: prepare_bucketed_fetch_ui("upvotes", uname, year, profile),
|
| 3143 |
inputs=[username, selected_year],
|
| 3144 |
outputs=upvotes_context_box,
|
| 3145 |
show_progress="hidden",
|
| 3146 |
).then(
|
| 3147 |
+
fn=lambda state, uname, year, mode, profile: fetch_and_render_bucketed_section("upvotes", state, uname, year, mode, profile),
|
| 3148 |
inputs=[upvotes_state, username, selected_year, upvotes_view_mode],
|
| 3149 |
outputs=upvotes_fetch_outputs,
|
| 3150 |
show_progress="full",
|
|
|
|
| 3155 |
)
|
| 3156 |
|
| 3157 |
upvotes_view_mode.change(
|
| 3158 |
+
fn=lambda state, mode: rerender_bucketed_from_state("upvotes", state, mode),
|
| 3159 |
inputs=[upvotes_state, upvotes_view_mode],
|
| 3160 |
outputs=upvotes_chart,
|
| 3161 |
show_progress="hidden",
|
| 3162 |
)
|
| 3163 |
|
| 3164 |
fetch_likes_button.click(
|
| 3165 |
+
fn=lambda uname, year, profile: prepare_bucketed_fetch_ui("likes", uname, year, profile),
|
| 3166 |
inputs=[username, selected_year],
|
| 3167 |
outputs=likes_context_box,
|
| 3168 |
show_progress="hidden",
|
| 3169 |
).then(
|
| 3170 |
+
fn=lambda state, uname, year, mode, profile: fetch_and_render_bucketed_section("likes", state, uname, year, mode, profile),
|
| 3171 |
inputs=[likes_state, username, selected_year, likes_view_mode],
|
| 3172 |
outputs=likes_fetch_outputs,
|
| 3173 |
show_progress="full",
|
|
|
|
| 3178 |
)
|
| 3179 |
|
| 3180 |
likes_view_mode.change(
|
| 3181 |
+
fn=lambda state, mode: rerender_bucketed_from_state("likes", state, mode),
|
| 3182 |
inputs=[likes_state, likes_view_mode],
|
| 3183 |
outputs=likes_chart,
|
| 3184 |
show_progress="hidden",
|
| 3185 |
)
|
| 3186 |
|
| 3187 |
fetch_papers_button.click(
|
| 3188 |
+
fn=lambda uname, year, profile: prepare_bucketed_fetch_ui("papers", uname, year, profile),
|
| 3189 |
inputs=[username, selected_year],
|
| 3190 |
outputs=papers_context_box,
|
| 3191 |
show_progress="hidden",
|
| 3192 |
).then(
|
| 3193 |
+
fn=lambda state, uname, year, mode, profile: fetch_and_render_bucketed_section("papers", state, uname, year, mode, profile),
|
| 3194 |
inputs=[papers_state, username, selected_year, papers_view_mode],
|
| 3195 |
outputs=papers_fetch_outputs,
|
| 3196 |
show_progress="full",
|
|
|
|
| 3201 |
)
|
| 3202 |
|
| 3203 |
papers_view_mode.change(
|
| 3204 |
+
fn=lambda state, mode: rerender_bucketed_from_state("papers", state, mode),
|
| 3205 |
inputs=[papers_state, papers_view_mode],
|
| 3206 |
outputs=papers_chart,
|
| 3207 |
show_progress="hidden",
|