experimaestro 2.0.0a8__py3-none-any.whl → 2.0.0b8__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.

Potentially problematic release.


This version of experimaestro might be problematic. Click here for more details.

Files changed (122) hide show
  1. experimaestro/__init__.py +10 -11
  2. experimaestro/annotations.py +167 -206
  3. experimaestro/cli/__init__.py +278 -7
  4. experimaestro/cli/filter.py +42 -74
  5. experimaestro/cli/jobs.py +157 -106
  6. experimaestro/cli/refactor.py +249 -0
  7. experimaestro/click.py +0 -1
  8. experimaestro/commandline.py +19 -3
  9. experimaestro/connectors/__init__.py +20 -1
  10. experimaestro/connectors/local.py +12 -0
  11. experimaestro/core/arguments.py +182 -46
  12. experimaestro/core/identifier.py +107 -6
  13. experimaestro/core/objects/__init__.py +6 -0
  14. experimaestro/core/objects/config.py +542 -25
  15. experimaestro/core/objects/config_walk.py +20 -0
  16. experimaestro/core/serialization.py +91 -34
  17. experimaestro/core/subparameters.py +164 -0
  18. experimaestro/core/types.py +175 -38
  19. experimaestro/exceptions.py +26 -0
  20. experimaestro/experiments/cli.py +111 -25
  21. experimaestro/generators.py +50 -9
  22. experimaestro/huggingface.py +3 -1
  23. experimaestro/launcherfinder/parser.py +29 -0
  24. experimaestro/launchers/__init__.py +26 -1
  25. experimaestro/launchers/direct.py +12 -0
  26. experimaestro/launchers/slurm/base.py +154 -2
  27. experimaestro/mkdocs/metaloader.py +0 -1
  28. experimaestro/mypy.py +452 -7
  29. experimaestro/notifications.py +63 -13
  30. experimaestro/progress.py +0 -2
  31. experimaestro/rpyc.py +0 -1
  32. experimaestro/run.py +19 -6
  33. experimaestro/scheduler/base.py +510 -125
  34. experimaestro/scheduler/dependencies.py +43 -28
  35. experimaestro/scheduler/dynamic_outputs.py +259 -130
  36. experimaestro/scheduler/experiment.py +256 -31
  37. experimaestro/scheduler/interfaces.py +501 -0
  38. experimaestro/scheduler/jobs.py +216 -206
  39. experimaestro/scheduler/remote/__init__.py +31 -0
  40. experimaestro/scheduler/remote/client.py +874 -0
  41. experimaestro/scheduler/remote/protocol.py +467 -0
  42. experimaestro/scheduler/remote/server.py +423 -0
  43. experimaestro/scheduler/remote/sync.py +144 -0
  44. experimaestro/scheduler/services.py +323 -23
  45. experimaestro/scheduler/state_db.py +437 -0
  46. experimaestro/scheduler/state_provider.py +2766 -0
  47. experimaestro/scheduler/state_sync.py +891 -0
  48. experimaestro/scheduler/workspace.py +52 -10
  49. experimaestro/scriptbuilder.py +7 -0
  50. experimaestro/server/__init__.py +147 -57
  51. experimaestro/server/data/index.css +0 -125
  52. experimaestro/server/data/index.css.map +1 -1
  53. experimaestro/server/data/index.js +194 -58
  54. experimaestro/server/data/index.js.map +1 -1
  55. experimaestro/settings.py +44 -5
  56. experimaestro/sphinx/__init__.py +3 -3
  57. experimaestro/taskglobals.py +20 -0
  58. experimaestro/tests/conftest.py +80 -0
  59. experimaestro/tests/core/test_generics.py +2 -2
  60. experimaestro/tests/identifier_stability.json +45 -0
  61. experimaestro/tests/launchers/bin/sacct +6 -2
  62. experimaestro/tests/launchers/bin/sbatch +4 -2
  63. experimaestro/tests/launchers/test_slurm.py +80 -0
  64. experimaestro/tests/tasks/test_dynamic.py +231 -0
  65. experimaestro/tests/test_cli_jobs.py +615 -0
  66. experimaestro/tests/test_deprecated.py +630 -0
  67. experimaestro/tests/test_environment.py +200 -0
  68. experimaestro/tests/test_file_progress_integration.py +1 -1
  69. experimaestro/tests/test_forward.py +3 -3
  70. experimaestro/tests/test_identifier.py +372 -41
  71. experimaestro/tests/test_identifier_stability.py +458 -0
  72. experimaestro/tests/test_instance.py +3 -3
  73. experimaestro/tests/test_multitoken.py +442 -0
  74. experimaestro/tests/test_mypy.py +433 -0
  75. experimaestro/tests/test_objects.py +312 -5
  76. experimaestro/tests/test_outputs.py +2 -2
  77. experimaestro/tests/test_param.py +8 -12
  78. experimaestro/tests/test_partial_paths.py +231 -0
  79. experimaestro/tests/test_progress.py +0 -48
  80. experimaestro/tests/test_remote_state.py +671 -0
  81. experimaestro/tests/test_resumable_task.py +480 -0
  82. experimaestro/tests/test_serializers.py +141 -1
  83. experimaestro/tests/test_state_db.py +434 -0
  84. experimaestro/tests/test_subparameters.py +160 -0
  85. experimaestro/tests/test_tags.py +136 -0
  86. experimaestro/tests/test_tasks.py +107 -121
  87. experimaestro/tests/test_token_locking.py +252 -0
  88. experimaestro/tests/test_tokens.py +17 -13
  89. experimaestro/tests/test_types.py +123 -1
  90. experimaestro/tests/test_workspace_triggers.py +158 -0
  91. experimaestro/tests/token_reschedule.py +4 -2
  92. experimaestro/tests/utils.py +2 -2
  93. experimaestro/tokens.py +154 -57
  94. experimaestro/tools/diff.py +1 -1
  95. experimaestro/tui/__init__.py +8 -0
  96. experimaestro/tui/app.py +2395 -0
  97. experimaestro/tui/app.tcss +353 -0
  98. experimaestro/tui/log_viewer.py +228 -0
  99. experimaestro/utils/__init__.py +23 -0
  100. experimaestro/utils/environment.py +148 -0
  101. experimaestro/utils/git.py +129 -0
  102. experimaestro/utils/resources.py +1 -1
  103. experimaestro/version.py +34 -0
  104. {experimaestro-2.0.0a8.dist-info → experimaestro-2.0.0b8.dist-info}/METADATA +68 -38
  105. experimaestro-2.0.0b8.dist-info/RECORD +187 -0
  106. {experimaestro-2.0.0a8.dist-info → experimaestro-2.0.0b8.dist-info}/WHEEL +1 -1
  107. experimaestro-2.0.0b8.dist-info/entry_points.txt +16 -0
  108. experimaestro/compat.py +0 -6
  109. experimaestro/core/objects.pyi +0 -221
  110. experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
  111. experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
  112. experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
  113. experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
  114. experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
  115. experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
  116. experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
  117. experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
  118. experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
  119. experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
  120. experimaestro-2.0.0a8.dist-info/RECORD +0 -166
  121. experimaestro-2.0.0a8.dist-info/entry_points.txt +0 -17
  122. {experimaestro-2.0.0a8.dist-info → experimaestro-2.0.0b8.dist-info}/licenses/LICENSE +0 -0
