Command to backfill to last entry
This commit is contained in:
parent
2abc99ff3b
commit
e158378a13
2 changed files with 95 additions and 0 deletions
93
src/app.rs
93
src/app.rs
|
|
@ -493,6 +493,17 @@ impl App {
|
||||||
self.delete_selected_frame()?;
|
self.delete_selected_frame()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
KeyCode::Char('b') => {
|
||||||
|
// Backfill - adjust start time to match previous entry's end time
|
||||||
|
// Only works in ByDate mode at Entry level
|
||||||
|
if !matches!(self.log_view_selection_level, LogViewSelection::Entry) {
|
||||||
|
self.set_status_message("Backfill only works at Entry level (use h/l)");
|
||||||
|
} else if !matches!(self.log_view_grouping, LogViewGrouping::ByDate) {
|
||||||
|
self.set_status_message("Backfill only works in By Date view (press g)");
|
||||||
|
} else {
|
||||||
|
self.fix_entry_gap()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
KeyCode::Char('h') | KeyCode::Left => {
|
KeyCode::Char('h') | KeyCode::Left => {
|
||||||
// Zoom out selection level
|
// Zoom out selection level
|
||||||
self.log_view_selection_level = match (&self.log_view_selection_level, &self.log_view_grouping) {
|
self.log_view_selection_level = match (&self.log_view_selection_level, &self.log_view_grouping) {
|
||||||
|
|
@ -824,6 +835,88 @@ impl App {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fix_entry_gap(&mut self) -> anyhow::Result<()> {
|
||||||
|
use chrono::DateTime;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
self.set_status_message("Backfill function called");
|
||||||
|
|
||||||
|
// Check if selection is valid
|
||||||
|
if self.log_view_selected >= self.log_view_frame_indices.len() {
|
||||||
|
self.set_status_message("Invalid selection");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't fix the first entry
|
||||||
|
if self.log_view_selected == 0 {
|
||||||
|
self.set_status_message("No previous entry!");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if previous entry is on the same day
|
||||||
|
let current_frame_idx = self.log_view_frame_indices[self.log_view_selected];
|
||||||
|
let prev_frame_idx = self.log_view_frame_indices[self.log_view_selected - 1];
|
||||||
|
|
||||||
|
if let (Some(current), Some(prev)) = (
|
||||||
|
self.log_view_frames.get(current_frame_idx),
|
||||||
|
self.log_view_frames.get(prev_frame_idx),
|
||||||
|
) {
|
||||||
|
// Parse timestamps
|
||||||
|
if let (Ok(curr_start), Ok(prev_stop)) = (
|
||||||
|
DateTime::parse_from_rfc3339(¤t.start),
|
||||||
|
DateTime::parse_from_rfc3339(&prev.stop),
|
||||||
|
) {
|
||||||
|
// Check if they're on the same day
|
||||||
|
let curr_date = curr_start.format("%Y-%m-%d").to_string();
|
||||||
|
let prev_date = prev_stop.format("%Y-%m-%d").to_string();
|
||||||
|
|
||||||
|
if curr_date != prev_date {
|
||||||
|
self.set_status_message("No previous entry on same day!");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read watson frames file
|
||||||
|
let frames_path = dirs::config_dir()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Could not find config directory"))?
|
||||||
|
.join("watson")
|
||||||
|
.join("frames");
|
||||||
|
|
||||||
|
let frames_content = std::fs::read_to_string(&frames_path)?;
|
||||||
|
let mut frames: Value = serde_json::from_str(&frames_content)?;
|
||||||
|
|
||||||
|
// Find and update the frame with matching id
|
||||||
|
if let Some(frames_array) = frames.as_array_mut() {
|
||||||
|
for frame in frames_array {
|
||||||
|
if let Some(frame_array) = frame.as_array_mut() {
|
||||||
|
// Frame format: [start, stop, project, id, tags, ...]
|
||||||
|
if frame_array.len() > 3 {
|
||||||
|
if let Some(id) = frame_array[3].as_str() {
|
||||||
|
if id == current.id {
|
||||||
|
// Update start timestamp (index 0)
|
||||||
|
let new_start_timestamp = prev_stop.timestamp();
|
||||||
|
frame_array[0] = Value::Number(new_start_timestamp.into());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back to frames file
|
||||||
|
let updated_content = serde_json::to_string_pretty(&frames)?;
|
||||||
|
std::fs::write(&frames_path, updated_content)?;
|
||||||
|
|
||||||
|
// Reload log content
|
||||||
|
self.load_log_content()?;
|
||||||
|
self.set_status_message("Entry start time adjusted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.needs_clear = true;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn copy_log_to_clipboard(&mut self) -> anyhow::Result<()> {
|
fn copy_log_to_clipboard(&mut self) -> anyhow::Result<()> {
|
||||||
if self.log_view_content.is_empty() {
|
if self.log_view_content.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
||||||
|
|
@ -955,6 +955,8 @@ fn render_log_view_help(frame: &mut Frame, app: &App) {
|
||||||
"Actions:",
|
"Actions:",
|
||||||
"- e: Edit the selected entry (Entry level only)",
|
"- e: Edit the selected entry (Entry level only)",
|
||||||
"- x: Delete the selected entry (Entry level only)",
|
"- x: Delete the selected entry (Entry level only)",
|
||||||
|
"- b: Backfill - set entry start time to previous entry's end time",
|
||||||
|
" (Entry level, By Date view only)",
|
||||||
"- c: Copy selection to clipboard (works at all levels)",
|
"- c: Copy selection to clipboard (works at all levels)",
|
||||||
" Copies based on current selection level",
|
" Copies based on current selection level",
|
||||||
"",
|
"",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue