Clinical: dataType "001" (내부 임상) + measurement.json/meta.json에 data_type 필드 노출

labdb 자료 구분 코드를 "000"(외부 일반 VesiScan) → "001"(내부 임상 R&D)로
변경. ClinicalSessionStore에 const DATA_TYPE을 두고, meta.json과
measurement.json의 meta 블록에 "data_type": "001"을 함께 기록 — 폴더만
보고도 내부 임상 데이터임을 즉시 식별 가능.

LabdbUploader는 measurement.json의 meta.data_type을 우선 사용하고
없으면 "001" fallback. 향후 dataType이 늘어나도 단일 진실 출처(meta)에서
파생되도록 분리.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 12:34:18 +09:00
parent b3d9c16780
commit 3b96e5b4cb
2 changed files with 10 additions and 3 deletions
@@ -32,6 +32,9 @@ data class MeasurementCycle(
*/ */
object ClinicalSessionStore { object ClinicalSessionStore {
/** labdb 자료 구분 코드 — "001" = 내부 임상 R&D (외부 일반 VesiScan "000"과 구분). */
const val DATA_TYPE: String = "001"
/** ClinicalHome 화면에 진입한 상태인지. BLE 연결을 위한 DeviceScan 우회/Back 라우팅에 사용. */ /** ClinicalHome 화면에 진입한 상태인지. BLE 연결을 위한 DeviceScan 우회/Back 라우팅에 사용. */
@Volatile @Volatile
var inClinicalFlow: Boolean = false var inClinicalFlow: Boolean = false
@@ -96,6 +99,7 @@ object ClinicalSessionStore {
val dir = currentLogDir() ?: return val dir = currentLogDir() ?: return
dir.mkdirs() dir.mkdirs()
val meta = JSONObject().apply { val meta = JSONObject().apply {
put("data_type", DATA_TYPE)
put("device_name", s.deviceName) put("device_name", s.deviceName)
put("posture", s.posture.name) put("posture", s.posture.name)
put("step", s.step.name) put("step", s.step.name)
@@ -174,6 +178,7 @@ object ClinicalSessionStore {
// meta: writeMeta()와 동일 내용 포함 (단독 파일로 자족 가능) // meta: writeMeta()와 동일 내용 포함 (단독 파일로 자족 가능)
val meta = JSONObject().apply { val meta = JSONObject().apply {
put("data_type", DATA_TYPE)
put("subject_id", s.subjectId) put("subject_id", s.subjectId)
put("device_name", s.deviceName) put("device_name", s.deviceName)
put("posture", s.posture.name) put("posture", s.posture.name)
@@ -22,7 +22,8 @@ import java.util.Locale
* - 성공: folder/labdb_upload.json (응답 본문) * - 성공: folder/labdb_upload.json (응답 본문)
* - 실패: folder/labdb_upload_error.json (에러 본문) — 폴더는 그대로 유지되어 수동 재시도 가능 * - 실패: folder/labdb_upload_error.json (에러 본문) — 폴더는 그대로 유지되어 수동 재시도 가능
* *
* dataType = "000" (VesiScan 6채널). vesiscan_test 프로토콜과 동일. * dataType = "001" (내부 임상 R&D — 외부 일반 VesiScan "000"과 구분).
* measurement.json의 meta.data_type을 우선 사용, 없으면 DEFAULT_DATA_TYPE fallback.
* *
* testId: * testId:
* - 폴더명 그대로 사용 (사용자 결정). 단 ^[A-Za-z0-9_-]{1,40}$ 제한 때문에 * - 폴더명 그대로 사용 (사용자 결정). 단 ^[A-Za-z0-9_-]{1,40}$ 제한 때문에
@@ -30,7 +31,7 @@ import java.util.Locale
*/ */
object LabdbUploader { object LabdbUploader {
private const val TAG = "LabdbUploader" private const val TAG = "LabdbUploader"
private const val DATA_TYPE = "000" private const val DEFAULT_DATA_TYPE = "001"
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val isoFmt: SimpleDateFormat private val isoFmt: SimpleDateFormat
@@ -74,6 +75,7 @@ object LabdbUploader {
val meta = measurement.optJSONObject("meta") ?: JSONObject() val meta = measurement.optJSONObject("meta") ?: JSONObject()
val cycles = measurement.optJSONArray("cycles") ?: JSONArray() val cycles = measurement.optJSONArray("cycles") ?: JSONArray()
val testId = sanitizeTestId(folderName) val testId = sanitizeTestId(folderName)
val dataType = meta.optString("data_type").ifBlank { DEFAULT_DATA_TYPE }
val records = JSONArray() val records = JSONArray()
for (i in 0 until cycles.length()) { for (i in 0 until cycles.length()) {
@@ -99,7 +101,7 @@ object LabdbUploader {
return JSONObject().apply { return JSONObject().apply {
put("testId", testId) put("testId", testId)
put("dataType", DATA_TYPE) put("dataType", dataType)
put("sessionName", meta.optString("label").ifBlank { folderName }) put("sessionName", meta.optString("label").ifBlank { folderName })
put("memo", meta.optString("notes")) put("memo", meta.optString("notes"))
meta.optLong("ended_at", 0L).takeIf { it > 0 }?.let { meta.optLong("ended_at", 0L).takeIf { it > 0 }?.let {