everyome 0.1.0a1__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 (491) hide show
  1. everyome/__init__.py +2 -0
  2. everyome/__main__.py +30 -0
  3. everyome/core/__init__.py +1 -0
  4. everyome/core/backends.py +166 -0
  5. everyome/core/demo/__init__.py +38 -0
  6. everyome/core/demo/fake_slurm.py +151 -0
  7. everyome/core/engine.py +78 -0
  8. everyome/core/manifest.py +301 -0
  9. everyome/core/modules.py +38 -0
  10. everyome/core/nfschema.py +69 -0
  11. everyome/core/projects.py +51 -0
  12. everyome/core/records.py +277 -0
  13. everyome/core/rerun.py +114 -0
  14. everyome/core/runs.py +169 -0
  15. everyome/core/server.py +512 -0
  16. everyome/core/session.py +113 -0
  17. everyome/core/sessions.py +68 -0
  18. everyome/core/slurm.py +62 -0
  19. everyome/core/suites.py +74 -0
  20. everyome/modules/__init__.py +1 -0
  21. everyome/modules/rna/__init__.py +1 -0
  22. everyome/modules/rna/bulk_de/__init__.py +1 -0
  23. everyome/modules/rna/bulk_de/demo_data.py +55 -0
  24. everyome/modules/rna/bulk_de/demo_pipeline.py +54 -0
  25. everyome/modules/rna/bulk_de/docs/manual.md +77 -0
  26. everyome/modules/rna/bulk_de/engine.py +191 -0
  27. everyome/modules/rna/bulk_de/helpers.py +68 -0
  28. everyome/modules/rna/bulk_de/methods.py +39 -0
  29. everyome/modules/rna/bulk_de/module.yaml +84 -0
  30. everyome/modules/rna/bulk_de/pipeline/LICENSE-nf-core-rnaseq +21 -0
  31. everyome/modules/rna/bulk_de/pipeline/README.md +9 -0
  32. everyome/modules/rna/bulk_de/pipeline/nextflow_schema.json +1063 -0
  33. everyome/modules/rna/bulk_de/pipeline/schema_input.json +72 -0
  34. everyome/modules/rna/bulk_de/ui.js +168 -0
  35. everyome/modules/singlecell/__init__.py +1 -0
  36. everyome/modules/singlecell/splitseq/__init__.py +1 -0
  37. everyome/modules/singlecell/splitseq/demo_data.py +135 -0
  38. everyome/modules/singlecell/splitseq/docs/manual.md +94 -0
  39. everyome/modules/singlecell/splitseq/docs/playground/UMAP-JS-LICENSE +202 -0
  40. everyome/modules/singlecell/splitseq/docs/playground/play_core.js +237 -0
  41. everyome/modules/singlecell/splitseq/docs/playground/play_ui.js +295 -0
  42. everyome/modules/singlecell/splitseq/docs/playground/umap-js.min.js +1 -0
  43. everyome/modules/singlecell/splitseq/engine.py +371 -0
  44. everyome/modules/singlecell/splitseq/helpers.py +70 -0
  45. everyome/modules/singlecell/splitseq/methods.py +90 -0
  46. everyome/modules/singlecell/splitseq/mock_stage.py +71 -0
  47. everyome/modules/singlecell/splitseq/module.yaml +159 -0
  48. everyome/modules/singlecell/splitseq/stage.py +82 -0
  49. everyome/modules/singlecell/splitseq/templates/cluster_identities_template.xlsx +0 -0
  50. everyome/modules/singlecell/splitseq/templates/round1_barcodes_template.xlsx +0 -0
  51. everyome/modules/singlecell/splitseq/ui.js +298 -0
  52. everyome/modules/singlecell/splitseq/workflow/python/10_label_cells.py +114 -0
  53. everyome/modules/singlecell/splitseq/workflow/python/11_qc_filter.py +56 -0
  54. everyome/modules/singlecell/splitseq/workflow/python/12_integrate_cluster.py +85 -0
  55. everyome/modules/singlecell/splitseq/workflow/python/13_marker_plots.py +92 -0
  56. everyome/modules/singlecell/splitseq/workflow/python/14_marker_reports.py +196 -0
  57. everyome/modules/singlecell/splitseq/workflow/python/15_annotate_clusters.py +115 -0
  58. everyome/modules/singlecell/splitseq/workflow/python/common.py +121 -0
  59. everyome/modules/singlecell/splitseq/workflow/reads/01_read_preprocessing.sh +101 -0
  60. everyome/modules/singlecell/splitseq/workflow/reads/02_align_merge_dge.sh +98 -0
  61. everyome/modules/singlecell/splitseq/workflow/reads/common.sh +57 -0
  62. everyome/modules/tutorial/__init__.py +1 -0
  63. everyome/modules/tutorial/sequence_summary/__init__.py +1 -0
  64. everyome/modules/tutorial/sequence_summary/demo/sequences.fasta +366 -0
  65. everyome/modules/tutorial/sequence_summary/docs/manual.md +37 -0
  66. everyome/modules/tutorial/sequence_summary/engine.py +77 -0
  67. everyome/modules/tutorial/sequence_summary/module.yaml +67 -0
  68. everyome/modules/tutorial/sequence_summary/steps.py +73 -0
  69. everyome/modules/tutorial/sequence_summary/ui.js +43 -0
  70. everyome/static/app.css +272 -0
  71. everyome/static/app.js +824 -0
  72. everyome/static/fonts/albert-sans-LICENSE +93 -0
  73. everyome/static/fonts/albert-sans-latin-400-italic.woff2 +0 -0
  74. everyome/static/fonts/albert-sans-latin-400-normal.woff2 +0 -0
  75. everyome/static/fonts/albert-sans-latin-500-italic.woff2 +0 -0
  76. everyome/static/fonts/albert-sans-latin-500-normal.woff2 +0 -0
  77. everyome/static/fonts/albert-sans-latin-600-italic.woff2 +0 -0
  78. everyome/static/fonts/albert-sans-latin-600-normal.woff2 +0 -0
  79. everyome/static/fonts/albert-sans-latin-700-italic.woff2 +0 -0
  80. everyome/static/fonts/albert-sans-latin-700-normal.woff2 +0 -0
  81. everyome/static/fonts/alegreya-LICENSE +93 -0
  82. everyome/static/fonts/alegreya-latin-400-italic.woff2 +0 -0
  83. everyome/static/fonts/alegreya-latin-400-normal.woff2 +0 -0
  84. everyome/static/fonts/alegreya-latin-500-italic.woff2 +0 -0
  85. everyome/static/fonts/alegreya-latin-500-normal.woff2 +0 -0
  86. everyome/static/fonts/alegreya-latin-600-italic.woff2 +0 -0
  87. everyome/static/fonts/alegreya-latin-600-normal.woff2 +0 -0
  88. everyome/static/fonts/alegreya-latin-700-italic.woff2 +0 -0
  89. everyome/static/fonts/alegreya-latin-700-normal.woff2 +0 -0
  90. everyome/static/fonts/alegreya-sans-LICENSE +93 -0
  91. everyome/static/fonts/alegreya-sans-latin-400-italic.woff2 +0 -0
  92. everyome/static/fonts/alegreya-sans-latin-400-normal.woff2 +0 -0
  93. everyome/static/fonts/alegreya-sans-latin-500-italic.woff2 +0 -0
  94. everyome/static/fonts/alegreya-sans-latin-500-normal.woff2 +0 -0
  95. everyome/static/fonts/alegreya-sans-latin-700-italic.woff2 +0 -0
  96. everyome/static/fonts/alegreya-sans-latin-700-normal.woff2 +0 -0
  97. everyome/static/fonts/archivo-LICENSE +93 -0
  98. everyome/static/fonts/archivo-latin-400-italic.woff2 +0 -0
  99. everyome/static/fonts/archivo-latin-400-normal.woff2 +0 -0
  100. everyome/static/fonts/archivo-latin-500-italic.woff2 +0 -0
  101. everyome/static/fonts/archivo-latin-500-normal.woff2 +0 -0
  102. everyome/static/fonts/archivo-latin-600-italic.woff2 +0 -0
  103. everyome/static/fonts/archivo-latin-600-normal.woff2 +0 -0
  104. everyome/static/fonts/archivo-latin-700-italic.woff2 +0 -0
  105. everyome/static/fonts/archivo-latin-700-normal.woff2 +0 -0
  106. everyome/static/fonts/barlow-LICENSE +93 -0
  107. everyome/static/fonts/barlow-latin-400-italic.woff2 +0 -0
  108. everyome/static/fonts/barlow-latin-400-normal.woff2 +0 -0
  109. everyome/static/fonts/barlow-latin-500-italic.woff2 +0 -0
  110. everyome/static/fonts/barlow-latin-500-normal.woff2 +0 -0
  111. everyome/static/fonts/barlow-latin-600-italic.woff2 +0 -0
  112. everyome/static/fonts/barlow-latin-600-normal.woff2 +0 -0
  113. everyome/static/fonts/barlow-latin-700-italic.woff2 +0 -0
  114. everyome/static/fonts/barlow-latin-700-normal.woff2 +0 -0
  115. everyome/static/fonts/bodoni-moda-LICENSE +93 -0
  116. everyome/static/fonts/bodoni-moda-latin-400-italic.woff2 +0 -0
  117. everyome/static/fonts/bodoni-moda-latin-400-normal.woff2 +0 -0
  118. everyome/static/fonts/bodoni-moda-latin-500-italic.woff2 +0 -0
  119. everyome/static/fonts/bodoni-moda-latin-500-normal.woff2 +0 -0
  120. everyome/static/fonts/bodoni-moda-latin-600-italic.woff2 +0 -0
  121. everyome/static/fonts/bodoni-moda-latin-600-normal.woff2 +0 -0
  122. everyome/static/fonts/bodoni-moda-latin-700-italic.woff2 +0 -0
  123. everyome/static/fonts/bodoni-moda-latin-700-normal.woff2 +0 -0
  124. everyome/static/fonts/bricolage-grotesque-LICENSE +93 -0
  125. everyome/static/fonts/bricolage-grotesque-latin-400-normal.woff2 +0 -0
  126. everyome/static/fonts/bricolage-grotesque-latin-500-normal.woff2 +0 -0
  127. everyome/static/fonts/bricolage-grotesque-latin-600-normal.woff2 +0 -0
  128. everyome/static/fonts/bricolage-grotesque-latin-700-normal.woff2 +0 -0
  129. everyome/static/fonts/chivo-LICENSE +93 -0
  130. everyome/static/fonts/chivo-latin-400-italic.woff2 +0 -0
  131. everyome/static/fonts/chivo-latin-400-normal.woff2 +0 -0
  132. everyome/static/fonts/chivo-latin-500-italic.woff2 +0 -0
  133. everyome/static/fonts/chivo-latin-500-normal.woff2 +0 -0
  134. everyome/static/fonts/chivo-latin-600-italic.woff2 +0 -0
  135. everyome/static/fonts/chivo-latin-600-normal.woff2 +0 -0
  136. everyome/static/fonts/chivo-latin-700-italic.woff2 +0 -0
  137. everyome/static/fonts/chivo-latin-700-normal.woff2 +0 -0
  138. everyome/static/fonts/cormorant-garamond-LICENSE +93 -0
  139. everyome/static/fonts/cormorant-garamond-latin-400-italic.woff2 +0 -0
  140. everyome/static/fonts/cormorant-garamond-latin-400-normal.woff2 +0 -0
  141. everyome/static/fonts/cormorant-garamond-latin-500-italic.woff2 +0 -0
  142. everyome/static/fonts/cormorant-garamond-latin-500-normal.woff2 +0 -0
  143. everyome/static/fonts/cormorant-garamond-latin-600-italic.woff2 +0 -0
  144. everyome/static/fonts/cormorant-garamond-latin-600-normal.woff2 +0 -0
  145. everyome/static/fonts/cormorant-garamond-latin-700-italic.woff2 +0 -0
  146. everyome/static/fonts/cormorant-garamond-latin-700-normal.woff2 +0 -0
  147. everyome/static/fonts/crimson-pro-LICENSE +93 -0
  148. everyome/static/fonts/crimson-pro-latin-400-italic.woff2 +0 -0
  149. everyome/static/fonts/crimson-pro-latin-400-normal.woff2 +0 -0
  150. everyome/static/fonts/crimson-pro-latin-500-italic.woff2 +0 -0
  151. everyome/static/fonts/crimson-pro-latin-500-normal.woff2 +0 -0
  152. everyome/static/fonts/crimson-pro-latin-600-italic.woff2 +0 -0
  153. everyome/static/fonts/crimson-pro-latin-600-normal.woff2 +0 -0
  154. everyome/static/fonts/crimson-pro-latin-700-italic.woff2 +0 -0
  155. everyome/static/fonts/crimson-pro-latin-700-normal.woff2 +0 -0
  156. everyome/static/fonts/dm-sans-LICENSE +93 -0
  157. everyome/static/fonts/dm-sans-latin-400-italic.woff2 +0 -0
  158. everyome/static/fonts/dm-sans-latin-400-normal.woff2 +0 -0
  159. everyome/static/fonts/dm-sans-latin-500-italic.woff2 +0 -0
  160. everyome/static/fonts/dm-sans-latin-500-normal.woff2 +0 -0
  161. everyome/static/fonts/dm-sans-latin-600-italic.woff2 +0 -0
  162. everyome/static/fonts/dm-sans-latin-600-normal.woff2 +0 -0
  163. everyome/static/fonts/dm-sans-latin-700-italic.woff2 +0 -0
  164. everyome/static/fonts/dm-sans-latin-700-normal.woff2 +0 -0
  165. everyome/static/fonts/dm-serif-display-LICENSE +93 -0
  166. everyome/static/fonts/dm-serif-display-latin-400-italic.woff2 +0 -0
  167. everyome/static/fonts/dm-serif-display-latin-400-normal.woff2 +0 -0
  168. everyome/static/fonts/eb-garamond-LICENSE +93 -0
  169. everyome/static/fonts/eb-garamond-latin-400-italic.woff2 +0 -0
  170. everyome/static/fonts/eb-garamond-latin-400-normal.woff2 +0 -0
  171. everyome/static/fonts/eb-garamond-latin-500-italic.woff2 +0 -0
  172. everyome/static/fonts/eb-garamond-latin-500-normal.woff2 +0 -0
  173. everyome/static/fonts/eb-garamond-latin-600-italic.woff2 +0 -0
  174. everyome/static/fonts/eb-garamond-latin-600-normal.woff2 +0 -0
  175. everyome/static/fonts/eb-garamond-latin-700-italic.woff2 +0 -0
  176. everyome/static/fonts/eb-garamond-latin-700-normal.woff2 +0 -0
  177. everyome/static/fonts/epilogue-LICENSE +93 -0
  178. everyome/static/fonts/epilogue-latin-400-italic.woff2 +0 -0
  179. everyome/static/fonts/epilogue-latin-400-normal.woff2 +0 -0
  180. everyome/static/fonts/epilogue-latin-500-italic.woff2 +0 -0
  181. everyome/static/fonts/epilogue-latin-500-normal.woff2 +0 -0
  182. everyome/static/fonts/epilogue-latin-600-italic.woff2 +0 -0
  183. everyome/static/fonts/epilogue-latin-600-normal.woff2 +0 -0
  184. everyome/static/fonts/epilogue-latin-700-italic.woff2 +0 -0
  185. everyome/static/fonts/epilogue-latin-700-normal.woff2 +0 -0
  186. everyome/static/fonts/exo-2-LICENSE +93 -0
  187. everyome/static/fonts/exo-2-latin-400-italic.woff2 +0 -0
  188. everyome/static/fonts/exo-2-latin-400-normal.woff2 +0 -0
  189. everyome/static/fonts/exo-2-latin-500-italic.woff2 +0 -0
  190. everyome/static/fonts/exo-2-latin-500-normal.woff2 +0 -0
  191. everyome/static/fonts/exo-2-latin-600-italic.woff2 +0 -0
  192. everyome/static/fonts/exo-2-latin-600-normal.woff2 +0 -0
  193. everyome/static/fonts/exo-2-latin-700-italic.woff2 +0 -0
  194. everyome/static/fonts/exo-2-latin-700-normal.woff2 +0 -0
  195. everyome/static/fonts/figtree-LICENSE +93 -0
  196. everyome/static/fonts/figtree-latin-400-italic.woff2 +0 -0
  197. everyome/static/fonts/figtree-latin-400-normal.woff2 +0 -0
  198. everyome/static/fonts/figtree-latin-500-italic.woff2 +0 -0
  199. everyome/static/fonts/figtree-latin-500-normal.woff2 +0 -0
  200. everyome/static/fonts/figtree-latin-600-italic.woff2 +0 -0
  201. everyome/static/fonts/figtree-latin-600-normal.woff2 +0 -0
  202. everyome/static/fonts/figtree-latin-700-italic.woff2 +0 -0
  203. everyome/static/fonts/figtree-latin-700-normal.woff2 +0 -0
  204. everyome/static/fonts/fonts.css +357 -0
  205. everyome/static/fonts/fraunces-LICENSE +93 -0
  206. everyome/static/fonts/fraunces-latin-400-italic.woff2 +0 -0
  207. everyome/static/fonts/fraunces-latin-400-normal.woff2 +0 -0
  208. everyome/static/fonts/fraunces-latin-500-italic.woff2 +0 -0
  209. everyome/static/fonts/fraunces-latin-500-normal.woff2 +0 -0
  210. everyome/static/fonts/fraunces-latin-600-italic.woff2 +0 -0
  211. everyome/static/fonts/fraunces-latin-600-normal.woff2 +0 -0
  212. everyome/static/fonts/fraunces-latin-700-italic.woff2 +0 -0
  213. everyome/static/fonts/fraunces-latin-700-normal.woff2 +0 -0
  214. everyome/static/fonts/gloock-LICENSE +93 -0
  215. everyome/static/fonts/gloock-latin-400-normal.woff2 +0 -0
  216. everyome/static/fonts/hanken-grotesk-LICENSE +93 -0
  217. everyome/static/fonts/hanken-grotesk-latin-400-italic.woff2 +0 -0
  218. everyome/static/fonts/hanken-grotesk-latin-400-normal.woff2 +0 -0
  219. everyome/static/fonts/hanken-grotesk-latin-500-italic.woff2 +0 -0
  220. everyome/static/fonts/hanken-grotesk-latin-500-normal.woff2 +0 -0
  221. everyome/static/fonts/hanken-grotesk-latin-600-italic.woff2 +0 -0
  222. everyome/static/fonts/hanken-grotesk-latin-600-normal.woff2 +0 -0
  223. everyome/static/fonts/hanken-grotesk-latin-700-italic.woff2 +0 -0
  224. everyome/static/fonts/hanken-grotesk-latin-700-normal.woff2 +0 -0
  225. everyome/static/fonts/ibm-plex-mono-LICENSE +93 -0
  226. everyome/static/fonts/ibm-plex-mono-latin-400-italic.woff2 +0 -0
  227. everyome/static/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  228. everyome/static/fonts/ibm-plex-mono-latin-500-italic.woff2 +0 -0
  229. everyome/static/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  230. everyome/static/fonts/ibm-plex-mono-latin-600-italic.woff2 +0 -0
  231. everyome/static/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  232. everyome/static/fonts/ibm-plex-mono-latin-700-italic.woff2 +0 -0
  233. everyome/static/fonts/ibm-plex-mono-latin-700-normal.woff2 +0 -0
  234. everyome/static/fonts/ibm-plex-sans-LICENSE +93 -0
  235. everyome/static/fonts/ibm-plex-sans-latin-400-italic.woff2 +0 -0
  236. everyome/static/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  237. everyome/static/fonts/ibm-plex-sans-latin-500-italic.woff2 +0 -0
  238. everyome/static/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  239. everyome/static/fonts/ibm-plex-sans-latin-600-italic.woff2 +0 -0
  240. everyome/static/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  241. everyome/static/fonts/ibm-plex-sans-latin-700-italic.woff2 +0 -0
  242. everyome/static/fonts/ibm-plex-sans-latin-700-normal.woff2 +0 -0
  243. everyome/static/fonts/ibm-plex-serif-LICENSE +93 -0
  244. everyome/static/fonts/ibm-plex-serif-latin-400-italic.woff2 +0 -0
  245. everyome/static/fonts/ibm-plex-serif-latin-400-normal.woff2 +0 -0
  246. everyome/static/fonts/ibm-plex-serif-latin-500-italic.woff2 +0 -0
  247. everyome/static/fonts/ibm-plex-serif-latin-500-normal.woff2 +0 -0
  248. everyome/static/fonts/ibm-plex-serif-latin-600-italic.woff2 +0 -0
  249. everyome/static/fonts/ibm-plex-serif-latin-600-normal.woff2 +0 -0
  250. everyome/static/fonts/ibm-plex-serif-latin-700-italic.woff2 +0 -0
  251. everyome/static/fonts/ibm-plex-serif-latin-700-normal.woff2 +0 -0
  252. everyome/static/fonts/instrument-sans-LICENSE +93 -0
  253. everyome/static/fonts/instrument-sans-latin-400-italic.woff2 +0 -0
  254. everyome/static/fonts/instrument-sans-latin-400-normal.woff2 +0 -0
  255. everyome/static/fonts/instrument-sans-latin-500-italic.woff2 +0 -0
  256. everyome/static/fonts/instrument-sans-latin-500-normal.woff2 +0 -0
  257. everyome/static/fonts/instrument-sans-latin-600-italic.woff2 +0 -0
  258. everyome/static/fonts/instrument-sans-latin-600-normal.woff2 +0 -0
  259. everyome/static/fonts/instrument-sans-latin-700-italic.woff2 +0 -0
  260. everyome/static/fonts/instrument-sans-latin-700-normal.woff2 +0 -0
  261. everyome/static/fonts/instrument-serif-LICENSE +93 -0
  262. everyome/static/fonts/instrument-serif-latin-400-italic.woff2 +0 -0
  263. everyome/static/fonts/instrument-serif-latin-400-normal.woff2 +0 -0
  264. everyome/static/fonts/jetbrains-mono-LICENSE +93 -0
  265. everyome/static/fonts/jetbrains-mono-latin-400-italic.woff2 +0 -0
  266. everyome/static/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  267. everyome/static/fonts/jetbrains-mono-latin-500-italic.woff2 +0 -0
  268. everyome/static/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  269. everyome/static/fonts/jetbrains-mono-latin-600-italic.woff2 +0 -0
  270. everyome/static/fonts/jetbrains-mono-latin-600-normal.woff2 +0 -0
  271. everyome/static/fonts/jetbrains-mono-latin-700-italic.woff2 +0 -0
  272. everyome/static/fonts/jetbrains-mono-latin-700-normal.woff2 +0 -0
  273. everyome/static/fonts/josefin-sans-LICENSE +93 -0
  274. everyome/static/fonts/josefin-sans-latin-400-italic.woff2 +0 -0
  275. everyome/static/fonts/josefin-sans-latin-400-normal.woff2 +0 -0
  276. everyome/static/fonts/josefin-sans-latin-500-italic.woff2 +0 -0
  277. everyome/static/fonts/josefin-sans-latin-500-normal.woff2 +0 -0
  278. everyome/static/fonts/josefin-sans-latin-600-italic.woff2 +0 -0
  279. everyome/static/fonts/josefin-sans-latin-600-normal.woff2 +0 -0
  280. everyome/static/fonts/josefin-sans-latin-700-italic.woff2 +0 -0
  281. everyome/static/fonts/josefin-sans-latin-700-normal.woff2 +0 -0
  282. everyome/static/fonts/jost-LICENSE +93 -0
  283. everyome/static/fonts/jost-latin-400-italic.woff2 +0 -0
  284. everyome/static/fonts/jost-latin-400-normal.woff2 +0 -0
  285. everyome/static/fonts/jost-latin-500-italic.woff2 +0 -0
  286. everyome/static/fonts/jost-latin-500-normal.woff2 +0 -0
  287. everyome/static/fonts/jost-latin-600-italic.woff2 +0 -0
  288. everyome/static/fonts/jost-latin-600-normal.woff2 +0 -0
  289. everyome/static/fonts/jost-latin-700-italic.woff2 +0 -0
  290. everyome/static/fonts/jost-latin-700-normal.woff2 +0 -0
  291. everyome/static/fonts/karla-LICENSE +93 -0
  292. everyome/static/fonts/karla-latin-400-italic.woff2 +0 -0
  293. everyome/static/fonts/karla-latin-400-normal.woff2 +0 -0
  294. everyome/static/fonts/karla-latin-500-italic.woff2 +0 -0
  295. everyome/static/fonts/karla-latin-500-normal.woff2 +0 -0
  296. everyome/static/fonts/karla-latin-600-italic.woff2 +0 -0
  297. everyome/static/fonts/karla-latin-600-normal.woff2 +0 -0
  298. everyome/static/fonts/karla-latin-700-italic.woff2 +0 -0
  299. everyome/static/fonts/karla-latin-700-normal.woff2 +0 -0
  300. everyome/static/fonts/lexend-LICENSE +93 -0
  301. everyome/static/fonts/lexend-latin-400-normal.woff2 +0 -0
  302. everyome/static/fonts/lexend-latin-500-normal.woff2 +0 -0
  303. everyome/static/fonts/lexend-latin-600-normal.woff2 +0 -0
  304. everyome/static/fonts/lexend-latin-700-normal.woff2 +0 -0
  305. everyome/static/fonts/libre-baskerville-LICENSE +93 -0
  306. everyome/static/fonts/libre-baskerville-latin-400-italic.woff2 +0 -0
  307. everyome/static/fonts/libre-baskerville-latin-400-normal.woff2 +0 -0
  308. everyome/static/fonts/libre-baskerville-latin-500-italic.woff2 +0 -0
  309. everyome/static/fonts/libre-baskerville-latin-500-normal.woff2 +0 -0
  310. everyome/static/fonts/libre-baskerville-latin-600-italic.woff2 +0 -0
  311. everyome/static/fonts/libre-baskerville-latin-600-normal.woff2 +0 -0
  312. everyome/static/fonts/libre-baskerville-latin-700-italic.woff2 +0 -0
  313. everyome/static/fonts/libre-baskerville-latin-700-normal.woff2 +0 -0
  314. everyome/static/fonts/lora-LICENSE +93 -0
  315. everyome/static/fonts/lora-latin-400-italic.woff2 +0 -0
  316. everyome/static/fonts/lora-latin-400-normal.woff2 +0 -0
  317. everyome/static/fonts/lora-latin-500-italic.woff2 +0 -0
  318. everyome/static/fonts/lora-latin-500-normal.woff2 +0 -0
  319. everyome/static/fonts/lora-latin-600-italic.woff2 +0 -0
  320. everyome/static/fonts/lora-latin-600-normal.woff2 +0 -0
  321. everyome/static/fonts/lora-latin-700-italic.woff2 +0 -0
  322. everyome/static/fonts/lora-latin-700-normal.woff2 +0 -0
  323. everyome/static/fonts/manrope-LICENSE +93 -0
  324. everyome/static/fonts/manrope-latin-400-normal.woff2 +0 -0
  325. everyome/static/fonts/manrope-latin-500-normal.woff2 +0 -0
  326. everyome/static/fonts/manrope-latin-600-normal.woff2 +0 -0
  327. everyome/static/fonts/manrope-latin-700-normal.woff2 +0 -0
  328. everyome/static/fonts/newsreader-LICENSE +93 -0
  329. everyome/static/fonts/newsreader-latin-400-italic.woff2 +0 -0
  330. everyome/static/fonts/newsreader-latin-400-normal.woff2 +0 -0
  331. everyome/static/fonts/newsreader-latin-500-italic.woff2 +0 -0
  332. everyome/static/fonts/newsreader-latin-500-normal.woff2 +0 -0
  333. everyome/static/fonts/newsreader-latin-600-italic.woff2 +0 -0
  334. everyome/static/fonts/newsreader-latin-600-normal.woff2 +0 -0
  335. everyome/static/fonts/newsreader-latin-700-italic.woff2 +0 -0
  336. everyome/static/fonts/newsreader-latin-700-normal.woff2 +0 -0
  337. everyome/static/fonts/nunito-LICENSE +93 -0
  338. everyome/static/fonts/nunito-latin-400-italic.woff2 +0 -0
  339. everyome/static/fonts/nunito-latin-400-normal.woff2 +0 -0
  340. everyome/static/fonts/nunito-latin-500-italic.woff2 +0 -0
  341. everyome/static/fonts/nunito-latin-500-normal.woff2 +0 -0
  342. everyome/static/fonts/nunito-latin-600-italic.woff2 +0 -0
  343. everyome/static/fonts/nunito-latin-600-normal.woff2 +0 -0
  344. everyome/static/fonts/nunito-latin-700-italic.woff2 +0 -0
  345. everyome/static/fonts/nunito-latin-700-normal.woff2 +0 -0
  346. everyome/static/fonts/onest-LICENSE +93 -0
  347. everyome/static/fonts/onest-latin-400-normal.woff2 +0 -0
  348. everyome/static/fonts/onest-latin-500-normal.woff2 +0 -0
  349. everyome/static/fonts/onest-latin-600-normal.woff2 +0 -0
  350. everyome/static/fonts/onest-latin-700-normal.woff2 +0 -0
  351. everyome/static/fonts/outfit-LICENSE +93 -0
  352. everyome/static/fonts/outfit-latin-400-normal.woff2 +0 -0
  353. everyome/static/fonts/outfit-latin-500-normal.woff2 +0 -0
  354. everyome/static/fonts/outfit-latin-600-normal.woff2 +0 -0
  355. everyome/static/fonts/outfit-latin-700-normal.woff2 +0 -0
  356. everyome/static/fonts/playfair-display-LICENSE +93 -0
  357. everyome/static/fonts/playfair-display-latin-400-italic.woff2 +0 -0
  358. everyome/static/fonts/playfair-display-latin-400-normal.woff2 +0 -0
  359. everyome/static/fonts/playfair-display-latin-500-italic.woff2 +0 -0
  360. everyome/static/fonts/playfair-display-latin-500-normal.woff2 +0 -0
  361. everyome/static/fonts/playfair-display-latin-600-italic.woff2 +0 -0
  362. everyome/static/fonts/playfair-display-latin-600-normal.woff2 +0 -0
  363. everyome/static/fonts/playfair-display-latin-700-italic.woff2 +0 -0
  364. everyome/static/fonts/playfair-display-latin-700-normal.woff2 +0 -0
  365. everyome/static/fonts/plus-jakarta-sans-LICENSE +93 -0
  366. everyome/static/fonts/plus-jakarta-sans-latin-400-italic.woff2 +0 -0
  367. everyome/static/fonts/plus-jakarta-sans-latin-400-normal.woff2 +0 -0
  368. everyome/static/fonts/plus-jakarta-sans-latin-500-italic.woff2 +0 -0
  369. everyome/static/fonts/plus-jakarta-sans-latin-500-normal.woff2 +0 -0
  370. everyome/static/fonts/plus-jakarta-sans-latin-600-italic.woff2 +0 -0
  371. everyome/static/fonts/plus-jakarta-sans-latin-600-normal.woff2 +0 -0
  372. everyome/static/fonts/plus-jakarta-sans-latin-700-italic.woff2 +0 -0
  373. everyome/static/fonts/plus-jakarta-sans-latin-700-normal.woff2 +0 -0
  374. everyome/static/fonts/quicksand-LICENSE +93 -0
  375. everyome/static/fonts/quicksand-latin-400-normal.woff2 +0 -0
  376. everyome/static/fonts/quicksand-latin-500-normal.woff2 +0 -0
  377. everyome/static/fonts/quicksand-latin-600-normal.woff2 +0 -0
  378. everyome/static/fonts/quicksand-latin-700-normal.woff2 +0 -0
  379. everyome/static/fonts/raleway-LICENSE +93 -0
  380. everyome/static/fonts/raleway-latin-400-italic.woff2 +0 -0
  381. everyome/static/fonts/raleway-latin-400-normal.woff2 +0 -0
  382. everyome/static/fonts/raleway-latin-500-italic.woff2 +0 -0
  383. everyome/static/fonts/raleway-latin-500-normal.woff2 +0 -0
  384. everyome/static/fonts/raleway-latin-600-italic.woff2 +0 -0
  385. everyome/static/fonts/raleway-latin-600-normal.woff2 +0 -0
  386. everyome/static/fonts/raleway-latin-700-italic.woff2 +0 -0
  387. everyome/static/fonts/raleway-latin-700-normal.woff2 +0 -0
  388. everyome/static/fonts/red-hat-display-LICENSE +93 -0
  389. everyome/static/fonts/red-hat-display-latin-400-italic.woff2 +0 -0
  390. everyome/static/fonts/red-hat-display-latin-400-normal.woff2 +0 -0
  391. everyome/static/fonts/red-hat-display-latin-500-italic.woff2 +0 -0
  392. everyome/static/fonts/red-hat-display-latin-500-normal.woff2 +0 -0
  393. everyome/static/fonts/red-hat-display-latin-600-italic.woff2 +0 -0
  394. everyome/static/fonts/red-hat-display-latin-600-normal.woff2 +0 -0
  395. everyome/static/fonts/red-hat-display-latin-700-italic.woff2 +0 -0
  396. everyome/static/fonts/red-hat-display-latin-700-normal.woff2 +0 -0
  397. everyome/static/fonts/red-hat-text-LICENSE +93 -0
  398. everyome/static/fonts/red-hat-text-latin-400-italic.woff2 +0 -0
  399. everyome/static/fonts/red-hat-text-latin-400-normal.woff2 +0 -0
  400. everyome/static/fonts/red-hat-text-latin-500-italic.woff2 +0 -0
  401. everyome/static/fonts/red-hat-text-latin-500-normal.woff2 +0 -0
  402. everyome/static/fonts/red-hat-text-latin-600-italic.woff2 +0 -0
  403. everyome/static/fonts/red-hat-text-latin-600-normal.woff2 +0 -0
  404. everyome/static/fonts/red-hat-text-latin-700-italic.woff2 +0 -0
  405. everyome/static/fonts/red-hat-text-latin-700-normal.woff2 +0 -0
  406. everyome/static/fonts/rubik-LICENSE +93 -0
  407. everyome/static/fonts/rubik-latin-400-italic.woff2 +0 -0
  408. everyome/static/fonts/rubik-latin-400-normal.woff2 +0 -0
  409. everyome/static/fonts/rubik-latin-500-italic.woff2 +0 -0
  410. everyome/static/fonts/rubik-latin-500-normal.woff2 +0 -0
  411. everyome/static/fonts/rubik-latin-600-italic.woff2 +0 -0
  412. everyome/static/fonts/rubik-latin-600-normal.woff2 +0 -0
  413. everyome/static/fonts/rubik-latin-700-italic.woff2 +0 -0
  414. everyome/static/fonts/rubik-latin-700-normal.woff2 +0 -0
  415. everyome/static/fonts/schibsted-grotesk-LICENSE +93 -0
  416. everyome/static/fonts/schibsted-grotesk-latin-400-italic.woff2 +0 -0
  417. everyome/static/fonts/schibsted-grotesk-latin-400-normal.woff2 +0 -0
  418. everyome/static/fonts/schibsted-grotesk-latin-500-italic.woff2 +0 -0
  419. everyome/static/fonts/schibsted-grotesk-latin-500-normal.woff2 +0 -0
  420. everyome/static/fonts/schibsted-grotesk-latin-600-italic.woff2 +0 -0
  421. everyome/static/fonts/schibsted-grotesk-latin-600-normal.woff2 +0 -0
  422. everyome/static/fonts/schibsted-grotesk-latin-700-italic.woff2 +0 -0
  423. everyome/static/fonts/schibsted-grotesk-latin-700-normal.woff2 +0 -0
  424. everyome/static/fonts/sora-LICENSE +93 -0
  425. everyome/static/fonts/sora-latin-400-normal.woff2 +0 -0
  426. everyome/static/fonts/sora-latin-500-normal.woff2 +0 -0
  427. everyome/static/fonts/sora-latin-600-normal.woff2 +0 -0
  428. everyome/static/fonts/sora-latin-700-normal.woff2 +0 -0
  429. everyome/static/fonts/source-serif-4-LICENSE +93 -0
  430. everyome/static/fonts/source-serif-4-latin-400-italic.woff2 +0 -0
  431. everyome/static/fonts/source-serif-4-latin-400-normal.woff2 +0 -0
  432. everyome/static/fonts/source-serif-4-latin-500-italic.woff2 +0 -0
  433. everyome/static/fonts/source-serif-4-latin-500-normal.woff2 +0 -0
  434. everyome/static/fonts/source-serif-4-latin-600-italic.woff2 +0 -0
  435. everyome/static/fonts/source-serif-4-latin-600-normal.woff2 +0 -0
  436. everyome/static/fonts/source-serif-4-latin-700-italic.woff2 +0 -0
  437. everyome/static/fonts/source-serif-4-latin-700-normal.woff2 +0 -0
  438. everyome/static/fonts/space-grotesk-LICENSE +93 -0
  439. everyome/static/fonts/space-grotesk-latin-400-normal.woff2 +0 -0
  440. everyome/static/fonts/space-grotesk-latin-500-normal.woff2 +0 -0
  441. everyome/static/fonts/space-grotesk-latin-600-normal.woff2 +0 -0
  442. everyome/static/fonts/space-grotesk-latin-700-normal.woff2 +0 -0
  443. everyome/static/fonts/spectral-LICENSE +93 -0
  444. everyome/static/fonts/spectral-latin-400-italic.woff2 +0 -0
  445. everyome/static/fonts/spectral-latin-400-normal.woff2 +0 -0
  446. everyome/static/fonts/spectral-latin-500-italic.woff2 +0 -0
  447. everyome/static/fonts/spectral-latin-500-normal.woff2 +0 -0
  448. everyome/static/fonts/spectral-latin-600-italic.woff2 +0 -0
  449. everyome/static/fonts/spectral-latin-600-normal.woff2 +0 -0
  450. everyome/static/fonts/spectral-latin-700-italic.woff2 +0 -0
  451. everyome/static/fonts/spectral-latin-700-normal.woff2 +0 -0
  452. everyome/static/fonts/syne-LICENSE +93 -0
  453. everyome/static/fonts/syne-latin-400-normal.woff2 +0 -0
  454. everyome/static/fonts/syne-latin-500-normal.woff2 +0 -0
  455. everyome/static/fonts/syne-latin-600-normal.woff2 +0 -0
  456. everyome/static/fonts/syne-latin-700-normal.woff2 +0 -0
  457. everyome/static/fonts/unbounded-LICENSE +93 -0
  458. everyome/static/fonts/unbounded-latin-400-normal.woff2 +0 -0
  459. everyome/static/fonts/unbounded-latin-500-normal.woff2 +0 -0
  460. everyome/static/fonts/unbounded-latin-600-normal.woff2 +0 -0
  461. everyome/static/fonts/unbounded-latin-700-normal.woff2 +0 -0
  462. everyome/static/fonts/urbanist-LICENSE +93 -0
  463. everyome/static/fonts/urbanist-latin-400-italic.woff2 +0 -0
  464. everyome/static/fonts/urbanist-latin-400-normal.woff2 +0 -0
  465. everyome/static/fonts/urbanist-latin-500-italic.woff2 +0 -0
  466. everyome/static/fonts/urbanist-latin-500-normal.woff2 +0 -0
  467. everyome/static/fonts/urbanist-latin-600-italic.woff2 +0 -0
  468. everyome/static/fonts/urbanist-latin-600-normal.woff2 +0 -0
  469. everyome/static/fonts/urbanist-latin-700-italic.woff2 +0 -0
  470. everyome/static/fonts/urbanist-latin-700-normal.woff2 +0 -0
  471. everyome/static/fonts/work-sans-LICENSE +93 -0
  472. everyome/static/fonts/work-sans-latin-400-italic.woff2 +0 -0
  473. everyome/static/fonts/work-sans-latin-400-normal.woff2 +0 -0
  474. everyome/static/fonts/work-sans-latin-500-italic.woff2 +0 -0
  475. everyome/static/fonts/work-sans-latin-500-normal.woff2 +0 -0
  476. everyome/static/fonts/work-sans-latin-600-italic.woff2 +0 -0
  477. everyome/static/fonts/work-sans-latin-600-normal.woff2 +0 -0
  478. everyome/static/fonts/work-sans-latin-700-italic.woff2 +0 -0
  479. everyome/static/fonts/work-sans-latin-700-normal.woff2 +0 -0
  480. everyome/static/fonts/young-serif-LICENSE +93 -0
  481. everyome/static/fonts/young-serif-latin-400-normal.woff2 +0 -0
  482. everyome/static/index.html +41 -0
  483. everyome/static/themes.json +1 -0
  484. everyome/suites.yaml +92 -0
  485. everyome-0.1.0a1.dist-info/METADATA +121 -0
  486. everyome-0.1.0a1.dist-info/RECORD +491 -0
  487. everyome-0.1.0a1.dist-info/WHEEL +5 -0
  488. everyome-0.1.0a1.dist-info/entry_points.txt +2 -0
  489. everyome-0.1.0a1.dist-info/licenses/LICENSE +21 -0
  490. everyome-0.1.0a1.dist-info/licenses/THIRD_PARTY_NOTICES.md +15 -0
  491. everyome-0.1.0a1.dist-info/top_level.txt +1 -0
