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.
Files changed (248) hide show
  1. plexora/__init__.py +129 -0
  2. plexora/__main__.py +15 -0
  3. plexora/_url.py +105 -0
  4. plexora/api/__init__.py +55 -0
  5. plexora/api/dataset.py +398 -0
  6. plexora/api/http.py +19 -0
  7. plexora/api/plugin.py +541 -0
  8. plexora/api/store.py +221 -0
  9. plexora/cli.py +777 -0
  10. plexora/client/dist/354_bundle.js +2 -0
  11. plexora/client/dist/354_bundle.js.LICENSE.txt +20 -0
  12. plexora/client/dist/418_bundle.js +2 -0
  13. plexora/client/dist/418_bundle.js.LICENSE.txt +1 -0
  14. plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +19 -0
  15. plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +18 -0
  16. plexora/client/dist/770_bundle.js +1 -0
  17. plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +23 -0
  18. plexora/client/dist/vendor_bundle.js +2 -0
  19. plexora/client/dist/vendor_bundle.js.LICENSE.txt +308 -0
  20. plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +28 -0
  21. plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +140 -0
  22. plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +501 -0
  23. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +17 -0
  24. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +116 -0
  25. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
  26. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
  27. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
  28. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
  29. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
  30. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
  31. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
  32. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
  33. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +9 -0
  34. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
  35. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
  36. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
  37. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
  38. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
  39. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
  40. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
  41. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
  42. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
  43. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
  44. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
  45. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
  46. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
  47. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
  48. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
  49. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
  50. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
  51. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
  52. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
  53. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
  54. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
  55. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
  56. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
  57. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
  58. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
  59. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
  60. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
  61. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
  62. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
  63. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
  64. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
  65. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
  66. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +562 -0
  67. plexora/client/src/css/import.css +474 -0
  68. plexora/client/src/css/main.css +903 -0
  69. plexora/client/src/css/openProject.css +363 -0
  70. plexora/client/src/css/quickView.css +166 -0
  71. plexora/client/src/css/tokens.css +51 -0
  72. plexora/client/src/css/viewer.css +1655 -0
  73. plexora/client/src/img/apple-touch-icon.png +0 -0
  74. plexora/client/src/img/favicon.ico +0 -0
  75. plexora/client/src/img/logo.ai +8892 -33
  76. plexora/client/src/img/logo.svg +1 -0
  77. plexora/client/src/img/logo_with_text.ai +9011 -34
  78. plexora/client/src/img/logo_with_text.svg +1 -0
  79. plexora/client/src/js/main.js +704 -0
  80. plexora/client/src/js/pluginRegistry.js +108 -0
  81. plexora/client/src/js/services/appStatus.js +286 -0
  82. plexora/client/src/js/services/browsePicker.js +73 -0
  83. plexora/client/src/js/services/dataLayer.js +400 -0
  84. plexora/client/src/js/services/datasetContext.js +143 -0
  85. plexora/client/src/js/services/glRenderer.js +143 -0
  86. plexora/client/src/js/services/importFormValidation.js +257 -0
  87. plexora/client/src/js/services/numericData.js +100 -0
  88. plexora/client/src/js/services/passVariablesToFrontend.js +20 -0
  89. plexora/client/src/js/services/simpleEventHandler.js +25 -0
  90. plexora/client/src/js/vendor.js +29 -0
  91. plexora/client/src/js/views/channelList.js +663 -0
  92. plexora/client/src/js/views/colorSwatchPicker.js +202 -0
  93. plexora/client/src/js/views/columnClassifier.js +146 -0
  94. plexora/client/src/js/views/coordinateField.js +133 -0
  95. plexora/client/src/js/views/dataSourceField.js +276 -0
  96. plexora/client/src/js/views/imageViewer.js +3306 -0
  97. plexora/client/src/js/views/miniMap.js +787 -0
  98. plexora/client/src/js/views/navbarControls.js +178 -0
  99. plexora/client/src/js/views/openProjectPage.js +239 -0
  100. plexora/client/src/js/views/projectEdit.js +335 -0
  101. plexora/client/src/js/views/quickViewLanding.js +136 -0
  102. plexora/client/src/js/views/rainbow.js +326 -0
  103. plexora/client/src/js/views/requirementsModal.js +572 -0
  104. plexora/client/src/js/views/rgbImageViewer.js +115 -0
  105. plexora/client/src/js/views/searchableSelect.js +411 -0
  106. plexora/client/src/js/views/segmentationProgress.js +122 -0
  107. plexora/client/src/js/views/toolLoader.js +747 -0
  108. plexora/client/src/js/views/viewerControls.js +689 -0
  109. plexora/client/src/js/views/viewerManager.js +434 -0
  110. plexora/client/src/js/views/viewerSidebar.js +1117 -0
  111. plexora/client/src/js/workers/tileDecoder.js +130 -0
  112. plexora/client/src/shaders/frag.glsl +528 -0
  113. plexora/client/src/shaders/vert.glsl +12 -0
  114. plexora/client/templates/base.html +212 -0
  115. plexora/client/templates/index.html +238 -0
  116. plexora/client/templates/open_project.html +96 -0
  117. plexora/client/templates/project_columns.html +86 -0
  118. plexora/client/templates/project_edit.html +196 -0
  119. plexora/client/templates/upload.html +118 -0
  120. plexora/connect.py +528 -0
  121. plexora/datasource.py +782 -0
  122. plexora/jupyter.py +406 -0
  123. plexora/notebook_env.py +232 -0
  124. plexora/paths.py +486 -0
  125. plexora/plugins/__init__.py +12 -0
  126. plexora/plugins/cell_explorer/__init__.py +100 -0
  127. plexora/plugins/cell_explorer/server/__init__.py +11 -0
  128. plexora/plugins/cell_explorer/server/routes.py +167 -0
  129. plexora/plugins/cell_explorer/server/state.py +281 -0
  130. plexora/plugins/cell_explorer/server/values.py +145 -0
  131. plexora/plugins/cell_explorer/server/variables.py +367 -0
  132. plexora/plugins/cell_explorer/static/cellExplorerApi.js +158 -0
  133. plexora/plugins/cell_explorer/static/cellExplorerColors.js +316 -0
  134. plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +403 -0
  135. plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +133 -0
  136. plexora/plugins/cell_explorer/static/cellExplorerLegend.js +188 -0
  137. plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +822 -0
  138. plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +826 -0
  139. plexora/plugins/cell_explorer/static/cellExplorerState.js +372 -0
  140. plexora/plugins/cell_explorer/static/cell_explorer.css +752 -0
  141. plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +137 -0
  142. plexora/plugins/figure_builder/__init__.py +125 -0
  143. plexora/plugins/figure_builder/server/__init__.py +7 -0
  144. plexora/plugins/figure_builder/server/compose.py +365 -0
  145. plexora/plugins/figure_builder/server/export.py +784 -0
  146. plexora/plugins/figure_builder/server/export_jobs.py +148 -0
  147. plexora/plugins/figure_builder/server/operations.py +556 -0
  148. plexora/plugins/figure_builder/server/pixels.py +142 -0
  149. plexora/plugins/figure_builder/server/provenance.py +174 -0
  150. plexora/plugins/figure_builder/server/render.py +278 -0
  151. plexora/plugins/figure_builder/server/repository.py +701 -0
  152. plexora/plugins/figure_builder/server/routes.py +594 -0
  153. plexora/plugins/figure_builder/server/schema.py +1023 -0
  154. plexora/plugins/figure_builder/server/sources.py +120 -0
  155. plexora/plugins/figure_builder/server/textmetrics.py +155 -0
  156. plexora/plugins/figure_builder/static/figureActions.js +363 -0
  157. plexora/plugins/figure_builder/static/figureBuilderApi.js +255 -0
  158. plexora/plugins/figure_builder/static/figureCanvas.js +2362 -0
  159. plexora/plugins/figure_builder/static/figureCaptureBoxes.js +369 -0
  160. plexora/plugins/figure_builder/static/figureCaptureDock.js +466 -0
  161. plexora/plugins/figure_builder/static/figureCaptureTool.js +1034 -0
  162. plexora/plugins/figure_builder/static/figureConfirm.js +136 -0
  163. plexora/plugins/figure_builder/static/figureContextBar.js +1193 -0
  164. plexora/plugins/figure_builder/static/figureContextMenu.js +253 -0
  165. plexora/plugins/figure_builder/static/figureDocumentState.js +275 -0
  166. plexora/plugins/figure_builder/static/figureExportUi.js +174 -0
  167. plexora/plugins/figure_builder/static/figureLibrary.js +247 -0
  168. plexora/plugins/figure_builder/static/figureQuickEdit.js +628 -0
  169. plexora/plugins/figure_builder/static/figureRichText.js +634 -0
  170. plexora/plugins/figure_builder/static/figureSceneSnapshot.js +361 -0
  171. plexora/plugins/figure_builder/static/figureSchema.js +218 -0
  172. plexora/plugins/figure_builder/static/figureSidebarController.js +1159 -0
  173. plexora/plugins/figure_builder/static/figureTextEditor.js +503 -0
  174. plexora/plugins/figure_builder/static/figureTextPanel.js +491 -0
  175. plexora/plugins/figure_builder/static/figureViewOptions.js +273 -0
  176. plexora/plugins/figure_builder/static/figureWorkspace.js +2030 -0
  177. plexora/plugins/figure_builder/static/figure_builder.css +3292 -0
  178. plexora/plugins/figure_builder/templates/figure_builder/library.html +74 -0
  179. plexora/plugins/figure_builder/templates/figure_builder/workspace.html +27 -0
  180. plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +458 -0
  181. plexora/plugins/gating/__init__.py +76 -0
  182. plexora/plugins/gating/server/__init__.py +0 -0
  183. plexora/plugins/gating/server/anndata_gates.py +367 -0
  184. plexora/plugins/gating/server/database.py +16 -0
  185. plexora/plugins/gating/server/model.py +320 -0
  186. plexora/plugins/gating/server/routes.py +235 -0
  187. plexora/plugins/gating/static/csvGatingList.js +947 -0
  188. plexora/plugins/gating/static/gating.css +289 -0
  189. plexora/plugins/gating/static/gatingApi.js +221 -0
  190. plexora/plugins/gating/static/gatingSidebarController.js +513 -0
  191. plexora/plugins/gating/templates/gating/legacy.html +26 -0
  192. plexora/plugins/gating/templates/gating/panel.html +69 -0
  193. plexora/plugins/roi/__init__.py +95 -0
  194. plexora/plugins/roi/server/__init__.py +8 -0
  195. plexora/plugins/roi/server/adapters.py +655 -0
  196. plexora/plugins/roi/server/geojson.py +267 -0
  197. plexora/plugins/roi/server/geometry.py +181 -0
  198. plexora/plugins/roi/server/mapping.py +169 -0
  199. plexora/plugins/roi/server/operations.py +293 -0
  200. plexora/plugins/roi/server/repository.py +215 -0
  201. plexora/plugins/roi/server/routes.py +409 -0
  202. plexora/plugins/roi/server/schema.py +323 -0
  203. plexora/plugins/roi/static/roi.css +583 -0
  204. plexora/plugins/roi/static/roiApi.js +177 -0
  205. plexora/plugins/roi/static/roiFigureBridge.js +107 -0
  206. plexora/plugins/roi/static/roiGeometry.js +350 -0
  207. plexora/plugins/roi/static/roiRenderer.js +278 -0
  208. plexora/plugins/roi/static/roiSidebarController.js +1038 -0
  209. plexora/plugins/roi/static/roiState.js +518 -0
  210. plexora/plugins/roi/static/roiTools.js +993 -0
  211. plexora/plugins/roi/templates/roi/panel.html +240 -0
  212. plexora/proxy.py +57 -0
  213. plexora/server/models/adapters/__init__.py +94 -0
  214. plexora/server/models/adapters/anndata_adapter.py +388 -0
  215. plexora/server/models/adapters/base.py +93 -0
  216. plexora/server/models/adapters/classify.py +192 -0
  217. plexora/server/models/adapters/csv_adapter.py +105 -0
  218. plexora/server/models/adapters/inspection.py +265 -0
  219. plexora/server/models/adapters/spatialdata_adapter.py +248 -0
  220. plexora/server/models/centroid_tiles.py +305 -0
  221. plexora/server/models/data_model.py +1659 -0
  222. plexora/server/models/database_model.py +169 -0
  223. plexora/server/models/project.py +1270 -0
  224. plexora/server/plugins.py +199 -0
  225. plexora/server/routes/browse_routes.py +41 -0
  226. plexora/server/routes/data_routes.py +294 -0
  227. plexora/server/routes/import_routes.py +603 -0
  228. plexora/server/routes/page_routes.py +139 -0
  229. plexora/server/routes/project_routes.py +438 -0
  230. plexora/server/routes/quick_view_routes.py +62 -0
  231. plexora/server/routes/system_routes.py +39 -0
  232. plexora/server/routes/tool_routes.py +418 -0
  233. plexora/server/utils/addHEColumns.py +11 -0
  234. plexora/server/utils/fast_png.py +97 -0
  235. plexora/server/utils/fullConversion.py +50 -0
  236. plexora/server/utils/native_dialog.py +168 -0
  237. plexora/server/utils/pre_normalization.py +47 -0
  238. plexora/server/utils/segmentation_pyramid.py +598 -0
  239. plexora/server/utils/smallestenclosingcircle.py +126 -0
  240. plexora/server/utils/tiffsurgeon.py +372 -0
  241. plexora/server_cli.py +48 -0
  242. plexora/test.py +0 -0
  243. plexora-0.0.1.dist-info/METADATA +341 -0
  244. plexora-0.0.1.dist-info/RECORD +248 -0
  245. plexora-0.0.1.dist-info/WHEEL +5 -0
  246. plexora-0.0.1.dist-info/entry_points.txt +12 -0
  247. plexora-0.0.1.dist-info/licenses/LICENSE +207 -0
  248. plexora-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,196 @@