@@ -7140,12 +7140,13 @@ __webpack_require__.r(__webpack_exports__);
7140
7140
  /* harmony export */ });
7141
7141
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "./node_modules/react/index.js");
7142
7142
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
7143
- /* harmony import */ var react_bootstrap_Container__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! react-bootstrap/Container */ "./node_modules/react-bootstrap/esm/Container.js");
7144
- /* harmony import */ var react_bootstrap_Nav__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react-bootstrap/Nav */ "./node_modules/react-bootstrap/esm/Nav.js");
7145
- /* harmony import */ var react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! react-bootstrap/Navbar */ "./node_modules/react-bootstrap/esm/Navbar.js");
7143
+ /* harmony import */ var react_bootstrap_Container__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react-bootstrap/Container */ "./node_modules/react-bootstrap/esm/Container.js");
7144
+ /* harmony import */ var react_bootstrap_Nav__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! react-bootstrap/Nav */ "./node_modules/react-bootstrap/esm/Nav.js");
7145
+ /* harmony import */ var react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! react-bootstrap/Navbar */ "./node_modules/react-bootstrap/esm/Navbar.js");
7146
7146
  /* harmony import */ var _Tasks__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Tasks */ "./src/Tasks.tsx");
7147
7147
  /* harmony import */ var _Services__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Services */ "./src/Services.tsx");
