diff --git a/src/view/processes_view.rs b/src/view/processes_view.rs index beea7ed..0aec5fb 100644 --- a/src/view/processes_view.rs +++ b/src/view/processes_view.rs @@ -1,5 +1,5 @@ use anyhow::{Error, Result}; -use chrono::{DateTime, Duration, Utc}; +use chrono::{DateTime, Duration, Utc, NaiveDateTime, TimeZone}; use chrono_tz::{Tz, UTC}; use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; @@ -11,7 +11,7 @@ use cursive::{ event::{Callback, Event, EventResult}, inner_getters, view::ViewWrapper, - views::{self, Dialog, EditView, OnEventView}, + views::{self, Dialog, EditView, OnEventView, LinearLayout, DummyView, TextView}, Cursive, }; use size::{Base, SizeFormatter, Style}; @@ -190,6 +190,7 @@ pub struct ProcessesView { filter: Arc>, // Used as a condition for event_time/event_date in system.query_log // (for navigating through system.query_log historically) + start_time: Arc>>>, end_time: Arc>>>, // Number of queries to render limit: Arc>, @@ -402,31 +403,59 @@ impl ProcessesView { return Ok((query_ids, min_query_start_microseconds)); } - pub fn update_end_time(&mut self, is_sub: bool, minutes: i64) { + pub fn shift_time_interval(&mut self, is_sub: bool, minutes: i64) { + let start_time_bind = self.start_time.clone(); + let start_time_guard = start_time_bind.lock(); + let end_time_bind = self.end_time.clone(); let end_time_guard = end_time_bind.lock(); + + let mut new_start_time = start_time_guard + .as_ref() + .unwrap() + .unwrap_or_else(|| Utc::now().with_timezone(&UTC) - Duration::hours(1)); + let mut new_end_time = end_time_guard .as_ref() .unwrap() .unwrap_or_else(|| Utc::now().with_timezone(&UTC)); + if is_sub { + new_start_time -= Duration::minutes(minutes); new_end_time -= Duration::minutes(minutes); log::debug!( - "Set end_time to {} (seeked to {} minutes backward)", + "Set start_time to {}, end_time to {} (seeked to {} minutes backward)", + new_start_time, new_end_time, minutes ); } else { + new_start_time += Duration::minutes(minutes); new_end_time += Duration::minutes(minutes); log::debug!( - "Set end_time to {} (seeked to {} minutes forward)", + "Set start_time to {}, end_time to {} (seeked to {} minutes backward)", + new_start_time, new_end_time, minutes ); } + *start_time_guard.unwrap() = Some(new_start_time); *end_time_guard.unwrap() = Some(new_end_time); } + pub fn update_time_interval(&mut self, start: DateTime, end: DateTime) { + let start_time_bind = self.start_time.clone(); + let start_time_guard = start_time_bind.lock(); + + let end_time_bind = self.end_time.clone(); + let end_time_guard = end_time_bind.lock(); + + log::debug!("Set start_time to {}, end_time to {}", start, end); + + *start_time_guard.unwrap() = Some(start); + *end_time_guard.unwrap() = Some(end); + } + pub fn update_limit(&mut self, is_sub: bool) { let new_limit = if is_sub { self.limit.clone().lock().unwrap().saturating_sub(20) @@ -446,6 +475,7 @@ impl ProcessesView { let is_system_processes = matches!(processes_type, Type::ProcessList); let filter = Arc::new(Mutex::new(String::new())); + let start_time = Arc::new(Mutex::new(Option::>::None)); let end_time = Arc::new(Mutex::new(Option::>::None)); let limit = Arc::new(Mutex::new(if matches!(processes_type, Type::ProcessList) { 10000 @@ -455,16 +485,18 @@ impl ProcessesView { let update_callback_context = context.clone(); let update_callback_filter = filter.clone(); + let update_callback_start_time = start_time.clone(); let update_callback_end_time = end_time.clone(); let update_callback_limit = limit.clone(); let update_callback = move || { let mut context = update_callback_context.lock().unwrap(); let filter = update_callback_filter.lock().unwrap().clone(); let limit = *update_callback_limit.lock().unwrap(); + let start_time = &*update_callback_start_time.lock().unwrap(); let end_time = &*update_callback_end_time.lock().unwrap(); let end_time = end_time.unwrap_or_else(|| Utc::now().with_timezone(&UTC)); - let start_time = end_time - Duration::hours(1); + let start_time = start_time.unwrap_or_else(|| end_time - Duration::hours(1)); match processes_type { // TODO: in case of end_time is set, render LastQueryLog automatically @@ -524,6 +556,7 @@ impl ProcessesView { options: view_options, is_system_processes, filter, + start_time, end_time, limit, bg_runner, @@ -550,28 +583,6 @@ impl ProcessesView { } return Some(EventResult::Ignored); }); - - fn create_update_end_time_cb( - view_name: &'static str, - is_sub: bool, - ) -> impl Fn(&mut Cursive, &str) { - move |siv: &mut Cursive, text: &str| { - siv.pop_layer(); - match text.parse::() { - Ok(num_mins) => { - siv.call_on_name(view_name, |v: &mut OnEventView| { - let v = v.get_inner_mut(); - v.update_end_time(is_sub, num_mins); - v.bg_runner.schedule(); - }); - } - Err(_) => { - siv.add_layer(Dialog::info("The number of minutes must be an integer")); - } - } - } - } - log::debug!("Adding views actions"); let mut context = context.lock().unwrap(); context.add_view_action(&mut event_view, "Select", ' ', |v| { @@ -988,42 +999,80 @@ impl ProcessesView { // Bindings T/t inspiried by atop(1) (so as this functionality) context.add_view_action(&mut event_view, "Seek 10 mins backward", 'T', |v| { let v = v.downcast_mut::().unwrap(); - v.update_end_time(true, 10); + v.shift_time_interval(true, 10); v.bg_runner.schedule(); return Ok(Some(EventResult::consumed())); }); context.add_view_action(&mut event_view, "Seek 10 mins forward", 't', |v| { let v = v.downcast_mut::().unwrap(); - v.update_end_time(false, 10); + v.shift_time_interval(false, 10); v.bg_runner.schedule(); return Ok(Some(EventResult::consumed())); }); context.add_view_action( &mut event_view, - "Seek N mins backward", - Event::AltChar('T'), - move |_v| { - return Ok(Some(EventResult::Consumed(Some(Callback::from_fn( - move |siv: &mut Cursive| { - let update_end_time_cb = create_update_end_time_cb(view_name, true); - let view = OnEventView::new( - EditView::new().on_submit(update_end_time_cb).min_width(10), - ); - siv.add_layer(view); - }, - ))))); - }, - ); - context.add_view_action( - &mut event_view, - "Seek N mins forward", + "Set time interval", Event::AltChar('t'), move |_v| { return Ok(Some(EventResult::Consumed(Some(Callback::from_fn( move |siv: &mut Cursive| { - let update_end_time_cb = create_update_end_time_cb(view_name, false); + let on_submit = move |siv: &mut Cursive| { + let begin = siv + .call_on_name("begin", |view: &mut EditView| view.get_content()) + .unwrap(); + let end = siv + .call_on_name("end", |view: &mut EditView| view.get_content()) + .unwrap(); + + siv.pop_layer(); + + let begin_str = begin.as_str(); + let end_str = end.as_str(); + + let mut new_start_time = Utc::now().with_timezone(&UTC) - Duration::hours(1); + let mut new_end_time = Utc::now().with_timezone(&UTC); + + if !(begin_str.is_empty() && end_str.is_empty()) { + let format_datetime = "%Y-%m-%d %H:%M:%S"; + + match NaiveDateTime::parse_from_str(begin_str, format_datetime) { + Ok(begin_native) => new_start_time = Utc.from_utc_datetime(&begin_native).with_timezone(&UTC), + Err(_) => { + siv.add_layer(Dialog::info("Error when parsing datetime \"begin\", datetime format should be YYYY-MM-DD hh:mm:ss. You can leave these fields blank to reset to \"default\"")); + return; + }, + } + + match NaiveDateTime::parse_from_str(end_str, format_datetime) { + Ok(end_native) => new_end_time = Utc.from_utc_datetime(&end_native).with_timezone(&UTC), + Err(_) => { + siv.add_layer(Dialog::info("Error when parsing datetime \"end\", datetime format should be YYYY-MM-DD hh:mm:ss. You can leave these fields blank to reset to \"default\"")); + return; + }, + } + } + + siv.call_on_name(view_name, |v: &mut OnEventView| { + let v = v.get_inner_mut(); + v.update_time_interval(new_start_time, new_end_time); + v.bg_runner.schedule(); + }); + }; + let view = OnEventView::new( - EditView::new().on_submit(update_end_time_cb).min_width(10), + Dialog::new() + .title("Set the time interval") + .content( + LinearLayout::vertical() + .child(TextView::new("format: YYYY-MM-DD hh:mm:ss")) + .child(DummyView) + .child(TextView::new("begin:")) + .child(EditView::new().with_name("begin")) + .child(DummyView) + .child(TextView::new("end:")) + .child(EditView::new().with_name("end")), + ) + .button("Submit", on_submit) ); siv.add_layer(view); },