richqueue 1.6__tar.gz → 1.8__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
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: richqueue
3
- Version: 1.6
3
+ Version: 1.8
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
@@ -3,7 +3,7 @@ requires = ["hatchling"]
3
3
  build-backend = "hatchling.build"
4
4
  [project]
5
5
  name = "richqueue"
6
- version = "1.6"
6
+ version = "1.8"
7
7
  authors = [
8
8
  { name = "Max Winokan", email = "max@winokan.com" },
9
9
  ]
@@ -8,6 +8,9 @@ def dual_layout(function, **kwargs):
8
8
 
9
9
  layout = Layout()
10
10
 
11
+ if "console_height" in kwargs:
12
+ layout.size = kwargs["console_height"]
13
+
11
14
  upper, lower = function(**kwargs)
12
15
 
13
16
  upper = Layout(renderable=upper, name="upper")
@@ -40,7 +40,9 @@ def show_log(
40
40
  if user is None:
41
41
  user = get_user()
42
42
 
43
- if job is None:
43
+ job_id = job
44
+
45
+ if job_id is None:
44
46
 
45
47
  def job_getter():
46
48
  return guess_current_job(user=user)
@@ -48,7 +50,7 @@ def show_log(
48
50
  else:
49
51
 
50
52
  def job_getter():
51
- return get_specific_job(job=job, user=None)
53
+ return get_specific_job(job=job_id, user=None)
52
54
 
53
55
  job = job_getter()
54
56
 
@@ -90,7 +92,10 @@ def log_layout_pair(job_getter, **kwargs):
90
92
  limit = console.size.height - 2 * PANEL_PADDING - upper.renderable.row_count
91
93
 
92
94
  lower = log_table(
93
- stdout=job.standard_output, stderr=job.standard_error, limit=limit
95
+ job_id=job.job_id,
96
+ stdout=job.standard_output,
97
+ stderr=job.standard_error,
98
+ limit=limit,
94
99
  )
95
100
  lower = Panel(lower, expand=False)
96
101
 
@@ -144,7 +149,13 @@ def get_specific_job(job: int, user: str, **kwargs):
144
149
  console.print("[red bold]Could not find job with ID {job_id=}")
145
150
  return None
146
151
 
147
- assert len(matches) == 1
152
+ elif len(matches) > 1:
153
+
154
+ matches = matches[matches["command"].notna()]
155
+
156
+ if len(matches) > 1:
157
+ console.print(matches.to_dict(orient="records"))
158
+ raise ValueError("Multiple job matches")
148
159
 
149
160
  return matches.iloc[0]
150
161
 
@@ -31,9 +31,9 @@ def show(
31
31
  ] = None,
32
32
  long: Annotated[bool, Option("-v", "--long", help="More detailed output")] = False,
33
33
  idle: Annotated[bool, Option("-i", "--idle", help="Show available nodes")] = False,
34
- loop: Annotated[
35
- bool, Option("-l", "--loop", help="Show a live-updating screen")
36
- ] = True,
34
+ no_loop: Annotated[
35
+ bool, Option("-nl", "--no-loop", help="Don't show a live-updating screen")
36
+ ] = False,
37
37
  hist: Annotated[
38
38
  int, Option("-h", "--hist", help="Show historical jobs from the last X weeks")
39
39
  ] = None,
@@ -49,6 +49,7 @@ def show(
49
49
  ] = False,
50
50
  ):
51
51
 
52
+ loop = True
52
53
  screen = True
53
54
  disappear = not screen
54
55
 
@@ -56,7 +57,7 @@ def show(
56
57
  "user": user,
57
58
  "long": long,
58
59
  "idle": idle,
59
- "loop": loop,
60
+ "no_loop": no_loop,
60
61
  "hist": hist,
61
62
  "hist_unit": hist_unit,
62
63
  "screen": screen,
@@ -66,6 +67,9 @@ def show(
66
67
 
67
68
  # console.print(kwargs)
68
69
 
70
+ if no_loop:
71
+ loop = False
72
+
69
73
  match (bool(idle), bool(hist), bool(job)):
70
74
  case (True, False, False):
71
75
  loop = False
@@ -110,6 +114,8 @@ def show(
110
114
  # static layout
111
115
  else:
112
116
 
117
+ kwargs["console_height"] = 500
118
+
113
119
  layout = layout_func(**kwargs)
114
120
  console.print(layout)
115
121
 
@@ -0,0 +1 @@
1
+ gAAAAABnEpbvOgLsVhspN5M4SH7oIIjr2t8BaSv96xKOIeaZfAOABpiQdZiLzMWre3LD14ZH-i0VeydLW6JSaepYtOW6qhnOtZMUh5LvXBubEhPPQkR6w3F7rMOLQQOsBHxHBsYktVf9-733DnWH7KgSMrUT5StcpFmM9mPG--hcJMWfFi5YsOkmlcMBn5elcku9AJRkgyYKzcxdZnX1uWdPJk3C2j-lzysAhecW4sLb7ygvFwvqsz2GFlYebf2JUHjTulYGuU7UuSh-N2NG1X5dZX3mRi9Pn8IDRvidVowdUdQq7IcAxSp8AxdF4OqS1IhIcUJ1xVlRe8qRvu5jtxP3NyBV39zBgfAp1AZInm2CoxLAzGL1_Lb5EeO0ev9PGu6eMSvHfenBLWS_WnB24z0dwfWF9bQPeyOoKbm-0GSBQPSjIJPOaCBfhkFxH0SaRdYVuwgqzCGZXQL_j8OjpDHi_XSWMXCFWuswE4WS2kiu4g46M4mQP-HOR4EuwibdxiPxhvuHwibnEoK0pgxG9wyQeowejaOm-Au7avwd2x_M_S1aY3c4TtI_Immx4BqBuBMFWDOgdwLzHuNgagU5NvYNsXElow0bwlIERjboNvq3Pn3m4kHRF3-RoxmiXuhNkN0Ho11GKGaylbFSTXHrIJUIDKSjzeQnVOPLYOb9JvqL9RI7HrikFIUASJTogH4whfLKNikTaHrSkDOpR5PiZhVTNARkZI9xZ6vEcmDemFTLsQ8RZ22a5w-dG03i7dpnjAnfL9at7fcno56Fz83tev_pb8EIY32SoaEGKjbz_jLIO2nKM238LttWfIefy93Q6Uk_W7Jc3gtmcWj1sQ-KEJ7qmk4QAC--lGNnF8AD1HvD6jyroKzGzRJCbef76kG_6m_zlsZXVSIctA2var1vIpHWMa459jIxpzNiydldvh9NxcP3GiLsFbUDVs3MtzxWz7Onqy2KlnDCFWS76fZIvwuHzdryKEEYEUebfEIxwFLK4dAH9gx6lK0HQY1_Q0NV3MEi5jTcc_I3vpLeiu3xTjmPAt14s_UESjjOoXqxgtG7EQwxX8j2NJdqNNf1NeeHN1Hz4ZKy1XB27hjFqI_bF_f-bW4UgZEddGdbwhjDH8AefKuOTXoi764xlhklvhBLBTfpnqzGK6Qy1tjG-QbbdllEzKlYyMHqQwXvUrYjmy3OnmkkxVN03eYNyeH8tXhx8TSOSouQK7lX31SSK9Oo05H1XQLt6XbmZlXLOo7Yu3zeHBj5MADW7AqLyAk6WDmtGP-UJZviy1anetLqWEV862WuZFEcVSQAylPDgSLF9AZexb0YYQ3soPHeRsiwsjFeFeCLJeq_fN8mZ8CRRIn9bTulhWiYGJurq58XOzvZPiIjdOjY3F9sGRw6vetfFD9STKo_CUlvf_rYrUzUl5Z5S9PEC-yxUrDI4D25d-pCyum_nO9YtQlsv1f4MPwpwzFQvgdqBk43FVKmy8j2N0WHXg_aUVukKYV882ECvq1JT9kIp70yr1fZMKcDEDmgSrExBUoXI7hoN5HkDTKzbe_Mq76-OKH_1xLk4s4Hvn5Zd_v9s0ArshK5W9shQ10SltzHhegT_H9hSWrXQLi3BJx1OE77RRzXoa_eBIFSnnSNQeerbbuK08QsdAtYuk7ikmwg5gKy-8dnX73c9hYu5Au1f_7ZBH5OrlJJe2mU18dX1-uVsyfqYQsUNZSS9YVmeVfcvKFl_dyxVyzfSCQLTizeslpbs8SH9p2iDTL5d2D4ybF3_4AVUiBA_Am7Ael2mM0UZCth4zUyirePiGYXrcxXjQtSHyb_kcnwDYIg2WGhfe99A6EZ51755HTkQHtlLUlCv8zEo57Mz7ljAPH6E0f-fcyhpOJQshe7PV3xsBZL_sHrI5VnNkIQmP7onXQNVY86FphNgKcXCuTkw65I9TEAOEN5_y_YkQGT4Swm0xc83cS0h9jlvVLaBHLOecKXFCbVLCziHghD0Z9bNiuh-Mf7R0iJGDzzRN9AW3oMpcWoPTGuC0QNJWsQN9fXX783LkyxbsxtjFGli4USNhWnes4CNFFMbA5JaLhFPlERxRFMUUB6Du7kSQAvAB9ooxjYNvNSAgRQBc9EaKCVXVc8gDCGvfZcIdI90ysDbeERBJ1gnfKUtO5kf-ar7D1N5X5loOdW0oZ340fd84eHYBEKRJ7kFlr66bHMeEk8nUxjQ_ZUTcHjxDCnhYRrZkCw86zNiWriMKm4Nw7ErZ6kCQqQ3Zr1zZW1IkiMZSmv7qxBviZvAU_LsCeRfm1lqx3rlIIw9J4vfIUwTb5yZ-jtq_BBX6eYxXGCfnuZMm9pcAlWjAjFGOvGQp6JolkBckDNk6RimLaW33WCssEEntVTqiJYFFpXKmD57pfN0hoWEzslnESWS4xTOIxG1U-aJfC5VNzYwgZkMpaZeTuVGQNKUMk2zcHuXWBCtRi0uN3dO7JfgCn2Oeaa6ZPUAWz241VBzg1ka_2pENH5Eki4BKNyw9h928gIiDVVFnaKJydU-GGWuVXtdkhA-xMIuS7H8Mnqtgv2LVpNJyDu1FYKIT_zme1MLScLbuoiIi-ZcTC3Vs-vcroHUMdFPJjscvKZ5XtFQelzLiURcQ9DDzehmf-2Py_pgg5-pWGnNxhaYbV7Nzf68Xfoe5guQzjyGBSjR3Fzk7rIx9p1OfnRLD7zC9HNGy35CqA0K0Gzjym573rUkJ2QaircGt4BNRMqTbKr7dmt6xPIv7dOpK3eBNJvk-HVnz8hKqZTtYt9Kr9qivMuk3eOXjPaeSd6Ih4gUYDnIoU9V3nca-QQhYeaMt8ujfRHl1oE2eod23cZWpecqPitbaJRJQ5Bwmx-PeyjxIlYEby4WO8zr75A7NlVUu4kADDK8XtpQJDkyGMRZzuiqx5rhldyspmkP2zXtMV9tn-KQGZbUM1BW5ldXZZjdVrL3TZabM0vLbpnN_Pf8_3TYWMYOkc5TlNJ9NHbgo7epucW2wGV6O_dytO4TpLEDf4SXR2t7sE8ClJXrYbRvDJlOi3NK_799S7lHzNqw1RU_SnGPujgDRht2wWVUSSXOWijkwFLgdEkHomyKhMBIAwUIDPw_PBxyTZJJW54IWlaGudy9Wxpgi_EszuhuG0IJdC6ynNL-VBUwHZCGkiM8RmQOQdV3UKlcp0MozknS9lVrlenBnGjSK4eJxA1w5dQHssDVW6iK4Dm8SBFDE3B1EwBFd5ck9knoZpnr3e6Wqd2cXyGrPR2axEnjUhwaH0woZi9sXmBS6lbTTG7YfFhFStBqo6v0qvWW3eGyHE9Uzdef1x6WDTtX3TQd1OonX6wrRPv1ne75EhD8gbS3Zat41o264ivzTVbK_6X7htAAHrPbd1_nxoDgT4ezHlOtN1jOzqShCPuwgOaZCowGdbJtQZI2i9GlIAlwYIj-OTpTZGAeYpx35c7jGgIl5h1ALNpAguM-MEF4nnsJDb9j2rqOZkJjacMVORQQuHtUX7iogsdpLR-_dwIjca5H0aM5o0aIrQQ6prwVTZzpb8SoAQq9uFJXfSWO3JpTMo_3jH6oI4d8UpMXApy-QK9hQHTh8dNvM5uZmmirrpFBfTE409BvuYhcojjwXaT7KHKUNlG9hm6-WpPK2WrUD7PeTCHI_s6rW55Tb8bhhvmW0SEiGM2TN9ymrKZ_16GQlVmNiepsl9Ox4AROgYNqcuX95X9JbgwzhKuekfIfdY-c_C4EdY4AtkNl7X3idRNjJWZJwftUzkEXKj9bCvKeaBsZ4rkV-7yETAnNykoWnjAcS2uPpEro_ryr-H47AkXuxlDPGaZgLKAnutyEJz0nDjV2LCmk5M5ApqnjRS5c1jAQpypWIeUa1e6O0NyYVhkY95LLHAcWC_1YsRy63t51ntvl9POZKO7wlwu5UJ8z0QEfWb_4Schs2RX_bSEFgYY0QVWLXP-DoeFudYiaK2m1e661-LfTz07lwROQQ8xiDNuuiuOzdm1bTr4FIQxulQlxcjQJH0H1IwqhcydnkUWCa5-us65flEUhGay5YqI0hPN3XB675xm7_o2Xps3spFy6U0bWv737GUC39KboVDKIcHvwNS72udwy8XW5ggNEyyUkRvXj82LNo88sk38j_A-dYR0nKOKA1j9FktkA_EPJJ8gfkAGOKzDx4B8FA_yQ_yo3IFr4ge52EuKInVsyx7gmRJp4oaZdskQFHIs7eR3qs_TN_eLQohoY-mv4bi7E_Z-OjCi-DEPEjTXt-DgHRG_GOjzvdVYbee1ZJu1BHO2TY8uXAq_vxcno59VvFRVhfcThd9OyFl7Z7ckAG1fbpD5OD_WuGD_1CKYQcqW5PsfKyb2YVrBPKV_GtHLix2fFufH134RrSoBxUSBqcejnP_k7QsG-qrJjG8hDw7-_Us07L8gMbC-s72sd21LBgoQ9wawrrYHyd9Ml46eFF0UDMQXoo1cAp4xMYwhJhQ58FNrH0KuqoZedYlDjc6u9aTGRTZMGy6lvZlyl_sN9N9LDa1FQnKEh3cjNVPOmDflNLshX_Q4ctg0zEhk7TGkrhcvLszfda7-bDt79YdIrjGCEQTn9yjzHjWtL1PbgA8gbIAu0ZL6wjkYk6BH9d6esShFRHsp1anqAd2U9kwvltQnpUkOogPiHBz41__ZrMk2FLiX8F8Ztr_wAFlHmb1Bii3nyGTVj6bvEoXEenxy_UYCZIXtP9-3fR-xSAKQFbKVcZn0C4cY9W2BQOlVP0ZDYL7kDajI5yDXFQeT49WGyq3i_JuNYVb11aUEnP4ghWh2t2WKUsD1L4qS8uw6jQiHPt219V3wnstwoOa1e1y6I9zyT3FpGpg-SPm7LgyQ-JnyBwkwaU7YVkCeEAH8dJX6vgzECqUcNGXSFrpYyYBiPUh70rOs8CYA_FLcj3c5WvC0ChZU7DWbiz3vp4Rx9ApQm2ErI-78s631wINQalqItcEAVU6OLpvmESgrokmyhShciTTu2EM8OHvcQjxsO8A8NhjUw91uCNoU8MjF7gZnItrtuNJc1fKzMdYSeWvf6YrJbfPvR22naeTDlAqex7n-amuNDWDsf-ltV7E4t2-FQc5d8rBYMK9CTQtlVxzBc57OGa-R5cmOaTW8Zq9LzyH2hrEnONYzHWaqlx0m2W9Hiu7hVvQFqxlfsdlvTT4PmKsn0Y-ehiIpMN-3XEUruFzqCoGuU2nbdC08lGLnK5CKGIeYmcWH8InE_rIEf4lvxHpH3w0dn0tUJM094pdhNfniE-dZmPkQJvRjvEVLiQAYfakoacT1PsRfV2D4AxlC38q4Bol-SqpHpJQZ16Rf40cyucZXYzF_6mBFadHSrWqvk8VyhiLPaQVNGmuNoCue1_6qSAU4-d74ywXP_j_e5QWks4fbrXOBlh3ZqFnH_QEZVi-qNMESjgZ52GmjIqtY1IjPH0jZJNk5WXQRmEoMAwsPFT82QltTpopO8EswrsFLL_zuG2kD1tK3TVz_0GYiIGthiZp5ysIekpXgNi6Wd6Ura7nuD4MmwDzLfW8Qyn4ZzOdK94ylv91zEgUlyQu7qRyixvWjQ4Vuf0Jb6FaHb39bm_PL42belMXIerpqly8SOOxG48L9m43s85f51oAC7ZiSJj2Xp5-6oxjwCZ9LGm1yEkVOlg7MBH2WT1bzDDvdZgmXvEt0pbWPWP5SGH9Tmjr6kOQsgBFbj2fse2dvkr8DFTdvauUfT4bDpC5UmmWXNDaDKUmZuLSZPj1dWw6o1lB5_GF2XOJCOeIXZfc48a-jPO0p4PfqKGDoFK7bEDErnD1-0TvzkZu98w-z0Rd21g2qzjePx58e9RoSoX-xUS1OoUvJdrTKlk10YGh5YTRUE09GnLGsah9IWzlv4a6TE7o4QOK881Jks0nb3TVtt2Z8fuROndfIndlXBvHqAwu38va5mYn5UQdxHKTsW1KwsyiHEyygqN--v_6OSVMWAVD-IvZCSW6a69IIJKTtJTWwB-5_w1tDGl_vVlbx-ePlF2h_odqujQRvlAOGvqn_vFuuuUd9kD_MB7aUKegQa5IRs0SvOp43iBLpmYP6sht8qdHzLdluzFWx51VCYMzWp2cZjGhGyHN2UjHhkgFVoRPltwL0LLihC6heXdk4rmBdoezQe8SbzDvMOD0CCI_U0gad8GotaAUgsV95XQeBuUsd6OtpddsEFIgJRtcytH8qlZMt0eGTfe_Gcob6Ss8wzRNvwISINQ29AidZ4jYb2DUYQUF-4f4Eg0KT-Ohi_F4QcQ4OE6VnyDZkYGLrrDT6KaA44cFmmWeQjuglMMKGtZRszs77W0AiGDwelXHlNTUQrmgmJ9x5b9xdw20FcEkKP1JG7jCSomjcQqDbur1ZXf8djuKCFMk17hVXtpcsAdXLpKuly8wZwDeUXmy_vApouIllQoZplBMI1oZZFy8VS98W_vOow6mlXIgyORpYwDcWmTac5bSSI1riek-DXJiEfY4snpzkc-Wy3XLbsDTBr47Q86bjeHMVFgOPpoyYUwj_ueOzxH0fAJiZjww2tPyKIiKJz3-2GXWJG4DCneVE2rECGYTsQDqjxMLST6xZDDFajaB5uolj4khtZd8ufMW7rXlXd0Ec-2sfv51AZXzkSmJCwID4nE_rUpHNKaHPav4GE-P-yAqkpuutslnTm7ZirXm8UTc1GxZmOJ1GGT7gAgNLQGjnp9h6Cq8UT7lah9MZqy9Wy9ID5lHoWcgZYTcxH2fejlOxzEaT7wicbZRDW-bZXx_FN7PUr-b-XqqBjqCyu722Io2iKsvM7_MdQF1O75mujFpTFsnSHEUgYFqwsAfpktuUE_rVZuQ9vkfV0GBvAUvu-w6qPnl6B1QfWapmNzoyQBhlRdTklR5gZtxdo_6lCJtjveuoSa7JCh10mAyOy8mAy57lp74WvZaOXr4abn0xfF9K3bCQeIS8TrAP8U3J6Vmd-W7vaTAtRHc2t-VNxkpHDpgP6U7s4-keNGyuMB4_IXIoqOSfVRKZsFfH9Cdn11Umh2gSJl9V_n3TPoQhqk7w-H0bL7kUwHZ0sScOijLQDxrSH-ZGbDjR2P1qb-isbAgqnUXDo5EdfanA3f5rXYoAqocaOG0cwLCDVbUhRtwoEha35HIViPGa4ZzEh_5Wwr4FveqvsiazYUg-11qKNJYkmPjiD-jbo7AhHWxwGqdTMlSDn7nWveZ2s_35aqW8yHiY9QSzYUA-RwQF32VVTQOHwmAavMOrM1MMFR4isGV2yRZ06xoNn5QlZpgxFLXWY-yHHJ4lYK7gLvS_XN_QmQVZBhEjDRsoJndD8KmXS1OtzMrUpXlUx1QlFcdgfNzdtcPKlNlg1F7xTR9s8_pB2Dd1YH6sk_G2ekoGOmZFDXMOY0B20HPXjAXSbwPvMuCJ4VN7RbwS-nblKKNpLj5WSMYUbmCS6o_sH-YyWxcZ2RlaEWmAM1gwKsofgY0RKlT1h309khqzLSNyqN8h81mXN0VDR0ZlZAdbZyP3BUUrpX5PmEQLxAkjH5CQZbeprFqHWNaUTC4mRKKtSr40LXJljRwF6PIfLat3DE_JrS8gg0XF34CMsi-u_amMl5OyvpsHWbTYYPgoe9S0HEHT41Jz0ENUdkoITCd3TIwJCJ1Jx5-N24sngC9mUcwz_rdsDO136vrPcM4Le63VYkXWA2OacgTrQArPCUAsJyK5gEt-CJF9UZW7HvwBOkZYazbDg3Hw8-ws2T22qjcJzc-wKxGUl5ZRTKAYWotTnk9YZYeaHCrSugepzj4H5fOtV5jdoWrpPHiuBr39QysGd2ahkdxSi9p93mlbjyT5EZL53cI7EX8JXHoRq02SuriPjU6cUE4VPo5JPHmM1LZXc2m4n3LGTlbOyZctk5xaR9xwhe3YzIdgYByEWzvi_pVHwNHNB5PUJvPS2_32SqNI_PHpL1QmfxHoQq5-QDI0uM6XHXr6Fq5JFI04374cPjzRiWNSzDGs7SZ6NpDnMredX7oz7uyuL4D--AkEtc6pUUHnTStetpaHS66l6sNctC2M9ST6NFNMbMHFg8M9PH9bxmC7zy-Mk_iMlMw4W-ACiSFlptwnHkTBcaR0eZdSVtGenjF3qO9VkUuLVk1Y3GbCBxTB5PNHF0c3vBU5rtSG-HzbBA4RUyI7v7ca6QWtzfxOzdtAM8Va5gNCwqmaq8LKYa_gb8rgmcjaKAcJ4feMDGBaugBJ9bzb83q1SFk-4sExA8QEfQDHyc-SQG2AmGaalIJD_pC4QMPfkv03bcIcVmiyeS0zGeaVHzd76udmvxxsQus5k2FVxQEfXddB8T_4O5ZKB8ibL5yO0PL7SC-WuFqydXmLJ_i3szFtH3C5XAMgWW1OSa1EbdNmmvVOquo31DirlLeYXysQ6kIVe94_Es6_sK52hewhogagy0MH610sBzEUpfFTyVe_WNzABjq4Q0k8rEb1vPx3DknceDb-WJqW2-pNtEq0L8-ER_Xqrx8TE2JS7uJkG2GqdviT0KF0mKJt2dz9b8ZjOG6HTSUk3t0jwhA_FrBPwLJoLnwEE5Ynq-mwgsAxWVXUw_WZx--t3byxHefGmtIOQulbSjwJL7tfRXnY9vBHGMEV2EB8fYRDH4syiUNmHuedX91SAsy3q3joAlmNdND4PmsJ63dIRlyrBx_BLONlNuTO3uDk33hjloToQ9ssIDM_v1ylwWj60yvJHnbm28WXZiTuRtqjZMGQlkRLoxXVGFeTU0xXZaaCS_xI1LfOLh5khikjYuu_ZRyxTHwO_LdYx2Yut2nXtjzVPwgR87qJBHYugGpWi3BClAPv3Q__7Mz6onkNUL_HC7tmxF6AdnHz1fopaKIPCWyLht_PMf2JPp_fveBw0G1ybu25aK_yD_ieoJgMjomysdtmYiedFXHpqNPRXHtJ7XYaNQyMiQAvy-JBKdzw5uwSDgMqKwoIgRMFAV_Ybl0qjFLnv_C1E5Bfvf2jLUR_OFk8exU17--jk5LifIpRVd7so7AS-JNmzYf0-RQe8rWU7fEtKcN7zqCI_n6IE2J4COb6IoZk4V2nv9wyMGo4LqL9s_aoBfSbHe1WiZYG8B0OO1Jecjcr-98ZZEgnjOQApfdkLHzz0357Z3NqEOPpZejo17_urndLrvKMWKXOcVL7SDr_7VcYpq4am-jpNb7e4JdiqYsetYd6oWlFDwoCCW0gBUAkQ3NL466XvPcJLnmGMECdJuWwDYQ_N4ODIcwcHew03aetbKdK0TejnZ1ZEbgmOBYbuUyHPl0_USHw_gZH-YsA9gCWtAxgse4MkoiFMdyqWgvThROUHyDziyPG-bHjLpHwqgC6thSX0cLY1LEYQj3Xzab2z4qYo083roHpiB9QJnuuB65z4hRuyjqaQm7Ep81ms3Pqdq6czPhwOIKubovjbv-zCckJXS589v8UpxEJFUi4Q615dcaNUFcuTtkjpMeRYyNhuzHytjL5L7Zw4qsNdxhLtNnPT5KSi25eA-LQWGZOtg50fkWfPf-hyqTzpnUUr4dJsLUDvTDaNGloGP1H1HQHiBhODvKSirBJFZU2gs4v117KiJnF7y75_kVad33OcbAcK3a3svhxtfC9q62BiPAJVfb6s0_85Od88L7TBmlW4-iF0RDc6qeO-0ivmNCFukxxxze48TvsyqEU5DFWaSvwvRbRzOSt-xIqJFQH-fP1slcFooK4yHrNQdBDEF4LNKxJyOytMvELETE4M4Tn91I5C0zUs_JUftf85d6GJDvWWfVc19KsmAu0aLbd2jN6g7Z-TifhNyudDQ3NCiTPaouLNzcggzYoJaZNCtA_aOdlckk7X-wjBWGtw9TEtKNGfET5xktFdhA69_dV3GVfQ5I-7LkW4UaQ2gZ-h4eDAvX0YWZz6CISElmPw50IZPOz1P8-wS3a0AdusbJ2C2PMbyc0rogQdca9y3uPHJq4F57jKGLZeLZroaV5fyTmyfpG3Dxqc8Z8GUB6AX1q0Nme7pbRiLrForjfq5B8ESFsyR-xUMMMQ4kXhHGD2L9Ko-EdfqAUuQ49NsP0zsAiaU6EQ-Jvy1F9bzBsjc3Iv6gXh-l6hAej6CQxR3mefYA6zuUXEx-zz56G2DALVtxNY3UBoXOZ4MVsVBrKCz4ZG7FdbkdgYV9LWobPmQNkjyxgmb_yBj2ezF7WQkuBwFcKWZsR9wzkph0kvBFbaDjqWMg3E4cVTROiKO6RWP0rQI1Le2c-fRAKe4O2VVM9ZqBOHxA-9Lj01xIq-JaB4qjw-zfQTiyFvckKgMS7bY7Yct0DM0xjSNpwgm6OozGxogyOD28TSVg1C6MENnINRruX5utM3gIK5nFoQLE6cBWxuEzeMhJJSpETC73hEYm2EXTIzcfCQwCiO4lFooMCuk4w58Fd-AYwwtUUS9rGKLjC9xlklmegRct0akyEfhO_kHJ7cCp-P035Ld-EkLRBkxriFMN8j1b9os8pGFUI0oTajEdebghpYKP1fq77P7-5aX8pLC7D3sqozhHhOC4Ih8qoLOr_YEuKoepkvzEETO_hY3k4ZCZcGo_5O9czD98RLxf5wibfqKvom2LUIrv_HjPyDI840txzkewxSqaxIB9J9iqRddZwLNYuL2RLDqy8aTMgOpcgYYpQVgijU1qPUG59MoJG9STJVxRqi6XEg68Imh5kP_NApI3Il7p6zRzJWAbML73UrurbQENN1buc2QALcL_upeAXyWQKnD9rHsCxTZbszV1QQwQKq4RojO7OZQwLjCUjpRnoOG1OjDiBvosBmuv2XHlkB6rQHElbmSsKAoTXvvu-MQpUp74TAoqb0G3JqfZgDoYMzizrB_sxZJbJtAWy-L0Yy5Kp5u6o4H1eB1KfH4IhQVEnMlDElY7jgmyjgSnwN9bEndR7NcZBaUklQpW00mbpY1_oQjXn8dfw5PIs-zEwCh2mkCvQCn5AEYYhuKz9hjlN_RcoDpuWkNDoheypO2pk_9ign-wdzd7LjcgEfjUrRqSW3REFI63CEZXEHPi38SrYotxyhRdXkUiIt04Zt12G4hTqAx2NJ4m0Z_0SY9VulyzGlsGb7hPQtw2OXIR40zXXMcWhIfiScq8QPDRTxpw705OwNA_UAWFVPHgli8OJ3IYT15GqEP1MS9UhAfFzhHwr1lCURpra09IKeZwNu8PVv6uTOCxGnTK1EL4AUFBqNUJt0Kg6-uD1LxrHxNDGjU_RwqLwIOrzrr9_QUveV4XTS7-4C5Wl7aazREzopWe78tMQqJ9Z2SQZoAEKQirmnhtVAcsnv8EmHJUc7vOul1xzKC56ysyeM066v9VmY8lE-pMUmxuybfDGJUMCKENCQ1oeEazIA11j0vzX4cAar4A--qpBCJXYt6LkA9ycMVurz55KzsyxJimuCH1-SW5EahcZ-VlEZS6euYrAzUH8kPJRlyRFX22RVqidaLHLEtqEvsq7d4XJxCa72PcXvzFnU3TVwbVNiotjbSAPeoWF75DHGi9CnN6hy-yTMhVek2hXpne-Uxc5mK_zdYv29WiRX0ZQR8bizWeuHIumFsQTDuM0dHcr7Tf6-o1pBqYTv_LepCBErCARVCIRBNIMloalkMirDnfSMhfwOrR27tAzgjeMS81dBdJuuALUTlWrgzcXxcmK_4IL8ts0GaZ8A15jAWyhX1K0zJkB73D_2NiN0OsTY-cvJeotu1cD2mQ7gi45tCsWJ5fzskBxVOHzkaxRa6GUxVgPfvZX6pSjjx0vtbHyYs9_v1EqXLvCM_Kyf942dIsH09Zyg2vbDwDdGYbgq2Mo9wH30szj215fsZevU5vGhHnLqbBkNG9l9A_ufNUdLR2afWTC1WZtsjONua2A06kHN1OhSDr_DTia0dniKd9-JPsDHBwtCmI5ziRSszBCqhE5uY5c_yUmJsxRvPr6MoL1wGe3k8tXjztZFirRSuEnCFwtxWbaajP8ChW4P2uliG1WSNdjiLw-CP6Z2pBPXOkRyZPEi8rl_sg9aCFRLRB0JGT-E1_vYTnZlBUGgpQoQ2nF8_92zAuNPJPEsw4PF1l0cH9oQXUfSenw0kWsmxEW7X0xfM_WEKJuJPcVYfhzvAF0qkNjEdAexy9z0E313boRwZfC3D-iFjkjx1g_A-PKbcMddQSIn2XKc35FFKq_M2X87EpmLoJETajHYmbxV8AKL-ZvfU7fbuTcafEr61UOQB_6Xk6m77xzfDCifbZtpJFtrt_hL20HZjrKgtRbRjPl7StQONMmI0IzcEuFN_WkXq9mfIC8hUzPt1oAVu4BuU4Xuk2uoXz_ePdx_je_HC-nZPpFUz1_M5PVOOy5hFV3yjNquiOQ9RRgqiZ920K8SLJ0Ri-uzqAgJG2bBEUSgHedd4dGkfbhKUZrpDlpIEPKOW4Ltz6PCq4T2yll4A-RVpP1KA3peXhexZIJCwwJSmc1g_NzR9jrCFY61SWpvO1V_tTfKfLXMCgGutwsP4RBgi6fPp2H97OU5rFCA3NWTGy43WmWVysdYue9YWmgwde0wvWfOcgVrJZ2dxYYJZfWBeaiCCuORmrWplKytlcI9cCO_qEHvCyUh7aXnwB4Ffo3Tww2vrJTvZqhKRCnOOVdfTn01wXa5G4JgzxEwDxI0xjdMVedNlTHlU7z3Ix_KVVplvF01Md0Y6U54lbagJSCL3xjbMB4gFL92vDSgDXM816F1NTXJAEsQkJ5WO2bZRt5wNRSkaypMYSgw4_1rtCePLhsUuz4PUz48TmjkxqQs99_DxTeol1-4r42o_3-f74zZQINooNSZBrgaLLIjEsXKYjx7_tPjSByI1uwIWz1lHNP--aNo8c0TH29Mlb2AE5oWMeVTr5WDmE6djkCsna49FOFmXu3y3Vva1sFQEVPlbE1c_zL5ECllnrRbw3PEeIsNwpzbf2R53RQ1DSuegDTEqnENGH1PZ_fM_56oBSFpaV5TGMUgpwyClqIZjsXRx4IqFwYqN2sFS3u2fOAzGE58cu0puL3_IF_UTCWErvW3BM6PTl1icF4bF_DYwDeZMja3Y4eqHK-x5pud3hCVeDpg6rs-2ANrRx354DNSsmweHQgEMz_GUy36bfem7ABcr-YF2okVhT4SLXYtpN0hXYWm108VXia2CXvo6QZp0H2-JusAWjSSPJGo_QnpsYa3Taii_IBgy_1PpFuvzDeZ9QKTE-acckQEzMq4jr2-x09-6-JqFI61AU1XctnomxXgK_KOVbtHXIJQjFS8oEoddqSABi7IhxkwctNHsenm4zdic9UQcxa9UgXF4GtYmktrjKQt7WKdKUbxYn_QZzaC3NY3bubv3pF5UB6FDEOpbhehIjMaQa65zp38rFBJ9q3R12ZhnZHrReBqH0WqmKNLlmL1IU9sXaMXTFmntj2B0VJzt-7BIwRVkoX27ny5OharThw-qCGxM3LWPOhkkTIxhyRRCDwemwb3D4QVUX9XYkmo_FFUWxMUPyZENv89Q6600QvJvOMSg_5XwXbHJWQO59nsuouBS1Le9JLJ3-J3rPAI4b6HEyI3Rh8PkLrePD12VOtKIWTXxvct3qF4ZVf7sRrEqGkymmSTOlxxwD1oQuoXIa-ZlCRnlf86JbzEIef2de3y6fF9EpatQZYZgwtH5eu-b1MP2BfoAxKRdnsxaW2n-7uOlKYalB2r640oka0hyRK4rk1mUFq7x3sdOuXuVqh40fIaieL41CLZOEIi8SLk6ZViDZKPU4hm_67RzWSnKszJH0iGtwutbVf3gRhz_gyunhwHUMXJZJkBtO3CqUBS9OWx-HTFgtwpLpGkIBn7_SC9XmKMSge45mnhMy12YPdrQ_2RX7ei9UR4IU1b9uvfP-krLDbwvrSd3nQb2DDc6_PJ6mfGvUy_vab_SD9o0jWZbNFdmqP4gVAn_MSW-LwefvcrWS5uTNInmkcl22pzfLmhjpvzrUo1uI4uNTyAdjTPVrU7-EOLPYi7QxHBfzIE-NyE4qU_8c3ZEu3LSwHTAuQPwqbqgbWu-sDwzgiRtqOLtPCbJxHpkbLFabZ1kBCgfbDEmijXd2ZjHXr9_PKNAyqijYm_BvS1fUpfkp7DbqZjVsz0I4Aye88DSNE3lrZY_WPNXRr4Bw_DVjav3hKFebib4RkwNWSpwe74NNFVpniVTmmzNsSk2t8N12wIlQbK3jwa6DvrWU03cNvqKdNJYXpNgJf8nAGmkLFUUPekoWlJKLrm8FhwTbGPCoTvXz0KVR6jA3ZaM7iibKYJeJyuGgrG5G8uIyKd8_vxl_0-ixJ_Cqe0swITZOd0QWWkkCpVeuZh8TE5g5QTCqCjRC53xhP5fSRiokmYhlUyGMe7Oj0TCZnep9C4wGAKWyDejTPg_4EF77K4I2XunjKntumh9E6HbryOv3YVcbjmhX8GszPzrkzmRataaCrONmw4noQUjCASI3q56kbiFdx1j0yZL9mfQiiVNF8zo6h2EVLUeliiYnJX5C3S1cY-TVpWGlZxUt0ZxjJfXpOfciRgEfVEPPuTZENGp9cEEEaBdmkTEbBGfwzm_Z1INB9qdJXkKlTnxyDAKi6hCDF8-FaRG2h75aNKWoBqiqTMQCld9AmDLlgx1ajYl78ksnPdAaprQ_fgLbZVXE7H2qFW3FYMsTLOZI6UD07YqeByYBtzdvFZNsxGNw1t9gZ8wnw4qDHWQteWCxK-YcQpm2AXGk64FIbRHeLNblAHCfCsVrPPgf79DP-0tu2dv6Pe2DNifndz3jkaFAzVK0LCS9WMBBG_XwKzQ3Om7HqjW-5MUhqFRvy7j1aD-szAvhUFTG8qqA-VpQVocS75tWltzyOZ9JKaxfDrliTboYGxQ3dwBslkvePWyOSNXm_OzU2aTcbJBAIBocIhHXGRQLQzbjKnYl0rloEJeSSw_fkL-JtIhaCm_7tA5kZYd-1xYAPfly0PjZu6Wwly6jAPRfQt7y8b5L_mG0xovQXwW-0PCtXT_HP3QlvmGf5-INCc2FQNtTpYWPgdK2kIsHK3DPv0TvwCFNE_9fXpkPq-0VvvVdOIJYFhg_cd3zhWzdP11ofDYSVyvGVDWBv-JqIO2esZD5g8H2ixVkNCE1rZl1akOIz7Iu-_rAFzLFGQLVJNwDIrJXV2Si8WRK6zRLKAZFYYOUcB7GFypJo5ByJMZKIBPcfkrU8oK520nn_DgUlMhjx6vEyBVJPgsjlar7eTWfc8d04CvHX_GfQpl-AGnfTvmaWSW_q-g-kvTN7hJrQanbxAlzMU71Eezg4QVZ19VPqGB3Dx3I_oHB2gfRs07FLGW5E4TJ4CiY5rb806EjVnwM2NWeDMbdKMQYGZsBEvVwjoNE9nVWhGoTnx0k5KH-L6yU1zHf2zCv_1h-a6zKPHCJol4mYmtrAl0Kb69uk8G5ax7-TURt_0SWZdyxGTEUcKx0GFGd872ydT9sjh6bpQkdO0SZUUNC7j-z390sVvmM8DPOXPpZXDPN41gdzhdDpvVu3IZ9ZhqIgI4B6YwoO9bt4VBdeR7aXGOGD68dnrfGG-JHcmxM0f3uwAuf9VVvvZg9xRAxR3L1Gbz3h_0UhMZNIGgo_12LXb0itJzQT4RkcCrD5h6pAq23_1d91lPIXeiIr710rGjebQ0fH8Lgyaqb4tKqCQxApx6YKeZktAOQ6Qkhlxsuq7OBVAgGGwnaMZ0mL1NOCpEW9naoVhq2e3kjiIfkz0aNbO5vrUFtVrWZm24mPNdcO-ErDoirFHiOJ_TZ2exmi0Ce1NXPXIFY9_1knOuMGvFPOTI1GVUP3Mu0y9CYlvf68rlYu-ktpLTUvHi0ec0W1T50Rc6-e7A_ZU0U3dwHyFQz5ictEtV4jg0YhCRVvKZMA6tKYpPf8nckUrtnv8HnjcaRK-R6ICLC9CCVD90zt2oMDLZ1tKohgepjwCU7kKCKPl7OJM-o42xBHei1PRaXK4rA7TVtVQhqGflu4cKKQDTrsWy4UkYukdZQon-YGWamAzJHmAVscCrvSOPpa5vb7EOK9lDKsNBsFEHWget1FUkEAe93sHXUcO1EcdfQOYYb1MrLq4-Wg0o0LZPxkIZnRchAW5GZaTPWJ80oLMxOCJQ_lZLHI_sYPiFgJnWb6esrvXKufUrJSgBK-XvmKQbLbkqRVQE07RtQj8aG4oguksskG6De0egc0kM_sl5nO5EoaWjlnfkEkwTjxpvsxp_lMKE3Z2dQjaaW_v15kzTYAS2AOOhWzMlP6s3PEh9W57K8txoYdUwjhwkcXtsA2DkWEDitVTIvFHX2DvKIVAQ==
@@ -24,7 +24,7 @@ def get_user() -> str:
24
24
  return output[0].strip().decode("utf-8")
25
25
 
26
26
 
27
- def get_layout_pair(user: str | None, **kwargs):
27
+ def get_layout_pair(user: str | None, console_height=None, **kwargs):
28
28
 
29
29
  if user == "all":
30
30
  user = None
@@ -33,17 +33,14 @@ def get_layout_pair(user: str | None, **kwargs):
33
33
 
34
34
  df = combined_df(user=user, **kwargs)
35
35
 
36
- # if df is None:
37
- # running = Panel(Text("[bold]No active jobs"), expand=False)
38
- # else:
39
-
40
36
  n_rows = len(df)
41
37
  n_running = len(df[df["job_state"] == "RUNNING"])
42
38
  n_pending = len(df[df["job_state"] == "PENDING"])
43
39
 
44
40
  hide_pending = False
45
41
 
46
- console_height = console.size.height
42
+ if console_height is None:
43
+ console_height = console.size.height
47
44
 
48
45
  history_df = df[~df["job_state"].isin(["RUNNING", "PENDING"])]
49
46
 
@@ -64,7 +61,6 @@ def get_layout_pair(user: str | None, **kwargs):
64
61
 
65
62
  # hide pending?
66
63
  elif n_rows - n_pending < max_rows:
67
- # running_df = running_df[running_df["job_state"]=="RUNNING"]
68
64
  running_limit = None
69
65
  history_limit = None
70
66
  hide_pending = n_pending
@@ -207,11 +203,15 @@ def get_squeue(
207
203
  if not len(df):
208
204
  return DataFrame(columns=columns)
209
205
 
206
+ if "exclusive" not in df.columns:
207
+ columns = [c for c in columns if c != "exclusive"]
208
+
210
209
  try:
211
210
  df = df[columns]
212
211
  except KeyError:
213
212
  for key in columns:
214
213
  if key not in df.columns:
214
+ print(df.iloc[0].to_dict())
215
215
  raise KeyError(key)
216
216
 
217
217
  extract_inner(df, "cpus", "number")
@@ -228,7 +228,9 @@ def get_squeue(
228
228
  extract_time_limit(df, "time_limit")
229
229
 
230
230
  extract_list(df, "job_state")
231
- extract_list(df, "exclusive")
231
+
232
+ if "exclusive" in columns:
233
+ extract_list(df, "exclusive")
232
234
  # extract_json(df, "exclusive")
233
235
 
234
236
  return df
@@ -397,15 +399,14 @@ def combined_df(**kwargs) -> "DataFrame":
397
399
  def add_run_time(df):
398
400
 
399
401
  def inner(row):
400
- # print(f'{row.end_time=} {type(row.end_time)=}')
401
402
 
402
403
  if row.job_state == "PENDING":
403
404
  return ""
404
405
 
405
- if isnull(row.end_time):
406
- return human_timedelta(datetime.datetime.now() - row.start_time)
407
- else:
408
- return human_timedelta(row.end_time - row.start_time)
406
+ if "end_time" not in row or isnull(row.end_time):
407
+ row.end_time = datetime.datetime.now()
408
+
409
+ return human_timedelta(row.end_time - row.start_time)
409
410
 
410
411
  return df.apply(inner, axis=1)
411
412
 
@@ -1,8 +1,12 @@
1
+ from rich.layout import Layout
1
2
  from rich.table import Table
2
3
  from .tools import human_datetime
3
4
  from pandas import isnull
4
5
  from .console import console
5
6
  import subprocess
7
+ from pandas import isna
8
+ from os import environ
9
+ from pathlib import Path
6
10
 
7
11
  ### TABLES
8
12
 
@@ -20,19 +24,21 @@ def running_job_table(
20
24
  pending_df = df[df["job_state"] == "PENDING"]
21
25
  df = df[df["job_state"] == "RUNNING"]
22
26
 
27
+ count = len(df)
28
+
23
29
  if limit:
24
30
  df = df[-limit:]
25
31
 
26
32
  from .slurm import METADATA
27
33
 
28
34
  if not user:
29
- title = f"[bold]all running jobs on {METADATA['cluster_name']}"
35
+ title = f"[bold]{count} running jobs on {METADATA['cluster_name']}"
30
36
  else:
31
- title = f"[bold]{user}'s running jobs on {METADATA['cluster_name']}"
37
+ title = f"[bold]{user}'s {count} running jobs on {METADATA['cluster_name']}"
32
38
 
33
39
  table = Table(title=title, box=None, header_style="")
34
40
 
35
- columns = RUNNING_JOB_COLUMNS[long]
41
+ columns = RUNNING_JOB_COLUMNS(long, user)
36
42
 
37
43
  for col in columns:
38
44
  col_data = COLUMNS[col]
@@ -77,6 +83,8 @@ def history_job_table(
77
83
  **kwargs,
78
84
  ):
79
85
 
86
+ count = len(df)
87
+
80
88
  if limit:
81
89
  df = df[-limit:]
82
90
 
@@ -91,13 +99,15 @@ def history_job_table(
91
99
  hist_string = f"last {hist} {hist_unit}"
92
100
 
93
101
  if not user:
94
- title = f"[bold]all previous jobs on {METADATA['cluster_name']} ({hist_string})"
102
+ title = (
103
+ f"[bold]{count} previous jobs on {METADATA['cluster_name']} ({hist_string})"
104
+ )
95
105
  else:
96
- title = f"[bold]{user}'s previous jobs on {METADATA['cluster_name']} ({hist_string})"
106
+ title = f"[bold]{user}'s {count} previous jobs on {METADATA['cluster_name']} ({hist_string})"
97
107
 
98
108
  table = Table(title=title, box=None, header_style="")
99
109
 
100
- columns = HISTORY_JOB_COLUMNS[long]
110
+ columns = HISTORY_JOB_COLUMNS(long, user)
101
111
 
102
112
  for col in columns:
103
113
  col_data = COLUMNS[col]
@@ -201,18 +211,48 @@ def job_table(row, job: int, long: bool = True, **kwargs):
201
211
  return table
202
212
 
203
213
 
204
- def log_table(stdout, stderr, limit):
214
+ def log_table(job_id, stdout, stderr, limit):
205
215
 
206
216
  from rich.text import Text
207
217
 
208
- dual = stdout != stderr
218
+ if isna(stdout) or isna(stderr):
219
+ log_dir = Path(environ.get("LOGS", "."))
220
+ matches = list(log_dir.glob(f"*{job_id}*.*"))
221
+
222
+ if len(matches) == 1:
223
+ stdout = matches[0]
224
+ stderr = matches[0]
225
+ elif len(matches) == 2:
226
+ raise NotImplementedError("separate log files not supported")
227
+
228
+ if stdout != stderr:
229
+
230
+ # layout = Layout()
231
+
232
+ # layout.split_column(
233
+ # Layout(name="upper"),
234
+ # Layout(name="lower")
235
+ # )
236
+
237
+ # table = Table(title="stdout", box=None, header_style="")
238
+
239
+ # table.add_column("[bold underline]#", style="bold")
240
+ # table.add_column("[bold underline]Log", no_wrap=True)
241
+
242
+ # lines = tail(stdout, limit)
243
+
244
+ # for i, line in enumerate(lines):
245
+ # line = line.decode("utf-8")
246
+
247
+ # line = line.split("\r")[-1]
248
+
249
+ # table.add_row(str(i), Text.from_ansi(line))
209
250
 
210
- if dual:
211
251
  raise NotImplementedError("separate log files not yet supported")
212
252
 
213
253
  else:
214
254
 
215
- table = Table(title="stdout", box=None, header_style="")
255
+ table = Table(title=str(stdout), box=None, header_style="")
216
256
 
217
257
  table.add_column("[bold underline]#", style="bold")
218
258
  table.add_column("[bold underline]Log", no_wrap=True)
@@ -245,6 +285,10 @@ def color_by_state(state):
245
285
  elif state == "IDLE":
246
286
  return "[bold bright_green]Idle"
247
287
  elif state == "PENDING":
288
+ return "[bright_yellow]Preempted"
289
+ elif state == "NODE_FAIL":
290
+ return "[bold bright_red]Node Failure"
291
+ elif state == "PREEMPTED":
248
292
  return "[bright_yellow]Pending"
249
293
  elif state == "MIXED":
250
294
  return "[bright_yellow]Mixed"
@@ -254,6 +298,8 @@ def color_by_state(state):
254
298
  return "[orange3]Cancelled"
255
299
  elif state == "FAILED":
256
300
  return "[bold bright_red]Failed"
301
+ elif state == "OUT_OF_MEMORY":
302
+ return "[bold bright_red]Out Of Memory"
257
303
  elif state == "COMPLETED":
258
304
  return "[bold bright_green]Completed"
259
305
  else:
@@ -345,55 +391,118 @@ JOB_COLUMNS = {
345
391
  ],
346
392
  }
347
393
 
348
- RUNNING_JOB_COLUMNS = {
349
- True: [
350
- "job_id",
351
- "name",
352
- "node_count",
353
- "cpus",
354
- "submit_time",
355
- "start_time",
356
- "run_time",
357
- "time_limit",
358
- "partition",
359
- "nodes",
360
- "job_state",
361
- ],
362
- False: [
363
- "job_id",
364
- "name",
365
- "node_count",
366
- "cpus",
367
- "start_time",
368
- "run_time",
369
- "time_limit",
370
- "job_state",
371
- ],
372
- }
373
394
 
374
- HISTORY_JOB_COLUMNS = {
375
- True: [
376
- "job_id",
377
- "name",
378
- # "node_count",
379
- # "cpus",
380
- "submit_time",
381
- "start_time",
382
- "run_time",
383
- "partition",
384
- "nodes",
385
- "job_state",
386
- ],
387
- False: [
388
- "job_id",
389
- "name",
390
- # "node_count",
391
- # "cpus",
392
- "start_time",
393
- "run_time",
394
- "job_state",
395
- ],
396
- }
395
+ def RUNNING_JOB_COLUMNS(long, user):
396
+ if user:
397
+ if long:
398
+ return [
399
+ "job_id",
400
+ "name",
401
+ "node_count",
402
+ "cpus",
403
+ "submit_time",
404
+ "start_time",
405
+ "run_time",
406
+ "time_limit",
407
+ "partition",
408
+ "nodes",
409
+ "job_state",
410
+ ]
411
+
412
+ else:
413
+ return [
414
+ "job_id",
415
+ "name",
416
+ "node_count",
417
+ "cpus",
418
+ "start_time",
419
+ "run_time",
420
+ "time_limit",
421
+ "job_state",
422
+ ]
423
+ else:
424
+ if long:
425
+ return [
426
+ "job_id",
427
+ "user_name",
428
+ "name",
429
+ "node_count",
430
+ "cpus",
431
+ "submit_time",
432
+ "start_time",
433
+ "run_time",
434
+ "time_limit",
435
+ "partition",
436
+ "nodes",
437
+ "job_state",
438
+ ]
439
+
440
+ else:
441
+ return [
442
+ "job_id",
443
+ "user_name",
444
+ "name",
445
+ "node_count",
446
+ "cpus",
447
+ "start_time",
448
+ "run_time",
449
+ "time_limit",
450
+ "job_state",
451
+ ]
452
+
453
+
454
+ def HISTORY_JOB_COLUMNS(long, user):
455
+ if user:
456
+ if long:
457
+ return [
458
+ "job_id",
459
+ "name",
460
+ # "node_count",
461
+ # "cpus",
462
+ "submit_time",
463
+ "start_time",
464
+ "run_time",
465
+ "partition",
466
+ "nodes",
467
+ "job_state",
468
+ ]
469
+ else:
470
+ return [
471
+ "job_id",
472
+ "name",
473
+ # "node_count",
474
+ # "cpus",
475
+ "start_time",
476
+ "run_time",
477
+ "job_state",
478
+ ]
479
+ else:
480
+ if long:
481
+ return [
482
+ "job_id",
483
+ "user_name",
484
+ "name",
485
+ # "node_count",
486
+ # "cpus",
487
+ "submit_time",
488
+ "start_time",
489
+ "run_time",
490
+ "partition",
491
+ "nodes",
492
+ "job_state",
493
+ ]
494
+ else:
495
+ return [
496
+ "job_id",
497
+ "user_name",
498
+ "name",
499
+ # "node_count",
500
+ # "cpus",
501
+ "start_time",
502
+ "run_time",
503
+ "job_state",
504
+ ]
505
+
397
506
 
398
507
  NODE_COLUMNS = {
399
508
  True: [
@@ -543,7 +652,10 @@ COLUMNS = {
543
652
  "no_wrap": True,
544
653
  },
545
654
  "threads_per_core": {"header": "#Threads/core", "style": "magenta"},
546
- "user_name": {"header": "User Group", "style": "green_yellow"},
655
+ "user_name": {
656
+ "header": "[bold underline green_yellow]User",
657
+ "style": "green_yellow",
658
+ },
547
659
  }
548
660
 
549
661
  FORMATTERS = {
@@ -1,7 +1,7 @@
1
1
  import datetime
2
2
 
3
3
 
4
- def curry(function, payload):
4
+ def curry(function, payload, *args, **kwargs):
5
5
  def f(*args, **kwargs):
6
6
  return function(payload, *args, **kwargs)
7
7
 
@@ -66,3 +66,22 @@ def human_datetime(dt):
66
66
  bits = (str(b) for b in bits)
67
67
 
68
68
  return " ".join(bits)
69
+
70
+
71
+ def human_timedelta_to_seconds(s):
72
+
73
+ values = s.split()
74
+
75
+ seconds = 0
76
+
77
+ for value in values:
78
+ if value.endswith("s"):
79
+ seconds += int(value[:-1])
80
+ elif value.endswith("m"):
81
+ seconds += 60 * int(value[:-1])
82
+ elif value.endswith("h"):
83
+ seconds += 60 * 60 * int(value[:-1])
84
+ elif value.endswith("d"):
85
+ seconds += 24 * 60 * 60 * int(value[:-1])
86
+
87
+ return seconds
@@ -0,0 +1,112 @@
1
+ from random import sample
2
+ from .console import console
3
+ from rich.table import Table
4
+ from rich.text import Text
5
+ from rich.panel import Panel
6
+ from rich.layout import Layout
7
+ from rich.padding import Padding
8
+ from typer import Typer
9
+
10
+ import textwrap
11
+
12
+ app = Typer()
13
+
14
+
15
+ def get_quotes():
16
+ import json
17
+ from pathlib import Path
18
+
19
+ root = Path(__file__).parent
20
+ if not (root / "quotes_decrypted.json").exists():
21
+ from cryptography.fernet import Fernet
22
+
23
+ key = console.input("[bold]Enter passkey:\n")
24
+
25
+ with open(root / "quotes_encrypted.py", "rb") as f:
26
+ encrypted = f.read()
27
+
28
+ fernet = Fernet(key)
29
+ decrypted = fernet.decrypt(encrypted)
30
+
31
+ with open(root / "quotes_decrypted.json", "wt") as f:
32
+ encrypted = f.write(decrypted.decode())
33
+
34
+ payload = json.loads(decrypted.decode())
35
+ return payload
36
+
37
+ else:
38
+ return json.load(open(root / "quotes_decrypted.json", "rt"))
39
+
40
+
41
+ @app.callback(invoke_without_command=True)
42
+ def print_random_quote():
43
+
44
+ width = console.size.width
45
+
46
+ if width < 12 + PADDING * 2:
47
+ return
48
+
49
+ quote = sample(QUOTES, 1)[0]
50
+ index = QUOTES.index(quote)
51
+
52
+ lines = [""]
53
+
54
+ paragraphs = [""]
55
+ for c in quote:
56
+ if c == "\n":
57
+ paragraphs.append("")
58
+ else:
59
+ paragraphs[-1] += c
60
+
61
+ for i, paragraph in enumerate(paragraphs):
62
+
63
+ if len(paragraph) == 0:
64
+ lines.append("")
65
+ continue
66
+
67
+ if i == 0:
68
+ first_padding = " " * PADDING
69
+ else:
70
+ first_padding = " " * (PADDING + 1)
71
+
72
+ subsequent_padding = " " * (PADDING + 1)
73
+
74
+ if i == 0:
75
+ paragraph = f'"{paragraph}'
76
+
77
+ if i + 1 == len(paragraphs):
78
+ paragraph = f'{paragraph}"'
79
+
80
+ lines += textwrap.wrap(
81
+ paragraph,
82
+ width=width - PADDING,
83
+ replace_whitespace=False,
84
+ initial_indent=first_padding,
85
+ subsequent_indent=subsequent_padding,
86
+ )
87
+
88
+ lines.append("")
89
+
90
+ longest = max(len(l) for l in lines)
91
+ signoff = f"-CONFUCIUS #{index+1}"
92
+ lines.append(" " * (longest - len(signoff)) + f"[italic]{signoff}[reset]")
93
+
94
+ lines.append("")
95
+
96
+ for line in lines:
97
+ console.print(line, highlight=False)
98
+
99
+
100
+ PADDING = 6
101
+
102
+
103
+ def main():
104
+ global QUOTES
105
+ QUOTES = get_quotes()
106
+ app()
107
+
108
+
109
+ if __name__ == "__main__":
110
+ global QUOTES
111
+ QUOTES = get_quotes()
112
+ app()
@@ -1,341 +0,0 @@
1
- from random import sample
2
- from .console import console
3
- from rich.table import Table
4
- from rich.text import Text
5
- from rich.panel import Panel
6
- from rich.layout import Layout
7
- from rich.padding import Padding
8
- from typer import Typer
9
-
10
- import textwrap
11
-
12
- app = Typer()
13
-
14
-
15
- @app.callback(invoke_without_command=True)
16
- def print_random_quote():
17
-
18
- width = console.size.width
19
-
20
- if width < 12 + PADDING * 2:
21
- return
22
-
23
- quote = sample(QUOTES, 1)[0]
24
- index = QUOTES.index(quote)
25
-
26
- lines = [""]
27
-
28
- paragraphs = [""]
29
- for c in quote:
30
- if c == "\n":
31
- paragraphs.append("")
32
- else:
33
- paragraphs[-1] += c
34
-
35
- for i, paragraph in enumerate(paragraphs):
36
-
37
- if len(paragraph) == 0:
38
- lines.append("")
39
- continue
40
-
41
- if i == 0:
42
- first_padding = " " * PADDING
43
- else:
44
- first_padding = " " * (PADDING + 1)
45
-
46
- subsequent_padding = " " * (PADDING + 1)
47
-
48
- if i == 0:
49
- paragraph = f'"{paragraph}'
50
-
51
- if i + 1 == len(paragraphs):
52
- paragraph = f'{paragraph}"'
53
-
54
- lines += textwrap.wrap(
55
- paragraph,
56
- width=width - PADDING,
57
- replace_whitespace=False,
58
- initial_indent=first_padding,
59
- subsequent_indent=subsequent_padding,
60
- )
61
-
62
- lines.append("")
63
-
64
- longest = max(len(l) for l in lines)
65
- signoff = f"-CONFUCIUS #{index+1}"
66
- lines.append(" " * (longest - len(signoff)) + f"[italic]{signoff}[reset]")
67
-
68
- lines.append("")
69
-
70
- for line in lines:
71
- console.print(line, highlight=False)
72
-
73
-
74
- PADDING = 6
75
-
76
- QUOTES = [
77
- "DO IT TOMORROW",
78
- "DO DFT TOMORROW",
79
- "HOW 'BOUT NAH",
80
- "STYLE OVER SUBSTANCE",
81
- "I'M NOT CHINESE",
82
- "TO DRIVE A TANK SIMPLY USE DRUIDRY TO TRANSFORM INTO A TANK DRIVER",
83
- "IF THE GAUSSIAN FAILS TWICE CONSECUTIVELY, GIVE UP",
84
- "THE FEWER HOURS YOU WORK ON A FIXED SALARY THE HIGHER YOUR HOURLY PAY", # MW
85
- "COMP. CHEM IS TEMPORARY. CONFUCIUS IS FOREVER",
86
- "BETTER TO ASK FOR FORGIVENESS THAN PERMISSION",
87
- "DO YOU THINK I WANT TO BE HERE?",
88
- "READ THE FUCKING MANUAL",
89
- "NEVER GIVE A COW FREE MILK",
90
- "FOR BETTER OR FOR WORSE I'LL ALWAYS HAVE ANOTHER COFFEE", # MW
91
- "I'M A BIG PROPONENT FOR HAVING FUN", # MW
92
- "I WOULDN'T BET MY LIFE ON IT",
93
- "DEATH DOES NOT PROTECT YOU FROM CRITICISM",
94
- "SOMETIMES YOU HAVE TO SPEND MONEY TO MAKE MONEY.\n\nSOMETIMES YOU HAVE TO SPEND MONEY TO LOSE MONEY.\n\nONE THING'S FOR SURE, YOU HAVE TO SPEND MONEY.", # Rust Valley Restorers
95
- "PREMATURE JOB FAILURE IS NOTHING TO BE ASHAMED OF",
96
- "LOUIE TOLD ME WHAT TO DO AND I DID MY BEST TO FUCK IT UP",
97
- "IT IS IMPOSSIBLE TO REVERSE THE CREATION OF A CAKE",
98
- "CAKE CAN BE CREATED AND DESTROYED",
99
- "I CAN HEAR BUT I CAN'T LISTEN",
100
- "I'M BETTER THAN THE MANUAL",
101
- "BEFORE TRYING, CONSULT ME",
102
- "CODE FIRST, THINK LATER",
103
- "WHEN IN DOUBT, CROSS PRODUCT",
104
- "CHEMIS[bold]DO[/bold] OR CHEMIS[bold]DON'T[/bold] THERE IS NO CHEMIS[bold]TRY[/bold]",
105
- "I HATE IT HERE",
106
- "I DIDN'T SIGN UP FOR THIS",
107
- "I DON'T UNDERSTAND WHY PEOPLE WOULD CHOOSE TO BE A BIOLOGIST",
108
- "I HATE PAST ME",
109
- "SOUNDS LIKE A FUTURE ME PROBLEM",
110
- "I STRUGGLE TO FIND PEACE",
111
- "I HEREBY CLASSIFY THIS AS A ROYAL BALL ACHE",
112
- "I'M ALL OUT OF PASSION",
113
- "WHAT IS A DECIMAL PLACE BETWEEN FRIENDS?",
114
- "[bold italic]OF COURSE",
115
- "[dodger_blue2 bold] 🇬🇷 OF COURSE [/bold /dodger_blue2]",
116
- "ΩΦ KΩΥΡΣΕ",
117
- "FRENCH? SO YOU'RE EASY TO SATISFY",
118
- "YOU DON'T WANT TO FINISH TOO EARLY",
119
- "BOLD AND UNPROVEN",
120
- "WHERE IS MY GIRAFFE??", # CV
121
- "THIS IS A HIPPOPOTAMUS",
122
- "MONOGAMOUS ENTANGLEMENT",
123
- "INVEST IN A STRAP-ON",
124
- "[bold italic]FRIZZ OFF", # AC
125
- "I'M FRIZZED OFF", # AC
126
- "I'M GONNA GO TO THAT BUS STATION TOMORROW AND KICK MIKE'S ASS",
127
- "STRUGGLING FROM SUCCESS",
128
- "I'D SHOW YOU MINE; BUT I DON'T THINK YOU'D LIKE IT",
129
- "I'M SEVERAL RAKI'S DEEP",
130
- "DON'T ASK ME QUESTIONS THAT COULD HAVE CONSEQUENCES",
131
- "ENORMOUS DRAGON OF FAECES, DANCING INSIDE ME", # FB
132
- "I HAD A CRISIS OF FAITH IN MY RESEARCH, BUT THEN I REALISED THAT IT'S FINE",
133
- "IT DEPENDS",
134
- "KIND OF A PROOF",
135
- "THE DEAD CAT CAN BE A RESOURCE",
136
- "WITH A TALK LIKE THAT YOU CAN EXPECT TO MEET A PERSON\nWHO TOTALLY DISAGREES WITH EVERYTHING YOU SAID.\nYOU WILL FIND THAT PERSON IN ME", # Moritz @ QuEBS 2022
137
- "MIDNIGHT MEAT",
138
- "EXISTENCE IS CANCER",
139
- "IT'S A PITY THEY FOUND A CURE FOR LEPROSY", # QuEBS 2022 Excursion
140
- "[bold italic]MEOW", # RG
141
- "DON’T KNOW DON’T CARE",
142
- "INTERESTING TRANSFER DOWN THESE RODS",
143
- # "DISSEMINATE THROUGHOUT THE UK",
144
- "DUMPING MY PUMP ON",
145
- "KINDA TOXIC, BUT DON’T WORRY ABOUT THAT",
146
- "WHACKING GREAT ABSORBER", # AM
147
- "I KNOW YOU’RE THEORISTS, AND THIS IS VERY MUDDY",
148
- "I HAVE A PROPENSITY FOR THE DENSITY", # AB @ QuEBS 2022
149
- "OH MY [bold]GOODNESS[/bold]", # GF
150
- "YOU ARE UNDERMINING MY AUTHORITY", # unknown @ QuEBS 2022
151
- "TO BE WEAK, IS ACTUALLY AN ADVANTAGE ",
152
- "SPEEDY BOARDING",
153
- "THE PHILOSOPHICAL SIGNIFICANCE OF POO",
154
- "THE CAFFEINE IS A MATTER OF LIFE AND DEATH ",
155
- "IT’S ALMOST CERTAINLY WRONG, BUT FUN",
156
- "THAT WOULD HAVE BEEN WISE, HOWEVER, I AM NOT A WISE MAN",
157
- "THAT MAKES YOU THINK SOFT",
158
- "I WOULD ARGUE THAT BARE LEGS IS BETTER",
159
- "ANYTHING IS MICROSCOPIC IF IT’S FAR AWAY ENOUGH",
160
- "AN ERROR OCCURRED, HOW IS THAT EVEN SCIENTIFICALLY POSSIBLE",
161
- "YOU HEAR THAT? THAT’S A TARDIGRADE",
162
- "I SEEM TO HAVE LOST CONTROL [OF MY MOUSE]",
163
- "THERE’S NO ANSWER, BY THE WAY",
164
- "BECAUSE OF SOME PHYSICS ",
165
- "PSEUDO-WEAK",
166
- "THESE BALLS ARE JUST REGULAR SHINY", # MW
167
- "THE LAST COUP KIND OF FIZZLED OUT",
168
- "I THINK IT’S STRAIGHT SO IT MUST BE ERADICATED",
169
- "KOBE BEEF IS ALSO FAMOUSLY DEAD COW",
170
- "I’VE ALSO DECIDED I AM NOT A MORAL PERSON",
171
- "IT WAS ONLY AN EMPTY THREAT", # JJMF
172
- "THREATS ARE A PART OF WORKING LIFE", # JJMF
173
- "WELL THE THREAT WORKED!", # JJMF
174
- "AN EMPTY THREAT IS STILL A THREAT",
175
- "WHAT DO YOU THINK I’M SOME SORT OF PEPSI CRACK WHORE?",
176
- "LOW IMPACT FACTOR? I WANT LOW IMPACT ON MY LIFE!",
177
- "NOT F*CK YO MAMA F*CK YO-YO MA!",
178
- "I FORGOT ABOUT THE PASSAGE OF TIME",
179
- "EVERYTHING IS JUST A BIG WEIRD LIGAND",
180
- "IT NEEDS TO TASTE GOOD, AND KILL YOU", # CV
181
- "MY ABILITY TO SEE FUNKINESS REQUIRES EXPERIENCE", # ISQBP 2022
182
- "HISTIDINE IS A PROBLEM",
183
- "I HATE THE FACT THAT THEY SPENT SO MUCH TIME ON THESE QUOTES, INSTEAD OF IMPROVING THEIR SIMULATION SOFTWARE", # Gromacs
184
- "BUTTER MUSS SEIN",
185
- "I DON’T NEED A CONTROL BECAUSE I DON’T HAVE ANY RESULTS",
186
- "LET THE ANIMAL EAT FIBRE, AND THEN EAT THE ANIMAL",
187
- "THAT’S NOT A SHOT MATE!!",
188
- "FUCK AROUND AND FIND OUT",
189
- "♫♬♪[bold]OH MON BEBE[/bold]", # Les Kaira
190
- "WHY DID TOM CHEATHAM KILL US LAST NIGHT?", # ISQBP 2022
191
- "NOSE-HOOVER? SO LIKE A COCAINE ADDICT?",
192
- "YOU CAN SLEEP ON A BANANA",
193
- "AMBER AND I BROKE UP", # MW
194
- "WE WENT TOO FAR",
195
- "THEY GOT JIGGLY PHYSICS?",
196
- "DO YOU WANT TO SEE MY STUFF JIGGLING?",
197
- "WE DON’T SEE COLOURS HERE, WE JUST SEE BALLS",
198
- "IN MY OPINION THIS IS EXACTLY WHAT IS WRONG WITH SCIENCE",
199
- "I DIDN’T REALISE YOU WERE A NONCE",
200
- "I HEARD YOU LIKE FINISHING EARLY",
201
- "JOIN THE QUANTUM BIOLOGY DISCORD SERVER! #GAMERSRISEUP",
202
- "WE SHOULD GET SOMETHING VENOMOUS LIKE A PLATYPUS",
203
- "THERE IS TOO MUCH ON YOUTUBE ABOUT PLATYPUS CUSTARD",
204
- "I DON'T BELIEVE IN GRANDCHILDREN",
205
- "WINNING IS NEVER AN OPTION, ONLY REVENGE",
206
- "FEDERICO’S ALLEYWAY BOUQUET", # MW
207
- "SENT FROM MY GALAXY",
208
- " _______\n / 12 \\ \n| | |\n|9 | 3|\n| \\ |\n| |\n \\___6___/\n\nGOOD HEAVENS, LOOK AT THE TIME!",
209
- # "Har🌳-Frick ✝️ ",
210
- "BRANEURISM\n\nSymptoms:\tlight-headed, delirium, dyslexic, autism, chicken sounds, flowcharts",
211
- f" LISTEN TO PHAT BRAHMS\n | \\\n V \\\n EAT KATSU \\ IS IT FRIDAY? ----> WAIT\n | \\ | Yes ^ No |\n V \\ V L-----------\n SPIN MESMERISING SPIN ORB | IS HILLSIDE OPEN? ---> KATSU\n | | | \n V ^ V \n [bold]BRANEURYSM[/bold] | IS YOUNG'S OPEN ---> KATSU\n / \\ | | \n /YES \\ | V\n L NO------->--------| IS MAX IN THE OFFICE?\nKATSU COMA | | Yes \\ No\n ----<---- \\\n END THE DAY\n",
212
- "I WOULDN’T PAY £100 FOR A WET DREAM,\nFOR £100 I CAN GET A WET REALITY",
213
- "I KNOW YOUR FUTURE… BUT I’LL TELL YOU AFTER I GO TO THE LOO",
214
- "YOU ARE HEADING FOR A SMACK",
215
- "[bold]F*CK!!! WHY ARE THESE GUYS SUCH *$#@'S?![/bold]\n\n...\n\nI NEED ANOTHER COFFEE, I'M NOT ON EDGE ENOUGH.", # MW
216
- "RESULTS ARE A SOCIAL CONSTRUCT",
217
- "AUTISOMANCY",
218
- "IS A HANGING MAN STANDING ON A BOX THAT HE IS HOLDING BENEATH HIS FEET A PERPETUAL MOTION MACHINE?",
219
- "HAVE YOU READ KARL MARX'S LATEST TWEET?",
220
- "WE NEED MORE GAY SCIENCE",
221
- "IF WE OVERCOME THIS I WILL JERK FROM NOW UNTIL THE NEXT YEAR", # FB
222
- "DO LOOK A THIEVING STUFFED DOUGHNUT IN THE ASS",
223
- "YOU'RE NOT HUMAN UNTIL YOU'VE BEEN CITED", # LS
224
- "TRUE [bold]AND[/bold] FALSE",
225
- "EINSTEIN? LARGELY A B*TCH",
226
- "I’M SCARED TO GO TO TEXAS, PEOPLE JUST SHOOTING ME", # MS
227
- "YOU’RE WRONG WITH THAT, YOU’RE ABSOLUTELY WRONG WITH THAT", # MS, supervisory meeting
228
- "WE SHOULD GET AN OFFICE DILDO",
229
- "AIDS SALIVA IS A-OK, SO YOU CAN GIVE THAT WHISTLE A SUCK",
230
- "NO ONE EVER SAID NOT TO EAT YELLOW RICE",
231
- "WALRUSES RAPING PENGUINS FOR REASONS THAT ARE NOT YET CLEAR",
232
- "THEY NEVER SAID DON'T EAT YELLOW RICE",
233
- "RUINING PEOPLE'S CAREERS BY KEEPING THEM IN THE SAME PLACE JUST BECAUSE I DID", # JAK
234
- "WHEN I FIRST STARTED LECTURING I DID A LOT OF BUM WIGGLING", # JAK
235
- "I DON'T UNDERSTAND HAVING VALUES",
236
- "PISS",
237
- "THAT'S THE ONLY REASON I'M NOT EATING YOUR FACE OFF RIGHT NOW",
238
- "DOUBLE-TEAMED BY GERIATRICS",
239
- "LIMPINGCHAN",
240
- "LET'S HAVE A QUICK CHAT? I NEED TO VENT SOME ANGER!",
241
- "THE FRENCHMAN BURNS EASILY",
242
- "WHY IS THE BROWN ALWAYS CAUSING PROBLEMS",
243
- "QUANTUM NATIONALISM",
244
- "THERE IS NO SUCH THING AS TIME",
245
- "IS OUR BANANA CORRECT?",
246
- "WOULD YOU RATHER:\n\nWALLACE HAMMERING FOR AN HOUR\n\nOR\n\nA BRIEF AL-QAEDA MOMENT?",
247
- "THE CONSPIRACY GROWS",
248
- "YOU'RE LOOKING GUILTIER BY THE SECOND",
249
- "I HAD TO MAKE SOMETHING, BUT I ONLY DESTROY",
250
- "QUANTUM CRY FOR HELP",
251
- "QB SOS",
252
- "UNFORUNATELY THE JAVA VERSION ON EUREKA IS TOO OLD TO RUN MINECRAFT", # MW
253
- "NOBODY CARES, ESPECIALLY ME",
254
- "GEOGRAPHICALLY-CHALLENGED",
255
- "I GUESS THAT'S ONE GOOD THING ABOUT FASCISTS",
256
- "TESTICLES HAVE SO MUCH POTENTIAL",
257
- "WHENEVER IN DOUBT, GIVE LOUIE A SHOUT",
258
- "IF IN MOGADISHU, TELL LOUIE 'I MISS YOU'",
259
- "IF YOU'RE IN A PICKLE, GIVE LOUIE A TICKLE",
260
- "DOORS - UNLIKE PEOPLE - CAN BE UNFUCKED", # MW
261
- "ARE YOU GOING TO GO TO HIS OFFICE, LIFT HIS ARMS UP AND GET YOUR FINGERS IN THERE?",
262
- "I CANNOT RIPEN MY OWN BANANA",
263
- "I DO BELIEVE!",
264
- "GREAT MINDS THINK THE SAME JUNK",
265
- """HI EVERYONE. HOW DO YOU KEEP WORKING WITH A COLLABORATOR IF YOUR LAST MEETING ENDED WITH YOU SAYING "SPARE ME THIS USELESS AND AGGRESSIVE RHETORIC, 'CAUSE I DON'T NEED IT, OR CARE ABOUT IT"?""",
266
- "LUCKILY SCIENCE AND DFT CALCULATIONS, DO NOT CARE ABOUT YOUR IMPRESSION OF THEM", # FB
267
- "I WAS ONLY DOING AN IMPRESSION OF A NONCE!",
268
- "I GUESS WE ALL LEAVE AS FLESH",
269
- "SHOULD I BUY AN END OF LIFE PLANNER FOR MY PHD?", # EN
270
- "WE NEED MORE MONEY TO MAKE STUDENTS SUFFER",
271
- "I WON'T TELL YOU WHAT TO DO, I'LL JUST JUDGE YOU",
272
- "NO, YOU DO NOT NEED TO STAY IN QB.\nAND YOU MIGHT NOT WANT TO DO SO",
273
- "SHIT CODE IS THE FIRST STEP TO NOT SHIT CODE", # MW
274
- "GRANTS MAKE IT RAIN!",
275
- "YOU CAN'T GET DRUNK ON PISS",
276
- "CAN'T GET CANCELLED IF YOU'RE NOT FAMOUS",
277
- "I'M STARTING TO BELIEVE THESE ARE NOT REAL CONFUCIUS QUOTES", # MS
278
- "WOULD YOU RATHER MAKE LOVE TO AN EYE OR A FOOT?", # unknown @ MMC 2023
279
- "QUANTUM PHYSICS MEANS THAT ANYTHING CAN HAPPEN AT ANY TIME, FOR NO REASON", # Futurama
280
- "I WOULD BE SCHLOBBIN ON SOMEONE'S KNOB FOR THAT", # LS
281
- "THERE IS NO GREAT LOVE, WITHOUT GREAT JEALOUSY", # Futurama
282
- "THE BAD BOYS WON'T WAIT ALL NIGHT", # MW @ ABC 2023
283
- "YOU WON'T BELIEVE WHAT THIS OLD ITALIAN MAN PUT IN MY MIND",
284
- "THERE'S NOTHING NATURAL ABOUT AVERAGING COORDINATES", # ABC 2023
285
- "ONIONS DON'T HURT ME",
286
- "THAT WAS BEFORE I DISCOVERED TURKISH MEN", # RG @ ABC 2023
287
- "ALL COSTS ARE SUBSTANTIAL",
288
- "ALWAYS A GOOD IDEA TO BLAME THE FRENCH",
289
- "CHEESE, SEX AND ALIENS", # about a museum in Basel
290
- "YOU HAVE TO ACCEPT YOUR MISERY",
291
- "COSTA RICA WAS A SIMULATION WITH THE WRONG FORCE FIELD", # Leandro @ ABC 2023
292
- "LOW ENTROPY REGION OF NERDS",
293
- "THANK YOU FOR BEING WILLING", # MW @ ABC 2023
294
- "HELLO STALKERS, COME TO ME",
295
- "YOU'RE ALWAYS ON THE RECEIVING END OF THE MOOSE SHAFTING",
296
- "IT'S A COCKTAIL OF SHIT",
297
- "F*CK HIM, CARVE YOUR OWN PATH", # LS about Löwdin
298
- "I AM A STRAIGHT WHITE WHALE",
299
- "TO KILL A MONKEYBIRD",
300
- "LAZINESS LEADS TO THE ULTIMATE CREATIVITY",
301
- "DO YOU KNOW HOW TO READ [bold]FACIAL[/bold] LIPS?",
302
- "THERE'S TOO MANY BALD BASTARDS",
303
- "MAYBE YOU'RE THE D&D CHARACTER OF SOMEONE ELSE, AND THEY ARE DOING A REALLY BAD JOB",
304
- "THIS IS NOT AN ACT OF GOD, IT IS PITY",
305
- "FREUD'S THEORY ON GRAPEFRUITING YOUR DAD",
306
- "NORMALLY IT'S SHIT. BUT TODAY, IT'S [bold]EXCEPTIONALLY[/bold] SHIT",
307
- "WHAT ARE THESE REVOLUTIONARY LOOKING FIGURES? AH YES, HEXAGONS",
308
- "IF YOU ABUSE THEM, THEY'LL MOVE",
309
- "IT'S NOT SEXUAL HARASSMENT, IT'S A COMPLIMENT",
310
- "NOT A GOOD CRAZY",
311
- "EVENTUALLY YOU HAVE TO CARE ABOUT [bold]SOMETHING[/bold]",
312
- "DON'T PUSH THE RIVER", # MW
313
- "THE DILDO OF CONSEQUENCES RARELY ARRIVES PRE-LUBED", # REDDIT
314
- "IT'S ALL UNDER CONTROL", # TH
315
- "THERE IS NO FUN WITHOUT VIOLENCE", # GF
316
- "AN UBER IS TRANSIENT BUT SWIM TRUNKS ARE FOREVER", # MW
317
- "IT'S NOT STEALING IF YOU PIONEER SOMETHING", # MW
318
- "THE SUFFERING IS OPTIONAL",
319
- "THAT'S THANKS TO LOUIE, AIMEE, AND - IT PAINS ME TO SAY IT - GEORGE", # alex jones @ QuEBS 2023
320
- "HOME IS WHERE YOUR CHEEKS IMPRESS", # MW
321
- "YOUR NUTS ARE DOING A REAL JOB ON MY THROAT", # MW
322
- "VICIOUS CIRCLE OF BOLLOCKING", # RL, XChem, Aug 2023
323
- "CROWDS GATHER TO WITNESS THE FIRST PANINI ARRIVE IN IRELAND", # Waterford Whispers News
324
- "BLESSED ARE THE TOASTED SANDWICH MAKERS", # Waterford Whispers News
325
- "BEEF JESUS DIED FOR OUR SINS", # MW
326
- "I'M NOT IN A PICKLE, I'M IN A POTENTIAL WELL",
327
- "THE ELECTRON IDENTITY CRISIS",
328
- "TOY STORY IS A SOCIAL COMMENTARY ON THE MEASUREMENT PROBLEM",
329
- "DO YOU THINK SOME STUDENTS JUST AREN'T INTELLIGENT ENOUGH?",
330
- "YOU DON'T NEED A DEBUGGER. YOU NEED A PRIEST", # MW
331
- "IF THE PAPER YOU WANT TO READ DOESN'T EXIST, WRITE IT", # MW
332
- "THE WORLD IS YOUR HOSTAGE", #
333
- ]
334
-
335
-
336
- def main():
337
- app()
338
-
339
-
340
- if __name__ == "__main__":
341
- app()
File without changes
File without changes
File without changes
File without changes
File without changes