7148
- /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./store */ "./src/store.ts");
7148
+ /* harmony import */ var _ExperimentSelector__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./ExperimentSelector */ "./src/ExperimentSelector.tsx");
7149
+ /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./store */ "./src/store.ts");
7149
7150
  /* provided dependency */ var __react_refresh_utils__ = __webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js */ "./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js");
7150
7151
  __webpack_require__.$Refresh$.runtime = __webpack_require__(/*! ./node_modules/react-refresh/runtime.js */ "./node_modules/react-refresh/runtime.js");
7151
7152
 
@@ -7157,11 +7158,12 @@ var _s = __webpack_require__.$Refresh$.signature();
7157
7158
 
7158
7159
 
7159
7160
 
7161
+
7160
7162
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_s(() => {
7161
7163
  _s();
7162
- const connected = (0,_store__WEBPACK_IMPORTED_MODULE_3__.useAppSelector)(state => state.db.connected);
7163
- const jobs = (0,_store__WEBPACK_IMPORTED_MODULE_3__.useAppSelector)(state => state.db.jobs);
7164
- const experiment = (0,_store__WEBPACK_IMPORTED_MODULE_3__.useAppSelector)(state => state.db.experiment);
7164
+ const connected = (0,_store__WEBPACK_IMPORTED_MODULE_4__.useAppSelector)(state => state.db.connected);
7165
+ const jobs = (0,_store__WEBPACK_IMPORTED_MODULE_4__.useAppSelector)(state => state.db.jobs);
7166
+ const experiment = (0,_store__WEBPACK_IMPORTED_MODULE_4__.useAppSelector)(state => state.db.experiment);
7165
7167
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
7166
7168
  id: "clipboard-holder",
7167
7169
  style: {
@@ -7169,24 +7171,24 @@ var _s = __webpack_require__.$Refresh$.signature();
7169
7171
  width: 0,
7170
7172
  height: 0
7171
7173
  }
7172
- }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_4__["default"], {
7174
+ }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_5__["default"], {
7173
7175
  bg: "light",
7174
7176
  expand: "lg"
7175
- }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Container__WEBPACK_IMPORTED_MODULE_5__["default"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_4__["default"].Brand, {
7177
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Container__WEBPACK_IMPORTED_MODULE_6__["default"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_5__["default"].Brand, {
7176
7178
  href: "/"
7177
- }, "Experimaestro ", experiment ? " – " + experiment : "", " "), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_4__["default"].Toggle, {
7179
+ }, "Experimaestro ", experiment ? " – " + experiment : "", " "), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_5__["default"].Toggle, {
7178
7180
  "aria-controls": "basic-navbar-nav"
7179
- }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_4__["default"].Collapse, {
7181
+ }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Navbar__WEBPACK_IMPORTED_MODULE_5__["default"].Collapse, {
7180
7182
  id: "basic-navbar-nav"
7181
- }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Nav__WEBPACK_IMPORTED_MODULE_6__["default"], {
7183
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Nav__WEBPACK_IMPORTED_MODULE_7__["default"], {
7182
7184
  className: "me-auto"
7183
- }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Nav__WEBPACK_IMPORTED_MODULE_6__["default"].Link, {
7185
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Nav__WEBPACK_IMPORTED_MODULE_7__["default"].Link, {
7184
7186
  href: "/"
7185
- }, "Tasks"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Services__WEBPACK_IMPORTED_MODULE_2__["default"], null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
7187
+ }, "Tasks"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Services__WEBPACK_IMPORTED_MODULE_2__["default"], null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ExperimentSelector__WEBPACK_IMPORTED_MODULE_3__.ExperimentSelector, null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
7186
7188
  className: `fab fa-staylinked ws-status ${connected ? "ws-link" : "ws-no-link"}`
7187
- })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Container__WEBPACK_IMPORTED_MODULE_5__["default"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Tasks__WEBPACK_IMPORTED_MODULE_1__["default"], null)));
7189
+ })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Container__WEBPACK_IMPORTED_MODULE_6__["default"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Tasks__WEBPACK_IMPORTED_MODULE_1__["default"], null)));
7188
7190
  }, "7dqzz7jx6wqNtfDrqc6yEFH70CQ=", false, function () {
7189
- return [_store__WEBPACK_IMPORTED_MODULE_3__.useAppSelector, _store__WEBPACK_IMPORTED_MODULE_3__.useAppSelector, _store__WEBPACK_IMPORTED_MODULE_3__.useAppSelector];
7191
+ return [_store__WEBPACK_IMPORTED_MODULE_4__.useAppSelector, _store__WEBPACK_IMPORTED_MODULE_4__.useAppSelector, _store__WEBPACK_IMPORTED_MODULE_4__.useAppSelector];
7190
7192
  }));
