richqueue 0.5__tar.gz → 0.6__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: richqueue
3
- Version: 0.5
3
+ Version: 0.6
4
4
  Summary: RichQueue
5
5
  Project-URL: Homepage, https://github.com/mwinokan/RichQueue/issues
6
6
  Project-URL: Bug Tracker, https://github.com/mwinokan/RichQueue/issues
@@ -16,4 +16,30 @@ Requires-Dist: typer
16
16
  Description-Content-Type: text/markdown
17
17
 
18
18
  # RichQueue
19
- 💰 RichQueue: A colourful and pythonic SLURM queue viewer
19
+
20
+ > 💰 RichQueue: A colourful and pythonic SLURM queue viewer
21
+
22
+ Installation from a modern python environment (>3.10) should be as simple as:
23
+
24
+ ```shell
25
+
26
+ pip install --upgrade richqueue
27
+ ```
28
+
29
+ To see your live-updating SLURM queue:
30
+
31
+ ```shell
32
+ rq
33
+ ```
34
+
35
+ ![queue_example](https://github.com/user-attachments/assets/d99ca5e9-7675-4853-ab28-2d7b4da855f2)
36
+
37
+ ## Other options
38
+
39
+ To see more detail:
40
+
41
+ ```rq --long```
42
+
43
+ To see someone else's queue:
44
+
45
+ ```rq --user USER```
@@ -0,0 +1,28 @@
1
+ # RichQueue
2
+
3
+ > 💰 RichQueue: A colourful and pythonic SLURM queue viewer
4
+
5
+ Installation from a modern python environment (>3.10) should be as simple as:
6
+
7
+ ```shell
8
+
9
+ pip install --upgrade richqueue
10
+ ```
11
+
12
+ To see your live-updating SLURM queue:
13
+
14
+ ```shell
15
+ rq
16
+ ```
17
+
18
+ ![queue_example](https://github.com/user-attachments/assets/d99ca5e9-7675-4853-ab28-2d7b4da855f2)
19
+
20
+ ## Other options
21
+
22
+ To see more detail:
23
+
24
+ ```rq --long```
25
+
26
+ To see someone else's queue:
27
+
28
+ ```rq --user USER```
@@ -3,7 +3,7 @@ requires = ["hatchling"]
3
3
  build-backend = "hatchling.build"
4
4
  [project]
5
5
  name = "richqueue"
6
- version = "0.5"
6
+ version = "0.6"
7
7
  authors = [
8
8
  { name = "Max Winokan", email = "max@winokan.com" },
9
9
  ]
@@ -5,6 +5,7 @@ from rich.console import Console
5
5
  from rich.panel import Panel
6
6
  from rich.layout import Layout
7
7
  from pandas import DataFrame
8
+ import subprocess
8
9
 
9
10
  console = Console()
10
11
 
@@ -28,6 +29,16 @@ def color_by_state(state):
28
29
  else:
29
30
  return state
30
31
 
32
+ def color_by_node_state(state):
33
+
34
+ if state == "ALLOCATED":
35
+ return "[orange1]Allocated"
36
+ elif state == "MIXED":
37
+ return "[bright_yellow]Mixed"
38
+ elif state == "IDLE":
39
+ return "[bright_green]Idle"
40
+ else:
41
+ return f"[red]{state}"
31
42
 
32
43
  def extract_inner(df, key, inner):
33
44
 
@@ -71,6 +82,72 @@ def extract_list(df, key):
71
82
 
72
83
  df[key] = df.apply(inner, axis=1)
73
84
 
85
+ def mem_string(mb):
86
+
87
+ # gb, mb = divmod(mb, 1024)
88
+
89
+ # return f"{gb}G {mb}M"
90
+
91
+ return f"{mb/1024:.0f}G"
92
+
93
+ def node_table(
94
+ df,
95
+ title: str = None,
96
+ box: bool = False,
97
+ panel: bool = True,
98
+ idle: bool = True,
99
+ ):
100
+
101
+ if idle:
102
+ df = df[df["node"].isin(["IDLE", "MIXED"])]
103
+ title = title or "idle nodes"
104
+ else:
105
+ title = title or "nodes"
106
+
107
+ from rich.table import Table
108
+
109
+ if box:
110
+ from rich.box import SIMPLE
111
+
112
+ box = SIMPLE
113
+
114
+ table = Table(title=title, box=box, header_style="")
115
+
116
+ table.add_column("[bold underline]Node", justify='left', no_wrap=True, style="bold")
117
+ table.add_column("[bold underline]State", justify='left', no_wrap=True, style="bold")
118
+ table.add_column("[green_yellow underline]Partition", justify='left', no_wrap=True, style="green_yellow")
119
+ table.add_column("[magenta underline]#C", justify='right', no_wrap=True, style="magenta")
120
+ table.add_column("[magenta underline]Memory", justify='right', no_wrap=True, style="magenta")
121
+ table.add_column("[dodger_blue2 underline]Features", justify='left', no_wrap=True, style="dodger_blue2")
122
+
123
+ reservation = any(df["reservation"].values)
124
+
125
+ if reservation:
126
+ table.add_column("[bold underline]Reservation", justify='left', no_wrap=True, style="bold")
127
+
128
+ for i,row in df.iterrows():
129
+
130
+ values = []
131
+
132
+ values.append(row.nodes)
133
+ # values.append(row.nodes)
134
+ values.append(color_by_node_state(row.node))
135
+ values.append(row.partition)
136
+ values.append(str(row.cpus_idle))
137
+ values.append(mem_string(row.memory_free))
138
+ values.append(row.features)
139
+
140
+ if reservation:
141
+ values.append(row.reservation)
142
+
143
+ table.add_row(*values)
144
+
145
+ # table.add_row(*[str(v) for v in row.values])
146
+
147
+ if panel:
148
+ return Panel(table, expand=False)
149
+
150
+ return table
74
151
 
75
152
  def job_table(
76
153
  df,
@@ -289,11 +366,11 @@ def show_queue(
289
366
  user: None | str = None,
290
367
  long: bool = False,
291
368
  return_table: bool = False,
369
+ hist: int = 2,
370
+ hist_unit: str= "weeks",
292
371
  box: bool = False,
293
372
  ):
294
373
 
295
- import subprocess
296
-
297
374
  if user == "all":
298
375
  user = None
299
376
  elif user is None:
@@ -304,7 +381,10 @@ def show_queue(
304
381
  if user:
305
382
  s_command = f"{command} -u {user} --json"
306
383
  else:
307
- s_command = f"{command} --json"
384
+ s_command = f"{command} -u {user} --json"
385
+
386
+ if command == "sacct":
387
+ s_command += f" -S now-{hist}{hist_unit}"
308
388
 
309
389
  x = subprocess.Popen([s_command], shell=True, stdout=subprocess.PIPE)
310
390
  output = x.communicate()
@@ -318,7 +398,7 @@ def show_queue(
318
398
  cluster = METADATA["cluster_name"]
319
399
 
320
400
  if command == "sacct":
321
- title = f"[bold]{user}'s history on {cluster}"
401
+ title = f"[bold]{user}'s previous jobs (last {hist} {hist_unit})"
322
402
 
323
403
  elif user:
324
404
  df = df[df["user_name"] == user]
@@ -366,13 +446,54 @@ def dual_layout(
366
446
  else:
367
447
  return layout
368
448
 
449
+ def parse_sinfo_json(payload: dict) -> "DataFrame":
450
+
451
+ df = DataFrame(payload['sinfo'])
452
+
453
+ df2 = df.drop(columns=["port", "weight", "disk", "sockets", "threads", "cluster", "comment", "extra", "gres", "reason", "cores"])
454
+ df2["node"] = df2.apply(lambda x: x["node"]["state"][0], axis=1)
455
+ df2["nodes"] = df2.apply(lambda x: x["nodes"]["nodes"][0], axis=1)
456
+ df2["cpus_max"] = df2.apply(lambda x: x["cpus"]["maximum"], axis=1)
457
+ df2["cpus_idle"] = df2.apply(lambda x: x["cpus"]["idle"], axis=1)
458
+ df2["cpus_allocated"] = df2.apply(lambda x: x["cpus"]["allocated"], axis=1)
459
+ df2["memory_max"] = df2.apply(lambda x: x["memory"]["maximum"], axis=1)
460
+ df2["memory_free"] = df2.apply(lambda x: x["memory"]["free"]["maximum"]["number"], axis=1)
461
+ df2["memory_allocated"] = df2.apply(lambda x: x["memory"]["allocated"], axis=1)
462
+ df2["features"] = df2.apply(lambda x: x["features"]["total"], axis=1)
463
+ df2["partition"] = df2.apply(lambda x: x["partition"]["name"], axis=1)
464
+
465
+ df2.drop(columns=["cpus", "memory"], inplace=True)
466
+
467
+ return df2
468
+
469
+ def idle_queue():
470
+
471
+ s_command = "sinfo -N --json"
472
+
473
+ x = subprocess.Popen([s_command], shell=True, stdout=subprocess.PIPE)
474
+ output = x.communicate()
475
+
476
+ payload = json.loads(output[0])
477
+
478
+ df = parse_sinfo_json(payload)
479
+
480
+ table = node_table(df)
481
+
482
+ console.print(table)
369
483
 
370
484
  @app.command()
371
- def show(user: None | str = None, long: bool = False):
485
+ def show(user: None | str = None, long: bool = False, idle: bool = False, hist: int | None = None, hist_unit: str = "weeks"):
372
486
 
373
487
  loop = True
374
488
 
375
- if loop:
489
+ if hist:
490
+
491
+ show_queue(user=user, command="sacct", long=long, hist=hist, hist_unit=hist_unit)
492
+
493
+ elif idle:
494
+ idle_queue()
495
+
496
+ elif loop:
376
497
 
377
498
  from rich.live import Live
378
499
  import time
@@ -393,7 +514,6 @@ def show(user: None | str = None, long: bool = False):
393
514
  # show_queue(command="squeue", user=user, long=long)
394
515
  # show_queue(command="sacct", user=user, long=long)
395
516
 
396
-
397
517
  def main():
398
518
  app()
399
519
 
richqueue-0.5/README.md DELETED
@@ -1,2 +0,0 @@
1
- # RichQueue
2
- 💰 RichQueue: A colourful and pythonic SLURM queue viewer
File without changes
File without changes
File without changes
File without changes