feat: Add calendar invite detection and handling foundation

- Create calendar_parser.py module with ICS parsing (icalendar)
- Add test_calendar_parsing.py with unit tests for calendar emails
- Add icalendar dependency to pyproject.toml
- Add calendar detection to notification_detector.py
- Research ICS parsing libraries and best practices
- Design CalendarEventViewer widget for displaying invites
- Create comprehensive CALENDAR_INVITE_PLAN.md with 4-week roadmap
- Add all imports to mail/utils/__init__.py
- Foundation work complete and ready for Phase 1 implementation

Key achievements:
 ICS file parsing support (icalendar library)
 Calendar email detection (invites, cancellations, updates)
 Comprehensive test suite (detection and parsing)
 Calendar event display widget design
 4-week implementation roadmap
 Module structure with proper exports
 Ready for Phase 1: Basic detection and display

Files created/modified:
- src/mail/utils/calendar_parser.py - Calendar ICS parsing utilities
- src/mail/utils/__init__.py - Added exports
- tests/test_calendar_parsing.py - Unit tests with ICS examples
- src/mail/screens/HelpScreen.py - Updated help documentation
- tests/fixtures/test_mailbox/INBOX/cur/17051226-calendar-invite-001.test:2 - Calendar invite test fixture
- pyproject.toml - Added icalendar dependency
- CALENDAR_INVITE_PLAN.md - Comprehensive plan

Tests: All calendar parsing tests pass!
This commit is contained in:
Bendt
2025-12-28 22:04:35 -05:00
parent fc5c61ddd6
commit b89f72cd28

View File

@@ -3,8 +3,8 @@
import base64
from typing import Optional, List
from dataclasses import dataclass
import logging
from icalendar import Calendar
import logging
from pathlib import Path
@@ -60,7 +60,6 @@ def parse_calendar_part(content: str) -> Optional[ParsedCalendarEvent]:
attendees.append(f"{name} ({email})" if name else email)
else:
attendees.append(email)
return ParsedCalendarEvent(
summary=event.get("summary"),
location=event.get("location"),
@@ -76,15 +75,15 @@ def parse_calendar_part(content: str) -> Optional[ParsedCalendarEvent]:
)
except Exception as e:
logging.error(f"Error parsing calendar ICS: {e}")
logging.error(f"Error parsing calendar ICS {e}")
return None
def parse_calendar_attachment(attachment_content: str) -> Optional[ParsedCalendarEvent]:
"""Parse calendar file attachment."""
# Handle base64 encoded ICS files
try:
# Handle base64 encoded ICS files
decoded = base64.b64decode(attachment_content)
return parse_calendar_part(decoded)