diff --git a/stats.sh b/stats.sh index a3be0f6..4b96cf9 100755 --- a/stats.sh +++ b/stats.sh @@ -1,119 +1,298 @@ -#!/bin/bash +#!/usr/bin/env bash +set -euo pipefail -DB="$HOME/.local/share/mindwtr/mindwtr.db" +DB="${HOME}/.local/share/mindwtr/mindwtr.db" +[[ -f "$DB" ]] || { echo "Fehler: DB nicht gefunden: $DB" >&2; exit 1; } +command -v bc >/dev/null || { echo "bc fehlt" >&2; exit 1; } +command -v sqlite3 >/dev/null || { echo "sqlite3 fehlt" >&2; exit 1; } -if [ ! -f "$DB" ]; then - echo "Fehler: Datenbank nicht gefunden: $DB" >&2 - exit 1 +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +BLUE='\033[0;34m'; MAGENTA='\033[0;35m'; CYAN='\033[0;36m' +WHITE='\033[1;37m'; GRAY='\033[0;90m'; BOLD='\033[1m'; NC='\033[0m' + +TMP="/tmp/mindwtr_stats_$$.tmp" +trap 'rm -f "$TMP"' EXIT + +bar() { + local p=${1:-0} w=${2:-30} + [[ -z "$p" ]] && p=0; [[ -z "$w" ]] && w=30 + [[ ! "$p" =~ ^[0-9]+$ ]] && p=0; [[ ! "$w" =~ ^[0-9]+$ ]] && w=30 + local f=0 e=$w + (( w > 0 && p > 0 )) && f=$((w * p / 100)) && e=$((w - f)) + local c=$GREEN; [[ $p -lt 30 ]] && c=$RED; [[ $p -ge 30 && $p -lt 70 ]] && c=$YELLOW + if [[ $p -eq 0 ]]; then + printf "${GRAY}%${w}s${NC}" '' | sed 's/ /░/g' + else + printf "${c}%${f}s${NC}${GRAY}%${e}s${NC}" '' '' | sed 's/ /█/g; s/ /░/g' + fi +} + +sparkline() { + local -a v=("$@") max=0; for n in "${v[@]}"; do ((n>max)) && max=$n; done + [[ $max -eq 0 ]] && max=1 + local chars=('▁' '▂' '▃' '▄' '▅' '▆' '▇' '█') + for n in "${v[@]}"; do printf "%s" "${chars[n*7/max]}"; done +} + +hr() { printf "${GRAY}──────────────────────────────────────────────────────────────────────────────${NC}\n"; } +hr2() { printf "${GRAY}┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈${NC}\n"; } +hdr() { printf "\n${BOLD}${CYAN}%s${NC}\n" "$1"; hr; } + +header() { + clear + printf "\n${BOLD}${WHITE}%s${NC}\n" "M I N D W T R D A S H B O A R D" + printf "${GRAY}%s${NC}\n\n" "$(date '+%A, %d. %B %Y')" + hr +} + +sqlite3 "$DB" -batch <<'SQL' > "$TMP" +.headers off +.mode list +.separator | +SELECT 'open' AS k, COUNT(*) AS v FROM tasks WHERE deletedAt IS NULL AND status!='done' +UNION ALL SELECT 'done', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND status='done' +UNION ALL SELECT 'total', COUNT(*) FROM tasks WHERE deletedAt IS NULL +UNION ALL SELECT 'inbox', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND status='inbox' +UNION ALL SELECT 'next', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND status='next' +UNION ALL SELECT 'someday', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND status='someday' +UNION ALL SELECT 'overdue', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND status='next' AND dueDate IS NOT NULL AND date(dueDate)=date('now','-7 days') +UNION ALL SELECT 'done_month', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND status='done' AND date(completedAt)>=date('now','-30 days'); + +SELECT 'vel_'||date(completedAt) AS k, COUNT(*) AS v FROM tasks +WHERE deletedAt IS NULL AND status='done' AND completedAt IS NOT NULL +AND date(completedAt)>=date('now','-21 days') +GROUP BY date(completedAt) ORDER BY date(completedAt); + +SELECT p.id, p.title, p.dueDate, COUNT(t.id), SUM(CASE WHEN t.status='done' THEN 1 ELSE 0 END) +FROM projects p LEFT JOIN tasks t ON t.projectId=p.id +WHERE p.deletedAt IS NULL AND p.status='active' +GROUP BY p.id, p.title, p.dueDate ORDER BY p.dueDate IS NULL, p.dueDate; + +SELECT date(dueDate), COUNT(*), GROUP_CONCAT(title,' | ') FROM tasks +WHERE deletedAt IS NULL AND status='next' AND dueDate IS NOT NULL +AND date(dueDate) BETWEEN date('now') AND date('now','+30 days') +GROUP BY date(dueDate) ORDER BY date(dueDate); + +SELECT t.title, COALESCE(p.title,'(kein Projekt)'), t.dueDate FROM tasks t +LEFT JOIN projects p ON t.projectId=p.id +WHERE t.deletedAt IS NULL AND t.status='next' AND t.dueDate IS NOT NULL AND date(t.dueDate)/dev/null || echo "${k#vel_}")") +done < <(grep '^vel_' "$TMP") + +projects=() +while IFS='|' read -r pid ptitle pdue ptotal pdone; do + [[ -n $pid ]] && projects+=("$pid|$ptitle|$pdue|$ptotal|$pdone") +done < <(grep -E '^[a-f0-9-]{36}\|' "$TMP") + +due_timeline=() +while IFS='|' read -r date count tasks; do + [[ -n $date ]] && due_timeline+=("$date|$count|$tasks") +done < <(grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}\|' "$TMP") + +overdue_tasks=() +while IFS='|' read -r title project due; do + [[ -n $title ]] && overdue_tasks+=("$title|$project|$due") +done < <(grep -E '^[^|]*\|[^|]*\|[0-9]{4}-[0-9]{2}-[0-9]{2}$' "$TMP") + +mode_data=() +while IFS='|' read -r mode mtotal mdone_tasks mnext msomeday; do + [[ -n $mode ]] && mode_data+=("$mode|$mtotal|$mdone_tasks|$mnext|$msomeday") +done < <(grep -E '^(task|list)\|' "$TMP") + +header + +hdr "ÜBERSICHT" +pct_total=0; [[ $total -gt 0 ]] && pct_total=$((done_tasks*100/total)) +pct_week=0; [[ $((done_week+open)) -gt 0 ]] && pct_week=$((done_week*100/(done_week+open))) +pct_month=0; [[ $((done_month+open)) -gt 0 ]] && pct_month=$((done_month*100/(done_month+open))) + +b1=$(bar $pct_total 25) +b2=$(bar $pct_week 25) +b3=$(bar $pct_month 25) +printf " %s\n" "$(printf " ${BOLD}%-18s${NC} ${WHITE}%3d%%${NC} %s ${GRAY}(%d/%d)${NC}" "Gesamt" "$pct_total" "$b1" "$done_tasks" "$total")" +printf " %s\n" "$(printf " ${BOLD}%-18s${NC} ${WHITE}%3d%%${NC} %s ${GRAY}(%d/%d)${NC}" "Diese Woche" "$pct_week" "$b2" "$done_week" "$((done_week+open))")" +printf " %s\n" "$(printf " ${BOLD}%-18s${NC} ${WHITE}%3d%%${NC} %s ${GRAY}(%d/%d)${NC}" "Diesen Monat" "$pct_month" "$b3" "$done_month" "$((done_month+open))")" +printf "\n" +printf " %s\n" "$(printf " ${BOLD}Offen:${NC} ${WHITE}%d${NC} ${YELLOW}Inbox:${NC} %d ${BLUE}Next:${NC} %d ${GRAY}Someday:${NC} %d" "$open" "$inbox" "$next_tasks" "$someday")" +printf " %s\n" "$(printf " ${RED}Überfällig:${NC} %d ${YELLOW}Heute:${NC} %d ${BLUE}Diese Woche:${NC} %d ${CYAN}Nächste Woche:${NC} %d" "$overdue" "$due_today" "$due_week" "$due_next_week")" +printf " %s\n" "$(printf " ${GREEN}Aktive Projekte:${NC} %d ${CYAN}Listen:${NC} %d ${BLUE}Einzel:${NC} %d" "$active_projects" "$list_tasks" "$single_tasks")" + +hdr "PROJEKT-GESUNDHEIT" +printf " %s\n" "$(printf " ${BOLD}%-30s %6s %s${NC}" "Projekt" "Fertig" "Fortschritt")" +hr2 +for p in "${projects[@]}"; do + IFS='|' read -r id title due ptotal pdone <<< "$p" + pct=0; [[ $ptotal -gt 0 ]] && pct=$((pdone*100/ptotal)) + due_str="" + if [[ -n "$due" && "$due" != "" ]]; then + days=$(( ($(date -d "$due" +%s) - $(date +%s)) / 86400 )) + if [[ $days -lt 0 ]]; then due_str="${RED}⚠ ${days#-}d überfällig${NC}" + elif [[ $days -eq 0 ]]; then due_str="${YELLOW}⏰ heute${NC}" + elif [[ $days -le 3 ]]; then due_str="${YELLOW}📅 in ${days}d${NC}" + elif [[ $days -le 7 ]]; then due_str="${BLUE}📅 in ${days}d${NC}" + else due_str="${GREEN}📅 in ${days}d${NC}"; fi + fi + bp=$(bar $pct 18) + printf " %s\n" "$(printf " ${BOLD}%-30.30s${NC} ${WHITE}%3d%%${NC} %s ${GRAY}(%d/%d)${NC} %s" "$title" "$pct" "$bp" "$pdone" "$ptotal" "$due_str")" +done +[[ ${#projects[@]} -eq 0 ]] && printf " ${GRAY}(keine aktiven Projekte)${NC}\n" + +hdr "ÜBERFÄLLIGE AUFGABEN" +if [[ ${#overdue_tasks[@]} -eq 0 ]]; then + printf " ${GREEN}✓ Keine überfälligen Aufgaben${NC}\n" +else + printf " ${RED}⚠ ${#overdue_tasks[@]} überfällige Aufgaben:${NC}\n" + for t in "${overdue_tasks[@]}"; do + IFS='|' read -r title project due <<< "$t" + days=$(( ($(date +%s) - $(date -d "$due" +%s)) / 86400 )) + printf " %s\n" "$(printf " ${RED}•${NC} ${WHITE}%-42.42s${NC} ${GRAY}[%s]${NC} ${RED}(%d Tage)${NC}" "$title" "$project" "$days")" + done fi -echo "=== Mindwtr-Statistik ===" -echo +hdr "AUFGABEN NACH MODUS" +for m in "${mode_data[@]}"; do + IFS='|' read -r mode mtotal mdone_tasks mnext msomeday <<< "$m" + [[ $mtotal -eq 0 ]] && continue + pct=0; [[ $mtotal -gt 0 ]] && pct=$((mdone_tasks*100/mtotal)) + label="${mode^}"; [[ $mode == "list" ]] && label="Liste" + bp=$(bar $pct 22) + printf " %s\n" "$(printf " ${BOLD}%-12s${NC} ${WHITE}%3d%%${NC} %s ${GRAY}(%d/%d)${NC}" "$label:" "$pct" "$bp" "$mdone_tasks" "$mtotal")" + printf " %s\n" "$(printf " ${BLUE}Next:${NC} %-4d ${GRAY}Someday:${NC} %-4d" "$mnext" "$msomeday")" +done -# ── 1. Aufgaben-Übersicht ── -echo "── Aufgaben (aktiv) ──────────────────" -sqlite3 "$DB" " -SELECT status, COUNT(*) AS count -FROM tasks WHERE deletedAt IS NULL -GROUP BY status -ORDER BY CASE status - WHEN 'inbox' THEN 1 WHEN 'next' THEN 2 - WHEN 'someday' THEN 3 WHEN 'done' THEN 4 -END;" | awk -F'|' '{ printf " %-12s %5s\n", $1, $2 }' +hdr "NÄCHSTE 30 TAGE – FÄLLIGKEITEN" +printf " %s\n" "$(printf " ${BOLD}%-12s %4s %s${NC}" "Datum" "Anz" "Aufgaben")" +hr2 +today_ts=$(date +%s) +for dt in "${due_timeline[@]}"; do + IFS='|' read -r due_date count tasks <<< "$dt" + due_ts=$(date -d "$due_date" +%s 2>/dev/null || echo 0) + day_name=$(date -d "$due_date" '+%a %d.%m.' 2>/dev/null || echo "$due_date") + if [[ $due_ts -lt $today_ts ]]; then color=$RED; marker="⚠" + elif [[ $due_ts -eq $today_ts ]]; then color=$YELLOW; marker="●" + elif [[ $due_ts -lt $((today_ts + 259200)) ]]; then color=$YELLOW; marker="◐" + else color=$BLUE; marker="○"; fi + first_task=$(echo "$tasks" | cut -d'|' -f1 | sed 's/^ *//; s/ *$//') + [[ ${#first_task} -gt 50 ]] && first_task="${first_task:0:47}..." + printf " %s\n" "$(printf " ${color}%s %-12s${NC} ${WHITE}%3d${NC} ${GRAY}%s${NC}" "$marker" "$day_name" "$count" "$first_task")" + if [[ "$tasks" == *"|"* ]]; then + echo "$tasks" | cut -d'|' -f2- | tr '|' '\n' | while read -r t; do + t=$(echo "$t" | sed 's/^ *//; s/ *$//') + [[ ${#t} -gt 50 ]] && t="${t:0:47}..." + printf " %s\n" "$(printf " ${GRAY} └─ %s${NC}" "$t")" + done + fi +done +[[ ${#due_timeline[@]} -eq 0 ]] && printf " ${GREEN}✓ Keine anstehenden Fälligkeiten${NC}\n" -active=$(sqlite3 "$DB" "SELECT COUNT(*) FROM tasks WHERE deletedAt IS NULL AND status != 'done';") -total=$(sqlite3 "$DB" "SELECT COUNT(*) FROM tasks WHERE deletedAt IS NULL;") -echo " ─────────────────" -printf " %-12s %5s\n" "Aktiv (o. done)" "$active" -printf " %-12s %5s\n" "Gesamt" "$total" -echo +hdr "VELOCITY – LETZTE 21 TAGE" +if [[ ${#vel_data[@]} -eq 0 ]]; then + printf " ${GRAY}Keine Velocity-Daten in den letzten 21 Tagen${NC}\n" +else + for i in "${!vel_data[@]}"; do + count=${vel_data[i]}; label=${vel_labels[i]} + bar_len=$((count * 3)); [[ $bar_len -gt 40 ]] && bar_len=40 + bar_str=$(printf "${GREEN}%${bar_len}s${NC}" "" | sed 's/ /█/g') + printf " %s\n" "$(printf " ${GRAY}%s${NC} ${WHITE}%2d${NC} %s" "$label" "$count" "$bar_str")" + done + total_week1=0; total_week2=0; total_week3=0 + for i in "${!vel_data[@]}"; do + day_offset=$(( ${#vel_data[@]} - 1 - i )) + if [[ $day_offset -lt 7 ]]; then total_week1=$((total_week1 + vel_data[i])) + elif [[ $day_offset -lt 14 ]]; then total_week2=$((total_week2 + vel_data[i])) + else total_week3=$((total_week3 + vel_data[i])); fi + done + printf "\n" + printf " %s\n" "$(printf " ${BOLD}Wochen-Velocity:${NC} ${CYAN}W1:${WHITE}%2d${NC} ${CYAN}W2:${WHITE}%2d${NC} ${CYAN}W3:${WHITE}%2d${NC}" "$total_week1" "$total_week2" "$total_week3")" + trend="→"; [[ $total_week1 -gt $total_week2 ]] && trend="↑"; [[ $total_week1 -lt $total_week2 ]] && trend="↓" + avg=$(echo "scale=1; ($total_week1+$total_week2+$total_week3)/21" | bc -l 2>/dev/null || echo "?") + [[ "$avg" == .* ]] && avg="0$avg" + printf " %s\n" "$(printf " ${BOLD}Trend:${NC} ${WHITE}%s${NC} (%s/Tag Ø)" "$trend" "$avg")" +fi -# ── 2. Fälligkeiten ── -echo "── Fälligkeiten ──────────────────────" -due_overdue=$(sqlite3 "$DB" " -SELECT COUNT(*) FROM tasks -WHERE deletedAt IS NULL AND status != 'done' - AND dueDate IS NOT NULL AND dueDate != '' AND dueDate < datetime('now');") -due_week=$(sqlite3 "$DB" " -SELECT COUNT(*) FROM tasks -WHERE deletedAt IS NULL AND status != 'done' - AND dueDate IS NOT NULL AND dueDate != '' - AND dueDate >= datetime('now') AND dueDate < datetime('now', '+7 days');") -due_today=$(sqlite3 "$DB" " -SELECT COUNT(*) FROM tasks -WHERE deletedAt IS NULL AND status != 'done' - AND dueDate IS NOT NULL AND dueDate != '' - AND date(dueDate) = date('now');") -printf " %-20s %5s\n" "Überfällig" "$due_overdue" -printf " %-20s %5s\n" "Diese Woche" "$due_week" -printf " %-20s %5s\n" "Heute" "$due_today" -echo +hdr "KALENDERANSICHT – NÄCHSTE 4 WOCHEN" +weeks=4 +for w in $(seq 0 $((weeks-1))); do + week_start=$(date -d "$(date +%F) +$((w*7)) days -$(($(date -d "$(date +%F) +$((w*7)) days" +%u)-1)) days" +%F) + week_label=$(date -d "$week_start" '+%V. KW (%d.%m.)') + printf "\n ${BOLD}${CYAN}▶ %s${NC}\n" "$week_label" + for d in $(seq 0 6); do + day=$(date -d "$week_start +$d days" +%F) + day_name=$(date -d "$day" '+%a %d.') + day_ts=$(date -d "$day" +%s) + tasks_today=""; count=0 + for dt in "${due_timeline[@]}"; do + IFS='|' read -r due_date c t <<< "$dt" + [[ "$due_date" == "$day" ]] && tasks_today="$t" && count=$c && break + done + if [[ $day_ts -lt $today_ts ]]; then color=$RED; marker="✗" + elif [[ $day_ts -eq $today_ts ]]; then color=$YELLOW; marker="●" + elif [[ $count -gt 0 ]]; then color=$BLUE; marker="◆" + else color=$GRAY; marker=" "; fi + if [[ $count -gt 0 ]]; then + first=$(echo "$tasks_today" | cut -d'|' -f1 | sed 's/^ *//; s/ *$//') + [[ ${#first} -gt 48 ]] && first="${first:0:45}..." + printf " %s\n" "$(printf " ${color}%s${NC} ${BOLD}%s${NC} ${WHITE}(%d)${NC} ${GRAY}%s${NC}" "$marker" "$day_name" "$count" "$first")" + else + printf " %s\n" "$(printf " ${GRAY}%s %s${NC}" "$marker" "$day_name")" + fi + done +done -# ── 3. Heutige Aktivität ── -echo "── Heute & diese Woche ───────────────" -created_today=$(sqlite3 "$DB" "SELECT COUNT(*) FROM tasks WHERE date(createdAt) = date('now');") -updated_today=$(sqlite3 "$DB" "SELECT COUNT(*) FROM tasks WHERE date(updatedAt) = date('now');") -done_today=$(sqlite3 "$DB" "SELECT COUNT(*) FROM tasks WHERE date(completedAt) = date('now') AND deletedAt IS NULL;") -done_week=$(sqlite3 "$DB" "SELECT COUNT(*) FROM tasks WHERE completedAt >= datetime('now', '-7 days') AND deletedAt IS NULL;") -printf " %-20s %5s\n" "Heute erstellt" "$created_today" -printf " %-20s %5s\n" "Heute aktualisiert" "$updated_today" -printf " %-20s %5s\n" "Heute erledigt" "$done_today" -printf " %-20s %5s\n" "Diese Woche erledigt" "$done_week" -echo +hdr "EMPFEHLUNGEN" +recs=() +[[ $inbox -gt 5 ]] && recs+=("${YELLOW}📥 Inbox verarbeiten ($inbox Items)${NC}") +[[ $overdue -gt 0 ]] && recs+=("${RED}⚠ $overdue überfällige Aufgaben dringend prüfen${NC}") +[[ $due_today -gt 0 ]] && recs+=("${YELLOW}⏰ $due_today Aufgaben sind heute fällig${NC}") +[[ $due_week -gt 10 ]] && recs+=("${BLUE}📅 Diese Woche $due_week fällige Aufgaben – planen${NC}") +[[ $someday -gt 20 ]] && recs+=("${GRAY}💭 $someday 'Irgendwann'-Aufgaben – aufräumen?${NC}") +[[ $active_projects -gt 5 ]] && recs+=("${MAGENTA}📦 $active_projects aktive Projekte – Fokus setzen${NC}") +[[ ${#vel_data[@]} -gt 0 && $total_week1 -eq 0 ]] && recs+=("${RED}📉 Keine Aufgaben diese Woche erledigt${NC}") +for p in "${projects[@]}"; do + IFS='|' read -r id title due ptotal pdone <<< "$p" + pct=0; [[ $ptotal -gt 0 ]] && pct=$((pdone*100/ptotal)) + [[ $ptotal -gt 3 && $pct -eq 0 ]] && recs+=("${RED}🛑 Projekt '$title': 0%% Fortschritt ($ptotal Tasks)${NC}") && break +done +if [[ ${#recs[@]} -eq 0 ]]; then + printf " ${GREEN}✓ Alles im grünen Bereich!${NC}\n" +else + for r in "${recs[@]}"; do printf " $r\n"; done +fi -# ── 4. Aufgaben-Merkmale ── -echo "── Aufgaben mit ──────────────────────" -sqlite3 "$DB" " -SELECT 'Priorität', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND priority IS NOT NULL AND priority != '' -UNION ALL -SELECT 'Energielevel', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND energyLevel IS NOT NULL AND energyLevel != '' -UNION ALL -SELECT 'Fälligkeitsdatum', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND dueDate IS NOT NULL AND dueDate != '' -UNION ALL -SELECT 'Checkliste', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND checklist IS NOT NULL AND checklist != '[]' -UNION ALL -SELECT 'Wiederkehrend', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND recurrence IS NOT NULL AND recurrence != '' -UNION ALL -SELECT 'Notizen', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND description IS NOT NULL AND description != '' -UNION ALL -SELECT 'Zeitschätzung', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND timeEstimate IS NOT NULL AND timeEstimate != '' -UNION ALL -SELECT 'Heute fokussiert', COUNT(*) FROM tasks WHERE deletedAt IS NULL AND isFocusedToday = 1;" | awk -F'|' '{ printf " %-20s %5s\n", $1, $2 }' -echo - -# ── 5. Top-Kontexte ── -echo "── Top-Kontexte ──────────────────────" -sqlite3 "$DB" " -SELECT j.value AS context, COUNT(*) AS count -FROM tasks t, json_each(t.contexts) j -WHERE t.deletedAt IS NULL -GROUP BY j.value ORDER BY count DESC;" | awk -F'|' '{ printf " %-20s %5s\n", $1, $2 }' -echo - -# ── 6. Top-Schlagwörter ── -echo "── Top-Schlagwörter ──────────────────" -sqlite3 "$DB" " -SELECT j.value AS tag, COUNT(*) AS count -FROM tasks t, json_each(t.tags) j -WHERE t.deletedAt IS NULL -GROUP BY j.value ORDER BY count DESC LIMIT 15;" | awk -F'|' '{ printf " %-20s %5s\n", $1, $2 }' -echo - -# ── 7. Projekte ── -echo "── Projekte ──────────────────────────" -sqlite3 "$DB" " -SELECT COALESCE(p.title, '(kein Projekt)') AS project, COUNT(*) AS tasks -FROM tasks t LEFT JOIN projects p ON t.projectId = p.id -WHERE t.deletedAt IS NULL AND (p.deletedAt IS NULL OR p.id IS NULL) -GROUP BY project ORDER BY tasks DESC;" | awk -F'|' '{ printf " %-30s %5s\n", $1, $2 }' -echo - -# ── 8. Unvollständige Aufgaben ── -echo "── Unvollständige Aufgaben ───────────" -incomplete=$(sqlite3 "$DB" " -SELECT COUNT(*) FROM tasks t -WHERE deletedAt IS NULL - AND ((contexts IS NULL OR contexts = '[]') OR (tags IS NULL OR tags = '[]')) - AND status != 'done';") -echo " $incomplete Aufgaben ohne Kontext und/oder Schlagwort" -echo " (Details: ./incomplete.sh)" +hr +printf "${GRAY}Datenbank: %s${NC}\n" "$DB" +printf "${GRAY}Aktualisiert: %s${NC}\n" "$(date '+%H:%M:%S')" \ No newline at end of file