MetaSubsetTabController¶
class MetaSubsetTabController()
Bases: MetaController
Shared base for the Controllers of the two database-backed analysis tabs.
MetadataController and ProteinController both drive a tab whose data comes from a MetaDatabaseLoader: the user picks an experiment and channels, builds a subset filter, and plots or exports the rows that come back. The two were written by copy-paste and carried seventeen methods verbatim between them; this base holds that shared half so there is one copy to fix.
What a subclass inherits:
Experiment and column state. request_column_names and request_experiment_structure ask the loader and hand its answer to the View, which is the Model-to-View half of the mediation this layer exists for.
Filter validation. validate_filter and validate_raw_filter answer the View’s two validation requests by calling the loader directly, so a filter the database refuses is reported through _refuse_filter rather than vanishing. relay_query receives the query the loader built - or the debug message explaining why it could not - and commits, renames or refuses the pending filter accordingly.
Session state. get_session_state and restore_session_state override MetaController’s hooks so a tab’s subset filters survive a save and reload.
What a subclass owes it:
Its own logger = logging.getLogger(__name__), so that records made by the methods it defines itself stay attributed to its own module. Methods defined here log under this module, which is the same convention MetaView and MetaController already follow for their shared code.
self.view and self.model, built in its _init() as MetaController requires.
- ivar logger:
the module logger the shared methods below log under
Public Methods¶
Abstract Methods¶
These methods must be implemented by subclasses.
(none)
Concrete Methods¶
- MetaSubsetTabController.filter_scatterplot(columns: Sequence[ndarray[tuple[int, ...], dtype[float64]]], log_flags: Sequence[bool], ax: Axes, axis_labels: Sequence[str], dataset_label: str) None¶
Filter and log-scale a scatterplot’s columns, and hand them back to draw.
The View asks, this slot calls the Model, and the answer goes back through a setter. Shared by both subset tabs, whose copies were identical: the drawing context arrives and departs unchanged, so this marshals and does not interpret it.
- Parameters:
- Returns:
None
- Return type:
None
- MetaSubsetTabController.get_session_state() Dict[str, Any]¶
Include the view’s live subset filters in this tab’s session history entry.
- Returns:
Extra state to serialize into this tab’s session history entry.
- Return type:
Dict[str, Any]
- MetaSubsetTabController.load_event_id_cache(loader: str, conditions: str | None, experiments_and_channels: Dict[str, List[int] | None] | None) None¶
Fetch the
event_idvalues a subset holds, for the navigation cache.Shared by both subset tabs because
_rebuild_event_id_cacheis. A failed query leaves the View’s answer atNone, which is what lets the caller tell “that did not run” from “the subset is empty” - leaving the previous call’s rows in place would make the two indistinguishable and rebuild the cache from them.
- MetaSubsetTabController.load_event_plot_data(loader: str, event_ids: List[int], exp: str | None, channel: int | None, experiments_and_channels: Dict[str, List[int | None]] | None, action_label: str) None¶
Resolve
event_idvalues to database ids within scope and load those rows.The whole chain runs here because only one place can run it end to end and say which part failed. It was three separate requests once, each answer parked on a View attribute and read back on the next statement; nothing outside the chain ever read the intermediate answers.
An experiment that does not resolve stops the plot rather than widening the query.
event_idis unique only within an experiment and channel, so an unscoped match returns whichever channel’s row happens to share the number - which is the “plots the wrong subset” fault, not a harmless one.action_labelnames what the caller is plotting - events, histograms - and appears only in the messages, which is the whole of what the two tabs’ copies of this method used to differ by.- Parameters:
loader (str) – the database loader plugin’s key
event_ids (List[int]) – the
event_idvalues to plot, already snapped to the cacheexp (Optional[str]) – the experiment the events belong to, or None to leave it out of scope
channel (Optional[int]) – the channel the events belong to, or None for all channels
experiments_and_channels (Optional[Dict[str, List[Optional[int]]]]) – the scope handed on to
load_event_dataaction_label (str) – what the caller is plotting, for its messages
- Returns:
None
- Return type:
None
- MetaSubsetTabController.load_filters(path: str, loader: str) None¶
Read a saved filter file and hand its contents to the View.
A file that cannot be read, or that does not hold a JSON object, is reported here and nothing is handed back - so the tab’s existing filters are left exactly as they were rather than half replaced.
- MetaSubsetTabController.relay_query(query: str, debug: str, table_name: str, intent: str | None = None, name: str | None = None, old_name: str | None = None, filter_text: str | None = None) None¶
Relay a query and optional debug message to the view, handling optional filter intents.
One copy for both subset tabs, and it reaches into no View state:
name,old_nameandfilter_textarrive as arguments, carried from the request that asked for the validation, and committing the result goes throughview.commit_filterrather than an assignment into the View’s dict. Both matter for the same reason - a value held on the widget between asking and being answered is one the next request can read by mistake.- Parameters:
query (str) – SQL query string to display or execute.
debug (str) – Debug message to display if query is empty.
table_name (str) – Name of the table associated with the query.
intent (Optional[str]) – ‘validate_new_filter’, ‘validate_edited_filter’, or None when the query is only being displayed
name (Optional[str]) – the name to commit the filter under, carried from the request
old_name (Optional[str]) – on an edit, the name being replaced; None when adding
filter_text (Optional[str]) – the expression to commit under
name, as the user wrote it; the query built around it is displayed, not stored
- Returns:
None
- Return type:
None
- MetaSubsetTabController.request_column_names(loader: str) None¶
Fetch a loader’s column names and hand them to the View.
The same shape as the reader and loader channel look-ups, against
MetaDatabaseLoader. An empty answer is logged rather than pushed: clearing the axis comboboxes would read as “this database has no columns” when what happened is that nobody could ask it.- Parameters:
loader (str) – the database loader plugin’s key
- Returns:
None
- Return type:
None
- MetaSubsetTabController.request_experiment_structure(loader_name: str) None¶
Fetch a loader’s experiment-and-channel structure and file it under its key.
The loader key was the bus’s
ret_argshere: the answer has to be filed under the loader it came from, and passing it forward explicitly is what replaces that. Channels are stringified for display, as they were.- Parameters:
loader_name (str) – the database loader plugin’s key
- Returns:
None
- Return type:
None
- MetaSubsetTabController.restore_session_state(state: Dict[str, Any]) None¶
Restore subset filters captured by
get_session_state()onto the view.- Parameters:
state (Dict[str, Any]) – This tab’s session history entry, as previously written by
get_session_state().
- MetaSubsetTabController.save_filters(path: str, filters: Dict[str, str]) None¶
Write the tab’s filters to the file the user chose.
A failure used to reach the log only, so a full disk or a read-only folder looked exactly like a successful save.
- MetaSubsetTabController.validate_filter(loader: str, filter_text: str, intent: str, name: str, old_name: str | None) None¶
Validate an assisted subset filter by asking the loader to build a query.
A filter counts as valid if
construct_metadata_querycan build a query around it; the query itself is thrown away. The call is made here rather than from the widget becauseconstruct_metadata_queryraises for a column it cannot map to a table, and a filter naming a column the database does not have has to reach the user rather than a log line.The columns are resolved here rather than passed in by the View, and only
eventscolumns are asked for. The View used to hand over a hardcoded["sublevel_current", "voltage", "duration"]- one column from each of the three tables, which forced the built query to join all three every time, even for a filter with no conditions at all. One events column yields exactly the joins the filter itself needs: none forduration < 300, one for a filter that really does reference sublevels or experiments.- Parameters:
loader (str) – the database loader plugin’s key
filter_text (str) – the filter expression to validate, without WHERE
intent (str) –
validate_new_filterorvalidate_edited_filter, passed through torelay_queryto say what to do with the answername (str) – the name to commit the filter under, carried from the request
old_name (Optional[str]) – on an edit, the name being replaced; None when adding
- Returns:
None
- Return type:
None
- MetaSubsetTabController.validate_raw_filter(loader: str, query: str, name: str, old_name: str | None) None¶
Validate a raw subset filter, which the loader checks without building.
A raw filter is a complete SELECT the loader runs verbatim, so it is checked with
validate_filter_queryrather than by constructing a query around it. As on the assisted path, the call is made here so that a failure reaches the user rather than a log line.LIMIT 0is appended here rather than by the View. The clause is what makes the check cheap - the database parses and plans the filter without returning a row - and choosing it is knowing SQL, which a widget has no business doing.- Parameters:
- Returns:
None
- Return type:
None
Private Methods¶
Abstract Methods¶
These methods must be implemented by subclasses.
(none)