Spaces:
Sleeping
Sleeping
| 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, | |
| } | |