autowisp.browser_interface.diagnostics.series_table module

Class Inheritance Diagram

Inheritance diagram of Camera, CameraChannel, DiagnosticType, ImageDiagnostics, ObservingSession, PipelineError, SeriesKey

What the table above a diagnostics plot offers, and what it binds.

The half of the plot page that knows what a row means: which series can be drawn at all, which channel each column of a row may be bound to, how many images a binding would draw, and what a row is called while the user chooses. It never evaluates an expression and never draws anything – image_diagnostics_views does both – which is not merely a tidy division. There is a row per observing session and image type, so evaluating one to fill the table would be work proportional to the whole image collection; every question asked here is answered by a SQL aggregate instead.

autowisp.browser_interface.diagnostics.series_table.build_group_rows(quantile_name, slot_needs, session_channels, db_session)[source]

Return one row per (session, image type) the axes can be drawn for.

A group is offered where every slot has at least one channel to choose from; the choosing then happens in the table, and nothing arrives bound. Pre-binding instead would mean enumerating a cartesian product of the channels across the axes’ parameters.

The exception is a camera defining a single channel – a monochrome one, zero not being a working configuration – where there is nothing to choose. Demanding a click with one possible outcome before anything can be drawn is ceremony, so those rows arrive bound and counted.

Parameters:
  • quantile_name (str) – Which pixel_q* these rows stand for, when an axis was selected as the pixel_quantiles family and the table expanded it into one group of rows per recorded member. None otherwise – including for an expression naming concrete quantiles, such as pixel_q99[0] / pixel_q50[0], which is an ordinary quantity reading two diagnostics and expands into nothing.

  • slot_needs (list) – What each slot reads, in column order.

  • session_channels (dict) – What get_session_channels() returned.

  • db_session – An active SQLAlchemy database session.

Returns:

Series entries, as make_series() builds them.

Return type:

list

autowisp.browser_interface.diagnostics.series_table.count_bound_images(slot_needs, channels, db_session)[source]

Count the images one binding draws on, for every group at once.

The exact question, where get_slot_options() answers the looser one that fills the dropdowns: an image counts when its rows cover every (diagnostic, channel) pair between them, which is what a quantity comparing channels needs and what reading each channel on its own cannot say.

Every group is counted in one aggregate rather than one per row, since a binding is usually shared – by every row of a monochrome project at render, and by nothing much afterwards, when one row is rebound at a time.

Parameters:
  • slot_needs (list) – What each slot reads, in column order.

  • channels (tuple) – The channel bound in each of those slots.

  • db_session – An active SQLAlchemy database session.

Returns:

{(session_id, image_type): count}, holding only the

groups with anything to draw.

Return type:

dict

autowisp.browser_interface.diagnostics.series_table.get_available_diagnostics(recorded, expressions)[source]

Return every quantity an axis may be set to.

One flat list rather than diagnostics and expressions kept apart: an axis reads a name, and a recorded diagnostic is simply an expression of itself as far as anything downstream is concerned. Sharing one name space is what lets the selectors, the URL and the series table treat all of them alike, and it is why an expression may not take a diagnostic’s name.

Parameters:
Returns:

jd, then every recorded diagnostic – with the

individual quantiles standing down in favour of the family name that expands to one series per member – then the expressions this project has the data to draw.

Return type:

list

autowisp.browser_interface.diagnostics.series_table.get_available_expressions(expressions, recorded)[source]

Return the expressions this project has the data to draw.

Availability, not validity. Every stored expression is valid in every project – the vocabulary is the same everywhere, see autowisp.diagnostics.diagnostic_types – so filtering by check_expression() would filter nothing and offer all of them everywhere. What decides whether one is offered here is whether the diagnostics it reaches, transitively, have actually been recorded.

Parameters:
  • expressions (dict) – The library, {name: expression}.

  • recorded (list) – What get_recorded_diagnostics() found. The raw names, since an expression may reference a concrete pixel_q* rather than the family.

Returns:

The names whose every diagnostic is recorded here,

alphabetically.

Return type:

list

autowisp.browser_interface.diagnostics.series_table.get_available_series(x_diagnostic, y_diagnostic, expressions, db_session)[source]

Return the (session, image type, channel) series plottable for an axis pair.

The count is the number of images recording every diagnostic both axes need – for an expression, every diagnostic it reaches transitively. It is an upper bound on the number of drawn points, since arithmetic can still yield NaN, so the column is labelled for the inputs rather than for the points. Nothing is evaluated to produce it: the count is a question about rows, and stays a SQL aggregate.

Parameters:
  • x_diagnostic (str) – Quantity on the X axis.

  • y_diagnostic (str) – Quantity on the Y axis.

  • expressions (dict) – The library, {name: expression}, passed in rather than fetched so that nothing below the view has to know it came from the browser-interface database.

  • db_session – An active SQLAlchemy database session.

Returns:

diagnostics_fields and diagnostics_list, in the

