Skip to content

Commit 344a3d1

Browse files
committed
add job log view in tui
1 parent 521bbdf commit 344a3d1

14 files changed

Lines changed: 232 additions & 16 deletions

File tree

coman/src/app/ids.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
pub enum Id {
33
Toolbar,
44
WorkloadList,
5+
WorkloadLogs,
56
GlobalListener,
67
Menu,
78
InfoPopup,

coman/src/app/messages.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ pub enum CscsMsg {
3838
SelectSystem,
3939
SystemSelected(String),
4040
}
41-
41+
#[derive(Debug, PartialEq)]
42+
pub enum JobMsg {
43+
ShowLog(usize),
44+
}
4245
#[derive(Debug, PartialEq)]
4346
pub enum Msg {
4447
AppClose,
@@ -50,5 +53,6 @@ pub enum Msg {
5053
Error(String),
5154
Info(String),
5255
Cscs(CscsMsg),
56+
Job(JobMsg),
5357
None,
5458
}

coman/src/app/model.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ use crate::{
1010
app::{
1111
ids::Id,
1212
messages::{
13-
CscsMsg, ErrorPopupMsg, InfoPopupMsg, LoginPopupMsg, MenuMsg, Msg, SystemSelectMsg,
13+
CscsMsg, ErrorPopupMsg, InfoPopupMsg, JobMsg, LoginPopupMsg, MenuMsg, Msg,
14+
SystemSelectMsg,
1415
},
1516
user_events::UserEvent,
1617
},
1718
components::{
1819
error_popup::ErrorPopup, info_popup::InfoPopup, login_popup::LoginPopup,
19-
system_select_popup::SystemSelectPopup, workload_menu::WorkloadMenu,
20+
system_select_popup::SystemSelectPopup, workload_log::WorkloadLog,
21+
workload_menu::WorkloadMenu,
2022
},
2123
cscs::{cli::cscs_login, handlers::cscs_system_set},
2224
trace_dbg,
@@ -42,6 +44,10 @@ where
4244

4345
/// Triggers async request to select current system
4446
pub select_system_tx: mpsc::Sender<()>,
47+
48+
/// Triggers watching job logs
49+
/// sending None stops watching
50+
pub job_log_tx: mpsc::Sender<Option<usize>>,
4551
}
4652

4753
impl<T> Model<T>
@@ -53,6 +59,7 @@ where
5359
bridge: TerminalBridge<T>,
5460
error_tx: mpsc::Sender<String>,
5561
select_system_tx: mpsc::Sender<()>,
62+
job_log_tx: mpsc::Sender<Option<usize>>,
5663
) -> Self {
5764
Self {
5865
app,
@@ -61,6 +68,7 @@ where
6168
terminal: bridge,
6269
error_tx,
6370
select_system_tx,
71+
job_log_tx,
6472
}
6573
}
6674

@@ -79,7 +87,9 @@ where
7987
.as_ref(),
8088
)
8189
.split(f.area());
82-
self.app.view(&Id::WorkloadList, f, chunks[0]);
90+
if self.app.mounted(&Id::WorkloadList) {
91+
self.app.view(&Id::WorkloadList, f, chunks[0]);
92+
}
8393
self.app.view(&Id::Toolbar, f, chunks[1]);
8494

8595
if self.app.mounted(&Id::Menu) {
@@ -217,6 +227,28 @@ where
217227
}
218228
}
219229
}
230+
fn handle_job_msg(&mut self, msg: JobMsg) -> Option<Msg> {
231+
match msg {
232+
JobMsg::ShowLog(jobid) => {
233+
if self.app.mounted(&Id::WorkloadList) {
234+
assert!(self.app.umount(&Id::WorkloadList).is_ok());
235+
}
236+
if !self.app.mounted(&Id::WorkloadLogs) {
237+
assert!(
238+
self.app
239+
.mount(Id::WorkloadLogs, Box::new(WorkloadLog::new()), vec![])
240+
.is_ok()
241+
);
242+
}
243+
assert!(self.app.active(&Id::WorkloadLogs).is_ok());
244+
let job_log_tx = self.job_log_tx.clone();
245+
tokio::spawn(async move {
246+
job_log_tx.send(Some(jobid)).await.unwrap();
247+
});
248+
None
249+
}
250+
}
251+
}
220252
}
221253

