Lonelyguyse1's picture
spec pipeline updates + doc updates + viewer updates + schema updates + tool additions
ce13b1c
Raw
History Blame Contribute Delete
5.02 kB
import json
from app.services.books import parse_books
from app.services.deterministic import compute_course_type, compute_hours, compute_program
from app.supabase import supabase
def _lines(value) -> list[str]:
if not value:
return []
if isinstance(value, list):
return [str(item).strip() for item in value if str(item).strip()]
return [line.strip() for line in str(value).splitlines() if line.strip()]
def _content(row: dict) -> dict:
raw = row.get("refined_content") or {}
if isinstance(raw, dict):
return raw
if isinstance(raw, str) and raw.strip():
return json.loads(raw)
return {}
def _text(*values) -> str:
for value in values:
if isinstance(value, str):
text = value.strip()
if text and text != "-":
return text
return ""
def build_course_preview(row: dict) -> dict:
content = _content(row)
submission = row.get("_submission") or {}
credit_category = str(submission.get("credit_category") or row.get("credit_category") or content.get("credits") or "")
target_department = str(submission.get("target_department") or "")
hours = {
"lecture_hours": row.get("lecture_hours", content.get("lecture_hours", 0)),
"tutorial_hours": row.get("tutorial_hours", content.get("tutorial_hours", 0)),
"practical_hours": row.get("practical_hours", content.get("practical_hours", 0)),
"self_study": row.get("self_study", content.get("self_study", 0)),
"credits": row.get("credits", content.get("credits", 0)),
}
if credit_category and not any(hours.values()):
hours = compute_hours(credit_category)
program = row.get("program") or content.get("program") or ""
if target_department and not program:
program = compute_program(target_department)
course_type = row.get("course_type") or content.get("course_type") or ""
if credit_category and not course_type:
course_type = compute_course_type(credit_category)
practical_hours = int(hours["practical_hours"] or 0)
status = str(row.get("status") or content.get("status") or "")
objectives = _lines(row.get("objectives") or content.get("objectives"))[:4]
course_outcomes = _lines(row.get("course_outcomes") or content.get("course_outcomes"))[:4]
units = row.get("units") or content.get("units") or []
lab_experiments = _lines(row.get("lab_experiments") or content.get("lab_experiments"))[:10] if practical_hours else []
text_books = parse_books(row.get("text_books") or content.get("text_books")) or parse_books(submission.get("text_books"))
reference_books = parse_books(row.get("reference_books") or content.get("reference_books")) or parse_books(submission.get("reference_books"))
has_content = any([objectives, course_outcomes, units, lab_experiments, text_books, reference_books])
return {
"refined_id": row.get("id"),
"course_code": str(row.get("course_code") or content.get("course_code") or ""),
"course_title": str(row.get("course_title") or content.get("course_title") or ""),
"program": str(program),
"lecture_hours": str(hours["lecture_hours"]),
"tutorial_hours": str(hours["tutorial_hours"]),
"practical_hours": str(practical_hours),
"self_study": str(hours["self_study"]),
"credits": str(hours["credits"]),
"semester": str(row.get("semester") or content.get("semester") or ""),
"course_type": str(course_type),
"is_elective": bool(row.get("is_elective") or content.get("is_elective") or False),
"status": status,
"render_detail": status != "summary_only" and has_content,
"tools_languages": _text(row.get("tools_languages"), content.get("tools_languages"), submission.get("preferred_tools")),
"desirable_knowledge": _text(row.get("desirable_knowledge"), content.get("desirable_knowledge")),
"prelude": row.get("prelude") or content.get("prelude") or "",
"objectives": objectives,
"course_outcomes": course_outcomes,
"units": units,
"lab_experiments": lab_experiments,
"text_books": text_books,
"reference_books": reference_books,
}
def build_specialization_context(academic_year: str = "") -> dict:
"""Load specialization tracks and their elective assignments for template rendering.
academic_year is accepted for forward compatibility (multiple batches) but the
current single-batch deployment returns every track. Filtering by an empty or
mismatched year would hide all tracks, so we load them all and let the caller
scope by semester inside the template.
"""
specs = supabase.table("specialization_definitions").select("*").order("semester").order("letter").execute().data
assignments = supabase.table("course_specialization_assignments").select("*").execute().data
return {
"specializations": specs,
"specialization_assignments": assignments,
}