7191
7193
 
7192
7194
  const $ReactRefreshModuleId$ = __webpack_require__.$Refresh$.moduleId;
@@ -7206,6 +7208,98 @@ if (typeof Promise !== 'undefined' && $ReactRefreshCurrentExports$ instanceof Pr
7206
7208
 
7207
7209
  /***/ }),
7208
7210
 
7211
+ /***/ "./src/ExperimentSelector.tsx":
7212
+ /*!************************************!*\
7213
+ !*** ./src/ExperimentSelector.tsx ***!
7214
+ \************************************/
7215
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
7216
+
7217
+ "use strict";
7218
+ __webpack_require__.r(__webpack_exports__);
7219
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
7220
+ /* harmony export */ ExperimentSelector: () => (/* binding */ ExperimentSelector)
7221
+ /* harmony export */ });
7222
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "./node_modules/react/index.js");
7223
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
7224
+ /* harmony import */ var react_bootstrap_Form__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! react-bootstrap/Form */ "./node_modules/react-bootstrap/esm/Form.js");
7225
+ /* harmony import */ var _store__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./store */ "./src/store.ts");
7226
+ /* harmony import */ var _reducers__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./reducers */ "./src/reducers.ts");
7227
+ /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! react-redux */ "./node_modules/react-redux/dist/react-redux.mjs");
7228
+ /* provided dependency */ var __react_refresh_utils__ = __webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js */ "./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js");
7229
+ __webpack_require__.$Refresh$.runtime = __webpack_require__(/*! ./node_modules/react-refresh/runtime.js */ "./node_modules/react-refresh/runtime.js");
7230
+
7231
+ var _s = __webpack_require__.$Refresh$.signature();
7232
+
7233
+
7234
+
7235
+
7236
+
7237
+ const ExperimentSelector = () => {
7238
+ _s();
7239
+ const dispatch = (0,react_redux__WEBPACK_IMPORTED_MODULE_3__.useDispatch)();
7240
+ const experiments = (0,_store__WEBPACK_IMPORTED_MODULE_1__.useAppSelector)(state => state.db.experiments);
7241
+ const currentExperiment = (0,_store__WEBPACK_IMPORTED_MODULE_1__.useAppSelector)(state => state.db.currentExperiment);
7242
+
7243
+ // Hide selector if there are 0 or 1 experiments
7244
+ if (experiments.ids.length <= 1) {
7245
+ return null;
7246
+ }
7247
+ const handleChange = event => {
7248
+ const value = event.target.value;
7249
+ dispatch(_reducers__WEBPACK_IMPORTED_MODULE_2__.actions.selectExperiment(value === "" ? null : value));
7250
+ };
7251
+
7252
+ // Helper to get experiment status color
7253
+ const getExperimentStatus = expId => {
7254
+ const exp = experiments.byId[expId];
7255
+ if (exp.failed_jobs > 0) return "danger";
7256
+ if (exp.finished_jobs < exp.total_jobs) return "warning";
7257
+ return "success";
7258
+ };
7259
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_bootstrap_Form__WEBPACK_IMPORTED_MODULE_4__["default"].Select, {
7260
+ value: currentExperiment || "",
7261
+ onChange: handleChange,
7262
+ style: {
7263
+ width: "auto",
7264
+ marginLeft: "1rem"
7265
+ },
7266
+ size: "sm"
7267
+ }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", {
7268
+ value: ""
7269
+ }, "All Experiments"), experiments.ids.map(expId => {
7270
+ const exp = experiments.byId[expId];
7271
+ const status = getExperimentStatus(expId);
7272
+ const icon = status === "danger" ? "❌" : status === "warning" ? "⏳" : "✓";
7273
+ return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("option", {
7274
+ key: expId,
7275
+ value: expId
7276
+ }, icon, " ", exp.experiment_id, " (", exp.finished_jobs, "/", exp.total_jobs, " jobs)");
7277
+ }));
7278
+ };
7279
+ _s(ExperimentSelector, "rw3tx9tK8pIHADyncpW+cdSSEkU=", false, function () {
7280
+ return [react_redux__WEBPACK_IMPORTED_MODULE_3__.useDispatch, _store__WEBPACK_IMPORTED_MODULE_1__.useAppSelector, _store__WEBPACK_IMPORTED_MODULE_1__.useAppSelector];
7281
+ });
7282
+ _c = ExperimentSelector;
7283
+ var _c;
7284
+ __webpack_require__.$Refresh$.register(_c, "ExperimentSelector");
7285
+
7286
+ const $ReactRefreshModuleId$ = __webpack_require__.$Refresh$.moduleId;
7287
+ const $ReactRefreshCurrentExports$ = __react_refresh_utils__.getModuleExports(
7288
+ $ReactRefreshModuleId$
7289
+ );
7290
+
7291
+ function $ReactRefreshModuleRuntime$(exports) {
7292
+ if (false) {}
7293
+ }
7294
+
7295
+ if (typeof Promise !== 'undefined' && $ReactRefreshCurrentExports$ instanceof Promise) {
7296
+ $ReactRefreshCurrentExports$.then($ReactRefreshModuleRuntime$);
7297
+ } else {
7298
+ $ReactRefreshModuleRuntime$($ReactRefreshCurrentExports$);
7299
+ }
7300
+
7301
+ /***/ }),
7302
+
7209
7303
  /***/ "./src/Services.tsx":
