Edit on GitHub

starplast.organisms

The organism registry: one declaration per species space, instead of literals in fifty files.

Starplast grew from one organism to two by copying: slots.SPECIES_TABLES, app.SPECIES, strategies.Context.shipped, the calibration targets, the slot generator's stages and the localization abbreviations each carry their own "Tg"/"Pf" table. Adding the next species -- a host, a vector, another apicomplexan -- would have meant editing every one of them, and missing one fails silently (several lookups default to Toxoplasma). This module is the one place a space is declared; everything else is to read from it (instruction 53, step R0 onward).

A space is one species' own gene table, graph, slots and calibration. Spaces are never merged: orthology and host-pathogen interfaces are bridges between them, declared per space.

HOST_TABLES = {'Hs': 'hs_host_proteins.parquet', 'Mm': 'mm_host_proteins.parquet'}
@dataclass(frozen=True)
class Space:

One species' space: what it is, how its genes are named, and where its tables live.

Space( code: str, species: str, reference: str, kind: str, gene_regex: str, prefixes: tuple, database: str, record_url: str, nodes: str, graph: str = '', host_bridges: str = '', partner: str = '', orthomcl: str = '', contexts: frozenset = frozenset(), targets: tuple = (), numbers: tuple = (), hosts: tuple = (), vectors: tuple = (), distribution: str = 'wheel')
code: str
species: str
reference: str
kind: str
gene_regex: str
prefixes: tuple
database: str
record_url: str
nodes: str
graph: str = ''
host_bridges: str = ''
partner: str = ''
orthomcl: str = ''
contexts: frozenset = frozenset()
targets: tuple = ()
numbers: tuple = ()
hosts: tuple = ()
vectors: tuple = ()
distribution: str = 'wheel'
def record(self, gene_id: str) -> str:

The URL of one gene's record in its genome database.

def matches(self, gene_id: str) -> bool:

Whether gene_id is a canonical id of this space.

SPACES: dict = {'Tg': Space(code='Tg', species='Toxoplasma gondii', reference='ME49', kind='parasite', gene_regex='TGME49_\\d{6}', prefixes=('TGME49_', 'TGGT1_'), database='ToxoDB', record_url='https://toxodb.org/toxo/app/record/gene/{id}', nodes='nodes.parquet', graph='graph.npz', host_bridges='host_bridges.parquet', partner='Pf', orthomcl='tgon', contexts=frozenset({'enteric', 'oocyst', 'sporozoite', 'tachyzoite', 'sexual', 'gametocyte', 'bradyzoite', 'merozoite'}), targets=('compartment', 'lopit_unified', 'dtm_class', 'screen_any_phenotype', 'stage_enriched_derived'), numbers=('fit_invitro_hff', 'fit_invivo_PE', 'expr_tachy'), hosts=(), vectors=(), distribution='wheel'), 'Pf': Space(code='Pf', species='Plasmodium falciparum', reference='3D7', kind='parasite', gene_regex='PF3D7_(?:\\d{7}|API\\d{5}|MIT\\d{5})', prefixes=('PF3D7_',), database='PlasmoDB', record_url='https://plasmodb.org/plasmo/app/record/gene/{id}', nodes='pf_nodes.parquet', graph='pf_graph.npz', host_bridges='pf_host_bridges.parquet', partner='Tg', orthomcl='pfal', contexts=frozenset({'liver stage', 'trophozoite', 'oocyst', 'ring', 'sporozoite', 'sexual', 'schizont', 'gametocyte', 'merozoite', 'asexual blood stage', 'ookinete'}), targets=('lopit_pf_location', 'pb_transferred_phenotype', 'stage_enriched_derived', 'is_exported'), numbers=('piggybac_mis', 'expr_schizont', 'mean_plddt'), hosts=(), vectors=(), distribution='wheel')}
def register(space: Space) -> Space:

Add a space. A code used twice, or a kind outside parasite/host/vector, is refused.

def get(code: str) -> Space:

One space by code, or a KeyError naming the ones that exist.

def codes(kind: str | None = None, available: bool = False) -> list:

Space codes in registry order, optionally of one kind or only those whose table is present.

def by_species(name: str) -> Space:

The space whose species is name (as the menus show it).

def nodes_path(code: str) -> str:

Where a space's node table lives (honouring STARPLAST_CACHE, as every table does).

def graph_path(code: str) -> str:

Where a space's edge layers live.

def table_map() -> dict:

code -> node table file: what slots.SPECIES_TABLES holds.

def prefix_map() -> dict:

code -> accession prefixes: what slots.SPECIES_PREFIXES holds.

def detect(gene_ids, sample: int = 500) -> str | None:

The space whose canonical ids MOST of gene_ids fully match; None if none reaches half.

A majority of full matches rather than the first prefix that appears anywhere: one stray accession from another species must not decide what a whole table is.