""" Calendar Sync Utilities Shared utility functions for calendar synchronization between Google Calendar and Advoware. """ import asyncpg import os import redis import time from typing import Optional, Any, List from googleapiclient.discovery import build from google.oauth2 import service_account from services.logging_utils import get_service_logger def get_logger(context=None): """Get logger for calendar sync operations""" return get_service_logger('calendar_sync', context) async def connect_db(context=None): """Connect to Postgres DB from environment variables.""" logger = get_logger(context) try: conn = await asyncpg.connect( host=os.getenv('POSTGRES_HOST', 'localhost'), user=os.getenv('POSTGRES_USER', 'calendar_sync_user'), password=os.getenv('POSTGRES_PASSWORD', 'default_password'), database=os.getenv('POSTGRES_DB_NAME', 'calendar_sync_db'), timeout=10 ) return conn except Exception as e: logger.error(f"Failed to connect to DB: {e}") raise async def get_google_service(context=None): """Initialize Google Calendar service.""" logger = get_logger(context) try: service_account_path = os.getenv('GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH', 'service-account.json') if not os.path.exists(service_account_path): raise FileNotFoundError(f"Service account file not found: {service_account_path}") scopes = ['https://www.googleapis.com/auth/calendar'] creds = service_account.Credentials.from_service_account_file( service_account_path, scopes=scopes ) service = build('calendar', 'v3', credentials=creds) return service except Exception as e: logger.error(f"Failed to initialize Google service: {e}") raise def get_redis_client(context=None) -> redis.Redis: """Initialize Redis client for calendar sync operations.""" logger = get_logger(context) try: redis_client = redis.Redis( host=os.getenv('REDIS_HOST', 'localhost'), port=int(os.getenv('REDIS_PORT', '6379')), db=int(os.getenv('REDIS_DB_CALENDAR_SYNC', '2')), socket_timeout=int(os.getenv('REDIS_TIMEOUT_SECONDS', '5')), decode_responses=True ) return redis_client except Exception as e: logger.error(f"Failed to initialize Redis client: {e}") raise async def get_advoware_employees(advoware, context=None) -> List[Any]: """Fetch list of employees from Advoware.""" logger = get_logger(context) try: result = await advoware.api_call('api/v1/advonet/Mitarbeiter', method='GET', params={'aktiv': 'true'}) employees = result if isinstance(result, list) else [] logger.info(f"Fetched {len(employees)} Advoware employees") return employees except Exception as e: logger.error(f"Failed to fetch Advoware employees: {e}") raise def set_employee_lock(redis_client: redis.Redis, kuerzel: str, triggered_by: str, context=None) -> bool: """Set lock for employee sync operation.""" logger = get_logger(context) employee_lock_key = f'calendar_sync_lock_{kuerzel}' if redis_client.set(employee_lock_key, triggered_by, ex=1800, nx=True) is None: logger.info(f"Sync already active for {kuerzel}, skipping") return False return True def clear_employee_lock(redis_client: redis.Redis, kuerzel: str, context=None) -> None: """Clear lock for employee sync operation and update last-synced timestamp.""" logger = get_logger(context) try: employee_lock_key = f'calendar_sync_lock_{kuerzel}' employee_last_synced_key = f'calendar_sync_last_synced_{kuerzel}' # Update last-synced timestamp (no TTL, persistent) current_time = int(time.time()) redis_client.set(employee_last_synced_key, current_time) # Delete the lock redis_client.delete(employee_lock_key) logger.debug(f"Cleared lock and updated last-synced for {kuerzel} to {current_time}") except Exception as e: logger.warning(f"Failed to clear lock and update last-synced for {kuerzel}: {e}")