7210
7304
  /*!**************************!*\
7211
7305
  !*** ./src/Services.tsx ***!
@@ -7346,12 +7440,11 @@ const useStyles = (0,react_jss__WEBPACK_IMPORTED_MODULE_4__.createUseStyles)({
7346
7440
  fontWeight: "bold"
7347
7441
  }
7348
7442
  });
7349
- /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_s(_ref => {
7443
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_s(({
7444
+ job,
7445
+ onHide
7446
+ }) => {
7350
7447
  _s();
7351
- let {
7352
- job,
7353
- onHide
7354
- } = _ref;
7355
7448
  const classes = useStyles();
7356
7449
  const {
7357
7450
  success,
@@ -7442,13 +7535,12 @@ var _s = __webpack_require__.$Refresh$.signature();
7442
7535
 
7443
7536
 
7444
7537
 
7445
- /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_s(_ref => {
7538
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_s(({
7539
+ job,
7540
+ onKill,
7541
+ onShow
7542
+ }) => {
7446
7543
  _s();
7447
- let {
7448
- job,
7449
- onKill,
7450
- onShow
7451
- } = _ref;
7452
7544
  const {
7453
7545
  success,
7454
7546
  error
@@ -7551,6 +7643,7 @@ var _s = __webpack_require__.$Refresh$.signature();
7551
7643
  info
7552
7644
  } = (0,_ui_messages__WEBPACK_IMPORTED_MODULE_4__.useMessages)();
7553
7645
  const jobs = (0,_store__WEBPACK_IMPORTED_MODULE_5__.useAppSelector)(state => state.db.jobs);
7646
+ const currentExperiment = (0,_store__WEBPACK_IMPORTED_MODULE_5__.useAppSelector)(state => state.db.currentExperiment);
7554
7647
  function updateTagSearch(tag) {
7555
7648
  let re = /(\S+):(?:([^"]\S*)|"([^"]+)")\s*/g;
