fix(graph-tree): class_code column, SubjectClass expansion handler
- _query_teacher_classes: fix code -> class_code (Supabase column name), add section_id param - _build_timetable_section: tag class nodes with section_id=timetable - _build_classes_section: tag class nodes with section_id=classes - SubjectClass handler: section=timetable -> taught_lessons for class; section=classes -> enrolled students Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b71995f4fb
commit
0d828315bb
@ -166,7 +166,9 @@ def _query_month_days(month_uuid: str) -> List[Dict]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def _query_teacher_classes(user_id: str, institute_id: str, institute_db: str) -> List[Dict]:
|
def _query_teacher_classes(
|
||||||
|
user_id: str, institute_id: str, institute_db: str, section_id: str = ""
|
||||||
|
) -> List[Dict]:
|
||||||
"""Query classes for a teacher or student from Supabase (source of truth)."""
|
"""Query classes for a teacher or student from Supabase (source of truth)."""
|
||||||
try:
|
try:
|
||||||
sb = _sb()
|
sb = _sb()
|
||||||
@ -192,7 +194,7 @@ def _query_teacher_classes(user_id: str, institute_id: str, institute_db: str) -
|
|||||||
return []
|
return []
|
||||||
classes = (
|
classes = (
|
||||||
sb.supabase.table("classes")
|
sb.supabase.table("classes")
|
||||||
.select("id, name, code, subject")
|
.select("id, name, class_code, subject")
|
||||||
.in_("id", all_ids)
|
.in_("id", all_ids)
|
||||||
.eq("institute_id", institute_id)
|
.eq("institute_id", institute_id)
|
||||||
.eq("is_active", True)
|
.eq("is_active", True)
|
||||||
@ -203,15 +205,24 @@ def _query_teacher_classes(user_id: str, institute_id: str, institute_db: str) -
|
|||||||
result = []
|
result = []
|
||||||
for c in classes:
|
for c in classes:
|
||||||
role = "teacher" if c["id"] in teacher_class_ids else "student"
|
role = "teacher" if c["id"] in teacher_class_ids else "student"
|
||||||
result.append({
|
label = c.get("class_code") or c.get("name") or "Class"
|
||||||
|
node: Dict = {
|
||||||
"neo4j_node_id": c["id"],
|
"neo4j_node_id": c["id"],
|
||||||
"label": c.get("name") or "Class",
|
"label": label,
|
||||||
"node_type": "SubjectClass",
|
"node_type": "SubjectClass",
|
||||||
"neo4j_db_name": institute_db,
|
"neo4j_db_name": institute_db,
|
||||||
"is_section": False,
|
"is_section": False,
|
||||||
"has_children": True,
|
"has_children": True,
|
||||||
"neo4j_props": {"role": role, "subject": c.get("subject") or ""},
|
"neo4j_props": {
|
||||||
})
|
"role": role,
|
||||||
|
"subject": c.get("subject") or "",
|
||||||
|
"name": c.get("name") or "",
|
||||||
|
"class_code": c.get("class_code") or "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if section_id:
|
||||||
|
node["section_id"] = section_id
|
||||||
|
result.append(node)
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not query classes for user {user_id}: {e}")
|
logger.warning(f"Could not query classes for user {user_id}: {e}")
|
||||||
@ -278,7 +289,7 @@ def _build_timetable_section(user_id: str, institute_id: Optional[str], institut
|
|||||||
tt = rec["tt"]
|
tt = rec["tt"]
|
||||||
tt_uuid = tt["uuid_string"]
|
tt_uuid = tt["uuid_string"]
|
||||||
# Load classes from Supabase (source of truth)
|
# Load classes from Supabase (source of truth)
|
||||||
classes = _query_teacher_classes(user_id, institute_id, institute_db)
|
classes = _query_teacher_classes(user_id, institute_id, institute_db, section_id="timetable")
|
||||||
return {
|
return {
|
||||||
**_section("timetable", "My Timetable", institute_db, "populated",
|
**_section("timetable", "My Timetable", institute_db, "populated",
|
||||||
has_children=True, children=classes if classes else None),
|
has_children=True, children=classes if classes else None),
|
||||||
@ -296,7 +307,7 @@ def _build_classes_section(user_id: str, institute_id: Optional[str], institute_
|
|||||||
if not institute_db or not institute_id:
|
if not institute_db or not institute_id:
|
||||||
return _section("classes", "My Classes", "", "no_school")
|
return _section("classes", "My Classes", "", "no_school")
|
||||||
|
|
||||||
classes = _query_teacher_classes(user_id, institute_id, institute_db)
|
classes = _query_teacher_classes(user_id, institute_id, institute_db, section_id="classes")
|
||||||
if classes:
|
if classes:
|
||||||
return _section("classes", "My Classes", institute_db, "populated",
|
return _section("classes", "My Classes", institute_db, "populated",
|
||||||
has_children=True, children=classes)
|
has_children=True, children=classes)
|
||||||
@ -451,6 +462,84 @@ def _get_children_for_node(
|
|||||||
logger.warning(f"TeacherTimetable term children failed: {e}")
|
logger.warning(f"TeacherTimetable term children failed: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
# SubjectClass — expand to taught lessons (timetable context) or members (classes context)
|
||||||
|
if node_type == "SubjectClass":
|
||||||
|
class_id = neo4j_node_id # Supabase class UUID
|
||||||
|
try:
|
||||||
|
sb = _sb()
|
||||||
|
if section_id == "timetable" and user_email:
|
||||||
|
# Resolve teacher profile_id from email
|
||||||
|
prof = sb.supabase.table("profiles").select("id").eq("email", user_email).single().execute()
|
||||||
|
teacher_id = (prof.data or {}).get("id")
|
||||||
|
if teacher_id:
|
||||||
|
lessons = (
|
||||||
|
sb.supabase.table("taught_lessons")
|
||||||
|
.select("id, date, period_code, class_name, subject")
|
||||||
|
.eq("class_id", class_id)
|
||||||
|
.eq("teacher_profile_id", teacher_id)
|
||||||
|
.order("date")
|
||||||
|
.order("period_code")
|
||||||
|
.execute()
|
||||||
|
.data or []
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"neo4j_node_id": tl["id"],
|
||||||
|
"label": "{} {}".format(
|
||||||
|
tl.get("period_code") or "",
|
||||||
|
tl.get("date") or ""
|
||||||
|
).strip(),
|
||||||
|
"node_type": "TaughtLesson",
|
||||||
|
"neo4j_db_name": neo4j_db_name,
|
||||||
|
"is_section": False,
|
||||||
|
"has_children": False,
|
||||||
|
"section_id": "timetable",
|
||||||
|
}
|
||||||
|
for tl in lessons
|
||||||
|
]
|
||||||
|
return []
|
||||||
|
if section_id == "classes":
|
||||||
|
# Class members: students enrolled in this class
|
||||||
|
members = (
|
||||||
|
sb.supabase.table("class_students")
|
||||||
|
.select("student_id, status, enrolled_at")
|
||||||
|
.eq("class_id", class_id)
|
||||||
|
.order("enrolled_at")
|
||||||
|
.execute()
|
||||||
|
.data or []
|
||||||
|
)
|
||||||
|
if not members:
|
||||||
|
return []
|
||||||
|
student_ids = [m["student_id"] for m in members]
|
||||||
|
status_map = {m["student_id"]: m["status"] for m in members}
|
||||||
|
profiles = (
|
||||||
|
sb.supabase.table("profiles")
|
||||||
|
.select("id, email, first_name, last_name, user_type")
|
||||||
|
.in_("id", student_ids)
|
||||||
|
.order("last_name")
|
||||||
|
.execute()
|
||||||
|
.data or []
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"neo4j_node_id": p["id"],
|
||||||
|
"label": "{} {} ({})".format(
|
||||||
|
p.get("first_name") or "",
|
||||||
|
p.get("last_name") or "",
|
||||||
|
status_map.get(p["id"], ""),
|
||||||
|
).strip(),
|
||||||
|
"node_type": "Student",
|
||||||
|
"neo4j_db_name": neo4j_db_name,
|
||||||
|
"is_section": False,
|
||||||
|
"has_children": False,
|
||||||
|
"section_id": "classes",
|
||||||
|
}
|
||||||
|
for p in profiles
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"SubjectClass 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:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user