Update New Features

This commit is contained in:
Koncept Kit
2025-12-10 17:52:32 +07:00
parent 005c56b43d
commit f051976881
20 changed files with 2776 additions and 57 deletions

127
calendar_service.py Normal file
View File

@@ -0,0 +1,127 @@
"""
Calendar Service for generating iCalendar (.ics) data
Implements RFC 5545 iCalendar format for universal calendar compatibility
"""
from icalendar import Calendar, Event as iCalEvent, Alarm
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import uuid
import os
class CalendarService:
"""Service for generating iCalendar (.ics) data compatible with all calendar apps"""
def __init__(self):
self.domain = os.getenv('CALENDAR_DOMAIN', 'loaf.community')
self.timezone = ZoneInfo(os.getenv('CALENDAR_TIMEZONE', 'America/New_York'))
def generate_event_uid(self) -> str:
"""
Generate unique event identifier (UUID4 hex-encoded per RFC 7986)
Returns:
str: Unique identifier in format {uuid}@{domain}
"""
return f"{uuid.uuid4().hex}@{self.domain}"
def event_to_ical_event(self, event, include_reminder: bool = True):
"""
Convert database Event model to iCalendar Event component
Args:
event: Event model instance from database
include_reminder: Whether to add 1-hour reminder alarm
Returns:
icalendar.Event: iCalendar event component
"""
ical_event = iCalEvent()
# Required properties
ical_event.add('uid', event.calendar_uid or self.generate_event_uid())
ical_event.add('dtstamp', datetime.now(self.timezone))
ical_event.add('dtstart', event.start_at)
ical_event.add('dtend', event.end_at)
ical_event.add('summary', event.title)
# Optional properties
if event.description:
ical_event.add('description', event.description)
if event.location:
ical_event.add('location', event.location)
# Metadata
ical_event.add('url', f"https://{self.domain}/events/{event.id}")
ical_event.add('status', 'CONFIRMED')
ical_event.add('sequence', 0)
# Add 1-hour reminder (VALARM component)
if include_reminder:
alarm = Alarm()
alarm.add('action', 'DISPLAY')
alarm.add('description', f"Reminder: {event.title}")
alarm.add('trigger', timedelta(hours=-1))
ical_event.add_component(alarm)
return ical_event
def create_calendar(self, name: str, description: str = None):
"""
Create base calendar with metadata
Args:
name: Calendar name (X-WR-CALNAME)
description: Optional calendar description
Returns:
icalendar.Calendar: Base calendar object
"""
cal = Calendar()
cal.add('prodid', '-//LOAF Membership Platform//EN')
cal.add('version', '2.0')
cal.add('x-wr-calname', name)
cal.add('x-wr-timezone', str(self.timezone))
if description:
cal.add('x-wr-caldesc', description)
cal.add('method', 'PUBLISH')
cal.add('calscale', 'GREGORIAN')
return cal
def create_single_event_calendar(self, event) -> bytes:
"""
Create calendar with single event for download
Args:
event: Event model instance
Returns:
bytes: iCalendar data as bytes
"""
cal = self.create_calendar(event.title)
ical_event = self.event_to_ical_event(event)
cal.add_component(ical_event)
return cal.to_ics()
def create_subscription_feed(self, events: list, feed_name: str) -> bytes:
"""
Create calendar subscription feed with multiple events
Args:
events: List of Event model instances
feed_name: Name for the calendar feed
Returns:
bytes: iCalendar data as bytes
"""
cal = self.create_calendar(
feed_name,
description="LOAF Community Events - Auto-syncing calendar feed"
)
for event in events:
ical_event = self.event_to_ical_event(event)
cal.add_component(ical_event)
return cal.to_ics()