format diagnostics_app.html expects.

Return type:

dict

Raises:

PipelineError – If an axis names nothing that resolves.

autowisp.browser_interface.diagnostics.series_table.get_axes_slot_needs(x_diagnostic, y_diagnostic, expressions, quantile_name)[source]

Return what each channel column of the table reads, in column order.

The x quantity’s slots followed by the y quantity’s, concatenated rather than merged: an expression’s numbers are formal parameters, so the two axes’ slots are unrelated even when written alike.

Parameters:
  • x_diagnostic (str) – Quantity on the X axis.

  • y_diagnostic (str) – Quantity on the Y axis.

  • expressions (dict) – The library, {name: expression}.

  • quantile_name (str) – The pixel_q* a row stands for, or None outside a quantile expansion.

Returns:

One frozenset of diagnostic names per column.

Return type:

list

autowisp.browser_interface.diagnostics.series_table.get_axis_slots(quantity, expressions)[source]

Return the diagnostics read in each channel one axis binds.

An axis binds one channel per parameter of the quantity it draws, and two axes never share one: their numbers are formal parameters, so the first slot of the x quantity and the first of the y quantity are unrelated, and tying them together would silently couple the axes.

Parameters:
  • quantity (str) – The concrete quantity the axis draws, the quantile family already resolved to a member.

  • expressions (dict) – The library, {name: expression}.

Returns:

One (parameter, needed) pair per channel to bind, in

column order: the slot number the definition writes, which names the column, and the diagnostics read in it. The parameter is None for a diagnostic, which has no numbering of its own. Empty for an axis over the time alone, which binds nothing.

Return type:

list

autowisp.browser_interface.diagnostics.series_table.get_binding_response(post_data, *, x_diagnostic, y_diagnostic, expressions, db_session)[source]

Return what a row that has just been fully bound earns.

Merged into the figure’s JSON response, so that choosing the last of a row’s channels costs one round trip rather than two. Nothing here re-renders the table: the client updates one cell and appends at most one row, which is what lets every other row keep its node, and with it what was typed into it and its place under whatever sort is in force.

Parameters:
  • post_data (dict) – The whole POST, holding every row’s state and bind, the id of the row whose channels were completed.

  • x_diagnostic (str) – Quantity on the X axis.

  • y_diagnostic (str) – Quantity on the Y axis.

  • expressions (dict) – The library, {name: expression}.

  • db_session – An active SQLAlchemy database session.

Returns:

bind echoed back with the row’s count and the

defaults that follow from its channels, plus spare_row where a further binding is still to be built. Empty when nothing was bound, which is every other redraw.

Return type:

dict

autowisp.browser_interface.diagnostics.series_table.get_quantile_names(db_session)[source]

Return the pixel_q* diagnostic names in use, quantile order.

autowisp.browser_interface.diagnostics.series_table.get_recorded_diagnostics(db_session)[source]

Return the DiagnosticType names anything has recorded in this project.

A per-type EXISTS probe rather than a GROUP BY over the whole of image_diagnostics: the question is only which names are in use, and the grouped form has to walk every row to answer it.

The names come back raw, individual pixel_q* entries included – before get_available_diagnostics() collapses them into the family name. That is what an expression has to be judged against, since one may reference a concrete quantile.

Parameters:

db_session – An active SQLAlchemy database session.

Returns:

The names in use, in DiagnosticType order.

Return type:

list

autowisp.browser_interface.diagnostics.series_table.get_series_key(series)[source]

Return what one posted row draws: its group, and what it binds.

Half of this comes from the row id and half from the client, and the split is not an inconsistency. Session, type and quantile are not editable in the table, so taking them from the id keeps one source of truth for them; the channels are edited there, and reading them from anywhere but the dropdown the user just changed would be reading a stale value.

Parameters:

series (dict) – One row as the client posted it back.

Returns:

What the row identifies, with empty channels where

its dropdowns are still unset.

Return type:

SeriesKey

autowisp.browser_interface.diagnostics.series_table.get_session_channels(db_session)[source]

Return the channels each observing session’s camera defines.

Not the channels it has recorded: a Bayer camera part way through processing has one channel with data and three still to come, and treating that one as the only possibility would be wrong by tomorrow. What the camera defines does not move.

Parameters:

db_session – An active SQLAlchemy database session.

Returns:

{session_id: [channel name, ...]}, in name order.

A session whose camera is not described is simply absent.

Return type:

dict

autowisp.browser_interface.diagnostics.series_table.get_slot_headings(axis, axis_name, slots)[source]

Return the column heading for each channel one axis binds.

Named for the axis as well as the quantity because the two axes may name one quantity – comparing a diagnostic between channels is exactly that – and two columns headed alike would say nothing. Where an axis binds several channels the heading is the reference as the definition writes it, so a column can be matched to the text it fills in.

