1212from rich .progress import Progress , TimeElapsedColumn , SpinnerColumn , TextColumn
1313from rich .console import Console , Group
1414from rich .logging import RichHandler
15- import rich .style
1615from rich .live import Live
17- from rich .theme import Theme
18-
19- custom_theme = Theme (
20- {
21- "progress.description" : "yellow bold" ,
22- }
23- )
24-
25-
26- JOB_STATUS_DICT = {
27- "C" : "Completed" ,
28- "E" : "Exiting" ,
29- "H" : "Held" ,
30- "Q" : "Queued" ,
31- "R" : "Running" ,
32- "T" : "Moving" ,
33- "W" : "Waiting" ,
34- "S" : "Suspended" ,
35- "B" : "Batch" ,
36- }
37-
38-
39- def _log_formatter (
40- record : dict ,
41- icon : bool = False ,
42- ) -> str :
43- """Log message formatter"""
44- color_map = {
45- "TRACE" : "dim blue" ,
46- "DEBUG" : "cyan" ,
47- "INFO" : "bold" ,
48- "SUCCESS" : "bold green" ,
49- "WARNING" : "yellow" ,
50- "ERROR" : "bold red" ,
51- "CRITICAL" : "bold white on red" ,
52- }
53- lvl_color = color_map .get (record ["level" ].name , "cyan" )
54-
55- if icon :
56- icon = "{level.icon}"
57- else :
58- icon = ""
59- return (
60- "[not bold green]{time:YYYY/MM/DD HH:mm:ss}[/not bold green] |"
61- + f"{ icon } - [{ lvl_color } ]{{message}}[/{ lvl_color } ]"
62- # Right-align code location:
63- + " [dim]{name}:{function}:{line}[/dim]"
64- )
16+ from rich .progress import Progress , ProgressColumn , Text
6517
18+ from pybs .constants import JOB_STATUS_DICT , POLL_INTERVAL
19+ from pybs .console import custom_theme , _log_formatter
20+ from pybs .server import PBSServer
21+ from pybs .console .tabcomplete import complete_remote_path , complete_hostname , complete_job_script
22+ from pybs .console .ui import CompactTimeColumn
6623
6724console = Console (
6825 theme = custom_theme ,
@@ -86,66 +43,6 @@ def _log_formatter(
8643 level = level ,
8744)
8845
89-
90- from pybs .server import PBSServer
91- from pybs .console .tabcomplete import complete_remote_path , complete_hostname , complete_job_script
92-
93- POLL_INTERVAL = 0.5
94-
95-
96- # DONE:
97- # - add tab autocompletion scripts
98- # - add hostname tab completion (use ~/.ssh/config)
99- # - fix logging for qsub wait
100- # - add TUI timer for job submission
101- # - add intelligent remote server expansion of paths example $SCRATCH
102- # to avoid issue where VS code does not evaluate $ variables correctly.
103- # - add support for local job scripts
104-
105- # TODO:
106- # - change character width on subcommand tab complete suggestions
107-
108- # - add help to ck args
109- # - add tab complete for remote paths similar to `scp```
110- # - add auto install of ssh config required hostname alias
111- # - add arbitrary command execution for any method (with certain decorator)
112- # from PBSServer class
113- # e.g. write `qsub` and this will call the `qsub` method of the PBSServer class
114- # if a method with that name exists.
115- # - refactoring of PBSServer class to use `ssh_command` decorator
116- # - add `config` command to add config items to a config file
117- # such as turning debug on/off
118-
119- # Future TODO:
120- # - add db for currently running jobs, able to login to
121- # any server and see resources, walltime etc.
122- # - add "autorefresh" or "keepalive" option to remember when the walltime will
123- # expire, and request another GPU node that overlaps so we can keep the session logged
124- # in on the same node.
125-
126- from rich .progress import Progress , ProgressColumn , Text
127- from datetime import timedelta
128-
129-
130- # TODO: make PR for this?
131- class CompactTimeColumn (ProgressColumn ):
132- """Renders time elapsed."""
133-
134- def render (self , task : "Task" ) -> Text :
135- """Show time elapsed."""
136- elapsed = task .finished_time if task .finished else task .elapsed
137- if elapsed is None :
138- return Text ("-:--:--" , style = "progress.elapsed" )
139- delta = timedelta (seconds = max (0 , elapsed ))
140- # get number of seconds
141- n_seconds = delta .total_seconds ()
142- # customise progress.elapsed to be gray text
143- # style = "progress.elapsed"
144- # style = "grey58"
145- style = "white"
146- return Text (f"({ n_seconds :.1f} s)" , style = style )
147-
148-
14946@ck .command ()
15047@ck .argument (
15148 "hostname" ,
@@ -204,9 +101,11 @@ def code(
204101 log .debug (f"Launching job on { hostname } with remote path { remote_path } " )
205102 log .debug (f"Job script location: { job_script_location } " )
206103
104+
207105 if job_script_location is None :
208106 log .info (f"Checking if job script { job_script } exists..." )
209107 if job_script .is_file ():
108+ job_script = job_script .resolve ()
210109 log .info (f"Using local job script: { job_script } " )
211110 job_script_location = "local"
212111 else :
@@ -218,18 +117,11 @@ def code(
218117 progress = Progress (
219118 SpinnerColumn (
220119 spinner_name = "line" ,
221- # spinner_name="simpleDots",
222- # spinner_name="simpleDotsScrolling",
223120 style = "blue" ,
224121 ),
225122 TextColumn ("[progress.description]{task.description}" , style = "blue" ),
226- # SpinnerColumn(
227- # spinner_name="simpleDotsScrolling",
228- # style="blue",
229- # ),
230123 CompactTimeColumn (),
231124 )
232-
233125 monitor_job_status = Progress (
234126 SpinnerColumn (spinner_name = "dots" , style = "white" ),
235127 TextColumn (
@@ -239,30 +131,27 @@ def code(
239131 """ ,
240132 #style="blink bold black on yellow",
241133 ),
242- # TextColumn("[progress.description]{task.fields[job_status]}"),
243- # TextColumn("[progress.description]Node: {task.fields[node]}"),
244- # CompactTimeColumn(), # show 1dp of seconds (elapsed time)
245134 )
246135
247136 import time
248-
249137 # If remote, check if the file exists on the remote server
250138 server = PBSServer (hostname , verbose = verbose )
139+ hostname_expanded = server .full_remotehost
251140 if job_script_location == "remote" :
252141 with progress :
253142 task1 = progress .add_task (
254- f"Checking job script on [bold][white]{ hostname } [/white][/bold] exists... " ,
143+ f"Checking job script on [bold][white]{ hostname_expanded } [/white][/bold] exists... " ,
255144 total = 1 ,
256145 )
257146 # expand remote path
258147 log .info (f"Expanding remote path { job_script } " )
259148 job_script = server .expand_remote_path (job_script )
260149 log .info (f"--> { job_script } " )
261150 if not server .check_file_exists (job_script ):
262- log .error (f"Job script { job_script } not found on { hostname } . Exiting." )
151+ log .error (f"Job script { job_script } not found on { hostname_expanded } . Exiting." )
263152 return
264153 else :
265- log .info (f"Job script found on { hostname } ." )
154+ log .info (f"Job script found on { hostname_expanded } ." )
266155 # mark progress as complete
267156 progress .update (task1 , completed = True )
268157
@@ -285,7 +174,7 @@ def code(
285174 # Expand path
286175 with progress :
287176 task = progress .add_task (
288- f"Expanding remote path on [bold][white]{ hostname } [/white][/bold]... " ,
177+ f"Expanding remote path on [bold][white]{ hostname_expanded } [/white][/bold]... " ,
289178 total = 1 ,
290179 )
291180 log .info (f"Expanding remote path { remote_path } " )
@@ -301,16 +190,16 @@ def code(
301190 else :
302191 with progress :
303192 task1 = progress .add_task (
304- f"Checking that workspace directory on [bold][white]{ hostname } [/white][/bold] exists... " ,
193+ f"Checking that workspace directory on [bold][white]{ hostname_expanded } [/white][/bold] exists... " ,
305194 total = 1 ,
306195 )
307196 if not server .check_dir_exists (remote_path ):
308197 log .error (
309- f"Remote path { remote_path } not found on { hostname } . Exiting."
198+ f"Remote path { remote_path } not found on { hostname_expanded } . Exiting."
310199 )
311200 return
312201 else :
313- log .info (f"Remote path { remote_path } found on { hostname } ." )
202+ log .info (f"Remote path { remote_path } found on { hostname_expanded } ." )
314203 # mark progress as complete
315204 progress .update (task1 , completed = True )
316205
@@ -439,7 +328,10 @@ def code(
439328 # Clear all tasks from progress
440329 ids = progress .task_ids
441330 for task_id in ids :
442- progress .remove_task (task_id )
331+ progress .remove_task (task_id )
332+
333+ for task_id in monitor_job_status .task_ids :
334+ monitor_job_status .remove_task (task_id )
443335
444336 progress_group = Group (
445337 progress ,
0 commit comments