latest
This commit is contained in:
@@ -11,7 +11,7 @@ import modules.database.schemas.relationships.calendar_timetable_rels as cal_tt_
|
||||
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):
|
||||
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.")
|
||||
@@ -22,10 +22,10 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
school_df = dataframes['school']
|
||||
if school_node is None:
|
||||
logger.info(f"School node is None, using school data from dataframe")
|
||||
school_unique_id = school_df[school_df['Identifier'] == 'SchoolID']['Data'].iloc[0]
|
||||
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_unique_id = school_node.unique_id
|
||||
school_uuid_string = school_node.uuid_string
|
||||
|
||||
terms_df = dataframes['terms']
|
||||
weeks_df = dataframes['weeks']
|
||||
@@ -54,20 +54,30 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
}
|
||||
|
||||
# Create AcademicTimetable Node
|
||||
school_timetable_unique_id = f"{school_unique_id}_{school_year_start_date.year}_{school_year_end_date.year}"
|
||||
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_unique_id,
|
||||
unique_id=school_timetable_unique_id,
|
||||
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,
|
||||
tldraw_snapshot=""
|
||||
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_unique_id} from Neo4j SchoolNode: {school_node.unique_id}")
|
||||
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)
|
||||
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),
|
||||
@@ -75,26 +85,34 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
)
|
||||
timetable_nodes['calendar_nodes'] = calendar_nodes
|
||||
else:
|
||||
logger.info(f"Creating calendar for {school_unique_id} from dataframe SchoolID: {school_unique_id}")
|
||||
calendar_nodes = init_calendar.create_calendar(db_name, school_year_start_date, school_year_end_date, attach_to_calendar_node=False, owner_node=None)
|
||||
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_unique_id = f"{school_timetable_unique_id}_{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(
|
||||
unique_id=academic_year_unique_id,
|
||||
uuid_string=academic_year_uuid_string,
|
||||
year=year_str,
|
||||
tldraw_snapshot=""
|
||||
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.unique_id}')
|
||||
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.unique_id} to {academic_year_node.unique_id}")
|
||||
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']:
|
||||
@@ -103,7 +121,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
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.unique_id} to {year_node.unique_id}")
|
||||
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
|
||||
@@ -121,29 +139,39 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
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_unique_id = f"{school_timetable_unique_id}_{academic_term_number}_{term_name_no_spaces}"
|
||||
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(
|
||||
unique_id=term_node_unique_id,
|
||||
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'),
|
||||
tldraw_snapshot=""
|
||||
node_storage_path=node_storage_path
|
||||
)
|
||||
academic_term_number += 1
|
||||
else:
|
||||
term_break_node_unique_id = f"{school_timetable_unique_id}_{term_name_no_spaces}"
|
||||
term_break_node_uuid_string = f"{school_timetable_uuid_string}_{term_name_no_spaces}"
|
||||
term_node = term_node_class(
|
||||
unique_id=term_break_node_unique_id,
|
||||
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'),
|
||||
tldraw_snapshot=""
|
||||
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.unique_id}')
|
||||
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
|
||||
|
||||
@@ -158,7 +186,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
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.unique_id} to {term_node.unique_id}")
|
||||
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
|
||||
@@ -168,28 +196,35 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
if isinstance(week_start_date, pd.Timestamp):
|
||||
week_start_date = week_start_date.strftime('%Y-%m-%d')
|
||||
|
||||
week_node_unique_id = f"{school_timetable_unique_id}_{week_row['WeekNumber']}_{week_row['WeekType']}Week"
|
||||
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(
|
||||
unique_id=week_node_unique_id,
|
||||
uuid_string=week_node_uuid_string,
|
||||
start_date=datetime.strptime(week_start_date, '%Y-%m-%d'),
|
||||
tldraw_snapshot=""
|
||||
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(
|
||||
unique_id=week_node_unique_id,
|
||||
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,
|
||||
tldraw_snapshot=""
|
||||
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.unique_id}")
|
||||
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):
|
||||
@@ -197,13 +232,13 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
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.unique_id} to {week_node.unique_id}")
|
||||
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.unique_id} to {week_node.unique_id}")
|
||||
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
|
||||
@@ -214,7 +249,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
relationship_class(source=term_node, target=week_node),
|
||||
database=db_name, operation='merge'
|
||||
)
|
||||
logger.info(f"Created school timetable relationship from {term_node.unique_id} to {week_node.unique_id}")
|
||||
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
|
||||
@@ -225,7 +260,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
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.unique_id} to {week_node.unique_id}")
|
||||
logger.info(f"Created school timetable relationship from {academic_year_node.uuid_string} to {week_node.uuid_string}")
|
||||
break
|
||||
|
||||
# Create Day nodes
|
||||
@@ -243,18 +278,24 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
'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 = {
|
||||
'unique_id': f"{school_timetable_unique_id}_{day_number}_{day_node_class.__name__}Day",
|
||||
'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'),
|
||||
'tldraw_snapshot': ""
|
||||
'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_data['tldraw_snapshot'] = ""
|
||||
|
||||
day_node = day_node_class(**day_node_data)
|
||||
|
||||
@@ -262,7 +303,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
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.unique_id}")
|
||||
logger.info(f"Created day node: {day_node.uuid_string}")
|
||||
|
||||
if isinstance(day_node, timetable.AcademicDayNode):
|
||||
relationship_class = cal_tt_rels.AcademicDayIsCalendarDay
|
||||
@@ -277,7 +318,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
relationship_class(source=day_node, target=calendar_node),
|
||||
database=db_name, operation='merge'
|
||||
)
|
||||
logger.info(f'Created relationship from {calendar_node.unique_id} to {day_node.unique_id}')
|
||||
logger.info(f'Created relationship from {calendar_node.uuid_string} to {day_node.uuid_string}')
|
||||
break
|
||||
|
||||
# Link day node to the correct academic week
|
||||
@@ -300,7 +341,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
relationship_class(source=academic_week_node, target=day_node),
|
||||
database=db_name, operation='merge'
|
||||
)
|
||||
logger.info(f"Created relationship from {academic_week_node.unique_id} to {day_node.unique_id}")
|
||||
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
|
||||
@@ -323,12 +364,12 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
relationship_class(source=term_node, target=day_node),
|
||||
database=db_name, operation='merge'
|
||||
)
|
||||
logger.info(f"Created relationship from {term_node.unique_id} to {day_node.unique_id}")
|
||||
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.unique_id}")
|
||||
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():
|
||||
@@ -340,15 +381,22 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
}[period_row['PeriodType']]
|
||||
|
||||
logger.info(f"Creating period node for {period_node_class.__name__} Period: {period_of_day}")
|
||||
period_node_unique_id = f"{school_timetable_unique_id}_{academic_day_number}_{period_of_day}_{period_node_class.__name__}Period"
|
||||
logger.debug(f"Period node unique id: {period_node_unique_id}")
|
||||
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 = {
|
||||
'unique_id': period_node_unique_id,
|
||||
'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']),
|
||||
'tldraw_snapshot': ""
|
||||
'node_storage_path': node_storage_path
|
||||
}
|
||||
logger.debug(f"Period node data: {period_node_data}")
|
||||
if period_row['PeriodType'] in ['Academic', 'Registration']:
|
||||
@@ -357,14 +405,13 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
period_code = period_row['PeriodCode']
|
||||
period_code_formatted = f"{week_type}{day_name_short}{period_code}"
|
||||
period_node_data['period_code'] = period_code_formatted
|
||||
period_node_data['tldraw_snapshot'] = ""
|
||||
|
||||
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.unique_id}')
|
||||
logger.info(f'Created period node: {period_node.uuid_string}')
|
||||
|
||||
relationship_class = {
|
||||
'Academic': tt_rels.AcademicDayHasAcademicPeriod,
|
||||
@@ -377,7 +424,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
relationship_class(source=day_node, target=period_node),
|
||||
database=db_name, operation='merge'
|
||||
)
|
||||
logger.info(f"Created relationship from {day_node.unique_id} to {period_node.unique_id}")
|
||||
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
|
||||
@@ -392,7 +439,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
relationship_class = relationship_map.get(node_type_pair)
|
||||
if relationship_class:
|
||||
# Avoid self-referential relationships
|
||||
if source_node.unique_id != target_node.unique_id:
|
||||
if source_node.uuid_string != target_node.uuid_string:
|
||||
neon.create_or_merge_neontology_relationship(
|
||||
relationship_class(
|
||||
source=source_node,
|
||||
@@ -400,9 +447,9 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
),
|
||||
database=db_name, operation='merge'
|
||||
)
|
||||
logger.info(f"Created relationship from {source_node.unique_id} to {target_node.unique_id}")
|
||||
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.unique_id}")
|
||||
logger.warning(f"Skipped self-referential relationship for node {source_node.uuid_string}")
|
||||
|
||||
# Relationship maps for different node types
|
||||
academic_year_relationship_map = {
|
||||
@@ -474,7 +521,7 @@ def create_school_timetable(dataframes, db_name, school_node=None):
|
||||
# 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"].unique_id}')
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user