Spaces:
Running
Running
| """The PRIMO pages, rendered as ``pm-*`` markup for ``gr.HTML`` blocks. | |
| Gradio has no card or rich-table component, so every page here is a string of | |
| HTML styled entirely by ``primo.css`` -- the rail, the board grid, the | |
| leaderboard tables and the tasks table. The functions are pure: they take the | |
| data the app already fetched (``Board`` objects, the results ``DataFrame``, the | |
| registry) and return a string, so they unit-test without Gradio or a network. | |
| Two rules hold everywhere: | |
| * Every string that comes from the registry or from a submission goes through | |
| ``html.escape`` -- board names, blurbs, disease names and, above all, the | |
| user-chosen model names that become table cells and column headers. | |
| * New submissions can carry a public institution, author-submission check and paper | |
| link. Private legacy ``hf_username`` values stay hidden. Public strings and | |
| links are validated and escaped before rendering. | |
| Navigation is reload-based: the rail is a column of ``<a href="?board=slug">`` | |
| and ``<a href="?tab=name">`` links, which costs a page load per click but buys | |
| shareable per-board URLs and needs no JavaScript, matching how the board cards | |
| have always worked. | |
| """ | |
| import math | |
| import numbers | |
| from html import escape | |
| from urllib.parse import urlparse | |
| import pandas as pd | |
| from boards import ( | |
| AREA_GROUP, | |
| CATEGORY_GROUP, | |
| GROUP_NOTE, | |
| MODALITY_GROUP, | |
| Board, | |
| OpenBoard, | |
| board_label, | |
| in_group, | |
| modality_label, | |
| open_in_group, | |
| ) | |
| from leaderboard import ModelCell, per_task_table, ranked_table, tasks_table, top_models | |
| SECTIONS = (MODALITY_GROUP, AREA_GROUP, CATEGORY_GROUP) | |
| MEDALS = ("🥇", "🥈", "🥉") | |
| N_TOP_MODELS = len(MEDALS) | |
| BRAND = ( | |
| '<a class="pm-brand" target="_self" href="?tab=boards">' | |
| '<img src="/gradio_api/file=assets/primo-mark.webp" alt="">' | |
| "<b>PRIMO</b><span>benchmark</span></a>" | |
| ) | |
| FOOT_LINKS = ( | |
| ("tasks", "Tasks"), | |
| ("submit", "Submit a model"), | |
| ("contribute", "Contribute"), | |
| ("method", "Method"), | |
| ) | |
| NAME_COLUMNS = frozenset({"Model", "Best"}) | |
| STRONG_COLUMNS = frozenset({"Task"}) | |
| METRIC_COLUMNS = frozenset({"Metric"}) | |
| RIGHT_COLUMNS = frozenset({"Rank", "Patients"}) | |
| METRIC_GUIDES = { | |
| "AUROC": "Range: 0 to 1 · Random predictor: 0.5", | |
| "Pearson": "Range: -1 to 1 · Random predictor: 0 (expected)", | |
| "Residual Spearman score": "Range: -1 to 1 · Random predictor: 0 (expected)", | |
| "Centered Spearman score": "Range: -1 to 1 · Random predictor: 0 (expected)", | |
| } | |
| def evaluation_confirmation() -> str: | |
| """Render the confirmation shown before a model evaluation starts.""" | |
| return ( | |
| '<dialog id="pm-evaluation-confirmation" class="pm-dialog" ' | |
| 'aria-labelledby="pm-evaluation-confirmation-title">' | |
| '<form method="dialog" class="pm-dialog-surface">' | |
| '<p class="pm-over">Confirm submission</p>' | |
| '<h2 id="pm-evaluation-confirmation-title">' | |
| "Evaluate and submit this model?</h2>" | |
| "<p>Evaluation can take several minutes. If it succeeds, this submission " | |
| "will update the public leaderboards.</p>" | |
| '<div class="pm-dialog-actions">' | |
| '<button class="pm-btn pm-btn--outline" value="cancel">Cancel</button>' | |
| '<button class="pm-btn pm-btn--primary" value="confirm">' | |
| "Evaluate model</button>" | |
| "</div></form></dialog>" | |
| ) | |
| def evaluation_status() -> str: | |
| """Render the progress message shown while an evaluation runs.""" | |
| return ( | |
| '<div class="pm-evaluation-status" role="status" aria-live="polite">' | |
| '<svg class="pm-spinner" aria-hidden="true" viewBox="0 0 24 24">' | |
| '<circle cx="12" cy="12" r="9"></circle>' | |
| '<path d="M12 3a9 9 0 0 1 9 9"></path></svg>' | |
| "<span>Your embeddings are being evaluated... This can take a few " | |
| "minutes.</span></div>" | |
| ) | |
| CHEVRON = ( | |
| '<svg class="pm-chevron" viewBox="0 0 16 16" width="16" height="16" ' | |
| 'aria-hidden="true"><path d="M6 4l4 4-4 4" fill="none" stroke="#fff" ' | |
| 'stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>' | |
| ) | |
| # --------------------------------------------------------------------- rail | |
| def rail_html( | |
| boards: list[Board], active_slug: str | None, active_tab: str | None | |
| ) -> str: | |
| """The left navigation: brand, a collapsible "Boards" menu, then the foot links. | |
| The board groups live inside a ``<details>`` toggle so the four foot links | |
| (Tasks, Submit, Contribute, Method) stay visible without scrolling. The toggle | |
| is pure HTML/CSS, keeping navigation reload-based and JavaScript-free. | |
| ``active_slug`` highlights the board a visitor is on; ``active_tab`` highlights | |
| a foot link, and its absence means we are on a board or the overview, so the | |
| "Boards" menu is the active one. Open boards link to Contribute, mirroring the | |
| overview cards. | |
| """ | |
| on_boards = active_tab is None | |
| summary_cls = "pm-rail-summary pm-active" if on_boards else "pm-rail-summary" | |
| parts = [ | |
| '<div class="pm-rail-inner">', | |
| BRAND, | |
| '<details class="pm-rail-section" open>', | |
| f'<summary class="{summary_cls}"><span>Boards</span>{CHEVRON}</summary>', | |
| '<div class="pm-rail-sections">', | |
| ] | |
| for group in SECTIONS: | |
| cards, opens = in_group(boards, group), open_in_group(boards, group) | |
| if not cards and not opens: | |
| continue | |
| parts.append( | |
| f'<div class="pm-rail-group"><p class="pm-rail-label">{escape(group)}</p>' | |
| ) | |
| for board in cards: | |
| cls = "pm-link pm-active" if board.slug == active_slug else "pm-link" | |
| parts.append( | |
| f'<a class="{cls}" target="_self" href="?board={escape(board.slug)}">' | |
| f'<span class="pm-label">{escape(board_label(board))}</span>' | |
| f'<span class="pm-count">{board.n_tasks}</span></a>' | |
| ) | |
| for board in opens: | |
| parts.append( | |
| '<a class="pm-link pm-link--open" target="_self" href="?tab=contribute">' | |
| f'<span class="pm-label">{escape(board.name)}</span>' | |
| '<span class="pm-badge pm-badge--quiet">open</span></a>' | |
| ) | |
| parts.append("</div>") | |
| parts.append("</div></details>") | |
| parts.append('<div class="pm-rail-foot">') | |
| for tab, label in FOOT_LINKS: | |
| cls = "pm-foot-link pm-active" if tab == active_tab else "pm-foot-link" | |
| parts.append( | |
| f'<a class="{cls}" target="_self" href="?tab={tab}">{escape(label)}</a>' | |
| ) | |
| parts.append("</div></div>") | |
| return "".join(parts) | |
| # ------------------------------------------------------------------ boards | |
| def _leading(top) -> str: | |
| """The card's mini-ranking: a medal, a name and an Elo, one line each. | |
| An empty board asks for the first submission instead of naming a leader. | |
| Every listed model gets a medal, so ``MEDALS`` is what bounds the podium and | |
| ``N_TOP_MODELS`` is derived from it -- the two cannot drift into a rank with | |
| no medal to print. | |
| A baseline is listed like any other leader. The card marks nothing; the board | |
| page is where a rating is read against the ``(baseline)`` it anchors on. | |
| """ | |
| parts = ['<p class="pm-over" style="margin-top:14px">Leading (Elo)</p>'] | |
| if not top: | |
| parts.append( | |
| '<p class="pm-leader pm-leader--2">No ranked model yet. Be the first.</p>' | |
| ) | |
| return "".join(parts) | |
| for medal, row in zip(MEDALS, top, strict=False): | |
| score = "n/a" if row.elo is None else str(row.elo) | |
| parts.append( | |
| f'<p class="pm-leader"><span class="pm-medal">{medal}</span>' | |
| f"{escape(row.name)}" | |
| f' <span class="pm-num--dim">{score}</span></p>' | |
| ) | |
| return "".join(parts) | |
| def _live_card(board: Board, df: pd.DataFrame, by_id: dict[str, dict]) -> str: | |
| top = top_models(df, by_id, board, N_TOP_MODELS) | |
| return ( | |
| f'<a class="pm-card pm-card--live" target="_self" href="?board={escape(board.slug)}">' | |
| f"<h3>{escape(board_label(board))}</h3>" | |
| f'<p class="pm-meta">{board.n_tasks} tasks · {board.n_cohorts} cohorts<br>' | |
| f"{board.n_patients:,} patients · {board.n_diseases} diseases</p>" | |
| f"{_leading(top)}</a>" | |
| ) | |
| def _open_card(board: OpenBoard) -> str: | |
| return ( | |
| '<a class="pm-card pm-card--open" target="_self" href="?tab=contribute">' | |
| f"<h3>{escape(board.name)}</h3>" | |
| '<p class="pm-meta"><span class="pm-badge pm-badge--quiet">OPEN · no cohort yet</span></p>' | |
| f'<p class="pm-asks">{escape(board.blurb)}</p>' | |
| '<p class="pm-cta">Propose a cohort →</p></a>' | |
| ) | |
| def render_boards(boards: list[Board], df: pd.DataFrame, by_id: dict[str, dict]) -> str: | |
| """The Boards overview: every board as a card, grouped by facet. | |
| Live cards link to their board and teaser their leaders; open cards state a | |
| gap and link to Contribute. A section counts its open slices so the page | |
| reads as showing its own gaps, not the registry as the whole territory. | |
| """ | |
| if not boards: | |
| return ( | |
| '<div class="pm-body"><p class="pm-caption">The task registry is ' | |
| "unavailable right now. Please retry in a moment.</p></div>" | |
| ) | |
| out = [ | |
| '<div class="pm-head"><div><h1>Leaderboards</h1>', | |
| "<p>PRIMO evaluates zero-shot representations of omics samples through " | |
| "drug-development-related tasks. Benchmarks are organized by data " | |
| "modality, therapeutic area, or task category.</p></div></div>", | |
| '<div class="pm-body">', | |
| ] | |
| for group in SECTIONS: | |
| cards, opens = in_group(boards, group), open_in_group(boards, group) | |
| if not cards and not opens: | |
| continue | |
| note = escape(GROUP_NOTE.get(group, "")) | |
| counter = f" · +{len(opens)} open" if opens else "" | |
| out.append( | |
| '<div class="pm-group"><div class="pm-group-head">' | |
| f'<p class="pm-over">{escape(group)}</p>' | |
| f'<p class="pm-note">{note}{counter}</p></div><div class="pm-grid">' | |
| ) | |
| out += [_live_card(b, df, by_id) for b in cards] | |
| out += [_open_card(b) for b in opens] | |
| out.append("</div></div>") | |
| out.append("</div>") | |
| return "".join(out) | |
| # ------------------------------------------------------------- html tables | |
| def _fmt_value(value, is_score: bool) -> str | None: | |
| """Display text for one cell; ``None`` marks a blank (``n/a``) cell.""" | |
| try: | |
| if pd.isna(value): | |
| return None | |
| except (TypeError, ValueError): | |
| pass | |
| if isinstance(value, str): | |
| return value | |
| if isinstance(value, numbers.Integral): | |
| return str(int(value)) | |
| if isinstance(value, numbers.Real): | |
| if not math.isfinite(float(value)): | |
| return None | |
| return f"{float(value):.3f}" if is_score else str(value) | |
| return str(value) | |
| def _model_cell_html(cell: ModelCell) -> str: | |
| """Render one model label, allowing only escaped HTTP(S) paper links.""" | |
| label = escape(str(cell)) | |
| parsed = urlparse(cell.paper_link) | |
| if parsed.scheme in {"http", "https"} and parsed.netloc: | |
| url = escape(cell.paper_link, quote=True) | |
| label = ( | |
| f'<a class="pm-model-link" href="{url}" target="_blank" ' | |
| f'rel="noopener noreferrer">{label}</a>' | |
| ) | |
| chip = ( | |
| ' <span class="pm-author-chip" title="Submitted by the model authors" ' | |
| 'aria-label="Submitted by the model authors">✓ Authors</span>' | |
| if cell.is_author_submission | |
| else "" | |
| ) | |
| return label + chip | |
| def _score_columns(df: pd.DataFrame) -> list[str]: | |
| """Numeric columns to format and bold -- ``Rank`` is an index, not a score.""" | |
| return [c for c in df.select_dtypes("number").columns if c not in RIGHT_COLUMNS] | |
| def _score_guide(col: str, row: pd.Series) -> str | None: | |
| """Return metric context for a native score cell, when identifiable.""" | |
| metric = next((name for name in METRIC_GUIDES if f"({name})" in col), None) | |
| if metric is None and col not in {"Elo", "Mean rank", "Mean score"}: | |
| metric = str(row.get("Metric", "")) | |
| return METRIC_GUIDES.get(metric) if metric is not None else None | |
| def _bold_cells(df: pd.DataFrame, score_cols: list[str], axis: int) -> set: | |
| """Which ``(row, col)`` cells hold the best value. | |
| ``axis=0`` bolds the best model per column (the ranked table, read down); | |
| ``axis=1`` bolds the best model per row (the per-task table, read across). | |
| """ | |
| bold = set() | |
| if not score_cols: | |
| return bold | |
| if axis == 0: | |
| for col in score_cols: | |
| best = df[col].max(skipna=True) | |
| if pd.notna(best): | |
| for idx, value in df[col].items(): | |
| if pd.notna(value) and value == best: | |
| bold.add((idx, col)) | |
| else: | |
| for idx, row in df.iterrows(): | |
| present = {c: row[c] for c in score_cols if pd.notna(row[c])} | |
| if present: | |
| best = max(present.values()) | |
| bold.update((idx, c) for c, v in present.items() if v == best) | |
| return bold | |
| def _cell_class(col: str, is_score: bool, blank: bool) -> str: | |
| if col in NAME_COLUMNS: | |
| return "pm-name" | |
| if col in STRONG_COLUMNS: | |
| return "pm-strong" | |
| if col in METRIC_COLUMNS: | |
| return "pm-metric" | |
| if is_score or col in RIGHT_COLUMNS: | |
| return "pm-num pm-num--dim" if blank else "pm-num" | |
| return "" | |
| def _task_tooltip(task: dict) -> str: | |
| """Build the expanded clinical context shown for a task name.""" | |
| description = str(task.get("description") or "").strip().rstrip(".") | |
| diseases = ( | |
| ", ".join(str(d) for d in task.get("diseases") or []) or "the listed cohort" | |
| ) | |
| patients = task.get("n_subjects") or task.get("n_samples") | |
| patient_text = ( | |
| f"{patients:,} patients" | |
| if isinstance(patients, int) | |
| else "an unspecified number of patients" | |
| ) | |
| n_samples = task.get("n_samples") | |
| if ( | |
| isinstance(n_samples, int) | |
| and isinstance(patients, int) | |
| and n_samples != patients | |
| ): | |
| patient_text += f" ({n_samples:,} collection samples)" | |
| modality = str(task.get("modality") or "omics") | |
| tissue = str(task.get("tissue") or "unspecified tissue") | |
| target = str(task.get("target") or task.get("title") or "the task target") | |
| return ( | |
| f"{description}. Patients: {patient_text} with {diseases}; " | |
| f"input data: {modality} profiles from {tissue.lower()} tissue; " | |
| f"outcome: {target}." | |
| ) | |
| def _df_to_table( | |
| df: pd.DataFrame, | |
| bold_axis: int | None, | |
| empty: str, | |
| task_guides: dict[str, dict] | None = None, | |
| ) -> str: | |
| """Render a DataFrame as a ``pm-table``, escaping every header and cell.""" | |
| if df.empty: | |
| return f'<p class="pm-caption">{escape(empty)}</p>' | |
| score_cols = _score_columns(df) | |
| bold = _bold_cells(df, score_cols, bold_axis) if bold_axis is not None else set() | |
| head = [] | |
| for index, col in enumerate(df.columns): | |
| align = ( | |
| ' style="text-align:right"' | |
| if col in score_cols or col in RIGHT_COLUMNS | |
| else "" | |
| ) | |
| head.append( | |
| f'<th class="pm-sort" data-sort-index="{index}" tabindex="0" ' | |
| f'role="button" aria-sort="none" title="Sort by {escape(str(col))}"' | |
| f"{align}>{escape(str(col))}</th>" | |
| ) | |
| body = [] | |
| for idx, row in df.iterrows(): | |
| cells = [] | |
| for col in df.columns: | |
| is_score = col in score_cols | |
| text = _fmt_value(row[col], is_score) | |
| cls = _cell_class(col, is_score, text is None) | |
| weight = ' style="font-weight:700"' if (idx, col) in bold else "" | |
| guide = ( | |
| _score_guide(str(col), row) if is_score and text is not None else None | |
| ) | |
| shown = ( | |
| _model_cell_html(row[col]) | |
| if isinstance(row[col], ModelCell) | |
| else "n/a" | |
| if text is None | |
| else escape(text) | |
| ) | |
| if guide: | |
| escaped_guide = escape(guide, quote=True) | |
| score_label = escape(f"Score {text}. {guide}", quote=True) | |
| shown = ( | |
| '<span class="pm-score-guide" tabindex="0" ' | |
| f'aria-label="{score_label}" ' | |
| f'data-tooltip="{escaped_guide}">{shown}</span>' | |
| ) | |
| if task_guides and col == "Task" and text is not None: | |
| task = next( | |
| ( | |
| candidate | |
| for candidate in task_guides.values() | |
| if candidate.get("title") == text | |
| ), | |
| None, | |
| ) | |
| if task: | |
| tooltip = _task_tooltip(task) | |
| escaped_tooltip = escape(tooltip, quote=True) | |
| task_label = escape(f"{text}. {tooltip}", quote=True) | |
| shown = ( | |
| '<span class="pm-task-guide" tabindex="0" ' | |
| f'aria-label="{task_label}" data-tooltip="{escaped_tooltip}">{shown}</span>' | |
| ) | |
| cells.append(f'<td class="{cls}"{weight}>{shown}</td>') | |
| body.append(f"<tr>{''.join(cells)}</tr>") | |
| return ( | |
| '<div class="pm-table-wrap"><table class="pm-table" style="table-layout:auto">' | |
| f"<thead><tr>{''.join(head)}</tr></thead>" | |
| f"<tbody>{''.join(body)}</tbody></table></div>" | |
| ) | |
| # ------------------------------------------------------------------- board | |
| def _board_meta(board: Board) -> str: | |
| return ( | |
| f"{escape(board.blurb)} · {board.n_tasks} tasks · {board.n_cohorts} cohorts · " | |
| f"{board.n_patients:,} patients · {escape(modality_label(board.modality))}" | |
| ) | |
| def render_board(board: Board | None, df: pd.DataFrame, by_id: dict[str, dict]) -> str: | |
| """One board: title strip, the ranked leaderboard, then the per-task table.""" | |
| if board is None: | |
| return ( | |
| '<div class="pm-head"><div><h1>No board available</h1>' | |
| "<p>The task registry could not be loaded. Please retry shortly.</p>" | |
| "</div></div>" | |
| ) | |
| ranked = _df_to_table( | |
| ranked_table(df, by_id, board), | |
| bold_axis=0, | |
| empty="No model has covered every scored task of this board yet. Be the first to submit.", | |
| ) | |
| per_task = _df_to_table( | |
| per_task_table(df, by_id, board), | |
| bold_axis=1, | |
| empty="No submission has scored on this board yet.", | |
| task_guides=by_id, | |
| ) | |
| return ( | |
| '<div class="pm-head"><div><p class="pm-over">Board</p>' | |
| f"<h1>{escape(board_label(board))}</h1>" | |
| f"<p>{_board_meta(board)}</p></div></div>" | |
| '<div class="pm-body">' | |
| '<div class="pm-group"><div class="pm-group-head">' | |
| '<p class="pm-over pm-over--marine">Ranked</p>' | |
| '<p class="pm-note">We evaluate zero-shot representations of models, so ' | |
| "performance should not be considered as the best we can obtain. Only " | |
| "models that covered every task scored by the " | |
| "<code>HVG-1200-genes</code> baseline are ranked. Elo compares models " | |
| "pairwise within each task and never compares AUROC, Pearson and " | |
| "centered Spearman directly; the baseline holds 1000 ELO.</p>" | |
| f"</div>{ranked}</div>" | |
| '<div class="pm-group"><div class="pm-group-head">' | |
| '<p class="pm-over pm-over--marine">Per task</p>' | |
| '<p class="pm-note">Every submission, partial ones included. Read across a row.</p>' | |
| f"</div>{per_task}</div>" | |
| "</div>" | |
| ) | |
| # ------------------------------------------------------------------- tasks | |
| def render_tasks(by_id: dict[str, dict]) -> str: | |
| """The Tasks page: one scannable row per hidden target. Provenance is never shown.""" | |
| df = tasks_table(list(by_id.values())) | |
| table = _df_to_table( | |
| df, | |
| bold_axis=None, | |
| empty="The task registry is unavailable right now.", | |
| task_guides=by_id, | |
| ) | |
| return ( | |
| '<div class="pm-head"><div><h1>Tasks</h1>' | |
| f"<p>{len(df)} hidden targets. One fixed task probe reads each " | |
| "one out of your embedding; the cohorts stay anonymous, the biology does " | |
| "not.</p></div></div>" | |
| f'<div class="pm-body">{table}</div>' | |
| ) | |