From cd75a177373dad9f065a460a2e9285ad024f0b8e Mon Sep 17 00:00:00 2001 From: Niki Vihtola Date: Mon, 1 Dec 2025 14:49:18 +0200 Subject: [PATCH] add delete log option --- jrnl/cli.py | 5 ++++ jrnl/commands/logs.py | 47 +++++++++++++++++++++++++++++++++++-- jrnl/database/operations.py | 29 +++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index db68f78..ebb4dd9 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -80,6 +80,9 @@ Examples: # View logs from last 3 days jrnl logs --days 3 + + # Delete a log entry by hash/label + jrnl logs --delete 5a546f30 ''', formatter_class=argparse.RawDescriptionHelpFormatter ) @@ -87,6 +90,8 @@ Examples: help='Filter logs by number of days') logs_parser.add_argument('-n', '--limit', type=int, default=50, help='Maximum number of logs to show (default: 50)') + logs_parser.add_argument('--delete', metavar='HASH', + help='Delete a log entry by its hash/label') # jrnl config config_parser = subparsers.add_parser( diff --git a/jrnl/commands/logs.py b/jrnl/commands/logs.py index b5a37e5..7da783a 100644 --- a/jrnl/commands/logs.py +++ b/jrnl/commands/logs.py @@ -1,14 +1,18 @@ """jrnl logs command - View log entries.""" import sqlite3 -from ..database.operations import get_logs_since, get_all_logs +from ..database.operations import get_logs_since, get_all_logs, get_log_by_label, delete_log from ..utils.date_utils import get_datetime_ago -from ..utils.formatting import format_log_entry +from ..utils.formatting import format_log_entry, format_success, format_error def handle(args): """Handle the 'logs' command.""" try: + # Handle deletion if --delete flag is provided + if args.delete: + return handle_delete(args.delete) + # Get logs based on filters if args.days: cutoff = get_datetime_ago(days=args.days) @@ -35,3 +39,42 @@ def handle(args): import traceback traceback.print_exc() return 1 + + +def handle_delete(label: str): + """Handle log deletion with confirmation.""" + try: + # Find the log entry + log = get_log_by_label(label) + + if not log: + print(format_error(f"Log entry not found: {label}")) + return 1 + + # Show the log entry to be deleted + print("\nLog entry to be deleted:") + print(format_log_entry(log)) + + # Ask for confirmation + response = input("\nAre you sure you want to delete this log entry? (y/N): ") + + if response.lower() != 'y': + print("Deletion cancelled") + return 0 + + # Delete the log + if delete_log(label): + print(format_success(f"Log entry deleted: {label}")) + return 0 + else: + print(format_error(f"Failed to delete log entry: {label}")) + return 1 + + except sqlite3.Error as e: + print(format_error(f"Database error: {e}")) + return 1 + except Exception as e: + print(format_error(f"Unexpected error: {type(e).__name__}: {e}")) + import traceback + traceback.print_exc() + return 1 diff --git a/jrnl/database/operations.py b/jrnl/database/operations.py index 678ca7a..944c354 100644 --- a/jrnl/database/operations.py +++ b/jrnl/database/operations.py @@ -57,6 +57,35 @@ def get_all_logs(limit: int = 50) -> List[Log]: ) for row in rows] +def get_log_by_label(label: str) -> Optional[Log]: + """Get a log entry by its label/hash.""" + with get_connection() as conn: + cursor = conn.cursor() + cursor.execute( + '''SELECT * FROM logs + WHERE label = ?''', + (label,) + ) + row = cursor.fetchone() + if row: + return Log( + id=row['id'], + timestamp=row['timestamp'], + log_message=row['log_message'], + type=row['type'], + label=row['label'] + ) + return None + + +def delete_log(label: str) -> bool: + """Delete a log entry by its label/hash. Returns True if deleted, False if not found.""" + with get_connection() as conn: + cursor = conn.cursor() + cursor.execute('DELETE FROM logs WHERE label = ?', (label,)) + return cursor.rowcount > 0 + + def insert_daily(daily: Daily) -> int: """Insert or replace a daily entry.""" with get_connection() as conn: