// Condition-aware model ranking (client-side mirror of backend/ranking.py). // // Given the conditions a user's peptides were generated under, species, enzyme, // MS protocol, rank the models by how similar their training conditions were. // Concise, generalizable rule: species dominates, then enzyme, then protocol, each // only when supplied and matched. Weighted score (species 100 > enzyme 10 > protocol // 1) so the order is exactly "same species first; among those same enzyme; among // those same protocol"; ties broken by the model's own AUROC. import { displayAurocValue, type RealModel } from '../data/models' export interface RankedModel extends RealModel { rankScore: number speciesMatch: boolean enzymeMatch: boolean protocolMatch: boolean } const norm = (x: string | null | undefined): string => (x ?? '').trim().toLowerCase() // Loose equality: exact, or one contains the other (handles "trypsin" vs "trypsin // then flavourzyme", "nano-LC-MS/MS" vs "LC-MS/MS"). function match(a: string, b: string): boolean { return !!a && !!b && (a === b || a.includes(b) || b.includes(a)) } export interface Conditions { species?: string enzyme?: string protocol?: string } export function rankModels(models: RealModel[], c: Conditions): RankedModel[] { const s = norm(c.species) const e = norm(c.enzyme) const p = norm(c.protocol) return models .map((m) => { const sp = match(s, norm(m.speciesKey) || norm(m.species)) const en = match(e, norm(m.enzyme)) const pr = match(p, norm(m.msProtocol)) return { ...m, rankScore: 100 * (sp ? 1 : 0) + 10 * (en ? 1 : 0) + 1 * (pr ? 1 : 0), speciesMatch: sp, enzymeMatch: en, protocolMatch: pr, } }) .sort( (a, b) => b.rankScore - a.rankScore || (displayAurocValue(b) ?? 0) - (displayAurocValue(a) ?? 0), ) } // Distinct, sorted option lists for the condition selectors (built from the models). export function distinctEnzymes(models: RealModel[]): string[] { return [...new Set(models.map((m) => m.enzyme).filter(Boolean))].sort() } export function distinctProtocols(models: RealModel[]): string[] { return [...new Set(models.map((m) => m.msProtocol).filter(Boolean))].sort() } export function distinctSpecies(models: RealModel[]): { key: string; label: string }[] { const seen = new Map() for (const m of models) if (!seen.has(m.speciesKey)) seen.set(m.speciesKey, m.species) return [...seen.entries()] .map(([key, label]) => ({ key, label })) .sort((a, b) => a.label.localeCompare(b.label)) }