7556
7649
  var match;
@@ -7571,6 +7664,12 @@ var _s = __webpack_require__.$Refresh$.signature();
7571
7664
  re_task = null;
7572
7665
  }
7573
7666
  function filter(job) {
7667
+ // Filter by experiment if one is selected
7668
+ if (currentExperiment !== null) {
7669
+ if (!job.experimentIds || !job.experimentIds.includes(currentExperiment)) {
7670
+ return false;
7671
+ }
7672
+ }
7574
7673
  if (re_task && job.taskId.match(re_task) === null) return false;
7575
7674
  mainloop: for (let {
7576
7675
  tag,
@@ -7584,7 +7683,7 @@ var _s = __webpack_require__.$Refresh$.signature();
7584
7683
  return true;
7585
7684
  }
7586
7685
  return jobs.ids.map(id => jobs.byId[id]).filter(filter);
7587
- }, [jobs.ids, jobs.byId, tagFilters, taskFilter]);
7686
+ }, [jobs.ids, jobs.byId, tagFilters, taskFilter, currentExperiment]);
7588
7687
  function cancelKill() {
7589
7688
  setKillJob(undefined);
7590
7689
  info("Action cancelled");
@@ -7634,8 +7733,8 @@ var _s = __webpack_require__.$Refresh$.signature();
7634
7733
  onHide: () => setShowJob(""),
7635
7734
  job: jobs.byId[showJob]
7636
7735
  })));
7637
- }, "o7CEAT2L0q9fN3TjuKOY42DeleQ=", false, function () {
7638
- return [_ui_messages__WEBPACK_IMPORTED_MODULE_4__.useMessages, _store__WEBPACK_IMPORTED_MODULE_5__.useAppSelector];
7736
+ }, "1IlhIW6FbLWkL2ptk38I9BAajh0=", false, function () {
7737
+ return [_ui_messages__WEBPACK_IMPORTED_MODULE_4__.useMessages, _store__WEBPACK_IMPORTED_MODULE_5__.useAppSelector, _store__WEBPACK_IMPORTED_MODULE_5__.useAppSelector];
7639
7738
  }));
7640
7739
 
7641
7740
  const $ReactRefreshModuleId$ = __webpack_require__.$Refresh$.moduleId;
