meerschaum 2.3.0rc1__py3-none-any.whl → 2.3.0rc3__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 (30) hide show
  1. meerschaum/_internal/arguments/__init__.py +1 -1
  2. meerschaum/_internal/arguments/_parse_arguments.py +56 -2
  3. meerschaum/_internal/entry.py +63 -22
  4. meerschaum/_internal/shell/Shell.py +80 -15
  5. meerschaum/actions/delete.py +17 -7
  6. meerschaum/actions/start.py +2 -6
  7. meerschaum/api/dash/callbacks/jobs.py +36 -44
  8. meerschaum/api/dash/jobs.py +24 -15
  9. meerschaum/api/routes/_actions.py +6 -5
  10. meerschaum/api/routes/_jobs.py +19 -1
  11. meerschaum/config/_jobs.py +1 -1
  12. meerschaum/config/_version.py +1 -1
  13. meerschaum/config/static/__init__.py +2 -0
  14. meerschaum/connectors/api/APIConnector.py +1 -0
  15. meerschaum/connectors/api/_jobs.py +13 -2
  16. meerschaum/connectors/parse.py +0 -1
  17. meerschaum/jobs/_Job.py +24 -7
  18. meerschaum/jobs/_SystemdExecutor.py +118 -49
  19. meerschaum/jobs/__init__.py +41 -12
  20. meerschaum/utils/daemon/Daemon.py +14 -0
  21. meerschaum/utils/daemon/_names.py +1 -1
  22. meerschaum/utils/formatting/_jobs.py +2 -14
  23. {meerschaum-2.3.0rc1.dist-info → meerschaum-2.3.0rc3.dist-info}/METADATA +1 -1
  24. {meerschaum-2.3.0rc1.dist-info → meerschaum-2.3.0rc3.dist-info}/RECORD +30 -30
  25. {meerschaum-2.3.0rc1.dist-info → meerschaum-2.3.0rc3.dist-info}/LICENSE +0 -0
  26. {meerschaum-2.3.0rc1.dist-info → meerschaum-2.3.0rc3.dist-info}/NOTICE +0 -0
  27. {meerschaum-2.3.0rc1.dist-info → meerschaum-2.3.0rc3.dist-info}/WHEEL +0 -0
  28. {meerschaum-2.3.0rc1.dist-info → meerschaum-2.3.0rc3.dist-info}/entry_points.txt +0 -0
  29. {meerschaum-2.3.0rc1.dist-info → meerschaum-2.3.0rc3.dist-info}/top_level.txt +0 -0
  30. {meerschaum-2.3.0rc1.dist-info → meerschaum-2.3.0rc3.dist-info}/zip-safe +0 -0
@@ -8,7 +8,7 @@ This package includes argument parsing utilities.
8
8
 
9
9
  from meerschaum._internal.arguments._parse_arguments import (
10
10
  parse_arguments, parse_line, remove_leading_action,
11
- parse_dict_to_sysargs,
11
+ parse_dict_to_sysargs, split_chained_sysargs,
12
12
  )
13
13
  from meerschaum._internal.arguments._parser import parser
14
14
  from meerschaum.plugins import add_plugin_argument
