api/run/initialization/demo_school.py
2025-11-14 14:47:19 +00:00

182 lines
7.8 KiB
Python

"""
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