Edit on GitHub

starplast.help_index

Everything in Starplast that can be reached by name, and how a query ranks it.

The search field beside the Help menu is only as good as this index: a user who knows what a thing is CALLED -- a strategy, a slot, a dataset, a menu command, a tab, a setting, a section of the guide -- should reach it without knowing which menu, panel or document it was filed under. The ranking is spaCR's (spacr.qt.help_index), so the two programs answer a query the same way.

THE INDEX IS NEVER A HAND LIST. Every row is derived from the thing that already decides the answer:

============== =========================================================================== kind where the rows come from ============== =========================================================================== action every command in the window's menu bar, with its tooltip and status tip panel every dock, every tab of the analysis, strategy and preference panels, the guided workflows and the slot tree setting every labelled control in the analysis panel and in Preferences, and every display choice the map's right-click menu offers strategy starplast.strategies.catalog -- key, number, name, method, family, question slot starplast.slots.all_slots -- the information categories of both organisms dataset starplast.datasets.REGISTRY doc the headings of docs/*.md in a source checkout, or the published pages tutorial the pages and sections of docs/tutorial ============== ===========================================================================

A command that cannot be found is then a fact about a registry, not about whether somebody remembered to add a row.

HEADLESS. Importing this module imports no Qt. The providers that read the window (action, panel, setting) are handed the window and walk its widgets, which works offscreen; the rest read files and registries and need no application at all, so the ranking and the static rows are tested without one. What a result DOES when it is chosen is starplast.help_search's business.

KIND_ORDER = ('action', 'panel', 'strategy', 'setting', 'slot', 'dataset', 'doc', 'tutorial')
KIND_LABEL = {'action': 'command', 'panel': 'panel', 'strategy': 'strategy', 'setting': 'setting', 'slot': 'slot', 'dataset': 'dataset', 'doc': 'guide', 'tutorial': 'tutorial'}
DOCS_DIR = '/home/runner/work/starplast/starplast/docs'
SITE = 'https://einarolafsson.github.io/starplast/'
REPO = 'https://github.com/EinarOlafsson/starplast/blob/main/'
PUBLISHED = {'index': 'Starplast documentation', 'guide': 'User guide', 'API': 'Python API', 'datasets': 'Dataset catalogue', 'structures': 'Protein structures', 'protein-sequences': 'Protein sequences', 'releases': 'Releases', 'workflows': 'Guided workflows', 'benchmark-0.43': 'Benchmark 0.43', 'repository-review': 'Repository review', 'scientific-roadmap': 'Scientific roadmap', 'calibration': 'Strategy calibration', 'graphspace': 'Integrated neighbour space', 'strategies': 'Strategy catalogue'}
@dataclass(frozen=True)
class HelpEntry:

One reachable thing, and what it takes to get back to it.

:ivar kind: which provider produced it, and so which opener acts on it; one of KIND_ORDER. :ivar title: what a user would type -- a command's label, a strategy's name, a slot's name. :ivar subtitle: where it lives, as a path: "View ▸ Color by", "Strategies ▸ Networks". :ivar description: what it does, in words. Matched too, which is what lets "density" find the additive overlap mode and "HDBSCAN" find every strategy that clusters with it. :ivar payload: what the opener needs and nothing more, as sorted (name, value) pairs so the entry stays hashable. Read it with data.

HelpEntry( kind: str, title: str, subtitle: str = '', description: str = '', payload: tuple = ())
kind: str
title: str
subtitle: str = ''
description: str = ''
payload: tuple = ()
data: dict

The payload as a dict.

haystack: str

Title, subtitle and description, lowercased, for matching.

def entry( kind: str, title: str, subtitle: str = '', description: str = '', **payload) -> HelpEntry:

Build an entry with a payload given as keywords.

def plain(text: str) -> str:

Tooltip text without its markup: the wrapped tooltips are HTML with padded lines.

def clean_label(text: str) -> str:

A menu label as a person reads it: no mnemonic ampersand, no trailing ellipsis.

def term_score(term: str, e: HelpEntry):

How well one query term matches one entry, or None for no match.

Five bands, and the order between them is the whole ranking: the name typed in full beats a word of it, which beats the start of a word, which beats a fuzzy spelling, which beats a word from the description. A description hit must never outrank a name hit, or typing a command's own label buries it under every tooltip that mentions it.

def score(e: HelpEntry, terms) -> float | None:

The entry's score for a whole query, or None when any term misses: terms are ANDed, so a second word narrows the list rather than widening it.

PER_KIND_LIMIT = 6
@lru_cache(maxsize=1)
def strategy_entries() -> tuple:

One row per strategy: its number and name, found by key, method, family and question.

def guided_entries() -> tuple:

The Start-here tab: its own row, one per question it asks and one per goal it offers.

Derived from starplast.guided, so a question or a goal that is added is findable by name without anybody remembering to write a row for it. Every one opens the same dock, which is where the answering happens.

@lru_cache(maxsize=1)
def slot_entries() -> tuple:

One row per slot of both organisms, found by its axis, context and three addresses.

@lru_cache(maxsize=1)
def dataset_entries() -> tuple:

One row per registered dataset, found by what it provides, its citation and its key.

@lru_cache(maxsize=4)
def doc_entries(docs_dir: str = '/home/runner/work/starplast/starplast/docs') -> tuple:

Every heading of every page in the docs directory, or the published pages without one.

A heading is the unit a reader asks for -- "leakage audit", "held-out recovery" -- and a page is too coarse to land on. Code blocks are skipped: a # comment inside one is not a heading.

@lru_cache(maxsize=4)
def tutorial_entries(docs_dir: str = '/home/runner/work/starplast/starplast/docs') -> tuple:

Each tutorial page, and each numbered section of the complete guide.

def static_entries() -> list:

Every row that does not need a window: strategies, slots, datasets, docs and tutorials.

Each provider is guarded on its own: a search field that loses its strategies because the slot catalogue could not be read is worse than one that quietly offers fewer kinds.

def action_entries(window) -> list:

Every command in the menu bar, including submenus, addressed by its menu path.

The panels' own show/hide toggles are left to panel_entries, which raises a panel rather than toggling it -- a search for "console" that hides the console because it was already open would be a search that does the opposite of what was asked.

DOCK_HELP = {'find': 'Find a gene by accession or product, colour the map, and tick the classes to show.', 'evidence': 'Everything recorded about the selected gene, with links to its source records.', 'analysis': 'Data, maps, clusters, inference, search, validation, discovery and questions.', 'strategies': 'Named ways of inferring something, each with a guide, settings and a self-test.', 'start here': 'One question at a time, from what you have to the strategies worth running.', 'gallery': 'Thumbnails of every map a walk or search built; click one to show it.', 'console': 'Everything the program has printed, including errors from background jobs.', 'jobs': 'Every job started this session, with stop buttons and memory use.', 'assistant': 'Ask questions about the map in front of you.'}
def panel_entries(window) -> list:

Every dock, and every tab inside the analysis, strategy, preference and workflow panels.

def setting_entries(window) -> list:

Every labelled control in the analysis panel and Preferences, and every display choice.

A control is addressed by its tab and its label, and found again from them when it is opened, so the index holds no reference to a widget that may since have been rebuilt.

def window_entries(window) -> list:

Every row that is read off a live window: commands, panels and settings.

def build_index(window=None) -> list:

The whole index: the window's rows when a window is given, and every static row.

Rows that would read identically in the list -- the same heading twice on one page, one per organism -- are kept once, since two rows nobody can tell apart are one row and a wasted seat.