plexora 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plexora/__init__.py +129 -0
- plexora/__main__.py +15 -0
- plexora/_url.py +105 -0
- plexora/api/__init__.py +55 -0
- plexora/api/dataset.py +398 -0
- plexora/api/http.py +19 -0
- plexora/api/plugin.py +541 -0
- plexora/api/store.py +221 -0
- plexora/cli.py +777 -0
- plexora/client/dist/354_bundle.js +2 -0
- plexora/client/dist/354_bundle.js.LICENSE.txt +20 -0
- plexora/client/dist/418_bundle.js +2 -0
- plexora/client/dist/418_bundle.js.LICENSE.txt +1 -0
- plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +19 -0
- plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +18 -0
- plexora/client/dist/770_bundle.js +1 -0
- plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +23 -0
- plexora/client/dist/vendor_bundle.js +2 -0
- plexora/client/dist/vendor_bundle.js.LICENSE.txt +308 -0
- plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +28 -0
- plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +140 -0
- plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +501 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +17 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +116 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +9 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +562 -0
- plexora/client/src/css/import.css +474 -0
- plexora/client/src/css/main.css +903 -0
- plexora/client/src/css/openProject.css +363 -0
- plexora/client/src/css/quickView.css +166 -0
- plexora/client/src/css/tokens.css +51 -0
- plexora/client/src/css/viewer.css +1655 -0
- plexora/client/src/img/apple-touch-icon.png +0 -0
- plexora/client/src/img/favicon.ico +0 -0
- plexora/client/src/img/logo.ai +8892 -33
- plexora/client/src/img/logo.svg +1 -0
- plexora/client/src/img/logo_with_text.ai +9011 -34
- plexora/client/src/img/logo_with_text.svg +1 -0
- plexora/client/src/js/main.js +704 -0
- plexora/client/src/js/pluginRegistry.js +108 -0
- plexora/client/src/js/services/appStatus.js +286 -0
- plexora/client/src/js/services/browsePicker.js +73 -0
- plexora/client/src/js/services/dataLayer.js +400 -0
- plexora/client/src/js/services/datasetContext.js +143 -0
- plexora/client/src/js/services/glRenderer.js +143 -0
- plexora/client/src/js/services/importFormValidation.js +257 -0
- plexora/client/src/js/services/numericData.js +100 -0
- plexora/client/src/js/services/passVariablesToFrontend.js +20 -0
- plexora/client/src/js/services/simpleEventHandler.js +25 -0
- plexora/client/src/js/vendor.js +29 -0
- plexora/client/src/js/views/channelList.js +663 -0
- plexora/client/src/js/views/colorSwatchPicker.js +202 -0
- plexora/client/src/js/views/columnClassifier.js +146 -0
- plexora/client/src/js/views/coordinateField.js +133 -0
- plexora/client/src/js/views/dataSourceField.js +276 -0
- plexora/client/src/js/views/imageViewer.js +3306 -0
- plexora/client/src/js/views/miniMap.js +787 -0
- plexora/client/src/js/views/navbarControls.js +178 -0
- plexora/client/src/js/views/openProjectPage.js +239 -0
- plexora/client/src/js/views/projectEdit.js +335 -0
- plexora/client/src/js/views/quickViewLanding.js +136 -0
- plexora/client/src/js/views/rainbow.js +326 -0
- plexora/client/src/js/views/requirementsModal.js +572 -0
- plexora/client/src/js/views/rgbImageViewer.js +115 -0
- plexora/client/src/js/views/searchableSelect.js +411 -0
- plexora/client/src/js/views/segmentationProgress.js +122 -0
- plexora/client/src/js/views/toolLoader.js +747 -0
- plexora/client/src/js/views/viewerControls.js +689 -0
- plexora/client/src/js/views/viewerManager.js +434 -0
- plexora/client/src/js/views/viewerSidebar.js +1117 -0
- plexora/client/src/js/workers/tileDecoder.js +130 -0
- plexora/client/src/shaders/frag.glsl +528 -0
- plexora/client/src/shaders/vert.glsl +12 -0
- plexora/client/templates/base.html +212 -0
- plexora/client/templates/index.html +238 -0
- plexora/client/templates/open_project.html +96 -0
- plexora/client/templates/project_columns.html +86 -0
- plexora/client/templates/project_edit.html +196 -0
- plexora/client/templates/upload.html +118 -0
- plexora/connect.py +528 -0
- plexora/datasource.py +782 -0
- plexora/jupyter.py +406 -0
- plexora/notebook_env.py +232 -0
- plexora/paths.py +486 -0
- plexora/plugins/__init__.py +12 -0
- plexora/plugins/cell_explorer/__init__.py +100 -0
- plexora/plugins/cell_explorer/server/__init__.py +11 -0
- plexora/plugins/cell_explorer/server/routes.py +167 -0
- plexora/plugins/cell_explorer/server/state.py +281 -0
- plexora/plugins/cell_explorer/server/values.py +145 -0
- plexora/plugins/cell_explorer/server/variables.py +367 -0
- plexora/plugins/cell_explorer/static/cellExplorerApi.js +158 -0
- plexora/plugins/cell_explorer/static/cellExplorerColors.js +316 -0
- plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +403 -0
- plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +133 -0
- plexora/plugins/cell_explorer/static/cellExplorerLegend.js +188 -0
- plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +822 -0
- plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +826 -0
- plexora/plugins/cell_explorer/static/cellExplorerState.js +372 -0
- plexora/plugins/cell_explorer/static/cell_explorer.css +752 -0
- plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +137 -0
- plexora/plugins/figure_builder/__init__.py +125 -0
- plexora/plugins/figure_builder/server/__init__.py +7 -0
- plexora/plugins/figure_builder/server/compose.py +365 -0
- plexora/plugins/figure_builder/server/export.py +784 -0
- plexora/plugins/figure_builder/server/export_jobs.py +148 -0
- plexora/plugins/figure_builder/server/operations.py +556 -0
- plexora/plugins/figure_builder/server/pixels.py +142 -0
- plexora/plugins/figure_builder/server/provenance.py +174 -0
- plexora/plugins/figure_builder/server/render.py +278 -0
- plexora/plugins/figure_builder/server/repository.py +701 -0
- plexora/plugins/figure_builder/server/routes.py +594 -0
- plexora/plugins/figure_builder/server/schema.py +1023 -0
- plexora/plugins/figure_builder/server/sources.py +120 -0
- plexora/plugins/figure_builder/server/textmetrics.py +155 -0
- plexora/plugins/figure_builder/static/figureActions.js +363 -0
- plexora/plugins/figure_builder/static/figureBuilderApi.js +255 -0
- plexora/plugins/figure_builder/static/figureCanvas.js +2362 -0
- plexora/plugins/figure_builder/static/figureCaptureBoxes.js +369 -0
- plexora/plugins/figure_builder/static/figureCaptureDock.js +466 -0
- plexora/plugins/figure_builder/static/figureCaptureTool.js +1034 -0
- plexora/plugins/figure_builder/static/figureConfirm.js +136 -0
- plexora/plugins/figure_builder/static/figureContextBar.js +1193 -0
- plexora/plugins/figure_builder/static/figureContextMenu.js +253 -0
- plexora/plugins/figure_builder/static/figureDocumentState.js +275 -0
- plexora/plugins/figure_builder/static/figureExportUi.js +174 -0
- plexora/plugins/figure_builder/static/figureLibrary.js +247 -0
- plexora/plugins/figure_builder/static/figureQuickEdit.js +628 -0
- plexora/plugins/figure_builder/static/figureRichText.js +634 -0
- plexora/plugins/figure_builder/static/figureSceneSnapshot.js +361 -0
- plexora/plugins/figure_builder/static/figureSchema.js +218 -0
- plexora/plugins/figure_builder/static/figureSidebarController.js +1159 -0
- plexora/plugins/figure_builder/static/figureTextEditor.js +503 -0
- plexora/plugins/figure_builder/static/figureTextPanel.js +491 -0
- plexora/plugins/figure_builder/static/figureViewOptions.js +273 -0
- plexora/plugins/figure_builder/static/figureWorkspace.js +2030 -0
- plexora/plugins/figure_builder/static/figure_builder.css +3292 -0
- plexora/plugins/figure_builder/templates/figure_builder/library.html +74 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace.html +27 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +458 -0
- plexora/plugins/gating/__init__.py +76 -0
- plexora/plugins/gating/server/__init__.py +0 -0
- plexora/plugins/gating/server/anndata_gates.py +367 -0
- plexora/plugins/gating/server/database.py +16 -0
- plexora/plugins/gating/server/model.py +320 -0
- plexora/plugins/gating/server/routes.py +235 -0
- plexora/plugins/gating/static/csvGatingList.js +947 -0
- plexora/plugins/gating/static/gating.css +289 -0
- plexora/plugins/gating/static/gatingApi.js +221 -0
- plexora/plugins/gating/static/gatingSidebarController.js +513 -0
- plexora/plugins/gating/templates/gating/legacy.html +26 -0
- plexora/plugins/gating/templates/gating/panel.html +69 -0
- plexora/plugins/roi/__init__.py +95 -0
- plexora/plugins/roi/server/__init__.py +8 -0
- plexora/plugins/roi/server/adapters.py +655 -0
- plexora/plugins/roi/server/geojson.py +267 -0
- plexora/plugins/roi/server/geometry.py +181 -0
- plexora/plugins/roi/server/mapping.py +169 -0
- plexora/plugins/roi/server/operations.py +293 -0
- plexora/plugins/roi/server/repository.py +215 -0
- plexora/plugins/roi/server/routes.py +409 -0
- plexora/plugins/roi/server/schema.py +323 -0
- plexora/plugins/roi/static/roi.css +583 -0
- plexora/plugins/roi/static/roiApi.js +177 -0
- plexora/plugins/roi/static/roiFigureBridge.js +107 -0
- plexora/plugins/roi/static/roiGeometry.js +350 -0
- plexora/plugins/roi/static/roiRenderer.js +278 -0
- plexora/plugins/roi/static/roiSidebarController.js +1038 -0
- plexora/plugins/roi/static/roiState.js +518 -0
- plexora/plugins/roi/static/roiTools.js +993 -0
- plexora/plugins/roi/templates/roi/panel.html +240 -0
- plexora/proxy.py +57 -0
- plexora/server/models/adapters/__init__.py +94 -0
- plexora/server/models/adapters/anndata_adapter.py +388 -0
- plexora/server/models/adapters/base.py +93 -0
- plexora/server/models/adapters/classify.py +192 -0
- plexora/server/models/adapters/csv_adapter.py +105 -0
- plexora/server/models/adapters/inspection.py +265 -0
- plexora/server/models/adapters/spatialdata_adapter.py +248 -0
- plexora/server/models/centroid_tiles.py +305 -0
- plexora/server/models/data_model.py +1659 -0
- plexora/server/models/database_model.py +169 -0
- plexora/server/models/project.py +1270 -0
- plexora/server/plugins.py +199 -0
- plexora/server/routes/browse_routes.py +41 -0
- plexora/server/routes/data_routes.py +294 -0
- plexora/server/routes/import_routes.py +603 -0
- plexora/server/routes/page_routes.py +139 -0
- plexora/server/routes/project_routes.py +438 -0
- plexora/server/routes/quick_view_routes.py +62 -0
- plexora/server/routes/system_routes.py +39 -0
- plexora/server/routes/tool_routes.py +418 -0
- plexora/server/utils/addHEColumns.py +11 -0
- plexora/server/utils/fast_png.py +97 -0
- plexora/server/utils/fullConversion.py +50 -0
- plexora/server/utils/native_dialog.py +168 -0
- plexora/server/utils/pre_normalization.py +47 -0
- plexora/server/utils/segmentation_pyramid.py +598 -0
- plexora/server/utils/smallestenclosingcircle.py +126 -0
- plexora/server/utils/tiffsurgeon.py +372 -0
- plexora/server_cli.py +48 -0
- plexora/test.py +0 -0
- plexora-0.0.1.dist-info/METADATA +341 -0
- plexora-0.0.1.dist-info/RECORD +248 -0
- plexora-0.0.1.dist-info/WHEEL +5 -0
- plexora-0.0.1.dist-info/entry_points.txt +12 -0
- plexora-0.0.1.dist-info/licenses/LICENSE +207 -0
- plexora-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dataSourceField.js -- "which data file?", asked the same way everywhere.
|
|
3
|
+
*
|
|
4
|
+
* A path alone does not name a table. A .zarr store can hold several, and a
|
|
5
|
+
* table can span several images, and neither is answerable from the file --
|
|
6
|
+
* the server refuses to import until one is picked (see import_routes'
|
|
7
|
+
* replace_project_data). The upload page has asked both questions since it was
|
|
8
|
+
* written; the requirements modal and the edit page asked neither, so choosing
|
|
9
|
+
* a multi-table store on either one posted a path the server could only
|
|
10
|
+
* reject, with the message "choose which one to load" and nothing to choose
|
|
11
|
+
* from.
|
|
12
|
+
*
|
|
13
|
+
* So the question lives here, once, and all three surfaces mount it: path
|
|
14
|
+
* input, its two pickers, and the table/image selects that appear only when
|
|
15
|
+
* the file itself forces the choice. What it produces is exactly the four keys
|
|
16
|
+
* the server reads -- `data`, `table`, `subset_column`, `subset_value`.
|
|
17
|
+
*
|
|
18
|
+
* Two properties worth keeping:
|
|
19
|
+
*
|
|
20
|
+
* - **Nothing is inspected until the path changes.** The edit page mounts this
|
|
21
|
+
* with the project's stored file, already imported and already answered;
|
|
22
|
+
* re-opening it on load would re-read a multi-gigabyte store to ask a
|
|
23
|
+
* question that has an answer.
|
|
24
|
+
* - **It reports what it is still waiting for.** `blocking()` is the caller's
|
|
25
|
+
* way to refuse to save rather than post something the server will reject --
|
|
26
|
+
* and to say why, in the same words the picker beside it is asking.
|
|
27
|
+
*/
|
|
28
|
+
window.PlexoraDataSourceField = (function () {
|
|
29
|
+
const DATA_TYPE_LABELS = {
|
|
30
|
+
csv: "CSV table",
|
|
31
|
+
anndata: "AnnData",
|
|
32
|
+
spatialdata: "SpatialData store",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const DEFAULT_HINT = "CSV, AnnData (.h5ad) or SpatialData (.zarr).";
|
|
36
|
+
|
|
37
|
+
function el(tag, className, text) {
|
|
38
|
+
const node = document.createElement(tag);
|
|
39
|
+
if (className) node.className = className;
|
|
40
|
+
if (text) node.textContent = text;
|
|
41
|
+
return node;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param container where to render
|
|
46
|
+
* @param options `value` the path already stored (never inspected),
|
|
47
|
+
* `id` for the input when a caller needs to address it, `hint` the
|
|
48
|
+
* resting note under the field, and `onChange` called with value()
|
|
49
|
+
* whenever any of the four keys changes.
|
|
50
|
+
* @returns { value(), blocking(), element }
|
|
51
|
+
*/
|
|
52
|
+
function mount(container, options = {}) {
|
|
53
|
+
const initial = (options.value || "").trim();
|
|
54
|
+
const onChange = options.onChange || function () {};
|
|
55
|
+
|
|
56
|
+
const root = el("div", "data-source-field");
|
|
57
|
+
const row = el("div", "import-field-row");
|
|
58
|
+
const input = el("input", "form-control");
|
|
59
|
+
input.type = "text";
|
|
60
|
+
input.placeholder = options.placeholder || "/path/to/cells.csv";
|
|
61
|
+
input.value = initial;
|
|
62
|
+
if (options.id) input.id = options.id;
|
|
63
|
+
row.appendChild(input);
|
|
64
|
+
|
|
65
|
+
// A .zarr store is a directory and a .csv/.h5ad is a file, and one
|
|
66
|
+
// input takes both -- so the picker offers each in turn, same as the
|
|
67
|
+
// upload page.
|
|
68
|
+
[["file", "File…", "data"], ["directory", "Store…", "any"]]
|
|
69
|
+
.forEach(([mode, label, filter]) => {
|
|
70
|
+
const button = el("button", "browse-button", label);
|
|
71
|
+
button.type = "button";
|
|
72
|
+
if (typeof attachBrowseButton === "function") {
|
|
73
|
+
attachBrowseButton(button, input, { mode, filter });
|
|
74
|
+
}
|
|
75
|
+
row.appendChild(button);
|
|
76
|
+
});
|
|
77
|
+
root.appendChild(row);
|
|
78
|
+
|
|
79
|
+
const note = el("span", "field-hint", options.hint || DEFAULT_HINT);
|
|
80
|
+
root.appendChild(note);
|
|
81
|
+
|
|
82
|
+
const tableField = el("div", "data-source-extra");
|
|
83
|
+
const tableSelect = el("select", "form-select");
|
|
84
|
+
tableField.append(tableSelect,
|
|
85
|
+
el("span", "field-hint",
|
|
86
|
+
"This store holds several tables. Choose which one holds the cells."));
|
|
87
|
+
tableField.hidden = true;
|
|
88
|
+
root.appendChild(tableField);
|
|
89
|
+
|
|
90
|
+
const subsetField = el("div", "data-source-extra");
|
|
91
|
+
const subsetSelect = el("select", "form-select");
|
|
92
|
+
const subsetHint = el("span", "field-hint");
|
|
93
|
+
subsetField.append(subsetSelect, subsetHint);
|
|
94
|
+
subsetField.hidden = true;
|
|
95
|
+
root.appendChild(subsetField);
|
|
96
|
+
|
|
97
|
+
container.appendChild(root);
|
|
98
|
+
|
|
99
|
+
const state = {
|
|
100
|
+
table: null,
|
|
101
|
+
subsetColumn: null,
|
|
102
|
+
subsetValue: null,
|
|
103
|
+
blocking: null,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Replies can arrive out of order when someone types quickly, and a
|
|
107
|
+
// stale one would re-show a picker for a file no longer in the box.
|
|
108
|
+
let token = 0;
|
|
109
|
+
let timer = null;
|
|
110
|
+
let pending = false;
|
|
111
|
+
|
|
112
|
+
function value() {
|
|
113
|
+
return {
|
|
114
|
+
data: input.value.trim(),
|
|
115
|
+
table: state.table || undefined,
|
|
116
|
+
subset_column: state.subsetColumn || undefined,
|
|
117
|
+
subset_value: state.subsetValue || undefined,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** What still stops this being saved, or null. */
|
|
122
|
+
function blocking() {
|
|
123
|
+
if (pending) return "Still reading the data file — try again in a moment.";
|
|
124
|
+
return state.blocking;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function emit() {
|
|
128
|
+
onChange(value());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function reset() {
|
|
132
|
+
state.table = null;
|
|
133
|
+
state.subsetColumn = null;
|
|
134
|
+
state.subsetValue = null;
|
|
135
|
+
state.blocking = null;
|
|
136
|
+
tableField.hidden = true;
|
|
137
|
+
subsetField.hidden = true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function schedule() {
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
const path = input.value.trim();
|
|
143
|
+
// The stored file is already imported and already answered. Only a
|
|
144
|
+
// change is a question, which is also what keeps a mount free.
|
|
145
|
+
if (path === initial) {
|
|
146
|
+
reset();
|
|
147
|
+
input.classList.remove("is-valid", "is-invalid");
|
|
148
|
+
note.textContent = options.hint || DEFAULT_HINT;
|
|
149
|
+
emit();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
reset();
|
|
153
|
+
emit();
|
|
154
|
+
if (!path) {
|
|
155
|
+
input.classList.remove("is-valid", "is-invalid");
|
|
156
|
+
note.textContent = options.hint || DEFAULT_HINT;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// Every inspection opens the file, and an .h5ad is not cheap to
|
|
160
|
+
// open -- so wait for a pause in typing rather than per keystroke.
|
|
161
|
+
timer = setTimeout(() => inspect(null), 250);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @param table which table inside a .zarr store to look at, on the
|
|
166
|
+
* second pass. Everything below the table picker is a question about
|
|
167
|
+
* a table, so a multi-table store cannot answer any of it until one
|
|
168
|
+
* is named.
|
|
169
|
+
*/
|
|
170
|
+
async function inspect(table) {
|
|
171
|
+
const mine = ++token;
|
|
172
|
+
const path = input.value.trim();
|
|
173
|
+
if (!path) return;
|
|
174
|
+
pending = true;
|
|
175
|
+
|
|
176
|
+
let payload;
|
|
177
|
+
try {
|
|
178
|
+
const response = await fetch(plexoraUrl("inspect_data"), {
|
|
179
|
+
method: "POST",
|
|
180
|
+
headers: { "Content-Type": "application/json" },
|
|
181
|
+
body: JSON.stringify({ path, table: table || null }),
|
|
182
|
+
});
|
|
183
|
+
payload = await response.json();
|
|
184
|
+
} catch (e) {
|
|
185
|
+
pending = false;
|
|
186
|
+
return; // transport failures are reported by PlexoraStatus
|
|
187
|
+
}
|
|
188
|
+
if (mine !== token) return; // a newer path is in the box
|
|
189
|
+
pending = false;
|
|
190
|
+
|
|
191
|
+
if (!payload.ok) {
|
|
192
|
+
input.classList.remove("is-valid");
|
|
193
|
+
input.classList.add("is-invalid");
|
|
194
|
+
note.textContent = payload.error || "Unreadable";
|
|
195
|
+
state.blocking = payload.error || "That data file cannot be read.";
|
|
196
|
+
emit();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
input.classList.remove("is-invalid");
|
|
201
|
+
input.classList.add("is-valid");
|
|
202
|
+
note.textContent = DATA_TYPE_LABELS[payload.data_type] || payload.data_type;
|
|
203
|
+
state.blocking = null;
|
|
204
|
+
|
|
205
|
+
if (!table && (payload.tables || []).length > 1) {
|
|
206
|
+
// Choosing re-runs this with the table, which is what puts the
|
|
207
|
+
// image question -- a store with several tables has no answer
|
|
208
|
+
// to it until one is named.
|
|
209
|
+
showTables(payload.tables);
|
|
210
|
+
emit();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
state.table = table || payload.table || null;
|
|
214
|
+
if ((payload.ambiguous || []).length) {
|
|
215
|
+
showSubset(payload.ambiguous[0]);
|
|
216
|
+
}
|
|
217
|
+
emit();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function showTables(tables) {
|
|
221
|
+
tableSelect.replaceChildren();
|
|
222
|
+
tableSelect.append(new Option("Choose a table…", ""));
|
|
223
|
+
tables.forEach((table) => {
|
|
224
|
+
tableSelect.append(new Option(
|
|
225
|
+
`${table.name} — ${table.n_obs} cells × ${table.n_var} markers`,
|
|
226
|
+
table.name,
|
|
227
|
+
));
|
|
228
|
+
});
|
|
229
|
+
// Assigned rather than added: this runs again on every
|
|
230
|
+
// re-inspection, and addEventListener would stack a handler each
|
|
231
|
+
// time.
|
|
232
|
+
tableSelect.onchange = () => {
|
|
233
|
+
subsetField.hidden = true;
|
|
234
|
+
state.subsetColumn = null;
|
|
235
|
+
state.subsetValue = null;
|
|
236
|
+
state.table = tableSelect.value || null;
|
|
237
|
+
state.blocking = tableSelect.value
|
|
238
|
+
? null : "Choose which table in the store holds the cells.";
|
|
239
|
+
emit();
|
|
240
|
+
if (tableSelect.value) inspect(tableSelect.value);
|
|
241
|
+
};
|
|
242
|
+
tableField.hidden = false;
|
|
243
|
+
state.table = null;
|
|
244
|
+
state.blocking = "Choose which table in the store holds the cells.";
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function showSubset(ambiguous) {
|
|
248
|
+
subsetHint.textContent =
|
|
249
|
+
`This table covers several images (column "${ambiguous.column}"). `
|
|
250
|
+
+ "Choose the one this project's image shows.";
|
|
251
|
+
subsetSelect.replaceChildren();
|
|
252
|
+
subsetSelect.append(new Option("Choose an image…", ""));
|
|
253
|
+
(ambiguous.values || []).forEach(
|
|
254
|
+
(item) => subsetSelect.append(new Option(item, item)));
|
|
255
|
+
subsetSelect.onchange = () => {
|
|
256
|
+
state.subsetValue = subsetSelect.value || null;
|
|
257
|
+
state.blocking = subsetSelect.value
|
|
258
|
+
? null : "Choose which image in the table this project shows.";
|
|
259
|
+
emit();
|
|
260
|
+
};
|
|
261
|
+
subsetField.hidden = false;
|
|
262
|
+
state.subsetColumn = ambiguous.column;
|
|
263
|
+
state.subsetValue = null;
|
|
264
|
+
state.blocking = "Choose which image in the table this project shows.";
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
input.addEventListener("input", schedule);
|
|
268
|
+
// browsePicker dispatches both; `input` alone would miss nothing, but
|
|
269
|
+
// the upload page's fields listen on keyup and this stays in step.
|
|
270
|
+
input.addEventListener("keyup", schedule);
|
|
271
|
+
|
|
272
|
+
return { value, blocking, element: root, input };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return { mount };
|
|
276
|
+
})();
|