feat: 病例列表联调
This commit is contained in:
+234
-76
@@ -1,4 +1,8 @@
|
||||
import { API_BASE_URL, ApiRequestError } from './auth'
|
||||
|
||||
export type CaseMode = 'training' | 'teaching'
|
||||
export type CaseListSource = 'recommended' | 'specialty' | 'weak' | 'teaching' | 'teacher-task'
|
||||
export type CaseTone = 'blue' | 'teal' | 'pink' | 'orange' | 'purple' | 'green'
|
||||
|
||||
export type ClinicalCase = {
|
||||
id: string
|
||||
@@ -7,87 +11,95 @@ export type ClinicalCase = {
|
||||
gender: '男' | '女'
|
||||
age: number
|
||||
department: string
|
||||
departmentId?: number
|
||||
scene: string
|
||||
caseNo: string
|
||||
tone: 'blue' | 'teal' | 'pink' | 'orange' | 'purple' | 'green'
|
||||
tone: CaseTone
|
||||
mode: CaseMode
|
||||
caseType?: string
|
||||
caseTypeDisplay?: string
|
||||
difficulty?: string
|
||||
difficultyScore?: number
|
||||
chiefComplaint?: string
|
||||
description?: string
|
||||
tags?: string
|
||||
competencyTags?: string[]
|
||||
estimatedMinutes?: number
|
||||
osceEnabled?: boolean
|
||||
myBestScore?: number | null
|
||||
myTrainCount?: number | null
|
||||
createdAt?: string
|
||||
source?: CaseListSource
|
||||
}
|
||||
|
||||
export function fetchCaseList(): Promise<ClinicalCase[]> {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: 'case-31190016',
|
||||
title: '间断四肢多关节肿痛5年,加重1个月',
|
||||
patientName: '郭爱和',
|
||||
gender: '男',
|
||||
age: 43,
|
||||
department: '风湿免疫科',
|
||||
scene: '门诊部',
|
||||
caseNo: '31190016',
|
||||
tone: 'blue',
|
||||
mode: 'training'
|
||||
},
|
||||
{
|
||||
id: 'case-31180002',
|
||||
title: '右膝关节疼痛8年,腰背部疼痛2年',
|
||||
patientName: '索航',
|
||||
gender: '男',
|
||||
age: 51,
|
||||
department: '风湿免疫科',
|
||||
scene: '住院部',
|
||||
caseNo: '31180002',
|
||||
tone: 'teal',
|
||||
mode: 'training'
|
||||
},
|
||||
{
|
||||
id: 'case-2238015',
|
||||
title: '阴道不规则流血4月。',
|
||||
patientName: '韩爱利',
|
||||
gender: '女',
|
||||
age: 52,
|
||||
department: '妇科',
|
||||
scene: '住院部',
|
||||
caseNo: '2238015',
|
||||
tone: 'pink',
|
||||
mode: 'training'
|
||||
},
|
||||
{
|
||||
id: 'case-1006004',
|
||||
title: '持续胸痛3小时',
|
||||
patientName: '陈先生',
|
||||
gender: '男',
|
||||
age: 60,
|
||||
department: '心血管内科',
|
||||
scene: '住院部',
|
||||
caseNo: '1006004',
|
||||
tone: 'orange',
|
||||
mode: 'teaching'
|
||||
},
|
||||
{
|
||||
id: 'case-31190042',
|
||||
title: '咳嗽、咳痰10余年,加重1周',
|
||||
patientName: '厉明',
|
||||
gender: '男',
|
||||
age: 52,
|
||||
department: '呼吸内科',
|
||||
scene: '普通门诊',
|
||||
caseNo: '31190042',
|
||||
tone: 'purple',
|
||||
mode: 'training'
|
||||
},
|
||||
{
|
||||
id: 'case-2238019',
|
||||
title: '尿频、尿急、尿痛3天',
|
||||
patientName: '刘晓元',
|
||||
gender: '女',
|
||||
age: 25,
|
||||
department: '泌尿外科',
|
||||
scene: '急诊留观',
|
||||
caseNo: '2238019',
|
||||
tone: 'green',
|
||||
mode: 'training'
|
||||
}
|
||||
])
|
||||
export type CaseListQuery = {
|
||||
search?: string
|
||||
case_type?: string
|
||||
difficulty?: string
|
||||
department?: string | number
|
||||
page?: number
|
||||
page_size?: number
|
||||
}
|
||||
|
||||
export type CaseListPage = {
|
||||
count: number
|
||||
next: string | null
|
||||
previous: string | null
|
||||
results: ClinicalCase[]
|
||||
}
|
||||
|
||||
type QueryValue = string | number | boolean | null | undefined
|
||||
|
||||
type ServerCaseListPage = {
|
||||
count?: number
|
||||
next?: string | null
|
||||
previous?: string | null
|
||||
results?: ServerCaseRecord[]
|
||||
}
|
||||
|
||||
type ServerCaseRecord = {
|
||||
id?: number | string
|
||||
title?: string
|
||||
case_type?: string
|
||||
case_type_display?: string
|
||||
difficulty?: string
|
||||
difficulty_score?: number
|
||||
department?: number
|
||||
department_name?: string
|
||||
chief_complaint?: string
|
||||
description?: string
|
||||
patient_age?: number
|
||||
patient_gender?: string
|
||||
tags?: string
|
||||
competency_tags?: string[]
|
||||
estimated_minutes?: number
|
||||
osce_enabled?: boolean
|
||||
created_at?: string
|
||||
my_best_score?: number | null
|
||||
my_train_count?: number | null
|
||||
}
|
||||
|
||||
const CASE_LIST_PATHS: Record<CaseListSource, string> = {
|
||||
recommended: '/case/mobile/recommended/',
|
||||
specialty: '/case/mobile/specialty/',
|
||||
weak: '/case/mobile/weak/',
|
||||
teaching: '/case/mobile/teaching/',
|
||||
'teacher-task': '/case/mobile/teacher-task/'
|
||||
}
|
||||
|
||||
const TONES: CaseTone[] = ['blue', 'teal', 'pink', 'orange', 'purple', 'green']
|
||||
|
||||
export async function fetchCaseList(source: CaseListSource = 'recommended', query: CaseListQuery = {}): Promise<ClinicalCase[]> {
|
||||
const page = await fetchCaseListPage(source, query)
|
||||
return page.results
|
||||
}
|
||||
|
||||
export async function fetchCaseListPage(source: CaseListSource = 'recommended', query: CaseListQuery = {}): Promise<CaseListPage> {
|
||||
const path = CASE_LIST_PATHS[source] || CASE_LIST_PATHS.recommended
|
||||
const payload = await requestCaseList(path, query)
|
||||
const page = normalizeCaseListPage(payload, source)
|
||||
|
||||
return page
|
||||
}
|
||||
|
||||
export function readStoredClinicalCase() {
|
||||
@@ -95,3 +107,149 @@ export function readStoredClinicalCase() {
|
||||
if (value && typeof value === 'object') return value as ClinicalCase
|
||||
return null
|
||||
}
|
||||
|
||||
export function resolveClinicalCaseId(caseItem?: ClinicalCase | null) {
|
||||
if (!caseItem) return 0
|
||||
|
||||
const explicitId = Number(caseItem.id)
|
||||
if (Number.isInteger(explicitId) && explicitId > 0) return explicitId
|
||||
|
||||
const caseNo = Number(caseItem.caseNo)
|
||||
if (Number.isInteger(caseNo) && caseNo > 0) return caseNo
|
||||
|
||||
const matched = String(caseItem.id).match(/\d+/)
|
||||
const fallbackId = matched ? Number(matched[0]) : 0
|
||||
return Number.isInteger(fallbackId) && fallbackId > 0 ? fallbackId : 0
|
||||
}
|
||||
|
||||
function requestCaseList(path: string, query: CaseListQuery): Promise<ServerCaseListPage> {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${API_BASE_URL}${withQuery(path, query)}`,
|
||||
method: 'GET',
|
||||
timeout: 10000,
|
||||
header: createAuthHeaders(),
|
||||
success: response => {
|
||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
resolve(response.data as ServerCaseListPage)
|
||||
return
|
||||
}
|
||||
|
||||
const payload = response.data as Record<string, unknown> | undefined
|
||||
const code = typeof payload?.code === 'string' ? payload.code : undefined
|
||||
reject(new ApiRequestError(readErrorMessage(response.data, `病例列表加载失败(${response.statusCode})`), code, response.statusCode))
|
||||
},
|
||||
fail: error => {
|
||||
reject(new ApiRequestError(error.errMsg || '无法连接服务'))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeCaseListPage(payload: ServerCaseListPage, source: CaseListSource): CaseListPage {
|
||||
const results = Array.isArray(payload.results) ? payload.results : []
|
||||
|
||||
return {
|
||||
count: readNumber(payload.count, results.length),
|
||||
next: typeof payload.next === 'string' ? payload.next : null,
|
||||
previous: typeof payload.previous === 'string' ? payload.previous : null,
|
||||
results: results.map((item, index) => normalizeCaseRecord(item, source, index))
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeCaseRecord(item: ServerCaseRecord, source: CaseListSource, index: number): ClinicalCase {
|
||||
const id = readString(item.id, `case-${index + 1}`)
|
||||
const title = readString(item.title, '未命名病例')
|
||||
const gender = normalizeGender(item.patient_gender)
|
||||
const departmentName = readString(item.department_name, '未配置科室')
|
||||
const caseType = readString(item.case_type)
|
||||
const caseTypeDisplay = readString(item.case_type_display, caseType || '练习病例')
|
||||
const chiefComplaint = readString(item.chief_complaint, title)
|
||||
const mode = source === 'teaching' || caseType === 'teaching' ? 'teaching' : 'training'
|
||||
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
patientName: gender === '女' ? '患者女士' : '患者先生',
|
||||
gender,
|
||||
age: readNumber(item.patient_age, 0),
|
||||
department: departmentName,
|
||||
departmentId: readOptionalNumber(item.department),
|
||||
scene: caseTypeDisplay,
|
||||
caseNo: id,
|
||||
tone: TONES[index % TONES.length],
|
||||
mode,
|
||||
caseType,
|
||||
caseTypeDisplay,
|
||||
difficulty: readString(item.difficulty),
|
||||
difficultyScore: readOptionalNumber(item.difficulty_score),
|
||||
chiefComplaint,
|
||||
description: readString(item.description),
|
||||
tags: readString(item.tags),
|
||||
competencyTags: Array.isArray(item.competency_tags) ? item.competency_tags.map(String) : [],
|
||||
estimatedMinutes: readOptionalNumber(item.estimated_minutes),
|
||||
osceEnabled: Boolean(item.osce_enabled),
|
||||
myBestScore: typeof item.my_best_score === 'number' ? item.my_best_score : null,
|
||||
myTrainCount: typeof item.my_train_count === 'number' ? item.my_train_count : null,
|
||||
createdAt: readString(item.created_at),
|
||||
source
|
||||
}
|
||||
}
|
||||
|
||||
function createAuthHeaders() {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json'
|
||||
}
|
||||
const token = readAccessToken()
|
||||
if (token) headers.Authorization = `Bearer ${token}`
|
||||
return headers
|
||||
}
|
||||
|
||||
function readAccessToken() {
|
||||
try {
|
||||
const token = uni.getStorageSync('clinical-thinking-access-token')
|
||||
return typeof token === 'string' ? token.trim() : ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function withQuery(path: string, query: Record<string, QueryValue>) {
|
||||
const params = Object.entries(query)
|
||||
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
|
||||
.join('&')
|
||||
|
||||
return params ? `${path}?${params}` : path
|
||||
}
|
||||
|
||||
function readErrorMessage(data: unknown, fallback: string) {
|
||||
if (data && typeof data === 'object') {
|
||||
const payload = data as Record<string, unknown>
|
||||
const message = payload.message || payload.detail || payload.error
|
||||
if (typeof message === 'string' && message.trim()) return message
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
function readString(value: unknown, fallback = '') {
|
||||
if (typeof value === 'string') return value.trim() || fallback
|
||||
if (typeof value === 'number') return String(value)
|
||||
return fallback
|
||||
}
|
||||
|
||||
function readNumber(value: unknown, fallback = 0) {
|
||||
const numberValue = Number(value)
|
||||
return Number.isFinite(numberValue) ? numberValue : fallback
|
||||
}
|
||||
|
||||
function readOptionalNumber(value: unknown) {
|
||||
const numberValue = Number(value)
|
||||
return Number.isFinite(numberValue) ? numberValue : undefined
|
||||
}
|
||||
|
||||
function normalizeGender(value: unknown): '男' | '女' {
|
||||
if (value === 'female' || value === '女') return '女'
|
||||
return '男'
|
||||
}
|
||||
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
import { API_BASE_URL, ApiRequestError } from './auth'
|
||||
import type { DimensionScore, EvaluationDetail, ScoreDetail } from './assessment'
|
||||
import type { ScoreType } from './session'
|
||||
|
||||
type QueryValue = string | number | boolean | null | undefined
|
||||
|
||||
type ServerEnvelope<T> = {
|
||||
code?: string
|
||||
message?: string
|
||||
data?: T
|
||||
}
|
||||
|
||||
export type UserProfile = {
|
||||
id: number
|
||||
username: string
|
||||
real_name: string
|
||||
phone: string
|
||||
avatar: string
|
||||
gender: number
|
||||
role_type: string
|
||||
institution: number | string | null
|
||||
institution_name: string
|
||||
department: number | string | null
|
||||
department_name: string
|
||||
title_name: string
|
||||
practice_years: string
|
||||
major: string
|
||||
training_stage: string
|
||||
learning_target: string
|
||||
competency_profile: Record<string, unknown>
|
||||
weak_dimensions: string[]
|
||||
strong_dimensions: string[]
|
||||
ai_preference: Record<string, unknown>
|
||||
total_training_count: number
|
||||
total_case_count: number
|
||||
current_level: string
|
||||
status: number
|
||||
last_login_time: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export type TrainingRecord = {
|
||||
record_id: number
|
||||
case_id: number
|
||||
case_title: string
|
||||
department: string
|
||||
trained_at: string
|
||||
score: number
|
||||
score_type: ScoreType
|
||||
evaluation_level: string
|
||||
training_mode: string
|
||||
case_type: string
|
||||
}
|
||||
|
||||
export type TrainingRecordSummary = {
|
||||
total_cases: number
|
||||
total_hours: number
|
||||
avg_accuracy: number
|
||||
}
|
||||
|
||||
export type TrainingRecordsResponse = {
|
||||
count: number
|
||||
next: string | null
|
||||
previous: string | null
|
||||
results: TrainingRecord[]
|
||||
summary: TrainingRecordSummary
|
||||
}
|
||||
|
||||
export type AnalysisTrendItem = {
|
||||
label: string
|
||||
score: number
|
||||
}
|
||||
|
||||
export type AnalysisRadarItem = {
|
||||
dimension: string
|
||||
score: number
|
||||
}
|
||||
|
||||
export type UserAnalysis = {
|
||||
current_score: number
|
||||
score_delta_pct: number | null
|
||||
recent_trend: AnalysisTrendItem[]
|
||||
radar: AnalysisRadarItem[]
|
||||
weak_dimensions: string[]
|
||||
comment: string
|
||||
}
|
||||
|
||||
export type CompetencyMetrics = {
|
||||
completed_cases: number
|
||||
completed_cases_week: number
|
||||
total_hours: number
|
||||
avg_score: number
|
||||
diagnosis_accuracy: number
|
||||
}
|
||||
|
||||
export type EvaluationListItem = Partial<EvaluationDetail> & Record<string, unknown>
|
||||
|
||||
export type EvaluationListResponse = {
|
||||
count: number
|
||||
next: string | null
|
||||
previous: string | null
|
||||
results: EvaluationListItem[]
|
||||
}
|
||||
|
||||
export type TrainingRecordQuery = {
|
||||
search?: string
|
||||
page?: number
|
||||
}
|
||||
|
||||
export type EvaluationListQuery = {
|
||||
page?: number
|
||||
page_size?: number
|
||||
}
|
||||
|
||||
export function fetchUserProfile() {
|
||||
return serverRequest<UserProfile>('/user/profile/')
|
||||
}
|
||||
|
||||
export function fetchUserTrainingRecords(query: TrainingRecordQuery = {}) {
|
||||
return serverRequest<TrainingRecordsResponse>('/user/training-records/', {
|
||||
search: query.search,
|
||||
page: query.page
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchUserAnalysis() {
|
||||
return serverRequest<UserAnalysis>('/user/analysis/')
|
||||
}
|
||||
|
||||
export function fetchCompetencyMetrics() {
|
||||
return serverRequest<CompetencyMetrics>('/user/competency-metrics/')
|
||||
}
|
||||
|
||||
export async function fetchEvaluationList(query: EvaluationListQuery = {}) {
|
||||
const payload = await serverRequest<unknown>('/v1/evaluations', {
|
||||
page: query.page,
|
||||
page_size: query.page_size
|
||||
})
|
||||
return unwrapServerData<EvaluationListResponse>(payload)
|
||||
}
|
||||
|
||||
export async function fetchServerEvaluationDetail(evaluationId: number) {
|
||||
const payload = await serverRequest<unknown>(`/v1/evaluations/${encodeURIComponent(String(evaluationId))}`)
|
||||
const data = unwrapServerData<Record<string, unknown>>(payload)
|
||||
return normalizeEvaluationDetail(data, evaluationId)
|
||||
}
|
||||
|
||||
function serverRequest<T>(path: string, query?: Record<string, QueryValue>): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${API_BASE_URL}${withQuery(path, query)}`,
|
||||
method: 'GET',
|
||||
timeout: 10000,
|
||||
header: createAuthHeaders(),
|
||||
success: response => {
|
||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
resolve(response.data as T)
|
||||
return
|
||||
}
|
||||
|
||||
const payload = response.data as Record<string, unknown> | undefined
|
||||
const code = typeof payload?.code === 'string' ? payload.code : undefined
|
||||
reject(new ApiRequestError(readErrorMessage(response.data, `请求失败(${response.statusCode})`), code, response.statusCode))
|
||||
},
|
||||
fail: error => {
|
||||
reject(new ApiRequestError(error.errMsg || '无法连接服务'))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function createAuthHeaders() {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json'
|
||||
}
|
||||
const token = readAccessToken()
|
||||
if (token) headers.Authorization = `Bearer ${token}`
|
||||
return headers
|
||||
}
|
||||
|
||||
function readAccessToken() {
|
||||
try {
|
||||
const token = uni.getStorageSync('clinical-thinking-access-token')
|
||||
return typeof token === 'string' ? token.trim() : ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function withQuery(path: string, query?: Record<string, QueryValue>) {
|
||||
if (!query) return path
|
||||
const params = Object.entries(query)
|
||||
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
|
||||
.join('&')
|
||||
|
||||
return params ? `${path}?${params}` : path
|
||||
}
|
||||
|
||||
function readErrorMessage(data: unknown, fallback: string) {
|
||||
if (data && typeof data === 'object') {
|
||||
const payload = data as Record<string, unknown>
|
||||
const message = payload.message || payload.detail || payload.error
|
||||
if (typeof message === 'string' && message.trim()) return message
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
function unwrapServerData<T>(payload: unknown): T {
|
||||
if (payload && typeof payload === 'object' && 'data' in payload) {
|
||||
const envelope = payload as ServerEnvelope<T>
|
||||
if (envelope.code && envelope.code !== 'OK' && envelope.code !== 'ok') {
|
||||
throw new ApiRequestError(envelope.message || '请求失败', envelope.code)
|
||||
}
|
||||
if (envelope.data !== undefined) return envelope.data
|
||||
}
|
||||
|
||||
return payload as T
|
||||
}
|
||||
|
||||
function normalizeEvaluationDetail(data: Record<string, unknown>, fallbackId: number): EvaluationDetail {
|
||||
const caseInfo = isRecord(data.case) ? data.case : {}
|
||||
const totalScore = readNumber(data.total_score ?? data.score ?? data.current_score, 0)
|
||||
|
||||
return {
|
||||
evaluation_id: readNumber(data.evaluation_id ?? data.id ?? data.record_id, fallbackId),
|
||||
session_id: readNumber(data.session_id, 0),
|
||||
case_id: readNumber(data.case_id ?? caseInfo.id, 0),
|
||||
case_title: readString(data.case_title ?? data.caseTitle ?? caseInfo.title, '未命名病例'),
|
||||
score_type: readScoreType(data.score_type),
|
||||
total_score: totalScore,
|
||||
dimension_scores: normalizeDimensionScores(data.dimension_scores ?? data.radar),
|
||||
score_details: normalizeScoreDetails(data.score_details),
|
||||
overall_comment: readString(data.overall_comment ?? data.comment, '本次评价暂无详细点评。'),
|
||||
pdf_file_path: readOptionalString(data.pdf_file_path ?? data.pdfFilePath),
|
||||
created_at: readOptionalString(data.created_at ?? data.trained_at)
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeDimensionScores(value: unknown): DimensionScore[] {
|
||||
if (!Array.isArray(value)) return []
|
||||
return value.map(item => {
|
||||
const record = isRecord(item) ? item : {}
|
||||
return {
|
||||
dimension: readString(record.dimension ?? record.label, '未命名维度'),
|
||||
score: readNumber(record.score, 0),
|
||||
max_score: readNumber(record.max_score, 100),
|
||||
comment: readOptionalString(record.comment),
|
||||
evidence: Array.isArray(record.evidence) ? record.evidence.map(String) : undefined,
|
||||
deductions: Array.isArray(record.deductions) ? record.deductions.map(String) : undefined,
|
||||
improvement: readOptionalString(record.improvement)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeScoreDetails(value: unknown): ScoreDetail[] {
|
||||
if (!Array.isArray(value)) return []
|
||||
return value.map(item => {
|
||||
const record = isRecord(item) ? item : {}
|
||||
return {
|
||||
dimension: readString(record.dimension, '未命名维度'),
|
||||
score: readNumber(record.score, 0),
|
||||
deducted_reason: readOptionalString(record.deducted_reason),
|
||||
ai_confidence: record.ai_confidence === undefined ? undefined : readNumber(record.ai_confidence, 0),
|
||||
comment: readOptionalString(record.comment)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function readScoreType(value: unknown): ScoreType {
|
||||
return value === 'five_point' ? 'five_point' : 'percentage'
|
||||
}
|
||||
|
||||
function readNumber(value: unknown, fallback = 0) {
|
||||
const numberValue = Number(value)
|
||||
return Number.isFinite(numberValue) ? numberValue : fallback
|
||||
}
|
||||
|
||||
function readString(value: unknown, fallback = '') {
|
||||
if (typeof value === 'string') return value.trim() || fallback
|
||||
if (typeof value === 'number') return String(value)
|
||||
return fallback
|
||||
}
|
||||
|
||||
function readOptionalString(value: unknown) {
|
||||
const text = readString(value)
|
||||
return text || undefined
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value && typeof value === 'object' && !Array.isArray(value))
|
||||
}
|
||||
+1
-1
@@ -194,7 +194,7 @@ export async function fetchTrainingConfigOptions(caseId: number) {
|
||||
|
||||
export function createScenarioConfig(payload: ScenarioConfigPayload) {
|
||||
return createTrainingSession({
|
||||
case_id: DEFAULT_CASE_ID,
|
||||
case_id: resolveCaseId(payload),
|
||||
training_type: 'diagnosis_treatment',
|
||||
mode: payload.mode,
|
||||
score_type: 'percentage',
|
||||
|
||||
Reference in New Issue
Block a user