@@ -7684,12 +7783,16 @@ class Client {
7684
7783
  const socket = this.socket;
7685
7784
  this.socket.on('connect', function () {
7686
7785
  _store__WEBPACK_IMPORTED_MODULE_1__["default"].dispatch(_reducers__WEBPACK_IMPORTED_MODULE_2__.actions.setConnected(true));
7786
+ socket.emit('experiments'); // Request experiment list
7687
7787
  socket.emit('refresh');
7688
7788
  socket.emit('services');
7689
7789
  });
7690
7790
  socket.on("disconnect", () => {
7691
7791
  _store__WEBPACK_IMPORTED_MODULE_1__["default"].dispatch(_reducers__WEBPACK_IMPORTED_MODULE_2__.actions.setConnected(false));
7692
7792
  });
7793
+ this.socket.on('experiment.add', function (data) {
7794
+ _store__WEBPACK_IMPORTED_MODULE_1__["default"].dispatch(_reducers__WEBPACK_IMPORTED_MODULE_2__.actions.addExperiment(data));
7795
+ });
7693
7796
  this.socket.on('service.add', function (data) {
7694
7797
  _store__WEBPACK_IMPORTED_MODULE_1__["default"].dispatch(_reducers__WEBPACK_IMPORTED_MODULE_2__.actions.addService(data));
7695
7798
  });
@@ -7711,11 +7814,25 @@ class Client {
7711
7814
  }
7712
7815
  });
7713
7816
  }
