Files
NxCalDav/import_events.py
Lennart J. Kurzweg (Nx2) f66f58f67f no shebang
2026-04-23 17:17:52 +02:00

124 lines
4.2 KiB
Python

import os
import argparse
import psycopg2
import yaml
from urllib.parse import urlparse
def get_config(config_path):
with open(config_path, 'r') as f:
return yaml.safe_load(f)
def count_events(data):
return data.count('BEGIN:VEVENT')
def import_events():
parser = argparse.ArgumentParser(description='Import CalDAV events from files to database.')
parser.add_argument('--config', default='config.yaml', help='Path to config.yaml')
parser.add_argument('--input', default='export', help='Input directory or file')
parser.add_argument('--user', help='Target user (filter or override)')
parser.add_argument('--calendar', help='Target calendar (filter or override)')
args = parser.parse_args()
cfg = get_config(args.config)
db_url = cfg.get('database', {}).get('url')
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
def upload_file(file_path, user_name, cal_name):
cur.execute("""
SELECT c.id, c.path
FROM calendars c
JOIN users u ON c.owner_id = u.id
WHERE u.name = %s AND c.name = %s
""", (user_name, cal_name))
res = cur.fetchone()
if not res:
print(f"Warning: Calendar '{cal_name}' for user '{user_name}' not found in DB. Skipping {file_path}")
return False
cal_id, cal_base_path = res
with open(file_path, 'r') as f:
data = f.read()
filename = os.path.basename(file_path)
obj_path = os.path.join(cal_base_path, filename)
etag = f'"{count_events(data)}"'
cur.execute("""
INSERT INTO calendar_objects (calendar_id, path, data, etag)
VALUES (%s, %s, %s, %s)
ON CONFLICT (calendar_id, path) DO UPDATE
SET data = EXCLUDED.data, etag = EXCLUDED.etag
""", (cal_id, obj_path, data, etag))
return True
# Gather all files
files_to_process = []
if os.path.isfile(args.input):
files_to_process.append(args.input)
elif os.path.isdir(args.input):
for root, _, filenames in os.walk(args.input):
for f in filenames:
if f.endswith('.ics'):
files_to_process.append(os.path.join(root, f))
else:
print(f"Error: Input '{args.input}' not found.")
return
success_count = 0
for f_path in files_to_process:
# Determine source structure
# If input is a dir, rel_path is relative to it. If file, it's just the filename.
if os.path.isdir(args.input):
rel_path = os.path.relpath(f_path, args.input)
else:
rel_path = os.path.basename(f_path)
parts = rel_path.split(os.sep)
source_user = None
source_cal = None
# Structure: user/calendar/file.ics (len 3)
if len(parts) >= 3:
source_user = parts[-3]
source_cal = parts[-2]
# Structure: calendar/file.ics (len 2)
elif len(parts) == 2:
source_cal = parts[-2]
# 1. Apply Filtering: If flag is set and we have a source value, they must match.
if args.user and source_user and args.user != source_user:
continue
if args.calendar and source_cal and args.calendar != source_cal:
continue
# 2. Determine Target: Flag overrides source.
target_user = args.user or source_user
target_cal = args.calendar or source_cal
if not target_user or not target_cal:
print(f"Skipping {f_path}: Cannot determine user/calendar. Use --user and --calendar flags.")
continue
if upload_file(f_path, target_user, target_cal):
print(f"Imported: {target_user}/{target_cal}/{os.path.basename(f_path)}")
success_count += 1
conn.commit()
cur.close()
conn.close()
print(f"Done. Successfully imported {success_count} events.")
if __name__ == "__main__":
import_events()