Step 3 of 6 for the survey2ddi retirement. Plan: HANDOVER_SURVEY2DDI.md. Best done after the xlsform2ddi --data issue, since kobo transform composes it.
Why
kobo2ddi in CorrelAid/survey2ddi is the only way to go from a Kobo asset UID to a codebook in one step. Its converter half is now duplicated here; its fetch half has no equivalent. Porting the fetch half is what lets that package stop shipping a second DDI implementation.
What to port
kobo2ddi/client.py — 98 lines of httpx, all straightforwardly fetch:
| Python |
Endpoint |
Notes |
list_assets() |
GET /api/v2/assets/ |
paginated (next) |
get_asset(uid) |
GET /api/v2/assets/<uid>/ |
title/metadata |
get_submissions(uid) |
GET /api/v2/assets/<uid>/data/ |
paginated; returns JSON records |
download_xlsform(uid, dest) |
GET /api/v2/assets/<uid>.xls |
serves xlsx despite the .xls path — SheetJS reads it, no legacy-BIFF work needed |
pull(uid, out) |
— |
writes submissions.json + form.xlsx into <out>/<uid>/ |
CLI surface mirroring the Python one:
formtransform kobo list
formtransform kobo pull <uid> -o output/
formtransform kobo transform <uid> -o output/ # pull + xlsform2ddi + data CSV
- Auth:
--token, else KOBO_API_TOKEN. Server: --server-url, else KOBO_SERVER_URL, else https://eu.kobotoolbox.org. Keep the env var names identical so existing .env files keep working; node --env-file replaces python-dotenv, no dependency needed.
- Kobo submission records are keyed
group/name with space-joined select_multiple values — exactly what buildDataCsv's readCell already handles, so transform is pull + the existing emitters with no adapter.
Constraints
- Node-only, like
src/cli.ts and src/fileChoices.ts. Do not re-export the client from src/index.ts. The app must not start shipping an HTTP client, and a node:fs / network import surfacing in the browser bundle is a regression.
- Fail loudly on a missing token rather than issuing an unauthenticated request.
- Do not print the token in error messages or verbose output.
Acceptance criteria
Step 3 of 6 for the survey2ddi retirement. Plan:
HANDOVER_SURVEY2DDI.md. Best done after thexlsform2ddi --dataissue, sincekobo transformcomposes it.Why
kobo2ddiinCorrelAid/survey2ddiis the only way to go from a Kobo asset UID to a codebook in one step. Its converter half is now duplicated here; its fetch half has no equivalent. Porting the fetch half is what lets that package stop shipping a second DDI implementation.What to port
kobo2ddi/client.py— 98 lines of httpx, all straightforwardlyfetch:list_assets()GET /api/v2/assets/next)get_asset(uid)GET /api/v2/assets/<uid>/get_submissions(uid)GET /api/v2/assets/<uid>/data/download_xlsform(uid, dest)GET /api/v2/assets/<uid>.xls.xlspath — SheetJS reads it, no legacy-BIFF work neededpull(uid, out)submissions.json+form.xlsxinto<out>/<uid>/CLI surface mirroring the Python one:
--token, elseKOBO_API_TOKEN. Server:--server-url, elseKOBO_SERVER_URL, elsehttps://eu.kobotoolbox.org. Keep the env var names identical so existing.envfiles keep working;node --env-filereplacespython-dotenv, no dependency needed.group/namewith space-joinedselect_multiplevalues — exactly whatbuildDataCsv'sreadCellalready handles, sotransformispull+ the existing emitters with no adapter.Constraints
src/cli.tsandsrc/fileChoices.ts. Do not re-export the client fromsrc/index.ts. The app must not start shipping an HTTP client, and anode:fs/ network import surfacing in the browser bundle is a regression.Acceptance criteria
list/pull/transformbehave as the Python CLI does, same env var namestransformproduces XML + CSV with a correctcaseQntysrc/index.ts--helpfor the command group and each subcommand