Team Ai
Apppublic

evalstate/diffusers-pr-api

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
dashboard.py674 linesDownload Raw Back to reports
1from __future__ import annotations2 3import json4from collections import Counter, defaultdict5from datetime import UTC, datetime, timedelta6from pathlib import Path7from typing import Any8 9from slop_farmer.config import DashboardDataOptions10from slop_farmer.data.parquet_io import read_json, read_parquet_rows11from slop_farmer.data.snapshot_paths import (12    ResolvedAnalysisReportPath,13    resolve_default_dashboard_analysis_report,14)15from slop_farmer.data.snapshot_source import resolve_snapshot_source_dir16 17 18def run_dashboard_data(options: DashboardDataOptions) -> Path:19    snapshot_dir = _resolve_snapshot_dir(options)20    manifest = _read_optional_json(snapshot_dir / "manifest.json")21    issues = read_parquet_rows(snapshot_dir / "issues.parquet")22    pull_requests = read_parquet_rows(snapshot_dir / "pull_requests.parquet")23    analysis_path = _resolve_analysis_input(snapshot_dir, options.analysis_input)24    analysis = _read_optional_json(analysis_path.path) if analysis_path is not None else {}25    contributor_report = _read_optional_json(26        options.contributors_input or snapshot_dir / "new-contributors-report.json"27    )28    pr_scope_report = _read_optional_json(29        options.pr_scope_input or snapshot_dir / "pr-scope-clusters.json"30    )31 32    repo = (33        manifest.get("repo")34        or (pull_requests[0]["repo"] if pull_requests else None)35        or (issues[0]["repo"] if issues else None)36        or ""37    )38    snapshot_id = manifest.get("snapshot_id") or snapshot_dir.name39    reference_time = _reference_time(snapshot_id, pull_requests)40    cutoff = reference_time - timedelta(days=options.window_days)41 42    issue_map = {int(row["number"]): row for row in issues if row.get("number") is not None}43    pr_map = {int(row["number"]): row for row in pull_requests if row.get("number") is not None}44    recent_pull_requests = []45    for row in pull_requests:46        created_at = _coerce_datetime(row.get("created_at"))47        if created_at is not None and created_at >= cutoff:48            recent_pull_requests.append(row)49    recent_pull_requests.sort(key=lambda row: row.get("created_at") or "", reverse=True)50    recent_numbers = {51        int(row["number"]) for row in recent_pull_requests if row.get("number") is not None52    }53 54    clusters, memberships = _cluster_rows(analysis, issue_map, pr_map, recent_numbers)55    pr_scope_clusters = _pr_scope_cluster_rows(pr_scope_report, pr_map, recent_numbers)56    contributors = _contributor_rows(contributor_report, recent_pull_requests, memberships)57    prs = _pr_rows(recent_pull_requests, memberships)58 59    summary = {60        "repo": repo,61        "snapshot_id": snapshot_id,62        "generated_at": datetime.now(tz=UTC)63        .replace(microsecond=0)64        .isoformat()65        .replace("+00:00", "Z"),66        "window_days": options.window_days,67        "reference_time": reference_time.isoformat().replace("+00:00", "Z"),68        "pr_count": len(prs),69        "open_pr_count": sum(1 for row in prs if row["state"] == "open"),70        "merged_pr_count": sum(1 for row in prs if row["merged"]),71        "cluster_count": len(clusters),72        "clustered_pr_count": sum(1 for row in prs if row["cluster_id"]),73        "contributor_count": len(contributors),74        "analysis_available": bool(analysis),75        "analysis_source": None if analysis_path is None else analysis_path.source,76        "analysis_variant": None if analysis_path is None else analysis_path.variant,77        "analysis_snapshot_id": (78            None79            if analysis_path is None80            else (81                analysis_path.snapshot_id82                or (83                    str(analysis.get("snapshot_id")).strip()84                    if analysis.get("snapshot_id") is not None85                    else None86                )87            )88        ),89        "analysis_id": None if analysis_path is None else analysis_path.analysis_id,90        "contributors_available": bool(contributor_report),91        "pr_scope_available": bool(pr_scope_report),92        "pr_scope_cluster_count": len(pr_scope_clusters),93    }94 95    output_dir = options.output_dir.resolve()96    output_dir.mkdir(parents=True, exist_ok=True)97    _write_json(summary, output_dir / "summary.json")98    _write_json(clusters, output_dir / "clusters.json")99    _write_json(pr_scope_clusters, output_dir / "pr_scope_clusters.json")100    _write_json(prs, output_dir / "prs.json")101    _write_json(contributors, output_dir / "contributors.json")102    return output_dir103 104 105def _resolve_snapshot_dir(options: DashboardDataOptions) -> Path:106    snapshots_root = (107        options.snapshot_root.resolve()108        if options.snapshot_root is not None109        else (Path("data") / "snapshots").resolve()110    )111    return resolve_snapshot_source_dir(112        snapshot_dir=options.snapshot_dir,113        local_snapshots_root=snapshots_root,114        hf_repo_id=options.hf_repo_id,115        hf_revision=options.hf_revision,116        hf_materialize_dir=options.hf_materialize_dir,117        hf_output_dir=snapshots_root.parent,118    )119 120 121def _resolve_analysis_input(122    snapshot_dir: Path, override_path: Path | None123) -> ResolvedAnalysisReportPath | None:124    if override_path is not None:125        resolved = override_path.resolve()126        if not resolved.exists():127            raise FileNotFoundError(f"Dashboard analysis input not found: {resolved}")128        return ResolvedAnalysisReportPath(129            path=resolved,130            variant=_analysis_variant_for_path(resolved),131            source="override",132        )133    return resolve_default_dashboard_analysis_report(snapshot_dir)134 135 136def _read_optional_json(path: Path) -> dict[str, Any]:137    if path.exists():138        return read_json(path)139    return {}140 141 142def _write_json(payload: Any, path: Path) -> None:143    path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")144 145 146def _reference_time(snapshot_id: str, pull_requests: list[dict[str, Any]]) -> datetime:147    parsed = _parse_snapshot_id(snapshot_id)148    if parsed is not None:149        return parsed150    timestamps = [151        timestamp152        for row in pull_requests153        for timestamp in (154            _coerce_datetime(row.get("updated_at")),155            _coerce_datetime(row.get("created_at")),156        )157        if timestamp is not None158    ]159    if timestamps:160        return max(timestamps)161    return datetime.now(tz=UTC)162 163 164def _parse_snapshot_id(value: str) -> datetime | None:165    try:166        return datetime.strptime(value, "%Y%m%dT%H%M%SZ").replace(tzinfo=UTC)167    except ValueError:168        return None169 170 171def _coerce_datetime(value: Any) -> datetime | None:172    if not value or not isinstance(value, str):173        return None174    try:175        return datetime.fromisoformat(value.replace("Z", "+00:00"))176    except ValueError:177        return None178 179 180def _coerce_int(value: Any) -> int | None:181    if value is None:182        return None183    try:184        return int(value)185    except (TypeError, ValueError):186        return None187 188 189def _excerpt(value: Any, limit: int = 240) -> str | None:190    if not value or not isinstance(value, str):191        return None192    compact = " ".join(value.split())193    if len(compact) <= limit:194        return compact195    return compact[: limit - 1].rstrip() + "…"196 197 198def _analysis_variant_for_path(path: Path) -> str:199    if path.name == "analysis-report-hybrid.json":200        return "hybrid"201    if path.name == "analysis-report.json":202        return "deterministic"203    return "override"204 205 206def _cluster_rows(207    analysis: dict[str, Any],208    issue_map: dict[int, dict[str, Any]],209    pr_map: dict[int, dict[str, Any]],210    recent_numbers: set[int],211) -> tuple[list[dict[str, Any]], dict[int, list[dict[str, str]]]]:212    rows: list[dict[str, Any]] = []213    memberships: dict[int, list[dict[str, str]]] = defaultdict(list)214    for cluster in analysis.get("meta_bugs", []):215        pr_numbers = [_coerce_int(value) for value in cluster.get("pr_numbers", [])]216        pr_numbers = [value for value in pr_numbers if value is not None]217        recent_pr_numbers = [number for number in pr_numbers if number in recent_numbers]218        outside_window_pr_numbers = [219            number for number in pr_numbers if number not in recent_numbers220        ]221        if not recent_pr_numbers:222            continue223        canonical_pr_number = _coerce_int(cluster.get("canonical_pr_number"))224        canonical_issue_number = _coerce_int(cluster.get("canonical_issue_number"))225        cluster_id = str(cluster.get("cluster_id") or f"cluster-{recent_pr_numbers[0]}")226        title = _cluster_title(227            cluster, issue_map, pr_map, canonical_issue_number, canonical_pr_number228        )229        recent_authors = sorted(230            {231                str(pr_map[number].get("author_login"))232                for number in recent_pr_numbers233                if number in pr_map and pr_map[number].get("author_login")234            }235        )236        last_activity_at = max(237            (238                pr_map[number].get("updated_at") or pr_map[number].get("created_at")239                for number in recent_pr_numbers240                if number in pr_map241            ),242            default=None,243        )244        row = {245            "cluster_id": cluster_id,246            "title": title,247            "summary": cluster.get("summary"),248            "status": cluster.get("status"),249            "confidence": cluster.get("confidence"),250            "canonical_issue_number": canonical_issue_number,251            "canonical_pr_number": canonical_pr_number,252            "issue_numbers": [253                _coerce_int(value)254                for value in cluster.get("issue_numbers", [])255                if _coerce_int(value) is not None256            ],257            "pr_numbers": pr_numbers,258            "recent_pr_numbers": recent_pr_numbers,259            "pr_count": len(pr_numbers),260            "recent_pr_count": len(recent_pr_numbers),261            "outside_window_prs": [262                _pr_member_stub(number, pr_map.get(number, {}))263                for number in outside_window_pr_numbers264            ],265            "authors": recent_authors,266            "last_activity_at": last_activity_at,267            "evidence_types": list(cluster.get("evidence_types", [])),268            "pr_similarity": _cluster_similarity_map(cluster, canonical_pr_number),269            "pairwise_similarity": _cluster_pairwise_similarity(cluster),270            "github_url": _cluster_github_url(271                issue_map, pr_map, canonical_issue_number, canonical_pr_number272            ),273        }274        rows.append(row)275        for number in recent_pr_numbers:276            role = "canonical" if canonical_pr_number == number else "member"277            memberships[number].append({"cluster_id": cluster_id, "role": role})278    rows.sort(279        key=lambda row: (280            -int(row["recent_pr_count"]),281            -int(row["pr_count"]),282            -(float(row["confidence"]) if row["confidence"] is not None else 0.0),283            row["last_activity_at"] or "",284        ),285        reverse=False,286    )287    return rows, memberships288 289 290def _cluster_title(291    cluster: dict[str, Any],292    issue_map: dict[int, dict[str, Any]],293    pr_map: dict[int, dict[str, Any]],294    canonical_issue_number: int | None,295    canonical_pr_number: int | None,296) -> str:297    if canonical_issue_number is not None and canonical_issue_number in issue_map:298        return str(299            issue_map[canonical_issue_number].get("title") or f"Issue #{canonical_issue_number}"300        )301    if canonical_pr_number is not None and canonical_pr_number in pr_map:302        return str(pr_map[canonical_pr_number].get("title") or f"PR #{canonical_pr_number}")303    summary = cluster.get("summary")304    if summary:305        return str(summary)306    cluster_id = cluster.get("cluster_id") or "cluster"307    return str(cluster_id)308 309 310def _cluster_github_url(311    issue_map: dict[int, dict[str, Any]],312    pr_map: dict[int, dict[str, Any]],313    canonical_issue_number: int | None,314    canonical_pr_number: int | None,315) -> str | None:316    if canonical_issue_number is not None and canonical_issue_number in issue_map:317        return issue_map[canonical_issue_number].get("html_url")318    if canonical_pr_number is not None and canonical_pr_number in pr_map:319        return pr_map[canonical_pr_number].get("html_url")320    return None321 322 323def _cluster_similarity_map(324    cluster: dict[str, Any], canonical_pr_number: int | None325) -> dict[str, dict[str, float]]:326    if canonical_pr_number is None:327        return {}328    scores: dict[str, dict[str, float]] = {}329    for comparison in cluster.get("pr_comparisons", []):330        left = _coerce_int(comparison.get("left_pr_number"))331        right = _coerce_int(comparison.get("right_pr_number"))332        if left != canonical_pr_number and right != canonical_pr_number:333            continue334        other = right if left == canonical_pr_number else left335        if other is None:336            continue337        scores[str(other)] = {338            "patch_similarity": float(comparison.get("patch_similarity") or 0.0),339            "code_similarity": float(comparison.get("code_similarity") or 0.0),340            "size_similarity": float(comparison.get("size_similarity") or 0.0),341            "file_overlap": float(comparison.get("file_overlap") or 0.0),342            "area_overlap": float(comparison.get("area_overlap") or 0.0),343        }344    return scores345 346 347def _cluster_pairwise_similarity(cluster: dict[str, Any]) -> list[dict[str, Any]]:348    rows: list[dict[str, Any]] = []349    for comparison in cluster.get("pr_comparisons", []):350        left = _coerce_int(comparison.get("left_pr_number"))351        right = _coerce_int(comparison.get("right_pr_number"))352        if left is None or right is None:353            continue354        rows.append(355            {356                "left_pr_number": left,357                "right_pr_number": right,358                "patch_similarity": float(comparison.get("patch_similarity") or 0.0),359                "code_similarity": float(comparison.get("code_similarity") or 0.0),360                "size_similarity": float(comparison.get("size_similarity") or 0.0),361                "file_overlap": float(comparison.get("file_overlap") or 0.0),362                "area_overlap": float(comparison.get("area_overlap") or 0.0),363            }364        )365    return rows366 367 368def _pr_scope_cluster_rows(369    pr_scope_report: dict[str, Any],370    pr_map: dict[int, dict[str, Any]],371    recent_numbers: set[int],372) -> list[dict[str, Any]]:373    rows: list[dict[str, Any]] = []374    for cluster in pr_scope_report.get("pr_scope_clusters", []):375        pr_numbers = [_coerce_int(value) for value in cluster.get("pr_numbers", [])]376        pr_numbers = [value for value in pr_numbers if value is not None]377        recent_pr_numbers = [number for number in pr_numbers if number in recent_numbers]378        outside_window_pr_numbers = [379            number for number in pr_numbers if number not in recent_numbers380        ]381        if not recent_pr_numbers:382            continue383        representative_pr_number = _coerce_int(cluster.get("representative_pr_number"))384        recent_authors = sorted(385            {386                str(pr_map[number].get("author_login"))387                for number in recent_pr_numbers388                if number in pr_map and pr_map[number].get("author_login")389            }390        )391        last_activity_at = max(392            (393                pr_map[number].get("updated_at") or pr_map[number].get("created_at")394                for number in recent_pr_numbers395                if number in pr_map396            ),397            default=None,398        )399        representative = pr_map.get(representative_pr_number or -1, {})400        rows.append(401            {402                "kind": "pr_scope",403                "cluster_id": str(cluster.get("cluster_id") or f"pr-scope-{recent_pr_numbers[0]}"),404                "title": _pr_scope_title(cluster, pr_map, representative_pr_number),405                "summary": cluster.get("summary"),406                "representative_pr_number": representative_pr_number,407                "representative_title": representative.get("title"),408                "representative_url": representative.get("html_url"),409                "pr_numbers": pr_numbers,410                "recent_pr_numbers": recent_pr_numbers,411                "pr_count": len(pr_numbers),412                "recent_pr_count": len(recent_pr_numbers),413                "outside_window_prs": [414                    _pr_member_stub(number, pr_map.get(number, {}))415                    for number in outside_window_pr_numbers416                ],417                "authors": recent_authors,418                "last_activity_at": last_activity_at,419                "average_similarity": float(cluster.get("average_similarity") or 0.0),420                "shared_filenames": list(cluster.get("shared_filenames") or []),421                "shared_directories": list(cluster.get("shared_directories") or []),422                "pairwise": _pr_scope_pairwise_rows(cluster),423            }424        )425    rows.sort(426        key=lambda row: (427            -int(row["recent_pr_count"]),428            -int(row["pr_count"]),429            -(float(row["average_similarity"]) if row["average_similarity"] is not None else 0.0),430            row["last_activity_at"] or "",431            str(row["cluster_id"]),432        )433    )434    return rows435 436 437def _pr_scope_title(438    cluster: dict[str, Any],439    pr_map: dict[int, dict[str, Any]],440    representative_pr_number: int | None,441) -> str:442    if representative_pr_number is not None and representative_pr_number in pr_map:443        title = pr_map[representative_pr_number].get("title")444        if title:445            return f"Scope: {title}"446    shared_filenames = [str(value) for value in (cluster.get("shared_filenames") or []) if value]447    if shared_filenames:448        return f"Scope: {shared_filenames[0]}"449    shared_directories = [450        str(value) for value in (cluster.get("shared_directories") or []) if value451    ]452    if shared_directories:453        return f"Scope: {shared_directories[0]}"454    summary = cluster.get("summary")455    if summary:456        return str(summary)457    return str(cluster.get("cluster_id") or "pr-scope")458 459 460def _pr_scope_pairwise_rows(cluster: dict[str, Any]) -> list[dict[str, Any]]:461    rows: list[dict[str, Any]] = []462    for comparison in cluster.get("pairwise", []):463        left = _coerce_int(comparison.get("left_pr_number"))464        right = _coerce_int(comparison.get("right_pr_number"))465        if left is None or right is None:466            continue467        rows.append(468            {469                "left_pr_number": left,470                "right_pr_number": right,471                "similarity": float(comparison.get("similarity") or 0.0),472                "content_similarity": float(comparison.get("content_similarity") or 0.0),473                "size_similarity": float(comparison.get("size_similarity") or 0.0),474                "breadth_similarity": float(comparison.get("breadth_similarity") or 0.0),475                "concentration_similarity": float(476                    comparison.get("concentration_similarity") or 0.0477                ),478                "shared_filenames": list(comparison.get("shared_filenames") or []),479                "shared_directories": list(comparison.get("shared_directories") or []),480            }481        )482    return rows483 484 485def _pr_member_stub(number: int, row: dict[str, Any]) -> dict[str, Any]:486    html_url = row.get("html_url")487    return {488        "number": number,489        "title": row.get("title"),490        "author": row.get("author_login"),491        "state": row.get("state"),492        "merged": bool(row.get("merged")),493        "draft": bool(row.get("draft")),494        "created_at": row.get("created_at"),495        "updated_at": row.get("updated_at"),496        "changed_files": _coerce_int(row.get("changed_files")),497        "additions": _coerce_int(row.get("additions")),498        "deletions": _coerce_int(row.get("deletions")),499        "html_url": html_url,500        "files_url": f"{html_url}/files" if html_url else None,501    }502 503 504def _pr_rows(505    pull_requests: list[dict[str, Any]],506    memberships: dict[int, list[dict[str, str]]],507) -> list[dict[str, Any]]:508    rows = []509    for row in pull_requests:510        number = _coerce_int(row.get("number"))511        if number is None:512            continue513        cluster_memberships = memberships.get(number, [])514        primary_membership = cluster_memberships[0] if cluster_memberships else None515        html_url = row.get("html_url")516        rows.append(517            {518                "number": number,519                "title": row.get("title"),520                "author": row.get("author_login"),521                "state": row.get("state"),522                "author_association": row.get("author_association"),523                "merged": bool(row.get("merged")),524                "draft": bool(row.get("draft")),525                "created_at": row.get("created_at"),526                "updated_at": row.get("updated_at"),527                "changed_files": _coerce_int(row.get("changed_files")),528                "additions": _coerce_int(row.get("additions")),529                "deletions": _coerce_int(row.get("deletions")),530                "comments_count": _coerce_int(row.get("comments_count")),531                "review_comments_count": _coerce_int(row.get("review_comments_count")),532                "labels": list(row.get("labels") or []),533                "body_excerpt": _excerpt(row.get("body")),534                "cluster_id": primary_membership["cluster_id"] if primary_membership else None,535                "cluster_role": primary_membership["role"] if primary_membership else None,536                "cluster_ids": [membership["cluster_id"] for membership in cluster_memberships],537                "html_url": html_url,538                "files_url": f"{html_url}/files" if html_url else None,539                "conversation_url": html_url,540            }541        )542    return rows543 544 545def _contributor_rows(546    contributor_report: dict[str, Any],547    pull_requests: list[dict[str, Any]],548    memberships: dict[int, list[dict[str, str]]],549) -> list[dict[str, Any]]:550    recent_pr_counts = Counter(551        str(row.get("author_login")) for row in pull_requests if row.get("author_login")552    )553    recent_associations = _recent_repo_associations(pull_requests)554    recent_cluster_counts = Counter(555        str(row.get("author_login"))556        for row in pull_requests557        if row.get("author_login")558        for _membership in memberships.get(_coerce_int(row.get("number")) or -1, [])559    )560    report_rows = contributor_report.get("contributors", [])561    if not report_rows:562        rows = [563            {564                "author": author,565                "name": None,566                "profile_url": f"https://github.com/{author}",567                "repo_pull_requests_url": None,568                "repo_issues_url": None,569                "snapshot_pr_count": count,570                "snapshot_issue_count": 0,571                "recent_pr_count": count,572                "cluster_count": recent_cluster_counts.get(author, 0),573                "repo_association": recent_associations.get(author),574                "new_to_repo": None,575                "first_seen_in_snapshot": None,576                "report_reason": None,577                "known_contributor": _is_known_repo_association(recent_associations.get(author)),578                "follow_through_score": None,579                "breadth_score": None,580                "automation_risk_signal": None,581                "heuristic_note": None,582                "account_age_days": None,583                "quality_score": None,584                "public_pr_count_42d": None,585                "public_repo_count_42d": None,586            }587            for author, count in recent_pr_counts.items()588        ]589        rows.sort(key=lambda row: (-int(row["recent_pr_count"]), row["author"]))590        return rows591 592    rows = []593    for contributor in report_rows:594        author = contributor.get("author_login")595        if not author:596            continue597        recent_pr_count = recent_pr_counts.get(str(author), 0)598        if recent_pr_count == 0 and not contributor.get("snapshot_pr_count"):599            continue600        rows.append(601            {602                "author": author,603                "name": contributor.get("name"),604                "profile_url": contributor.get("profile_url"),605                "repo_pull_requests_url": contributor.get("repo_pull_requests_url"),606                "repo_issues_url": contributor.get("repo_issues_url"),607                "snapshot_pr_count": _coerce_int(contributor.get("snapshot_pr_count")) or 0,608                "snapshot_issue_count": _coerce_int(contributor.get("snapshot_issue_count")) or 0,609                "recent_pr_count": recent_pr_count,610                "cluster_count": recent_cluster_counts.get(str(author), 0),611                "repo_association": contributor.get("repo_association")612                or recent_associations.get(str(author)),613                "new_to_repo": contributor.get("new_to_repo"),614                "first_seen_in_snapshot": contributor.get("first_seen_in_snapshot"),615                "report_reason": contributor.get("report_reason"),616                "known_contributor": _known_contributor(contributor),617                "follow_through_score": contributor.get("follow_through_score"),618                "breadth_score": contributor.get("breadth_score"),619                "automation_risk_signal": contributor.get("automation_risk_signal"),620                "heuristic_note": contributor.get("heuristic_note"),621                "account_age_days": _coerce_int(contributor.get("account_age_days")),622                "quality_score": None,623                "public_pr_count_42d": _coerce_int(624                    (contributor.get("activity") or {}).get("visible_authored_pr_count")625                ),626                "public_repo_count_42d": _coerce_int(627                    (contributor.get("activity") or {}).get("distinct_repos_with_authored_prs")628                ),629            }630        )631    rows.sort(632        key=lambda row: (633            -int(row["recent_pr_count"]),634            -int(row["snapshot_pr_count"]),635            -int(row["cluster_count"]),636            str(row["author"]),637        )638    )639    return rows640 641 642def _known_contributor(contributor: dict[str, Any]) -> bool:643    return _is_known_repo_association(contributor.get("repo_association"))644 645 646def _recent_repo_associations(pull_requests: list[dict[str, Any]]) -> dict[str, str | None]:647    grouped: dict[str, set[str]] = defaultdict(set)648    for row in pull_requests:649        login = str(row.get("author_login") or "").strip()650        association = str(row.get("author_association") or "").strip()651        if not login or not association:652            continue653        grouped[login].add(association)654    return {login: _select_repo_association(sorted(values)) for login, values in grouped.items()}655 656 657def _select_repo_association(values: list[str]) -> str | None:658    if not values:659        return None660    priority = {661        "OWNER": 70,662        "MEMBER": 60,663        "COLLABORATOR": 50,664        "CONTRIBUTOR": 40,665        "FIRST_TIME_CONTRIBUTOR": 30,666        "FIRST_TIMER": 20,667        "NONE": 10,668    }669    return max(values, key=lambda value: (priority.get(value, 0), value))670 671 672def _is_known_repo_association(value: Any) -> bool:673    return str(value or "") in {"OWNER", "MEMBER", "COLLABORATOR"}674