fix(nav): fix AcademicWeek Cypher bug, add TeacherTimetable tree handlers, timetable-term view
- Fix uuid_string: $id Cypher bug in AcademicWeek handler (days were never loading) - Pre-load SubjectClass children in _build_timetable_section (By Class view) - Add TeacherTimetable handler: By Class (TIMETABLE_HAS_CLASS) + By Term (ACADEMIC_TIMETABLE_HAS_ACADEMIC_YEAR chain) - Add timetable-term context propagation through AcademicTerm -> AcademicWeek -> TaughtLesson - AcademicWeek in timetable-term context returns TaughtLessons filtered by teacher email - Pass user_email from credentials to _get_children_for_node - Propagate section_id on AcademicWeek nodes so week expansion stays in timetable context Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b42b409bb2
commit
bf3df05632
@ -248,10 +248,32 @@ def _build_timetable_section(institute_db: Optional[str], teacher_uuid: Optional
|
|||||||
).single()
|
).single()
|
||||||
if rec:
|
if rec:
|
||||||
tt = rec["tt"]
|
tt = rec["tt"]
|
||||||
|
tt_uuid = tt["uuid_string"]
|
||||||
|
classes = []
|
||||||
|
try:
|
||||||
|
cls_result = session.run(
|
||||||
|
"MATCH (tt2:TeacherTimetable {uuid_string: $id})"
|
||||||
|
"-[:TIMETABLE_HAS_CLASS]->(c:SubjectClass) "
|
||||||
|
"RETURN c ORDER BY c.name",
|
||||||
|
id=tt_uuid,
|
||||||
|
)
|
||||||
|
classes = [
|
||||||
|
{
|
||||||
|
"neo4j_node_id": r["c"]["uuid_string"],
|
||||||
|
"label": r["c"].get("name") or "Class",
|
||||||
|
"node_type": "SubjectClass",
|
||||||
|
"neo4j_db_name": institute_db,
|
||||||
|
"is_section": False,
|
||||||
|
"has_children": True,
|
||||||
|
}
|
||||||
|
for r in cls_result
|
||||||
|
]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return {
|
return {
|
||||||
**_section("timetable", "My Timetable", institute_db, "populated",
|
**_section("timetable", "My Timetable", institute_db, "populated",
|
||||||
has_children=True),
|
has_children=True, children=classes if classes else None),
|
||||||
"neo4j_node_id": tt["uuid_string"],
|
"neo4j_node_id": tt_uuid,
|
||||||
"node_type": "TeacherTimetable",
|
"node_type": "TeacherTimetable",
|
||||||
"is_section": True,
|
"is_section": True,
|
||||||
}
|
}
|
||||||
@ -379,6 +401,7 @@ def _get_children_for_node(
|
|||||||
neo4j_db_name: str,
|
neo4j_db_name: str,
|
||||||
node_type: str,
|
node_type: str,
|
||||||
section_id: str = "",
|
section_id: str = "",
|
||||||
|
user_email: str = "",
|
||||||
) -> List[Dict]:
|
) -> List[Dict]:
|
||||||
# Calendar
|
# Calendar
|
||||||
if node_type == "CalendarYear":
|
if node_type == "CalendarYear":
|
||||||
@ -387,6 +410,57 @@ def _get_children_for_node(
|
|||||||
if node_type == "CalendarMonth":
|
if node_type == "CalendarMonth":
|
||||||
return _query_month_days(neo4j_node_id)
|
return _query_month_days(neo4j_node_id)
|
||||||
|
|
||||||
|
# TeacherTimetable lazy-load (fallback if not pre-loaded, or for By-Term view)
|
||||||
|
if node_type == "TeacherTimetable" and neo4j_db_name:
|
||||||
|
if section_id in ("", "timetable"):
|
||||||
|
try:
|
||||||
|
with driver_tools.get_session(database=neo4j_db_name) as session:
|
||||||
|
result = session.run(
|
||||||
|
"MATCH (tt:TeacherTimetable {uuid_string: $id})"
|
||||||
|
"-[:TIMETABLE_HAS_CLASS]->(c:SubjectClass) "
|
||||||
|
"RETURN c ORDER BY c.name",
|
||||||
|
id=neo4j_node_id,
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"neo4j_node_id": r["c"]["uuid_string"],
|
||||||
|
"label": r["c"].get("name") or "Class",
|
||||||
|
"node_type": "SubjectClass",
|
||||||
|
"neo4j_db_name": neo4j_db_name,
|
||||||
|
"is_section": False,
|
||||||
|
"has_children": True,
|
||||||
|
}
|
||||||
|
for r in result
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"TeacherTimetable class children failed: {e}")
|
||||||
|
return []
|
||||||
|
if section_id == "timetable-term":
|
||||||
|
try:
|
||||||
|
with driver_tools.get_session(database=neo4j_db_name) as session:
|
||||||
|
result = session.run(
|
||||||
|
"MATCH (tt:TeacherTimetable {uuid_string: $id}) "
|
||||||
|
"-[:ACADEMIC_TIMETABLE_HAS_ACADEMIC_YEAR]->(ay:AcademicYear) "
|
||||||
|
"-[:ACADEMIC_YEAR_HAS_ACADEMIC_TERM]->(t:AcademicTerm) "
|
||||||
|
"RETURN t ORDER BY toInteger(t.term_number)",
|
||||||
|
id=neo4j_node_id,
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"neo4j_node_id": r["t"]["uuid_string"],
|
||||||
|
"label": r["t"].get("term_name") or "Term {}".format(r["t"].get("term_number", "")),
|
||||||
|
"node_type": "AcademicTerm",
|
||||||
|
"neo4j_db_name": neo4j_db_name,
|
||||||
|
"section_id": "timetable-term",
|
||||||
|
"is_section": False,
|
||||||
|
"has_children": True,
|
||||||
|
}
|
||||||
|
for r in result
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"TeacherTimetable term children failed: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
# Section containers that need lazy loading
|
# Section containers that need lazy loading
|
||||||
if node_type == "Section":
|
if node_type == "Section":
|
||||||
if section_id == "timetable" and neo4j_db_name:
|
if section_id == "timetable" and neo4j_db_name:
|
||||||
@ -489,17 +563,46 @@ def _get_children_for_node(
|
|||||||
logger.warning(f"AcademicYear children failed: {e}")
|
logger.warning(f"AcademicYear children failed: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# AcademicWeek → days
|
# AcademicWeek → days (or TaughtLessons in timetable-term context)
|
||||||
if node_type == "AcademicWeek" and neo4j_db_name:
|
if node_type == "AcademicWeek" and neo4j_db_name:
|
||||||
|
if section_id == "timetable-term" and user_email:
|
||||||
|
try:
|
||||||
|
with driver_tools.get_session(database=neo4j_db_name) as session:
|
||||||
|
result = session.run(
|
||||||
|
"MATCH (w:AcademicWeek {uuid_string: $id}) "
|
||||||
|
"-[:ACADEMIC_WEEK_HAS_ACADEMIC_DAY]->(d:AcademicDay) "
|
||||||
|
"-[:ACADEMIC_DAY_HAS_PERIOD]->(p:AcademicPeriod) "
|
||||||
|
"-[:ACADEMIC_PERIOD_HAS_TAUGHT_LESSON]->(tl:TaughtLesson) "
|
||||||
|
"WHERE tl.teacher_email = "
|
||||||
|
"RETURN tl, d.date AS date ORDER BY d.date, p.start_time",
|
||||||
|
id=neo4j_node_id,
|
||||||
|
email=user_email,
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"neo4j_node_id": r["tl"]["uuid_string"],
|
||||||
|
"label": (r["tl"].get("period_code") or "")
|
||||||
|
+ " — "
|
||||||
|
+ (r["tl"].get("class_name") or r["tl"].get("subject_class") or "Lesson"),
|
||||||
|
"node_type": "TaughtLesson",
|
||||||
|
"neo4j_db_name": neo4j_db_name,
|
||||||
|
"is_section": False,
|
||||||
|
"has_children": False,
|
||||||
|
}
|
||||||
|
for r in result
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"AcademicWeek timetable-term lessons failed: {e}")
|
||||||
|
return []
|
||||||
try:
|
try:
|
||||||
with driver_tools.get_session(database=neo4j_db_name) as session:
|
with driver_tools.get_session(database=neo4j_db_name) as session:
|
||||||
result = session.run(
|
result = session.run(
|
||||||
"MATCH (w:AcademicWeek {uuid_string: })"
|
"MATCH (w:AcademicWeek {uuid_string: $id}) "
|
||||||
"-[:ACADEMIC_WEEK_HAS_ACADEMIC_DAY]->(d:AcademicDay) "
|
"-[:ACADEMIC_WEEK_HAS_ACADEMIC_DAY]->(d:AcademicDay) "
|
||||||
"RETURN d ORDER BY d.date",
|
"RETURN d ORDER BY d.date",
|
||||||
id=neo4j_node_id,
|
id=neo4j_node_id,
|
||||||
)
|
)
|
||||||
days = [
|
return [
|
||||||
{
|
{
|
||||||
"neo4j_node_id": r["d"]["uuid_string"],
|
"neo4j_node_id": r["d"]["uuid_string"],
|
||||||
"label": r["d"].get("date", ""),
|
"label": r["d"].get("date", ""),
|
||||||
@ -510,7 +613,6 @@ def _get_children_for_node(
|
|||||||
}
|
}
|
||||||
for r in result
|
for r in result
|
||||||
]
|
]
|
||||||
return days
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"AcademicWeek children failed: {e}")
|
logger.warning(f"AcademicWeek children failed: {e}")
|
||||||
return []
|
return []
|
||||||
@ -528,9 +630,10 @@ def _get_children_for_node(
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"neo4j_node_id": r["w"]["uuid_string"],
|
"neo4j_node_id": r["w"]["uuid_string"],
|
||||||
"label": f"Week {r['w']['academic_week_number']}",
|
"label": "Week {}".format(r["w"].get("academic_week_number", r["w"].get("week_number", "?"))),
|
||||||
"node_type": "AcademicWeek",
|
"node_type": "AcademicWeek",
|
||||||
"neo4j_db_name": neo4j_db_name,
|
"neo4j_db_name": neo4j_db_name,
|
||||||
|
"section_id": section_id if section_id == "timetable-term" else "",
|
||||||
"is_section": False,
|
"is_section": False,
|
||||||
"has_children": True,
|
"has_children": True,
|
||||||
}
|
}
|
||||||
@ -620,7 +723,8 @@ async def get_node_children(
|
|||||||
section_id: str = "",
|
section_id: str = "",
|
||||||
credentials: dict = Depends(SupabaseBearer()),
|
credentials: dict = Depends(SupabaseBearer()),
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
children = _get_children_for_node(neo4j_node_id, neo4j_db_name, node_type, section_id)
|
user_email = credentials.get("email", "")
|
||||||
|
children = _get_children_for_node(neo4j_node_id, neo4j_db_name, node_type, section_id, user_email)
|
||||||
return {"status": "success", "children": children}
|
return {"status": "success", "children": children}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user