#!/usr/bin/env python import os import argparse import psycopg2 import yaml from urllib.parse import urlparse def get_db_url(config_path): with open(config_path, 'r') as f: cfg = yaml.safe_load(f) return cfg.get('database', {}).get('url') def export_events(): parser = argparse.ArgumentParser(description='Export CalDAV events from database to files.') parser.add_argument('--config', default='config.yaml', help='Path to config.yaml') parser.add_argument('--output', default='export', help='Output directory') parser.add_argument('--user', help='Filter by user name') parser.add_argument('--calendar', help='Filter by calendar ID (name in DB)') args = parser.parse_args() db_url = get_db_url(args.config) if not db_url: print("Error: Could not find database URL in config.") return try: conn = psycopg2.connect(db_url) cur = conn.cursor() except Exception as e: print(f"Error connecting to database: {e}") return query = """ SELECT u.name as user_name, c.name as cal_name, co.path, co.data FROM calendar_objects co JOIN calendars c ON co.calendar_id = c.id JOIN users u ON c.owner_id = u.id WHERE 1=1 """ params = [] if args.user: query += " AND u.name = %s" params.append(args.user) if args.calendar: query += " AND c.name = %s" params.append(args.calendar) cur.execute(query, params) rows = cur.fetchall() if not rows: print("No events found matching the filters.") return for user_name, cal_name, obj_path, data in rows: # Create directory structure: output/user/calendar/ target_dir = os.path.join(args.output, user_name, cal_name) os.makedirs(target_dir, exist_ok=True) # Filename from the path (the last part after /) filename = os.path.basename(obj_path) if not filename: # Should not happen with valid paths continue file_path = os.path.join(target_dir, filename) with open(file_path, 'w') as f: f.write(data) print(f"Exported: {user_name}/{cal_name}/{filename}") cur.close() conn.close() print(f"Done. Exported {len(rows)} events to '{args.output}'.") if __name__ == "__main__": export_events()