vignette/apps/web/src/pages/session-review/calibrationTransferApi.ts

121 lines
4.9 KiB
TypeScript

import { api, supplementalApi } from "../../lib/api";
import type { components } from "../../lib/api.gen";
type ApiSchema<Name extends keyof components["schemas"]> =
components["schemas"][Name];
/** G5 wire DTO는 생성된 FastAPI OpenAPI 계약을 그대로 사용한다. */
export type CalibrationConfidenceInterval = ApiSchema<"ConfidenceInterval">;
export type CalibrationPair = ApiSchema<"CalibrationPair">;
export type CompetencyCalibrationAssessment =
ApiSchema<"CompetencyCalibrationAssessment">;
export type MetacognitivePrescription = ApiSchema<"MetacognitivePrescription">;
export type PredictionRevisionItem = ApiSchema<"PredictionRevisionItem">;
export type PredictionLockItem = ApiSchema<"PredictionLockItem">;
export type PerformanceObservationItem = ApiSchema<"PerformanceObservationItem">;
export type PredictionHistoryItem = ApiSchema<"PredictionHistoryItem">;
export type CalibrationAssessmentItem = ApiSchema<"CalibrationAssessmentItem">;
export type TransferAssessment = ApiSchema<"TransferAssessment">;
export type TransferTrialItem = ApiSchema<"TransferTrialItem">;
export type TransferAssessmentItem = ApiSchema<"TransferAssessmentItem">;
export type SyntheticSubgroupResult = ApiSchema<"SyntheticSubgroupResult">;
export type SubgroupDriftReport = ApiSchema<"SubgroupDriftReport">;
export type DriftReportItem = ApiSchema<"DriftReportItem">;
export type TransferSuiteItem = ApiSchema<"TransferSuiteItem">;
export type TeacherReviewItem = ApiSchema<"TeacherReviewItem">;
export type CalibrationTransferReadModelResponse =
ApiSchema<"CalibrationTransferReadModelResponse">;
export type PredictionRevisionRequest = ApiSchema<"PredictionRevisionRequest">;
export type PredictionRevisionResponse = ApiSchema<"PredictionRevisionResponse">;
export type PredictionLockRequest = ApiSchema<"PredictionLockRequest">;
export type PredictionLockResponse = ApiSchema<"PredictionLockResponse">;
export type TeacherCorrectionResponse = ApiSchema<"TeacherReviewResponse">;
export type ActualTransferAssessment = ApiSchema<"ActualTransferAssessment">;
export type ActualTransferExecutionRequest =
ApiSchema<"ActualTransferExecutionRequest">;
export type ActualTransferExecutionResponse =
ApiSchema<"ActualTransferExecutionResponse">;
export type CalibrationBias = CompetencyCalibrationAssessment["bias"];
export type CalibrationImprovement =
CompetencyCalibrationAssessment["improvement"];
export type PerformanceStatus = PerformanceObservationItem["status"];
export type RelationshipStyle = TransferTrialItem["relationship_style"];
export type DriftStatus = SubgroupDriftReport["status"];
/** 교수자 UI는 원판정 쓰기를 열지 않고 append-only 교정만 허용한다. */
export type TeacherCorrectionRequest = Omit<
ApiSchema<"TeacherReviewRequest">,
"disposition" | "correction_payload"
> & {
disposition: "corrected";
correction_payload: { teacher_note: string };
};
interface TeacherSessionRef {
session_id: string;
learner_id: string;
}
interface TeacherDashboardLookup {
pending_reviews?: TeacherSessionRef[];
recent_sessions?: TeacherSessionRef[];
}
function learnerPath(learnerId?: string): string {
return learnerId
? `/calibration/learners/${encodeURIComponent(learnerId)}`
: "/calibration/learners/me";
}
export class CalibrationLearnerContextError extends Error {
constructor() {
super("이 회기의 학습자 캘리브레이션 원장을 찾지 못했습니다.");
this.name = "CalibrationLearnerContextError";
}
}
export const calibrationTransferApi = {
getForLearner: (sessionId: string, signal?: AbortSignal) =>
supplementalApi.get<CalibrationTransferReadModelResponse>(
`${learnerPath()}?session_id=${encodeURIComponent(sessionId)}`,
{ signal },
),
getForTeacherSession: async (sessionId: string, signal?: AbortSignal) => {
const dashboard = await supplementalApi.get<TeacherDashboardLookup>(
"/teacher/dashboard",
{ signal },
);
const session = [
...(dashboard.pending_reviews ?? []),
...(dashboard.recent_sessions ?? []),
].find((item) => item.session_id === sessionId);
if (!session) throw new CalibrationLearnerContextError();
return supplementalApi.get<CalibrationTransferReadModelResponse>(
learnerPath(session.learner_id),
{ signal },
);
},
appendPredictionRevision: (body: PredictionRevisionRequest) =>
api.post<PredictionRevisionResponse>(
"/calibration/predictions/revisions",
body,
),
lockPrediction: (historyId: string, body: PredictionLockRequest) =>
api.post<PredictionLockResponse>(
`/calibration/predictions/${encodeURIComponent(historyId)}/lock`,
body,
),
appendTeacherCorrection: (body: TeacherCorrectionRequest) =>
api.post<TeacherCorrectionResponse>("/calibration/reviews", body),
appendActualTransferExecution: (body: ActualTransferExecutionRequest) =>
api.post<ActualTransferExecutionResponse>(
"/calibration/transfer-executions",
body,
),
};