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,240 @@
|
|
|
1
|
+
<section class="sidebar-section" id="roi_panel_section">
|
|
2
|
+
<div class="section-heading">
|
|
3
|
+
<h2><span class="fas fa-draw-polygon section-heading-icon"></span>Regions</h2>
|
|
4
|
+
<div class="section-actions">
|
|
5
|
+
<button id="roi_import_button" class="icon-button" title="Import ROIs from GeoJSON" type="button">
|
|
6
|
+
<span class="fas fa-file-import"></span>
|
|
7
|
+
</button>
|
|
8
|
+
<button id="roi_export_button" class="icon-button" title="Export ROIs as GeoJSON" type="button">
|
|
9
|
+
<span class="fas fa-file-export"></span>
|
|
10
|
+
</button>
|
|
11
|
+
<button id="roi_panel_close" class="icon-button" title="Close Regions" type="button">
|
|
12
|
+
<span class="fas fa-xmark"></span>
|
|
13
|
+
</button>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
{# Only ever populated from a real file the user picked. #}
|
|
18
|
+
<input id="roi_import_file" class="roi-hidden-file" type="file" accept=".geojson,.json,application/geo+json,application/json">
|
|
19
|
+
|
|
20
|
+
<div id="roi_blocked_banner" class="roi-banner roi-banner-danger" hidden>
|
|
21
|
+
<span class="fas fa-triangle-exclamation"></span>
|
|
22
|
+
<div>
|
|
23
|
+
<strong>These ROIs belong to a different image.</strong>
|
|
24
|
+
<div id="roi_blocked_detail" class="roi-banner-detail"></div>
|
|
25
|
+
<div class="roi-banner-detail">Nothing is shown, and editing is off, so regions cannot be attached to the wrong image. Export them to keep them.</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div id="roi_conflict_banner" class="roi-banner roi-banner-warning" hidden>
|
|
30
|
+
<span class="fas fa-code-branch"></span>
|
|
31
|
+
<div>
|
|
32
|
+
<strong>Changed in another session.</strong>
|
|
33
|
+
<div class="roi-banner-detail">Someone -- probably another tab -- saved since this one loaded.</div>
|
|
34
|
+
<div class="roi-banner-actions">
|
|
35
|
+
<button id="roi_conflict_reload" class="sidebar-action secondary" type="button">Use theirs</button>
|
|
36
|
+
<button id="roi_conflict_keep" class="sidebar-action" type="button">Export mine, then reload</button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
{# The three that MAKE a shape start disabled, because until the store has
|
|
42
|
+
loaded this panel does not yet know whether there is a category to put
|
|
43
|
+
one in -- and offering a live pen that refuses every stroke is worse
|
|
44
|
+
than one that visibly waits. The first render decides. #}
|
|
45
|
+
<label class="control-label">Tools</label>
|
|
46
|
+
<div id="roi_toolbar" class="roi-toolbar">
|
|
47
|
+
<button id="roi_tool_select" class="roi-tool" data-tool="select" type="button" title="Select (V)">
|
|
48
|
+
<span class="fas fa-arrow-pointer"></span><span class="roi-tool-key">V</span>
|
|
49
|
+
</button>
|
|
50
|
+
<button id="roi_tool_polygon" class="roi-tool" data-tool="polygon" type="button" disabled title="Polygon (P) -- click to place points, Enter or double-click to finish">
|
|
51
|
+
<span class="fas fa-draw-polygon"></span><span class="roi-tool-key">P</span>
|
|
52
|
+
</button>
|
|
53
|
+
<button id="roi_tool_freehand" class="roi-tool" data-tool="freehand" type="button" disabled title="Freehand (F) -- press and drag">
|
|
54
|
+
<span class="fas fa-pen-nib"></span><span class="roi-tool-key">F</span>
|
|
55
|
+
</button>
|
|
56
|
+
<button id="roi_tool_rectangle" class="roi-tool" data-tool="rectangle" type="button" disabled title="Rectangle (R) -- press and drag">
|
|
57
|
+
<span class="far fa-square"></span><span class="roi-tool-key">R</span>
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="roi-hint">Hold <kbd>Space</kbd> to pan. The wheel always zooms.</div>
|
|
61
|
+
|
|
62
|
+
<label class="control-label">Categories</label>
|
|
63
|
+
<div id="roi_category_list" class="roi-category-list"></div>
|
|
64
|
+
{# A project starts with none: the names are the user's to choose, so the
|
|
65
|
+
panel asks rather than shipping an "Uncategorized" nobody picked. #}
|
|
66
|
+
<div id="roi_category_empty" class="roi-hint roi-empty" hidden>
|
|
67
|
+
Name what you are about to outline — Tumor, Stroma, Necrosis — then draw.
|
|
68
|
+
</div>
|
|
69
|
+
{# Deliberately not a <form>. This markup is server-rendered and on screen
|
|
70
|
+
before the plugin's scripts have finished loading, so for a moment there
|
|
71
|
+
is nobody to call preventDefault -- and an Enter in that window submits
|
|
72
|
+
natively and NAVIGATES THE PAGE, taking the panel and anything unsaved
|
|
73
|
+
with it. No form, no default action to suppress. #}
|
|
74
|
+
<div id="roi_category_add" class="roi-category-add">
|
|
75
|
+
<input id="roi_category_name" class="roi-input" type="text" autocomplete="off" spellcheck="false"
|
|
76
|
+
maxlength="200" placeholder="New category…">
|
|
77
|
+
<button id="roi_category_add_button" class="icon-button" title="Add category" type="button">
|
|
78
|
+
<span class="fas fa-plus"></span>
|
|
79
|
+
</button>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{# Compact on purpose. This block appears and disappears as regions are
|
|
83
|
+
clicked, right under the category list the user is also working in, so
|
|
84
|
+
every row it costs is a row the list loses. The two things worth reading
|
|
85
|
+
-- what it is called and what it is -- sit on one line; the two things
|
|
86
|
+
worth doing are icons on the heading. #}
|
|
87
|
+
<div id="roi_selection_panel" hidden>
|
|
88
|
+
<div class="roi-selection-head">
|
|
89
|
+
<label class="control-label">Selected</label>
|
|
90
|
+
<div class="roi-selection-tools">
|
|
91
|
+
{# Both padlocks ship, and CSS picks which one shows. Swapping
|
|
92
|
+
the class in JS does not work here: FontAwesome's SVG mode
|
|
93
|
+
has already replaced these spans with <svg> elements by the
|
|
94
|
+
time anything is selected, so there is no span left to
|
|
95
|
+
rewrite -- and rewriting the svg's class strips what FA put
|
|
96
|
+
there. The `fa-*` class survives onto the svg, which is what
|
|
97
|
+
the rule in roi.css hangs on. #}
|
|
98
|
+
<button id="roi_selection_locked" class="icon-button roi-selection-action"
|
|
99
|
+
type="button" aria-pressed="false" title="Lock this region">
|
|
100
|
+
<span class="fas fa-lock-open"></span>
|
|
101
|
+
<span class="fas fa-lock"></span>
|
|
102
|
+
</button>
|
|
103
|
+
<button id="roi_selection_delete" class="icon-button roi-selection-action roi-danger-action"
|
|
104
|
+
type="button" title="Delete this region">
|
|
105
|
+
<span class="fas fa-trash"></span>
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="roi-selection-row">
|
|
110
|
+
<input id="roi_selection_name" class="roi-input roi-selection-name" type="text"
|
|
111
|
+
autocomplete="off" spellcheck="false" maxlength="200"
|
|
112
|
+
placeholder="Name" title="Name of this region">
|
|
113
|
+
{# The swatch is a sibling sitting over the select's left padding:
|
|
114
|
+
a native <select> cannot colour its own options portably, and
|
|
115
|
+
the only colour worth showing is the current one. #}
|
|
116
|
+
<div class="roi-select-wrap">
|
|
117
|
+
<span id="roi_selection_swatch" class="roi-selection-swatch" aria-hidden="true"></span>
|
|
118
|
+
<select id="roi_selection_category" class="roi-input" title="Category"></select>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div id="roi_selection_note" class="roi-hint roi-selection-note" hidden></div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
{# One of these two, never both. A project with a native destination gets
|
|
125
|
+
the file write; a CSV or image-only project gets the download in the same
|
|
126
|
+
place and at the same size, because "how do I keep this?" is the same
|
|
127
|
+
question either way.
|
|
128
|
+
|
|
129
|
+
The name field is a step in the save, not part of the resting panel: the
|
|
130
|
+
first click on the button opens it with the name already filled in, and
|
|
131
|
+
the second click writes. Most saves are the same name as last time, so
|
|
132
|
+
standing the field up permanently would spend three rows of a 320px
|
|
133
|
+
column on a decision almost nobody is making. Enter writes, Escape and
|
|
134
|
+
the x close it. #}
|
|
135
|
+
<div id="roi_destination" class="roi-destination" hidden>
|
|
136
|
+
<div class="roi-destination-head">
|
|
137
|
+
<label class="control-label" for="roi_destination_name">Save as</label>
|
|
138
|
+
<button id="roi_destination_close" class="icon-button roi-destination-close"
|
|
139
|
+
type="button" title="Close">
|
|
140
|
+
<span class="fas fa-xmark"></span>
|
|
141
|
+
</button>
|
|
142
|
+
</div>
|
|
143
|
+
<input id="roi_destination_name" class="roi-input" type="text" maxlength="200"
|
|
144
|
+
autocomplete="off" spellcheck="false">
|
|
145
|
+
<div id="roi_destination_hint" class="roi-hint roi-destination-hint"></div>
|
|
146
|
+
</div>
|
|
147
|
+
{# The two ways out of this panel, side by side, because they are the same
|
|
148
|
+
size of decision: one keeps the shapes, the other puts them on the rows.
|
|
149
|
+
Stacked they read as a sequence -- save, THEN map -- which is not true;
|
|
150
|
+
either can be done without the other, in either order.
|
|
151
|
+
|
|
152
|
+
Exactly one save button is ever shown, so the row is two buttons wide
|
|
153
|
+
whatever the project is. Both say "Save": which file it lands in is what
|
|
154
|
+
the hint above and the tooltip are for, and a button label is not the
|
|
155
|
+
place to name a storage format. #}
|
|
156
|
+
<div class="roi-actions">
|
|
157
|
+
<button id="roi_save_to_source" class="sidebar-action-fancy" type="button" hidden
|
|
158
|
+
aria-expanded="false">
|
|
159
|
+
<span class="fas fa-file-export"></span>
|
|
160
|
+
<span id="roi_save_to_source_label">Save</span>
|
|
161
|
+
</button>
|
|
162
|
+
<button id="roi_export_download" class="sidebar-action-fancy" type="button" hidden
|
|
163
|
+
title="Download these regions as a GeoJSON file">
|
|
164
|
+
<span class="fas fa-download"></span>
|
|
165
|
+
<span>Save GeoJSON</span>
|
|
166
|
+
</button>
|
|
167
|
+
|
|
168
|
+
{# Only for a project that has cells. The save buttons write the
|
|
169
|
+
polygons somewhere; this writes onto the rows, which is a different
|
|
170
|
+
thing to want and is meaningless without a table -- so it is absent
|
|
171
|
+
rather than disabled for a project that is nothing but an image. #}
|
|
172
|
+
<button id="roi_map_to_cells" class="sidebar-action-fancy" type="button" hidden>
|
|
173
|
+
<span class="fas fa-table-cells"></span>
|
|
174
|
+
<span>Map to cells</span>
|
|
175
|
+
</button>
|
|
176
|
+
|
|
177
|
+
{# The one control on this panel whose result a user cannot see before
|
|
178
|
+
pressing it -- it writes onto their own rows, and the rules it writes
|
|
179
|
+
by (overlaps, image scope, what a blank means) do not fit in a line
|
|
180
|
+
of hint text under a button. They used to be attempted in one, which
|
|
181
|
+
said only what the columns were called. #}
|
|
182
|
+
<button id="roi_map_info" class="icon-button roi-map-info" type="button" hidden
|
|
183
|
+
title="What does Map to cells do?" aria-haspopup="dialog">
|
|
184
|
+
<span class="fas fa-question"></span>
|
|
185
|
+
</button>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
{# Opened by the ? above. Plain <dialog>: it is modal, Escape closes it and
|
|
189
|
+
focus is handled by the browser, none of which is worth reimplementing.
|
|
190
|
+
The prose is static -- it is the same for every project -- except for the
|
|
191
|
+
one line naming the columns, which follows the save name. #}
|
|
192
|
+
<dialog id="roi_map_info_dialog" class="roi-modal">
|
|
193
|
+
<div class="roi-modal-head">
|
|
194
|
+
<h3 class="roi-modal-title">Map to cells</h3>
|
|
195
|
+
<button id="roi_map_info_close" class="icon-button roi-modal-close"
|
|
196
|
+
type="button" title="Close">
|
|
197
|
+
<span class="fas fa-xmark"></span>
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="roi-modal-body">
|
|
201
|
+
<p>Assigns each cell to the ROI(s) that contain it and writes the
|
|
202
|
+
annotations directly to the data table.</p>
|
|
203
|
+
|
|
204
|
+
<p>For the current save name, Plexora creates:</p>
|
|
205
|
+
|
|
206
|
+
{# The only two values in here that are about THIS project. Written
|
|
207
|
+
by openMapInfo() from the save name, not hard-coded: a fixed pair
|
|
208
|
+
of names would be wrong for every user who saved under their own,
|
|
209
|
+
and this dialog exists to tell them what they are about to get. #}
|
|
210
|
+
<ul id="roi_map_info_columns" class="roi-modal-note">
|
|
211
|
+
<li><code id="roi_map_info_category">rois_category</code>
|
|
212
|
+
— ROI category, e.g. Tumour</li>
|
|
213
|
+
<li><code id="roi_map_info_name">rois_name</code>
|
|
214
|
+
— specific ROI name, e.g. Tumour 1</li>
|
|
215
|
+
</ul>
|
|
216
|
+
|
|
217
|
+
<p>If a cell falls within overlapping ROIs, values are joined with
|
|
218
|
+
<code>_</code>. Duplicate categories are collapsed, while distinct
|
|
219
|
+
ROI names are retained.</p>
|
|
220
|
+
|
|
221
|
+
<p>Cells outside all ROIs are left blank. Only cells belonging to the
|
|
222
|
+
currently displayed image are evaluated; cells from other images
|
|
223
|
+
are not modified.</p>
|
|
224
|
+
|
|
225
|
+
<p>Existing columns are not overwritten automatically. If they
|
|
226
|
+
already exist, Plexora prompts you to replace them or use a new
|
|
227
|
+
save name.</p>
|
|
228
|
+
|
|
229
|
+
<p class="roi-modal-aside"><strong>Note:</strong> Map to cells saves
|
|
230
|
+
ROI assignments to individual cells. The separate Save function
|
|
231
|
+
saves the ROI shapes themselves.</p>
|
|
232
|
+
</div>
|
|
233
|
+
</dialog>
|
|
234
|
+
|
|
235
|
+
<div id="roi_status" class="roi-status">
|
|
236
|
+
<span id="roi_status_dot" class="roi-status-dot"></span>
|
|
237
|
+
<span id="roi_status_text">Saved</span>
|
|
238
|
+
</div>
|
|
239
|
+
<div id="roi_message" class="roi-message" hidden></div>
|
|
240
|
+
</section>
|
plexora/proxy.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def setup_plexora():
|
|
6
|
+
data_dir = os.environ.get("PLEXORA_DATA_PATH", "")
|
|
7
|
+
plugins = os.environ.get("PLEXORA_PLUGINS")
|
|
8
|
+
base_url_template = "{base_url}plexora"
|
|
9
|
+
command = [
|
|
10
|
+
sys.executable,
|
|
11
|
+
"-m",
|
|
12
|
+
"plexora.server_cli",
|
|
13
|
+
"--host",
|
|
14
|
+
"127.0.0.1",
|
|
15
|
+
"--port",
|
|
16
|
+
"{port}",
|
|
17
|
+
"--base-url",
|
|
18
|
+
base_url_template,
|
|
19
|
+
"--notebook-mode",
|
|
20
|
+
]
|
|
21
|
+
# Omitted entirely when unset, exactly as the `environment` dict below
|
|
22
|
+
# does. Passing `--plugins ""` here instead used to be a silent
|
|
23
|
+
# catastrophe: server_cli.py writes the flag straight into PLEXORA_PLUGINS,
|
|
24
|
+
# and "" is not "unset" -- it is the deliberate core-only build. Every
|
|
25
|
+
# launch from the JupyterHub launcher tile came up with all plugins
|
|
26
|
+
# disabled, and looked to the user like Plexora simply had no tools.
|
|
27
|
+
if plugins is not None:
|
|
28
|
+
command.extend(["--plugins", plugins])
|
|
29
|
+
if data_dir:
|
|
30
|
+
command.extend(["--data-dir", data_dir])
|
|
31
|
+
|
|
32
|
+
# jupyter_server_proxy sets these as real OS env vars before the child
|
|
33
|
+
# process starts (handlers.py: ensure_process's server_env.update(get_env())),
|
|
34
|
+
# which is what actually reaches plexora/__init__.py's import-time
|
|
35
|
+
# env snapshot -- the --base-url/--data-dir/--plugins CLI flags
|
|
36
|
+
# above are consumed too late relative to that import. Keep the two in
|
|
37
|
+
# sync if you change one.
|
|
38
|
+
environment = {
|
|
39
|
+
"PLEXORA_BASE_URL": base_url_template,
|
|
40
|
+
"PLEXORA_NOTEBOOK_MODE": "1",
|
|
41
|
+
**({"PLEXORA_PLUGINS": plugins} if plugins is not None else {}),
|
|
42
|
+
}
|
|
43
|
+
if data_dir:
|
|
44
|
+
environment["PLEXORA_DATA_PATH"] = data_dir
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
"command": command,
|
|
48
|
+
"environment": environment,
|
|
49
|
+
"absolute_url": False,
|
|
50
|
+
"new_browser_tab": False,
|
|
51
|
+
"timeout": 30,
|
|
52
|
+
"launcher_entry": {
|
|
53
|
+
"enabled": True,
|
|
54
|
+
"title": "Plexora",
|
|
55
|
+
"path_info": "",
|
|
56
|
+
},
|
|
57
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from .base import DatasourceAdapter, MetadataColumn, NormalizedDatasource
|
|
4
|
+
from .classify import classify_columns, classify_from_inspection
|
|
5
|
+
from .csv_adapter import CsvAdapter
|
|
6
|
+
from .anndata_adapter import AnnDataAdapter
|
|
7
|
+
from .spatialdata_adapter import SpatialDataAdapter
|
|
8
|
+
|
|
9
|
+
_ADAPTERS = {
|
|
10
|
+
"csv": CsvAdapter,
|
|
11
|
+
"anndata": AnnDataAdapter,
|
|
12
|
+
"spatialdata": SpatialDataAdapter,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
#: Extensions the single "Data" input on the upload page accepts, mapped to the
|
|
16
|
+
#: adapter that reads them. A directory is decided by inspecting it (a .zarr
|
|
17
|
+
#: store holding a `tables/` group is SpatialData, otherwise it is a
|
|
18
|
+
#: zarr-backed AnnData), since the extension alone cannot tell them apart.
|
|
19
|
+
_SUFFIX_TYPES = {
|
|
20
|
+
".csv": "csv",
|
|
21
|
+
".tsv": "csv",
|
|
22
|
+
".txt": "csv",
|
|
23
|
+
".h5ad": "anndata",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#: What to tell the user when a path is none of the above. Kept here rather
|
|
27
|
+
#: than in the route so the accepted list cannot drift from the table above.
|
|
28
|
+
SUPPORTED_DATA_DESCRIPTION = "a .csv, .h5ad or .zarr file"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_adapter(data_type: str):
|
|
32
|
+
"""Look up the adapter class registered for a project's data type."""
|
|
33
|
+
try:
|
|
34
|
+
return _ADAPTERS[data_type]
|
|
35
|
+
except KeyError:
|
|
36
|
+
raise ValueError(f"Unknown datasource data_type: {data_type!r}") from None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def detect_data_type(path) -> str:
|
|
40
|
+
"""Which adapter reads the file at `path`.
|
|
41
|
+
|
|
42
|
+
The upload page has one Data input rather than a tab per format, so this
|
|
43
|
+
is what decides where a dropped path goes. It reads the filesystem -- a
|
|
44
|
+
.zarr store is a directory, and only looking inside distinguishes a
|
|
45
|
+
SpatialData store from a plain zarr-backed AnnData -- so it is a detection
|
|
46
|
+
step, not a string parse.
|
|
47
|
+
|
|
48
|
+
Raises ValueError naming the accepted formats, which is what the upload
|
|
49
|
+
form shows: an unrecognized path is ordinary user error, not a bug.
|
|
50
|
+
"""
|
|
51
|
+
path = Path(path).expanduser()
|
|
52
|
+
if not path.exists():
|
|
53
|
+
raise ValueError(f"No such file: {path}")
|
|
54
|
+
|
|
55
|
+
suffix = path.suffix.lower()
|
|
56
|
+
if path.is_dir():
|
|
57
|
+
if suffix != ".zarr":
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"{path.name} is a directory but not a .zarr store. Provide "
|
|
60
|
+
f"{SUPPORTED_DATA_DESCRIPTION}."
|
|
61
|
+
)
|
|
62
|
+
return "spatialdata" if _has_spatialdata_tables(path) else "anndata"
|
|
63
|
+
|
|
64
|
+
data_type = _SUFFIX_TYPES.get(suffix)
|
|
65
|
+
if data_type is None:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"Cannot read {path.name}: expected {SUPPORTED_DATA_DESCRIPTION}."
|
|
68
|
+
)
|
|
69
|
+
return data_type
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _has_spatialdata_tables(store) -> bool:
|
|
73
|
+
"""Whether a .zarr directory is a SpatialData store rather than a bare
|
|
74
|
+
AnnData written to zarr. Structural, and cheap: SpatialData keeps its
|
|
75
|
+
tables under a `tables/` group, and a plain AnnData has no such thing.
|
|
76
|
+
Nothing is opened -- a store with thousands of chunks costs one stat."""
|
|
77
|
+
from .spatialdata_adapter import TABLES_GROUP
|
|
78
|
+
|
|
79
|
+
return (Path(store) / TABLES_GROUP).is_dir()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
__all__ = [
|
|
83
|
+
"DatasourceAdapter",
|
|
84
|
+
"MetadataColumn",
|
|
85
|
+
"NormalizedDatasource",
|
|
86
|
+
"CsvAdapter",
|
|
87
|
+
"AnnDataAdapter",
|
|
88
|
+
"SpatialDataAdapter",
|
|
89
|
+
"SUPPORTED_DATA_DESCRIPTION",
|
|
90
|
+
"classify_columns",
|
|
91
|
+
"classify_from_inspection",
|
|
92
|
+
"detect_data_type",
|
|
93
|
+
"get_adapter",
|
|
94
|
+
]
|