Initial commit
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
from dotenv import load_dotenv, find_dotenv
|
||||
load_dotenv(find_dotenv())
|
||||
import os
|
||||
import modules.logger_tool as logger
|
||||
log_name = 'api_routers_database_init_calendar'
|
||||
log_dir = os.getenv("LOG_PATH", "/logs") # Default path as fallback
|
||||
logging = logger.get_logger(
|
||||
name=log_name,
|
||||
log_level=os.getenv("LOG_LEVEL", "DEBUG"),
|
||||
log_path=log_dir,
|
||||
log_file=log_name,
|
||||
runtime=True,
|
||||
log_format='default'
|
||||
)
|
||||
from modules.database.tools.neontology.basenode import BaseNode
|
||||
import modules.database.init.init_calendar as init_calendar
|
||||
from fastapi import APIRouter
|
||||
from datetime import date
|
||||
from fastapi import HTTPException
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/create-calendar")
|
||||
async def create_calendar(db_name: str, start_date: date, end_date: date, attach_to_calendar_node: bool = False, entity_node: BaseNode = None):
|
||||
try:
|
||||
logging.info(f"Creating calendar for {db_name} from {start_date} to {end_date}")
|
||||
if entity_node is None:
|
||||
logging.info("No user entity node provided, proceeding without attaching to user entity.")
|
||||
return init_calendar.create_calendar(db_name, start_date, end_date, attach_to_calendar_node, entity_node)
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing request: {e}")
|
||||
raise HTTPException(status_code=422, detail=str(e))
|
||||
@@ -0,0 +1,16 @@
|
||||
import os
|
||||
from modules.logger_tool import initialise_logger
|
||||
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)
|
||||
|
||||
from fastapi import APIRouter, File, UploadFile, Form, BackgroundTasks
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/upload-class-list")
|
||||
async def upload_class_list(
|
||||
background_tasks: BackgroundTasks,
|
||||
file: UploadFile = File(...),
|
||||
user_node: str = Form(...),
|
||||
worker_node: str = Form(...)
|
||||
):
|
||||
pass
|
||||
@@ -0,0 +1,50 @@
|
||||
from dotenv import load_dotenv, find_dotenv
|
||||
load_dotenv(find_dotenv())
|
||||
import os
|
||||
import modules.logger_tool as logger
|
||||
log_name = 'api_routers_database_init_curriculum'
|
||||
log_dir = os.getenv("LOG_PATH", "/logs") # Default path as fallback
|
||||
logging = logger.get_logger(
|
||||
name=log_name,
|
||||
log_level=os.getenv("LOG_LEVEL", "DEBUG"),
|
||||
log_path=log_dir,
|
||||
log_file=log_name,
|
||||
runtime=True,
|
||||
log_format='default'
|
||||
)
|
||||
import modules.database.init.xl_tools as xl
|
||||
import modules.database.init.init_school_curriculum as init_school_curriculum
|
||||
from modules.database.schemas.nodes.schools.schools import SchoolNode
|
||||
from fastapi import APIRouter, File, UploadFile, Form
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/upload-curriculum")
|
||||
async def upload_curriculum(file: UploadFile = File(...), db_name: str = Form(...)):
|
||||
if file.content_type != 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
return {"status": "Error", "message": "Invalid file format"}
|
||||
logging.info(f"Uploading curriculum for {db_name}")
|
||||
dataframes = xl.create_dataframes_from_fastapiuploadfile(file)
|
||||
return init_school_curriculum.create_curriculum(db_name, dataframes)
|
||||
|
||||
@router.post("/upload-school-curriculum")
|
||||
async def upload_school_curriculum(
|
||||
file: UploadFile = File(...),
|
||||
db_name: str = Form(...),
|
||||
school_uuid: str = Form(...),
|
||||
school_name: str = Form(...),
|
||||
school_website: str = Form(...),
|
||||
school_path: str = Form(...)
|
||||
):
|
||||
if file.content_type != 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
return {"status": "Error", "message": "Invalid file format"}
|
||||
logging.info(f"Uploading curriculum for school {school_name} in {db_name}")
|
||||
dataframes = xl.create_dataframes_from_fastapiuploadfile(file)
|
||||
school_node = SchoolNode(
|
||||
unique_id=f'School_{school_uuid}',
|
||||
school_uuid=school_uuid,
|
||||
school_name=school_name,
|
||||
school_website=school_website,
|
||||
path=school_path
|
||||
)
|
||||
return init_school_curriculum.create_curriculum(db_name, dataframes, school_node)
|
||||
@@ -0,0 +1,280 @@
|
||||
import os
|
||||
from modules.logger_tool import initialise_logger
|
||||
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)
|
||||
import modules.database.tools.neo4j_driver_tools as driver_tools
|
||||
import modules.database.tools.neo4j_session_tools as session_tools
|
||||
import modules.database.init.init_user as init_user
|
||||
from modules.database.tools.neo4j_db_formatter import format_user_email_for_neo_db
|
||||
import modules.database.init.init_school as init_school
|
||||
import modules.database.init.init_school_timetable as init_school_timetable
|
||||
import modules.database.init.init_school_curriculum as init_school_curriculum
|
||||
import modules.database.init.xl_tools as xl
|
||||
from modules.database.schemas.nodes.schools.schools import SchoolNode, SubjectClassNode, RoomNode, DepartmentNode
|
||||
from fastapi import APIRouter, Form, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
import json
|
||||
|
||||
VALID_USER_TYPES = ['admin', 'cc_admin', 'cc_email_school_admin', 'cc_ms_school_admin', 'email_school_admin', 'ms_school_admin', 'cc_email_teacher', 'cc_ms_teacher', 'cc_email_student', 'cc_ms_student', 'email_teacher', 'ms_teacher', 'email_student', 'ms_student', 'ms_federated_teacher', 'ms_federated_student', 'standard', 'developer'] # TODO: Implement dev_ user types for pytests, consider use of cc_ user types
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Helpers
|
||||
def initialise_schools_from_config():
|
||||
"""Initialize a school with the configuration provided from env variables
|
||||
"""
|
||||
default_config = {
|
||||
"school_uuid": "kevlarai",
|
||||
"school_name": "KevlarAI School",
|
||||
"school_website": "https://kevlarai.com",
|
||||
"timetable_file": "kevlarai_data/kevlarai_timetable.xlsx",
|
||||
"curriculum_file": "kevlarai_data/kevlarai_curriculum.xlsx"
|
||||
}
|
||||
|
||||
# school_config_str = os.getenv("SCHOOL_CONFIG") # TODO: Implement this
|
||||
school_config = default_config
|
||||
|
||||
db_name = f"cc.institutes.{school_config['school_uuid']}"
|
||||
curriculum_db_name = f"{db_name}.curriculum"
|
||||
|
||||
logger.info(f"Creating database for {school_config['school_name']} using db_name: {db_name}")
|
||||
driver = driver_tools.get_driver()
|
||||
if driver is None:
|
||||
logger.error("Failed to connect to Neo4j")
|
||||
return
|
||||
|
||||
with driver.session() as session:
|
||||
# Create main school database
|
||||
session_tools.create_database(session, db_name)
|
||||
logger.debug(f"Database {db_name} created")
|
||||
|
||||
# Create curriculum database
|
||||
session_tools.create_database(session, curriculum_db_name)
|
||||
logger.debug(f"Curriculum database {curriculum_db_name} created")
|
||||
|
||||
# Add filesystem path debugging
|
||||
base_path = os.getenv("NODE_FILESYSTEM_PATH")
|
||||
schools_path = os.path.join(base_path, "schools")
|
||||
school_path = os.path.join(schools_path, f"cc.institutes.{school_config['school_uuid']}")
|
||||
|
||||
logger.debug("Filesystem paths:", {
|
||||
"base_path": base_path,
|
||||
"schools_path": schools_path,
|
||||
"school_path": school_path
|
||||
})
|
||||
|
||||
# Check if directories exist
|
||||
logger.debug("Directory existence check:", {
|
||||
"base_exists": os.path.exists(base_path),
|
||||
"schools_exists": os.path.exists(schools_path),
|
||||
"school_exists": os.path.exists(school_path)
|
||||
})
|
||||
|
||||
# Create database entry for school without timetable or curriculum
|
||||
logger.info(f"Creating school entry for {school_config['school_name']} in database {db_name} without timetable or curriculum")
|
||||
result = init_school.create_school(
|
||||
db_name=db_name,
|
||||
school_uuid=school_config["school_uuid"],
|
||||
school_name=school_config["school_name"],
|
||||
school_website=school_config["school_website"]
|
||||
)
|
||||
logger.success(f"{school_config['school_name']} school entry created successfully")
|
||||
|
||||
# Create school node from result
|
||||
school_node = result['school_node']
|
||||
refreshed_school_node = SchoolNode(
|
||||
unique_id=school_node.unique_id,
|
||||
school_uuid=school_node.school_uuid,
|
||||
school_name=school_node.school_name,
|
||||
school_website=school_node.school_website,
|
||||
path=school_node.path
|
||||
)
|
||||
|
||||
# Create timetable entries for school from Excel file
|
||||
timetable_file = os.path.join(os.getenv("BACKEND_INIT_PATH"), school_config["timetable_file"])
|
||||
|
||||
logger.info(f"Creating timetable entries for {school_config['school_name']} using timetable file: {timetable_file}.")
|
||||
school_timetable_dataframes = xl.create_dataframes(timetable_file)
|
||||
|
||||
|
||||
init_school_timetable.create_school_timetable(
|
||||
dataframes=school_timetable_dataframes,
|
||||
db_name=db_name,
|
||||
school_node=refreshed_school_node
|
||||
)
|
||||
logger.success("Timetable entries created successfully")
|
||||
|
||||
# Create curriculum entries for school from Excel file in both databases
|
||||
curriculum_file = os.path.join(os.getenv("BACKEND_INIT_PATH"), school_config["curriculum_file"])
|
||||
school_curriculum_dataframes = xl.create_dataframes(curriculum_file)
|
||||
|
||||
logger.info(f"Creating curriculum entries for {school_config['school_name']} using curriculum file: {curriculum_file}.")
|
||||
init_school_curriculum.create_curriculum(
|
||||
dataframes=school_curriculum_dataframes,
|
||||
db_name=db_name,
|
||||
curriculum_db_name=curriculum_db_name,
|
||||
school_node=refreshed_school_node
|
||||
)
|
||||
logger.success("Curriculum entries created successfully")
|
||||
|
||||
|
||||
@router.post("/create-user")
|
||||
async def create_user(
|
||||
user_id: str = Form(...),
|
||||
user_type: str = Form(...),
|
||||
user_name: str = Form(...),
|
||||
user_email: str = Form(...),
|
||||
school_uuid: str = Form(None),
|
||||
school_name: str = Form(None),
|
||||
school_website: str = Form(None),
|
||||
school_path: str = Form(None),
|
||||
worker_data: str = Form(None)
|
||||
):
|
||||
logger.info(f"Creating user with user_id: {user_id}, user_type: {user_type}, user_name: {user_name}, user_email: {user_email}")
|
||||
|
||||
if school_uuid:
|
||||
logger.info(f"School UUID provided: {school_uuid}")
|
||||
else:
|
||||
logger.info(f"No school UUID provided")
|
||||
|
||||
if school_name:
|
||||
logger.info(f"School name provided: {school_name}")
|
||||
else:
|
||||
logger.info(f"No school name provided")
|
||||
|
||||
if school_website:
|
||||
logger.info(f"School website provided: {school_website}")
|
||||
else:
|
||||
logger.info(f"No school website provided")
|
||||
|
||||
if school_path:
|
||||
logger.info(f"School path provided: {school_path}")
|
||||
else:
|
||||
logger.info(f"No school path provided")
|
||||
|
||||
if worker_data:
|
||||
logger.info(f"Worker data provided: {worker_data}")
|
||||
else:
|
||||
logger.info(f"No worker data provided")
|
||||
|
||||
# Validate inputs
|
||||
if any(param is None for param in (user_type, user_name, user_email, user_id)):
|
||||
raise HTTPException(status_code=400, detail=f"Invalid user data")
|
||||
|
||||
if user_type not in VALID_USER_TYPES:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid user type: {user_type}")
|
||||
|
||||
try:
|
||||
# Parse worker data
|
||||
worker_data_dict = json.loads(worker_data) if worker_data else None
|
||||
|
||||
# Create school node if school data provided
|
||||
school_node = None
|
||||
if all([school_uuid, school_name, school_website, school_path]):
|
||||
school_node = SchoolNode(
|
||||
unique_id=f'School_{school_uuid}',
|
||||
school_uuid=school_uuid,
|
||||
school_name=school_name,
|
||||
school_website=school_website,
|
||||
path=school_path
|
||||
)
|
||||
|
||||
# Create user with single database reference
|
||||
formatted_email = format_user_email_for_neo_db(user_email)
|
||||
user_db_name = f"cc.users.{formatted_email}"
|
||||
|
||||
result = init_user.create_user(
|
||||
db_name=user_db_name,
|
||||
user_id=user_id,
|
||||
user_type=user_type,
|
||||
username=user_name,
|
||||
email=user_email,
|
||||
school_node=school_node,
|
||||
worker_data=worker_data_dict
|
||||
)
|
||||
|
||||
# Ensure the result is JSON serializable
|
||||
response_data = {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"user_node": result['user_node'],
|
||||
"worker_node": result['worker_node'],
|
||||
"calendar_nodes": result.get('calendar_nodes')
|
||||
}
|
||||
}
|
||||
|
||||
return JSONResponse(content=response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating user in Neo4j: {str(e)}", exc_info=True)
|
||||
return JSONResponse(
|
||||
content={"status": "error", "message": str(e)},
|
||||
status_code=500
|
||||
)
|
||||
|
||||
@router.post("/create-schools")
|
||||
async def create_schools():
|
||||
initialise_schools_from_config()
|
||||
return JSONResponse(content={"status": "success", "message": "Schools created successfully"})
|
||||
|
||||
@router.post("/create-department")
|
||||
async def create_department(
|
||||
db_name: str = Form(...),
|
||||
unique_id: str = Form(...),
|
||||
department_name: str = Form(...),
|
||||
department_code: str = Form(...),
|
||||
path: str = Form(...)
|
||||
):
|
||||
if db_name is None or unique_id is None or department_name is None or department_code is None or path is None:
|
||||
logging.error(f"Invalid department data: {db_name}, {unique_id}, {department_name}, {department_code}, {path}")
|
||||
raise HTTPException(status_code=400, detail="Invalid department data")
|
||||
|
||||
department = DepartmentNode(
|
||||
unique_id=unique_id,
|
||||
department_name=department_name,
|
||||
department_code=department_code,
|
||||
path=path
|
||||
)
|
||||
|
||||
logger.info(f"Creating department {department_name} with unique_id {unique_id}")
|
||||
try:
|
||||
result = init_school.create_department(db_name, department)
|
||||
return JSONResponse(content={"status": "success", "data": result})
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating department: {str(e)}")
|
||||
return JSONResponse(content={"status": "error", "message": str(e)}, status_code=500)
|
||||
|
||||
@router.post("/create-class")
|
||||
async def create_class(
|
||||
db_name: str = Form(...),
|
||||
unique_id: str = Form(...),
|
||||
subject_class_code: str = Form(...),
|
||||
year_group: str = Form(...),
|
||||
subject: str = Form(...),
|
||||
subject_code: str = Form(...),
|
||||
path: str = Form(...)
|
||||
):
|
||||
subject_class_node = SubjectClassNode(
|
||||
unique_id=unique_id,
|
||||
subject_class_code=subject_class_code,
|
||||
year_group=year_group,
|
||||
subject=subject,
|
||||
subject_code=subject_code,
|
||||
path=path
|
||||
)
|
||||
# Implementation for creating a class
|
||||
pass
|
||||
|
||||
@router.post("/create-room")
|
||||
async def create_room(
|
||||
db_name: str = Form(...),
|
||||
room_unique_id: str = Form(...),
|
||||
room_code: str = Form(...),
|
||||
path: str = Form(...)
|
||||
):
|
||||
room = RoomNode(
|
||||
room_unique_id=room_unique_id,
|
||||
room_code=room_code,
|
||||
path=path
|
||||
)
|
||||
# Implementation for creating a room
|
||||
pass
|
||||
@@ -0,0 +1,28 @@
|
||||
from dotenv import load_dotenv, find_dotenv
|
||||
load_dotenv(find_dotenv())
|
||||
import os
|
||||
import modules.logger_tool as logger
|
||||
log_name = 'api_routers_database_init_get_data'
|
||||
log_dir = os.getenv("LOG_PATH", "/logs") # Default path as fallback
|
||||
logging = logger.get_logger(
|
||||
name=log_name,
|
||||
log_level=os.getenv("LOG_LEVEL", "DEBUG"),
|
||||
log_path=log_dir,
|
||||
log_file=log_name,
|
||||
runtime=True,
|
||||
log_format='default'
|
||||
)
|
||||
import modules.database.init.xl_tools as xl
|
||||
from fastapi import APIRouter, File, UploadFile
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/get-dataframes-from-xl")
|
||||
async def get_dataframes_from_xl(file: UploadFile = File(...)):
|
||||
if file.content_type != 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
return {"status": "Error", "message": "Invalid file format"}
|
||||
try:
|
||||
logging.info(f"Getting dataframes from {file.filename}")
|
||||
return xl.create_dataframes(await file.read())
|
||||
except Exception as e:
|
||||
return {"status": "Error", "message": str(e)}
|
||||
@@ -0,0 +1,115 @@
|
||||
from dotenv import load_dotenv, find_dotenv
|
||||
load_dotenv(find_dotenv())
|
||||
import os
|
||||
import modules.logger_tool as logger
|
||||
log_name = 'api_routers_database_init_schools'
|
||||
log_dir = os.getenv("LOG_PATH", "/logs") # Default path as fallback
|
||||
logging = logger.get_logger(
|
||||
name=log_name,
|
||||
log_level=os.getenv("LOG_LEVEL", "DEBUG"),
|
||||
log_path=log_dir,
|
||||
log_file=log_name,
|
||||
runtime=True,
|
||||
log_format='default'
|
||||
)
|
||||
from fastapi import APIRouter, File, UploadFile, Form, HTTPException, BackgroundTasks
|
||||
import pandas as pd
|
||||
import modules.database.tools.neo4j_driver_tools as driver
|
||||
from modules.database.tools.neo4j_session_tools import get_node_by_unique_id
|
||||
import modules.database.init.init_school_timetable as init_school_timetable
|
||||
import modules.database.init.init_worker_timetable as init_worker_timetable
|
||||
from modules.database.schemas.nodes.schools.schools import SchoolNode
|
||||
import modules.database.init.xl_tools as xl
|
||||
import json
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/upload-school-timetable")
|
||||
async def upload_school_timetable(
|
||||
file: UploadFile = File(...),
|
||||
db_name: str = Form(...),
|
||||
unique_id: str = Form(...),
|
||||
school_uuid: str = Form(...),
|
||||
school_name: str = Form(...),
|
||||
school_website: str = Form(...),
|
||||
path: str = Form(...)
|
||||
):
|
||||
school_node = SchoolNode(
|
||||
unique_id=unique_id,
|
||||
school_uuid=school_uuid,
|
||||
school_name=school_name,
|
||||
school_website=school_website,
|
||||
path=path
|
||||
)
|
||||
if file.content_type != 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
return {"status": "Error", "message": "Invalid file format"}
|
||||
logging.info(f"Uploading timetable for {db_name} from {file.filename}")
|
||||
dataframes = xl.create_dataframes_from_fastapiuploadfile(file)
|
||||
return init_school_timetable.create_school_timetable(dataframes, db_name, school_node)
|
||||
|
||||
@router.post("/upload-worker-timetable")
|
||||
async def upload_worker_timetable(
|
||||
background_tasks: BackgroundTasks,
|
||||
file: UploadFile = File(...),
|
||||
worker_node: str = Form(...)
|
||||
):
|
||||
if file.content_type != 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
raise HTTPException(status_code=422, detail="Invalid file format")
|
||||
|
||||
try:
|
||||
worker_node_data = json.loads(worker_node)
|
||||
logging.info(f"Uploading worker timetable for {worker_node_data['teacher_code']} from {file.filename} for {worker_node_data['worker_db_name']}")
|
||||
logging.debug(f"Worker node data: {worker_node_data}")
|
||||
|
||||
# Read file content into memory
|
||||
file_content = await file.read()
|
||||
|
||||
# Schedule the processing of the timetable in the background
|
||||
background_tasks.add_task(
|
||||
process_worker_timetable,
|
||||
file_content,
|
||||
worker_node_data
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "Accepted",
|
||||
"message": "Processing of teacher timetable started"
|
||||
}
|
||||
except Exception as e:
|
||||
logging.error(f"Error handling timetable upload: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
async def process_worker_timetable(file_content, worker_node_data):
|
||||
neo_driver = driver.get_driver(db_name=worker_node_data['worker_db_name'])
|
||||
if neo_driver is None:
|
||||
logging.error(f"Failed to connect to the database {worker_node_data['worker_db_name']}")
|
||||
return
|
||||
|
||||
try:
|
||||
# Create a DataFrame from the file content
|
||||
from io import BytesIO
|
||||
timetable_df = pd.read_excel(BytesIO(file_content))
|
||||
|
||||
# Get the school version of the worker node
|
||||
logging.info(f"Getting school worker node for {worker_node_data['unique_id']} from {worker_node_data['worker_db_name']}")
|
||||
|
||||
with neo_driver.session(database=worker_node_data['worker_db_name']) as neo_session:
|
||||
school_worker_node = get_node_by_unique_id(session=neo_session, unique_id=worker_node_data['unique_id'])
|
||||
|
||||
if school_worker_node is None:
|
||||
error_msg = f"School worker node not found for unique_id: {worker_node_data['unique_id']}"
|
||||
logging.error(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
logging.debug(f"School worker node found: {school_worker_node}")
|
||||
|
||||
logging.info(f"Initializing worker timetable for school worker: {school_worker_node['teacher_code']}")
|
||||
init_worker_timetable.init_worker_timetable(timetable_df, school_worker_node)
|
||||
logging.info(f"Worker timetable initialized for school worker: {school_worker_node['teacher_code']}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing worker timetable: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logging.info(f"Closing driver for {worker_node_data['worker_db_name']}")
|
||||
driver.close_driver(neo_driver)
|
||||
@@ -0,0 +1,166 @@
|
||||
from dotenv import load_dotenv, find_dotenv
|
||||
load_dotenv(find_dotenv())
|
||||
import os
|
||||
import modules.logger_tool as logger
|
||||
log_name = 'api_routers_database_init_timetables'
|
||||
log_dir = os.getenv("LOG_PATH", "/logs") # Default path as fallback
|
||||
logging = logger.get_logger(
|
||||
name=log_name,
|
||||
log_level=os.getenv("LOG_LEVEL", "DEBUG"),
|
||||
log_path=log_dir,
|
||||
log_file=log_name,
|
||||
runtime=True,
|
||||
log_format='default'
|
||||
)
|
||||
from fastapi import APIRouter, File, UploadFile, Form, HTTPException, BackgroundTasks
|
||||
import pandas as pd
|
||||
import modules.database.tools.neo4j_driver_tools as driver
|
||||
from modules.database.tools.neo4j_session_tools import get_node_by_unique_id
|
||||
import modules.database.init.init_school_timetable as init_school_timetable
|
||||
import modules.database.init.init_worker_timetable as init_worker_timetable
|
||||
from modules.database.schemas.nodes.users import UserNode
|
||||
from modules.database.schemas.nodes.schools.schools import SchoolNode
|
||||
from modules.database.schemas.nodes.workers.workers import TeacherNode
|
||||
import modules.database.init.xl_tools as xl
|
||||
import json
|
||||
import modules.database.tools.neontology_tools as neon
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/upload-school-timetable")
|
||||
async def upload_school_timetable(
|
||||
file: UploadFile = File(...),
|
||||
db_name: str = Form(...),
|
||||
unique_id: str = Form(...),
|
||||
school_uuid: str = Form(...),
|
||||
school_name: str = Form(...),
|
||||
school_website: str = Form(...),
|
||||
path: str = Form(...)
|
||||
):
|
||||
school_node = SchoolNode(
|
||||
unique_id=unique_id,
|
||||
school_uuid=school_uuid,
|
||||
school_name=school_name,
|
||||
school_website=school_website,
|
||||
path=path
|
||||
)
|
||||
if file.content_type != 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
return {"status": "Error", "message": "Invalid file format"}
|
||||
logging.info(f"Uploading timetable for {db_name} from {file.filename}")
|
||||
dataframes = xl.create_dataframes_from_fastapiuploadfile(file)
|
||||
return init_school_timetable.create_school_timetable(dataframes, db_name, school_node)
|
||||
|
||||
@router.post("/upload-worker-timetable")
|
||||
async def upload_worker_timetable(
|
||||
background_tasks: BackgroundTasks,
|
||||
file: UploadFile = File(...),
|
||||
user_node: str = Form(...),
|
||||
worker_node: str = Form(...)
|
||||
):
|
||||
if file.content_type != 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||
raise HTTPException(status_code=422, detail="Invalid file format")
|
||||
|
||||
try:
|
||||
worker_node_data = json.loads(worker_node)
|
||||
user_node_data = json.loads(user_node)
|
||||
logging.info(f"Uploading worker timetable for {worker_node_data['teacher_code']} from {file.filename} for {worker_node_data['worker_db_name']}")
|
||||
logging.debug(f"Worker node data: {worker_node_data}")
|
||||
logging.debug(f"User node data: {user_node_data}")
|
||||
|
||||
# Read file content into memory
|
||||
file_content = await file.read()
|
||||
|
||||
# Schedule the processing of the timetable in the background
|
||||
background_tasks.add_task(
|
||||
process_worker_timetable,
|
||||
file_content,
|
||||
user_node_data,
|
||||
worker_node_data
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "Accepted",
|
||||
"message": "Processing of teacher timetable started"
|
||||
}
|
||||
except Exception as e:
|
||||
logging.error(f"Error handling timetable upload: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
async def process_worker_timetable(file_content, user_node_data, worker_node_data):
|
||||
# Initialize neontology connection first
|
||||
neon.init_neontology_connection()
|
||||
|
||||
neo_driver = driver.get_driver(db_name=worker_node_data['worker_db_name'])
|
||||
if neo_driver is None:
|
||||
logging.error(f"Failed to connect to the database {worker_node_data['worker_db_name']}")
|
||||
return
|
||||
|
||||
try:
|
||||
# Create a DataFrame from the file content
|
||||
from io import BytesIO
|
||||
timetable_df = pd.read_excel(BytesIO(file_content))
|
||||
|
||||
# Get the school version of the worker node
|
||||
logging.info(f"Getting school worker node for {worker_node_data['unique_id']} from {worker_node_data['worker_db_name']}")
|
||||
|
||||
with neo_driver.session(database=worker_node_data['worker_db_name']) as neo_session:
|
||||
school_worker_node = get_node_by_unique_id(session=neo_session, unique_id=worker_node_data['unique_id'])
|
||||
|
||||
if school_worker_node is None:
|
||||
error_msg = f"School worker node not found for unique_id: {worker_node_data['unique_id']}"
|
||||
logging.error(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
logging.debug(f"School worker node found: {school_worker_node}")
|
||||
|
||||
# Create timetable in school database
|
||||
logging.info(f"Initializing worker timetable for school worker: {school_worker_node['teacher_code']}")
|
||||
init_worker_timetable.init_worker_timetable(timetable_df, school_worker_node)
|
||||
logging.info(f"Worker timetable initialized for school worker: {school_worker_node['teacher_code']}")
|
||||
|
||||
# Create timetable in user database
|
||||
if 'user_db_name' in worker_node_data:
|
||||
from modules.database.init.init_user_timetable import create_user_worker_timetable
|
||||
from modules.database.schemas.nodes.workers.workers import TeacherNode
|
||||
|
||||
logging.info(f"Creating user timetable structure in {worker_node_data['user_db_name']}")
|
||||
|
||||
# Create TeacherNode from worker_node_data
|
||||
user_worker_node = TeacherNode(
|
||||
unique_id=worker_node_data['unique_id'],
|
||||
teacher_code=worker_node_data['teacher_code'],
|
||||
teacher_name_formal=worker_node_data['teacher_name_formal'],
|
||||
teacher_email=worker_node_data['teacher_email'],
|
||||
path=worker_node_data['path'],
|
||||
worker_db_name=worker_node_data['worker_db_name'],
|
||||
user_db_name=worker_node_data['user_db_name']
|
||||
)
|
||||
|
||||
# Create user node
|
||||
user_node = UserNode(
|
||||
unique_id=user_node_data['unique_id'],
|
||||
user_id=user_node_data['user_id'],
|
||||
user_type=user_node_data['user_type'],
|
||||
user_name=user_node_data['user_name'],
|
||||
user_email=user_node_data['user_email'],
|
||||
path=user_node_data['path'],
|
||||
worker_node_data=user_node_data['worker_node_data']
|
||||
)
|
||||
|
||||
# Create user timetable structure
|
||||
create_user_worker_timetable(
|
||||
user_node=user_node,
|
||||
user_worker_node=user_worker_node,
|
||||
school_db_name=worker_node_data['worker_db_name']
|
||||
)
|
||||
|
||||
logging.info(f"User timetable structure created in {worker_node_data['user_db_name']}")
|
||||
else:
|
||||
logging.warning("No user_db_name provided, skipping user timetable creation")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing worker timetable: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
logging.info(f"Closing driver for {worker_node_data['worker_db_name']}")
|
||||
driver.close_driver(neo_driver)
|
||||
Reference in New Issue
Block a user