534 lines
30 KiB
Python
534 lines
30 KiB
Python
import os
|
|
from modules.logger_tool import initialise_logger
|
|
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)
|
|
from datetime import timedelta, datetime
|
|
import pandas as pd
|
|
from modules.database.schemas.structures import structures
|
|
import modules.database.schemas.nodes.schools.timetable as timetable
|
|
import modules.database.schemas.relationships.timetables as tt_rels
|
|
import modules.database.schemas.relationships.entity_timetable_rels as entity_tt_rels
|
|
import modules.database.schemas.relationships.calendar_timetable_rels as cal_tt_rels
|
|
import modules.database.init.init_calendar as init_calendar
|
|
import modules.database.tools.neontology_tools as neon
|
|
|
|
def create_school_timetable(dataframes, db_name, school_node=None, filesystem=None):
|
|
logger.info(f"Creating school timetable for {db_name}")
|
|
if dataframes is None:
|
|
raise ValueError("Data is required to create the calendar and timetable.")
|
|
|
|
logger.info(f"Initialising neo4j connection...")
|
|
neon.init_neontology_connection()
|
|
|
|
school_df = dataframes['school']
|
|
if school_node is None:
|
|
logger.info(f"School node is None, using school data from dataframe")
|
|
school_uuid_string = school_df[school_df['Identifier'] == 'SchoolID']['Data'].iloc[0]
|
|
else:
|
|
logger.info(f"School node is not None, using school data from school node: {school_node}")
|
|
school_uuid_string = school_node.uuid_string
|
|
|
|
terms_df = dataframes['terms']
|
|
weeks_df = dataframes['weeks']
|
|
days_df = dataframes['days']
|
|
periods_df = dataframes['periods']
|
|
|
|
school_df_year_start = school_df[school_df['Identifier'] == 'AcademicYearStart']['Data'].iloc[0]
|
|
school_df_year_end = school_df[school_df['Identifier'] == 'AcademicYearEnd']['Data'].iloc[0]
|
|
if isinstance(school_df_year_start, str):
|
|
school_year_start_date = datetime.strptime(school_df_year_start, '%Y-%m-%d')
|
|
else:
|
|
school_year_start_date = school_df_year_start
|
|
if isinstance(school_df_year_end, str):
|
|
school_year_end_date = datetime.strptime(school_df_year_end, '%Y-%m-%d')
|
|
else:
|
|
school_year_end_date = school_df_year_end
|
|
|
|
# Create a dictionary to store the timetable nodes
|
|
timetable_nodes = {
|
|
'timetable_node': None,
|
|
'academic_year_nodes': [],
|
|
'academic_term_nodes': [],
|
|
'academic_week_nodes': [],
|
|
'academic_day_nodes': [],
|
|
'academic_period_nodes': []
|
|
}
|
|
|
|
# Create AcademicTimetable Node
|
|
school_timetable_uuid_string = f"{school_uuid_string}_{school_year_start_date.year}_{school_year_end_date.year}"
|
|
|
|
# Generate storage path for timetable node
|
|
if filesystem:
|
|
timetable_dir_created, timetable_path = filesystem.create_school_timetable_directory()
|
|
node_storage_path = os.path.relpath(timetable_path, filesystem.base_path)
|
|
logger.info(f"Generated timetable node_storage_path: {node_storage_path}")
|
|
else:
|
|
node_storage_path = ""
|
|
logger.warning("No filesystem provided, using empty storage path")
|
|
|
|
school_timetable_node = timetable.SchoolTimetableNode(
|
|
school_timetable_id=school_timetable_uuid_string,
|
|
uuid_string=school_timetable_uuid_string,
|
|
start_date=school_year_start_date,
|
|
end_date=school_year_end_date,
|
|
node_storage_path=node_storage_path
|
|
)
|
|
neon.create_or_merge_neontology_node(school_timetable_node, database=db_name, operation='merge')
|
|
timetable_nodes['timetable_node'] = school_timetable_node
|
|
|
|
if school_node:
|
|
logger.info(f"Creating calendar for {school_uuid_string} from Neo4j SchoolNode: {school_node.uuid_string}")
|
|
calendar_nodes = init_calendar.create_calendar(db_name, school_year_start_date, school_year_end_date, attach_to_calendar_node=True, owner_node=school_node, filesystem=filesystem)
|
|
# Link the school node to the timetable node
|
|
neon.create_or_merge_neontology_relationship(
|
|
entity_tt_rels.SchoolHasTimetable(source=school_node, target=school_timetable_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
timetable_nodes['calendar_nodes'] = calendar_nodes
|
|
else:
|
|
logger.info(f"Creating calendar for {school_uuid_string} from dataframe SchoolID: {school_uuid_string}")
|
|
calendar_nodes = init_calendar.create_calendar(db_name, school_year_start_date, school_year_end_date, attach_to_calendar_node=False, owner_node=None, filesystem=filesystem)
|
|
|
|
# Create AcademicYear nodes for each year within the range
|
|
for year in range(school_year_start_date.year, school_year_end_date.year + 1):
|
|
year_str = str(year)
|
|
academic_year_uuid_string = f"{school_timetable_uuid_string}_{year}"
|
|
|
|
# Generate storage path for academic year node
|
|
if filesystem:
|
|
year_dir_created, year_path = filesystem.create_school_timetable_year_directory(timetable_path, year)
|
|
node_storage_path = os.path.relpath(year_path, filesystem.base_path)
|
|
else:
|
|
node_storage_path = ""
|
|
|
|
academic_year_node = timetable.AcademicYearNode(
|
|
uuid_string=academic_year_uuid_string,
|
|
year=year_str,
|
|
node_storage_path=node_storage_path
|
|
)
|
|
neon.create_or_merge_neontology_node(academic_year_node, database=db_name, operation='merge')
|
|
timetable_nodes['academic_year_nodes'].append(academic_year_node)
|
|
logger.info(f'Created academic year node: {academic_year_node.uuid_string}')
|
|
neon.create_or_merge_neontology_relationship(
|
|
tt_rels.AcademicTimetableHasAcademicYear(source=school_timetable_node, target=academic_year_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created school timetable relationship from {school_timetable_node.uuid_string} to {academic_year_node.uuid_string}")
|
|
|
|
# Link the academic year with the corresponding calendar year node
|
|
for year_node in calendar_nodes['calendar_year_nodes']:
|
|
if year_node.year == year:
|
|
neon.create_or_merge_neontology_relationship(
|
|
cal_tt_rels.AcademicYearIsCalendarYear(source=academic_year_node, target=year_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created school timetable relationship from {academic_year_node.uuid_string} to {year_node.uuid_string}")
|
|
break
|
|
|
|
# Create Term and TermBreak nodes linked to AcademicYear
|
|
term_number = 1
|
|
academic_term_number = 1
|
|
for _, term_row in terms_df.iterrows():
|
|
term_node_class = timetable.AcademicTermNode if term_row['TermType'] == 'Term' else timetable.AcademicTermBreakNode
|
|
term_name = term_row['TermName']
|
|
term_name_no_spaces = term_name.replace(' ', '')
|
|
term_start_date = term_row['StartDate']
|
|
if isinstance(term_start_date, pd.Timestamp):
|
|
term_start_date = term_start_date.strftime('%Y-%m-%d')
|
|
|
|
term_end_date = term_row['EndDate']
|
|
if isinstance(term_end_date, pd.Timestamp):
|
|
term_end_date = term_end_date.strftime('%Y-%m-%d')
|
|
|
|
# Generate storage path for term node
|
|
if filesystem:
|
|
if term_row['TermType'] == 'Term':
|
|
term_dir_created, term_path = filesystem.create_school_timetable_academic_term_directory(timetable_path, term_name, academic_term_number)
|
|
else:
|
|
term_dir_created, term_path = filesystem.create_school_timetable_academic_term_break_directory(timetable_path, term_name)
|
|
node_storage_path = os.path.relpath(term_path, filesystem.base_path)
|
|
else:
|
|
node_storage_path = ""
|
|
|
|
if term_row['TermType'] == 'Term':
|
|
term_node_uuid_string = f"{school_timetable_uuid_string}_{academic_term_number}_{term_name_no_spaces}"
|
|
academic_term_number_str = str(academic_term_number)
|
|
term_node = term_node_class(
|
|
uuid_string=term_node_uuid_string,
|
|
term_name=term_name,
|
|
term_number=academic_term_number_str,
|
|
start_date=datetime.strptime(term_start_date, '%Y-%m-%d'),
|
|
end_date=datetime.strptime(term_end_date, '%Y-%m-%d'),
|
|
node_storage_path=node_storage_path
|
|
)
|
|
academic_term_number += 1
|
|
else:
|
|
term_break_node_uuid_string = f"{school_timetable_uuid_string}_{term_name_no_spaces}"
|
|
term_node = term_node_class(
|
|
uuid_string=term_break_node_uuid_string,
|
|
term_break_name=term_name,
|
|
start_date=datetime.strptime(term_start_date, '%Y-%m-%d'),
|
|
end_date=datetime.strptime(term_end_date, '%Y-%m-%d'),
|
|
node_storage_path=node_storage_path
|
|
)
|
|
neon.create_or_merge_neontology_node(term_node, database=db_name, operation='merge')
|
|
logger.info(f'Created academic term break node: {term_node.uuid_string}')
|
|
timetable_nodes['academic_term_nodes'].append(term_node)
|
|
term_number += 1 # We don't use this but we could
|
|
|
|
# Link term node to the correct academic year
|
|
term_years = set()
|
|
term_years.update([term_node.start_date.year, term_node.end_date.year])
|
|
|
|
for academic_year_node in timetable_nodes['academic_year_nodes']:
|
|
if int(academic_year_node.year) in term_years:
|
|
relationship_class = tt_rels.AcademicYearHasAcademicTerm if term_row['TermType'] == 'Term' else tt_rels.AcademicYearHasAcademicTermBreak
|
|
neon.create_or_merge_neontology_relationship(
|
|
relationship_class(source=academic_year_node, target=term_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created school timetable relationship from {academic_year_node.uuid_string} to {term_node.uuid_string}")
|
|
|
|
# Create Week nodes
|
|
academic_week_number = 1
|
|
for _, week_row in weeks_df.iterrows():
|
|
week_node_class = timetable.HolidayWeekNode if week_row['WeekType'] == 'Holiday' else timetable.AcademicWeekNode
|
|
week_start_date = week_row['WeekStart']
|
|
if isinstance(week_start_date, pd.Timestamp):
|
|
week_start_date = week_start_date.strftime('%Y-%m-%d')
|
|
|
|
week_node_uuid_string = f"{school_timetable_uuid_string}_{week_row['WeekNumber']}_{week_row['WeekType']}Week"
|
|
|
|
# Generate storage path for week node
|
|
if filesystem:
|
|
week_dir_created, week_path = filesystem.create_school_timetable_academic_week_directory(timetable_path, week_row['WeekNumber'])
|
|
node_storage_path = os.path.relpath(week_path, filesystem.base_path)
|
|
else:
|
|
node_storage_path = ""
|
|
|
|
if week_row['WeekType'] == 'Holiday':
|
|
week_node = week_node_class(
|
|
uuid_string=week_node_uuid_string,
|
|
start_date=datetime.strptime(week_start_date, '%Y-%m-%d'),
|
|
node_storage_path=node_storage_path
|
|
)
|
|
else:
|
|
academic_week_number_str = str(academic_week_number)
|
|
week_type = week_row['WeekType']
|
|
week_node = week_node_class(
|
|
uuid_string=week_node_uuid_string,
|
|
academic_week_number=academic_week_number_str,
|
|
start_date=datetime.strptime(week_start_date, '%Y-%m-%d'),
|
|
week_type=week_type,
|
|
node_storage_path=node_storage_path
|
|
)
|
|
academic_week_number += 1
|
|
neon.create_or_merge_neontology_node(week_node, database=db_name, operation='merge')
|
|
timetable_nodes['academic_week_nodes'].append(week_node)
|
|
logger.info(f"Created week node: {week_node.uuid_string}")
|
|
for calendar_node in calendar_nodes['calendar_week_nodes']:
|
|
if calendar_node.start_date == week_node.start_date:
|
|
if isinstance(week_node, timetable.AcademicWeekNode):
|
|
neon.create_or_merge_neontology_relationship(
|
|
cal_tt_rels.AcademicWeekIsCalendarWeek(source=week_node, target=calendar_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created school timetable relationship from {calendar_node.uuid_string} to {week_node.uuid_string}")
|
|
elif isinstance(week_node, timetable.HolidayWeekNode):
|
|
neon.create_or_merge_neontology_relationship(
|
|
cal_tt_rels.HolidayWeekIsCalendarWeek(source=week_node, target=calendar_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created school timetable relationship from {calendar_node.uuid_string} to {week_node.uuid_string}")
|
|
break
|
|
|
|
# Link week node to the correct academic term
|
|
for term_node in timetable_nodes['academic_term_nodes']:
|
|
if term_node.start_date <= week_node.start_date <= term_node.end_date:
|
|
relationship_class = tt_rels.AcademicTermHasAcademicWeek if week_row['WeekType'] != 'Holiday' else tt_rels.AcademicTermBreakHasHolidayWeek
|
|
neon.create_or_merge_neontology_relationship(
|
|
relationship_class(source=term_node, target=week_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created school timetable relationship from {term_node.uuid_string} to {week_node.uuid_string}")
|
|
break
|
|
|
|
# Link week node to the correct academic year
|
|
for academic_year_node in timetable_nodes['academic_year_nodes']:
|
|
if int(academic_year_node.year) == week_node.start_date.year:
|
|
relationship_class = tt_rels.AcademicYearHasAcademicWeek if week_row['WeekType'] != 'Holiday' else tt_rels.AcademicYearHasHolidayWeek
|
|
neon.create_or_merge_neontology_relationship(
|
|
relationship_class(source=academic_year_node, target=week_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created school timetable relationship from {academic_year_node.uuid_string} to {week_node.uuid_string}")
|
|
break
|
|
|
|
# Create Day nodes
|
|
day_number = 1
|
|
academic_day_number = 1
|
|
for _, day_row in days_df.iterrows():
|
|
date_str = day_row['Date']
|
|
if isinstance(date_str, pd.Timestamp):
|
|
date_str = date_str.strftime('%Y-%m-%d')
|
|
|
|
day_node_class = {
|
|
'Academic': timetable.AcademicDayNode,
|
|
'Holiday': timetable.HolidayDayNode,
|
|
'OffTimetable': timetable.OffTimetableDayNode,
|
|
'StaffDay': timetable.StaffDayNode
|
|
}[day_row['DayType']]
|
|
|
|
# Generate storage path for day node
|
|
if filesystem:
|
|
day_dir_created, day_path = filesystem.create_school_timetable_academic_day_directory(timetable_path, academic_day_number)
|
|
node_storage_path = os.path.relpath(day_path, filesystem.base_path)
|
|
else:
|
|
node_storage_path = ""
|
|
|
|
# Format the unique ID as {day_node_class.__name__}Day
|
|
day_node_data = {
|
|
'uuid_string': f"{school_timetable_uuid_string}_{day_number}_{day_node_class.__name__}Day",
|
|
'date': datetime.strptime(date_str, '%Y-%m-%d'),
|
|
'day_of_week': datetime.strptime(date_str, '%Y-%m-%d').strftime('%A'),
|
|
'node_storage_path': node_storage_path
|
|
}
|
|
|
|
if day_row['DayType'] == 'Academic':
|
|
day_node_data['academic_day'] = str(academic_day_number)
|
|
day_node_data['day_type'] = day_row['WeekType']
|
|
|
|
day_node = day_node_class(**day_node_data)
|
|
|
|
for calendar_node in calendar_nodes['calendar_day_nodes']:
|
|
if calendar_node.date == day_node.date:
|
|
neon.create_or_merge_neontology_node(day_node, database=db_name, operation='merge')
|
|
timetable_nodes['academic_day_nodes'].append(day_node)
|
|
logger.info(f"Created day node: {day_node.uuid_string}")
|
|
|
|
if isinstance(day_node, timetable.AcademicDayNode):
|
|
relationship_class = cal_tt_rels.AcademicDayIsCalendarDay
|
|
elif isinstance(day_node, timetable.HolidayDayNode):
|
|
relationship_class = cal_tt_rels.HolidayDayIsCalendarDay
|
|
elif isinstance(day_node, timetable.OffTimetableDayNode):
|
|
relationship_class = cal_tt_rels.OffTimetableDayIsCalendarDay
|
|
elif isinstance(day_node, timetable.StaffDayNode):
|
|
relationship_class = cal_tt_rels.StaffDayIsCalendarDay
|
|
|
|
neon.create_or_merge_neontology_relationship(
|
|
relationship_class(source=day_node, target=calendar_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f'Created relationship from {calendar_node.uuid_string} to {day_node.uuid_string}')
|
|
break
|
|
|
|
# Link day node to the correct academic week
|
|
for academic_week_node in timetable_nodes['academic_week_nodes']:
|
|
if academic_week_node.start_date <= day_node.date <= (academic_week_node.start_date + timedelta(days=6)):
|
|
if day_row['DayType'] == 'Academic':
|
|
relationship_class = tt_rels.AcademicWeekHasAcademicDay
|
|
elif day_row['DayType'] == 'Holiday':
|
|
if hasattr(academic_week_node, 'week_type') and academic_week_node.week_type in ['A', 'B']:
|
|
relationship_class = tt_rels.AcademicWeekHasHolidayDay
|
|
else:
|
|
relationship_class = tt_rels.HolidayWeekHasHolidayDay
|
|
elif day_row['DayType'] == 'OffTimetable':
|
|
relationship_class = tt_rels.AcademicWeekHasOffTimetableDay
|
|
elif day_row['DayType'] == 'Staff':
|
|
relationship_class = tt_rels.AcademicWeekHasStaffDay
|
|
else:
|
|
continue # Skip linking for other day types
|
|
neon.create_or_merge_neontology_relationship(
|
|
relationship_class(source=academic_week_node, target=day_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created relationship from {academic_week_node.uuid_string} to {day_node.uuid_string}")
|
|
break
|
|
|
|
# Link day node to the correct academic term
|
|
for term_node in timetable_nodes['academic_term_nodes']:
|
|
if term_node.start_date <= day_node.date <= term_node.end_date:
|
|
if day_row['DayType'] == 'Academic':
|
|
relationship_class = tt_rels.AcademicTermHasAcademicDay
|
|
elif day_row['DayType'] == 'Holiday':
|
|
if isinstance(term_node, timetable.AcademicTermNode):
|
|
relationship_class = tt_rels.AcademicTermHasHolidayDay
|
|
else:
|
|
relationship_class = tt_rels.AcademicTermBreakHasHolidayDay
|
|
elif day_row['DayType'] == 'OffTimetable':
|
|
relationship_class = tt_rels.AcademicTermHasOffTimetableDay
|
|
elif day_row['DayType'] == 'Staff':
|
|
relationship_class = tt_rels.AcademicTermHasStaffDay
|
|
else:
|
|
continue # Skip linking for other day types
|
|
neon.create_or_merge_neontology_relationship(
|
|
relationship_class(source=term_node, target=day_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created relationship from {term_node.uuid_string} to {day_node.uuid_string}")
|
|
break
|
|
|
|
# Create Period nodes for each academic day
|
|
if day_row['DayType'] == 'Academic':
|
|
logger.info(f"Creating periods for {day_node.uuid_string}")
|
|
period_of_day = 1
|
|
academic_or_registration_period_of_day = 1
|
|
for _, period_row in periods_df.iterrows():
|
|
period_node_class = {
|
|
'Academic': timetable.AcademicPeriodNode,
|
|
'Registration': timetable.RegistrationPeriodNode,
|
|
'Break': timetable.BreakPeriodNode,
|
|
'OffTimetable': timetable.OffTimetablePeriodNode
|
|
}[period_row['PeriodType']]
|
|
|
|
logger.info(f"Creating period node for {period_node_class.__name__} Period: {period_of_day}")
|
|
period_node_uuid_string = f"{school_timetable_uuid_string}_{academic_day_number}_{period_of_day}_{period_node_class.__name__}Period"
|
|
logger.debug(f"Period node unique id: {period_node_uuid_string}")
|
|
# Generate storage path for period node
|
|
if filesystem:
|
|
period_dir_created, period_path = filesystem.create_school_timetable_period_directory(timetable_path, academic_day_number, period_row['PeriodCode'])
|
|
node_storage_path = os.path.relpath(period_path, filesystem.base_path)
|
|
else:
|
|
node_storage_path = ""
|
|
|
|
period_node_data = {
|
|
'uuid_string': period_node_uuid_string,
|
|
'name': period_row['PeriodName'],
|
|
'date': day_node.date,
|
|
'start_time': datetime.combine(day_node.date, period_row['StartTime']),
|
|
'end_time': datetime.combine(day_node.date, period_row['EndTime']),
|
|
'node_storage_path': node_storage_path
|
|
}
|
|
logger.debug(f"Period node data: {period_node_data}")
|
|
if period_row['PeriodType'] in ['Academic', 'Registration']:
|
|
week_type = day_row['WeekType']
|
|
day_name_short = day_node.day_of_week[:3]
|
|
period_code = period_row['PeriodCode']
|
|
period_code_formatted = f"{week_type}{day_name_short}{period_code}"
|
|
period_node_data['period_code'] = period_code_formatted
|
|
|
|
academic_or_registration_period_of_day += 1
|
|
|
|
period_node = period_node_class(**period_node_data)
|
|
neon.create_or_merge_neontology_node(period_node, database=db_name, operation='merge')
|
|
timetable_nodes['academic_period_nodes'].append(period_node)
|
|
logger.info(f'Created period node: {period_node.uuid_string}')
|
|
|
|
relationship_class = {
|
|
'Academic': tt_rels.AcademicDayHasAcademicPeriod,
|
|
'Registration': tt_rels.AcademicDayHasRegistrationPeriod,
|
|
'Break': tt_rels.AcademicDayHasBreakPeriod,
|
|
'OffTimetable': tt_rels.AcademicDayHasOffTimetablePeriod
|
|
}[period_row['PeriodType']]
|
|
|
|
neon.create_or_merge_neontology_relationship(
|
|
relationship_class(source=day_node, target=period_node),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created relationship from {day_node.uuid_string} to {period_node.uuid_string}")
|
|
period_of_day += 1 # We don't use this but we could
|
|
academic_day_number += 1 # This is a bit of a hack but it works to keep the directories aligned (reorganise)
|
|
day_number += 1 # We don't use this but we could
|
|
|
|
def create_school_timetable_node_sequence_rels(timetable_nodes):
|
|
def sort_and_create_relationships(nodes, relationship_map, sort_key):
|
|
sorted_nodes = sorted(nodes, key=sort_key)
|
|
for i in range(len(sorted_nodes) - 1):
|
|
source_node = sorted_nodes[i]
|
|
target_node = sorted_nodes[i + 1]
|
|
node_type_pair = (type(source_node), type(target_node))
|
|
relationship_class = relationship_map.get(node_type_pair)
|
|
if relationship_class:
|
|
# Avoid self-referential relationships
|
|
if source_node.uuid_string != target_node.uuid_string:
|
|
neon.create_or_merge_neontology_relationship(
|
|
relationship_class(
|
|
source=source_node,
|
|
target=target_node
|
|
),
|
|
database=db_name, operation='merge'
|
|
)
|
|
logger.info(f"Created relationship from {source_node.uuid_string} to {target_node.uuid_string}")
|
|
else:
|
|
logger.warning(f"Skipped self-referential relationship for node {source_node.uuid_string}")
|
|
|
|
# Relationship maps for different node types
|
|
academic_year_relationship_map = {
|
|
(timetable.AcademicYearNode, timetable.AcademicYearNode): tt_rels.AcademicYearFollowsAcademicYear
|
|
}
|
|
|
|
academic_term_relationship_map = {
|
|
(timetable.AcademicTermNode, timetable.AcademicTermBreakNode): tt_rels.AcademicTermBreakFollowsAcademicTerm,
|
|
(timetable.AcademicTermBreakNode, timetable.AcademicTermNode): tt_rels.AcademicTermFollowsAcademicTermBreak
|
|
}
|
|
|
|
academic_week_relationship_map = {
|
|
(timetable.AcademicWeekNode, timetable.AcademicWeekNode): tt_rels.AcademicWeekFollowsAcademicWeek,
|
|
(timetable.HolidayWeekNode, timetable.HolidayWeekNode): tt_rels.HolidayWeekFollowsHolidayWeek,
|
|
(timetable.AcademicWeekNode, timetable.HolidayWeekNode): tt_rels.HolidayWeekFollowsAcademicWeek,
|
|
(timetable.HolidayWeekNode, timetable.AcademicWeekNode): tt_rels.AcademicWeekFollowsHolidayWeek
|
|
}
|
|
|
|
academic_day_relationship_map = {
|
|
(timetable.AcademicDayNode, timetable.AcademicDayNode): tt_rels.AcademicDayFollowsAcademicDay,
|
|
(timetable.HolidayDayNode, timetable.HolidayDayNode): tt_rels.HolidayDayFollowsHolidayDay,
|
|
(timetable.OffTimetableDayNode, timetable.OffTimetableDayNode): tt_rels.OffTimetableDayFollowsOffTimetableDay,
|
|
(timetable.StaffDayNode, timetable.StaffDayNode): tt_rels.StaffDayFollowsStaffDay,
|
|
|
|
(timetable.AcademicDayNode, timetable.HolidayDayNode): tt_rels.HolidayDayFollowsAcademicDay,
|
|
(timetable.AcademicDayNode, timetable.OffTimetableDayNode): tt_rels.OffTimetableDayFollowsAcademicDay,
|
|
(timetable.AcademicDayNode, timetable.StaffDayNode): tt_rels.StaffDayFollowsAcademicDay,
|
|
|
|
(timetable.HolidayDayNode, timetable.AcademicDayNode): tt_rels.AcademicDayFollowsHolidayDay,
|
|
(timetable.HolidayDayNode, timetable.OffTimetableDayNode): tt_rels.OffTimetableDayFollowsHolidayDay,
|
|
(timetable.HolidayDayNode, timetable.StaffDayNode): tt_rels.StaffDayFollowsHolidayDay,
|
|
|
|
(timetable.OffTimetableDayNode, timetable.AcademicDayNode): tt_rels.AcademicDayFollowsOffTimetableDay,
|
|
(timetable.OffTimetableDayNode, timetable.HolidayDayNode): tt_rels.HolidayDayFollowsOffTimetableDay,
|
|
(timetable.OffTimetableDayNode, timetable.StaffDayNode): tt_rels.StaffDayFollowsOffTimetableDay,
|
|
|
|
(timetable.StaffDayNode, timetable.AcademicDayNode): tt_rels.AcademicDayFollowsStaffDay,
|
|
(timetable.StaffDayNode, timetable.HolidayDayNode): tt_rels.HolidayDayFollowsStaffDay,
|
|
(timetable.StaffDayNode, timetable.OffTimetableDayNode): tt_rels.OffTimetableDayFollowsStaffDay,
|
|
}
|
|
|
|
academic_period_relationship_map = {
|
|
(timetable.AcademicPeriodNode, timetable.AcademicPeriodNode): tt_rels.AcademicPeriodFollowsAcademicPeriod,
|
|
(timetable.AcademicPeriodNode, timetable.BreakPeriodNode): tt_rels.BreakPeriodFollowsAcademicPeriod,
|
|
(timetable.AcademicPeriodNode, timetable.RegistrationPeriodNode): tt_rels.RegistrationPeriodFollowsAcademicPeriod,
|
|
(timetable.AcademicPeriodNode, timetable.OffTimetablePeriodNode): tt_rels.OffTimetablePeriodFollowsAcademicPeriod,
|
|
(timetable.BreakPeriodNode, timetable.AcademicPeriodNode): tt_rels.AcademicPeriodFollowsBreakPeriod,
|
|
(timetable.BreakPeriodNode, timetable.BreakPeriodNode): tt_rels.BreakPeriodFollowsBreakPeriod,
|
|
(timetable.BreakPeriodNode, timetable.RegistrationPeriodNode): tt_rels.RegistrationPeriodFollowsBreakPeriod,
|
|
(timetable.BreakPeriodNode, timetable.OffTimetablePeriodNode): tt_rels.OffTimetablePeriodFollowsBreakPeriod,
|
|
(timetable.RegistrationPeriodNode, timetable.AcademicPeriodNode): tt_rels.AcademicPeriodFollowsRegistrationPeriod,
|
|
(timetable.RegistrationPeriodNode, timetable.RegistrationPeriodNode): tt_rels.RegistrationPeriodFollowsRegistrationPeriod,
|
|
(timetable.RegistrationPeriodNode, timetable.BreakPeriodNode): tt_rels.BreakPeriodFollowsRegistrationPeriod,
|
|
(timetable.RegistrationPeriodNode, timetable.OffTimetablePeriodNode): tt_rels.OffTimetablePeriodFollowsRegistrationPeriod,
|
|
(timetable.OffTimetablePeriodNode, timetable.OffTimetablePeriodNode): tt_rels.OffTimetablePeriodFollowsOffTimetablePeriod,
|
|
(timetable.OffTimetablePeriodNode, timetable.AcademicPeriodNode): tt_rels.AcademicPeriodFollowsOffTimetablePeriod,
|
|
(timetable.OffTimetablePeriodNode, timetable.BreakPeriodNode): tt_rels.BreakPeriodFollowsOffTimetablePeriod,
|
|
(timetable.OffTimetablePeriodNode, timetable.RegistrationPeriodNode): tt_rels.RegistrationPeriodFollowsOffTimetablePeriod,
|
|
}
|
|
|
|
|
|
# Sort and create relationships
|
|
sort_and_create_relationships(timetable_nodes['academic_year_nodes'], academic_year_relationship_map, lambda x: int(x.year))
|
|
sort_and_create_relationships(timetable_nodes['academic_term_nodes'], academic_term_relationship_map, lambda x: x.start_date)
|
|
sort_and_create_relationships(timetable_nodes['academic_week_nodes'], academic_week_relationship_map, lambda x: x.start_date)
|
|
sort_and_create_relationships(timetable_nodes['academic_day_nodes'], academic_day_relationship_map, lambda x: x.date)
|
|
sort_and_create_relationships(timetable_nodes['academic_period_nodes'], academic_period_relationship_map, lambda x: (x.start_time, x.end_time))
|
|
|
|
# Call the function with the created timetable nodes
|
|
create_school_timetable_node_sequence_rels(timetable_nodes)
|
|
|
|
logger.info(f'Created timetable: {timetable_nodes["timetable_node"].uuid_string}')
|
|
|
|
# Log the directory structure after creation
|
|
# root_timetable_directory = fs_handler.root_path # Access the root directory of the filesystem handler
|
|
# fs_handler.log_directory_structure(root_timetable_directory)
|
|
|
|
return {
|
|
'school_node': school_node,
|
|
'school_calendar_nodes': calendar_nodes,
|
|
'school_timetable_nodes': timetable_nodes
|
|
} |