Parameters:
  • axis (str) – Which axis these columns belong to.

  • axis_name (str) – The quantity as the selector names it, which for the quantile family is the family rather than a member: the table has one header for all of them.

  • slots (list) – What get_axis_slots() returned for the axis.

Returns:

One heading per channel to bind.

Return type:

list

autowisp.browser_interface.diagnostics.series_table.get_slot_options(slot_needs, db_session)[source]

Return what each slot of the table may be bound to, and the labels.

One aggregate per distinct set of diagnostics among the slots – usually one for the whole table, since the commonest axis pairs read the same diagnostics in every slot. Nothing is evaluated: which channels a slot may offer is a question about rows.

Parameters:
  • slot_needs (list) – What each slot reads, from get_axis_slots() for each axis in turn.

  • db_session – An active SQLAlchemy database session.

Returns:

dict: One entry per distinct set of needs, holding

{(session_id, image_type): {channel: count}}.

dict: {session_id: label}, the same whatever is read.

Return type:

tuple

autowisp.browser_interface.diagnostics.series_table.make_row_id(session_id, image_type, quantile_name, ordinal)[source]

Return the id of one row of the series table.

A row outlives the channels bound in it – binding one is an edit made in the row – so its id names the group it belongs to and its place among the rows of that group, and says nothing about channels. That is what lets the four element ids derived from it (plot-color:, marker-button:, scale:, label:) and the key the client posts it under stay put while the user chooses.

Parameters:
  • session_id (int) – The observing session.

  • image_type (str) – The frame type.

  • quantile_name (str) – The pixel_q* this row stands for, or None outside a quantile expansion.

  • ordinal (int) – Distinguishes the rows of one group, which differ only by what they bind.

Returns:

The opaque id, which has to survive a round trip through

the client unchanged.

Return type:

str

Raises:

ValueError – If a part contains the separator, which would make the id ambiguous. Worth failing on rather than trusting, since the alternative is a plot that silently draws the wrong rows.

autowisp.browser_interface.diagnostics.series_table.make_series(row_id, session_label, series_key, slots, count)[source]

Build the entry describing one row of the series table.

Parameters:
  • row_id (str) – What make_row_id() returned for this row.

  • session_label (str) – The label of the observing session.

  • series_key (SeriesKey) – What the row draws once bound. Its channels are empty for a row still to be bound.

  • slots (list) – One entry per channel the axes bind, as get_slot_options() describes them.

  • count (int) – The number of images contributing, or None where nothing is bound yet and there is nothing to count.

Returns:

A series entry with the keys expected by

diagnostics/_series_row.html and plot_image_diagnostic_series().

Return type:

dict

autowisp.browser_interface.diagnostics.series_table.plan_spare_row(row_id, slot_needs, options, labels, datasets)[source]

Return the entry for a fresh unbound row, or None for no spare.

Completing a row’s channels summons one below it, so that a second binding of the same series can be built and drawn on the same figure. Two cases earn none: a spare is already waiting – this row is not the last of its group, so it was rebound rather than newly bound – and a group whose every possible binding is already present, where a further row could only repeat one.

Kept apart from rendering it so that the decision can be tested without Django, which is where every other rule in this module is checked.

Parameters:
  • row_id (str) – The row that was just bound. What it binds does not matter here – a spare binds nothing – so only its group and its ordinal are read from it.

  • slot_needs (list) – What each channel column reads.

  • options (dict) – What get_slot_options() returned.

  • labels (dict) – The session labels from the same call.

  • datasets (dict) – Every row of the table as the client posted it.

Returns:

A series entry, as make_series() builds it.

Return type:

dict or None

autowisp.browser_interface.diagnostics.series_table.resolve_quantity(quantity_name, quantile_name)[source]

Map an axis name onto the concrete quantity for one series.

pixel_quantiles names a family rather than a quantity: each series picks one pixel_q* member of it, recorded in the series id. Resolving that here, once, is what lets everything downstream handle a single concrete name – leaving jd as the only quantity that still needs a branch anywhere, because it alone comes from the image table rather than from image_diagnostics.

Parameters:
  • quantity_name (str) – The name an axis was selected as.

  • quantile_name (str) – The pixel_q* this series stands for, or None outside a quantile expansion.

Returns:

The quantity to actually read.

Return type:

str

autowisp.browser_interface.diagnostics.series_table.row_id_separator = '|'

Separates the fields of a row id. Not the underscore an earlier encoding used: pixel_q* names contain those, so unpacking had to guess which underscores separated fields. Neither a session id, an image type nor a diagnostic name can contain this one.

autowisp.browser_interface.diagnostics.series_table.row_ordinal(row_id)[source]

Return which of its group’s rows this is, counting from zero.

autowisp.browser_interface.diagnostics.series_table.split_row_id(row_id)[source]

Return (session_id, image_type, quantile_name) from a row id.

The group a row belongs to, which is what almost everything wants: the ordinal only tells sibling rows apart, and is read separately by the one caller that needs it.