Switch from flat text file to sqlite3 databases for task storage. Many new flags added including -f for separate files meaning you can have multiple task lists.
This commit is contained in:
548
ged
548
ged
@@ -2,19 +2,39 @@
|
||||
|
||||
# Git 'er Done - Simple Todo Manager
|
||||
# Usage:
|
||||
# ged [task description] [due_date] - Add a task
|
||||
# ged - List all tasks
|
||||
# ged -d <task_number> - Mark task as done
|
||||
# ged -r <task_number> <new_date> - Reschedule task
|
||||
# ged [-f file_name] [task description] [due_date] - Add a task
|
||||
# ged [-f file_name] - List all tasks
|
||||
# ged [-f file_name] -d <task_number> - Mark task as done
|
||||
# ged [-f file_name] -r <task_number> <new_date> - Reschedule task
|
||||
|
||||
# Use XDG data directory or fallback
|
||||
XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||
TASKS_DIR="$XDG_DATA_HOME/git-er-done"
|
||||
TASKS_FILE="$TASKS_DIR/tasks"
|
||||
DEFAULT_DB="tasks.db"
|
||||
TASKS_DB="$TASKS_DIR/$DEFAULT_DB"
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
mkdir -p "$TASKS_DIR"
|
||||
|
||||
# Initialize database with schema if it doesn't exist
|
||||
init_db() {
|
||||
sqlite3 "$TASKS_DB" <<'EOF'
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
description TEXT NOT NULL,
|
||||
due_date TEXT,
|
||||
status TEXT DEFAULT 'pending',
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TEXT,
|
||||
project TEXT DEFAULT 'default'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_status ON tasks(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_due_date ON tasks(due_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_project ON tasks(project);
|
||||
EOF
|
||||
}
|
||||
|
||||
help() {
|
||||
echo "${0##*/}"
|
||||
echo "Git 'er Done - Simple Todo Manager"
|
||||
@@ -33,16 +53,36 @@ help() {
|
||||
done | sort
|
||||
echo ""
|
||||
echo "When adding tasks:"
|
||||
echo " ged \"task description\" [due_date]"
|
||||
echo " ged [-f file_name] \"task description\" [due_date]"
|
||||
echo ""
|
||||
echo "Multiple task lists:"
|
||||
echo " Use -f to specify different task files (creates separate .db files)"
|
||||
echo " Default file is 'tasks.db' if no -f flag is used"
|
||||
echo ""
|
||||
echo "Date formats: YYYY-MM-DD, 2025-08-10, tomorrow, next monday, etc."
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " ged \"Fix bug in project\""
|
||||
echo " ged \"Call dentist\" tomorrow"
|
||||
echo " ged \"Submit report\" 2025-08-15"
|
||||
echo " ged -d 1"
|
||||
echo " ged -r 2 \"next friday\""
|
||||
echo " Basic usage:"
|
||||
echo " ged \"Fix bug in project\" # Add task"
|
||||
echo " ged \"Call dentist\" tomorrow # Add task with due date"
|
||||
echo " ged # List pending tasks"
|
||||
echo " ged -d 1 # Mark task 1 as done"
|
||||
echo " ged -r 2 \"next friday\" # Reschedule task 2"
|
||||
echo ""
|
||||
echo " Multiple task lists:"
|
||||
echo " ged -f errands \"Buy groceries\" # Add to errands.db"
|
||||
echo " ged -f work -l # List work tasks"
|
||||
echo " ged -f personal -s # Show personal stats"
|
||||
echo ""
|
||||
echo " View completed and all tasks:"
|
||||
echo " ged -c (--completed) # Show completed tasks"
|
||||
echo " ged -a (--all) # Show all tasks with status"
|
||||
echo " ged -s (--stats) # Show statistics"
|
||||
echo ""
|
||||
echo " Task management:"
|
||||
echo " ged -x 3 (--remove) # Permanently delete task 3"
|
||||
echo " ged -p (--purge) # Remove tasks completed >30 days ago"
|
||||
echo " ged -p 7 (--purge 7) # Remove tasks completed >7 days ago"
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -101,11 +141,18 @@ add_task() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create tasks file if it doesn't exist
|
||||
touch "$TASKS_FILE"
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
# Add task to file (format: description|due_date)
|
||||
echo "$description|$parsedDue" >> "$TASKS_FILE"
|
||||
# Add task to database using safe SQL escaping
|
||||
local escapedDesc
|
||||
escapedDesc="${description//\'/\'\'}" # Escape single quotes
|
||||
|
||||
if [[ -n "$parsedDue" ]]; then
|
||||
sqlite3 "$TASKS_DB" "INSERT INTO tasks (description, due_date) VALUES ('$escapedDesc', '$parsedDue');"
|
||||
else
|
||||
sqlite3 "$TASKS_DB" "INSERT INTO tasks (description, due_date) VALUES ('$escapedDesc', NULL);"
|
||||
fi
|
||||
|
||||
if [[ -n "$parsedDue" ]]; then
|
||||
local formattedDate
|
||||
@@ -117,58 +164,42 @@ add_task() {
|
||||
}
|
||||
|
||||
list_tasks() {
|
||||
if [[ ! -f "$TASKS_FILE" ]] || [[ ! -s "$TASKS_FILE" ]]; then
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
# Check if there are any pending tasks
|
||||
local taskCount
|
||||
taskCount=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks WHERE status = 'pending';")
|
||||
|
||||
if [[ "$taskCount" -eq 0 ]]; then
|
||||
echo "No tasks found. Add some tasks to get started!"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Git 'er Done task list"
|
||||
|
||||
# Read tasks, sort by due date (tasks with dates first, then tasks without)
|
||||
local tasksWithDates=()
|
||||
local tasksWithoutDates=()
|
||||
local lineNum=1
|
||||
|
||||
while IFS='|' read -r description dueDate; do
|
||||
if [[ -n "$dueDate" ]]; then
|
||||
tasksWithDates+=("$lineNum|$description|$dueDate")
|
||||
else
|
||||
tasksWithoutDates+=("$lineNum|$description|")
|
||||
fi
|
||||
((lineNum++))
|
||||
done < "$TASKS_FILE"
|
||||
|
||||
# Sort tasks with dates by date
|
||||
if [[ ${#tasksWithDates[@]} -gt 0 ]]; then
|
||||
readarray -t tasksWithDates < <(printf '%s\n' "${tasksWithDates[@]}" | sort -t'|' -k3)
|
||||
fi
|
||||
|
||||
local displayNum=1
|
||||
|
||||
# Display tasks with dates first
|
||||
for task in "${tasksWithDates[@]}"; do
|
||||
IFS='|' read -r _ description dueDate <<< "$task"
|
||||
local formattedDate
|
||||
formattedDate=$(format_date "$dueDate")
|
||||
echo "$displayNum. $description Due by $formattedDate"
|
||||
# Get tasks with dates first, sorted by due_date
|
||||
while IFS='|' read -r description dueDate; do
|
||||
if [[ -n "$dueDate" ]]; then
|
||||
local formattedDate
|
||||
formattedDate=$(format_date "$dueDate")
|
||||
echo "$displayNum. $description Due by $formattedDate"
|
||||
else
|
||||
echo "$displayNum. $description"
|
||||
fi
|
||||
((displayNum++))
|
||||
done
|
||||
|
||||
# Then display tasks without dates
|
||||
for task in "${tasksWithoutDates[@]}"; do
|
||||
IFS='|' read -r _ description _ <<< "$task"
|
||||
echo "$displayNum. $description"
|
||||
((displayNum++))
|
||||
done
|
||||
done < <(sqlite3 "$TASKS_DB" "SELECT description, due_date FROM tasks WHERE status = 'pending' ORDER BY
|
||||
CASE WHEN due_date IS NULL THEN 1 ELSE 0 END,
|
||||
due_date ASC;")
|
||||
}
|
||||
|
||||
mark_done() {
|
||||
local taskNum="$1"
|
||||
|
||||
if [[ ! -f "$TASKS_FILE" ]] || [[ ! -s "$TASKS_FILE" ]]; then
|
||||
echo "No tasks found."
|
||||
return 1
|
||||
fi
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
if [[ ! "$taskNum" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: Task number must be a number"
|
||||
@@ -183,23 +214,22 @@ mark_done() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Remove the task from the file
|
||||
local tempFile
|
||||
tempFile=$(mktemp)
|
||||
remove_task_by_display_number "$taskNum" > "$tempFile"
|
||||
mv "$tempFile" "$TASKS_FILE"
|
||||
# Get the task ID by display number
|
||||
local taskId
|
||||
taskId=$(get_task_id_by_display_number "$taskNum")
|
||||
|
||||
echo "Task $taskNum \"$taskDescription\" marked as done and removed from list."
|
||||
# Mark task as completed with timestamp
|
||||
sqlite3 "$TASKS_DB" "UPDATE tasks SET status = 'completed', completed_at = CURRENT_TIMESTAMP WHERE id = $taskId;"
|
||||
|
||||
echo "Task $taskNum \"$taskDescription\" marked as done."
|
||||
}
|
||||
|
||||
reschedule_task() {
|
||||
local taskNum="$1"
|
||||
local newDate="$2"
|
||||
|
||||
if [[ ! -f "$TASKS_FILE" ]] || [[ ! -s "$TASKS_FILE" ]]; then
|
||||
echo "No tasks found."
|
||||
return 1
|
||||
fi
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
if [[ ! "$taskNum" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: Task number must be a number"
|
||||
@@ -221,11 +251,12 @@ reschedule_task() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Update the task
|
||||
local tempFile
|
||||
tempFile=$(mktemp)
|
||||
update_task_by_display_number "$taskNum" "$parsedDate" > "$tempFile"
|
||||
mv "$tempFile" "$TASKS_FILE"
|
||||
# Get the task ID by display number
|
||||
local taskId
|
||||
taskId=$(get_task_id_by_display_number "$taskNum")
|
||||
|
||||
# Update the task's due date
|
||||
sqlite3 "$TASKS_DB" "UPDATE tasks SET due_date = '$parsedDate' WHERE id = $taskId;"
|
||||
|
||||
local formattedDate
|
||||
formattedDate=$(format_date "$parsedDate")
|
||||
@@ -234,171 +265,259 @@ reschedule_task() {
|
||||
|
||||
get_task_by_display_number() {
|
||||
local targetNum="$1"
|
||||
local currentNum=1
|
||||
|
||||
# Build sorted list like in list_tasks
|
||||
local tasksWithDates=()
|
||||
local tasksWithoutDates=()
|
||||
local lineNum=1
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
while IFS='|' read -r description dueDate; do
|
||||
if [[ -n "$dueDate" ]]; then
|
||||
tasksWithDates+=("$lineNum|$description|$dueDate")
|
||||
else
|
||||
tasksWithoutDates+=("$lineNum|$description|")
|
||||
fi
|
||||
((lineNum++))
|
||||
done < "$TASKS_FILE"
|
||||
|
||||
if [[ ${#tasksWithDates[@]} -gt 0 ]]; then
|
||||
readarray -t tasksWithDates < <(printf '%s\n' "${tasksWithDates[@]}" | sort -t'|' -k3)
|
||||
fi
|
||||
|
||||
# Check tasks with dates first
|
||||
for task in "${tasksWithDates[@]}"; do
|
||||
if [[ $currentNum -eq $targetNum ]]; then
|
||||
IFS='|' read -r _ description _ <<< "$task"
|
||||
echo "$description"
|
||||
return
|
||||
fi
|
||||
((currentNum++))
|
||||
done
|
||||
|
||||
# Then check tasks without dates
|
||||
for task in "${tasksWithoutDates[@]}"; do
|
||||
if [[ $currentNum -eq $targetNum ]]; then
|
||||
IFS='|' read -r _ description _ <<< "$task"
|
||||
echo "$description"
|
||||
return
|
||||
fi
|
||||
((currentNum++))
|
||||
done
|
||||
# Get the task description by row number from ordered query
|
||||
sqlite3 "$TASKS_DB" "SELECT description FROM tasks WHERE status = 'pending' ORDER BY
|
||||
CASE WHEN due_date IS NULL THEN 1 ELSE 0 END,
|
||||
due_date ASC
|
||||
LIMIT 1 OFFSET $((targetNum - 1));"
|
||||
}
|
||||
|
||||
remove_task_by_display_number() {
|
||||
get_task_id_by_display_number() {
|
||||
local targetNum="$1"
|
||||
local currentNum=1
|
||||
|
||||
# Build sorted list and track original line numbers
|
||||
local tasksWithDates=()
|
||||
local tasksWithoutDates=()
|
||||
local lineNum=1
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
while IFS='|' read -r description dueDate; do
|
||||
if [[ -n "$dueDate" ]]; then
|
||||
tasksWithDates+=("$lineNum|$description|$dueDate")
|
||||
else
|
||||
tasksWithoutDates+=("$lineNum|$description|")
|
||||
fi
|
||||
((lineNum++))
|
||||
done < "$TASKS_FILE"
|
||||
|
||||
if [[ ${#tasksWithDates[@]} -gt 0 ]]; then
|
||||
readarray -t tasksWithDates < <(printf '%s\n' "${tasksWithDates[@]}" | sort -t'|' -k3)
|
||||
fi
|
||||
|
||||
local skipLine=0
|
||||
|
||||
# Find which original line to skip
|
||||
for task in "${tasksWithDates[@]}"; do
|
||||
if [[ $currentNum -eq $targetNum ]]; then
|
||||
IFS='|' read -r origLine _ _ <<< "$task"
|
||||
skipLine=$origLine
|
||||
break
|
||||
fi
|
||||
((currentNum++))
|
||||
done
|
||||
|
||||
if [[ $skipLine -eq 0 ]]; then
|
||||
for task in "${tasksWithoutDates[@]}"; do
|
||||
if [[ $currentNum -eq $targetNum ]]; then
|
||||
IFS='|' read -r origLine _ _ <<< "$task"
|
||||
skipLine=$origLine
|
||||
break
|
||||
fi
|
||||
((currentNum++))
|
||||
done
|
||||
fi
|
||||
|
||||
# Output all lines except the one to skip
|
||||
lineNum=1
|
||||
while IFS='|' read -r description dueDate; do
|
||||
if [[ $lineNum -ne $skipLine ]]; then
|
||||
echo "$description|$dueDate"
|
||||
fi
|
||||
((lineNum++))
|
||||
done < "$TASKS_FILE"
|
||||
# Get the task ID by row number from ordered query
|
||||
sqlite3 "$TASKS_DB" "SELECT id FROM tasks WHERE status = 'pending' ORDER BY
|
||||
CASE WHEN due_date IS NULL THEN 1 ELSE 0 END,
|
||||
due_date ASC
|
||||
LIMIT 1 OFFSET $((targetNum - 1));"
|
||||
}
|
||||
|
||||
update_task_by_display_number() {
|
||||
local targetNum="$1"
|
||||
local newDueDate="$2"
|
||||
local currentNum=1
|
||||
|
||||
|
||||
# Function to set database file based on -f flag
|
||||
set_db_file() {
|
||||
local fileName="$1"
|
||||
if [[ -n "$fileName" ]]; then
|
||||
TASKS_DB="$TASKS_DIR/${fileName}.db"
|
||||
fi
|
||||
}
|
||||
|
||||
# Remove a task permanently
|
||||
remove_task() {
|
||||
local taskNum="$1"
|
||||
|
||||
# Build sorted list and track original line numbers
|
||||
local tasksWithDates=()
|
||||
local tasksWithoutDates=()
|
||||
local lineNum=1
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
while IFS='|' read -r description dueDate; do
|
||||
if [[ ! "$taskNum" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: Task number must be a number"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Get the task description for confirmation
|
||||
local taskDescription
|
||||
taskDescription=$(get_task_by_display_number "$taskNum")
|
||||
if [[ -z "$taskDescription" ]]; then
|
||||
echo "Error: Task $taskNum not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Get the task ID by display number
|
||||
local taskId
|
||||
taskId=$(get_task_id_by_display_number "$taskNum")
|
||||
|
||||
# Permanently delete the task
|
||||
sqlite3 "$TASKS_DB" "DELETE FROM tasks WHERE id = $taskId;"
|
||||
|
||||
echo "Task $taskNum \"$taskDescription\" permanently removed."
|
||||
}
|
||||
|
||||
# List completed tasks only
|
||||
list_completed_tasks() {
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
# Check if there are any completed tasks
|
||||
local taskCount
|
||||
taskCount=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks WHERE status = 'completed';")
|
||||
|
||||
if [[ "$taskCount" -eq 0 ]]; then
|
||||
echo "No completed tasks found."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Completed tasks:"
|
||||
|
||||
local displayNum=1
|
||||
|
||||
# Get completed tasks sorted by completion date
|
||||
while IFS='|' read -r description dueDate completedAt; do
|
||||
local completedDate
|
||||
if [[ -n "$completedAt" ]]; then
|
||||
completedDate=$(date -d "$completedAt" "+%Y-%m-%d" 2>/dev/null || echo "$completedAt")
|
||||
else
|
||||
completedDate="unknown"
|
||||
fi
|
||||
|
||||
if [[ -n "$dueDate" ]]; then
|
||||
tasksWithDates+=("$lineNum|$description|$dueDate")
|
||||
local formattedDue
|
||||
formattedDue=$(format_date "$dueDate")
|
||||
echo "$displayNum. ✓ $description (was due $formattedDue) - completed $completedDate"
|
||||
else
|
||||
tasksWithoutDates+=("$lineNum|$description|")
|
||||
echo "$displayNum. ✓ $description - completed $completedDate"
|
||||
fi
|
||||
((lineNum++))
|
||||
done < "$TASKS_FILE"
|
||||
((displayNum++))
|
||||
done < <(sqlite3 "$TASKS_DB" "SELECT description, due_date, completed_at FROM tasks WHERE status = 'completed' ORDER BY completed_at DESC;")
|
||||
}
|
||||
|
||||
# List all tasks with status
|
||||
list_all_tasks() {
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
if [[ ${#tasksWithDates[@]} -gt 0 ]]; then
|
||||
readarray -t tasksWithDates < <(printf '%s\n' "${tasksWithDates[@]}" | sort -t'|' -k3)
|
||||
# Check if there are any tasks
|
||||
local taskCount
|
||||
taskCount=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks;")
|
||||
|
||||
if [[ "$taskCount" -eq 0 ]]; then
|
||||
echo "No tasks found."
|
||||
return
|
||||
fi
|
||||
|
||||
local updateLine=0
|
||||
local targetDescription=""
|
||||
echo "All tasks:"
|
||||
|
||||
# Find which original line to update
|
||||
for task in "${tasksWithDates[@]}"; do
|
||||
if [[ $currentNum -eq $targetNum ]]; then
|
||||
IFS='|' read -r origLine description _ <<< "$task"
|
||||
updateLine=$origLine
|
||||
targetDescription="$description"
|
||||
break
|
||||
local displayNum=1
|
||||
|
||||
# Get all tasks sorted by status (pending first), then by due date
|
||||
while IFS='|' read -r description dueDate status completedAt; do
|
||||
local statusIcon=""
|
||||
local statusText=""
|
||||
|
||||
case "$status" in
|
||||
"pending")
|
||||
statusIcon="○"
|
||||
statusText=""
|
||||
;;
|
||||
"completed")
|
||||
statusIcon="✓"
|
||||
local completedDate
|
||||
if [[ -n "$completedAt" ]]; then
|
||||
completedDate=$(date -d "$completedAt" "+%m-%d" 2>/dev/null || echo "$completedAt")
|
||||
statusText=" (done $completedDate)"
|
||||
else
|
||||
statusText=" (done)"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n "$dueDate" ]]; then
|
||||
local formattedDate
|
||||
formattedDate=$(format_date "$dueDate")
|
||||
echo "$displayNum. $statusIcon $description Due by $formattedDate$statusText"
|
||||
else
|
||||
echo "$displayNum. $statusIcon $description$statusText"
|
||||
fi
|
||||
((currentNum++))
|
||||
done
|
||||
((displayNum++))
|
||||
done < <(sqlite3 "$TASKS_DB" "SELECT description, due_date, status, completed_at FROM tasks ORDER BY
|
||||
CASE WHEN status = 'pending' THEN 0 ELSE 1 END,
|
||||
CASE WHEN due_date IS NULL THEN 1 ELSE 0 END,
|
||||
due_date ASC;")
|
||||
}
|
||||
|
||||
# Show task statistics
|
||||
show_stats() {
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
if [[ $updateLine -eq 0 ]]; then
|
||||
for task in "${tasksWithoutDates[@]}"; do
|
||||
if [[ $currentNum -eq $targetNum ]]; then
|
||||
IFS='|' read -r origLine description _ <<< "$task"
|
||||
updateLine=$origLine
|
||||
targetDescription="$description"
|
||||
break
|
||||
fi
|
||||
((currentNum++))
|
||||
done
|
||||
echo "Task Statistics:"
|
||||
echo "=================="
|
||||
|
||||
# Get basic counts
|
||||
local totalTasks pendingTasks completedTasks
|
||||
totalTasks=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks;")
|
||||
pendingTasks=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks WHERE status = 'pending';")
|
||||
completedTasks=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks WHERE status = 'completed';")
|
||||
|
||||
echo "Total tasks: $totalTasks"
|
||||
echo "Pending: $pendingTasks"
|
||||
echo "Completed: $completedTasks"
|
||||
|
||||
if [[ "$totalTasks" -gt 0 ]]; then
|
||||
local completionRate
|
||||
completionRate=$(sqlite3 "$TASKS_DB" "SELECT ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM tasks), 1) FROM tasks WHERE status = 'completed';")
|
||||
echo "Completion rate: ${completionRate}%"
|
||||
fi
|
||||
|
||||
# Output all lines, updating the target line
|
||||
lineNum=1
|
||||
while IFS='|' read -r description dueDate; do
|
||||
if [[ $lineNum -eq $updateLine ]]; then
|
||||
echo "$targetDescription|$newDueDate"
|
||||
else
|
||||
echo "$description|$dueDate"
|
||||
fi
|
||||
((lineNum++))
|
||||
done < "$TASKS_FILE"
|
||||
# Overdue tasks
|
||||
local overdueTasks
|
||||
overdueTasks=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks WHERE status = 'pending' AND due_date < date('now');")
|
||||
if [[ "$overdueTasks" -gt 0 ]]; then
|
||||
echo "⚠️ Overdue tasks: $overdueTasks"
|
||||
fi
|
||||
|
||||
# Tasks due today
|
||||
local todayTasks
|
||||
todayTasks=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks WHERE status = 'pending' AND due_date = date('now');")
|
||||
if [[ "$todayTasks" -gt 0 ]]; then
|
||||
echo "📅 Due today: $todayTasks"
|
||||
fi
|
||||
}
|
||||
|
||||
# Purge old completed tasks
|
||||
purge_old_tasks() {
|
||||
local daysOld="${1:-30}"
|
||||
|
||||
# Initialize database
|
||||
init_db
|
||||
|
||||
if [[ ! "$daysOld" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: Days must be a number"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Count tasks to be purged
|
||||
local tasksToDelete
|
||||
tasksToDelete=$(sqlite3 "$TASKS_DB" "SELECT COUNT(*) FROM tasks WHERE status = 'completed' AND completed_at < datetime('now', '-$daysOld days');")
|
||||
|
||||
if [[ "$tasksToDelete" -eq 0 ]]; then
|
||||
echo "No completed tasks older than $daysOld days found."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Found $tasksToDelete completed tasks older than $daysOld days."
|
||||
read -p "Are you sure you want to permanently delete them? (y/N): " -n 1 -r
|
||||
echo
|
||||
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
sqlite3 "$TASKS_DB" "DELETE FROM tasks WHERE status = 'completed' AND completed_at < datetime('now', '-$daysOld days');"
|
||||
echo "$tasksToDelete old completed tasks have been purged."
|
||||
else
|
||||
echo "Purge cancelled."
|
||||
fi
|
||||
}
|
||||
|
||||
# Array of command line arguments
|
||||
declare -A command=(
|
||||
[d:task_number]="Mark task as done and remove from list."
|
||||
[a]="List all tasks with their status (--all)."
|
||||
[c]="List completed tasks only (--completed)."
|
||||
[d:task_number]="Mark task as done."
|
||||
[f:file_name]="Use specified database file (without .db extension)."
|
||||
[h]="This help screen."
|
||||
[l]="List pending tasks (same as no arguments)."
|
||||
[p:days]="Remove completed tasks older than N days (--purge, default: 30)."
|
||||
[r:task_number:new_date]="Reschedule task to new date."
|
||||
[l]="List all tasks (same as no arguments)."
|
||||
[s]="Show task statistics (--stats)."
|
||||
[x:task_number]="Permanently delete a task (--remove)."
|
||||
)
|
||||
|
||||
# Parse -f flag first if present
|
||||
if [[ "$1" == "-f" ]]; then
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Error: File name required for -f option"
|
||||
exit 1
|
||||
fi
|
||||
set_db_file "$2"
|
||||
# Shift arguments to remove -f and filename
|
||||
shift 2
|
||||
fi
|
||||
|
||||
# Main script logic with improved help but simple case parsing
|
||||
case "$1" in
|
||||
"")
|
||||
@@ -414,6 +533,10 @@ case "$1" in
|
||||
fi
|
||||
mark_done "$2"
|
||||
;;
|
||||
"-f")
|
||||
echo "Error: -f flag must be used as first argument"
|
||||
exit 1
|
||||
;;
|
||||
"-r")
|
||||
if [[ -z "$2" ]] || [[ -z "$3" ]]; then
|
||||
echo "Error: Task number and new date required for -r option"
|
||||
@@ -424,6 +547,25 @@ case "$1" in
|
||||
"-l")
|
||||
list_tasks
|
||||
;;
|
||||
"--remove"|"--rm"|"-x")
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Error: Task number required for --remove option"
|
||||
exit 1
|
||||
fi
|
||||
remove_task "$2"
|
||||
;;
|
||||
"--completed"|"--done"|"-c")
|
||||
list_completed_tasks
|
||||
;;
|
||||
"--all"|"-a")
|
||||
list_all_tasks
|
||||
;;
|
||||
"--stats"|"-s")
|
||||
show_stats
|
||||
;;
|
||||
"--purge"|"-p")
|
||||
purge_old_tasks "$2"
|
||||
;;
|
||||
*)
|
||||
# Add new task
|
||||
add_task "$1" "$2"
|
||||
|
Reference in New Issue
Block a user