// api/session-export.prg — Format CSV/JSON export // // ctx: // format ("csv"|"json") // session (JSON of session row) // rows (JSON array of records) // // PRG generates the body string. server.js sets Content-Type/Disposition. FUNCTION Main() LOCAL cFormat, hSession, aRows, hRow, hSensor, aChannels, hCh LOCAL cBOM, cCRLF, aLines, aCells, i, hChMap cFormat := Lower(ctx_get("format", "csv")) hSession := hb_jsonDecode(ctx_get("session", "{}")) aRows := hb_jsonDecode(ctx_get("rows", "[]")) IF ! HB_ISHASH(hSession) ; hSession := { => } ; ENDIF IF ! HB_ISARRAY(aRows) ; aRows := {} ; ENDIF IF cFormat == "json" AP_JSONRESPONSE({ "session" => hSession, "records" => aRows }) RETURN NIL ENDIF // CSV with UTF-8 BOM (Excel Korean support) cBOM := Chr(239) + Chr(187) + Chr(191) cCRLF := Chr(13) + Chr(10) aLines := {} AAdd(aLines, "# Session: " + AllTrim(fn_HGet(hSession, "session_name"))) AAdd(aLines, "# Device: " + AllTrim(fn_HGet(hSession, "device_name")) + " (" + AllTrim(fn_HGet(hSession, "device_address")) + ")") // Header AAdd(aLines, "row,timestamp,cmd,batt_mv,batt_pct,temp_c,raa,ch0_peak,ch0_pidx,ch1_peak,ch1_pidx,ch2_peak,ch2_pidx,ch3_peak,ch3_pidx,ch4_peak,ch4_pidx,ch5_peak,ch5_pidx") FOR EACH hRow IN aRows hSensor := fn_HGet(hRow, "sensor") IF ! HB_ISHASH(hSensor) ; hSensor := { => } ; ENDIF // Build channel map (ch → {peak, peakIdx}) hChMap := { => } aChannels := fn_HGet(hRow, "channels") IF HB_ISARRAY(aChannels) FOR EACH hCh IN aChannels IF HB_ISHASH(hCh) .AND. hb_HHasKey(hCh, "ch") hChMap[ AllTrim(Str(hCh["ch"])) ] := hCh ENDIF NEXT ENDIF aCells := { ; AllTrim(Str(fn_HGet(hRow, "row_index"))), ; AllTrim(fn_HGet(hRow, "timestamp")), ; AllTrim(fn_HGet(hRow, "command_type")), ; numStr(fn_HGet(hSensor, "batteryMv")), ; numStr(fn_HGet(hSensor, "batteryPct")), ; numStr(fn_HGet(hSensor, "tempC")), ; numStr(fn_HGet(hRow, "raa_status")) ; } FOR i := 0 TO 5 IF hb_HHasKey(hChMap, AllTrim(Str(i))) AAdd(aCells, numStr(fn_HGet(hChMap[AllTrim(Str(i))], "peak"))) AAdd(aCells, numStr(fn_HGet(hChMap[AllTrim(Str(i))], "peakIdx"))) ELSE AAdd(aCells, "") AAdd(aCells, "") ENDIF NEXT AAdd(aLines, fn_Join(aCells, ",")) NEXT // Use AP_RPUTS to send raw CSV string AP_RPUTS(cBOM + fn_Join(aLines, cCRLF)) RETURN NIL STATIC FUNCTION fn_HGet(h, k) IF HB_ISHASH(h) .AND. hb_HHasKey(h, k) ; RETURN h[k] ; ENDIF RETURN "" STATIC FUNCTION numStr(x) IF x == NIL .OR. Empty(x) ; RETURN "" ; ENDIF IF ValType(x) == "N" ; RETURN AllTrim(Str(x)) ; ENDIF RETURN AllTrim(x) STATIC FUNCTION fn_Join(arr, sep) LOCAL out := "", i FOR i := 1 TO Len(arr) IF i > 1 ; out += sep ; ENDIF out += arr[i] NEXT RETURN out