import json
|
from copy import deepcopy
|
from pathlib import Path
|
from typing import Any
|
|
from app.config import settings
|
|
|
class CourseService:
|
def __init__(self, data_dir: Path | None = None) -> None:
|
self.data_dir = data_dir or settings.data_dir
|
|
def _read_json(self, path: Path) -> dict[str, Any]:
|
with path.open(encoding="utf-8") as f:
|
return json.load(f)
|
|
def static_url(self, server_rel_path: str, base_url: str) -> str:
|
rel = server_rel_path.replace("\\", "/").lstrip("/")
|
return f"{base_url.rstrip('/')}/static/{rel}"
|
|
def _resolve_media_url(self, path: str, base_url: str) -> str:
|
if path.startswith("http://") or path.startswith("https://"):
|
return path
|
return self.static_url(path, base_url)
|
|
def _resolve_asset_urls(self, asset: dict[str, Any], base_url: str) -> dict[str, Any]:
|
resolved = dict(asset)
|
if resolved.get("url"):
|
resolved["url"] = self._resolve_media_url(resolved["url"], base_url)
|
if resolved.get("cover_url"):
|
resolved["cover_url"] = self._resolve_media_url(resolved["cover_url"], base_url)
|
return resolved
|
|
def get_course_index(self) -> dict[str, Any]:
|
return self._read_json(self.data_dir / "courses" / "index.json")
|
|
def get_course(self, course_id: str) -> dict[str, Any]:
|
index = self.get_course_index()
|
for item in index.get("courses", []):
|
if item["course_id"] == course_id:
|
config_path = self.data_dir / item["config"]
|
return self._read_json(config_path)
|
raise FileNotFoundError(f"Course not found: {course_id}")
|
|
def get_tasklist(self) -> dict[str, Any]:
|
return self._read_json(self.data_dir / "tasklist.json")
|
|
def get_course_task(self, course_id: str) -> dict[str, Any]:
|
path = self.data_dir / "courses" / course_id / "courseTask.json"
|
if not path.exists():
|
raise FileNotFoundError(f"Course task not found: {course_id}")
|
return self._read_json(path)
|
|
def build_course_task_sync_payload(self, course_id: str, base_url: str) -> dict[str, Any]:
|
task = deepcopy(self.get_course_task(course_id))
|
assets = task.get("assets", [])
|
task["assets"] = [self._resolve_asset_urls(asset, base_url) for asset in assets]
|
return task
|
|
def build_base_course_sync_payloads(self, base_url: str) -> list[dict[str, Any]]:
|
index = self.get_course_index()
|
payloads: list[dict[str, Any]] = []
|
for item in index.get("courses", []):
|
course_id = item["course_id"]
|
task_path = self.data_dir / "courses" / course_id / "courseTask.json"
|
if task_path.exists():
|
payloads.append(self.build_course_task_sync_payload(course_id, base_url))
|
return payloads
|
|
def build_manifest_assets(self, course_id: str, base_url: str) -> list[dict[str, Any]]:
|
task = self.get_course_task(course_id)
|
return [self._resolve_asset_urls(asset, base_url) for asset in task.get("assets", [])]
|