222254
// Let's implement Update for model
@@ -284,6 +316,7 @@ where
284316
}
285317
Msg::LoginPopup(msg) => self.handle_login_popup_msg(msg),
286318
Msg::SystemSelectPopup(msg) => self.handle_system_select_popup_msg(msg),
319+
Msg::Job(msg) => self.handle_job_msg(msg),
287320
Msg::None => None,
288321
}
289322
} else {

coman/src/app/user_events.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::cscs::api_client::{Job, System};
44
pub enum CscsEvent {
55
LoggedIn,
66
GotWorkloadData(Vec<Job>),
7+
GotJobLog(String),
78
SelectSystemList(Vec<System>),
89
}
910

coman/src/components/global_listener.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use tuirealm::{
55
};
66

77
use crate::app::{
8-
messages::{MenuMsg, Msg, SystemSelectMsg},
8+
messages::{JobMsg, MenuMsg, Msg, SystemSelectMsg},
99
user_events::{CscsEvent, UserEvent},
1010
};
1111

coman/src/components/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ pub(crate) mod login_popup;
55
pub(crate) mod system_select_popup;
66
pub(crate) mod toolbar;
77
pub(crate) mod workload_list;
8+
pub(crate) mod workload_log;
89
pub(crate) mod workload_menu;

coman/src/components/toolbar.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ impl Toolbar {
1212
pub fn new() -> Self {
1313
Self {
1414
component: Label::default()
15-
.text("q: quit, tab/shift+tab: change focus, x: menu, ?: help"),
15+
.text("q: quit, l: logs, tab/shift+tab: change focus, x: menu, ?: help"),
1616
}
1717
}
1818
}

coman/src/components/workload_list.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
use tui_realm_stdlib::List;
22
use tuirealm::{
3-
AttrValue, Attribute, Component, Event, MockComponent,
3+
AttrValue, Attribute, Component, Event, MockComponent, State, StateValue,
44
command::{Cmd, CmdResult, Direction, Position},
5-
event::{Key, KeyEvent},
5+
event::{Key, KeyEvent, KeyModifiers},
66
props::{Alignment, BorderType, Borders, Color, TableBuilder, TextSpan},
77
};
88

9-
use crate::app::{
10-
messages::Msg,
11-
user_events::{CscsEvent, UserEvent},
9+
use crate::{
10+
app::{
11+
messages::{JobMsg, Msg},
12+
user_events::{CscsEvent, UserEvent},
13+
},
14+
cscs::api_client::Job,
1215
};
1316

1417
#[derive(MockComponent)]
1518
pub(crate) struct WorkloadList {
1619
component: List,
20+
jobs: Vec<Job>,
1721
}
1822

1923
impl Default for WorkloadList {
@@ -31,6 +35,7 @@ impl Default for WorkloadList {
3135
.highlighted_str("-")
3236
.rewind(true)
3337
.step(4),
38+
jobs: vec![],
3439
}
3540
}
3641
}
@@ -59,8 +64,10 @@ impl Component<Msg, UserEvent> for WorkloadList {
5964
}
6065
Event::User(UserEvent::Cscs(CscsEvent::GotWorkloadData(data))) => {
6166
if data.len() == 0 {
67+
self.jobs = vec![];
6268
self.attr(Attribute::Content, AttrValue::Table(vec![]));
6369
} else {
70+
self.jobs = data.clone();
6471
let mut table = TableBuilder::default();
6572
for (idx, job) in data.iter().enumerate() {
6673
if idx > 0 {
@@ -77,6 +84,18 @@ impl Component<Msg, UserEvent> for WorkloadList {
7784
}
7885
self.perform(Cmd::Change)
7986
}
87+
Event::Keyboard(KeyEvent {
88+
code: Key::Char('l'),
89+
modifiers: KeyModifiers::NONE,
90+
}) => {
91+
if let State::One(StateValue::Usize(index)) = self.state()
92+
&& !self.jobs.is_empty()
93+
{
94+
let job = self.jobs[index].clone();
95+
return Some(Msg::Job(JobMsg::ShowLog(job.id)));
96+
}
97+
CmdResult::None
98+
}
8099
_ => CmdResult::None,
81100
};
82101
Some(Msg::None)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use tui_realm_stdlib::{List, Textarea};
2+
use tuirealm::{
3+
AttrValue, Attribute, Component, Event, MockComponent, State, StateValue,
4+
command::{Cmd, CmdResult, Direction, Position},
5+
event::{Key, KeyEvent, KeyModifiers},
6+
props::{
7+
Alignment, BorderType, Borders, Color, PropPayload, PropValue, TableBuilder, TextSpan,
8+
},
9+
};
10+
11+
use crate::app::{
12+
messages::Msg,
13+
user_events::{CscsEvent, UserEvent},
14+
};
15+
16+
#[derive(MockComponent)]
17+
pub struct WorkloadLog {
18+
component: Textarea,
19+
}
20+
21+
impl WorkloadLog {
22+
pub fn new() -> Self {
23+
Self {
24+
component: Textarea::default()
25+
.borders(
26+
Borders::default()
27+
.modifiers(BorderType::Rounded)
28+
.color(Color::Yellow),
29+
)
30+
.title("Workload Log", Alignment::Center)
31+
.step(4),
32+
}
33+
}
34+
}
35+
impl Component<Msg, UserEvent> for WorkloadLog {
36+
fn on(&mut self, ev: tuirealm::Event<UserEvent>) -> Option<Msg> {
37+
let _ = match ev {
38+
Event::User(UserEvent::Cscs(CscsEvent::GotJobLog(log))) => {
39+
self.attr(
40+
Attribute::Content,
41+
AttrValue::Payload(PropPayload::Vec(
42+
log.lines()
43+
.map(|l| TextSpan::from(l))
44+
.map(PropValue::TextSpan)
45+
.collect(),
46+
)),
47+
);
48+
CmdResult::None
49+
}
50+
_ => CmdResult::None,
51+
};
52+
Some(Msg::None)
53+
}
54+
}

coman/src/cscs/api_client.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use firecrest_client::{
77
post_compute_system_job, stop_compute_system_job,
88
},
99
filesystem_api::{
10-
post_filesystem_ops_mkdir, post_filesystem_ops_upload, put_filesystem_ops_chmod,
10+
get_filesystem_ops_tail, post_filesystem_ops_mkdir, post_filesystem_ops_upload,
11+
put_filesystem_ops_chmod,
1112
},
1213
status_api::{get_status_systems, get_status_userinfo},
1314
types::{
@@ -349,6 +350,11 @@ impl CscsApi {
349350
.wrap_err("couldn't upload file")?;
350351
Ok(())
351352
}
353+
pub async fn tail(&self, system_name: &str, path: PathBuf, lines: usize) -> Result<String> {
354+
get_filesystem_ops_tail(&self.client, system_name, path, lines)
355+
.await
356+
.wrap_err("couldn't tail file")
357+
}
352358
pub async fn get_userinfo(&self, system_name: &str) -> Result<UserInfo> {
353359
let result = get_status_userinfo(&self.client, system_name)
354360
.await

0 commit comments

Comments
 (0)