Metadata-Version: 2.4
Name: comhom-sdk
Version: 0.1.0
Summary: The official SDK for integrating homelessness data into the Comhom Platform.
Author: Socialit
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: jsonschema~=4.25.1
Dynamic: author
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: license-file
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# Uploading Answers with `comhom_sdk.py`

This guide is for end users who want to upload answer data to Comhom using a file.

Script location: `sdk/comhom_sdk.py`

## Before you start

You need:

- Python 3.10+
- A valid API key with permission to upload answers
- A data file in `.json` or `.csv` format

If needed, install dependency:

```bash
pip install jsonschema
```

When you generate an API key on the platform, it is downloaded as `comhom.key`. The SDK uses that filename by default for `--api-key-file`.

Your organization is resolved automatically from the API key — you do not pass an organization ID to the SDK.

## 1) Prepare your API key file

Create a plain text file (for example `comhom.key`) that contains only your key:

```text
your-api-key-here
```

## 2) Prepare your answers file

You can use **JSON** or **CSV**.

Each row is one answer. Required fields:

| Field | Description |
|---|---|
| `respondent_code` | Stable, pseudonymous identifier for the respondent |
| `question_code` | Code of the question being answered |
| `value` | The answer (format depends on question type; see below) |
| `answered_at` | Date the answer was collected (`YYYY-MM-DD`) |
| `consent_signed` | Whether the respondent consented to data use (`true` / `false`) |

### Question codes and values

Valid `question_code` values come from the Comhom question catalogue. Examples from the platform fixtures:

