fix: khal and notmuch in path

This commit is contained in:
2025-09-30 00:14:47 +02:00
parent 15077fe6fa
commit 2d3f97cae1
3 changed files with 130 additions and 51 deletions

View File

@@ -1,7 +1,7 @@
import json import json
import subprocess import subprocess
import shutil import shutil
from datetime import datetime from datetime import datetime, date
from fabric.widgets.box import Box from fabric.widgets.box import Box
from fabric.widgets.label import Label from fabric.widgets.label import Label
from fabric.widgets.button import Button from fabric.widgets.button import Button
@@ -10,6 +10,17 @@ from fabric.widgets.wayland import WaylandWindow as Window
from loguru import logger from loguru import logger
from bar.config import CALENDAR from bar.config import CALENDAR
# Try to import khal as a Python library
try:
from khal.cli.main import main_khal
from khal.settings import get_config
from khal.khalendar import CalendarCollection
KHAL_AVAILABLE = True
logger.info("[Calendar] Using khal as Python library")
except ImportError:
KHAL_AVAILABLE = False
logger.info("[Calendar] khal Python library not available, falling back to subprocess")
class CalendarService: class CalendarService:
def __init__(self, update_interval=300000): # 5 minutes default def __init__(self, update_interval=300000): # 5 minutes default
@@ -64,15 +75,52 @@ class CalendarService:
"""Get cached events without triggering update""" """Get cached events without triggering update"""
return self.events return self.events
def update_events(self): def update_events_python_api(self):
"""Fetch today's events from khal""" """Fetch today's events using khal Python API"""
# Check if calendar is enabled try:
if not CALENDAR.get("enable", True): # Get khal configuration
logger.info("[Calendar] Calendar is disabled in config") config = get_config()
self.events = []
self.emit_events_changed(self.events)
return
# Create calendar collection
collection = CalendarCollection.from_calendars(
calendars=config['calendars'],
dbpath=config['sqlite']['path'],
locale=config['locale'],
color=config['default']['print_new'],
unicode_symbols=config['default']['unicode_symbols'],
default_calendar=config['default']['default_calendar'],
readonly=True
)
# Get today's events
today = date.today()
events = collection.get_events_on(today)
# Format events to match our expected structure
formatted_events = []
for event in events:
formatted_event = {
'title': str(event.summary),
'start': event.start.strftime('%m-%d %H:%M') if hasattr(event.start, 'strftime') else '',
'end': event.end.strftime('%m-%d %H:%M') if hasattr(event.end, 'strftime') else '',
'location': str(event.location) if event.location else ''
}
formatted_events.append(formatted_event)
# Sort by start time
formatted_events.sort(key=lambda e: e.get('start', ''))
self.events = formatted_events
logger.info(f"[Calendar] Found {len(self.events)} events using Python API")
self.emit_events_changed(self.events)
except Exception as e:
logger.error(f"[Calendar] Error using khal Python API: {e}")
# Fall back to subprocess method
self.update_events_subprocess()
def update_events_subprocess(self):
"""Fetch today's events using khal subprocess (fallback)"""
# Get khal path from config # Get khal path from config
khal_path = CALENDAR.get("khal_path", "khal") khal_path = CALENDAR.get("khal_path", "khal")
@@ -115,56 +163,38 @@ class CalendarService:
except json.JSONDecodeError: except json.JSONDecodeError:
continue continue
# Filter events for today - both past and upcoming self.events = all_events
now = datetime.now() logger.info(f"[Calendar] Found {len(self.events)} events using subprocess")
current_time = now.strftime("%H:%M") self.emit_events_changed(self.events)
current_date = now.strftime("%m-%d") else:
self.events = []
past_events = []
upcoming_events = []
for event in all_events:
event_date = (
event.get("start", "").split()[0] if event.get("start") else ""
)
event_start_time = (
event.get("start", "").split()[1] if event.get("start") else ""
)
event_end_time = (
event.get("end", "").split()[1] if event.get("end") else ""
)
# Only process events from today
if event_date == current_date:
if not event_end_time: # All-day events
upcoming_events.append(event)
elif event_end_time > current_time: # Haven't ended yet
upcoming_events.append(event)
elif event_end_time <= current_time: # Already ended
past_events.append(event)
# Sort past events by start time (most recent first)
past_events.sort(key=lambda e: e.get("start", ""), reverse=True)
# Take up to 3 most recent past events and up to 5 upcoming events
selected_past = past_events[:3]
selected_upcoming = upcoming_events[:5]
# Combine: past events first, then upcoming events
self.events = selected_past + selected_upcoming
logger.info(f"[Calendar] Found {len(self.events)} upcoming events")
for i, event in enumerate(self.events):
logger.info(
f"[Calendar] Event {i+1}: {event.get('title', 'No title')} at {event.get('start', 'No time')}"
)
self.emit_events_changed(self.events) self.emit_events_changed(self.events)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
logger.error(f"[Calendar] Failed to fetch events: {e}") logger.error(f"[Calendar] Failed to fetch events: {e}")
self.events = [] self.events = []
self.emit_events_changed(self.events)
except Exception as e: except Exception as e:
logger.error(f"[Calendar] Error processing events: {e}") logger.error(f"[Calendar] Error processing events: {e}")
self.events = [] self.events = []
self.emit_events_changed(self.events)
def update_events(self):
"""Fetch today's events from khal"""
# Check if calendar is enabled
if not CALENDAR.get("enable", True):
logger.info("[Calendar] Calendar is disabled in config")
self.events = []
self.emit_events_changed(self.events)
return
# Try Python API first, fall back to subprocess
if KHAL_AVAILABLE:
logger.info("[Calendar] Using khal Python API")
self.update_events_python_api()
else:
logger.info("[Calendar] Using khal subprocess")
self.update_events_subprocess()
class CalendarPopup(Window): class CalendarPopup(Window):

