Clinical #1: Subject ID 자유입력 → Phantom/Human/ETC 카테고리 (회의 결정)

회의 결정: 자유 텍스트 ID 입력 대신 측정 대상을 PHANTOM/HUMAN/ETC 세
카테고리로 명확히 구분. JSON/labdb 저장 방식도 함께 변경.

데이터 모델 (ClinicalSession.kt):
  - SubjectType enum 추가: PHANTOM / HUMAN / ETC
  - val subjectId: String 제거 → val subjectType: SubjectType
  - 폴더명: "{subjectId}_{...}" → "{SUBJECT_TYPE}_{...}"
    예: PHANTOM_VBT26050202_SUPINE_ALIGN_0CM_2026-06-08_15-30-00

JSON 변경 (meta.json + measurement.json):
  - "subject_id": "S001" → "subject_type": "PHANTOM"

labdb payload (LabdbUploader):
  - params.subject_id → params.subject_type (서버는 free JSON이라 코드 변경 불필요)

UI (ClinicalHomeView):
  - Subject ID OutlinedTextField 제거
  - LabeledSegmented "Subject Type" 3개 버튼 (Phantom/Human/ETC)
  - 마지막 선택 기억 (ClinicalSessionStore.lastSubjectType)

ClinicalLive 상단 sub line: "S=S001 · TV=300mL · Ab=22mm"
                          → "Phantom · TV=300mL · Ab=22mm"

호환성: 기존 측정 폴더는 그대로. 새 폴더부터 새 형식 적용.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 09:03:22 +09:00
parent f00aef50e7
commit f3cca9a4c7
5 changed files with 28 additions and 26 deletions
@@ -51,7 +51,8 @@ object ClinicalSessionStore {
/** 다음 측정 시 기본값으로 자동 채울 사용자 입력 — 같은 피험자/검사자 여러 측정 시 편의 */
var lastExaminer: String = ""
var lastNotes: String = ""
var lastSubjectId: String = ""
var lastSubjectType: com.example.medilightv2android.models.SubjectType =
com.example.medilightv2android.models.SubjectType.PHANTOM
var lastTrueVolumeMl: String = "" // 문자열로 보관 (입력 폼과 동일 형식)
var lastAbdomenThicknessMm: String = ""
@@ -62,7 +63,7 @@ object ClinicalSessionStore {
currentSession = session
lastExaminer = session.examiner
lastNotes = session.notes
lastSubjectId = session.subjectId
lastSubjectType = session.subjectType
lastTrueVolumeMl = session.trueVolumeMl?.toString() ?: ""
lastAbdomenThicknessMm = session.abdomenThicknessMm?.toString() ?: ""
cyclesBuffer.clear()
@@ -107,8 +108,9 @@ object ClinicalSessionStore {
put("examiner", s.examiner)
put("notes", s.notes)
put("label", s.label)
// 측정 대상 분류 (회의 결정: Phantom/Human/ETC)
put("subject_type", s.subjectType.name)
// 임상 시험 참값 (외부 측정기, vesiscan_test 프로토콜)
if (s.subjectId.isNotBlank()) put("subject_id", s.subjectId)
s.trueVolumeMl?.let { put("true_volume_ml", it) }
s.abdomenThicknessMm?.let { put("abdomen_thickness_mm", it) }
put("started_at", s.startedAt)
@@ -179,7 +181,7 @@ object ClinicalSessionStore {
// meta: writeMeta()와 동일 내용 포함 (단독 파일로 자족 가능)
val meta = JSONObject().apply {
put("data_type", DATA_TYPE)
put("subject_id", s.subjectId)
put("subject_type", s.subjectType.name)
put("device_name", s.deviceName)
put("posture", s.posture.name)
put("step", s.step.name)
@@ -89,7 +89,7 @@ object LabdbUploader {
put("step", meta.optString("step"))
put("is_alignment", meta.optBoolean("is_alignment"))
put("examiner", meta.optString("examiner"))
meta.optString("subject_id").takeIf { it.isNotBlank() }?.let { put("subject_id", it) }
meta.optString("subject_type").takeIf { it.isNotBlank() }?.let { put("subject_type", it) }
if (meta.has("true_volume_ml")) put("true_volume_ml", meta.optDouble("true_volume_ml"))
if (meta.has("abdomen_thickness_mm")) put("abdomen_thickness_mm", meta.optDouble("abdomen_thickness_mm"))
meta.optString("firmware_version").takeIf { it.isNotBlank() }?.let { put("firmware_version", it) }