latest
This commit is contained in:
@@ -18,7 +18,6 @@ import re
|
||||
|
||||
class ClassroomCopilotFilesystem:
|
||||
def __init__(self, db_name: str, init_run_type: str = None):
|
||||
logging.info(f"Initializing ClassroomCopilotFilesystem with db_name: {db_name} and init_run_type: {init_run_type}")
|
||||
|
||||
self.db_name = db_name
|
||||
|
||||
@@ -27,19 +26,21 @@ class ClassroomCopilotFilesystem:
|
||||
if not self.base_path:
|
||||
raise ValueError("NODE_FILESYSTEM_PATH environment variable not set")
|
||||
|
||||
logging.info(f"Initializing ClassroomCopilotFilesystem with db_name: {db_name} and init_run_type: {init_run_type} with base path: {self.base_path}")
|
||||
|
||||
# Set root path based on init type
|
||||
if init_run_type == "school":
|
||||
self.root_path = os.path.join(self.base_path, "schools", self.db_name)
|
||||
self.root_path = os.path.join(self.base_path, "schools")
|
||||
logging.debug(f"School root path: {self.root_path}")
|
||||
elif init_run_type == "user":
|
||||
self.root_path = os.path.join(self.base_path, "users", self.db_name)
|
||||
self.root_path = os.path.join(self.base_path, "users")
|
||||
logging.debug(f"User root path: {self.root_path}")
|
||||
elif init_run_type == "multiplayer":
|
||||
self.root_path = os.path.join(self.base_path, "multiplayer")
|
||||
logging.debug(f"Multiplayer root path: {self.root_path}")
|
||||
else:
|
||||
self.root_path = os.path.join(self.base_path, self.db_name)
|
||||
logging.debug(f"Default root path: {self.root_path}")
|
||||
self.root_path = self.base_path
|
||||
logging.debug(f"Database root path: {self.root_path}")
|
||||
|
||||
# Ensure root directory exists
|
||||
os.makedirs(self.root_path, exist_ok=True)
|
||||
@@ -63,45 +64,33 @@ class ClassroomCopilotFilesystem:
|
||||
return True
|
||||
return False
|
||||
|
||||
def sanitize_username(self, username):
|
||||
return re.sub(r'[^\w\-_\.]', '_', username)
|
||||
|
||||
def create_user_directory(self, username, user_type=None, school_path=None):
|
||||
def create_private_user_directory(self, user_id):
|
||||
"""Create a directory for a specific user."""
|
||||
sanitized_username = self.sanitize_username(username)
|
||||
|
||||
if school_path:
|
||||
# For school database: /schools/[school_db]/users/[user_type]/[username]
|
||||
user_path = os.path.join(self.root_path, "users", user_type, sanitized_username)
|
||||
else:
|
||||
# For user database: /users/[user_db]/[username]
|
||||
user_path = os.path.join(self.root_path, sanitized_username)
|
||||
# For user database: /users/[user_db]/[username]
|
||||
user_path = os.path.join(self.root_path, user_id)
|
||||
|
||||
logging.info(f"Creating user directory at {user_path}")
|
||||
return self.create_directory(user_path), user_path
|
||||
|
||||
def create_user_worker_directory(self, user_path, worker_code):
|
||||
def create_user_worker_directory(self, user_path, worker_id, worker_type):
|
||||
"""Create a worker directory under the user directory."""
|
||||
# Create worker directory: [user_path]/[worker_code]
|
||||
worker_path = os.path.join(user_path, worker_code)
|
||||
worker_path = os.path.join(user_path, worker_type, worker_id)
|
||||
logging.info(f"Creating worker directory at {worker_path}")
|
||||
return self.create_directory(worker_path), worker_path
|
||||
|
||||
def create_school_worker_directory(self, school_path, worker_type):
|
||||
def create_school_worker_directory(self, school_path, worker_type, worker_id):
|
||||
"""Create a worker directory under the school directory."""
|
||||
worker_path = os.path.join(school_path, "workers", worker_type)
|
||||
logging.info(f"Creating school worker directory at {worker_path}")
|
||||
worker_path = os.path.join(school_path, "workers", worker_type, worker_id)
|
||||
logging.info(f"Creating school {worker_type} worker directory at {worker_path}")
|
||||
return self.create_directory(worker_path), worker_path
|
||||
|
||||
def create_school_directory(self, school_uuid=None):
|
||||
def create_school_directory(self, school_uuid_string):
|
||||
"""Create a directory for a specific school."""
|
||||
logging.info(f"Creating school directory with school_uuid: {school_uuid}")
|
||||
if school_uuid is None:
|
||||
logging.debug(f"School UUID is None, creating school directory at {self.root_path}")
|
||||
school_path = self.root_path
|
||||
else:
|
||||
logging.debug(f"School UUID is not None, creating school directory at {os.path.join(self.root_path, school_uuid)}")
|
||||
school_path = os.path.join(self.root_path, school_uuid)
|
||||
logging.info(f"Creating school directory with uuid_string: {school_uuid_string}")
|
||||
logging.debug(f"School UUID is not None, creating school directory at {os.path.join(self.root_path, school_uuid_string)}")
|
||||
school_path = os.path.join(self.root_path, school_uuid_string)
|
||||
return self.create_directory(school_path), school_path
|
||||
|
||||
def create_year_directory(self, year, calendar_path=None):
|
||||
@@ -248,7 +237,12 @@ class ClassroomCopilotFilesystem:
|
||||
"""Create a directory for a specific topic under a year group syllabus."""
|
||||
topic_path = os.path.join(year_group_syllabus_path, "topics", f"{topic_id}")
|
||||
return self.create_directory(topic_path), topic_path
|
||||
|
||||
|
||||
def create_curriculum_keystage_topic_directory(self, keystage_syllabus_path, topic_id):
|
||||
"""Create a directory for a specific key stage topic under a key stage group syllabus."""
|
||||
topic_path = os.path.join(keystage_syllabus_path, "core_topics", f"{topic_id}")
|
||||
return self.create_directory(topic_path), topic_path
|
||||
|
||||
def create_curriculum_lesson_directory(self, topic_path, lesson_id):
|
||||
"""Create a directory for a specific lesson under a topic."""
|
||||
lesson_path = os.path.join(topic_path, "lessons", f"{lesson_id}")
|
||||
@@ -276,285 +270,4 @@ class ClassroomCopilotFilesystem:
|
||||
|
||||
def create_teacher_planned_lesson_directory(self, class_path, lesson_id):
|
||||
planned_lesson_path = os.path.join(class_path, "planned_lessons", lesson_id)
|
||||
return self.create_directory(planned_lesson_path), planned_lesson_path
|
||||
|
||||
# TLDraw File Creation
|
||||
def create_default_tldraw_file(self, node_path, node_data):
|
||||
"""Create a tldraw file for a node."""
|
||||
logging.info(f"Creating tldraw file for node at {node_path}")
|
||||
|
||||
# Ensure the directory exists
|
||||
os.makedirs(node_path, exist_ok=True)
|
||||
|
||||
tldraw_path = os.path.join(node_path, 'tldraw_file.json')
|
||||
|
||||
# Create default tldraw content
|
||||
tldraw_content = {
|
||||
"document": {
|
||||
"store": {
|
||||
"document:document": {
|
||||
"gridSize": 10,
|
||||
"name": "",
|
||||
"meta": {},
|
||||
"id": "document:document",
|
||||
"typeName": "document"
|
||||
},
|
||||
"page:page": {
|
||||
"meta": {},
|
||||
"id": "page:page",
|
||||
"name": "Page 1",
|
||||
"index": "a1",
|
||||
"typeName": "page"
|
||||
}
|
||||
},
|
||||
"schema":
|
||||
{"schemaVersion":2,
|
||||
"sequences": {
|
||||
"com.tldraw.store":4,
|
||||
"com.tldraw.asset":1,
|
||||
"com.tldraw.camera":1,
|
||||
"com.tldraw.document":2,
|
||||
"com.tldraw.instance":25,
|
||||
"com.tldraw.instance_page_state":5,
|
||||
"com.tldraw.page":1,
|
||||
"com.tldraw.instance_presence":5,
|
||||
"com.tldraw.pointer":1,
|
||||
"com.tldraw.shape":4,
|
||||
"com.tldraw.asset.bookmark":2,
|
||||
"com.tldraw.asset.image":5,
|
||||
"com.tldraw.asset.video":5,
|
||||
"com.tldraw.shape.arrow":5,
|
||||
"com.tldraw.shape.bookmark":2,
|
||||
"com.tldraw.shape.draw":2,
|
||||
"com.tldraw.shape.embed":4,
|
||||
"com.tldraw.shape.frame":0,
|
||||
"com.tldraw.shape.geo":9,
|
||||
"com.tldraw.shape.group":0,
|
||||
"com.tldraw.shape.highlight":1,
|
||||
"com.tldraw.shape.image":4,
|
||||
"com.tldraw.shape.line":5,
|
||||
"com.tldraw.shape.note":8,
|
||||
"com.tldraw.shape.text":2,
|
||||
"com.tldraw.shape.video":2,
|
||||
"com.tldraw.shape.youtube-embed":0,
|
||||
"com.tldraw.shape.calendar":0,
|
||||
"com.tldraw.shape.microphone":1,
|
||||
"com.tldraw.shape.transcriptionText":0,
|
||||
"com.tldraw.shape.slide":0,"com.tldraw.shape.slideshow":0,
|
||||
"com.tldraw.shape.user_node":1,
|
||||
"com.tldraw.shape.developer_node":1,
|
||||
"com.tldraw.shape.student_node":1,
|
||||
"com.tldraw.shape.teacher_node":1,
|
||||
"com.tldraw.shape.calendar_node":1,
|
||||
"com.tldraw.shape.calendar_year_node":1,
|
||||
"com.tldraw.shape.calendar_month_node":1,
|
||||
"com.tldraw.shape.calendar_week_node":1,
|
||||
"com.tldraw.shape.calendar_day_node":1,
|
||||
"com.tldraw.shape.calendar_time_chunk_node":1,
|
||||
"com.tldraw.shape.teacher_timetable_node":1,
|
||||
"com.tldraw.shape.timetable_lesson_node":1,
|
||||
"com.tldraw.shape.planned_lesson_node":1,
|
||||
"com.tldraw.shape.pastoral_structure_node":1,
|
||||
"com.tldraw.shape.year_group_node":1,
|
||||
"com.tldraw.shape.curriculum_structure_node":1,
|
||||
"com.tldraw.shape.key_stage_node":1,
|
||||
"com.tldraw.shape.key_stage_syllabus_node":1,
|
||||
"com.tldraw.shape.year_group_syllabus_node":1,
|
||||
"com.tldraw.shape.subject_node":1,
|
||||
"com.tldraw.shape.topic_node":1,
|
||||
"com.tldraw.shape.topic_lesson_node":1,
|
||||
"com.tldraw.shape.learning_statement_node":1,
|
||||
"com.tldraw.shape.science_lab_node":1,
|
||||
"com.tldraw.shape.school_timetable_node":1,
|
||||
"com.tldraw.shape.academic_year_node":1,
|
||||
"com.tldraw.shape.academic_term_node":1,
|
||||
"com.tldraw.shape.academic_week_node":1,
|
||||
"com.tldraw.shape.academic_day_node":1,
|
||||
"com.tldraw.shape.academic_period_node":1,
|
||||
"com.tldraw.shape.registration_period_node":1,
|
||||
"com.tldraw.shape.school_node":1,
|
||||
"com.tldraw.shape.department_node":1,
|
||||
"com.tldraw.shape.room_node":1,
|
||||
"com.tldraw.shape.subject_class_node":1,
|
||||
"com.tldraw.shape.general_relationship":1,
|
||||
"com.tldraw.binding.arrow":0,
|
||||
"com.tldraw.binding.slide-layout":0
|
||||
}
|
||||
},
|
||||
"recordVersions": {
|
||||
"asset": { "version": 1, "subTypeKey": "type", "subTypeVersions": {} },
|
||||
"camera": { "version": 1 },
|
||||
"document": { "version": 2 },
|
||||
"instance": { "version": 21 },
|
||||
"instance_page_state": { "version": 5 },
|
||||
"page": { "version": 1 },
|
||||
"shape": { "version": 3, "subTypeKey": "type", "subTypeVersions": {} },
|
||||
"instance_presence": { "version": 5 },
|
||||
"pointer": { "version": 1 }
|
||||
},
|
||||
"rootShapeIds":[],
|
||||
"bindings":[],
|
||||
"assets":[]
|
||||
},
|
||||
"session": {
|
||||
"version": 0,
|
||||
"currentPageId": "page:page",
|
||||
"pageStates": [{
|
||||
"pageId": "page:page",
|
||||
"camera": {"x": 0, "y": 0, "z": 1},
|
||||
"selectedShapeIds": []
|
||||
}]
|
||||
},
|
||||
"node_data": node_data
|
||||
}
|
||||
|
||||
with open(tldraw_path, 'w') as f:
|
||||
json.dump(tldraw_content, f, indent=4)
|
||||
|
||||
logging.info(f"tldraw file created at {tldraw_path}")
|
||||
return tldraw_path
|
||||
|
||||
def create_default_tldraw_file_in_storage(self, admin_supabase, bucket_id, file_path, node_data):
|
||||
"""Create a tldraw file in Supabase storage."""
|
||||
logging.info(f"Creating tldraw file in storage at {file_path}")
|
||||
|
||||
# Create default tldraw content
|
||||
tldraw_content = {
|
||||
"document": {
|
||||
"store": {
|
||||
"document:document": {
|
||||
"gridSize": 10,
|
||||
"name": "",
|
||||
"meta": {},
|
||||
"id": "document:document",
|
||||
"typeName": "document"
|
||||
},
|
||||
"page:page": {
|
||||
"meta": {},
|
||||
"id": "page:page",
|
||||
"name": "Page 1",
|
||||
"index": "a1",
|
||||
"typeName": "page"
|
||||
}
|
||||
},
|
||||
"schema":
|
||||
{"schemaVersion":2,
|
||||
"sequences": {
|
||||
"com.tldraw.store":4,
|
||||
"com.tldraw.asset":1,
|
||||
"com.tldraw.camera":1,
|
||||
"com.tldraw.document":2,
|
||||
"com.tldraw.instance":25,
|
||||
"com.tldraw.instance_page_state":5,
|
||||
"com.tldraw.page":1,
|
||||
"com.tldraw.instance_presence":5,
|
||||
"com.tldraw.pointer":1,
|
||||
"com.tldraw.shape":4,
|
||||
"com.tldraw.asset.bookmark":2,
|
||||
"com.tldraw.asset.image":5,
|
||||
"com.tldraw.asset.video":5,
|
||||
"com.tldraw.shape.arrow":5,
|
||||
"com.tldraw.shape.bookmark":2,
|
||||
"com.tldraw.shape.draw":2,
|
||||
"com.tldraw.shape.embed":4,
|
||||
"com.tldraw.shape.frame":0,
|
||||
"com.tldraw.shape.geo":9,
|
||||
"com.tldraw.shape.group":0,
|
||||
"com.tldraw.shape.highlight":1,
|
||||
"com.tldraw.shape.image":4,
|
||||
"com.tldraw.shape.line":5,
|
||||
"com.tldraw.shape.note":8,
|
||||
"com.tldraw.shape.text":2,
|
||||
"com.tldraw.shape.video":2,
|
||||
"com.tldraw.shape.youtube-embed":0,
|
||||
"com.tldraw.shape.calendar":0,
|
||||
"com.tldraw.shape.microphone":1,
|
||||
"com.tldraw.shape.transcriptionText":0,
|
||||
"com.tldraw.shape.slide":0,"com.tldraw.shape.slideshow":0,
|
||||
"com.tldraw.shape.user_node":1,
|
||||
"com.tldraw.shape.developer_node":1,
|
||||
"com.tldraw.shape.student_node":1,
|
||||
"com.tldraw.shape.teacher_node":1,
|
||||
"com.tldraw.shape.calendar_node":1,
|
||||
"com.tldraw.shape.calendar_year_node":1,
|
||||
"com.tldraw.shape.calendar_month_node":1,
|
||||
"com.tldraw.shape.calendar_week_node":1,
|
||||
"com.tldraw.shape.calendar_day_node":1,
|
||||
"com.tldraw.shape.calendar_time_chunk_node":1,
|
||||
"com.tldraw.shape.teacher_timetable_node":1,
|
||||
"com.tldraw.shape.timetable_lesson_node":1,
|
||||
"com.tldraw.shape.planned_lesson_node":1,
|
||||
"com.tldraw.shape.pastoral_structure_node":1,
|
||||
"com.tldraw.shape.year_group_node":1,
|
||||
"com.tldraw.shape.curriculum_structure_node":1,
|
||||
"com.tldraw.shape.key_stage_node":1,
|
||||
"com.tldraw.shape.key_stage_syllabus_node":1,
|
||||
"com.tldraw.shape.year_group_syllabus_node":1,
|
||||
"com.tldraw.shape.subject_node":1,
|
||||
"com.tldraw.shape.topic_node":1,
|
||||
"com.tldraw.shape.topic_lesson_node":1,
|
||||
"com.tldraw.shape.learning_statement_node":1,
|
||||
"com.tldraw.shape.science_lab_node":1,
|
||||
"com.tldraw.shape.school_timetable_node":1,
|
||||
"com.tldraw.shape.academic_year_node":1,
|
||||
"com.tldraw.shape.academic_term_node":1,
|
||||
"com.tldraw.shape.academic_week_node":1,
|
||||
"com.tldraw.shape.academic_day_node":1,
|
||||
"com.tldraw.shape.academic_period_node":1,
|
||||
"com.tldraw.shape.registration_period_node":1,
|
||||
"com.tldraw.shape.school_node":1,
|
||||
"com.tldraw.shape.department_node":1,
|
||||
"com.tldraw.shape.room_node":1,
|
||||
"com.tldraw.shape.subject_class_node":1,
|
||||
"com.tldraw.shape.general_relationship":1,
|
||||
"com.tldraw.binding.arrow":0,
|
||||
"com.tldraw.binding.slide-layout":0
|
||||
}
|
||||
},
|
||||
"recordVersions": {
|
||||
"asset": { "version": 1, "subTypeKey": "type", "subTypeVersions": {} },
|
||||
"camera": { "version": 1 },
|
||||
"document": { "version": 2 },
|
||||
"instance": { "version": 21 },
|
||||
"instance_page_state": { "version": 5 },
|
||||
"page": { "version": 1 },
|
||||
"shape": { "version": 3, "subTypeKey": "type", "subTypeVersions": {} },
|
||||
"instance_presence": { "version": 5 },
|
||||
"pointer": { "version": 1 }
|
||||
},
|
||||
"rootShapeIds":[],
|
||||
"bindings":[],
|
||||
"assets":[]
|
||||
},
|
||||
"session": {
|
||||
"version": 0,
|
||||
"currentPageId": "page:page",
|
||||
"pageStates": [{
|
||||
"pageId": "page:page",
|
||||
"camera": {"x": 0, "y": 0, "z": 1},
|
||||
"selectedShapeIds": []
|
||||
}]
|
||||
},
|
||||
"node_data": node_data
|
||||
}
|
||||
|
||||
# Convert the content to JSON string
|
||||
tldraw_json = json.dumps(tldraw_content, indent=4)
|
||||
|
||||
try:
|
||||
# Upload the file to Supabase storage
|
||||
result = admin_supabase.storage.from_(bucket_id).upload(
|
||||
path=file_path,
|
||||
file=tldraw_json,
|
||||
file_options={"content-type": "application/json"}
|
||||
)
|
||||
|
||||
if result.get('error'):
|
||||
logging.error(f"Error creating tldraw file in storage: {result['error']}")
|
||||
raise Exception(f"Failed to create tldraw file: {result['error']}")
|
||||
|
||||
logging.info(f"tldraw file created in storage at {file_path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"Error creating tldraw file in storage: {str(e)}")
|
||||
raise e
|
||||
return self.create_directory(planned_lesson_path), planned_lesson_path
|
||||
@@ -13,8 +13,8 @@ def get_static_nodes(context: str, db_name: str) -> List[Dict[str, Any]]:
|
||||
query = """
|
||||
MATCH (t:Teacher)
|
||||
RETURN DISTINCT {
|
||||
id: t.unique_id,
|
||||
path: t.path,
|
||||
id: t.uuid_string,
|
||||
path: t.node_storage_path,
|
||||
label: t.teacher_name_formal,
|
||||
type: 'Teacher',
|
||||
isStatic: true,
|
||||
@@ -24,8 +24,8 @@ def get_static_nodes(context: str, db_name: str) -> List[Dict[str, Any]]:
|
||||
UNION ALL
|
||||
MATCH (t:UserTeacherTimetable)
|
||||
RETURN DISTINCT {
|
||||
id: t.unique_id,
|
||||
path: t.path,
|
||||
id: t.uuid_string,
|
||||
path: t.node_storage_path,
|
||||
label: t.name,
|
||||
type: 'UserTeacherTimetable',
|
||||
isStatic: true,
|
||||
@@ -35,8 +35,8 @@ def get_static_nodes(context: str, db_name: str) -> List[Dict[str, Any]]:
|
||||
UNION ALL
|
||||
MATCH (t:UserTeacherTimetable)-[:HAS_CLASS]->(c:Class)
|
||||
RETURN DISTINCT {
|
||||
id: c.unique_id,
|
||||
path: c.path,
|
||||
id: c.uuid_string,
|
||||
path: c.node_storage_path,
|
||||
label: c.name,
|
||||
type: 'Class',
|
||||
isStatic: true,
|
||||
@@ -49,8 +49,8 @@ def get_static_nodes(context: str, db_name: str) -> List[Dict[str, Any]]:
|
||||
query = """
|
||||
MATCH (u:User)
|
||||
RETURN DISTINCT {
|
||||
id: u.unique_id,
|
||||
path: u.path,
|
||||
id: u.uuid_string,
|
||||
path: u.node_storage_path,
|
||||
label: u.user_name,
|
||||
type: 'User',
|
||||
isStatic: true,
|
||||
@@ -70,8 +70,8 @@ def get_static_nodes(context: str, db_name: str) -> List[Dict[str, Any]]:
|
||||
ELSE 1
|
||||
END as nodeOrder
|
||||
RETURN DISTINCT {
|
||||
id: n.unique_id,
|
||||
path: n.path,
|
||||
id: n.uuid_string,
|
||||
path: n.node_storage_path,
|
||||
label: n.name,
|
||||
type: 'Calendar',
|
||||
isStatic: true,
|
||||
@@ -98,7 +98,7 @@ def get_today_calendar_node(db_name: str) -> Optional[Dict[str, Any]]:
|
||||
query = """
|
||||
MATCH (n:Calendar)
|
||||
WHERE date($today) >= date(n.start_date) AND date($today) <= date(n.end_date)
|
||||
RETURN n.unique_id as id, n.path as path, n.name as label,
|
||||
RETURN n.uuid_string as id, n.path as path, n.name as label,
|
||||
'Calendar' as type
|
||||
LIMIT 1
|
||||
"""
|
||||
@@ -117,7 +117,7 @@ def get_relative_calendar_node(day_offset: int, db_name: str) -> Optional[Dict[s
|
||||
query = """
|
||||
MATCH (n:Calendar)
|
||||
WHERE date($target_date) >= date(n.start_date) AND date($target_date) <= date(n.end_date)
|
||||
RETURN n.unique_id as id, n.path as path, n.name as label,
|
||||
RETURN n.uuid_string as id, n.node_storage_path as path, n.name as label,
|
||||
'Calendar' as type
|
||||
LIMIT 1
|
||||
"""
|
||||
@@ -136,7 +136,7 @@ def get_next_month_node(db_name: str) -> Optional[Dict[str, Any]]:
|
||||
query = """
|
||||
MATCH (n:Calendar)
|
||||
WHERE date($next_month_start) >= date(n.start_date) AND date($next_month_start) <= date(n.end_date)
|
||||
RETURN n.unique_id as id, n.path as path, n.name as label,
|
||||
RETURN n.uuid_string as id, n.node_storage_path as path, n.name as label,
|
||||
'Calendar' as type
|
||||
LIMIT 1
|
||||
"""
|
||||
@@ -155,7 +155,7 @@ def get_previous_month_node(db_name: str) -> Optional[Dict[str, Any]]:
|
||||
query = """
|
||||
MATCH (n:Calendar)
|
||||
WHERE date($prev_month_start) >= date(n.start_date) AND date($prev_month_start) <= date(n.end_date)
|
||||
RETURN n.unique_id as id, n.path as path, n.name as label,
|
||||
RETURN n.uuid_string as id, n.node_storage_path as path, n.name as label,
|
||||
'Calendar' as type
|
||||
LIMIT 1
|
||||
"""
|
||||
@@ -172,7 +172,7 @@ def get_user_timetables(db_name: str) -> List[Dict[str, Any]]:
|
||||
"""Get user's timetables."""
|
||||
query = """
|
||||
MATCH (t:UserTeacherTimetable)
|
||||
RETURN t.unique_id as id, t.path as path, t.name as label,
|
||||
RETURN t.uuid_string as id, t.node_storage_path as path, t.name as label,
|
||||
'UserTeacherTimetable' as type
|
||||
"""
|
||||
try:
|
||||
@@ -186,8 +186,8 @@ def get_user_timetables(db_name: str) -> List[Dict[str, Any]]:
|
||||
def get_timetable_classes(timetable_id: str, db_name: str) -> List[Dict[str, Any]]:
|
||||
"""Get classes for a timetable."""
|
||||
query = """
|
||||
MATCH (t:UserTeacherTimetable {unique_id: $timetable_id})-[:HAS_CLASS]->(c:Class)
|
||||
RETURN c.unique_id as id, c.path as path, c.name as label,
|
||||
MATCH (t:UserTeacherTimetable {uuid_string: $timetable_id})-[:HAS_CLASS]->(c:Class)
|
||||
RETURN c.uuid_string as id, c.node_storage_path as path, c.name as label,
|
||||
'Class' as type
|
||||
"""
|
||||
try:
|
||||
@@ -202,9 +202,9 @@ def get_next_lesson(class_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get next lesson for a class."""
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
query = """
|
||||
MATCH (c:Class {unique_id: $class_id})-[:HAS_LESSON]->(l:Lesson)
|
||||
MATCH (c:Class {uuid_string: $class_id})-[:HAS_LESSON]->(l:Lesson)
|
||||
WHERE l.start_time > $now
|
||||
RETURN l.unique_id as id, l.path as path, l.name as label,
|
||||
RETURN l.uuid_string as id, l.node_storage_path as path, l.name as label,
|
||||
'Lesson' as type
|
||||
ORDER BY l.start_time ASC
|
||||
LIMIT 1
|
||||
@@ -222,9 +222,9 @@ def get_previous_lesson(class_id: str, db_name: str) -> Optional[Dict[str, Any]]
|
||||
"""Get previous lesson for a class."""
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
query = """
|
||||
MATCH (c:Class {unique_id: $class_id})-[:HAS_LESSON]->(l:Lesson)
|
||||
MATCH (c:Class {uuid_string: $class_id})-[:HAS_LESSON]->(l:Lesson)
|
||||
WHERE l.start_time < $now
|
||||
RETURN l.unique_id as id, l.path as path, l.name as label,
|
||||
RETURN l.uuid_string as id, l.path as path, l.name as label,
|
||||
'Lesson' as type
|
||||
ORDER BY l.start_time DESC
|
||||
LIMIT 1
|
||||
@@ -251,20 +251,20 @@ def save_shared_snapshot(path: str, room_id: str, snapshot: Dict[str, Any]) -> b
|
||||
def get_connected_nodes_for_workers(node_id: str, db_name: str) -> List[Dict[str, Any]]:
|
||||
"""Get connected nodes specific to the workers context."""
|
||||
query = """
|
||||
MATCH (n {unique_id: $node_id})
|
||||
MATCH (n {uuid_string: $node_id})
|
||||
WITH n
|
||||
CALL {
|
||||
WITH n
|
||||
MATCH (n:UserTeacherTimetable)-[:HAS_CLASS]->(c:Class)
|
||||
RETURN c.unique_id as id, c.path as path, c.name as label,
|
||||
RETURN c.uuid_string as id, c.node_storage_path as path, c.name as label,
|
||||
'Class' as type
|
||||
UNION
|
||||
MATCH (n:Class)<-[:HAS_CLASS]-(t:UserTeacherTimetable)
|
||||
RETURN t.unique_id as id, t.path as path, t.name as label,
|
||||
RETURN t.uuid_string as id, t.node_storage_path as path, t.name as label,
|
||||
'UserTeacherTimetable' as type
|
||||
UNION
|
||||
MATCH (n:Class)-[:HAS_LESSON]->(l:Lesson)
|
||||
RETURN l.unique_id as id, l.path as path, l.name as label,
|
||||
RETURN l.uuid_string as id, l.node_storage_path as path, l.name as label,
|
||||
'Lesson' as type
|
||||
}
|
||||
RETURN DISTINCT id, path, label, type
|
||||
@@ -284,8 +284,8 @@ def get_connected_nodes(node_id: str, db_name: str, context: str = None) -> List
|
||||
|
||||
# Default query for other contexts
|
||||
query = """
|
||||
MATCH (n {unique_id: $node_id})-[r]-(connected)
|
||||
RETURN DISTINCT connected.unique_id as id, connected.path as path,
|
||||
MATCH (n {uuid_string: $node_id})-[r]-(connected)
|
||||
RETURN DISTINCT connected.uuid_string as id, connected.path as path,
|
||||
connected.name as label, labels(connected)[0] as type
|
||||
"""
|
||||
try:
|
||||
@@ -314,37 +314,37 @@ def get_worker_structure(db_name: str) -> Dict[str, Any]:
|
||||
// Collect all nodes
|
||||
RETURN {
|
||||
schools: collect(DISTINCT {
|
||||
id: s.unique_id,
|
||||
path: s.path,
|
||||
id: s.uuid_string,
|
||||
path: s.node_storage_path,
|
||||
name: s.school_name,
|
||||
__primarylabel__: 'School'
|
||||
}),
|
||||
departments: collect(DISTINCT {
|
||||
id: d.unique_id,
|
||||
path: d.path,
|
||||
id: d.uuid_string,
|
||||
path: d.node_storage_path,
|
||||
code: d.department_code,
|
||||
school_id: s.unique_id,
|
||||
school_id: s.uuid_string,
|
||||
__primarylabel__: 'Department'
|
||||
}),
|
||||
timetables: collect(DISTINCT {
|
||||
id: t.unique_id,
|
||||
path: t.path,
|
||||
id: t.uuid_string,
|
||||
path: t.node_storage_path,
|
||||
name: t.name,
|
||||
department_id: d.unique_id,
|
||||
department_id: d.uuid_string,
|
||||
__primarylabel__: 'UserTeacherTimetable'
|
||||
}),
|
||||
classes: collect(DISTINCT {
|
||||
id: c.unique_id,
|
||||
path: c.path,
|
||||
id: c.uuid_string,
|
||||
path: c.node_storage_path,
|
||||
code: c.class_code,
|
||||
timetable_id: t.unique_id,
|
||||
timetable_id: t.uuid_string,
|
||||
__primarylabel__: 'Class'
|
||||
}),
|
||||
lessons: collect(DISTINCT {
|
||||
id: l.unique_id,
|
||||
path: l.path,
|
||||
id: l.uuid_string,
|
||||
path: l.node_storage_path,
|
||||
start_time: l.start_time,
|
||||
class_id: c.unique_id,
|
||||
class_id: c.uuid_string,
|
||||
__primarylabel__: 'TimetableLesson'
|
||||
})
|
||||
} as structure
|
||||
@@ -369,10 +369,10 @@ def get_worker_structure(db_name: str) -> Dict[str, Any]:
|
||||
def get_school_node(school_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get a specific school node."""
|
||||
query = """
|
||||
MATCH (s:School {unique_id: $school_id})
|
||||
MATCH (s:School {uuid_string: $school_id})
|
||||
RETURN {
|
||||
id: s.unique_id,
|
||||
path: s.path,
|
||||
id: s.uuid_string,
|
||||
path: s.node_storage_path,
|
||||
name: s.school_name,
|
||||
__primarylabel__: 'School'
|
||||
} as node
|
||||
@@ -389,10 +389,10 @@ def get_school_node(school_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
def get_department_node(dept_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get a specific department node."""
|
||||
query = """
|
||||
MATCH (d:Department {unique_id: $dept_id})
|
||||
MATCH (d:Department {uuid_string: $dept_id})
|
||||
RETURN {
|
||||
id: d.unique_id,
|
||||
path: d.path,
|
||||
id: d.uuid_string,
|
||||
path: d.node_storage_path,
|
||||
code: d.department_code,
|
||||
__primarylabel__: 'Department'
|
||||
} as node
|
||||
@@ -409,10 +409,10 @@ def get_department_node(dept_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
def get_timetable_node(timetable_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get a specific timetable node."""
|
||||
query = """
|
||||
MATCH (t:UserTeacherTimetable {unique_id: $timetable_id})
|
||||
MATCH (t:UserTeacherTimetable {uuid_string: $timetable_id})
|
||||
RETURN {
|
||||
id: t.unique_id,
|
||||
path: t.path,
|
||||
id: t.uuid_string,
|
||||
path: t.node_storage_path,
|
||||
name: t.name,
|
||||
__primarylabel__: 'UserTeacherTimetable'
|
||||
} as node
|
||||
@@ -429,10 +429,10 @@ def get_timetable_node(timetable_id: str, db_name: str) -> Optional[Dict[str, An
|
||||
def get_class_node(class_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get a specific class node."""
|
||||
query = """
|
||||
MATCH (c:Class {unique_id: $class_id})
|
||||
MATCH (c:Class {uuid_string: $class_id})
|
||||
RETURN {
|
||||
id: c.unique_id,
|
||||
path: c.path,
|
||||
id: c.uuid_string,
|
||||
path: c.node_storage_path,
|
||||
code: c.class_code,
|
||||
__primarylabel__: 'Class'
|
||||
} as node
|
||||
@@ -449,10 +449,10 @@ def get_class_node(class_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
def get_lesson_node(lesson_id: str, db_name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get a specific lesson node."""
|
||||
query = """
|
||||
MATCH (l:TimetableLesson {unique_id: $lesson_id})
|
||||
MATCH (l:TimetableLesson {uuid_string: $lesson_id})
|
||||
RETURN {
|
||||
id: l.unique_id,
|
||||
path: l.path,
|
||||
id: l.uuid_string,
|
||||
path: l.node_storage_path,
|
||||
start_time: l.start_time,
|
||||
__primarylabel__: 'TimetableLesson'
|
||||
} as node
|
||||
@@ -473,8 +473,8 @@ def get_current_lesson(db_name: str) -> Optional[Dict[str, Any]]:
|
||||
MATCH (l:TimetableLesson)
|
||||
WHERE l.start_time >= $now
|
||||
RETURN {
|
||||
id: l.unique_id,
|
||||
path: l.path,
|
||||
id: l.uuid_string,
|
||||
path: l.node_storage_path,
|
||||
start_time: l.start_time,
|
||||
__primarylabel__: 'TimetableLesson'
|
||||
} as node
|
||||
|
||||
@@ -15,8 +15,6 @@ logging = logger.get_logger(
|
||||
import requests
|
||||
import base64
|
||||
|
||||
dev_mode = os.getenv('DEV_MODE', 'false')
|
||||
|
||||
def send_query(query, encoded_credentials=None, params=None, method='POST', database="system", endpoint="/tx/commit"):
|
||||
if encoded_credentials is None:
|
||||
logging.debug(f"Sending query to Neo4j: {query}")
|
||||
|
||||
@@ -15,16 +15,16 @@ logging = logger.get_logger(
|
||||
import modules.database.tools.queries as query
|
||||
from contextlib import suppress
|
||||
|
||||
def get_node_by_unique_id_and_adjacent_nodes(session, unique_id):
|
||||
return session.read_transaction(_get_node_by_unique_id_and_adjacent_nodes, unique_id)
|
||||
def get_node_by_uuid_string_and_adjacent_nodes(session, uuid_string):
|
||||
return session.read_transaction(_get_node_by_uuid_string_and_adjacent_nodes, uuid_string)
|
||||
|
||||
def _get_node_by_unique_id_and_adjacent_nodes(tx, unique_id):
|
||||
def _get_node_by_uuid_string_and_adjacent_nodes(tx, uuid_string):
|
||||
query = """
|
||||
MATCH (n {unique_id: $unique_id})
|
||||
MATCH (n {uuid_string: $uuid_string})
|
||||
OPTIONAL MATCH (n)-[r]-(adjacent)
|
||||
RETURN n AS node, COLLECT(DISTINCT {node: adjacent, relationship: r}) AS connected_nodes
|
||||
"""
|
||||
result = tx.run(query, unique_id=unique_id)
|
||||
result = tx.run(query, uuid_string=uuid_string)
|
||||
record = result.single()
|
||||
if record:
|
||||
node = record["node"]
|
||||
@@ -242,20 +242,20 @@ def _find_nodes_by_label(tx, label):
|
||||
result = tx.run(query)
|
||||
return [record["n"] for record in result]
|
||||
|
||||
def get_node_by_unique_id(session, unique_id):
|
||||
return session.read_transaction(_get_node_by_unique_id, unique_id)
|
||||
def get_node_by_uuid_string(session, uuid_string):
|
||||
return session.read_transaction(_get_node_by_uuid_string, uuid_string)
|
||||
|
||||
def _get_node_by_unique_id(tx, unique_id):
|
||||
def _get_node_by_uuid_string(tx, uuid_string):
|
||||
query = f"""
|
||||
MATCH (n)
|
||||
WHERE n.unique_id = $unique_id
|
||||
WHERE n.uuid_string = $uuid_string
|
||||
RETURN n
|
||||
"""
|
||||
logging.debug(f"Executing query with unique_id: {unique_id}")
|
||||
result = tx.run(query, unique_id=unique_id)
|
||||
logging.debug(f"Executing query with uuid_string: {uuid_string}")
|
||||
result = tx.run(query, uuid_string=uuid_string)
|
||||
record = result.single()
|
||||
if record is None:
|
||||
logging.warning(f"No node found with unique_id: {unique_id}")
|
||||
logging.warning(f"No node found with uuid_string: {uuid_string}")
|
||||
return None
|
||||
return record[0]
|
||||
|
||||
|
||||
@@ -70,11 +70,10 @@ class BaseNode(CommonModel): # pyre-ignore[13]
|
||||
|
||||
def merge(self, database: str = 'neo4j') -> None:
|
||||
"""Merge this node into the graph."""
|
||||
|
||||
|
||||
params = self._get_merge_parameters()
|
||||
|
||||
all_labels = [self.__primarylabel__] + self.__secondarylabels__
|
||||
|
||||
|
||||
cypher = f"""
|
||||
MERGE (n:{":".join(all_labels)} {{ {self.__primaryproperty__}: $pp }})
|
||||
ON MATCH SET n += $set_on_match
|
||||
@@ -82,10 +81,15 @@ class BaseNode(CommonModel): # pyre-ignore[13]
|
||||
SET n += $always_set
|
||||
RETURN n
|
||||
"""
|
||||
|
||||
|
||||
print(f"DEBUG: Executing merge query: {cypher}")
|
||||
print(f"DEBUG: With params: {params}")
|
||||
print(f"DEBUG: Database: {database}")
|
||||
|
||||
graph = GraphConnection()
|
||||
with graph.driver.session(database=database) as session:
|
||||
result = session.run(cypher, params).single()
|
||||
print(f"DEBUG: Merge result: {result}")
|
||||
if result:
|
||||
return self.__class__(**dict(result["n"]))
|
||||
return None
|
||||
|
||||
@@ -60,14 +60,20 @@ def create_or_merge_neontology_node(node: BaseNode, database: str = 'neo4j', ope
|
||||
operation (str): The operation to perform ('create' or 'merge'). Defaults to 'merge'.
|
||||
"""
|
||||
try:
|
||||
logging.debug(f"Creating/merging node: {node.__class__.__name__} with label '{node.__primarylabel__}' in database '{database}'")
|
||||
logging.debug(f"Node data: {node.to_dict()}")
|
||||
|
||||
if operation == "create":
|
||||
node.create(database=database)
|
||||
result = node.create(database=database)
|
||||
logging.debug(f"Create result: {result}")
|
||||
elif operation == "merge":
|
||||
node.merge(database=database)
|
||||
result = node.merge(database=database)
|
||||
logging.debug(f"Merge result: {result}")
|
||||
else:
|
||||
logging.error(f"Invalid operation: {operation}")
|
||||
except Exception as e:
|
||||
logging.error(f"Error in processing node: {e}")
|
||||
raise # Re-raise to see the actual error
|
||||
|
||||
# Create or merge a Neontology node in the Neo4j database. If a ValidationError occurs
|
||||
# due to a NaN value, replace it with a default value and retry.
|
||||
|
||||
@@ -0,0 +1,451 @@
|
||||
"""
|
||||
Supabase Storage Tools for ClassroomCopilot
|
||||
Replaces local filesystem paths with Supabase Storage bucket paths
|
||||
"""
|
||||
import os
|
||||
from modules.logger_tool import initialise_logger
|
||||
from typing import Tuple, Optional
|
||||
|
||||
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)
|
||||
|
||||
class SupabaseStorageTools:
|
||||
"""
|
||||
Generates Supabase Storage paths for TLDraw snapshots and other files.
|
||||
|
||||
Path format: bucket/nodetype/node_unique_id
|
||||
Example: cc.snapshots/User/fda8dca7-4d18-43c9-bb74-260777043447
|
||||
"""
|
||||
|
||||
def __init__(self, db_name: str, init_run_type: str = None):
|
||||
self.db_name = db_name
|
||||
self.init_run_type = init_run_type
|
||||
|
||||
# Define bucket mappings based on node types
|
||||
self.bucket_mappings = {
|
||||
'User': 'cc.public.snapshots',
|
||||
'Teacher': 'cc.public.snapshots',
|
||||
'Student': 'cc.public.snapshots',
|
||||
'School': 'cc.public.snapshots',
|
||||
'Department': 'cc.public.snapshots',
|
||||
'Subject': 'cc.public.snapshots',
|
||||
'CalendarYear': 'cc.public.snapshots',
|
||||
'CalendarMonth': 'cc.public.snapshots',
|
||||
'CalendarWeek': 'cc.public.snapshots',
|
||||
'CalendarDay': 'cc.public.snapshots',
|
||||
'CalendarTimeChunk': 'cc.public.snapshots',
|
||||
'KeyStage': 'cc.public.snapshots',
|
||||
'YearGroup': 'cc.public.snapshots',
|
||||
'KeyStageSyllabus': 'cc.public.snapshots',
|
||||
'YearGroupSyllabus': 'cc.public.snapshots',
|
||||
'Topic': 'cc.public.snapshots',
|
||||
'TopicLesson': 'cc.public.snapshots',
|
||||
'LearningStatement': 'cc.public.snapshots',
|
||||
'UserTeacherTimetable': 'cc.public.snapshots',
|
||||
'Class': 'cc.public.snapshots',
|
||||
'TimetableLesson': 'cc.public.snapshots',
|
||||
'SuperAdmin': 'cc.public.snapshots',
|
||||
'Developer': 'cc.public.snapshots',
|
||||
'CurriculumStructure': 'cc.public.snapshots',
|
||||
'PastoralStructure': 'cc.public.snapshots',
|
||||
'DepartmentStructure': 'cc.public.snapshots',
|
||||
}
|
||||
|
||||
logger.info(f"Initializing SupabaseStorageTools with db_name: {db_name} and init_run_type: {init_run_type}")
|
||||
|
||||
def get_storage_path(self, node_type: str, node_id: str) -> str:
|
||||
"""
|
||||
Generate Supabase Storage path for a node.
|
||||
|
||||
Args:
|
||||
node_type: The type of node (e.g., 'User', 'Teacher', 'School')
|
||||
node_id: The unique identifier for the node
|
||||
|
||||
Returns:
|
||||
str: Storage path in format bucket/nodetype/node_id
|
||||
"""
|
||||
bucket = self.bucket_mappings.get(node_type, 'cc.public.snapshots')
|
||||
path = f"{bucket}/{node_type}/{node_id}"
|
||||
|
||||
logger.debug(f"Generated storage path for {node_type} {node_id}: {path}")
|
||||
return path
|
||||
|
||||
def create_user_storage_path(self, user_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a user node.
|
||||
|
||||
Args:
|
||||
user_id: The user's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('User', user_id)
|
||||
logger.info(f"Created user storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_teacher_storage_path(self, teacher_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a teacher node.
|
||||
|
||||
Args:
|
||||
teacher_id: The teacher's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('Teacher', teacher_id)
|
||||
logger.info(f"Created teacher storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_student_storage_path(self, student_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a student node.
|
||||
|
||||
Args:
|
||||
student_id: The student's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('Student', student_id)
|
||||
logger.info(f"Created student storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_school_storage_path(self, school_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a school node.
|
||||
|
||||
Args:
|
||||
school_id: The school's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('School', school_id)
|
||||
logger.info(f"Created school storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_calendar_year_storage_path(self, year: int) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a calendar year node.
|
||||
|
||||
Args:
|
||||
year: The year
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('CalendarYear', str(year))
|
||||
logger.info(f"Created calendar year storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_calendar_month_storage_path(self, year: int, month: int) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a calendar month node.
|
||||
|
||||
Args:
|
||||
year: The year
|
||||
month: The month
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
month_id = f"{year}_{month:02d}"
|
||||
path = self.get_storage_path('CalendarMonth', month_id)
|
||||
logger.info(f"Created calendar month storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_calendar_week_storage_path(self, year: int, week: int) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a calendar week node.
|
||||
|
||||
Args:
|
||||
year: The ISO year
|
||||
week: The ISO week number
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
week_id = f"{year}_{week:02d}"
|
||||
path = self.get_storage_path('CalendarWeek', week_id)
|
||||
logger.info(f"Created calendar week storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_calendar_day_storage_path(self, year: int, month: int, day: int) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a calendar day node.
|
||||
|
||||
Args:
|
||||
year: The year
|
||||
month: The month
|
||||
day: The day
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
day_id = f"{year}_{month:02d}_{day:02d}"
|
||||
path = self.get_storage_path('CalendarDay', day_id)
|
||||
logger.info(f"Created calendar day storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_calendar_time_chunk_storage_path(self, day_id: str, chunk_index: int) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a calendar time chunk node.
|
||||
|
||||
Args:
|
||||
day_id: The day identifier (e.g., "2025_01_15")
|
||||
chunk_index: The time chunk index within the day
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
chunk_id = f"{day_id}_{chunk_index:02d}"
|
||||
path = self.get_storage_path('CalendarTimeChunk', chunk_id)
|
||||
logger.info(f"Created calendar time chunk storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_department_storage_path(self, department_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a department node.
|
||||
|
||||
Args:
|
||||
department_id: The department's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('Department', department_id)
|
||||
logger.info(f"Created department storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_subject_storage_path(self, subject_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a subject node.
|
||||
|
||||
Args:
|
||||
subject_id: The subject's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('Subject', subject_id)
|
||||
logger.info(f"Created subject storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_key_stage_storage_path(self, key_stage_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a key stage node.
|
||||
|
||||
Args:
|
||||
key_stage_id: The key stage's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('KeyStage', key_stage_id)
|
||||
logger.info(f"Created key stage storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_year_group_storage_path(self, year_group_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a year group node.
|
||||
|
||||
Args:
|
||||
year_group_id: The year group's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('YearGroup', year_group_id)
|
||||
logger.info(f"Created year group storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_key_stage_syllabus_storage_path(self, syllabus_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a key stage syllabus node.
|
||||
|
||||
Args:
|
||||
syllabus_id: The syllabus's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('KeyStageSyllabus', syllabus_id)
|
||||
logger.info(f"Created key stage syllabus storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_year_group_syllabus_storage_path(self, syllabus_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a year group syllabus node.
|
||||
|
||||
Args:
|
||||
syllabus_id: The syllabus's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('YearGroupSyllabus', syllabus_id)
|
||||
logger.info(f"Created year group syllabus storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_topic_storage_path(self, topic_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a topic node.
|
||||
|
||||
Args:
|
||||
topic_id: The topic's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('Topic', topic_id)
|
||||
logger.info(f"Created topic storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_topic_lesson_storage_path(self, lesson_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a topic lesson node.
|
||||
|
||||
Args:
|
||||
lesson_id: The lesson's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('TopicLesson', lesson_id)
|
||||
logger.info(f"Created topic lesson storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_learning_statement_storage_path(self, statement_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a learning statement node.
|
||||
|
||||
Args:
|
||||
statement_id: The statement's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('LearningStatement', statement_id)
|
||||
logger.info(f"Created learning statement storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_timetable_storage_path(self, timetable_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a timetable node.
|
||||
|
||||
Args:
|
||||
timetable_id: The timetable's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('UserTeacherTimetable', timetable_id)
|
||||
logger.info(f"Created timetable storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_class_storage_path(self, class_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a class node.
|
||||
|
||||
Args:
|
||||
class_id: The class's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('Class', class_id)
|
||||
logger.info(f"Created class storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_timetable_lesson_storage_path(self, lesson_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a timetable lesson node.
|
||||
|
||||
Args:
|
||||
lesson_id: The lesson's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('TimetableLesson', lesson_id)
|
||||
logger.info(f"Created timetable lesson storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_super_admin_storage_path(self, admin_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a super admin node.
|
||||
|
||||
Args:
|
||||
admin_id: The admin's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('SuperAdmin', admin_id)
|
||||
logger.info(f"Created super admin storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_curriculum_storage_path(self, curriculum_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a curriculum structure node.
|
||||
|
||||
Args:
|
||||
curriculum_id: The curriculum's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('CurriculumStructure', curriculum_id)
|
||||
logger.info(f"Created curriculum structure storage path: {path}")
|
||||
return True, path
|
||||
|
||||
def create_pastoral_storage_path(self, pastoral_id: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Create storage path for a pastoral structure node.
|
||||
|
||||
Args:
|
||||
pastoral_id: The pastoral's unique identifier
|
||||
|
||||
Returns:
|
||||
Tuple[bool, str]: (success, storage_path)
|
||||
"""
|
||||
path = self.get_storage_path('PastoralStructure', pastoral_id)
|
||||
logger.info(f"Created pastoral structure storage path: {path}")
|
||||
return True, path
|
||||
|
||||
# Legacy compatibility methods that return the same interface as filesystem tools
|
||||
def create_private_user_directory(self, user_id: str) -> Tuple[bool, str]:
|
||||
"""Legacy compatibility method."""
|
||||
return self.create_user_storage_path(user_id)
|
||||
|
||||
def create_user_worker_directory(self, user_path: str, worker_id: str, worker_type: str) -> Tuple[bool, str]:
|
||||
"""Legacy compatibility method."""
|
||||
if worker_type in ['teacher', 'email_teacher', 'ms_teacher']:
|
||||
return self.create_teacher_storage_path(worker_id)
|
||||
elif worker_type in ['student', 'email_student', 'ms_student']:
|
||||
return self.create_student_storage_path(worker_id)
|
||||
elif worker_type == 'superadmin':
|
||||
return self.create_super_admin_storage_path(worker_id)
|
||||
elif worker_type == 'developer':
|
||||
return self.create_developer_storage_path(worker_id)
|
||||
else:
|
||||
# Default to generic storage path
|
||||
path = self.get_storage_path(worker_type.title(), worker_id)
|
||||
return True, path
|
||||
|
||||
def create_school_directory(self, school_uuid_string: str) -> Tuple[bool, str]:
|
||||
"""Legacy compatibility method."""
|
||||
return self.create_school_storage_path(school_uuid_string)
|
||||
|
||||
def create_school_curriculum_directory(self, school_path: Optional[str] = None) -> Tuple[bool, str]:
|
||||
"""Legacy compatibility method - returns empty path since curriculum is handled by individual nodes."""
|
||||
return True, ""
|
||||
|
||||
def create_school_pastoral_directory(self, school_path: Optional[str] = None) -> Tuple[bool, str]:
|
||||
"""Legacy compatibility method - returns empty path since pastoral is handled by individual nodes."""
|
||||
return True, ""
|
||||
|
||||
def create_directory(self, path: str) -> bool:
|
||||
"""Legacy compatibility method - always returns True since we don't create physical directories."""
|
||||
return True
|
||||
Reference in New Issue
Block a user