View File

@@ -100,6 +100,35 @@
default = { enable = false; }; default = { enable = false; };
description = "Stylix configuration passed from the stylix module"; description = "Stylix configuration passed from the stylix module";
}; };
calendar = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to enable the calendar widget";
};
khal_path = lib.mkOption {
type = lib.types.str;
default = "khal";
description = "Path to the khal binary";
};
};
notmuch = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to enable the notmuch email widget";
};
notmuch_path = lib.mkOption {
type = lib.types.str;
default = "notmuch";
description = "Path to the notmuch binary";
};
emacsclient_command = lib.mkOption {
type = lib.types.str;
default = "emacsclient";
description = "Path to the emacsclient binary";
};
};
}; };
}; };
default = { default = {
@@ -108,6 +137,15 @@
height = 40; height = 40;
window_title.enable = true; window_title.enable = true;
stylix.enable = false; stylix.enable = false;
calendar = {
enable = true;
khal_path = "khal";
};
notmuch = {
enable = true;
notmuch_path = "notmuch";
emacsclient_command = "emacsclient";
};
}; };
}; };
}; };

View File

@@ -12,6 +12,9 @@
wrapGAppsHook3, wrapGAppsHook3,
playerctl, playerctl,
webp-pixbuf-loader, webp-pixbuf-loader,
notmuch,
khal,
emacs,
... ...
}: }:
@@ -38,6 +41,8 @@ python3Packages.buildPythonApplication {
gdk-pixbuf gdk-pixbuf
playerctl playerctl
webp-pixbuf-loader webp-pixbuf-loader
notmuch
khal
]; ];
dependencies = with python3Packages; [ dependencies = with python3Packages; [
@@ -60,13 +65,19 @@ python3Packages.buildPythonApplication {
cp scripts/launcher.py $out/bin/bar cp scripts/launcher.py $out/bin/bar
chmod +x $out/bin/bar chmod +x $out/bin/bar
runHook postInstall runHook postInstall
''; '';
preFixup = '' preFixup = ''
makeWrapperArgs+=("''${gappsWrapperArgs[@]}") makeWrapperArgs+=("''${gappsWrapperArgs[@]}")
makeWrapperArgs+=(--prefix PATH : ${lib.makeBinPath [ khal notmuch emacs ]})
''; '';
passthru = {
inherit khal notmuch emacs;
};
meta = { meta = {
changelog = ""; changelog = "";
description = '' description = ''