152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import FastAPI, HTTPException
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from .config import settings
|
|
from .extractor import SolidWorksExtractor
|
|
from .grader import FeatureGrader
|
|
from .knowledge import KnowledgeRepository
|
|
from .models import (
|
|
AuditPartRequest,
|
|
ObservationRequest,
|
|
SessionMode,
|
|
StandardKind,
|
|
StartSessionRequest,
|
|
SubmitTestRequest,
|
|
)
|
|
from .sessions import LearningService, SessionStore
|
|
from .skillflow import normalize_steps
|
|
|
|
|
|
app = FastAPI(title=settings.app_name)
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=[
|
|
"http://localhost:5173",
|
|
"http://127.0.0.1:5173",
|
|
"http://localhost:5174",
|
|
"http://127.0.0.1:5174",
|
|
"http://localhost:5175",
|
|
"http://127.0.0.1:5175",
|
|
],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
knowledge = KnowledgeRepository()
|
|
store = SessionStore()
|
|
grader = FeatureGrader()
|
|
learning = LearningService(store, grader)
|
|
extractor = SolidWorksExtractor()
|
|
|
|
|
|
@app.get("/health")
|
|
async def health() -> dict[str, object]:
|
|
return {
|
|
"ok": True,
|
|
"service": settings.app_name,
|
|
"standards": len(knowledge.list_standards()),
|
|
"software_paths": settings.software_paths(),
|
|
}
|
|
|
|
|
|
@app.post("/api/knowledge/reload")
|
|
async def reload_knowledge() -> dict[str, object]:
|
|
knowledge.reload()
|
|
return {"ok": True, "standards": len(knowledge.list_standards())}
|
|
|
|
|
|
@app.get("/api/standards")
|
|
async def list_standards(query: str = "", kind: StandardKind | None = None) -> dict[str, object]:
|
|
items = knowledge.list_standards(query=query, kind=kind)
|
|
return {"items": [item.model_dump(mode="json", exclude={"raw"}) for item in items]}
|
|
|
|
|
|
@app.get("/api/standards/{standard_id}")
|
|
async def get_standard(standard_id: str) -> dict[str, object]:
|
|
try:
|
|
return knowledge.require(standard_id).model_dump(mode="json")
|
|
except KeyError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@app.post("/api/teaching/sessions")
|
|
async def start_teaching(request: StartSessionRequest) -> dict[str, object]:
|
|
try:
|
|
standard = knowledge.require(request.standard_id)
|
|
except KeyError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return store.start(SessionMode.teaching, standard).model_dump(mode="json")
|
|
|
|
|
|
@app.post("/api/testing/sessions")
|
|
async def start_testing(request: StartSessionRequest) -> dict[str, object]:
|
|
try:
|
|
standard = knowledge.require(request.standard_id)
|
|
except KeyError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return store.start(SessionMode.testing, standard).model_dump(mode="json")
|
|
|
|
|
|
@app.get("/api/sessions/{session_id}")
|
|
async def get_session(session_id: str) -> dict[str, object]:
|
|
try:
|
|
return store.require(session_id).model_dump(mode="json")
|
|
except KeyError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@app.post("/api/teaching/sessions/{session_id}/observations")
|
|
async def submit_teaching_observation(session_id: str, request: ObservationRequest) -> dict[str, object]:
|
|
observed = normalize_steps({"steps": request.steps})
|
|
try:
|
|
result = learning.submit_teaching_observation(session_id, observed)
|
|
return result.model_dump(mode="json")
|
|
except KeyError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@app.post("/api/teaching/sessions/{session_id}/audit-part")
|
|
async def audit_part_for_teaching(session_id: str, request: AuditPartRequest) -> dict[str, object]:
|
|
try:
|
|
observed = extractor.audit_part(request.part_path)
|
|
result = learning.submit_teaching_observation(session_id, observed[-1:] if observed else [])
|
|
return {"observed_steps": [item.model_dump(mode="json") for item in observed], "result": result.model_dump(mode="json")}
|
|
except KeyError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
|
|
|
|
@app.post("/api/teaching/sessions/{session_id}/audit-active-part")
|
|
async def audit_active_part_for_teaching(session_id: str) -> dict[str, object]:
|
|
try:
|
|
session = store.require(session_id)
|
|
observed = extractor.audit_active_part()
|
|
current_index = session.current_step_index
|
|
current_step = observed[current_index : current_index + 1] if current_index < len(observed) else []
|
|
result = learning.submit_teaching_observation(session_id, current_step)
|
|
return {
|
|
"observed_steps": [item.model_dump(mode="json") for item in observed],
|
|
"submitted_steps": [item.model_dump(mode="json") for item in current_step],
|
|
"result": result.model_dump(mode="json"),
|
|
}
|
|
except KeyError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
|
|
|
|
@app.post("/api/testing/sessions/{session_id}/submit")
|
|
async def submit_test(session_id: str, request: SubmitTestRequest) -> dict[str, object]:
|
|
try:
|
|
observed = extractor.audit_part(request.part_path) if request.part_path else normalize_steps({"steps": request.steps})
|
|
report = learning.submit_test(session_id, observed)
|
|
return report.model_dump(mode="json")
|
|
except KeyError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|