1
+ {% extends 'base.html' %}
2
+ {% block style %}
3
+ <link rel="stylesheet" href="{{ data.base_url }}/client/src/css/import.css?v=20260819_data_table_pick">
4
+ {% endblock %}
5
+ {% block content %}
6
+ <title>Plexora - Edit {{ data.datasetName }}</title>
7
+ {#
8
+ Generated from the project record: every section below is gated on
9
+ `project.has`, so a project sees only the fields it actually has. The old
10
+ edit page rendered the CSV column-matching screen for every project
11
+ regardless of format, which is what made editing an AnnData project destroy
12
+ it.
13
+ #}
14
+ {% set project = data.project %}
15
+ <div class="import-page">
16
+ <div id="upload-parent">
17
+ {# Single quotes, and they matter. `tojson` escapes < > & and ' but NOT
18
+ the double quotes JSON is built out of, so in a double-quoted
19
+ attribute the value ends at the record's first key and the rest
20
+ spills into the tag as garbage attributes. `id` still parsed, so the
21
+ page looked completely normal -- and projectEdit.js threw on
22
+ JSON.parse("{"), which is where the Save button lost its listener. #}
23
+ <div class="import-card import-card-wide" id="project-edit"
24
+ data-project='{{ project|tojson }}'
25
+ {% if data.needs %}data-needs='{{ data.needs|tojson }}'{% endif %}>
26
+ <span class="import-kicker">Edit Project</span>
27
+ <h1>{{ data.datasetName }}</h1>
28
+
29
+ <div id="edit_needs" class="import-alert" role="status" hidden></div>
30
+
31
+ {# Image: read-only. Everything the project derives is keyed to it. #}
32
+ <div class="config-section">
33
+ <div class="config-section-title">Image</div>
34
+ <p class="config-summary">
35
+ <strong>{{ project.image.src }}</strong><br>
36
+ {{ project.image.channelCount or 0 }} channel(s)
37
+ {% if project.image.size[0] %}
38
+ &middot; {{ project.image.size[0] }} &times; {{ project.image.size[1] }} px
39
+ {% endif %}
40
+ </p>
41
+ <span class="field-hint">The image cannot be changed — import a new project to use a different one.</span>
42
+ </div>
43
+
44
+ <div class="config-section">
45
+ <div class="config-section-title">Segmentation Mask</div>
46
+ <div class="import-field">
47
+ <div class="import-field-row">
48
+ <input type="text" id="edit_segmentation" class="form-control"
49
+ value="{{ project.segmentation.src or '' }}"
50
+ placeholder="/path/to/segmentation.ome.tif">
51
+ <button type="button" class="browse-button" data-browse-target="edit_segmentation"
52
+ data-browse-mode="file" data-browse-filter="image">Browse&hellip;</button>
53
+ </div>
54
+ <span class="field-hint">
55
+ {% if project.segmentation.pending %}
56
+ Converting — the mask will be ready shortly.
57
+ {% else %}
58
+ Clear the box to remove the mask.
59
+ {% endif %}
60
+ </span>
61
+ </div>
62
+ </div>
63
+
64
+ {# Mounted by projectEdit.js from the shared control
65
+ (dataSourceField.js), not written out here: swapping in a .zarr
66
+ store with several tables needs a table picker, and the two
67
+ static Browse buttons this used to carry were never wired to
68
+ anything -- only the upload page's script attaches
69
+ [data-browse-target], and it does not load on this page. #}
70
+ <div class="config-section">
71
+ <div class="config-section-title">Data</div>
72
+ {# The path itself comes from the project record the page is
73
+ already carrying; only the note needs saying here. #}
74
+ <div class="import-field" id="edit_data_field"
75
+ data-hint="{% if project.data %}Detected as {{ project.data.type }}{% if project.data.table %} &middot; table &ldquo;{{ project.data.table }}&rdquo;{% endif %}. Replacing the file re-detects its format.{% else %}CSV, AnnData (.h5ad) or SpatialData (.zarr).{% endif %}">
76
+ </div>
77
+ </div>
78
+
79
+ {# What the numbers in this project actually are. The select is shown
80
+ only when the file carries more than one matrix -- raw counts and a
81
+ log-transformed copy live side by side in one .h5ad, and which was
82
+ read decides what every marker histogram is a histogram of. The
83
+ log1p switch is offered either way: a file may carry raw counts and
84
+ nothing else, and thresholds on those are unreadable untransformed. #}
85
+ {% if project.has.features %}
86
+ <div class="config-section">
87
+ <div class="config-section-title">Expression Values</div>
88
+ {% if project.data.layers %}
89
+ <div class="import-field">
90
+ <select id="edit_features_layer" class="form-select">
91
+ {% for option in project.featureOptions %}
92
+ <option value="{{ option.value }}"
93
+ {% if project.featureSource == option.value %}selected{% endif %}>
94
+ {{ option.label }}
95
+ </option>
96
+ {% endfor %}
97
+ </select>
98
+ <span class="field-hint">Which matrix the marker intensities are read from.
99
+ Thresholds set against one do not carry to another.</span>
100
+ </div>
101
+ {% endif %}
102
+ <div class="import-field">
103
+ <label class="config-check">
104
+ <input type="checkbox" class="form-check-input" id="edit_features_log"
105
+ {% if project.featureLog %}checked{% endif %}>
106
+ <span class="form-check-label">Log-transform (log1p)</span>
107
+ <span class="field-hint">Turn this on when the values above are raw counts.</span>
108
+ </label>
109
+ </div>
110
+ </div>
111
+ {% endif %}
112
+
113
+ {# The read spec is how the adapter gets from file to table. Shown
114
+ so the user can see what was auto-detected; changing it means
115
+ replacing the data file above, which re-detects it. #}
116
+ {% if project.has.readSpec %}
117
+ <div class="config-section">
118
+ <div class="config-section-title">How this file is read</div>
119
+ {# Coordinates are no longer summarised here: they are a real
120
+ control in the Columns section below, because which array
121
+ holds a cell's position is a choice and not a detail. This
122
+ line read "auto-detected" for a project whose source was
123
+ picked by matching an obsm key against a hardcoded list of
124
+ names -- accurate, and no help to anyone it was wrong for. #}
125
+ <p class="config-summary">
126
+ Features: <strong>{{ project.data.features.get('layer') or project.data.features.get('source') or 'X' }}</strong>
127
+ {% if project.data.subset.get('column') %}
128
+ <br>Image: <strong>{{ project.data.subset['column'] }} = {{ project.data.subset['value'] }}</strong>
129
+ {% endif %}
130
+ </p>
131
+ </div>
132
+ {% endif %}
133
+
134
+ {# CSV only: the marker/metadata split, which also owns the roles. #}
135
+ {% if project.has.columns %}
136
+ <div class="config-section">
137
+ <div class="config-section-title">Columns</div>
138
+ <div id="edit_classifier"></div>
139
+ </div>
140
+ {% endif %}
141
+
142
+ {# Everything else with a table: only the roles backed by real
143
+ columns (the adapter synthesizes the rest). #}
144
+ {% if project.has.data and not project.has.columns %}
145
+ <div class="config-section">
146
+ <div class="config-section-title">Columns</div>
147
+ <div id="edit_roles"></div>
148
+ </div>
149
+ {% endif %}
150
+
151
+ {# How tools draw per-cell results. Defaulted rather than asked --
152
+ the mask if there is one, centroids otherwise -- so this is an
153
+ override.
154
+
155
+ "Automatic" is a real option and the one an untouched project is
156
+ on, because the alternative is that saving this page for any
157
+ other reason records a choice nobody made. That mattered: a
158
+ project whose mask was still converting could only offer
159
+ centroids here, and the save froze centroids in place for good
160
+ -- the mask landed minutes later and every tool went on drawing
161
+ points. #}
162
+ {% if project.has.cellLayer %}
163
+ <div class="config-section">
164
+ <div class="config-section-title">Cell Layer</div>
165
+ <div class="import-field">
166
+ <select id="edit_cell_layer" class="form-select"
167
+ data-stored="{{ project.cellLayerChoice or '' }}">
168
+ <option value="" {% if not project.cellLayerChoice %}selected{% endif %}>
169
+ Automatic — the mask if there is one, otherwise centroids
170
+ </option>
171
+ {% for option in project.cellLayerOptions %}
172
+ <option value="{{ option }}" {% if project.cellLayerChoice == option %}selected{% endif %}>
173
+ {% if option == 'segmentation' %}Segmentation mask — real cell outlines
174
+ {% else %}Centroids — one point per cell{% endif %}
175
+ </option>
176
+ {% endfor %}
177
+ </select>
178
+ <span class="field-hint">Used by tools that colour cells, such as Cell Explorer
179
+ and Thresholding. Currently drawing:
180
+ {% if project.cellLayer == 'segmentation' %}the segmentation mask{% else %}centroids{% endif %}.</span>
181
+ </div>
182
+ </div>
183
+ {% endif %}
184
+
185
+ <div class="import-actions">
186
+ <button id="edit_save" class="btn btn-primary" type="button">Save</button>
187
+ </div>
188
+ <div id="edit_error" class="import-alert" role="alert" hidden></div>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ <script src="{{ data.base_url }}/client/src/js/views/columnClassifier.js?v=20260820_csv_markers_log1p"></script>
193
+ <script src="{{ data.base_url }}/client/src/js/views/coordinateField.js?v=20260819_ask_cell_id"></script>
194
+ <script src="{{ data.base_url }}/client/src/js/views/segmentationProgress.js?v=20260817_single_import"></script>
195
+ <script src="{{ data.base_url }}/client/src/js/views/projectEdit.js?v=20260821_cell_panel_one_line" defer></script>
196
+ {% endblock %}
@@ -0,0 +1,118 @@
1
+ {% extends 'base.html' %}
2
+ {% block style %}
3
+ <link rel="stylesheet" href="{{ data.base_url }}/client/src/css/import.css?v=20260819_data_table_pick">
4
+ {% endblock %}
5
+ {% block content %}
6
+ <title>Plexora - New Project</title>
7
+ {#
8
+ One form, three inputs. The format of the data file is detected server-side
9
+ (/inspect_data), so there is no tab strip and no per-format duplicate of the
10
+ name/image/mask fields.
11
+
12
+ The two extra controls below are hidden until the file itself makes them
13
+ necessary: a .zarr store with several tables, and a table spanning several
14
+ images, cannot be read at all until the user picks one. Everything else the
15
+ old import wizard asked for is either worked out from the file or asked for
16
+ later, by whichever feature actually needs it.
17
+ #}
18
+ <div class="import-page">
19
+ <div id="upload-parent">
20
+ <div class="import-card" id="upload-container">
21
+ <span class="import-kicker">New Project</span>
22
+ <h1>Import Data</h1>
23
+ <p class="import-subtitle">An image is all that is required. Add a segmentation mask
24
+ and single-cell data now, or later from the project's Edit page.</p>
25
+
26
+ {% if data.error %}
27
+ <div class="import-alert" role="alert">{{ data.error }}</div>
28
+ {% endif %}
29
+
30
+ <form action="{{ data.base_url }}/import" id="import_form" method="POST"
31
+ class="needs-validation" enctype="multipart/form-data" novalidate>
32
+ <div class="import-field">
33
+ <label for="name">Project Name</label>
34
+ <input type="text" class="form-control" id="name" name="name"
35
+ oninput="markDatasetNameEdited()" required placeholder="e.g. patient_01"
36
+ value="{{ data.form_name or '' }}">
37
+ <div class="invalid-feedback">Please name the project.</div>
38
+ </div>
39
+
40
+ <div class="import-field">
41
+ <label for="image_file">Image</label>
42
+ <div class="import-field-row">
43
+ <input type="text" id="image_file" class="form-control" name="image_file"
44
+ onkeyup="checkFileExistence(this); suggestDatasetName(this);" required
45
+ placeholder="/path/to/image.ome.tif"
46
+ value="{{ data.form_image or '' }}">
47
+ <button type="button" class="browse-button" data-browse-target="image_file"
48
+ data-browse-mode="file" data-browse-filter="image">Browse&hellip;</button>
49
+ </div>
50
+ <div class="invalid-feedback">Please provide a valid path to the image file.</div>
51
+ </div>
52
+
53
+ <div class="import-field">
54
+ <label for="label_file">Segmentation Mask <span class="field-hint">(optional)</span></label>
55
+ <div class="import-field-row">
56
+ <input type="text" id="label_file" class="form-control" name="label_file"
57
+ onkeyup="checkOptionalFileExistence(this)"
58
+ placeholder="/path/to/segmentation.ome.tif"
59
+ value="{{ data.form_mask or '' }}">
60
+ <button type="button" class="browse-button" data-browse-target="label_file"
61
+ data-browse-mode="file" data-browse-filter="image">Browse&hellip;</button>
62
+ </div>
63
+ </div>
64
+
65
+ <div class="import-field">
66
+ <label for="data_file">Data <span class="field-hint">(optional)</span></label>
67
+ <div class="import-field-row">
68
+ <input type="text" id="data_file" class="form-control" name="data_file"
69
+ onkeyup="inspectDataFile(this)"
70
+ placeholder="/path/to/cells.csv"
71
+ value="{{ data.form_data or '' }}">
72
+ {# A .zarr store is a directory and a .csv/.h5ad is a file, and
73
+ one input takes both -- so the picker offers each in turn. #}
74
+ <button type="button" class="browse-button" data-browse-target="data_file"
75
+ data-browse-mode="file" data-browse-filter="data">File&hellip;</button>
76
+ <button type="button" class="browse-button" data-browse-target="data_file"
77
+ data-browse-mode="directory" data-browse-filter="any">Store&hellip;</button>
78
+ </div>
79
+ <span class="field-hint" id="data_file_hint">CSV, AnnData (.h5ad) or SpatialData (.zarr).</span>
80
+ <div class="invalid-feedback" id="data_file_error"></div>
81
+ </div>
82
+
83
+ {# Shown only when the store holds more than one table. #}
84
+ <div class="import-field" id="data_table_field" hidden>
85
+ <label for="data_table">Table</label>
86
+ <select id="data_table" class="form-select" name="data_table"></select>
87
+ <span class="field-hint">This store holds several tables. Choose which one holds the cells.</span>
88
+ </div>
89
+
90
+ {# Which expression matrix to read is deliberately NOT asked here.
91
+ It matters only to a plugin that reads marker intensities, so
92
+ it is that plugin's requirement (Requires(features=True)) and
93
+ the modal asks it -- along with whether to log-transform --
94
+ the first time such a tool opens. Importing an image is not
95
+ the moment to answer a question about thresholding, and this
96
+ form only asks what registering the project cannot proceed
97
+ without. The layer names are still recorded from the
98
+ inspection above, which is what lets the modal offer them
99
+ without reopening the file. #}
100
+
101
+ {# Shown only when the table spans several images. Loading them all
102
+ would silently mix cells from different slides onto one image. #}
103
+ <div class="import-field" id="subset_field" hidden>
104
+ <label for="subset_value">Image</label>
105
+ <input type="hidden" id="subset_column" name="subset_column">
106
+ <select id="subset_value" class="form-select" name="subset_value"></select>
107
+ <span class="field-hint" id="subset_hint"></span>
108
+ </div>
109
+
110
+ <div class="import-actions">
111
+ <button id="upload_button" class="btn btn-primary" type="submit">Create Project</button>
112
+ </div>
113
+ </form>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ <script src="{{ data.base_url }}/client/src/js/services/importFormValidation.js?v=20260819_feature_source" defer></script>
118
+ {% endblock %}