- Translated comments and docstrings from German to English for better clarity. - Improved logging consistency across various webhook handlers for create, delete, and update operations. - Centralized logging functionality by utilizing a dedicated logger utility. - Added new enums for file and XAI sync statuses in models. - Updated Redis client factory to use a centralized logger and improved error handling. - Enhanced API responses to include more descriptive messages and status codes.
114 lines
4.2 KiB
Python
114 lines
4.2 KiB
Python
"""
|
|
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}")
|