| `question_code` | Type | Example `value` |
|---|---|---|
| `sex_gender` | Single choice | `cisgender_woman` |
| `age` | Single choice | `25_34` |
| `nationality` | Single choice | `national_country` |
| `A2` | Date | `2026-01-10` |
| `A4` | Single choice | `programme_completion` |
| `A5` | Program | `housing_first` (your org's program code) |
| `A6` | Multiple choice | `["cm", "hn"]` |

**Value formats:**

- **Single choice** — one option code (for example `sex_gender` → `cisgender_woman`, or `age` → `25_34`).
- **Multiple choice** — a list of option codes (for example `A6` → `["cm", "hn"]`).
- **Date** — `YYYY-MM-DD` (for example `A2` → `2026-01-10`).
- **Program (A5)** — a program `code` from your organization (created on the platform first; not a global option).

Only rows with `consent_signed: true` are loaded.

### JSON format

Use either:

- A top-level array of answers, or
- An object with an `answers` array

Example — one beneficiary with micro- and meso-variable answers:

```json
[
  {
    "respondent_code": "hash_7f3a9c2e",
    "question_code": "sex_gender",
    "answered_at": "2026-06-15",
    "consent_signed": true,
    "value": "cisgender_woman"
  },
  {
    "respondent_code": "hash_7f3a9c2e",
    "question_code": "age",
    "answered_at": "2026-06-15",
    "consent_signed": true,
    "value": "25_34"
  },
  {
    "respondent_code": "hash_7f3a9c2e",
    "question_code": "A2",
    "answered_at": "2026-06-15",
    "consent_signed": true,
    "value": "2026-01-10"
  },
  {
    "respondent_code": "hash_7f3a9c2e",
    "question_code": "A5",
    "answered_at": "2026-06-15",
    "consent_signed": true,
    "value": "housing_first"
  },
  {
    "respondent_code": "hash_7f3a9c2e",
    "question_code": "A6",
    "answered_at": "2026-06-15",
    "consent_signed": true,
    "value": ["cm", "hn"]
  }
]
```

### CSV format

Required columns:

- `respondent_code`
- `question_code`
- `answered_at`
- `consent_signed`
- `value`

`answered_at` is the collection/snapshot date for each answer. If your source system does not track per-answer timestamps, set one date for the whole export.

Upload behavior:

- Duplicate rows in the same file for the same beneficiary/question/date: the **last row wins**.
- Same value as the latest known snapshot on or before that date: the row is **skipped** (safe to re-upload).
- Same date with a changed value: the existing snapshot is **updated**.
- New date with a changed value: a new history row is **created**.

Example:

```csv
respondent_code,question_code,answered_at,consent_signed,value
hash_7f3a9c2e,sex_gender,2026-06-15,true,cisgender_woman
hash_7f3a9c2e,age,2026-06-15,true,25_34
hash_7f3a9c2e,A2,2026-06-15,true,2026-01-10
hash_7f3a9c2e,A5,2026-06-15,true,housing_first
hash_7f3a9c2e,A6,2026-06-15,true,"[""cm"",""hn""]"
```

## 3) Run the upload

```bash
python3 sdk/comhom_sdk.py upload \
  --input-file "/path/to/answers.json" \
  --api-key-file comhom.key
```

If your input is CSV, just change `--input-file` to the CSV path.

By default, uploads go to **sandbox**. Add `--production` to target production.

## Generating synthetic answers (optional)

You can generate synthetic answers to a file using the platform's answer upload JSON schema. The SDK fetches the org-specific schema automatically using your API key. Generation is separate from upload: write a file with `generate`, then upload it with `upload`.

```bash
python3 sdk/comhom_sdk.py generate \
  --total-answers 1000 \
  --respondent-count 20 \
  --output-file "/path/to/synthetic_answers.json" \
  --api-key-file comhom.key
```

To upload the generated file:

```bash
python3 sdk/comhom_sdk.py upload \
  --input-file "/path/to/synthetic_answers.json" \
  --api-key-file comhom.key
```

The output format is determined by the extension:

- `.json` -> JSON array
- `.csv` -> CSV with the required columns

## Command options

### `upload`

| Option | Short | Required | Description |
|---|---|---|---|
| `--input-file` | `-i` | Yes | Path to `.json` or `.csv` file |
| `--api-key-file` | `-k` | No | Path to API key file (default: `comhom.key`) |
| `--production` | `-p` | No | Upload to production (default is sandbox) |
| `--batch-size` | `-b` | No | Answers per request (default `500`) |

### `generate`

| Option | Short | Required | Description |
|---|---|---|---|
| `--total-answers` | `-n` | Yes | Number of synthetic answers to generate |
| `--output-file` | `-o` | Yes | Path to write `.json` or `.csv` output |
| `--respondent-count` | `-c` | No | Number of respondent codes to use (default `1`) |
| `--respondent-prefix` | `-p` | No | Prefix for respondent codes (default `R`) |
| `--answered-at` | `-a` | No | Fixed answered date (`YYYY-MM-DD`) for all answers |
| `--days-back` | `-d` | No | If `--answered-at` is not set, random date within last N days (default `365`; use `0` for today only) |
| `--seed` | `-s` | No | Seed for deterministic generation |
| `--api-key-file` | `-k` | No | Path to API key file (default: `comhom.key`) |

### `validate`

| Option | Short | Required | Description |
|---|---|---|---|
| `--input-file` | `-i` | Yes | Path to `.json` or `.csv` file |
| `--api-key-file` | `-k` | No | Path to API key file (default: `comhom.key`) |
| `--errors-file` | `-e` | No | Write all validation errors to a CSV file when validation fails |

### `delete`

| Option | Short | Required | Description |
|---|---|---|---|
| `--respondent-code` | `-r` | Yes | Beneficiary/respondent code whose answers to delete |
| `--api-key-file` | `-k` | No | Path to API key file (default: `comhom.key`) |
| `--production` | `-p` | No | Delete production data instead of sandbox (default is sandbox) |

## What the script does

### `upload`

1. Read your API key from file
2. Read and parse your JSON/CSV answers
3. Download the current upload schema from the platform using your API key (includes org-specific enums such as A5 program codes)
4. Validate your file locally before sending
5. Upload answers in batches

### `generate`

1. Fetch the answer upload JSON schema from the platform using your API key
2. Generate synthetic answers that conform to the schema
3. Write them to a JSON or CSV file

### `validate`

1. Read and parse your JSON/CSV answers
2. Download the current upload schema from the platform using your API key
3. Validate every answer row locally without uploading

Example:

```bash
python3 sdk/comhom_sdk.py validate \
  --input-file "/path/to/answers.json" \
  --api-key-file comhom.key \
  --errors-file errors.csv
```

### `delete`

1. Read your API key from file
2. Resolve your organization from the API key
3. Delete all answers for the given `respondent_code` in sandbox or production

Example:

```bash
python3 sdk/comhom_sdk.py delete \
  --respondent-code hash_7f3a9c2e \
  --api-key-file comhom.key
```

## Success and errors

- On success, upload returns counts for `created`, `updated`, and `skipped` rows.
- On failure, you will see an error message and no further batches are sent.
- When `validate` fails, use `--errors-file` to export every error row for review.

Common issues:

- Wrong API key or no permission
- Missing required columns in CSV
- Invalid date format (must be `YYYY-MM-DD`)
- `consent_signed` not set to `true`
- Invalid `question_code` or option code for that question
- A5 `value` not matching a program code in your organization
- Extra/invalid fields that do not match schema

## Notes

- By default, uploads go to sandbox.
- Use `--production` (or `-p`) to upload to or delete from production.
- The API key file defaults to `comhom.key` if `--api-key-file` is omitted.
- Client-side schema validation checks structure and required fields before upload.
- Question-specific `value` validation is finalized by the server during upload.
- Keep `comhom.key` out of source control.
