Spaces:
Sleeping
Sleeping
| # api/store.py | |
| from __future__ import annotations | |
| import json | |
| import os | |
| from typing import Dict, Any, List, Optional | |
| from pathlib import Path | |
| from api.models import CourseDirectoryItem, Workspace, PersonContact, WorkspaceCourseRef, WorkspaceMember | |
| DATA_DIR = Path(os.getenv("CLARE_DATA_DIR", "./data")) | |
| COURSE_FILE = DATA_DIR / "course_directory.json" | |
| WORKSPACE_FILE = DATA_DIR / "workspaces.json" | |
| def _ensure_dir(): | |
| DATA_DIR.mkdir(parents=True, exist_ok=True) | |
| def _read_json(path: Path) -> Optional[Any]: | |
| if not path.exists(): | |
| return None | |
| with path.open("r", encoding="utf-8") as f: | |
| return json.load(f) | |
| def _write_json(path: Path, obj: Any) -> None: | |
| _ensure_dir() | |
| with path.open("w", encoding="utf-8") as f: | |
| json.dump(obj, f, ensure_ascii=False, indent=2) | |
| def seed_if_missing() -> None: | |
| """ | |
| Provide sane defaults so HF can run out-of-the-box. | |
| Replace these with your real course list/workspaces later. | |
| """ | |
| _ensure_dir() | |
| if not COURSE_FILE.exists(): | |
| courses = [ | |
| CourseDirectoryItem( | |
| id="course_ai_001", | |
| name="Introduction to AI", | |
| instructor=PersonContact(name="Dr. Smith", email="smith@uni.edu"), | |
| teachingAssistant=PersonContact(name="Alice", email="alice@uni.edu"), | |
| ).model_dump() | |
| ] | |
| _write_json(COURSE_FILE, {"items": courses}) | |
| if not WORKSPACE_FILE.exists(): | |
| workspaces = [ | |
| Workspace( | |
| id="ws_group_ai_g3", | |
| type="group", | |
| category="course", | |
| name="Group Alpha", | |
| groupNo=3, | |
| courseId="course_ai_001", | |
| courseInfo=WorkspaceCourseRef(id="course_ai_001", name="Introduction to AI"), | |
| members=[ | |
| WorkspaceMember(id="u1", name="Sarah", email="sarah@uni.edu", role="owner"), | |
| WorkspaceMember(id="u2", name="Bob", email="bob@uni.edu", role="member"), | |
| ], | |
| ).model_dump() | |
| ] | |
| _write_json(WORKSPACE_FILE, {"items": workspaces}) | |
| def load_courses() -> List[CourseDirectoryItem]: | |
| seed_if_missing() | |
| raw = _read_json(COURSE_FILE) or {} | |
| items = raw.get("items", []) | |
| out: List[CourseDirectoryItem] = [] | |
| for x in items: | |
| out.append(CourseDirectoryItem(**x)) | |
| return out | |
| def save_courses(courses: List[CourseDirectoryItem]) -> None: | |
| _write_json(COURSE_FILE, {"items": [c.model_dump() for c in courses]}) | |
| def load_workspaces() -> List[Workspace]: | |
| seed_if_missing() | |
| raw = _read_json(WORKSPACE_FILE) or {} | |
| items = raw.get("items", []) | |
| out: List[Workspace] = [] | |
| for x in items: | |
| out.append(Workspace(**x)) | |
| return out | |
| def save_workspaces(workspaces: List[Workspace]) -> None: | |
| _write_json(WORKSPACE_FILE, {"items": [w.model_dump() for w in workspaces]}) | |
| def enrich_workspace_course_info(workspaces: List[Workspace], courses: List[CourseDirectoryItem]) -> List[Workspace]: | |
| """ | |
| Ensure workspace.courseInfo has instructor/TA to satisfy CourseInfoSection fallback. | |
| Frontend prefers availableCourses, but this makes fallback robust. | |
| """ | |
| by_id = {c.id.strip().lower(): c for c in courses} | |
| by_name = {c.name.strip().lower(): c for c in courses} | |
| enriched: List[Workspace] = [] | |
| for w in workspaces: | |
| ws_course = w.courseInfo | |
| hit: Optional[CourseDirectoryItem] = None | |
| if w.courseId: | |
| hit = by_id.get(w.courseId.strip().lower()) | |
| if not hit and ws_course and ws_course.id: | |
| hit = by_id.get(ws_course.id.strip().lower()) | |
| if not hit and ws_course and ws_course.name: | |
| hit = by_name.get(ws_course.name.strip().lower()) | |
| if hit: | |
| w.courseId = w.courseId or hit.id | |
| w.courseInfo = WorkspaceCourseRef( | |
| id=hit.id, | |
| name=hit.name, | |
| instructor=hit.instructor, | |
| teachingAssistant=hit.teachingAssistant, | |
| ) | |
| else: | |
| # keep whatever exists; CourseInfoSection will show fallback if nothing matches | |
| w.courseInfo = ws_course | |
| enriched.append(w) | |
| return enriched | |
| def rename_workspace(workspace_id: str, new_name: str) -> Workspace: | |
| workspaces = load_workspaces() | |
| found = None | |
| for w in workspaces: | |
| if w.id == workspace_id: | |
| w.name = new_name | |
| found = w | |
| break | |
| if not found: | |
| raise KeyError(f"workspace not found: {workspace_id}") | |
| save_workspaces(workspaces) | |
| return found | |