7714
- job_details(jobid) {
7715
- this.socket.emit('job.details', jobid);
7817
+ job_details(jobid, experimentId) {
7818
+ if (experimentId) {
7819
+ this.socket.emit('job.details', {
7820
+ experimentId,
7821
+ jobId: jobid
7822
+ });
7823
+ } else {
7824
+ this.socket.emit('job.details', jobid); // Backward compatibility
7825
+ }
7716
7826
  }
7717
- job_kill(jobid) {
7718
- this.socket.emit('job.kill', jobid);
7827
+ job_kill(jobid, experimentId) {
7828
+ if (experimentId) {
7829
+ this.socket.emit('job.kill', {
7830
+ experimentId,
7831
+ jobId: jobid
7832
+ });
7833
+ } else {
7834
+ this.socket.emit('job.kill', jobid); // Backward compatibility
7835
+ }
7719
7836
  }
7720
7837
  }
7721
7838
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (new Client());
@@ -7899,6 +8016,11 @@ const slice = (0,_reduxjs_toolkit__WEBPACK_IMPORTED_MODULE_2__.createSlice)({
7899
8016
  initialState: {
7900
8017
  connected: false,
7901
8018
  experiment: "",
8019
+ currentExperiment: null,
8020
+ experiments: {
8021
+ byId: {},
8022
+ ids: []
8023
+ },
7902
8024
  jobs: {
7903
8025
  byId: {},
7904
8026
  ids: []
@@ -7927,17 +8049,15 @@ const slice = (0,_reduxjs_toolkit__WEBPACK_IMPORTED_MODULE_2__.createSlice)({
7927
8049
  }
7928
8050
  draft.jobs.ids.sort(jobComparator(draft.jobs.byId));
7929
8051
  },
7930
- addService(draft, _ref) {
7931
- let {
7932
- payload
7933
- } = _ref;
8052
+ addService(draft, {
8053
+ payload
8054
+ }) {
7934
8055
  draft.services.byId[payload.id] = payload;
7935
8056
  draft.services.ids = Object.keys(draft.services.byId);
7936
8057
  },
7937
- updateService(draft, _ref2) {
7938
- let {
7939
- payload
7940
- } = _ref2;
8058
+ updateService(draft, {
8059
+ payload
8060
+ }) {
7941
8061
  if (!payload.id) {
7942
8062
  return;
7943
8063
  }
@@ -7948,6 +8068,30 @@ const slice = (0,_reduxjs_toolkit__WEBPACK_IMPORTED_MODULE_2__.createSlice)({
7948
8068
  },
7949
8069
  setConnected(draft, action) {
7950
8070
  draft.connected = action.payload;
8071
+ },
8072
+ addExperiment(draft, action) {
8073
+ const exp = action.payload;
8074
+ if (draft.experiments.byId[exp.experiment_id] === undefined) {
8075
+ draft.experiments.ids.push(exp.experiment_id);
8076
+ }
8077
+ draft.experiments.byId[exp.experiment_id] = exp;
8078
+
8079
+ // Set as current experiment if it's the first one
8080
+ if (draft.experiments.ids.length === 1) {
8081
+ draft.currentExperiment = exp.experiment_id;
8082
+ draft.experiment = exp.experiment_id; // Backward compatibility
8083
+ }
8084
+ },
8085
+ updateExperiment(draft, action) {
8086
+ const exp = action.payload;
8087
+ if (draft.experiments.byId[exp.experiment_id] !== undefined) {
8088
+ lodash__WEBPACK_IMPORTED_MODULE_1___default().merge(draft.experiments.byId[exp.experiment_id], exp);
8089
+ }
8090
+ },
8091
+ selectExperiment(draft, action) {
8092
+ draft.currentExperiment = action.payload;
8093
+ // Update backward compatibility field
8094
+ draft.experiment = action.payload || "";
7951
8095
  }
7952
8096
  }
7953
8097
  });
@@ -8123,18 +8267,10 @@ function useMessages() {
8123
8267
  _s();
8124
8268
  const dispatch = (0,react_redux__WEBPACK_IMPORTED_MODULE_2__.useDispatch)();
8125
8269
  return {
8126
- success: function () {
8127
- return dispatch(success(...arguments));
8128
- },
8129
- info: function () {
8130
- return dispatch(info(...arguments));
8131
- },
8132
- warning: function () {
8133
- return dispatch(warning(...arguments));
8134
- },
8135
- error: function () {
8136
- return dispatch(error(...arguments));
8137
- }
8270
+ success: (...args) => dispatch(success(...args)),
8271
+ info: (...args) => dispatch(info(...args)),
8272
+ warning: (...args) => dispatch(warning(...args)),
8273
+ error: (...args) => dispatch(error(...args))
8138
8274
  };
8139
8275
  }
8140
8276
 
@@ -74965,13 +75101,13 @@ module.exports = warning;
74965
75101
 
74966
75102
  /***/ }),
74967
75103
 
74968
- /***/ "./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ErrorOverlayEntry.js?sockPort=5000&sockProtocol=http":
75104
+ /***/ "./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ErrorOverlayEntry.js?sockPort=5010&sockProtocol=http":
74969
75105
  /*!***********************************************************************************************************************!*\
74970
- !*** ./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ErrorOverlayEntry.js?sockPort=5000&sockProtocol=http ***!
75106
+ !*** ./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ErrorOverlayEntry.js?sockPort=5010&sockProtocol=http ***!
74971
75107
  \***********************************************************************************************************************/
74972
75108
  /***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
74973
75109
 
74974
- var __resourceQuery = "?sockPort=5000&sockProtocol=http";
75110
+ var __resourceQuery = "?sockPort=5010&sockProtocol=http";
74975
75111
  /* provided dependency */ var __react_refresh_error_overlay__ = __webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/overlay/index.js */ "./node_modules/@pmmmwh/react-refresh-webpack-plugin/overlay/index.js");
74976
75112
  /* provided dependency */ var __react_refresh_socket__ = __webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/sockets/WDSSocket.js */ "./node_modules/@pmmmwh/react-refresh-webpack-plugin/sockets/WDSSocket.js");
74977
75113
  /* global __react_refresh_error_overlay__, __react_refresh_socket__, __resourceQuery */
@@ -101761,7 +101897,7 @@ function hasBinary(obj, toJSON) {
101761
101897
  /******/ // startup
101762
101898
  /******/ // Load entry module and return exports
101763
101899
  /******/ __webpack_require__("./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js");
101764
- /******/ __webpack_require__("./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ErrorOverlayEntry.js?sockPort=5000&sockProtocol=http");
101900
+ /******/ __webpack_require__("./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ErrorOverlayEntry.js?sockPort=5010&sockProtocol=http");
101765
101901
  /******/ __webpack_require__("./src/theme/theme.scss");
101766
101902
  /******/ var __webpack_exports__ = __webpack_require__("./src/index.tsx");
101767
101903
  /******/