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,168 @@
1
+ # Native OS file/folder picker for the "Browse..." buttons in the quick-view
2
+ # and import UIs. Returns a path only -- never file bytes.
3
+ #
4
+ # macOS goes through `osascript`'s `choose file`/`choose folder` (a real
5
+ # Cocoa panel driven by AppleScript, no Python GUI toolkit involved) rather
6
+ # than tkinter. tkinter's askopenfilename on macOS routes through a hidden Tk
7
+ # root window that Cocoa uses to size/position/parent the panel, and that
8
+ # path is a long-standing source of broken behavior on recent macOS -- the
9
+ # panel can appear tiny and pinned to a screen corner, barely resizable, or
10
+ # even dismiss itself immediately (the click that opened it double-counts as
11
+ # a click "outside" the misplaced panel). osascript's panel is a normal,
12
+ # independently-managed system dialog and doesn't have any of that.
13
+ #
14
+ # Other platforms keep the tkinter subprocess approach, run out-of-process
15
+ # for two reasons: Tk needs to run on the process's *main* thread on macOS
16
+ # (moot here since macOS no longer uses this path, but still true in
17
+ # principle), but more importantly a request handled by waitress lands on an
18
+ # arbitrary worker thread; and a subprocess crash/hang (e.g. no display on a
19
+ # headless box) can't take the server down with it.
20
+ #
21
+ # Only meant for the terminal/`python run.py` and Jupyter (`server_cli.py`)
22
+ # launch paths, where sys.executable is a real Python interpreter. It is not
23
+ # safe to use as-is from a frozen/packaged build, where sys.executable is the
24
+ # packaged exe itself.
25
+
26
+ import json
27
+ import subprocess
28
+ import sys
29
+
30
+ # Keyed by the caller's `file_filter` argument (kept in sync with
31
+ # browsePicker.js's data-browse-filter values).
32
+ _TK_FILTERS = {
33
+ "image": [("Image files", "*.tif *.tiff *.ome.tif *.ome.tiff *.svs *.qptiff *.png *.jpg *.jpeg"), ("All files", "*.*")],
34
+ "csv": [("CSV files", "*.csv"), ("All files", "*.*")],
35
+ "h5ad": [("AnnData files", "*.h5ad"), ("All files", "*.*")],
36
+ # The single Data input accepts any feature-table format, so its picker
37
+ # offers all of them at once rather than making the user pick a format
38
+ # before picking a file. A .zarr store is a directory and is reached by
39
+ # the same field's "Store..." button in directory mode.
40
+ "data": [("Single-cell data", "*.csv *.tsv *.txt *.h5ad"), ("All files", "*.*")],
41
+ "any": [("All files", "*.*")],
42
+ }
43
+
44
+ #: The filter names callers may ask for. Exported so the route validates
45
+ #: against this table rather than its own hand-typed copy -- the two drifted
46
+ #: once already: "data" was added here for the single Data input on the import
47
+ #: page but not to the route's allowlist, so that field's "File..." button
48
+ #: posted a filter the server rejected with a 400 and did nothing at all.
49
+ FILTER_NAMES = frozenset(_TK_FILTERS)
50
+
51
+ # AppleScript's `choose file of type` filters by Uniform Type Identifier
52
+ # conformance, not literally by filename extension -- an extension string
53
+ # only works here when macOS actually has a UTI registered for it (true for
54
+ # common ones like these image formats, via system/Preview/etc.
55
+ # declarations). ".h5ad" has no such registration on a stock Mac, so filtering
56
+ # by it doesn't just fail to narrow the list -- it greys out every file,
57
+ # including real .h5ad ones, since none of them "conform" to an unregistered
58
+ # type. Left unfiltered (None -> no `of type` clause) rather than chasing a
59
+ # UTI that may not exist on the user's machine.
60
+ _APPLESCRIPT_EXTENSIONS = {
61
+ "image": ["tif", "tiff", "svs", "qptiff", "png", "jpg", "jpeg"],
62
+ "csv": ["csv"],
63
+ "h5ad": None,
64
+ # Unfiltered for the same reason as "h5ad" above: the set includes .h5ad,
65
+ # and one unregistered extension greys out every file in the dialog rather
66
+ # than just failing to match its own.
67
+ "data": None,
68
+ "any": None,
69
+ }
70
+
71
+ # The dialog script only ever receives a fixed, known filter key on argv --
72
+ # never an interpolated filetypes string -- since the script itself is
73
+ # passed to `python -c`.
74
+ _TK_DIALOG_SCRIPT = r"""
75
+ import json, sys
76
+ import tkinter as tk
77
+ from tkinter import filedialog
78
+
79
+ mode = sys.argv[1]
80
+ filter_key = sys.argv[2] if len(sys.argv) > 2 else "any"
81
+
82
+ FILTERS = %(filters)s
83
+
84
+ root = tk.Tk()
85
+ root.withdraw()
86
+ root.attributes("-topmost", True)
87
+ if mode == "directory":
88
+ path = filedialog.askdirectory(parent=root)
89
+ else:
90
+ path = filedialog.askopenfilename(parent=root, filetypes=FILTERS.get(filter_key, FILTERS["any"]))
91
+ root.destroy()
92
+ print(json.dumps({"path": path or None}))
93
+ """ % {"filters": repr(_TK_FILTERS)}
94
+
95
+
96
+ def _browse_for_path_macos(mode, file_filter, timeout):
97
+ if mode == "directory":
98
+ script = 'POSIX path of (choose folder with prompt "Select a folder")'
99
+ else:
100
+ extensions = _APPLESCRIPT_EXTENSIONS.get(file_filter)
101
+ of_type = ""
102
+ if extensions:
103
+ quoted = ", ".join(f'"{ext}"' for ext in extensions)
104
+ of_type = f" of type {{{quoted}}}"
105
+ script = f'POSIX path of (choose file{of_type} with prompt "Select a file")'
106
+
107
+ try:
108
+ result = subprocess.run(
109
+ ["osascript", "-e", script],
110
+ capture_output=True,
111
+ text=True,
112
+ timeout=timeout,
113
+ )
114
+ except subprocess.TimeoutExpired:
115
+ raise RuntimeError("Timed out waiting for the file browser.")
116
+ except OSError as exc:
117
+ raise RuntimeError(str(exc))
118
+
119
+ if result.returncode != 0:
120
+ stderr = (result.stderr or "").strip()
121
+ # AppleScript error -128 is exactly "user cancelled" -- not a failure.
122
+ if "-128" in stderr or "User canceled" in stderr:
123
+ return None
124
+ message = stderr.splitlines()[-1] if stderr else "Could not open the file browser."
125
+ raise RuntimeError(message)
126
+
127
+ path = result.stdout.strip()
128
+ return path or None
129
+
130
+
131
+ def _browse_for_path_tk(mode, file_filter, timeout):
132
+ try:
133
+ result = subprocess.run(
134
+ [sys.executable, "-c", _TK_DIALOG_SCRIPT, mode, file_filter],
135
+ capture_output=True,
136
+ text=True,
137
+ timeout=timeout,
138
+ )
139
+ except subprocess.TimeoutExpired:
140
+ raise RuntimeError("Timed out waiting for the file browser.")
141
+ except OSError as exc:
142
+ raise RuntimeError(str(exc))
143
+
144
+ if result.returncode != 0:
145
+ message = (result.stderr or "").strip().splitlines()
146
+ raise RuntimeError(message[-1] if message else "Could not open the file browser.")
147
+
148
+ try:
149
+ return json.loads(result.stdout.strip().splitlines()[-1])["path"]
150
+ except (ValueError, IndexError, KeyError):
151
+ raise RuntimeError("Unexpected response from the file browser.")
152
+
153
+
154
+ def browse_for_path(mode="file", file_filter="any", timeout=300):
155
+ """Opens a native file/folder picker and returns the chosen absolute
156
+ path, or None if the user cancelled.
157
+
158
+ `file_filter` narrows the file-type dropdown for mode="file" (ignored
159
+ for mode="directory") -- one of "image", "csv", "h5ad", or "any"
160
+ (default, no narrowing beyond "All files").
161
+
162
+ Raises RuntimeError with a user-facing message if no picker could be
163
+ shown at all (no display, tkinter not installed, timed out, ...) so
164
+ callers can fall back to a manual path input.
165
+ """
166
+ if sys.platform == "darwin":
167
+ return _browse_for_path_macos(mode, file_filter, timeout)
168
+ return _browse_for_path_tk(mode, file_filter, timeout)
@@ -0,0 +1,47 @@
1
+ import numpy as np
2
+
3
+
4
+ def preNormalize(input_csvPath, output_csvPath, skip_columns=[]):
5
+ RAW_DATA = np.genfromtxt(input_csvPath, names=True, dtype=float, delimiter=',')
6
+ marker_list = RAW_DATA.dtype.names
7
+ norm_data = RAW_DATA.view((np.float64, len(marker_list)))
8
+
9
+ # A list of markers to skip normalization
10
+ # markers_notToNorm = ['Field_Row', 'Field_Col', 'CellID', 'X_position','Y_position','Percent_Touching','Number_Neighbors','Neighbor_1','Neighbor_2','Neighbor_3','Neighbor_4','Neighbor_5', 'Eccentricity', 'Solidity', 'Extent', 'EulerNumber', 'Perimeter', 'MajorAxisLength', 'MinorAxisLength', 'Orientation', 'X_position', 'Y_position']
11
+ # A list of markers to skip log10 transform
12
+ # markers_notToLog = ['DAPI1', 'A488b1', 'A555b1'] # skip 'DAPI1', 'A488b1', 'A555b1'
13
+ markers_notToLog = [] # nothing to skip
14
+
15
+ for marker_id in range(norm_data.shape[1]):
16
+ if marker_list[marker_id] in skip_columns:
17
+ continue
18
+ if marker_list[marker_id] in markers_notToLog:
19
+ # Log10 transform
20
+ norm_data[:, marker_id] = np.log10(norm_data[:, marker_id] + 1)
21
+ print(marker_list[marker_id], 'with log10 transform')
22
+ else:
23
+ print(marker_list[marker_id], 'without log10 transform')
24
+ # Percentile normalization by mapping [0.1%, 99.9%] into [0, 1]
25
+ min_tile, max_tile = np.percentile(norm_data[:, marker_id], [0.1, 99.9])
26
+
27
+ # norm_data[:, marker_id] =(norm_data[:, marker_id] - min_tile) * (max_tile - min_tile) / (max_tile - min_tile) + min_tile
28
+ norm_data[:, marker_id] = (norm_data[:, marker_id] - min_tile) / (max_tile - min_tile)
29
+ norm_data[:, marker_id] = np.minimum(norm_data[:, marker_id], 1)
30
+ norm_data[:, marker_id] = np.maximum(norm_data[:, marker_id], 0)
31
+
32
+ with open(output_csvPath, 'w') as f:
33
+ for marker_id, marker_name in enumerate(marker_list):
34
+ f.write(marker_name)
35
+ if marker_id != (len(marker_list) - 1):
36
+ f.write(',')
37
+ f.write('\n')
38
+ for norm_row in norm_data:
39
+ for elem_id, norm_elem in enumerate(norm_row):
40
+ f.write(str(norm_elem))
41
+ if elem_id != (norm_row.shape[0] - 1):
42
+ f.write(',')
43
+ f.write('\n')
44
+
45
+ # input_csvPath = 'Sample_23.csv'
46
+ # output_csvPath = 'Sample_23_norm2.csv'
47
+ # preNormalize(input_csvPath, output_csvPath)