@@ -19,6 +19,44 @@ _locks = {
19
19
  _loaded_plugins_args: bool = False
20
20
 
21
21
 
22
+ def split_chained_sysargs(sysargs: List[str]) -> List[List[str]]:
23
+ """
24
+ Split a `sysargs` list containing "and" keys (`&&`)
25
+ into a list of individual `sysargs`.
26
+ """
27
+ from meerschaum.config.static import STATIC_CONFIG
28
+ and_key = STATIC_CONFIG['system']['arguments']['and_key']
29
+
30
+ if not sysargs or and_key not in sysargs:
31
+ return [sysargs]
32
+
33
+ ### Coalesce and consecutive joiners into one.
34
+ coalesce_args = []
35
+ previous_arg = None
36
+ for arg in [_arg for _arg in sysargs]:
37
+ if arg == and_key and previous_arg == and_key:
38
+ continue
39
+ coalesce_args.append(arg)
40
+ previous_arg = arg
41
+
42
+ ### Remove any joiners from the ends.
43
+ if coalesce_args[0] == and_key:
44
+ coalesce_args = coalesce_args[1:]
45
+ if coalesce_args[-1] == and_key:
46
+ coalesce_args = coalesce_args[:-1]
47
+
48
+ chained_sysargs = []
49
+ current_sysargs = []
50
+ for arg in coalesce_args:
51
+ if arg != and_key:
52
+ current_sysargs.append(arg)
53
+ else:
54
+ chained_sysargs.append(current_sysargs)
55
+ current_sysargs = []
56
+ chained_sysargs.append(current_sysargs)
57
+ return chained_sysargs
58
+
59
+
22
60
  def parse_arguments(sysargs: List[str]) -> Dict[str, Any]:
23
61
  """
24
62
  Parse a list of arguments into standard Meerschaum arguments.
@@ -206,9 +244,24 @@ def parse_dict_to_sysargs(
206
244
  args_dict: Dict[str, Any]
207
245
  ) -> List[str]:
208
246
  """Revert an arguments dictionary back to a command line list."""
247
+ import shlex
209
248
  from meerschaum._internal.arguments._parser import get_arguments_triggers
210
- sysargs = []
211
- sysargs += args_dict.get('action', [])
249
+ from meerschaum.config.static import STATIC_CONFIG
250
+ from meerschaum.utils.warnings import warn
251
+
252
+ and_key = STATIC_CONFIG['system']['arguments']['and_key']
253
+ if (line := args_dict.get('line', None)):
254
+ return shlex.split(line)
255
+
256
+ if (_sysargs := args_dict.get('sysargs', None)):
257
+ return _sysargs
258
+
259
+ action = args_dict.get('action', None)
260
+ if action and and_key in action:
261
+ warn(f"Cannot determine flags from chained actions:\n{args_dict}")
262
+
263
+ sysargs: List[str] = []
264
+ sysargs.extend(action or [])
212
265
  allow_none_args = {'location_keys'}
213
266
 
214
267
  triggers = get_arguments_triggers()
@@ -216,6 +269,7 @@ def parse_dict_to_sysargs(
216
269
  for a, t in triggers.items():
217
270
  if a == 'action' or a not in args_dict:
218
271
  continue
272
+
219
273
  ### Add boolean flags
220
274
  if isinstance(args_dict[a], bool):
221
275
  if args_dict[a] is True:
@@ -40,31 +40,68 @@ def entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
40
40
  -------
41
41
  A `SuccessTuple` indicating success.
42
42
  """
43
- from meerschaum._internal.arguments import parse_arguments
43
+ import shlex
44
+ from meerschaum.utils.formatting import make_header
45
+ from meerschaum._internal.arguments import parse_arguments, split_chained_sysargs
44
46
  from meerschaum.config.static import STATIC_CONFIG
45
47
  if sysargs is None:
46
48
  sysargs = []
47
49
  if not isinstance(sysargs, list):
48
- import shlex
49
50
  sysargs = shlex.split(sysargs)
50
- args = parse_arguments(sysargs)
51
- argparse_exception = args.get(
52
- STATIC_CONFIG['system']['arguments']['failure_key'],
53
- None,
51
+
52
+ has_daemon = '-d' in sysargs or '--daemon' in sysargs
53
+ has_start_job = sysargs[:2] == ['start', 'job']
54
+ chained_sysargs = (
55
+ [sysargs]
56
+ if has_daemon or has_start_job
57
+ else split_chained_sysargs(sysargs)
54
58
  )
55
- if argparse_exception is not None:
56
- args_text = args.get('text', '')
57
- if not args_text.startswith('show arguments'):
58
- return (
59
- False,
59
+ results: List[SuccessTuple] = []
60
+
61
+ for _sysargs in chained_sysargs:
62
+ args = parse_arguments(_sysargs)
63
+ argparse_exception = args.get(
64
+ STATIC_CONFIG['system']['arguments']['failure_key'],
65
+ None,
66
+ )
67
+ if argparse_exception is not None:
68
+ args_text = args.get('text', '')
69
+ if not args_text.startswith('show arguments'):
70
+ return (
71
+ False,
72
+ (
73
+ "Invalid arguments:"
74
+ + (f"\n{args_text}" if args_text else '')
75
+ + f"\n {argparse_exception}"
76
+ )
77
+ )
78
+
79
+ entry_success, entry_msg = entry_with_args(**args)
80
+ if not entry_success:
81
+ return entry_success, entry_msg
82
+
83
+ results.append((entry_success, entry_msg))
84
+
85
+ success = all(_success for _success, _ in results)
86
+ msg = (
87
+ results[0][1]
88
+ if len(results) == 1
89
+ else 'Successfully completed steps:\n\n' + '\n'.join(
90
+ [
60
91
  (
61
- "Invalid arguments:"
62
- + (f"\n{args_text}" if args_text else '')
63
- + f"\n {argparse_exception}"
92
+ make_header(shlex.join(_sysargs))
93
+ + '\n ' + _msg + '\n'
64
94
  )
65
- )
95
+ for i, ((_, _msg), _sysargs) in enumerate(zip(results, chained_sysargs))
96
+ ]
97
+ )
98
+ )
99
+ if _systemd_result_path:
100
+ import json
101
+ with open(_systemd_result_path, 'w+', encoding='utf-8') as f:
102
+ json.dump((success, msg), f)
66
103
 
67
- return entry_with_args(**args)
104
+ return success, msg
68
105
 
69
106
 
70
107
  def entry_with_args(
@@ -79,7 +116,16 @@ def entry_with_args(
79
116
  import inspect
80
117
  from meerschaum.actions import get_action, get_main_action_name
81
118
  from meerschaum._internal.arguments import remove_leading_action
82
- from meerschaum.utils.venv import Venv, active_venvs, deactivate_venv
119
+ from meerschaum.utils.venv import active_venvs, deactivate_venv
120
+ from meerschaum.config.static import STATIC_CONFIG
121
+
122
+ and_key = STATIC_CONFIG['system']['arguments']['and_key']
123
+ escaped_and_key = STATIC_CONFIG['system']['arguments']['escaped_and_key']
124
+ if and_key in (sysargs := kw.get('sysargs', [])):
125
+ if '-d' in sysargs or '--daemon' in sysargs:
126
+ sysargs = [(arg if arg != and_key else escaped_and_key) for arg in sysargs]
127
+ return entry(sysargs)
128
+
83
129
  if kw.get('trace', None):
84
130
  from meerschaum.utils.misc import debug_trace
85
131
  debug_trace()
@@ -159,11 +205,6 @@ def entry_with_args(
159
205
  for venv in [venv for venv in active_venvs]:
160
206
  deactivate_venv(venv, debug=kw.get('debug', False), force=True)
161
207
 
162
- if _systemd_result_path:
163
- import json
164
- with open(_systemd_result_path, 'w+', encoding='utf-8') as f:
165
- json.dump(result, f)
166
-
167
208
  return result
168
209
 
169
210
 
@@ -8,6 +8,8 @@ This module is the entry point for the interactive shell.
8
8
  from __future__ import annotations
9
9
  import os
10
10
  from copy import deepcopy
11
+ from itertools import chain
12
+
11
13
  from meerschaum.utils.typing import Union, SuccessTuple, Any, Callable, Optional, List, Dict
12
14
  from meerschaum.utils.packages import attempt_import
13
15
  from meerschaum.config import __doc__, __version__ as version, get_config
@@ -30,6 +32,12 @@ from meerschaum._internal.shell.ShellCompleter import ShellCompleter
30
32
  _clear_screen = get_config('shell', 'clear_screen', patch=True)
31
33
  from meerschaum.utils.misc import string_width, remove_ansi
32
34
  from meerschaum.jobs import get_executor_keys_from_context
35
+ from meerschaum.config.static import STATIC_CONFIG
36
+ from meerschaum._internal.arguments._parse_arguments import (
37
+ split_chained_sysargs,
38
+ parse_arguments,
39
+ parse_line,
40
+ )
33
41
 
34
42
  patch = True
35
43
  ### remove default cmd2 commands
@@ -63,6 +71,8 @@ reserved_completers = {
63
71
  ### To handle dynamic reloading, store shell attributes externally.
64
72
  ### This is because the shell object address gets lost upon reloads.
65
73
  shell_attrs = {}
74
+ AND_KEY: str = STATIC_CONFIG['system']['arguments']['and_key']
75
+ ESCAPED_AND_KEY: str = STATIC_CONFIG['system']['arguments']['escaped_and_key']
66
76
 
67
77
  def _insert_shell_actions(
68
78
  _shell: Optional['Shell'] = None,
@@ -111,7 +121,6 @@ def _completer_wrapper(
111
121
  if _check_keys is not None:
112
122
  return _check_keys
113
123
 
114
- from meerschaum._internal.arguments._parse_arguments import parse_line
115
124
  args = parse_line(line)
116
125
  if target.__name__ != 'default_action_completer':
117
126
  if len(args['action']) > 0:
@@ -149,7 +158,6 @@ def default_action_completer(
149
158
 
150
159
  def _check_complete_keys(line: str) -> Optional[List[str]]:
151
160
  from meerschaum._internal.arguments._parser import parser, get_arguments_triggers
152
- from meerschaum._internal.arguments._parse_arguments import parse_line
153
161
 
154
162
  ### TODO Add all triggers
155
163
  trigger_args = {
@@ -546,16 +554,68 @@ class Shell(cmd.Cmd):
546
554
  else:
547
555
  main_action_name = args['action'][0]
548
556
 
557
+ def _add_flag_to_sysargs(
558
+ chained_sysargs, key, value, flag, shell_key=None,
559
+ ):
560
+ shell_key = shell_key or key
561
+ shell_value = remove_ansi(shell_attrs.get(shell_key) or '')
562
+ if key == 'mrsm_instance':
563
+ default_value = get_config('meerschaum', 'instance')
564
+ elif key == 'repository':
565
+ default_value = get_config('meerschaum', 'default_repository')
566
+ elif key == 'executor_keys':
567
+ default_value = get_executor_keys_from_context()
568
+ else:
569
+ default_value = None
570
+
571
+ if shell_value == default_value:
572
+ return
573
+
574
+ for sysargs in chained_sysargs:
575
+ kwargs = parse_arguments(sysargs)
576
+ if key in kwargs:
577
+ continue
578
+ sysargs.extend([flag, value])
579
+
580
+ args['sysargs'] = list(
581
+ chain.from_iterable(
582
+ sub_sysargs + [AND_KEY]
583
+ for sub_sysargs in chained_sysargs
584
+ )
585
+ )[:-1]
586
+
587
+
549
588
  ### if no instance is provided, use current shell default,
550
589
  ### but not for the 'api' command (to avoid recursion)
551
- if 'mrsm_instance' not in args and main_action_name != 'api':
552
- args['mrsm_instance'] = str(shell_attrs['instance_keys'])
590
+ if main_action_name != 'api':
591
+ chained_sysargs = split_chained_sysargs(args['sysargs'])
592
+ chained_filtered_sysargs = split_chained_sysargs(args['filtered_sysargs'])
593
+ mrsm_instance = shell_attrs['instance_keys']
594
+ args['mrsm_instance'] = mrsm_instance
595
+ _add_flag_to_sysargs(
596
+ chained_sysargs, 'mrsm_instance', mrsm_instance, '-i', 'instance_keys',
597
+ )
598
+ _add_flag_to_sysargs(
599
+ chained_filtered_sysargs, 'mrsm_instance', mrsm_instance, '-i', 'instance_keys',
600
+ )
553
601
 
554
- if 'repository' not in args and main_action_name != 'api':
555
- args['repository'] = str(shell_attrs['repo_keys'])
602
+ repo_keys = str(shell_attrs['repo_keys'])
603
+ args['repository'] = repo_keys
604
+ _add_flag_to_sysargs(
605
+ chained_sysargs, 'repository', repo_keys, '-r', 'repo_keys',
606
+ )
607
+ _add_flag_to_sysargs(
608
+ chained_filtered_sysargs, 'repository', repo_keys, '-r', 'repo_keys',
609
+ )
556
610
 
557
- if 'executor_keys' not in args:
558
- args['executor_keys'] = remove_ansi(str(shell_attrs['executor_keys']))
611
+ executor_keys = remove_ansi(str(shell_attrs['executor_keys']))
612
+ args['executor_keys'] = remove_ansi(executor_keys)
613
+ _add_flag_to_sysargs(
614
+ chained_sysargs, 'executor_keys', executor_keys, '-e',
615
+ )
616
+ _add_flag_to_sysargs(
617
+ chained_filtered_sysargs, 'executor_keys', executor_keys, '-e',
618
+ )
559
619
 
560
620
  ### parse out empty strings
561
621
  if args['action'][0].strip("\"'") == '':
@@ -630,12 +690,12 @@ class Shell(cmd.Cmd):
630
690
  info(f"Debug mode is {'on' if shell_attrs['debug'] else 'off'}.")
631
691
 
632
692
  def do_instance(
633
- self,
634
- action: Optional[List[str]] = None,
635
- executor_keys=None,
636
- debug: bool = False,
637
- **kw: Any
638
- ) -> SuccessTuple:
693
+ self,
694
+ action: Optional[List[str]] = None,
695
+ executor_keys=None,
696
+ debug: bool = False,
697
+ **kw: Any
698
+ ) -> SuccessTuple:
639
699
  """
640
700
  Temporarily set a default Meerschaum instance for the duration of the shell.
641
701
  The default instance is loaded from the Meerschaum configuraton file
@@ -804,6 +864,7 @@ class Shell(cmd.Cmd):
804
864
  from meerschaum import get_connector
805
865
  from meerschaum.connectors.parse import parse_executor_keys
806
866
  from meerschaum.utils.warnings import warn, info
867
+ from meerschaum.jobs import get_executor_keys_from_context
807
868
 
808
869
  if action is None:
809
870
  action = []
@@ -814,6 +875,10 @@ class Shell(cmd.Cmd):
814
875
  executor_keys = ''
815
876
  if executor_keys == '':
816
877
  executor_keys = get_executor_keys_from_context()
878
+
879
+ if executor_keys == 'systemd' and get_executor_keys_from_context() != 'systemd':
880
+ warn(f"Cannot execute via `systemd`, falling back to `local`...", stack=False)
881
+ executor_keys = 'local'
817
882
 
818
883
  conn = parse_executor_keys(executor_keys, debug=debug)
819
884
 
@@ -987,7 +1052,7 @@ def input_with_sigint(_input, session, shell: Optional[Shell] = None):
987
1052
  )
988
1053
 
989
1054
  try:
990
- typ, label = shell_attrs['instance_keys'].split(':')
1055
+ typ, label = shell_attrs['instance_keys'].split(':', maxsplit=1)
991
1056
  connected = typ in connectors and label in connectors[typ]
992
1057
  except Exception as e:
993
1058
  connected = False
@@ -509,12 +509,22 @@ def _complete_delete_jobs(
509
509
  get_stopped_jobs,
510
510
  get_paused_jobs,
511
511
  get_running_jobs,
512
+ get_executor_keys_from_context,
512
513
  )
513
514
  from meerschaum.utils.misc import remove_ansi
515
+ from meerschaum.connectors.parse import parse_executor_keys
514
516
 
515
- executor_keys = executor_keys or remove_ansi(shell_attrs.get('executor_keys', 'local'))
517
+ executor_keys = (
518
+ executor_keys
519
+ or remove_ansi(
520
+ shell_attrs.get('executor_keys', get_executor_keys_from_context())
521
+ )
522
+ )
523
+
524
+ if parse_executor_keys(executor_keys, construct=False) is None:
525
+ return []
516
526
 
517
- jobs = get_jobs(executor_keys)
527
+ jobs = get_jobs(executor_keys, include_hidden=False)
518
528
  if _get_job_method:
519
529
  method_keys = [_get_job_method] if isinstance(_get_job_method, str) else _get_job_method
520
530
  method_jobs = {}
@@ -540,11 +550,11 @@ def _complete_delete_jobs(
540
550
 
541
551
 
542
552
  def _delete_venvs(
543
- action: Optional[List[str]] = None,
544
- yes: bool = False,
545
- force: bool = False,
546
- **kwargs: Any
547
- ) -> SuccessTuple:
553
+ action: Optional[List[str]] = None,
554
+ yes: bool = False,
555
+ force: bool = False,
556
+ **kwargs: Any
557
+ ) -> SuccessTuple:
548
558
  """
549
559
  Remove virtual environments.
550
560
  Specify which venvs to remove, or remove everything at once.
@@ -82,6 +82,7 @@ def _start_api(action: Optional[List[str]] = None, **kw):
82
82
  from meerschaum.actions import actions
83
83
  return actions['api'](action=['start'], **kw)
84
84
 
85
+
85
86
  def _start_jobs(
86
87
  action: Optional[List[str]] = None,
87
88
  name: Optional[str] = None,
@@ -119,25 +120,20 @@ def _start_jobs(
119
120
  Start the job 'happy_seal' but via the `--name` flag.
120
121
  This only applies when no text follows the words 'start job'.
121
122
  """
122
- import textwrap
123
123
  from meerschaum.utils.warnings import warn, info
124
124
  from meerschaum.utils.daemon._names import get_new_daemon_name
125
125
  from meerschaum.jobs import (
126
126
  Job,
127
- get_jobs,
128
127
  get_filtered_jobs,
129
128
  get_stopped_jobs,
130
129
  get_running_jobs,
131
130
  get_paused_jobs,
132
- get_restart_jobs,
133
131
  _install_healthcheck_job,
134
132
  )
135
- from meerschaum._internal.arguments._parse_arguments import parse_arguments
136
133
  from meerschaum.actions import actions
137
134
  from meerschaum.utils.prompt import yes_no
138
135
  from meerschaum.utils.formatting import print_tuple
139
- from meerschaum.utils.formatting._jobs import pprint_job, pprint_jobs
140
- from meerschaum.utils.formatting._shell import clear_screen
136
+ from meerschaum.utils.formatting._jobs import pprint_jobs
141
137
  from meerschaum.utils.misc import items_str
142
138
 
143
139
  names = []
@@ -24,13 +24,13 @@ from dash import Patch
24
24
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
25
25
  import dash_bootstrap_components as dbc
26
26
  from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
27
- from meerschaum.utils.daemon import Daemon
28
27
  from dash.exceptions import PreventUpdate
29
28
  from meerschaum.api.dash.jobs import (
30
29
  build_manage_job_buttons_div_children,
31
30
  build_status_children,
32
31
  build_process_timestamps_children,
33
32
  )
33
+ from meerschaum.jobs import Job
34
34
  from meerschaum.api.dash.users import is_session_authenticated
35
35
 
36
36
  @dash_app.callback(
@@ -53,15 +53,11 @@ def download_job_logs(n_clicks):
53
53
  raise PreventUpdate
54
54
 
55
55
  component_dict = json.loads(ctx[0]['prop_id'].split('.' + 'n_clicks')[0])
56
- daemon_id = component_dict['index']
57
- daemon = Daemon(daemon_id=daemon_id)
56
+ job_name = component_dict['index']
58
57
  now = datetime.now(timezone.utc)
59
- filename = (
60
- daemon.rotating_log.file_path.name[:(-1 * len('.log'))]
61
- + '_' + str(int(now.timestamp())) + '.log'
62
- )
58
+ filename = job_name + '_' + str(int(now.timestamp())) + '.log'
63
59
  return {
64
- 'content': daemon.log_text,
60
+ 'content': job.get_logs(),
65
61
  'filename': filename,
66
62
  }
67
63
 
@@ -73,12 +69,14 @@ def download_job_logs(n_clicks):
73
69
  Output({'type': 'process-timestamps-div', 'index': MATCH}, 'children'),
74
70
  Input({'type': 'manage-job-button', 'action': ALL, 'index': MATCH}, 'n_clicks'),
75
71
  State('session-store', 'data'),
72
+ State({'type': 'job-label-p', 'index': MATCH}, 'children'),
76
73
  prevent_initial_call = True,
77
74
  )
78
75
  def manage_job_button_click(
79
- n_clicks: Optional[int] = None,
80
- session_data: Optional[Dict[str, Any]] = None,
81
- ):
76
+ n_clicks: Optional[int] = None,
77
+ session_data: Optional[Dict[str, Any]] = None,
78
+ job_label: Optional[str] = None,
79
+ ):
82
80
  """
83
81
  Start, stop, pause, or delete the given job.
84
82
  """
@@ -102,20 +100,20 @@ def manage_job_button_click(
102
100
  raise PreventUpdate
103
101
 
104
102
  component_dict = json.loads(ctx[0]['prop_id'].split('.' + 'n_clicks')[0])
105
- daemon_id = component_dict['index']
103
+ job_name = component_dict['index']
106
104
  manage_job_action = component_dict['action']
107
105
  try:
108
- daemon = Daemon(daemon_id=daemon_id)
106
+ job = Job(job_name, job_label.replace('\n', ' ') if job_label else None)
109
107
  except Exception as e:
110
- daemon = None
111
- if daemon is None:
108
+ job = None
109
+ if job is None:
112
110
  raise PreventUpdate
113
111
 
114
112
  manage_functions = {
115
- 'start': functools.partial(daemon.run, allow_dirty_run=True),
116
- 'stop': daemon.quit,
117
- 'pause': daemon.pause,
118
- 'delete': daemon.cleanup,
113
+ 'start': job.start,
114
+ 'stop': job.stop,
115
+ 'pause': job.pause,
116
+ 'delete': job.delete,
119
117
  }
120
118
  if manage_job_action not in manage_functions:
121
119
  return (
@@ -125,7 +123,7 @@ def manage_job_button_click(
125
123
  dash.no_update,
126
124
  )
127
125
 
128
- old_status = daemon.status
126
+ old_status = job.status
129
127
  try:
130
128
  success, msg = manage_functions[manage_job_action]()
131
129
  except Exception as e:
@@ -136,15 +134,15 @@ def manage_job_button_click(
136
134
  check_interval_seconds = 0.01
137
135
  begin = time.perf_counter()
138
136
  while (time.perf_counter() - begin) < timeout_seconds:
139
- if daemon.status != old_status:
137
+ if job.status != old_status:
140
138
  break
141
139
  time.sleep(check_interval_seconds)
142
140
 
143
141
  return (
144
142
  alert_from_success_tuple((success, msg)),
145
- build_manage_job_buttons_div_children(daemon),
146
- build_status_children(daemon),
147
- build_process_timestamps_children(daemon),
143
+ build_manage_job_buttons_div_children(job),
144
+ build_status_children(job),
145
+ build_process_timestamps_children(job),
148
146
  )
149
147
 
150
148
  dash_app.clientside_callback(
@@ -165,7 +163,7 @@ dash_app.clientside_callback(
165
163
  }
166
164
 
167
165
  const triggered_id = dash_clientside.callback_context.triggered_id;
168
- const job_daemon_id = triggered_id["index"];
166
+ const job_name = triggered_id["index"];
169
167
 
170
168
  iframe = document.getElementById('webterm-iframe');
171
169
  if (!iframe){ return dash_clientside.no_update; }
@@ -174,7 +172,7 @@ dash_app.clientside_callback(
174
172
  {
175
173
  action: "show",
176
174
  subaction: "logs",
177
- subaction_text: job_daemon_id,
175
+ subaction_text: job_name,
178
176
  },
179
177
  url
180
178
  );
@@ -197,44 +195,38 @@ dash_app.clientside_callback(
197
195
  prevent_initial_call = True,
198
196
  )
199
197
  def refresh_jobs_on_interval(
200
- n_intervals: Optional[int] = None,
201
- session_data: Optional[Dict[str, Any]] = None,
202
- ):
198
+ n_intervals: Optional[int] = None,
199
+ session_data: Optional[Dict[str, Any]] = None,
200
+ ):
203
201
  """
204
202
  When the jobs refresh interval fires, rebuild the jobs' onscreen components.
205
203
  """
206
204
  session_id = session_data.get('session-id', None)
207
205
  is_authenticated = is_session_authenticated(session_id)
208
206
 
209
- daemon_ids = [
207
+ job_names = [
210
208
  component_dict['id']['index']
211
209
  for component_dict in dash.callback_context.outputs_grouping[0]
212
210
  ]
213
211
 
214
- ### NOTE: The daemon may have been deleted, but the card may still exist.
215
- daemons = []
216
- for daemon_id in daemon_ids:
217
- try:
218
- daemon = Daemon(daemon_id=daemon_id)
219
- except Exception as e:
220
- daemon = None
221
- daemons.append(daemon)
212
+ ### NOTE: The job may have been deleted, but the card may still exist.
213
+ jobs = [Job(name) for name in job_names]
222
214
 
223
215
  return (
224
216
  [
225
217
  (
226
- build_manage_job_buttons_div_children(daemon)
218
+ build_manage_job_buttons_div_children(job)
227
219
  if is_authenticated
228
220
  else []
229
221
  )
230
- for daemon in daemons
222
+ for job in jobs
231
223
  ],
232
224
  [
233
- build_status_children(daemon)
234
- for daemon in daemons
225
+ build_status_children(job)
226
+ for job in jobs
235
227
  ],
236
228
  [
237
- build_process_timestamps_children(daemon)
238
- for daemon in daemons
229
+ build_process_timestamps_children(job)
230
+ for job in jobs
239
231
  ],
240
232
  )