""" Demo school initialization module for ClassroomCopilot Creates the KevlarAI demo school """ import os import json import requests from typing import Dict, Any from modules.logger_tool import initialise_logger from modules.database.services.provisioning_service import ProvisioningService import time logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True) class DemoSchoolInitializer: """Handles demo school creation""" def __init__(self, supabase_url: str, service_role_key: str): self.supabase_url = supabase_url self.service_role_key = service_role_key self.supabase_headers = { "apikey": service_role_key, "Authorization": f"Bearer {service_role_key}", "Content-Type": "application/json" } self.provisioning_service = ProvisioningService() def create_kevlarai_school(self) -> Dict[str, Any]: """Create the KevlarAI demo school""" logger.info("Creating KevlarAI demo school...") try: # Check if KevlarAI school already exists response = self._supabase_request_with_retry( 'get', f"{self.supabase_url}/rest/v1/institutes", headers=self.supabase_headers, params={ "select": "*", "name": "eq.KevlarAI" } ) if response.status_code == 200: existing_schools = response.json() if existing_schools and len(existing_schools) > 0: logger.info("KevlarAI school already exists") school = existing_schools[0] try: self.provisioning_service.ensure_school(school["id"]) except Exception as provisioning_error: logger.warning(f"Provisioning KevlarAI school failed: {provisioning_error}") return { "success": True, "message": "KevlarAI school already exists", "school": school } # Create KevlarAI school school_data = { "name": "KevlarAI", "urn": "KEVLARAI001", "status": "active", "address": { "street": "123 Innovation Drive", "town": "Tech City", "county": "Digital County", "postcode": "TC1 2AI", "country": "United Kingdom" }, "website": "https://kevlar.ai", "metadata": { "school_type": "AI and Technology", "phase_of_education": "Secondary and Further Education", "establishment_status": "Open", "specialization": "Artificial Intelligence, Machine Learning, Robotics" } } # Insert the school response = self._supabase_request_with_retry('post', f"{self.supabase_url}/rest/v1/institutes", headers={**self.supabase_headers, "Prefer": "return=representation"}, json=school_data, params={"select": "*"}) logger.info(f"Supabase response status: {response.status_code}") logger.info(f"Supabase response headers: {dict(response.headers)}") logger.info(f"Supabase response text: {response.text}") if response.status_code in (200, 201): try: data = response.json() school = data[0] if isinstance(data, list) and data else data logger.info("Successfully created KevlarAI school") # Ensure Neo4j provisioning is in place try: self.provisioning_service.ensure_school(school["id"]) except Exception as provisioning_error: logger.warning(f"Provisioning KevlarAI school failed: {provisioning_error}") return { "success": True, "message": "Successfully created KevlarAI school", "school": school } except json.JSONDecodeError as e: logger.error(f"Failed to parse JSON response: {str(e)}") logger.error(f"Response text: {response.text}") # If the status code is successful but we can't parse JSON, # the school was likely created successfully return { "success": True, "message": "Successfully created KevlarAI school (response not JSON)", "school": None } else: logger.error(f"Failed to create KevlarAI school: {response.text}") return { "success": False, "message": f"Failed to create KevlarAI school: {response.text}" } except Exception as e: logger.error(f"Error creating KevlarAI school: {str(e)}") return { "success": False, "message": f"Error creating KevlarAI school: {str(e)}" } def _supabase_request_with_retry(self, method, url, **kwargs): """Make a request to Supabase with retry logic""" max_retries = 3 retry_delay = 2 # seconds for attempt in range(max_retries): try: if method.lower() == 'get': response = requests.get(url, **kwargs) elif method.lower() == 'post': response = requests.post(url, **kwargs) elif method.lower() == 'put': response = requests.put(url, **kwargs) elif method.lower() == 'delete': response = requests.delete(url, **kwargs) else: raise ValueError(f"Unsupported HTTP method: {method}") # If successful or client error (4xx), don't retry if response.status_code < 500: return response # Server error (5xx), retry after delay logger.warning(f"Supabase server error (attempt {attempt+1}/{max_retries}): {response.status_code} - {response.text}") time.sleep(retry_delay * (attempt + 1)) # Exponential backoff except requests.RequestException as e: logger.warning(f"Supabase request exception (attempt {attempt+1}/{max_retries}): {str(e)}") if attempt == max_retries - 1: raise time.sleep(retry_delay * (attempt + 1)) # If we get here, all retries failed with server errors raise requests.RequestException(f"Failed after {max_retries} attempts to {method} {url}") def initialize_demo_school() -> Dict[str, Any]: """Initialize demo school (KevlarAI)""" logger.info("Starting demo school initialization...") supabase_url = os.getenv("SUPABASE_URL") service_role_key = os.getenv("SERVICE_ROLE_KEY") if not supabase_url or not service_role_key: return {"success": False, "message": "Missing SUPABASE_URL or SERVICE_ROLE_KEY environment variables"} initializer = DemoSchoolInitializer(supabase_url, service_role_key) # Create KevlarAI school result = initializer.create_kevlarai_school() if result["success"]: logger.info("Demo school initialization completed successfully") else: logger.error(f"Demo school initialization failed: {result['message']}") return result