everyome/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ """Everyome: every -ome, for everyone. Created by Helen R Horkan PhD (Stowers Institute); please cite it if you use it."""
2
+ __version__ = "0.1.0a1"
everyome/__main__.py ADDED
@@ -0,0 +1,30 @@
1
+ """Start Everyome: `everyome` (or `python -m everyome`) opens the app in your browser."""
2
+ import argparse
3
+ import os
4
+ import threading
5
+ import webbrowser
6
+
7
+ import uvicorn
8
+
9
+
10
+ def main():
11
+ ap = argparse.ArgumentParser(description="Everyome: every -ome, for everyone")
12
+ ap.add_argument("--port", type=int, default=8777)
13
+ ap.add_argument("--no-browser", action="store_true", help="do not open a browser window")
14
+ a = ap.parse_args()
15
+ try:
16
+ import setproctitle
17
+ # Intent: Stowers policy names every process Claude_<user>_<tool>_<stage>.
18
+ setproctitle.setproctitle(f"Claude_{os.environ.get('USER', 'user')}_everyome_app")
19
+ except ImportError:
20
+ pass
21
+ url = f"http://127.0.0.1:{a.port}"
22
+ if not a.no_browser:
23
+ threading.Timer(1.2, lambda: webbrowser.open(url)).start()
24
+ print(f"Everyome is running at {url} (press Ctrl+C to quit)")
25
+ # Intent: listen on localhost only; the app is for this computer's user.
26
+ uvicorn.run("everyome.core.server:app", host="127.0.0.1", port=a.port, log_level="warning")
27
+
28
+
29
+ if __name__ == "__main__":
30
+ main()
@@ -0,0 +1 @@
1
+ """Core: backends, scheduler, projects, runs and the module loader (see docs/PLAN.md, section 3)."""
@@ -0,0 +1,166 @@
1
+ """How the app talks to the computer that does the work (carried over from SPLiTscape).
2
+
3
+ Two backends with one interface:
4
+ * SSHBackend — a real cluster. Uses the system `ssh` client, so the user's
5
+ key, ssh-agent, ~/.ssh/config and jump hosts all apply. The
6
+ app never reads the private key. BatchMode means it never
7
+ prompts for a password: key-based login only.
8
+ * LocalBackend — this computer, inside a folder. With fake Slurm on PATH it is
9
+ the demo cluster; without it, it runs local modules.
10
+
11
+ Both expose: run, read_bytes, write_bytes, listdir, exists, tunnel, close.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ import shlex
17
+ import socket
18
+ import subprocess
19
+ from dataclasses import dataclass
20
+ from pathlib import Path
21
+
22
+
23
+ @dataclass
24
+ class Entry:
25
+ name: str
26
+ is_dir: bool
27
+ size: int
28
+
29
+
30
+ def free_port() -> int:
31
+ with socket.socket() as s:
32
+ s.bind(("127.0.0.1", 0))
33
+ return s.getsockname()[1]
34
+
35
+
36
+ class Backend:
37
+ label = "cluster"
38
+ user = "unknown"
39
+
40
+ def run(self, cmd: str, timeout: float = 60, stdin: bytes | None = None) -> tuple[int, str, str]:
41
+ raise NotImplementedError
42
+
43
+ def read_bytes(self, path: str) -> bytes:
44
+ rc, out, err = self.run_bytes(f"cat {shlex.quote(path)}")
45
+ if rc:
46
+ raise FileNotFoundError(f"{path}: {err.strip()}")
47
+ return out
48
+
49
+ def read_text(self, path: str) -> str:
50
+ return self.read_bytes(path).decode()
51
+
52
+ def write_bytes(self, path: str, data: bytes) -> None:
53
+ q = shlex.quote(path)
54
+ rc, _, err = self.run(f"mkdir -p $(dirname {q}) && cat > {q}", stdin=data)
55
+ if rc:
56
+ raise OSError(f"could not write {path}: {err.strip()}")
57
+
58
+ def write_text(self, path: str, text: str) -> None:
59
+ self.write_bytes(path, text.encode())
60
+
61
+ def exists(self, path: str) -> bool:
62
+ return self.run(f"test -e {shlex.quote(path)}")[0] == 0
63
+
64
+ def listdir(self, path: str) -> list[Entry]:
65
+ # Intent: portable listing (type, size, name), one entry per line.
66
+ cmd = (f"cd {shlex.quote(path)} && for f in * .[!.]*; do [ -e \"$f\" ] || continue; "
67
+ "if [ -d \"$f\" ]; then t=d; s=0; else t=f; s=$(wc -c < \"$f\" | tr -d ' '); fi; "
68
+ "printf '%s\\t%s\\t%s\\n' \"$t\" \"$s\" \"$f\"; done")
69
+ rc, out, err = self.run(cmd)
70
+ if rc:
71
+ raise FileNotFoundError(f"{path}: {err.strip() or 'not a folder'}")
72
+ rows = [line.split("\t", 2) for line in out.splitlines() if line.count("\t") >= 2]
73
+ return sorted((Entry(n, t == "d", int(s)) for t, s, n in rows), key=lambda e: (not e.is_dir, e.name.lower()))
74
+
75
+ def run_bytes(self, cmd: str, timeout: float = 120) -> tuple[int, bytes, str]:
76
+ raise NotImplementedError
77
+
78
+ def tunnel(self, node: str, remote_port: int) -> int:
79
+ """Forward a local port to node:remote_port; returns the local port."""
80
+ raise NotImplementedError
81
+
82
+ def close(self) -> None:
83
+ pass
84
+
85
+
86
+ class LocalBackend(Backend):
87
+ """This computer: a working folder, optionally with fake Slurm commands (the demo cluster)."""
88
+
89
+ def __init__(self, root: Path, fake_slurm_bin: Path | None = None, label: str = "this computer"):
90
+ self.root = root
91
+ self.label = label
92
+ self.env = dict(os.environ)
93
+ if fake_slurm_bin:
94
+ self.env["PATH"] = f"{fake_slurm_bin}{os.pathsep}{self.env.get('PATH', '')}"
95
+ self.env["FAKE_SLURM_HOME"] = str(root / ".slurm")
96
+ # Intent: jobs import this same copy of Everyome, as they would from a cluster install.
97
+ self.env["PYTHONPATH"] = os.pathsep.join(filter(None, [str(Path(__file__).resolve().parents[2]), self.env.get("PYTHONPATH")]))
98
+ self.user = os.environ.get("USER", "user")
99
+ self.host = "localhost"
100
+
101
+ def run(self, cmd, timeout=60, stdin=None):
102
+ p = subprocess.run(["bash", "-c", cmd], cwd=self.root, env=self.env, input=stdin,
103
+ capture_output=True, timeout=timeout)
104
+ return p.returncode, p.stdout.decode(errors="replace"), p.stderr.decode(errors="replace")
105
+
106
+ def run_bytes(self, cmd, timeout=120):
107
+ p = subprocess.run(["bash", "-c", cmd], cwd=self.root, env=self.env, capture_output=True, timeout=timeout)
108
+ return p.returncode, p.stdout, p.stderr.decode(errors="replace")
109
+
110
+ def tunnel(self, node, remote_port):
111
+ return remote_port # same machine: nothing to forward
112
+
113
+
114
+ class SSHBackend(Backend):
115
+ """Real cluster over OpenSSH with connection sharing (one login, many commands)."""
116
+
117
+ def __init__(self, host: str, user: str, port: int = 22, ssh_options: list[str] | None = None):
118
+ self.host, self.user, self.port = host, user, port
119
+ self.label = f"{user}@{host}"
120
+ ctl = Path.home() / ".ssh" / "everyome-%r@%h-%p"
121
+ (Path.home() / ".ssh").mkdir(mode=0o700, exist_ok=True)
122
+ # Intent: share one login across commands (not supported by Windows' OpenSSH).
123
+ share = [] if os.name == "nt" else ["-o", "ControlMaster=auto", "-o", f"ControlPath={ctl}", "-o", "ControlPersist=15m"]
124
+ self.base = ["ssh", "-p", str(port), "-o", "BatchMode=yes", "-o", "ConnectTimeout=15", *share,
125
+ "-o", "ServerAliveInterval=30", *(ssh_options or []), f"{user}@{host}"]
126
+ self.tunnels: list[subprocess.Popen] = []
127
+
128
+ def _ssh(self, cmd, stdin=None, timeout=60):
129
+ # Intent: run under a login shell so the user's cluster PATH (Slurm, modules) is set.
130
+ return subprocess.run([*self.base, f"bash -lc {shlex.quote(cmd)}"], input=stdin, capture_output=True, timeout=timeout)
131
+
132
+ def run(self, cmd, timeout=60, stdin=None):
133
+ p = self._ssh(cmd, stdin, timeout)
134
+ return p.returncode, p.stdout.decode(errors="replace"), p.stderr.decode(errors="replace")
135
+
136
+ def run_bytes(self, cmd, timeout=120):
137
+ p = self._ssh(cmd, None, timeout)
138
+ return p.returncode, p.stdout, p.stderr.decode(errors="replace")
139
+
140
+ def tunnel(self, node, remote_port):
141
+ lport = free_port()
142
+ args = [a for a in self.base if a != f"{self.user}@{self.host}"]
143
+ proc = subprocess.Popen([*args, "-N", "-o", "ExitOnForwardFailure=yes",
144
+ "-L", f"127.0.0.1:{lport}:{node}:{remote_port}", f"{self.user}@{self.host}"],
145
+ stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
146
+ self.tunnels.append(proc)
147
+ return lport
148
+
149
+ def close(self):
150
+ for t in self.tunnels:
151
+ t.terminate()
152
+ subprocess.run([*self.base[:-1], "-O", "exit", f"{self.user}@{self.host}"], capture_output=True)
153
+
154
+
155
+ def describe_ssh_error(err: str) -> str:
156
+ """Turn common ssh failures into plain-language advice."""
157
+ e = err.lower()
158
+ if "permission denied" in e:
159
+ return "The cluster refused your key. Check that your SSH key is added to the cluster and loaded (ssh-add)."
160
+ if "could not resolve" in e or "name or service not known" in e:
161
+ return "That cluster address could not be found. Check the host name, and that you are on the institute network or VPN."
162
+ if "timed out" in e or "no route" in e:
163
+ return "The cluster did not answer. Check you are on the institute network or VPN."
164
+ if "host key verification failed" in e:
165
+ return "Your computer has not seen this cluster before. Log in once from a terminal to accept its host key, or ask IT."
166
+ return err.strip() or "Connection failed."
@@ -0,0 +1,38 @@
1
+ """The demo cluster: a folder on this computer plus a pretend Slurm.
2
+
3
+ <root>/
4
+ projects/ where demo projects are saved
5
+ data/<module id>/ each module's small demo dataset
6
+ .bin/ sbatch, squeue, sacct, scancel (fake_slurm.py)
7
+ .slurm/ the pretend scheduler's job records
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import importlib
12
+ import shlex
13
+ import shutil
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ HERE = Path(__file__).resolve().parent
18
+
19
+
20
+ def setup(root: Path, modules: dict) -> Path:
21
+ """Create or refresh the demo cluster; returns the folder holding the fake Slurm commands."""
22
+ (root / "projects").mkdir(parents=True, exist_ok=True)
23
+ for m in modules.values():
24
+ if m.demo.get("data"):
25
+ # Intent: refresh demo data from the installed modules, never touching users' demo projects.
26
+ shutil.copytree(m.dir / m.demo["data"], root / "data" / m.id, dirs_exist_ok=True)
27
+ if m.demo.get("build") and not (root / "data" / m.id / ".demo_ready").exists():
28
+ # Intent: modules whose demo data is generated (too large to ship) build it once, here.
29
+ mod, _, fn = m.demo["build"].partition(":")
30
+ getattr(importlib.import_module(mod), fn)(root / "data" / m.id)
31
+ (root / "data" / m.id / ".demo_ready").write_text("1")
32
+ bin_dir = root / ".bin"
33
+ bin_dir.mkdir(exist_ok=True)
34
+ for cmd in ("sbatch", "squeue", "sacct", "scancel"):
35
+ f = bin_dir / cmd
36
+ f.write_text(f"#!/bin/bash\nexec {shlex.quote(sys.executable)} {shlex.quote(str(HERE / 'fake_slurm.py'))} {cmd} \"$@\"\n")
37
+ f.chmod(0o755)
38
+ return bin_dir
@@ -0,0 +1,151 @@
1
+ """A tiny stand-in for Slurm, used by the demo cluster.
2
+
3
+ Implements the subset Everyome uses (carried over from SPLiTscape):
4
+ sbatch [--parsable] [--dependency=afterok:ID[:ID]] [--job-name=..] [--output=..] script
5
+ squeue -h -j IDS -o "%i %T %N"
6
+ sacct -n -P -j IDS -o JobID,State,Elapsed
7
+ scancel ID [ID ...]
8
+
9
+ Jobs run as background processes; state lives in $FAKE_SLURM_HOME.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import os
15
+ import re
16
+ import socket
17
+ import subprocess
18
+ import sys
19
+ import time
20
+ from pathlib import Path
21
+
22
+ HOME = Path(os.environ.get("FAKE_SLURM_HOME", Path.home() / ".fake_slurm"))
23
+ JOBS = HOME / "jobs"
24
+
25
+
26
+ def _load(jid):
27
+ return json.loads((JOBS / f"{jid}.json").read_text())
28
+
29
+
30
+ def _save(job):
31
+ tmp = JOBS / f"{job['id']}.json.tmp"
32
+ tmp.write_text(json.dumps(job))
33
+ tmp.replace(JOBS / f"{job['id']}.json")
34
+
35
+
36
+ def _directives(script: str) -> dict:
37
+ out = {}
38
+ for line in Path(script).read_text().splitlines():
39
+ m = re.match(r"#SBATCH\s+--([\w-]+)(?:[= ](.+))?", line.strip())
40
+ if m:
41
+ out[m.group(1)] = (m.group(2) or "").strip()
42
+ return out
43
+
44
+
45
+ def sbatch(args):
46
+ JOBS.mkdir(parents=True, exist_ok=True)
47
+ opts, script = {}, None
48
+ for a in args:
49
+ if a.startswith("--"):
50
+ k, _, v = a[2:].partition("=")
51
+ opts[k] = v
52
+ else:
53
+ script = a
54
+ if not script:
55
+ sys.exit("sbatch: no script given")
56
+ opts = {**_directives(script), **opts}
57
+ counter = HOME / "counter"
58
+ jid = int(counter.read_text()) + 1 if counter.exists() else 1001
59
+ counter.write_text(str(jid))
60
+ deps = []
61
+ if opts.get("dependency", "").startswith("afterok:"):
62
+ deps = [int(x) for x in opts["dependency"][8:].split(":") if x]
63
+ out = opts.get("output", str(Path.cwd() / f"slurm-{jid}.out")).replace("%j", str(jid))
64
+ job = {"id": jid, "name": opts.get("job-name", Path(script).name), "script": str(Path(script).resolve()),
65
+ "cwd": str(Path.cwd()), "output": out, "deps": deps, "state": "PENDING", "start": None, "end": None,
66
+ "node": socket.gethostname(), "pid": None}
67
+ _save(job)
68
+ # Intent: detach a runner that waits for dependencies, then executes the script.
69
+ subprocess.Popen([sys.executable, __file__, "_run", str(jid)], start_new_session=True,
70
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=os.environ.copy())
71
+ print(jid if "parsable" in opts else f"Submitted batch job {jid}")
72
+
73
+
74
+ def _run(jid):
75
+ job = _load(jid)
76
+ while True:
77
+ states = [_load(d)["state"] for d in job["deps"]]
78
+ if any(s in ("FAILED", "CANCELLED", "TIMEOUT") for s in states):
79
+ job = _load(jid); job.update(state="CANCELLED", reason="DependencyNeverSatisfied", end=time.time()); _save(job); return
80
+ if all(s == "COMPLETED" for s in states):
81
+ break
82
+ if _load(jid)["state"] == "CANCELLED":
83
+ return
84
+ time.sleep(0.5)
85
+ job = _load(jid)
86
+ if job["state"] == "CANCELLED":
87
+ return
88
+ Path(job["output"]).parent.mkdir(parents=True, exist_ok=True)
89
+ with open(job["output"], "w") as log:
90
+ env = dict(os.environ, SLURM_JOB_ID=str(jid), SLURMD_NODENAME=job["node"])
91
+ p = subprocess.Popen(["bash", job["script"]], cwd=job["cwd"], stdout=log, stderr=subprocess.STDOUT, env=env, start_new_session=True)
92
+ job.update(state="RUNNING", start=time.time(), pid=p.pid); _save(job)
93
+ rc = p.wait()
94
+ job = _load(jid)
95
+ if job["state"] != "CANCELLED":
96
+ job.update(state="COMPLETED" if rc == 0 else "FAILED", end=time.time(), exit=rc); _save(job)
97
+
98
+
99
+ def _ids(args):
100
+ for i, a in enumerate(args):
101
+ if a == "-j" and i + 1 < len(args):
102
+ return [int(x) for x in args[i + 1].split(",") if x]
103
+ if a.startswith("--jobs="):
104
+ return [int(x) for x in a[7:].split(",") if x]
105
+ return sorted(int(p.stem) for p in JOBS.glob("*.json")) if JOBS.exists() else []
106
+
107
+
108
+ def _elapsed(job):
109
+ if not job["start"]:
110
+ return "00:00:00"
111
+ s = int((job["end"] or time.time()) - job["start"])
112
+ return f"{s // 3600:02d}:{s % 3600 // 60:02d}:{s % 60:02d}"
113
+
114
+
115
+ def squeue(args):
116
+ for jid in _ids(args):
117
+ try:
118
+ j = _load(jid)
119
+ except FileNotFoundError:
120
+ continue
121
+ if j["state"] in ("PENDING", "RUNNING"):
122
+ print(f"{jid} {j['state']} {j['node'] if j['state'] == 'RUNNING' else '(None)'}")
123
+
124
+
125
+ def sacct(args):
126
+ for jid in _ids(args):
127
+ try:
128
+ j = _load(jid)
129
+ except FileNotFoundError:
130
+ continue
131
+ print(f"{jid}|{j['state']}|{_elapsed(j)}")
132
+
133
+
134
+ def scancel(args):
135
+ for a in args:
136
+ if not a.isdigit():
137
+ continue
138
+ j = _load(int(a))
139
+ if j["state"] in ("PENDING", "RUNNING"):
140
+ if j.get("pid"):
141
+ try:
142
+ os.killpg(j["pid"], 15)
143
+ except ProcessLookupError:
144
+ pass
145
+ j.update(state="CANCELLED", end=time.time()); _save(j)
146
+
147
+
148
+ if __name__ == "__main__":
149
+ cmd, rest = sys.argv[1], sys.argv[2:]
150
+ {"sbatch": sbatch, "squeue": squeue, "sacct": sacct, "scancel": scancel,
151
+ "_run": lambda a: _run(int(a[0]))}[cmd](rest)
@@ -0,0 +1,78 @@
1
+ """Base class for interactive module engines (generalised from SPLiTscape part 2).
2
+
3
+ An engine runs inside a session on a compute node (or this computer for the
4
+ demo). It works on one run folder, keeps its decisions in session/state.json,
5
+ and records every decision in order so the run can be written up later.
6
+ Long steps run in the background and report progress.
7
+
8
+ Subclasses list the methods the GUI may call in API; nothing else is reachable.
9
+ Methods listed in REPLAY change the analysis; each call is logged with its
10
+ arguments, so the session can be replayed exactly to repeat the run.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import threading
16
+ import time
17
+ import traceback
18
+ from pathlib import Path
19
+
20
+
21
+ class BaseEngine:
22
+ API: tuple[str, ...] = ()
23
+ REPLAY: tuple[str, ...] = ()
24
+
25
+ def __init__(self, run_dir: str):
26
+ self.dir = Path(run_dir)
27
+ self.out = self.dir / "session"
28
+ self.out.mkdir(exist_ok=True)
29
+ (self.dir / "results").mkdir(exist_ok=True)
30
+ self.run = json.loads((self.dir / "run.json").read_text())
31
+ self.params = json.loads((self.dir / "params.json").read_text())
32
+ self.state_path = self.out / "state.json"
33
+ self.state = json.loads(self.state_path.read_text()) if self.state_path.exists() else {"step": "start", "history": []}
34
+ self.task = {"running": False, "step": "", "progress": 0, "error": None, "message": ""}
35
+ self.lock = threading.Lock()
36
+ self._state_lock = threading.RLock() # Intent: the call log and background steps both write state.json
37
+
38
+ # ---------------------------------------------------------------- state
39
+ def _save_state(self):
40
+ with self._state_lock:
41
+ self.state_path.write_text(json.dumps(self.state, indent=1))
42
+
43
+ def _record(self, action: str, **details):
44
+ """Note a decision (in order) for the run record and the lab-book protocol."""
45
+ with self._state_lock:
46
+ self.state.setdefault("history", []).append({"at": time.strftime("%Y-%m-%d %H:%M:%S"), "action": action, **details})
47
+ self._save_state()
48
+
49
+ def _log_call(self, method: str, args: dict):
50
+ """Keep the exact call (method and arguments) for replaying the session later."""
51
+ with self._state_lock:
52
+ self.state.setdefault("calls", []).append({"at": time.strftime("%Y-%m-%d %H:%M:%S"), "method": method, "args": args})
53
+ self._save_state()
54
+
55
+ def status(self):
56
+ return {"step": self.state.get("step"), "task": self.task, "module": self.run["module"], "run": self.run["id"]}
57
+
58
+ # ---------------------------------------------------------------- background work
59
+ def _background(self, fn, *args):
60
+ if self.task["running"]:
61
+ raise RuntimeError("Another step is still running.")
62
+ self.task = {"running": True, "step": fn.__name__, "progress": 0, "error": None, "message": ""}
63
+
64
+ def wrap():
65
+ try:
66
+ with self.lock:
67
+ fn(*args)
68
+ except Exception as e: # report, never crash the session
69
+ self.task["error"] = f"{e}"
70
+ traceback.print_exc()
71
+ finally:
72
+ self.task["running"] = False
73
+ self.task["progress"] = 100 if not self.task["error"] else self.task["progress"]
74
+ threading.Thread(target=wrap, daemon=True).start()
75
+ return self.task
76
+
77
+ def _prog(self, p, msg):
78
+ self.task.update(progress=p, message=msg)